#!/usr/bin/env node import { readFileSync } from 'node:fs'; import { join, dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; import { loadConfig } from './config.js'; import { openDb } from './db/index.js'; import { buildApp } from './app.js'; const pkg = JSON.parse( readFileSync(join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json'), 'utf8'), ) as { version: string }; async function main(): Promise { const config = loadConfig(); const db = openDb(config.dbPath); const { app, auth, manager, discovery } = buildApp(config, db, pkg.version); const bootstrapToken = auth.ensureBootstrapToken(); await app.listen({ port: config.port, host: config.bind }); discovery.start(); // scan initial + rafraîchissement périodique des sessions découvertes const url = `http://${config.bind === '0.0.0.0' ? '127.0.0.1' : config.bind}:${config.port}`; app.log.info(`Arboretum v${pkg.version} — ${url}`); if (bootstrapToken) { // Affiché une seule fois : le hash seul est stocké. console.log('\n┌──────────────────────────────────────────────────────────────────┐'); console.log('│ First start — your access token (shown once, store it safely): │'); console.log('└──────────────────────────────────────────────────────────────────┘'); console.log(`\n ${bootstrapToken}\n`); console.log(` Login at: ${url}/\n`); } else if (config.printToken) { console.log('Tokens are stored hashed and cannot be re-printed. Create a new one from Settings (or reset the db).'); } let shuttingDown = false; const shutdown = (signal: string): void => { if (shuttingDown) return; shuttingDown = true; app.log.info(`${signal} received — draining sessions then exiting`); discovery.stop(); manager.shutdown(); setTimeout(() => { void app.close().then(() => process.exit(0)); }, 1000).unref(); }; process.on('SIGINT', () => shutdown('SIGINT')); process.on('SIGTERM', () => shutdown('SIGTERM')); } main().catch((err) => { console.error(err instanceof Error ? err.message : err); process.exit(1); });