From 80a5f3bf425fc2b4ea2f78d33266ac4b2792763e Mon Sep 17 00:00:00 2001 From: Emanuel Almeida Date: Thu, 23 Apr 2026 01:19:21 +0100 Subject: [PATCH] feat(observabilidade): watcher chokidar incremental --- api/scripts/sessions-indexer.ts | 7 ++--- api/services/sessions/watcher.ts | 49 ++++++++++++++++++++++++++++---- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/api/scripts/sessions-indexer.ts b/api/scripts/sessions-indexer.ts index 6beff24..fb43786 100644 --- a/api/scripts/sessions-indexer.ts +++ b/api/scripts/sessions-indexer.ts @@ -9,7 +9,7 @@ * Env: * OBSERVABILIDADE_DB Override ao caminho da BD SQLite */ -import { indexAll, DEFAULT_DB_PATH } from '../services/sessions/indexer.js' +import { indexAll, DEFAULT_DB_PATH, PROJECTS_ROOT } from '../services/sessions/indexer.js' import { startWatcher } from '../services/sessions/watcher.js' async function main(): Promise { @@ -24,11 +24,10 @@ async function main(): Promise { console.log(`[indexer] modo=${mode} db=${dbPath}`) if (mode === '--watch') { - console.log(`[indexer] watch mode em ${dbPath}`) + console.log(`[indexer] watch mode em ${PROJECTS_ROOT} -> ${dbPath}`) await indexAll({ dbPath }) await startWatcher(dbPath) - console.log('[indexer] watcher ainda não implementado (ver Task 8) — saída limpa') - process.exit(0) + return } const start = Date.now() diff --git a/api/services/sessions/watcher.ts b/api/services/sessions/watcher.ts index 679361d..57e7d5d 100644 --- a/api/services/sessions/watcher.ts +++ b/api/services/sessions/watcher.ts @@ -1,7 +1,44 @@ -/** - * Stub — implementação real chega em Task 8 (chokidar). - * Devolve imediatamente; o CLI imprime aviso e termina com exit 0. - */ -export async function startWatcher(_dbPath: string): Promise { - return +import chokidar from 'chokidar' +import { openSessionsDb } from './db.js' +import { indexFile, PROJECTS_ROOT } from './indexer.js' + +export async function startWatcher(dbPath: string): Promise { + const db = openSessionsDb(dbPath) + const watcher = chokidar.watch(`${PROJECTS_ROOT}/**/*.jsonl`, { + persistent: true, + ignoreInitial: true, + awaitWriteFinish: { stabilityThreshold: 2000, pollInterval: 500 }, + }) + + async function reindex(path: string): Promise { + try { + await indexFile(db, path) + console.log(`[watcher] indexed ${path}`) + } catch (err) { + console.error(`[watcher] erro ${path}:`, err) + } + } + + watcher + .on('add', reindex) + .on('change', reindex) + .on('unlink', (path) => { + db.deleteByJsonlPath(path) + console.log(`[watcher] removed ${path}`) + }) + .on('error', (err) => console.error('[watcher] error:', err)) + + console.log('[watcher] pronto') + + // Registar handler SIGTERM/SIGINT para fechar DB limpa (evita WAL corruption em Task 9 systemd restart) + const cleanup = async (): Promise => { + console.log('[watcher] SIGTERM/SIGINT — a fechar watcher e DB') + await watcher.close() + db.close() + process.exit(0) + } + process.on('SIGTERM', () => { void cleanup() }) + process.on('SIGINT', () => { void cleanup() }) + + return new Promise(() => {}) // nunca resolve — processo mantém-se vivo }