Arboretum découvre désormais toutes les sessions Claude de la machine (scan ~/.claude/projects + registre ~/.claude/sessions), distingue vivantes/mortes par pid+procStart, et permet de reprendre une morte (--resume dans son cwd d'origine) ou forker une vivante sans la corrompre. - shared: SessionSummary enrichi (source, claudeSessionId, pid, resumable, attachable, registryStatus) — additif, PROTOCOL_VERSION inchangé ; types REST resume/fork. - db: migration id:2 (claude_session_id, resumed_from). - core: jsonl-discovery (parseur tolérant, scan asynchrone non bloquant), session-registry (vivacité pid+procStart), discovery-service (cache + refresh périodique + diff/broadcast), pty-manager (resume/fork + capture du claudeSessionId via le registre). - routes: /sessions/:id/resume (garde-fou 409 anti-corruption sur session vivante) et /fork ; GET fusionné managées + découvertes ; relais WS. - web: badges managed/discovered + busy/idle/waiting, actions conditionnelles (Open/Observe/Kill vs Fork/View vs Resume/Fork), vue read-only des sessions externes, i18n EN/FR. - tests: jsonl-discovery, session-registry, discovery-service + resume/fork (130 verts) ; acceptation E2E acceptance-p2.mjs (sans quota) ALL GREEN. Conforme aux verdicts S1 (resume dans cwd d'origine, vivacité pid+procStart) et S4 (munge cwd, parseur tête+queue, priorité de titre). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
54 lines
2.3 KiB
JavaScript
54 lines
2.3 KiB
JavaScript
#!/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<void> {
|
|
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);
|
|
});
|