#!/usr/bin/env bun /** * LLM contact name derivation worker. * * Iterates all clients with name_override=0, fetches recent conversation * history from imessage-sync, calls model-boss to derive a stable * display_name, and writes it back. Idempotent. * * Required env vars: * DB_PATH Path to the SQLite database (default: ./data/quinn-api.db) * SYNC_API_URL Base URL for imessage-sync (e.g. http://localhost:3100) * SYNC_API_KEY X-Service-Key header value for sync-api * MODEL_BOSS_URL Base URL for model-boss (e.g. http://apricot.wg:8000) * * Optional env vars: * MODEL_BOSS_API_KEY Bearer token for model-boss (if required) * MODEL_BOSS_MODEL Model identifier (default: auto) * * Usage: * DB_PATH=/opt/quinn-api/data/quinn-api.db \ * SYNC_API_URL=http://localhost:3100 \ * SYNC_API_KEY=... \ * MODEL_BOSS_URL=http://apricot.wg:8000 \ * bun run codebase/@features/api/src/scripts/derive-names.ts */ import { openDb } from '@/shared/db'; import { clientMigrations } from '@/entities/client'; import { runMigrations } from '@/shared/db'; import { logger } from '@/shared/logger'; import { parseDeriveConfig, deriveDisplayNames } from '@/features/derive-display-name'; async function main(): Promise { let config: ReturnType; try { config = parseDeriveConfig(process.env as Record); } catch (err) { logger.error('derive-names: config error', { error: String(err) }); process.exit(1); } const dbPath = process.env['DB_PATH'] ?? './data/quinn-api.db'; const db = openDb(dbPath); runMigrations(db, clientMigrations); const result = await deriveDisplayNames(db, config); if (result.failed > 0) { logger.error('derive-names: completed with failures', { processed: result.processed, updated: result.updated, skipped: result.skipped, failed: result.failed, }); for (const e of result.errors) { logger.error('derive-names: error detail', { error: e }); } process.exit(1); } logger.info('derive-names: success', { processed: result.processed, updated: result.updated, skipped: result.skipped, }); } main().catch((err) => { logger.error('derive-names: fatal', { error: String(err) }); process.exit(1); });