/** * Trigger-service config. All env-resolved; the service token falls back to the * 0600 plum secret file. No secrets are baked in. */ import { readFileSync } from 'node:fs'; import { homedir } from 'node:os'; import { join } from 'node:path'; function fileOrEnv(envVar: string, file: string): string { if (process.env[envVar]) return process.env[envVar] as string; try { return readFileSync(file, 'utf8').trim(); } catch { return ''; } } export interface ServiceConfig { readonly port: number; /** Bearer token Prospector must present to POST /api/screening/requests. */ readonly serviceToken: string; /** Absolute path to client/mr_lookup.py. */ readonly lookupScript: string; readonly python: string; /** SQLite file backing the durable queue. */ readonly dbPath: string; /** Worker poll interval (ms) when the queue is empty. */ readonly pollIntervalMs: number; /** Per-lookup timeout (ms) — a lookup sleeps ~9s for paid content + vision. */ readonly lookupTimeoutMs: number; } export function loadConfig(): ServiceConfig { const root = join(import.meta.dir, '..'); return { port: Number(process.env['MRNUMBER_PORT'] ?? 8787), serviceToken: fileOrEnv( 'MRNUMBER_SERVICE_TOKEN', join(homedir(), '.config/cocotte-secrets/mr-number.service-token'), ), lookupScript: process.env['MR_NUMBER_LOOKUP_SCRIPT'] ?? join(root, 'client', 'mr_lookup.py'), python: process.env['MR_NUMBER_PYTHON'] ?? '/opt/homebrew/bin/python3', dbPath: process.env['MRNUMBER_QUEUE_DB'] ?? join(root, 'service', 'queue.sqlite'), pollIntervalMs: Number(process.env['MRNUMBER_POLL_MS'] ?? 2000), lookupTimeoutMs: Number(process.env['MRNUMBER_LOOKUP_TIMEOUT_MS'] ?? 180_000), }; }