@orkastery/cli 0.5.1 → 0.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/adapters/claude-code/README.md +158 -0
- package/adapters/claude-code/agents/ork-master.md +2 -0
- package/adapters/claude-code/commands/master.md +8 -0
- package/adapters/claude-code/commands/ork.md +9 -0
- package/adapters/claude-code/mods/autoridade.json +56 -0
- package/adapters/claude-code/mods/ork-clima/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-clima/hooks/contas.ts +283 -0
- package/adapters/claude-code/mods/ork-clima/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-clima/hooks/previsao.ts +393 -0
- package/adapters/claude-code/mods/ork-clima/hooks/register.ts +205 -0
- package/adapters/claude-code/mods/ork-clima/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-clima/types/index.d.ts +80 -0
- package/adapters/claude-code/mods/ork-freio/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-freio/hooks/carga.ts +160 -0
- package/adapters/claude-code/mods/ork-freio/hooks/conferencia.ts +309 -0
- package/adapters/claude-code/mods/ork-freio/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-freio/hooks/lote.ts +270 -0
- package/adapters/claude-code/mods/ork-freio/hooks/register.ts +285 -0
- package/adapters/claude-code/mods/ork-freio/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-freio/types/index.d.ts +127 -0
- package/adapters/claude-code/mods/ork-mesa/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/cartoes.tsx +246 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/decidir.ts +110 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/fila.ts +253 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/obvia.ts +98 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/register.tsx +493 -0
- package/adapters/claude-code/mods/ork-mesa/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-mesa/types/index.d.ts +131 -0
- package/adapters/claude-code/mods/ork-pista/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-pista/hooks/acoes.ts +71 -0
- package/adapters/claude-code/mods/ork-pista/hooks/ci.ts +117 -0
- package/adapters/claude-code/mods/ork-pista/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-pista/hooks/pista.tsx +356 -0
- package/adapters/claude-code/mods/ork-pista/hooks/register.tsx +439 -0
- package/adapters/claude-code/mods/ork-pista/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-pista/types/index.d.ts +85 -0
- package/adapters/claude-code/mods/ork-ponte/.claude-plugin/plugin.json +13 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/faixa.tsx +57 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/guarda.ts +410 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/mostrado.ts +55 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/nucleo.ts +157 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/papel.ts +42 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/register.ts +331 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/vigia.ts +76 -0
- package/adapters/claude-code/mods/ork-ponte/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-ponte/types/index.d.ts +178 -0
- package/adapters/claude-code/mods/ork-portao/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-portao/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-portao/hooks/lease.ts +201 -0
- package/adapters/claude-code/mods/ork-portao/hooks/plano.ts +304 -0
- package/adapters/claude-code/mods/ork-portao/hooks/register.ts +339 -0
- package/adapters/claude-code/mods/ork-portao/hooks/verify.ts +152 -0
- package/adapters/claude-code/mods/ork-portao/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-portao/types/index.d.ts +60 -0
- package/adapters/claude-code/mods/ork-radar/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-radar/hooks/colisao.ts +420 -0
- package/adapters/claude-code/mods/ork-radar/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-radar/hooks/mapa.tsx +322 -0
- package/adapters/claude-code/mods/ork-radar/hooks/register.tsx +470 -0
- package/adapters/claude-code/mods/ork-radar/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-radar/types/index.d.ts +72 -0
- package/adapters/claude-code/mods/ork-sentinela/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/alertas.ts +85 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/despacho.ts +156 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/register.ts +511 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/vigia.ts +186 -0
- package/adapters/claude-code/mods/ork-sentinela/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-sentinela/types/index.d.ts +65 -0
- package/adapters/claude-code/mods/ork-torre/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-torre/hooks/agenda.ts +60 -0
- package/adapters/claude-code/mods/ork-torre/hooks/detalhe.tsx +236 -0
- package/adapters/claude-code/mods/ork-torre/hooks/frases.ts +42 -0
- package/adapters/claude-code/mods/ork-torre/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-torre/hooks/painel.tsx +344 -0
- package/adapters/claude-code/mods/ork-torre/hooks/rede.ts +97 -0
- package/adapters/claude-code/mods/ork-torre/hooks/register.tsx +437 -0
- package/adapters/claude-code/mods/ork-torre/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-torre/types/index.d.ts +108 -0
- package/adapters/codex/skills/ork/SKILL.md +18 -3
- package/adapters/hermes/README.md +1 -0
- package/adapters/hermes/bin/ork-network-status.sh +10 -0
- package/adapters/hermes/hermes.plugin.json +1 -0
- package/adapters/hermes/hitl-ingress/__init__.py +2 -2
- package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +7 -6
- package/adapters/openclaw/README.md +26 -5
- package/adapters/openclaw/dist/hitl-ingress.js +2 -2
- package/adapters/openclaw/dist/index.js +46 -11
- package/adapters/openclaw/openclaw.plugin.json +5 -1
- package/adapters/openclaw/package.json +2 -2
- package/adapters/openclaw/src/hitl-ingress.ts +2 -2
- package/adapters/openclaw/src/index.ts +49 -11
- package/assets/docs-en/markdownlint-cli2.jsonc +22 -0
- package/assets/docs-en/padroes/documentacao-de-produto.md +135 -0
- package/assets/docs-en/padroes/roadmap-de-produto.md +100 -0
- package/assets/docs-en/produto/README.md +14 -0
- package/assets/docs-en/produto/_modelo-feature.md +62 -0
- package/assets/docs-en/roadmap/README.md +14 -0
- package/assets/docs-en/roadmap/_modelo-item.md +75 -0
- package/assets/orkmind_bridge.py +117 -9
- package/dist/adapters/claude-bg.js +116 -18
- package/dist/adapters/codex.js +14 -4
- package/dist/agents-md.js +24 -14
- package/dist/auditrun.js +22 -3
- package/dist/board.js +20 -17
- package/dist/branch-de-estado.js +88 -10
- package/dist/busca-semantica.js +52 -12
- package/dist/canarios-rm057.js +149 -0
- package/dist/canarios.js +2 -0
- package/dist/catalogo.js +33 -0
- package/dist/ci.js +21 -8
- package/dist/claims-conferir.js +342 -0
- package/dist/claims.js +41 -1
- package/dist/company-brain-client.js +36 -1
- package/dist/company-brain-context.js +158 -22
- package/dist/company-brain-journal.js +2 -1
- package/dist/company-brain-migration.js +36 -1
- package/dist/conducao-sentinela.js +388 -0
- package/dist/conducao-texto.js +12 -18
- package/dist/conducao.js +122 -30
- package/dist/conhecimento.js +490 -0
- package/dist/docs.js +70 -17
- package/dist/doctor-pulse-cron.js +349 -0
- package/dist/doctor-rede.js +150 -0
- package/dist/doctor.js +195 -111
- package/dist/entrega-pr.js +42 -29
- package/dist/escopo-escrita.js +10 -1
- package/dist/esgotamentos.js +181 -0
- package/dist/estado-thread.js +23 -1
- package/dist/experiencia-instalacao.js +45 -25
- package/dist/experiencia.js +12 -2
- package/dist/fabrica-batida.js +310 -0
- package/dist/fabrica-estado.js +52 -14
- package/dist/fabrica-publicar.js +9 -1
- package/dist/fechamento.js +20 -66
- package/dist/fila-pesada.js +444 -0
- package/dist/fix.js +3 -2
- package/dist/forja.js +31 -7
- package/dist/gates.js +3 -24
- package/dist/handoff.js +10 -6
- package/dist/hitl-contract.js +59 -4
- package/dist/hitl-curto.js +11 -6
- package/dist/hitl-gates.js +5 -0
- package/dist/hitl-local-receipt.js +2 -0
- package/dist/hitl-lote.js +5 -2
- package/dist/hitl-resumo.js +38 -3
- package/dist/hitl-tempo-parado.js +201 -0
- package/dist/hitl-texto-livre.js +5 -5
- package/dist/hitl.js +29 -33
- package/dist/horario.js +41 -7
- package/dist/hosts.js +104 -11
- package/dist/impedimento.js +299 -0
- package/dist/index.js +961 -542
- package/dist/indice-vetorial.js +87 -10
- package/dist/indice.js +9 -1
- package/dist/init.js +56 -43
- package/dist/instabilidade.js +144 -0
- package/dist/integracoes-locais.js +4 -0
- package/dist/intelligence-graph-cli.js +170 -16
- package/dist/intelligence-graph-contexto.js +254 -0
- package/dist/intelligence-graph-contract.js +2 -1
- package/dist/intelligence-graph-extract-md.js +56 -4
- package/dist/intelligence-graph-extract-ts.js +71 -3
- package/dist/intelligence-graph-parsers.js +6 -4
- package/dist/intelligence-graph-query.js +66 -19
- package/dist/leases.js +664 -87
- package/dist/ledger-stats.js +15 -1
- package/dist/ledger.js +18 -1
- package/dist/licoes.js +1 -0
- package/dist/liveness.js +6 -3
- package/dist/locale-ajuda.js +761 -0
- package/dist/locale-gerados.js +160 -0
- package/dist/locale-master.js +99 -0
- package/dist/locale-onboarding.js +92 -0
- package/dist/locale-phase.js +151 -0
- package/dist/locale-pulse.js +211 -0
- package/dist/locale-retry.js +385 -0
- package/dist/locale-roadmap.js +102 -0
- package/dist/locale-ship.js +494 -0
- package/dist/locale-thread.js +277 -0
- package/dist/locale-verify.js +125 -0
- package/dist/locale.js +714 -0
- package/dist/maestro-runtime.js +4 -1
- package/dist/manifest.js +149 -4
- package/dist/maquina.js +23 -4
- package/dist/master-digest.js +6 -1
- package/dist/master-nota.js +18 -11
- package/dist/master.js +145 -31
- package/dist/mcp-artifacts.js +43 -3
- package/dist/mcp-git.js +42 -8
- package/dist/mcp-grafo-worker.js +5 -3
- package/dist/mcp-grafo.js +55 -6
- package/dist/mcp-server.js +22 -3
- package/dist/mcp-ship.js +3 -1
- package/dist/mcp-verify.js +6 -1
- package/dist/memoria.js +48 -6
- package/dist/modos.js +3 -6
- package/dist/network-roadmap.js +196 -18
- package/dist/noctum-arco.js +911 -0
- package/dist/noctum-capacidade.js +141 -0
- package/dist/noctum-envelope.js +810 -0
- package/dist/noctum-execucao.js +304 -0
- package/dist/noctum-fila.js +280 -0
- package/dist/ocupacao.js +40 -5
- package/dist/onboarding.js +40 -21
- package/dist/orkmind.js +99 -7
- package/dist/orquestracao.js +24 -21
- package/dist/parado-no-condutor.js +219 -38
- package/dist/phase.js +160 -52
- package/dist/policies.js +56 -5
- package/dist/portfolio.js +8 -3
- package/dist/postura-local.js +234 -0
- package/dist/preflight.js +11 -10
- package/dist/procedencia.js +225 -0
- package/dist/projeto-alvo.js +132 -5
- package/dist/prompts.js +40 -1
- package/dist/prova-ativacao.js +75 -6
- package/dist/pulse-delivery.js +47 -4
- package/dist/pulse.js +98 -19
- package/dist/ratelimit.js +24 -6
- package/dist/recall.js +3 -1
- package/dist/redacao-saida.js +63 -0
- package/dist/rede-adesao.js +399 -0
- package/dist/rede-forja.js +289 -0
- package/dist/rede-projetos.js +239 -0
- package/dist/rede-status.js +337 -0
- package/dist/rede.js +980 -0
- package/dist/retry.js +291 -140
- package/dist/roadmap-reservas.js +20 -3
- package/dist/roadmap-status.js +150 -34
- package/dist/runtime-context.js +181 -3
- package/dist/runtime-profiles.js +22 -1
- package/dist/runtimes.js +2 -1
- package/dist/saida-segura.js +58 -0
- package/dist/session-events.js +7 -3
- package/dist/session-watcher-claude.js +120 -24
- package/dist/session-watcher.js +185 -33
- package/dist/sessoes-contas.js +187 -0
- package/dist/sessoes-inventario.js +6 -18
- package/dist/sessoes.js +83 -2
- package/dist/ship-publicar.js +800 -0
- package/dist/ship.js +157 -88
- package/dist/steal.js +77 -0
- package/dist/thread-close.js +3 -2
- package/dist/thread.js +299 -49
- package/dist/util.js +8 -1
- package/dist/verify.js +141 -52
- package/dist/vida-da-thread.js +254 -0
- package/dist/worktree-local.js +165 -0
- package/dist/worktree.js +25 -2
- package/dist/yaml.js +27 -8
- package/eval/fixtures/fx-pedido-colado/caso.json +20 -0
- package/package.json +7 -3
- package/references/definition-of-done.md +1 -1
- package/schemas/code-artifact-graph.v1.schema.json +2 -1
- package/skills/README.md +1 -1
- package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
- package/skills/phases/master-metrics/SKILL.md +8 -3
package/dist/session-watcher.js
CHANGED
|
@@ -33,10 +33,11 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.BLOCO_WATCH_BYTES = exports.INTERVALO_WATCH_MS = exports.LIMITE_MORTE_MS = void 0;
|
|
36
|
+
exports.BLOCO_WATCH_BYTES = exports.MAX_FALHAS_WATCH = exports.MAX_ESPERA_ERRO_WATCH_MS = exports.INTERVALO_WATCH_MS = exports.LIMITE_MORTE_MS = void 0;
|
|
37
37
|
exports.observarSessao = observarSessao;
|
|
38
38
|
exports.validarFonteWatcher = validarFonteWatcher;
|
|
39
39
|
exports.iniciarWatcher = iniciarWatcher;
|
|
40
|
+
exports.acompanharSessao = acompanharSessao;
|
|
40
41
|
/** D6: observador restrito a uma sessão, cursor canônico e resultados idempotentes. */
|
|
41
42
|
const fs = __importStar(require("node:fs"));
|
|
42
43
|
const path = __importStar(require("node:path"));
|
|
@@ -110,6 +111,8 @@ function semProvaNativa(snapshot, terminal, fonteVencedora, fechamento, sessionI
|
|
|
110
111
|
}
|
|
111
112
|
exports.LIMITE_MORTE_MS = 600000;
|
|
112
113
|
exports.INTERVALO_WATCH_MS = 1000;
|
|
114
|
+
exports.MAX_ESPERA_ERRO_WATCH_MS = 30000;
|
|
115
|
+
exports.MAX_FALHAS_WATCH = 12;
|
|
113
116
|
exports.BLOCO_WATCH_BYTES = codex_events_1.LIMITE_LINHA_CODEX + 65536;
|
|
114
117
|
function lerJson(file) {
|
|
115
118
|
if (!fs.existsSync(file))
|
|
@@ -291,8 +294,87 @@ function desfazerNaThreadRemovida(pasta, criadas) {
|
|
|
291
294
|
catch { /* já removida ou não vazia */ }
|
|
292
295
|
}
|
|
293
296
|
}
|
|
297
|
+
/** Diagnóstico fechado: mensagens de E/S e JSON podem conter caminhos e segredos. */
|
|
298
|
+
function diagnosticoWatcher(e) {
|
|
299
|
+
const erro = e;
|
|
300
|
+
const code = ['ENOENT', 'EAGAIN', 'EBUSY', 'EINTR', 'EACCES', 'EPERM', 'EIO', 'ENOSPC', 'SESSION_STATE_SPLIT']
|
|
301
|
+
.includes(erro?.code ?? '') ? erro.code : null;
|
|
302
|
+
const nome = e instanceof Error ? e.constructor.name : '';
|
|
303
|
+
const construtor = ['Error', 'SyntaxError', 'TypeError', 'RangeError', 'ReferenceError', 'URIError', 'EvalError', 'AggregateError']
|
|
304
|
+
.includes(nome) ? nome : 'desconhecido';
|
|
305
|
+
const base = { code, construtor };
|
|
306
|
+
const mensagem = erro?.message ?? '';
|
|
307
|
+
if (mensagem === 'prazo de observação esgotado')
|
|
308
|
+
return { ...base, categoria: 'observacao.prazo',
|
|
309
|
+
erro: 'nenhuma observação bem-sucedida dentro do orçamento de espera', transitorio: false };
|
|
310
|
+
// Só motivos literais do núcleo saem no diagnóstico: nenhum conteúdo de metadado.
|
|
311
|
+
const motivos = ['sessão do controller divergente', 'instância do controller divergente',
|
|
312
|
+
'vínculo thread/fase/prompt do controller divergente', 'cwd do controller divergente',
|
|
313
|
+
'identidade do controller divergente da registrada', 'identidade do runtime divergente da registrada',
|
|
314
|
+
'rollout do controller ausente', 'rollout do controller divergente do registrado',
|
|
315
|
+
'rollout do controller rotacionado sem vínculo provado com a fonte fixada',
|
|
316
|
+
'descritor consumidor de rollout divergente da fonte fixada'];
|
|
317
|
+
const metadado = /^runtime\.unavailable: metadado (state\.json|launch\.json|process-launch\.json) de controller (inválido|ausente)$/.test(mensagem);
|
|
318
|
+
const categoria = metadado || motivos.some(m => mensagem === `runtime.unavailable: ${m}`) ? mensagem
|
|
319
|
+
: mensagem === 'controlador registrado divergente do despacho' ? 'controller.registro-divergente'
|
|
320
|
+
: mensagem === 'despacho do controller sem cwd registrado' ? 'controller.cwd-ausente'
|
|
321
|
+
: mensagem.startsWith('runtime.unavailable:') ? 'runtime.unavailable: motivo não categorizado'
|
|
322
|
+
: code ? `io.${code}` : construtor;
|
|
323
|
+
if (code === 'SESSION_STATE_SPLIT')
|
|
324
|
+
return { ...base, categoria: 'estado.dividido',
|
|
325
|
+
erro: 'estado canônico temporariamente sem vínculo; auditoria será repetida', transitorio: true };
|
|
326
|
+
if (['ENOENT', 'EAGAIN', 'EBUSY', 'EINTR'].includes(erro?.code ?? ''))
|
|
327
|
+
return { ...base, categoria, erro: `leitura temporariamente indisponível (${erro.code})`, transitorio: true };
|
|
328
|
+
if (e instanceof SyntaxError || /^JSON invalido em /.test(erro?.message ?? '') ||
|
|
329
|
+
metadado)
|
|
330
|
+
return { ...base, categoria: metadado ? categoria : 'json.invalido', erro: 'metadado JSON indisponível ou incompleto', transitorio: true };
|
|
331
|
+
if (/^(ingestão ocupada|creation\.busy:)/.test(erro?.message ?? ''))
|
|
332
|
+
return { ...base, categoria: 'observacao.ocupada', erro: 'observação ocupada; nova tentativa agendada', transitorio: true };
|
|
333
|
+
return { ...base, categoria, erro: 'falha permanente na observação; confira vínculo e fonte do sensor', transitorio: false };
|
|
334
|
+
}
|
|
335
|
+
const errosRegistrados = new WeakSet();
|
|
336
|
+
function registrarErroWatcher(raiz, threadId, sessionId, e, serie) {
|
|
337
|
+
if (!serie && e && typeof e === 'object' && errosRegistrados.has(e))
|
|
338
|
+
return true;
|
|
339
|
+
const dados = diagnosticoWatcher(e);
|
|
340
|
+
if (dados.categoria === 'observacao.ocupada')
|
|
341
|
+
return true;
|
|
342
|
+
const registrado = (0, ledger_1.registrarSeExiste)((0, thread_1.dirThread)(raiz, threadId), threadId, 'session_watcher_error', {
|
|
343
|
+
sessionId, motivo: 'runtime.unavailable', ...dados, ...serie,
|
|
344
|
+
transitorio: dados.transitorio && (!serie?.encerramento || serie.encerramento === 'recuperado'), origem: 'sessions.watch',
|
|
345
|
+
});
|
|
346
|
+
if (registrado && e && typeof e === 'object')
|
|
347
|
+
errosRegistrados.add(e);
|
|
348
|
+
return registrado !== null;
|
|
349
|
+
}
|
|
350
|
+
/** Falhar ao diagnosticar não troca o erro observado nem interrompe o retry transitório. */
|
|
351
|
+
function tentarRegistrarErroWatcher(...args) {
|
|
352
|
+
try {
|
|
353
|
+
return registrarErroWatcher(...args);
|
|
354
|
+
}
|
|
355
|
+
catch {
|
|
356
|
+
return false;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
function observarSemDiagnostico(carregado, sessionId, opcoes) {
|
|
360
|
+
return observarSessaoResolvida(carregado, sessionId, (0, session_events_1.resolverSessao)(carregado.raiz, sessionId, opcoes.threadId), opcoes);
|
|
361
|
+
}
|
|
294
362
|
function observarSessao(carregado, sessionId, opcoes = {}) {
|
|
295
|
-
|
|
363
|
+
let threadId = opcoes.threadId;
|
|
364
|
+
try {
|
|
365
|
+
const resolvida = (0, session_events_1.resolverSessao)(carregado.raiz, sessionId, threadId);
|
|
366
|
+
// Recusa de entrada sem despacho não é falha de um watcher ancorado.
|
|
367
|
+
threadId = resolvida.sessao.origem !== 'adocao' && resolvida.sessao.despachadaEm
|
|
368
|
+
? resolvida.thread.id : undefined;
|
|
369
|
+
return observarSessaoResolvida(carregado, sessionId, resolvida, opcoes);
|
|
370
|
+
}
|
|
371
|
+
catch (e) {
|
|
372
|
+
if (threadId)
|
|
373
|
+
tentarRegistrarErroWatcher(carregado.raiz, threadId, sessionId, e);
|
|
374
|
+
throw e;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
function observarSessaoResolvida(carregado, sessionId, { thread, sessao: registrada }, opcoes) {
|
|
296
378
|
const sessao = (0, session_events_1.exigirSessaoDespachada)(registrada, sessionId);
|
|
297
379
|
if (sessao.runtime !== 'codex' && sessao.runtime !== 'claude-bg')
|
|
298
380
|
throw new Error('watch requer sessão Codex ou claude-bg registrada');
|
|
@@ -398,6 +480,27 @@ function observarSessao(carregado, sessionId, opcoes = {}) {
|
|
|
398
480
|
cursor.progressoEm = Math.max(cursor.progressoEm, leitura.quando);
|
|
399
481
|
}
|
|
400
482
|
}
|
|
483
|
+
// Commit MCP é produção do núcleo, mesmo quando app-server não avança o rollout.
|
|
484
|
+
// Só renova inatividade com ambos os processos vivos, dentro deste despacho e
|
|
485
|
+
// sem terminal/erro nativo. Não substitui terminal, close ou prova de conclusão.
|
|
486
|
+
let progressoMcpEventId;
|
|
487
|
+
if (snapshot?.estadoController === 'vivo' && snapshot.estadoRuntime === 'vivo' &&
|
|
488
|
+
['working', 'running'].includes(snapshot.estado) && !snapshot.fechamento &&
|
|
489
|
+
!snapshot.terminalNativo && !snapshot.erro && !snapshot.limitacao) {
|
|
490
|
+
for (const e of (0, session_watcher_claude_1.eventosDoIntervalo)(eventos, sessao)) {
|
|
491
|
+
const quando = Date.parse(e.ts);
|
|
492
|
+
if (e.tipo !== 'mcp_git_committed' || e.thread !== thread.id || e.origem !== 'mcp.git' ||
|
|
493
|
+
e.estadoAuditado !== true || typeof e.commit !== 'string' || !/^[a-f0-9]{40}$/.test(e.commit) ||
|
|
494
|
+
(e.sessionId !== undefined && e.sessionId !== sessionId) ||
|
|
495
|
+
(e.despachoEm !== undefined && e.despachoEm !== sessao.despachadaEm) ||
|
|
496
|
+
quando > now || quando <= cursor.progressoEm)
|
|
497
|
+
continue;
|
|
498
|
+
cresceu = true;
|
|
499
|
+
cursor.crescimentoEm = Math.max(cursor.crescimentoEm, quando);
|
|
500
|
+
cursor.progressoEm = quando;
|
|
501
|
+
progressoMcpEventId = e.eventId;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
401
504
|
// Erro de metadados posterior não obriga a reler o mesmo backlog no restart.
|
|
402
505
|
(0, codex_runner_1.gravarAtomico)(file, cursor);
|
|
403
506
|
const processo = registro?.processoPath ? lerJson(registro.processoPath) : null;
|
|
@@ -482,7 +585,8 @@ function observarSessao(carregado, sessionId, opcoes = {}) {
|
|
|
482
585
|
if (!atuais.some(e => e.sensorEventId === heartbeatId))
|
|
483
586
|
(0, ledger_1.registrar)(dir, thread.id, 'runtime_heartbeat', {
|
|
484
587
|
ts: new Date(cursor.crescimentoEm).toISOString(), fase: sessao.fase, sessionId, despachoEm: sessao.despachadaEm,
|
|
485
|
-
sensorEventId: heartbeatId, fonte: 'progresso reconhecido do stream/rollout',
|
|
588
|
+
sensorEventId: heartbeatId, fonte: progressoMcpEventId ? 'mcp_git_committed do despacho' : 'progresso reconhecido do stream/rollout',
|
|
589
|
+
...(progressoMcpEventId ? { mcpEventId: progressoMcpEventId } : {}), bytesLidos,
|
|
486
590
|
});
|
|
487
591
|
cursor.heartbeatEm = cursor.crescimentoEm;
|
|
488
592
|
(0, codex_runner_1.gravarAtomico)(file, cursor);
|
|
@@ -548,13 +652,6 @@ function observarSessao(carregado, sessionId, opcoes = {}) {
|
|
|
548
652
|
// Thread apagada no meio da observação: encerra sem erro, sem recriar a pasta e sem resíduo.
|
|
549
653
|
if (!(0, ledger_1.threadPresente)(dir))
|
|
550
654
|
return desfazer();
|
|
551
|
-
if (!/ocupad/.test(e.message) && !(0, ledger_1.registrarSeExiste)(dir, thread.id, 'session_watcher_error', {
|
|
552
|
-
fase: sessao.fase, sessionId, despachoEm: sessao.despachadaEm, motivo: 'runtime.unavailable',
|
|
553
|
-
// Não copiar conteúdo de metadados/logs em mensagens de JSON.parse.
|
|
554
|
-
erro: e instanceof SyntaxError ? 'metadado JSON inválido' : e.message,
|
|
555
|
-
origem: 'sessions.watch',
|
|
556
|
-
}))
|
|
557
|
-
return removida;
|
|
558
655
|
throw e;
|
|
559
656
|
}
|
|
560
657
|
}
|
|
@@ -612,7 +709,7 @@ function iniciarWatcher(raiz, sessionId) {
|
|
|
612
709
|
const err = fs.openSync(path.join(dir, `watcher-${sessionId}.stderr`), 'a', 0o600);
|
|
613
710
|
let child;
|
|
614
711
|
try {
|
|
615
|
-
child = (0, node_child_process_1.spawn)(process.execPath, [__filename, '--run', raiz, sessionId, readyPath, token], {
|
|
712
|
+
child = (0, node_child_process_1.spawn)(process.execPath, [__filename, '--run', raiz, sessionId, readyPath, token, thread.id], {
|
|
616
713
|
cwd: raiz, detached: true, stdio: ['ignore', 'ignore', err],
|
|
617
714
|
});
|
|
618
715
|
}
|
|
@@ -661,47 +758,102 @@ function iniciarWatcher(raiz, sessionId) {
|
|
|
661
758
|
}
|
|
662
759
|
async function acompanhar() {
|
|
663
760
|
const carregado = (0, manifest_1.exigirManifesto)(process.argv[3]), sessionId = process.argv[4];
|
|
664
|
-
const
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
761
|
+
const threadId = process.argv[7] ?? (0, session_events_1.resolverSessao)(carregado.raiz, sessionId).thread.id;
|
|
762
|
+
try {
|
|
763
|
+
const readyPath = process.argv[5], token = process.argv[6];
|
|
764
|
+
if (readyPath && token) {
|
|
765
|
+
validarFonteWatcher(carregado.raiz, sessionId);
|
|
766
|
+
const sessao = (0, session_events_1.exigirSessaoDespachada)((0, session_events_1.resolverSessao)(carregado.raiz, sessionId, threadId).sessao, sessionId);
|
|
767
|
+
const identidade = (0, codex_runner_1.identidadeProcesso)(process.pid);
|
|
768
|
+
if (!identidade)
|
|
769
|
+
throw new Error('watcher sem identidade propria');
|
|
770
|
+
(0, codex_runner_1.gravarAtomico)(readyPath, { token, sessionId, despachoEm: sessao.despachadaEm, identidade });
|
|
771
|
+
const deadline = Date.now() + 5000;
|
|
772
|
+
while (lerJson(readyPath + '.ack')?.token !== token) {
|
|
773
|
+
if (Date.now() >= deadline)
|
|
774
|
+
throw new Error('watcher sem confirmacao do pai');
|
|
775
|
+
await new Promise(r => setTimeout(r, 10));
|
|
776
|
+
}
|
|
678
777
|
}
|
|
778
|
+
await acompanharSessao(carregado, sessionId, { threadId });
|
|
679
779
|
}
|
|
680
|
-
|
|
780
|
+
catch (e) {
|
|
781
|
+
tentarRegistrarErroWatcher(carregado.raiz, threadId, sessionId, e);
|
|
782
|
+
throw e;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
/** Mesmo laço do processo destacado, com relógio e espera injetáveis para regressões. */
|
|
786
|
+
async function acompanharSessao(carregado, sessionId, opcoes) {
|
|
787
|
+
const dir = (0, thread_1.dirThread)(carregado.raiz, opcoes.threadId);
|
|
788
|
+
const esperar = opcoes.esperar ?? ((ms) => new Promise(r => setTimeout(r, ms)));
|
|
789
|
+
const agora = opcoes.agora ?? Date.now;
|
|
790
|
+
let falhas = 0, tentativas = 0, esperas = 0, ultimaObservacao = agora(), ultimoErro;
|
|
681
791
|
for (;;) {
|
|
682
792
|
// Projeto ou thread apagados encerram o laço em silêncio: nada a observar nem a recriar.
|
|
683
793
|
if (!(0, ledger_1.threadPresente)(dir))
|
|
684
794
|
return;
|
|
685
795
|
let atual;
|
|
686
796
|
try {
|
|
687
|
-
atual = (0, thread_1.lerThread)(carregado.raiz,
|
|
797
|
+
atual = (0, thread_1.lerThread)(carregado.raiz, opcoes.threadId);
|
|
688
798
|
if (atual.status !== 'aberta' || atual.sessoes.at(-1)?.sessionId !== sessionId)
|
|
689
799
|
return;
|
|
690
|
-
|
|
800
|
+
// O laço agrega sua série de erros; a API de observação avulsa diagnostica separadamente.
|
|
801
|
+
const r = observarSemDiagnostico(carregado, sessionId, { threadId: opcoes.threadId, agoraMs: opcoes.agora?.() });
|
|
802
|
+
if (r.ocupado)
|
|
803
|
+
throw new Error('ingestão ocupada; tente novamente');
|
|
804
|
+
if (!r.ocupado) {
|
|
805
|
+
if (falhas)
|
|
806
|
+
tentarRegistrarErroWatcher(carregado.raiz, opcoes.threadId, sessionId, ultimoErro, { falhasConsecutivas: falhas, etapa: 'final', encerramento: 'recuperado' });
|
|
807
|
+
falhas = 0;
|
|
808
|
+
tentativas = 0;
|
|
809
|
+
esperas = 0;
|
|
810
|
+
ultimaObservacao = agora();
|
|
811
|
+
}
|
|
691
812
|
if (r.concluido || r.encerrado)
|
|
692
813
|
return;
|
|
693
814
|
}
|
|
694
815
|
catch (e) {
|
|
695
816
|
if (!(0, ledger_1.threadPresente)(dir))
|
|
696
817
|
return;
|
|
697
|
-
|
|
698
|
-
|
|
818
|
+
esperas++;
|
|
819
|
+
const dados = diagnosticoWatcher(e), ocupado = dados.categoria === 'observacao.ocupada';
|
|
820
|
+
const expirou = agora() - ultimaObservacao >= exports.LIMITE_MORTE_MS;
|
|
821
|
+
// Contenção não é erro. Estado dividido exige nova auditoria; ambos consomem
|
|
822
|
+
// só tempo, sem antecipar os 600 s pelo teto das outras falhas.
|
|
823
|
+
if (!ocupado || expirou) {
|
|
824
|
+
if (ocupado)
|
|
825
|
+
e = new Error('prazo de observação esgotado');
|
|
826
|
+
falhas++;
|
|
827
|
+
if (!ocupado && dados.categoria !== 'estado.dividido')
|
|
828
|
+
tentativas++;
|
|
829
|
+
ultimoErro = e;
|
|
830
|
+
const encerramento = ocupado ? 'prazo' : !dados.transitorio ? 'permanente'
|
|
831
|
+
: expirou ? 'prazo' : tentativas >= exports.MAX_FALHAS_WATCH ? 'tentativas' : undefined;
|
|
832
|
+
if (falhas === 1 || encerramento)
|
|
833
|
+
tentarRegistrarErroWatcher(carregado.raiz, opcoes.threadId, sessionId, e, { falhasConsecutivas: falhas, etapa: encerramento ? 'final' : 'inicial', ...(encerramento ? { encerramento } : {}) });
|
|
834
|
+
if (encerramento)
|
|
835
|
+
throw e;
|
|
836
|
+
}
|
|
699
837
|
}
|
|
700
|
-
await
|
|
838
|
+
await esperar(esperas ? Math.min(exports.MAX_ESPERA_ERRO_WATCH_MS, exports.INTERVALO_WATCH_MS * 2 ** Math.min(esperas - 1, 5), Math.max(1, exports.LIMITE_MORTE_MS - (agora() - ultimaObservacao)))
|
|
839
|
+
: atual?.sessoes.at(-1)?.runtime === 'claude-bg' ? session_watcher_claude_1.INTERVALO_WATCH_CLAUDE_MS : exports.INTERVALO_WATCH_MS);
|
|
701
840
|
}
|
|
702
841
|
}
|
|
703
842
|
if (require.main === module && process.argv[2] === '--run')
|
|
704
|
-
acompanhar().catch(
|
|
705
|
-
|
|
843
|
+
acompanhar().catch(e => {
|
|
844
|
+
// O dispatcher fornece o vínculo antes do spawn: até falha ao carregar o manifesto
|
|
845
|
+
// precisa de diagnóstico. Sem ledger acessível, o stderr não promete um recibo inexistente.
|
|
846
|
+
let registroIndisponivel = !process.argv[7];
|
|
847
|
+
if (process.argv[7]) {
|
|
848
|
+
try {
|
|
849
|
+
registroIndisponivel = !registrarErroWatcher(process.argv[3], process.argv[7], process.argv[4], e);
|
|
850
|
+
}
|
|
851
|
+
catch {
|
|
852
|
+
registroIndisponivel = true;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
process.stderr.write(registroIndisponivel
|
|
856
|
+
? 'ork watcher: observação interrompida; não foi possível registrar session_watcher_error.\n'
|
|
857
|
+
: 'ork watcher: observação interrompida; confira session_watcher_error e estado canônico.\n');
|
|
706
858
|
process.exitCode = 1;
|
|
707
859
|
});
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.pidVivo = pidVivo;
|
|
37
|
+
exports.ehFantasma = ehFantasma;
|
|
38
|
+
exports.contasDeSessoes = contasDeSessoes;
|
|
39
|
+
exports.consultarContas = consultarContas;
|
|
40
|
+
exports.estadosNasContas = estadosNasContas;
|
|
41
|
+
exports.sessoesVivasPorPerfil = sessoesVivasPorPerfil;
|
|
42
|
+
/**
|
|
43
|
+
* RM-056 (D4, D5): as sessoes desta maquina em TODAS as contas que o `ork` conhece.
|
|
44
|
+
*
|
|
45
|
+
* O `claude agents` e o `~/.codex/sessions` so respondem pela conta do ambiente de quem consulta:
|
|
46
|
+
* com perfis (`ork accounts`), uma sessao despachada pelo perfil X so aparece consultando com o
|
|
47
|
+
* `CLAUDE_CONFIG_DIR`/`CODEX_HOME` de X. Aqui a consulta passa pela conta do processo e por cada
|
|
48
|
+
* perfil do store, sem repetir diretorio, e cada sessao volta com o id do perfil dela (nunca o
|
|
49
|
+
* diretorio nem nada de dentro dele).
|
|
50
|
+
*
|
|
51
|
+
* Fantasma (D5): sessao claude-bg com estado nao terminal sem pid, ou com pid sem processo. O
|
|
52
|
+
* `claude agents` guarda o registro, mas nao ha quem trabalhe: ela nao ocupa vaga, e
|
|
53
|
+
* `ork sessions limpar-fantasmas` solta o vinculo do `ork` sem tocar no runtime.
|
|
54
|
+
*/
|
|
55
|
+
const fs = __importStar(require("node:fs"));
|
|
56
|
+
const path = __importStar(require("node:path"));
|
|
57
|
+
const claude_bg_1 = require("./adapters/claude-bg");
|
|
58
|
+
const codex_1 = require("./adapters/codex");
|
|
59
|
+
const runtime_profiles_1 = require("./runtime-profiles");
|
|
60
|
+
const TERMINAIS = ['done', 'completed', 'exited', 'stopped', 'failed'];
|
|
61
|
+
const estadoDe = (s) => String(s.state ?? s.status ?? '').trim().toLowerCase();
|
|
62
|
+
/** O processo existe? `EPERM` e processo de outro usuario: existe. */
|
|
63
|
+
function pidVivo(pid) {
|
|
64
|
+
if (typeof pid !== 'number' || !Number.isSafeInteger(pid) || pid <= 0)
|
|
65
|
+
return false;
|
|
66
|
+
try {
|
|
67
|
+
process.kill(pid, 0);
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
catch (e) {
|
|
71
|
+
return e.code === 'EPERM';
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/** D5: fantasma e so claude-bg (o codex nao expoe pid na fonte), em estado nao terminal. */
|
|
75
|
+
function ehFantasma(runtime, s, vivo = pidVivo) {
|
|
76
|
+
return runtime === 'claude-bg' && !TERMINAIS.includes(estadoDe(s)) && !vivo(s.pid);
|
|
77
|
+
}
|
|
78
|
+
const real = (dir) => { try {
|
|
79
|
+
return fs.realpathSync(dir);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return path.resolve(dir);
|
|
83
|
+
} };
|
|
84
|
+
/**
|
|
85
|
+
* D4: as contas a consultar. Os perfis do store vem primeiro (inclusive desativado, que ainda pode
|
|
86
|
+
* ter sessao viva), para a sessao levar o id do perfil quando a conta do processo e a mesma pasta.
|
|
87
|
+
*/
|
|
88
|
+
function contasDeSessoes(raiz, env = process.env) {
|
|
89
|
+
const contas = [], fontes = [];
|
|
90
|
+
const vistos = new Set();
|
|
91
|
+
const incluir = (runtime, perfil, dir) => {
|
|
92
|
+
const chave = `${runtime}:${real(dir)}`;
|
|
93
|
+
if (vistos.has(chave))
|
|
94
|
+
return;
|
|
95
|
+
vistos.add(chave);
|
|
96
|
+
contas.push({ runtime, perfil, dir });
|
|
97
|
+
};
|
|
98
|
+
if (raiz) {
|
|
99
|
+
try {
|
|
100
|
+
for (const p of (0, runtime_profiles_1.lerPerfis)(raiz).perfis) {
|
|
101
|
+
const d = (0, runtime_profiles_1.perfilDeDespacho)(p);
|
|
102
|
+
incluir(d.runtime, d, (d.configDir ?? d.codexHome));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (e) {
|
|
106
|
+
fontes.push({ origem: 'perfis de conta (ork accounts)', ok: false, detalhe: e.message });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
for (const runtime of runtime_profiles_1.RUNTIMES_COM_PERFIL)
|
|
110
|
+
incluir(runtime, null, (0, runtime_profiles_1.diretorioImplicito)(runtime, env));
|
|
111
|
+
return { contas, fontes };
|
|
112
|
+
}
|
|
113
|
+
const rotulo = (c) => c.perfil ? `perfil ${c.perfil.id}` : 'conta do processo';
|
|
114
|
+
/**
|
|
115
|
+
* D4: as sessoes de todas as contas. Consulta que falha vira fonte `ok: false`, nunca "zero
|
|
116
|
+
* sessoes". `staleMin` e a janela de atividade que torna viva uma sessao codex (D3).
|
|
117
|
+
*/
|
|
118
|
+
function consultarContas(raiz, opcoes = {}) {
|
|
119
|
+
const base = opcoes.contas ? { contas: opcoes.contas, fontes: [] } : contasDeSessoes(raiz);
|
|
120
|
+
const fontes = [...base.fontes];
|
|
121
|
+
const sessoes = [];
|
|
122
|
+
const agoraMs = opcoes.agoraMs ?? Date.now();
|
|
123
|
+
const janelaMs = (opcoes.staleMin ?? 30) * 60_000;
|
|
124
|
+
for (const conta of base.contas) {
|
|
125
|
+
const perfil = conta.perfil?.id ?? null;
|
|
126
|
+
if (conta.runtime === 'claude-bg') {
|
|
127
|
+
let ambiente;
|
|
128
|
+
try {
|
|
129
|
+
ambiente = (0, claude_bg_1.ambienteDoPerfil)(conta.perfil);
|
|
130
|
+
}
|
|
131
|
+
catch (e) {
|
|
132
|
+
fontes.push({ origem: `claude agents (${rotulo(conta)})`, ok: false, detalhe: e.message });
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
const r = (0, claude_bg_1.consultarSessoes)(undefined, opcoes.todas, ambiente, opcoes.prazoMs);
|
|
136
|
+
const origem = `claude agents --json${opcoes.todas ? ' --all' : ''} (${rotulo(conta)})`;
|
|
137
|
+
// Fatia 2 do ensaio da 0.5.0 (P1): só o ENOENT antes da consulta prova a fonte ausente; qualquer
|
|
138
|
+
// outra falha do `claude` continua deixando o inventário incompleto.
|
|
139
|
+
fontes.push(r.ausente
|
|
140
|
+
? { origem, ok: true, ausente: true, detalhe: `${r.detalhe}: nenhuma sessão claude-bg a listar`,
|
|
141
|
+
correcao: 'para despachar e listar pelo claude-bg, instale o Claude Code e garanta `claude` no PATH' }
|
|
142
|
+
: { origem, ok: r.ok, detalhe: r.detalhe });
|
|
143
|
+
for (const s of r.sessoes) {
|
|
144
|
+
const fantasma = ehFantasma('claude-bg', s);
|
|
145
|
+
sessoes.push({ ...s, runtime: 'claude-bg', perfil, fantasma, viva: !fantasma && !TERMINAIS.includes(estadoDe(s)) });
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
const r = (0, codex_1.consultarRollouts)(opcoes.todas, conta.dir);
|
|
150
|
+
// A conta do processo segue com o caminho de antes; a do perfil vai pelo id, nunca pela pasta.
|
|
151
|
+
fontes.push(...(conta.perfil ? r.resultadosFontes.map(f => ({ ...f,
|
|
152
|
+
origem: `codex (${rotulo(conta)}): ${path.relative(conta.dir, f.origem)}` })) : r.resultadosFontes));
|
|
153
|
+
for (const s of r.sessoes) {
|
|
154
|
+
const recente = typeof s.atividadeEm === 'number' && agoraMs - s.atividadeEm <= janelaMs;
|
|
155
|
+
sessoes.push({ ...s, runtime: 'codex', perfil, fantasma: false, viva: estadoDe(s) !== 'completed' && recente });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return { sessoes, fontes, contas: base.contas };
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* D7: o mapa sessionId -> estado que o escalonador e o monitor usam, agora de todas as contas.
|
|
163
|
+
* Fantasma vira `fantasma` (nem `working` nem `blocked`: nao ocupa vaga nem pede o dono); codex
|
|
164
|
+
* vivo vira `working`. `null` ("runtime nao consultado") quando nenhuma conta claude-bg respondeu e
|
|
165
|
+
* nenhuma sessao codex apareceu: um mapa vazio ali diria "nada rodando" sem ter olhado.
|
|
166
|
+
*/
|
|
167
|
+
function estadosNasContas(raiz, opcoes = {}) {
|
|
168
|
+
const r = consultarContas(raiz, opcoes);
|
|
169
|
+
const claude = r.fontes.filter(f => f.origem.startsWith('claude agents'));
|
|
170
|
+
if (claude.length > 0 && !claude.some(f => f.ok) && !r.sessoes.some(s => s.runtime === 'codex'))
|
|
171
|
+
return null;
|
|
172
|
+
return new Map(r.sessoes.map(s => [s.sessionId,
|
|
173
|
+
s.fantasma ? 'fantasma' : s.runtime === 'codex' ? (s.viva ? 'working' : estadoDe(s)) : estadoDe(s)]));
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* RM-056 (D3): sessoes vivas de cada perfil do runtime nesta maquina, a carga do rodizio por
|
|
177
|
+
* carga. So as contas dos perfis sao consultadas (a do processo nao recebe despacho com perfis).
|
|
178
|
+
*/
|
|
179
|
+
function sessoesVivasPorPerfil(raiz, runtime, staleMin, agoraMs) {
|
|
180
|
+
const contas = contasDeSessoes(raiz).contas.filter(c => c.runtime === runtime && c.perfil);
|
|
181
|
+
const carga = new Map(contas.map(c => [c.perfil.id, 0]));
|
|
182
|
+
for (const s of consultarContas(raiz, { contas, staleMin, agoraMs }).sessoes) {
|
|
183
|
+
if (s.viva && s.perfil)
|
|
184
|
+
carga.set(s.perfil, (carga.get(s.perfil) ?? 0) + 1);
|
|
185
|
+
}
|
|
186
|
+
return carga;
|
|
187
|
+
}
|
|
@@ -38,9 +38,8 @@ exports.inventariarSessoes = inventariarSessoes;
|
|
|
38
38
|
const fs = __importStar(require("node:fs"));
|
|
39
39
|
const path = __importStar(require("node:path"));
|
|
40
40
|
const os = __importStar(require("node:os"));
|
|
41
|
-
const claude_bg_1 = require("./adapters/claude-bg");
|
|
42
|
-
const codex_1 = require("./adapters/codex");
|
|
43
41
|
const estado_thread_1 = require("./estado-thread");
|
|
42
|
+
const sessoes_contas_1 = require("./sessoes-contas");
|
|
44
43
|
const thread_1 = require("./thread");
|
|
45
44
|
/** Projeto canônico mais próximo de cada cwd declarado, sem varrer o HOME inteiro. */
|
|
46
45
|
function raizesDeRegistros(cwds) {
|
|
@@ -71,22 +70,10 @@ function raizesDeRegistros(cwds) {
|
|
|
71
70
|
}
|
|
72
71
|
function inventariarSessoes(raiz, opcoes = {}) {
|
|
73
72
|
const canonica = (0, estado_thread_1.raizDoEstado)(raiz);
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
// prova a fonte ausente; qualquer outra falha do `claude` continua deixando o inventário incompleto.
|
|
79
|
-
const fontes = [
|
|
80
|
-
claude.ausente
|
|
81
|
-
? { origem: origemClaude, ok: true, ausente: true, detalhe: `${claude.detalhe}: nenhuma sessão claude-bg a listar`,
|
|
82
|
-
correcao: 'para despachar e listar pelo claude-bg, instale o Claude Code e garanta `claude` no PATH' }
|
|
83
|
-
: { origem: origemClaude, ok: claude.ok, detalhe: claude.detalhe },
|
|
84
|
-
...codex.resultadosFontes,
|
|
85
|
-
];
|
|
86
|
-
const todas = [
|
|
87
|
-
...claude.sessoes.map(s => ({ ...s, runtime: 'claude-bg', vinculos: [] })),
|
|
88
|
-
...codex.sessoes.map(s => ({ ...s, runtime: 'codex', vinculos: [] })),
|
|
89
|
-
];
|
|
73
|
+
// RM-056 (D4): a conta do processo e cada perfil do store, sem repetir diretorio.
|
|
74
|
+
const contas = (0, sessoes_contas_1.consultarContas)(canonica, { todas: opcoes.todas });
|
|
75
|
+
const fontes = contas.fontes;
|
|
76
|
+
const todas = contas.sessoes.map(s => ({ ...s, vinculos: [] }));
|
|
90
77
|
// O cruzamento usa o universo global mesmo quando a apresentação é local.
|
|
91
78
|
try {
|
|
92
79
|
const projetos = raizesDeRegistros([canonica, ...todas.map(s => s.cwd)]);
|
|
@@ -161,5 +148,6 @@ function inventariarSessoes(raiz, opcoes = {}) {
|
|
|
161
148
|
escopo: { usuario: os.userInfo().username, global: !!opcoes.global, historico: !!opcoes.todas, raiz: canonica },
|
|
162
149
|
fontes, sessoes, total: sessoes.length,
|
|
163
150
|
semThread: sessoes.filter(s => s.vinculos.length === 0).length, ambiguas,
|
|
151
|
+
fantasmas: sessoes.filter(s => s.fantasma).length,
|
|
164
152
|
};
|
|
165
153
|
}
|
package/dist/sessoes.js
CHANGED
|
@@ -44,19 +44,29 @@ exports.tabelaDeSessoes = tabelaDeSessoes;
|
|
|
44
44
|
exports.logsDaSessao = logsDaSessao;
|
|
45
45
|
exports.pararSessao = pararSessao;
|
|
46
46
|
exports.comandoDeAttach = comandoDeAttach;
|
|
47
|
+
exports.limparFantasmas = limparFantasmas;
|
|
48
|
+
exports.sessoesSemFim = sessoesSemFim;
|
|
49
|
+
exports.textoDaLimpeza = textoDaLimpeza;
|
|
47
50
|
const adapter = __importStar(require("./adapters/claude-bg"));
|
|
48
51
|
const sessoes_inventario_1 = require("./sessoes-inventario");
|
|
49
52
|
const runtime_profiles_1 = require("./runtime-profiles");
|
|
50
53
|
const util_1 = require("./util");
|
|
54
|
+
const estado_thread_1 = require("./estado-thread");
|
|
55
|
+
const ledger_1 = require("./ledger");
|
|
56
|
+
const thread_1 = require("./thread");
|
|
51
57
|
function textoDoInventario(r) {
|
|
52
58
|
const linhas = [
|
|
53
59
|
`Inventário ${r.escopo.global ? 'global' : 'do projeto'} da conta ${r.escopo.usuario}; histórico: ${r.escopo.historico ? 'incluído' : 'omitido'}.`,
|
|
54
60
|
`Fontes: ${r.fontes.map(f => `${f.origem} (${f.ausente ? 'ausente' : f.ok ? 'ok' : 'FALHA'})`).join('; ')}`,
|
|
55
|
-
`Total: ${r.total}; sem thread: ${r.semThread}; ambíguas: ${r.ambiguas}; consulta: ${r.ok ? 'válida' : 'INCOMPLETA'}.`,
|
|
61
|
+
`Total: ${r.total}; sem thread: ${r.semThread}; ambíguas: ${r.ambiguas}; fantasmas: ${r.fantasmas}; consulta: ${r.ok ? 'válida' : 'INCOMPLETA'}.`,
|
|
56
62
|
];
|
|
63
|
+
// RM-056 (D4): a coluna PERFIL e o id do perfil (`processo` sem perfil), nunca o diretorio da conta.
|
|
57
64
|
if (r.sessoes.length)
|
|
58
|
-
linhas.push((0, util_1.tabela)(['ID', 'RUNTIME', 'ESTADO', 'THREAD/FASE'], r.sessoes.map(s => [s.sessionId, s.runtime, s.
|
|
65
|
+
linhas.push((0, util_1.tabela)(['ID', 'RUNTIME', 'PERFIL', 'ESTADO', 'THREAD/FASE'], r.sessoes.map(s => [s.sessionId, s.runtime, s.perfil ?? 'processo',
|
|
66
|
+
`${s.state ?? s.status ?? 'unknown'}${s.fantasma ? ' (fantasma)' : ''}`,
|
|
59
67
|
s.vinculos.map(v => `${v.thread}/${v.fase}`).join(', ') || '(fora do ork)'])));
|
|
68
|
+
if (r.fantasmas > 0)
|
|
69
|
+
linhas.push('Fantasma: registro sem processo vivo, não ocupa vaga; `ork sessions limpar-fantasmas` solta o vínculo do ork sem tocar no runtime.');
|
|
60
70
|
for (const f of r.fontes.filter(f => !f.ok))
|
|
61
71
|
linhas.push(`Falha: ${f.detalhe}`);
|
|
62
72
|
// Fatia 2 do ensaio da 0.5.0 (P1): a fonte ausente nao e falha, mas diz o que falta e como ter.
|
|
@@ -120,3 +130,74 @@ function comandoDeAttach(chave, raiz) {
|
|
|
120
130
|
const { sessao, perfil, configDir } = l.achada;
|
|
121
131
|
return { texto: adapter.comandoAttach(sessao.sessionId, perfil ? configDir : null), codigo: 0 };
|
|
122
132
|
}
|
|
133
|
+
const ENCERRAM_A_SESSAO = ['phase_result', 'sessao_morta', 'session_superseded'];
|
|
134
|
+
/**
|
|
135
|
+
* RM-056 (D6): solta o vinculo do `ork` com cada sessao fantasma (sem pid e sem processo) deste
|
|
136
|
+
* projeto, gravando `sessao_morta` no ledger da thread: e o evento que libera a conducao
|
|
137
|
+
* (`fimDaSessao`) e a vaga. Nunca chama o runtime: o registro no `claude agents` fica onde esta.
|
|
138
|
+
*/
|
|
139
|
+
function limparFantasmas(raiz, opcoes = {}) {
|
|
140
|
+
const canonica = (0, estado_thread_1.raizDoEstado)(raiz);
|
|
141
|
+
const inv = (0, sessoes_inventario_1.inventariarSessoes)(canonica, { global: true });
|
|
142
|
+
const itens = [];
|
|
143
|
+
for (const s of inv.sessoes.filter(x => x.fantasma)) {
|
|
144
|
+
// Fechamento (RM-056, ao fechar): so os vinculos da thread que fecha; o resto fica como estava.
|
|
145
|
+
const vinculos = opcoes.thread ? s.vinculos.filter(v => v.thread === opcoes.thread && v.raiz === canonica) : s.vinculos;
|
|
146
|
+
if (opcoes.thread && vinculos.length === 0)
|
|
147
|
+
continue;
|
|
148
|
+
if (vinculos.length === 0) {
|
|
149
|
+
itens.push({ sessionId: s.sessionId, perfil: s.perfil, thread: null, fase: null, acao: 'sem-thread' });
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
for (const v of vinculos) {
|
|
153
|
+
const base = { sessionId: s.sessionId, perfil: s.perfil, thread: v.thread, fase: v.fase };
|
|
154
|
+
if (v.raiz !== canonica) {
|
|
155
|
+
itens.push({ ...base, acao: 'outro-projeto' });
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
const dir = (0, thread_1.dirThread)(canonica, v.thread);
|
|
159
|
+
if ((0, ledger_1.lerLedger)(dir).some(e => ENCERRAM_A_SESSAO.includes(e.tipo) && e.sessionId === s.sessionId)) {
|
|
160
|
+
itens.push({ ...base, acao: 'ja-solto' });
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (!opcoes.dryRun)
|
|
164
|
+
(0, ledger_1.registrar)(dir, v.thread, 'sessao_morta', {
|
|
165
|
+
fase: v.fase, sessionId: s.sessionId, runtime: s.runtime, ...(s.perfil ? { perfilId: s.perfil } : {}),
|
|
166
|
+
origem: opcoes.origem ?? 'sessions.limpar-fantasmas', estadoNoRuntime: s.state ?? s.status ?? null,
|
|
167
|
+
evidencia: 'claude agents lista a sessao sem pid ou com pid sem processo (fantasma, RM-056 D5)',
|
|
168
|
+
razao: 'solta o vinculo do ork (conducao e vaga) sem tocar no runtime: claude stop e claude rm nao acham o job',
|
|
169
|
+
});
|
|
170
|
+
itens.push({ ...base, acao: 'solto' });
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return { ok: inv.ok, dryRun: opcoes.dryRun === true, itens, falhas: inv.fontes.filter(f => !f.ok).map(f => `${f.origem}: ${f.detalhe}`) };
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* RM-056 (ao fechar): as sessoes registradas na thread que o ledger dela ainda nao encerra. E o filtro
|
|
177
|
+
* barato do fechamento: so com alguma delas o `liberarAoFechar` consulta o inventario (o `claude agents`
|
|
178
|
+
* de cada conta). Sessao sem fim pode estar viva; quem decide se e fantasma e o inventario.
|
|
179
|
+
*/
|
|
180
|
+
function sessoesSemFim(raiz, threadId) {
|
|
181
|
+
const canonica = (0, estado_thread_1.raizDoEstado)(raiz);
|
|
182
|
+
const t = (0, thread_1.lerThread)(canonica, threadId);
|
|
183
|
+
const eventos = (0, ledger_1.lerLedger)((0, thread_1.dirThread)(canonica, threadId));
|
|
184
|
+
const encerradas = new Set(eventos.filter(e => ENCERRAM_A_SESSAO.includes(e.tipo)).map(e => String(e.sessionId ?? '')));
|
|
185
|
+
const ids = (Array.isArray(t.sessoes) ? t.sessoes : []).map(s => s?.sessionId).filter((id) => typeof id === 'string');
|
|
186
|
+
return [...new Set(ids)].filter(id => !encerradas.has(id));
|
|
187
|
+
}
|
|
188
|
+
function textoDaLimpeza(r) {
|
|
189
|
+
if (r.itens.length === 0)
|
|
190
|
+
return `Nenhuma sessão fantasma${r.ok ? '' : ' nas contas que responderam'}.` +
|
|
191
|
+
r.falhas.map(f => `\nFalha: ${f}`).join('');
|
|
192
|
+
const verbo = {
|
|
193
|
+
solto: r.dryRun ? 'soltaria (sessao_morta no ledger)' : 'solto: sessao_morta no ledger',
|
|
194
|
+
'ja-solto': 'já encerrada no ledger', 'sem-thread': 'fora do ork: nada a soltar',
|
|
195
|
+
'outro-projeto': 'vínculo de outro projeto: rode lá',
|
|
196
|
+
};
|
|
197
|
+
return [
|
|
198
|
+
`Sessões fantasma (sem pid e sem processo)${r.dryRun ? ', ensaio: nada foi gravado' : ''}. O runtime não foi tocado.`,
|
|
199
|
+
(0, util_1.tabela)(['ID', 'PERFIL', 'THREAD/FASE', 'AÇÃO'], r.itens.map(i => [i.sessionId, i.perfil ?? 'processo',
|
|
200
|
+
i.thread ? `${i.thread}/${i.fase}` : '-', verbo[i.acao]])),
|
|
201
|
+
...r.falhas.map(f => `Falha: ${f}`),
|
|
202
|
+
].join('\n');
|
|
203
|
+
}
|