import { parseArgs } from 'node:util'; import { join } from 'node:path'; import { homedir } from 'node:os'; import { mkdirSync } from 'node:fs'; export interface Config { port: number; bind: string; dbPath: string; dataDir: string; /** origins supplémentaires autorisées (ex. https://machine.tailnet.ts.net) */ allowedOrigins: string[]; printToken: boolean; /** ~/.claude/projects (transcripts JSONL) — surchargeable via --claude-home (tests). */ claudeProjectsDir: string; /** ~/.claude/sessions (registre des sessions CLI vivantes). */ claudeSessionsDir: string; /** sujet VAPID des notifications Web Push (mailto: ou URL). */ vapidContact: string; } export function loadConfig(argv = process.argv.slice(2)): Config { const { values } = parseArgs({ args: argv, options: { port: { type: 'string', default: '7317' }, bind: { type: 'string', default: '127.0.0.1' }, db: { type: 'string' }, 'allow-origin': { type: 'string', multiple: true }, 'print-token': { type: 'boolean', default: false }, 'i-know-this-exposes-a-terminal': { type: 'boolean', default: false }, // racine de l'install Claude (~/.claude par défaut) — surchargée par les tests d'acceptation. 'claude-home': { type: 'string' }, // sujet VAPID des notifications push (contact requis par la spec Web Push). 'vapid-contact': { type: 'string' }, }, strict: true, }); const bind = values.bind ?? '127.0.0.1'; const loopback = bind === '127.0.0.1' || bind === '::1' || bind === 'localhost'; if (!loopback && !values['i-know-this-exposes-a-terminal']) { throw new Error( `Refusing to bind to ${bind}: an Arboretum server is remote code execution by design.\n` + `Use Tailscale Serve against the default 127.0.0.1 bind (recommended), or pass\n` + `--i-know-this-exposes-a-terminal if you really know what you are doing.`, ); } const dataDir = join(process.env.XDG_DATA_HOME ?? join(homedir(), '.local', 'share'), 'arboretum'); mkdirSync(dataDir, { recursive: true }); const claudeHome = values['claude-home'] ?? join(homedir(), '.claude'); return { port: Number(values.port), bind, dbPath: values.db ?? join(dataDir, 'arboretum.db'), dataDir, allowedOrigins: values['allow-origin'] ?? [], printToken: values['print-token'] ?? false, claudeProjectsDir: join(claudeHome, 'projects'), claudeSessionsDir: join(claudeHome, 'sessions'), vapidContact: values['vapid-contact'] ?? 'mailto:arboretum@localhost', }; }