Some checks failed
CI / Build & test (Node 24) (push) Has been cancelled
CI / Pack & boot smoke (Node 22) (push) Has been cancelled
CI / No em/en dashes (push) Has been cancelled
CI / Build & test (Node 22) (push) Has been cancelled
Deploy site (production) / build-and-deploy (push) Successful in 20s
Remplace les 547 tirets cadratins (U+2014) et demi-cadratins (U+2013) des fichiers versionnés par la ponctuation contextuelle adaptée (point médian, deux-points, virgule, parenthèses ; tiret simple pour les plages), sur 122 fichiers (appli, vitrine, doc, tests, workflows, scripts). Ajoute le job CI « lint-dashes » (git grep -P) qui échoue si un tiret cadratin/demi-cadratin réapparaît, hors logo binaire et captures brutes du terminal (fidélité des fixtures de détection de dialogue).
103 lines
4.2 KiB
JavaScript
103 lines
4.2 KiB
JavaScript
#!/usr/bin/env node
|
|
import { readFileSync } from 'node:fs';
|
|
import { join, dirname } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { loadConfig, type Config } from './config.js';
|
|
import { openDb, type Db } from './db/index.js';
|
|
import { buildApp } from './app.js';
|
|
import { readClaudeHome } from './core/claude-settings.js';
|
|
import { runInstall, runUninstall, runStatus, printUsage, printTokenBanner } from './cli/install.js';
|
|
|
|
const pkg = JSON.parse(
|
|
readFileSync(join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json'), 'utf8'),
|
|
) as { version: string };
|
|
|
|
/**
|
|
* Applique l'override `claude_home` (réglage UI) sur la config runtime, sauf si --claude-home a été
|
|
* passé explicitement (le flag gagne). Lu une seule fois au démarrage → un changement via l'UI prend
|
|
* effet au redémarrage du daemon (comme l'intervalle de scan). Mute `config`.
|
|
*/
|
|
function applyClaudeHomeOverride(config: Config, db: Db): void {
|
|
if (config.claudeHomeFromFlag) return;
|
|
const override = readClaudeHome(db);
|
|
if (!override) return;
|
|
config.claudeHome = override;
|
|
config.claudeProjectsDir = join(override, 'projects');
|
|
config.claudeSessionsDir = join(override, 'sessions');
|
|
}
|
|
|
|
/** Démarre le daemon : écoute HTTP, scan des sessions, drain propre au SIGTERM/SIGINT. */
|
|
export async function runDaemon(config: Config): Promise<void> {
|
|
const db = openDb(config.dbPath);
|
|
applyClaudeHomeOverride(config, db); // override claude_home (réglage UI) avant de câbler les services
|
|
const { app, auth, manager, discovery, sessionArchive, repoDiscovery, worktrees, fsWatcher } = 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
|
|
sessionArchive.start(); // archivage auto des sessions terminées (rétention configurable)
|
|
worktrees.armMainCheckoutWatchers(); // P11 : temps réel du checkout principal de chaque repo
|
|
if (config.autoDiscover) repoDiscovery.start(); // découverte auto des repos : scan au boot + re-scan périodique
|
|
|
|
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é.
|
|
printTokenBanner(bootstrapToken, url);
|
|
} 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();
|
|
sessionArchive.stop();
|
|
repoDiscovery.stop();
|
|
void fsWatcher.closeAll(); // libère les descripteurs des watchers FS
|
|
manager.shutdown();
|
|
setTimeout(() => {
|
|
void app.close().then(() => process.exit(0));
|
|
}, 1000).unref();
|
|
};
|
|
process.on('SIGINT', () => shutdown('SIGINT'));
|
|
process.on('SIGTERM', () => shutdown('SIGTERM'));
|
|
}
|
|
|
|
// Routeur de sous-commandes. On inspecte argv[0] AVANT loadConfig (parseArgs strict throw sur
|
|
// un positionnel inconnu). Aucune sous-commande (ou un flag en tête) → daemon : rétrocompat stricte
|
|
// de `arboretum`, `arboretum --port 8080`, `npx @johanleroy/git-arboretum --allow-origin …`.
|
|
async function main(): Promise<void> {
|
|
const argv = process.argv.slice(2);
|
|
const cmd = argv[0];
|
|
switch (cmd) {
|
|
case 'install':
|
|
return runInstall(argv.slice(1));
|
|
case 'uninstall':
|
|
return runUninstall(argv.slice(1));
|
|
case 'status':
|
|
return runStatus(argv.slice(1));
|
|
case 'serve':
|
|
return runDaemon(loadConfig(argv.slice(1)));
|
|
case 'help':
|
|
case '--help':
|
|
case '-h':
|
|
printUsage(pkg.version);
|
|
return;
|
|
default:
|
|
if (cmd && !cmd.startsWith('-')) {
|
|
console.error(`Unknown command: ${cmd}\n`);
|
|
printUsage(pkg.version);
|
|
process.exit(1);
|
|
}
|
|
return runDaemon(loadConfig(argv));
|
|
}
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error(err instanceof Error ? err.message : err);
|
|
process.exit(1);
|
|
});
|