← back to Dw Photo Capture
Batch OCR: load GEMINI_API_KEY from secrets + default to gemini engine
a138b398c6f6282e905da7c066921610a5e19668 · 2026-09-18 14:35:15 -0700 · Steve Abrams
The pm2 process launched without GEMINI_API_KEY exported, so the gemini
engine read as unavailable and identify silently fell back to the retired
Ollama (qwen2.5vl) path. Load the key from the canonical secrets-manager/.env
the same way the Shopify token is loaded, and prefer gemini whenever a key is
present (override with OCR_ENGINE=macvision). Verified live: identify now
reaches gemini-2.5-flash; only a credits top-up remains.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uns3k4K15WBcFJNr3GcUd3
Files touched
Diff
commit a138b398c6f6282e905da7c066921610a5e19668
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Fri Sep 18 14:35:15 2026 -0700
Batch OCR: load GEMINI_API_KEY from secrets + default to gemini engine
The pm2 process launched without GEMINI_API_KEY exported, so the gemini
engine read as unavailable and identify silently fell back to the retired
Ollama (qwen2.5vl) path. Load the key from the canonical secrets-manager/.env
the same way the Shopify token is loaded, and prefer gemini whenever a key is
present (override with OCR_ENGINE=macvision). Verified live: identify now
reaches gemini-2.5-flash; only a credits top-up remains.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uns3k4K15WBcFJNr3GcUd3
---
server.js | 27 +++++++++++++++++++--------
1 file changed, 19 insertions(+), 8 deletions(-)
diff --git a/server.js b/server.js
index 5c07408..d6977db 100644
--- a/server.js
+++ b/server.js
@@ -86,12 +86,20 @@ const AUTH_PASS = process.env.AUTH_PASS || 'DW2024!';
const SHOP = 'designer-laboratory-sandbox.myshopify.com';
const API = '2024-10';
let TOKEN = process.env.SHOPIFY_ADMIN_TOKEN || '';
-if (!TOKEN) {
- try {
- for (const l of fs.readFileSync(path.join(process.env.HOME, 'Projects/secrets-manager/.env'), 'utf8').split('\n'))
- if (l.startsWith('SHOPIFY_ADMIN_TOKEN=')) { TOKEN = l.split('=').slice(1).join('=').trim(); break; }
- } catch (e) {}
-}
+// Load selected secrets from the canonical secrets-manager/.env into process.env.
+// pm2 launches this process WITHOUT these exported, so without this loop GEMINI_API_KEY
+// is empty at runtime and the gemini OCR engine silently falls back to the retired
+// Ollama path (resolveEngines → engineAvailable('gemini')===false). Loading it here is
+// durable across restarts. Never overwrites an already-set env value.
+try {
+ const NEED = ['SHOPIFY_ADMIN_TOKEN', 'GEMINI_API_KEY'];
+ for (const l of fs.readFileSync(path.join(process.env.HOME, 'Projects/secrets-manager/.env'), 'utf8').split('\n')) {
+ const i = l.indexOf('='); if (i < 0) continue;
+ const k = l.slice(0, i).trim();
+ if (NEED.includes(k) && !process.env[k]) process.env[k] = l.slice(i + 1).trim();
+ }
+} catch (e) {}
+if (!TOKEN) TOKEN = process.env.SHOPIFY_ADMIN_TOKEN || '';
fs.mkdirSync(PHOTOS, { recursive: true });
@@ -653,7 +661,10 @@ const GEMINI_VISION_MODEL = process.env.GEMINI_VISION_MODEL || 'gemini-2.5-flash
const GCV_API_KEY = process.env.GCV_API_KEY || process.env.GOOGLE_VISION_API_KEY || '';
const IS_DARWIN = process.platform === 'darwin';
const HAS_MACVISION = IS_DARWIN && fs.existsSync(path.join(ROOT, 'bin/ocr'));
-const DEFAULT_ENGINE = (process.env.OCR_ENGINE || (HAS_MACVISION ? 'macvision' : 'gemini')).toLowerCase();
+// Prefer gemini whenever a key is present: macvision's identify path routes to the
+// retired Ollama (qwen2.5vl), so gemini is the only working OCR engine. Override with
+// OCR_ENGINE=macvision to force the local path back. (Steve chose gemini, 2026-09-18.)
+const DEFAULT_ENGINE = (process.env.OCR_ENGINE || (GEMINI_API_KEY ? 'gemini' : (HAS_MACVISION ? 'macvision' : 'gemini'))).toLowerCase();
const ALL_ENGINES = ['macvision', 'gemini', 'gcv'];
// Rough per-scan cost so the caller always sees the $ (Steve's "always show costs" rule).
const ENGINE_COST_USD = { macvision: 0, gemini: 0.0006, gcv: 0.0015 };
@@ -2609,7 +2620,7 @@ async function createNewItem(p, b64, dryRun) {
const fm = buildFmMaster(p, mint, vreg);
const specsObj = { material, collection: p.collection || '', width: p.width || '', roll_length: p.roll_length || '',
repeat: p.repeat || '', pattern_match: p.pattern_match || '', substrate: p.substrate || '',
- how_sold: p.how_sold || '', price_code: p.price_code || '' };
+ how_sold: p.how_sold || '', price_code: p.price_code || '', notes: p.notes || '' };
const flags = [mint.flag, fm.vid_flag, dup ? `mfr# ${mfr} already in catalog as ${dup.dw_sku || dup.product_id}` : null].filter(Boolean);
// FULL 3-system preview — exactly what Shopify / dw_unified / FileMaker WOULD receive.
const preview = {
← bad2f48 auto-data-snapshot: 2026-09-18T14:19:46 (2 data files) — dat
·
back to Dw Photo Capture
·
Photo capture: linear 2-photo flow, Measure Depth, White Bal 14ba745 →