Files
arboretum/packages/server/src/config.ts
Johan LEROY 664e2b583c P4-B: notifications Web Push (VAPID) sur passage en waiting
- db: migration id=4 `push_subscriptions` (liées au token d'auth) ; clés VAPID en settings
- PushService: bootstrap idempotent des clés VAPID, subscribe/unsubscribe/count,
  notify() best-effort (purge des abonnements 410/404 Gone) ; sender injectable pour les tests
- routes/push.ts: GET vapid-public-key, POST subscribe/unsubscribe/test (toutes sous auth globale)
- pty-manager: déclencheur push sur FRONT MONTANT vers waiting, débouncé 1500ms et annulable
  (faux positif ignoré) ; câblage app/index/config (--vapid-contact)
- shared/api.ts: types VapidKeyResponse / PushSubscribeRequest / PushUnsubscribeRequest
- deps: web-push (+ @types/web-push) côté serveur
- tests: push-service (idempotence, UPSERT, 410-purge, payload) + trigger pty-manager (169 verts)
2026-06-15 12:13:56 +02:00

65 lines
2.5 KiB
TypeScript

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',
};
}