@orkastery/cli 0.5.6 → 0.6.0
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/README.md +18 -0
- package/adapters/claude-code/.claude-plugin/plugin.json +1 -0
- package/adapters/claude-code/README.md +37 -5
- package/adapters/claude-code/agents/ork-check.md +1 -0
- package/adapters/claude-code/agents/ork-go.md +1 -0
- package/adapters/claude-code/agents/ork-goal.md +1 -0
- package/adapters/claude-code/agents/ork-master.md +1 -0
- package/adapters/claude-code/agents/ork-plan.md +1 -0
- package/adapters/claude-code/agents/ork-ship.md +1 -0
- package/adapters/claude-code/hooks/hooks.json +2 -1
- package/adapters/claude-code/hooks/ork-guard.js +15 -1
- package/adapters/claude-code/hooks/ork-resposta.js +91 -0
- package/adapters/claude-code/hooks/ork-sensor.js +18 -2
- package/adapters/claude-code/hooks/quota-signal.js +14 -0
- package/adapters/claude-code/mods/ork-mensageiro/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-mensageiro/hooks/caixa.ts +215 -0
- package/adapters/claude-code/mods/ork-mensageiro/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-mensageiro/hooks/painel.tsx +165 -0
- package/adapters/claude-code/mods/ork-mensageiro/hooks/recepcao.ts +256 -0
- package/adapters/claude-code/mods/ork-mensageiro/hooks/register.tsx +481 -0
- package/adapters/claude-code/mods/ork-mensageiro/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-mensageiro/types/index.d.ts +85 -0
- package/adapters/claude-code/mods/ork-pista/hooks/ci.ts +9 -5
- package/adapters/claude-code/mods/ork-ritual/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-ritual/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-ritual/hooks/nota.ts +88 -0
- package/adapters/claude-code/mods/ork-ritual/hooks/register.tsx +494 -0
- package/adapters/claude-code/mods/ork-ritual/hooks/replay.tsx +271 -0
- package/adapters/claude-code/mods/ork-ritual/hooks/ritual.ts +218 -0
- package/adapters/claude-code/mods/ork-ritual/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-ritual/types/index.d.ts +106 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/vigia.ts +1 -1
- package/adapters/codex/skills/ork/SKILL.md +2 -0
- package/adapters/hermes/README.md +64 -1
- package/adapters/hermes/bin/ork-conhecimento-colecoes.sh +14 -0
- package/adapters/hermes/bin/ork-conhecimento-listar.sh +15 -0
- package/adapters/hermes/bin/ork-conhecimento-mostrar.sh +16 -0
- package/adapters/hermes/bin/ork-conhecimento-verificar.sh +15 -0
- package/adapters/hermes/bin/ork-vigia-batida.py +127 -0
- package/adapters/hermes/hermes.plugin.json +4 -0
- package/adapters/hermes/hitl-ingress/__init__.py +101 -8
- package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +2 -2
- package/adapters/openclaw/README.md +81 -9
- package/adapters/openclaw/bin/ork-vigia-batida.js +86 -0
- package/adapters/openclaw/dist/hitl-ingress.js +106 -9
- package/adapters/openclaw/dist/index.js +86 -0
- package/adapters/openclaw/openclaw.plugin.json +34 -4
- package/adapters/openclaw/package.json +2 -2
- package/adapters/openclaw/src/hitl-ingress.ts +103 -9
- package/adapters/openclaw/src/index.ts +94 -0
- package/assets/adapters-en/claude-code/README.md +403 -0
- package/assets/adapters-en/claude-code/agents/ork-check.md +33 -0
- package/assets/adapters-en/claude-code/agents/ork-go.md +33 -0
- package/assets/adapters-en/claude-code/agents/ork-goal.md +34 -0
- package/assets/adapters-en/claude-code/agents/ork-master.md +35 -0
- package/assets/adapters-en/claude-code/agents/ork-plan.md +33 -0
- package/assets/adapters-en/claude-code/agents/ork-ship.md +33 -0
- package/assets/adapters-en/claude-code/commands/check.md +46 -0
- package/assets/adapters-en/claude-code/commands/go.md +46 -0
- package/assets/adapters-en/claude-code/commands/goal.md +55 -0
- package/assets/adapters-en/claude-code/commands/master.md +54 -0
- package/assets/adapters-en/claude-code/commands/onboarding.md +22 -0
- package/assets/adapters-en/claude-code/commands/ork.md +126 -0
- package/assets/adapters-en/claude-code/commands/plan.md +47 -0
- package/assets/adapters-en/claude-code/commands/ship.md +45 -0
- package/assets/adapters-en/codex/skills/ork/SKILL.md +85 -0
- package/assets/adapters-en/hermes/README.md +273 -0
- package/assets/adapters-en/hermes/skills/orkastery-devmaster/SKILL.md +169 -0
- package/assets/references-en/README.md +25 -0
- package/assets/references-en/code-review-axes.md +82 -0
- package/assets/references-en/definition-of-done.md +74 -0
- package/assets/references-en/performance-checklist.md +53 -0
- package/assets/references-en/security-checklist.md +101 -0
- package/assets/references-en/testing-patterns.md +51 -0
- package/assets/security-scan/defaults/.golangci.yml +2 -0
- package/assets/security-scan/defaults/eslint.config.mjs +11 -0
- package/assets/security-scan/defaults/gitleaks.toml +3 -0
- package/assets/security-scan/defaults/ruff.toml +1 -0
- package/assets/security-scan/tools-agil.json +87 -0
- package/assets/security-scan/tools.json +625 -0
- package/assets/skills-en/core/onboarding/SKILL.md +58 -0
- package/assets/skills-en/core/orkastery-bootstrap/SKILL.md +119 -0
- package/assets/skills-en/core/thread-state/SKILL.md +88 -0
- package/assets/skills-en/governance/decision-triage/SKILL.md +65 -0
- package/assets/skills-en/governance/narrative-guardian/SKILL.md +66 -0
- package/assets/skills-en/governance/roadmap-keeper/SKILL.md +60 -0
- package/assets/skills-en/governance/scope-check-capability-map/SKILL.md +71 -0
- package/assets/skills-en/observability/thread-tracing/SKILL.md +63 -0
- package/assets/skills-en/phases/check-quality/SKILL.md +103 -0
- package/assets/skills-en/phases/go-implementation/SKILL.md +78 -0
- package/assets/skills-en/phases/goal-definition/SKILL.md +69 -0
- package/assets/skills-en/phases/master-metrics/SKILL.md +72 -0
- package/assets/skills-en/phases/plan-specification/SKILL.md +67 -0
- package/assets/skills-en/phases/security-remediation/SKILL.md +70 -0
- package/assets/skills-en/phases/ship-release/SKILL.md +71 -0
- package/assets/skills-en/reviewers/code-reviewer/SKILL.md +61 -0
- package/assets/skills-en/reviewers/security-auditor/SKILL.md +93 -0
- package/assets/skills-en/reviewers/test-engineer/SKILL.md +66 -0
- package/assets/skills-en/reviewers/web-performance-auditor/SKILL.md +63 -0
- package/dist/adapters/claude-bg.js +118 -4
- package/dist/adapters/codex-controller-worker.js +12 -5
- package/dist/adapters/codex-events.js +16 -4
- package/dist/adapters/codex.js +3 -1
- package/dist/atualizar-branch.js +245 -0
- package/dist/auditoria.js +13 -11
- package/dist/auditrun.js +51 -46
- package/dist/autorizacao-duravel.js +327 -0
- package/dist/board.js +94 -40
- package/dist/branch-de-estado.js +16 -8
- package/dist/busca-semantica.js +7 -5
- package/dist/catalogo.js +2 -1
- package/dist/changelog-reconciliacao.js +290 -0
- package/dist/check-agil.js +500 -0
- package/dist/check-modo.js +41 -0
- package/dist/check-paralelo.js +183 -0
- package/dist/ci.js +248 -45
- package/dist/ciclos.js +8 -6
- package/dist/claim-lint.js +12 -8
- package/dist/claims-conferir.js +47 -39
- package/dist/claims.js +10 -9
- package/dist/company-brain-client.js +10 -3
- package/dist/company-brain-migration.js +11 -5
- package/dist/conducao-batida.js +759 -0
- package/dist/conducao-sentinela.js +25 -8
- package/dist/conducao.js +84 -29
- package/dist/conhecimento-consulta.js +270 -0
- package/dist/conhecimento.js +111 -87
- package/dist/conta-esgotada.js +127 -0
- package/dist/dado-privado.js +816 -0
- package/dist/decisao-autonoma.js +5 -4
- package/dist/demo.js +14 -12
- package/dist/dependencias-da-worktree.js +25 -0
- package/dist/divida.js +38 -36
- package/dist/docs.js +81 -74
- package/dist/doctor-forja.js +345 -0
- package/dist/doctor-pulse-cron.js +113 -1
- package/dist/doctor.js +164 -6
- package/dist/dsn-da-memoria.js +417 -0
- package/dist/entrega-por-mod.js +503 -0
- package/dist/entrega-pr.js +143 -43
- package/dist/esgotamentos.js +13 -11
- package/dist/estado-thread.js +21 -18
- package/dist/evalrunner.js +35 -32
- package/dist/fabrica-batida.js +34 -21
- package/dist/fabrica-estado.js +13 -10
- package/dist/fix.js +21 -19
- package/dist/forja/adaptador.js +6 -0
- package/dist/forja/ambiente-de-ci.js +27 -0
- package/dist/forja/api.js +256 -0
- package/dist/forja/cache.js +89 -0
- package/dist/forja/cli.js +60 -0
- package/dist/forja/contrato.js +9 -0
- package/dist/forja/endereco.js +43 -0
- package/dist/forja/estado.js +22 -0
- package/dist/forja/executor.js +73 -0
- package/dist/forja/github/ci.js +95 -0
- package/dist/forja/github/dono-da-rede.js +18 -0
- package/dist/forja/github/metadados.js +15 -0
- package/dist/forja/github/pedidos.js +200 -0
- package/dist/forja/gitlab/ci.js +141 -0
- package/dist/forja/gitlab/consultas.js +59 -0
- package/dist/forja/gitlab/dono-da-rede.js +43 -0
- package/dist/forja/gitlab/metadados.js +15 -0
- package/dist/forja/gitlab/pedidos-abertos.js +158 -0
- package/dist/forja/gitlab/pedidos.js +141 -0
- package/dist/forja/produtor.js +32 -0
- package/dist/forja/registro.js +31 -0
- package/dist/forja/resolver.js +121 -0
- package/dist/forja/texto.js +13 -0
- package/dist/forja/versao.js +71 -0
- package/dist/forja.js +123 -80
- package/dist/handoff.js +23 -13
- package/dist/hitl-contract.js +46 -43
- package/dist/hitl-curto.js +37 -25
- package/dist/hitl-gates.js +13 -11
- package/dist/hitl-local-receipt.js +51 -0
- package/dist/hitl-local.js +11 -2
- package/dist/hitl-lock.js +5 -11
- package/dist/hitl-public-receipt.js +11 -0
- package/dist/hitl-resumo.js +44 -1
- package/dist/hitl-sessions.js +62 -9
- package/dist/hitl-texto-livre.js +7 -4
- package/dist/hitl.js +34 -31
- package/dist/horario.js +16 -0
- package/dist/hosts.js +104 -134
- package/dist/index.js +848 -548
- package/dist/indice-vetorial.js +14 -12
- package/dist/init.js +11 -2
- package/dist/integracoes-locais.js +2 -0
- package/dist/intelligence-graph-cli.js +123 -69
- package/dist/intelligence-graph-query.js +37 -22
- package/dist/leases.js +19 -18
- package/dist/ledger-stats.js +17 -16
- package/dist/ledger.js +31 -1
- package/dist/licoes.js +12 -11
- package/dist/locale-accounts.js +220 -0
- package/dist/locale-adapter.js +291 -0
- package/dist/locale-ajuda.js +76 -4
- package/dist/locale-audit.js +199 -0
- package/dist/locale-claims.js +213 -0
- package/dist/locale-conducao.js +138 -0
- package/dist/locale-conhecimento.js +181 -0
- package/dist/locale-decisao.js +205 -0
- package/dist/locale-docs.js +230 -0
- package/dist/locale-estado.js +101 -0
- package/dist/locale-forja.js +290 -0
- package/dist/locale-gerados.js +5 -0
- package/dist/locale-grafo.js +129 -0
- package/dist/locale-master.js +83 -0
- package/dist/locale-network.js +524 -0
- package/dist/locale-operacao-restante.js +931 -0
- package/dist/locale-projetos.js +100 -0
- package/dist/locale-prompts.js +194 -0
- package/dist/locale-pulse.js +140 -2
- package/dist/locale-reservas.js +87 -0
- package/dist/locale-retry.js +145 -6
- package/dist/locale-security-scan.js +69 -0
- package/dist/locale-setup.js +192 -0
- package/dist/locale-ship.js +169 -66
- package/dist/locale-thread.js +149 -5
- package/dist/locale-verify.js +80 -4
- package/dist/locale-worktree.js +246 -0
- package/dist/locale.js +553 -9
- package/dist/maestro-runtime.js +22 -3
- package/dist/manifest.js +287 -11
- package/dist/maquina-ociosa.js +211 -0
- package/dist/maquina.js +10 -5
- package/dist/master-nota-local.js +200 -0
- package/dist/master-nota.js +10 -2
- package/dist/master.js +118 -41
- package/dist/mcp-conhecimento.js +52 -0
- package/dist/mcp-experiencia.js +26 -5
- package/dist/mcp-install.js +73 -8
- package/dist/mcp-noctum.js +101 -0
- package/dist/mcp-server.js +82 -14
- package/dist/mcp-ship-transporte.js +977 -0
- package/dist/mcp-ship.js +68 -24
- package/dist/mcp-transportes.js +17 -0
- package/dist/memoria.js +63 -58
- package/dist/modos-migracao.js +8 -6
- package/dist/modos.js +17 -18
- package/dist/network-roadmap.js +85 -49
- package/dist/noctum-aceite.js +789 -0
- package/dist/noctum-arco.js +540 -33
- package/dist/noctum-capacidade.js +3 -3
- package/dist/noctum-changelog.js +399 -0
- package/dist/noctum-cli.js +482 -0
- package/dist/noctum-coordenacao.js +1225 -0
- package/dist/noctum-entrega.js +1169 -0
- package/dist/noctum-envelope.js +252 -16
- package/dist/noctum-execucao.js +95 -23
- package/dist/noctum-fechamento.js +405 -0
- package/dist/noctum-fila.js +43 -9
- package/dist/noctum-guarda.js +256 -0
- package/dist/noctum-integracao.js +507 -0
- package/dist/noctum-maquinas.js +383 -0
- package/dist/noctum-medicao.js +450 -0
- package/dist/noctum-modo.js +362 -0
- package/dist/noctum-pergunta-canal.js +96 -0
- package/dist/noctum-pergunta.js +115 -0
- package/dist/noctum-provas.js +838 -0
- package/dist/noctum-reconciliacao.js +591 -0
- package/dist/noctum-registro.js +215 -0
- package/dist/noctum-relatorio.js +827 -0
- package/dist/noctum-resposta.js +207 -0
- package/dist/noctum-sessoes-em-voo.js +93 -0
- package/dist/noctum-supervisao.js +649 -0
- package/dist/noctum-terminal.js +459 -0
- package/dist/noctum-transporte.js +655 -0
- package/dist/noctum-vigia.js +639 -0
- package/dist/ocupacao.js +3 -1
- package/dist/onboarding.js +16 -5
- package/dist/orkmind.js +39 -23
- package/dist/orquestracao.js +31 -35
- package/dist/parado-no-condutor.js +146 -124
- package/dist/phase.js +53 -27
- package/dist/policies.js +33 -0
- package/dist/postura-local.js +4 -3
- package/dist/procedencia.js +9 -14
- package/dist/projeto-alvo.js +59 -20
- package/dist/prompts.js +136 -42
- package/dist/prova-minima.js +5 -6
- package/dist/pulse-cadencia.js +5 -4
- package/dist/pulse-consentimento.js +53 -1
- package/dist/pulse-delivery.js +143 -27
- package/dist/pulse-resposta.js +368 -4
- package/dist/pulse.js +136 -3
- package/dist/ratelimit.js +12 -4
- package/dist/recall.js +14 -13
- package/dist/rede-adesao.js +13 -8
- package/dist/rede-forja.js +108 -24
- package/dist/rede-status.js +63 -45
- package/dist/rede.js +86 -46
- package/dist/retomada-checkpoint.js +101 -0
- package/dist/retry.js +228 -73
- package/dist/roadmap-reservas.js +32 -32
- package/dist/runtime-ambiente.js +5 -0
- package/dist/runtime-context.js +117 -43
- package/dist/runtime-escada.js +423 -0
- package/dist/runtime-profiles.js +103 -46
- package/dist/saida-segura.js +3 -2
- package/dist/security-scan/agil.js +355 -0
- package/dist/security-scan/cli.js +76 -0
- package/dist/security-scan/compare.js +149 -0
- package/dist/security-scan/config.js +93 -0
- package/dist/security-scan/detect.js +97 -0
- package/dist/security-scan/executor.js +188 -0
- package/dist/security-scan/guard.js +180 -0
- package/dist/security-scan/imagem-de-lint.js +96 -0
- package/dist/security-scan/normalize/actionlint.js +12 -0
- package/dist/security-scan/normalize/eslint.js +12 -0
- package/dist/security-scan/normalize/gitleaks.js +13 -0
- package/dist/security-scan/normalize/golangci-lint.js +13 -0
- package/dist/security-scan/normalize/govulncheck.js +36 -0
- package/dist/security-scan/normalize/index.js +25 -0
- package/dist/security-scan/normalize/npm-audit.js +30 -0
- package/dist/security-scan/normalize/osv-scanner.js +32 -0
- package/dist/security-scan/normalize/oxlint.js +51 -0
- package/dist/security-scan/normalize/pip-audit.js +16 -0
- package/dist/security-scan/normalize/ruff.js +12 -0
- package/dist/security-scan/normalize/semgrep.js +20 -0
- package/dist/security-scan/normalize/trivy.js +33 -0
- package/dist/security-scan/normalize/util.js +204 -0
- package/dist/security-scan/normalize/zizmor.js +19 -0
- package/dist/security-scan/report.js +156 -0
- package/dist/security-scan/run.js +448 -0
- package/dist/security-scan/tools.js +62 -0
- package/dist/security-scan/trabalhador.js +74 -0
- package/dist/security-scan/types.js +15 -0
- package/dist/session-events.js +19 -2
- package/dist/session-watcher-claude.js +74 -19
- package/dist/session-watcher.js +156 -10
- package/dist/sessoes-contas.js +9 -0
- package/dist/sessoes-inventario.js +1 -1
- package/dist/sessoes.js +25 -23
- package/dist/setup.js +59 -58
- package/dist/ship-publicar.js +263 -92
- package/dist/ship.js +160 -17
- package/dist/superficie.js +14 -11
- package/dist/thread-close.js +9 -9
- package/dist/thread.js +121 -18
- package/dist/tokens.js +15 -11
- package/dist/trabalho-thread.js +297 -0
- package/dist/transporte-do-canal.js +189 -0
- package/dist/types.js +12 -1
- package/dist/util.js +5 -2
- package/dist/varredura-acoes.js +507 -0
- package/dist/verify.js +472 -29
- package/dist/worktree-local.js +12 -10
- package/dist/worktree.js +81 -85
- package/eval/casos/check-quality.json +77 -0
- package/eval/casos/security-auditor.json +148 -0
- package/eval/casos/security-remediation.json +161 -0
- package/eval/fixtures/security-scan/README.md +8 -0
- package/eval/fixtures/security-scan/go/.github/workflows/ci.yml +12 -0
- package/eval/fixtures/security-scan/go/Dockerfile +4 -0
- package/eval/fixtures/security-scan/go/README.md +4 -0
- package/eval/fixtures/security-scan/go/config.go +4 -0
- package/eval/fixtures/security-scan/go/go.mod +5 -0
- package/eval/fixtures/security-scan/go/go.sum +2 -0
- package/eval/fixtures/security-scan/go/main.go +31 -0
- package/eval/fixtures/security-scan/js/.github/workflows/ci.yml +12 -0
- package/eval/fixtures/security-scan/js/Dockerfile +3 -0
- package/eval/fixtures/security-scan/js/README.md +4 -0
- package/eval/fixtures/security-scan/js/package-lock.json +844 -0
- package/eval/fixtures/security-scan/js/package.json +10 -0
- package/eval/fixtures/security-scan/js/src/config.js +4 -0
- package/eval/fixtures/security-scan/js/src/server.js +17 -0
- package/eval/fixtures/security-scan/js/src/util.ts +4 -0
- package/eval/fixtures/security-scan/js/tsconfig.json +4 -0
- package/eval/fixtures/security-scan/python/.github/workflows/ci.yml +12 -0
- package/eval/fixtures/security-scan/python/Dockerfile +3 -0
- package/eval/fixtures/security-scan/python/README.md +4 -0
- package/eval/fixtures/security-scan/python/app/__init__.py +0 -0
- package/eval/fixtures/security-scan/python/app/config.py +2 -0
- package/eval/fixtures/security-scan/python/app/main.py +19 -0
- package/eval/fixtures/security-scan/python/requirements.txt +1 -0
- package/package.json +9 -4
- package/references/security-checklist.md +3 -3
- package/skills/README.md +1 -0
- package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
- package/skills/phases/check-quality/SKILL.md +16 -0
- package/skills/phases/go-implementation/SKILL.md +6 -0
- package/skills/phases/security-remediation/SKILL.md +70 -0
- package/skills/reviewers/security-auditor/SKILL.md +25 -0
|
@@ -0,0 +1,827 @@
|
|
|
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.ARQUIVO_DO_TRANSPORTE = exports.MAXIMO_DE_VERSOES = exports.JANELA_DE_ENTREGA_MS = exports.INTERVALO_ENTRE_TENTATIVAS_MS = exports.MAXIMO_DE_TENTATIVAS = exports.TETO_DA_PAGINA = exports.CONTRATO_RELATORIO = void 0;
|
|
37
|
+
exports.fatosDaSupervisao = fatosDaSupervisao;
|
|
38
|
+
exports.vencimentoDoRelatorio = vencimentoDoRelatorio;
|
|
39
|
+
exports.medidorDoRelatorio = medidorDoRelatorio;
|
|
40
|
+
exports.fatosDoRelatorio = fatosDoRelatorio;
|
|
41
|
+
exports.linhasDoRelatorio = linhasDoRelatorio;
|
|
42
|
+
exports.paginar = paginar;
|
|
43
|
+
exports.transporteDoCanal = transporteDoCanal;
|
|
44
|
+
exports.enviarPeloTransporte = enviarPeloTransporte;
|
|
45
|
+
exports.correcoesDoRelatorio = correcoesDoRelatorio;
|
|
46
|
+
exports.entregarRelatorioSeDevido = entregarRelatorioSeDevido;
|
|
47
|
+
exports.reenviarRelatorio = reenviarRelatorio;
|
|
48
|
+
exports.relatorioNoTerminal = relatorioNoTerminal;
|
|
49
|
+
exports.retratoDoRelatorio = retratoDoRelatorio;
|
|
50
|
+
/**
|
|
51
|
+
* RM-058, fatia N6 (D3, D6, D10 e D11 da ork-rm058n6): o relatório da manhã do arco.
|
|
52
|
+
*
|
|
53
|
+
* O formato é o da seção "Relatório da manhã" da página: sete grupos, cada fato tirado do núcleo (o log do arco, a fila,
|
|
54
|
+
* a medição, os ledgers das threads e o fechamento), nunca de texto de agente nem de número inventado:
|
|
55
|
+
*
|
|
56
|
+
* ✅ Entregue (só com a prova do push; commit local fica em curso), 🔄 Em curso (checkpoint, quem retoma, próximo passo e
|
|
57
|
+
* reserva ativa), ⏸️ Impedido (causa, alcance e tentativas; `#HITL` só no que depende do dono), 🧭 Decidi no envelope, 💰
|
|
58
|
+
* Custo e duração (consumo medido contra o teto, com lacuna e teto superior ditos), 📝 Correções ("foi informado" e "fato
|
|
59
|
+
* confirmado agora") e 👤 O que precisa de você (o lote do dono com as alternativas, e o próximo trabalho elegível).
|
|
60
|
+
*
|
|
61
|
+
* O texto é o mesmo nos três canais (terminal, Telegram e OpenClaw), em páginas de até 3.900 caracteres, cada uma com o
|
|
62
|
+
* arco, a versão e a página no cabeçalho: o envio tem o id do arco, e uma repetição se reconhece.
|
|
63
|
+
*
|
|
64
|
+
* Horário: vence no primeiro `HH:MM` do fuso da janela no instante do início ou depois (D10), pelo `instanteNoFuso`, que
|
|
65
|
+
* trata o horário de verão; o fechamento antecipado não antecipa nada. Entregas são tentadas até 24 h depois do vencimento.
|
|
66
|
+
*
|
|
67
|
+
* Versões: a 1 é composta no vencimento e não muda mais. Versão nova só quando um item que uma versão entregue deu como
|
|
68
|
+
* entregue deixa de valer (a entrega revertida na base): a correção vai explícita, sem apagar o rastro, até 3 versões.
|
|
69
|
+
*
|
|
70
|
+
* Envio (D11): o canal sai pelo transporte local `.orkastery/monitor/noctum-host.json` (fora do git, como o do pulse),
|
|
71
|
+
* uma entrada por canal com o executável e os argumentos com `{{mensagem}}`; a mensagem também vai pela entrada. Código 0
|
|
72
|
+
* é o recibo da página. A tentativa é gravada antes de chamar o transporte e o desfecho depois, sob a trava do registro do
|
|
73
|
+
* relatório. Falha explícita (código diferente de 0, executável ausente) se repete até 5 vezes, com 5 min entre elas;
|
|
74
|
+
* tentativa sem desfecho (prazo estourado, sinal, processo que morreu no meio) nunca se repete sozinha, porque a mensagem
|
|
75
|
+
* pode ter chegado: o dono força com `ork noctum relatorio <arco> --reenviar <canal>`. O terminal tem recibo só quando o
|
|
76
|
+
* relatório é mostrado num terminal interativo pelo `ork noctum relatorio`. Sem recibo, a lacuna aparece no retrato do arco
|
|
77
|
+
* (`retratoDoRelatorio`) e nas correções da versão seguinte.
|
|
78
|
+
*
|
|
79
|
+
* Interfaces para as fatias que correm em paralelo (D6): a supervisão da N4 entra por `supervisao`; desde a integração
|
|
80
|
+
* (T3 da ork-rm058integra), sem ela vale a leitura do log da supervisão do arco (`fatosDaSupervisao`), e o log que não se
|
|
81
|
+
* lê vira lacuna com o motivo. A entrega da N5 é lida dos fatos que o ledger da thread já guarda (`ship_done`,
|
|
82
|
+
* `rollback_done`).
|
|
83
|
+
*/
|
|
84
|
+
const fs = __importStar(require("node:fs"));
|
|
85
|
+
const path = __importStar(require("node:path"));
|
|
86
|
+
const node_crypto_1 = require("node:crypto");
|
|
87
|
+
const node_child_process_1 = require("node:child_process");
|
|
88
|
+
const estado_thread_1 = require("./estado-thread");
|
|
89
|
+
const hitl_1 = require("./hitl");
|
|
90
|
+
const horario_1 = require("./horario");
|
|
91
|
+
const ledger_1 = require("./ledger");
|
|
92
|
+
const noctum_arco_1 = require("./noctum-arco");
|
|
93
|
+
const noctum_envelope_1 = require("./noctum-envelope");
|
|
94
|
+
const noctum_fechamento_1 = require("./noctum-fechamento");
|
|
95
|
+
const noctum_fila_1 = require("./noctum-fila");
|
|
96
|
+
const noctum_medicao_1 = require("./noctum-medicao");
|
|
97
|
+
const noctum_registro_1 = require("./noctum-registro");
|
|
98
|
+
const noctum_supervisao_1 = require("./noctum-supervisao");
|
|
99
|
+
const procedencia_1 = require("./procedencia");
|
|
100
|
+
const thread_1 = require("./thread");
|
|
101
|
+
exports.CONTRATO_RELATORIO = 'ork.noctum-relatorio/v1';
|
|
102
|
+
/** O teto de uma mensagem do Telegram é 4.096; a página fica abaixo, com folga, como o digest do MASTER. */
|
|
103
|
+
exports.TETO_DA_PAGINA = 3900;
|
|
104
|
+
exports.MAXIMO_DE_TENTATIVAS = 5;
|
|
105
|
+
exports.INTERVALO_ENTRE_TENTATIVAS_MS = 5 * 60_000;
|
|
106
|
+
exports.JANELA_DE_ENTREGA_MS = 24 * 3600_000;
|
|
107
|
+
exports.MAXIMO_DE_VERSOES = 3;
|
|
108
|
+
const PRAZO_DO_TRANSPORTE_MS = 30_000;
|
|
109
|
+
/** O arquivo local dos transportes do relatório, ao lado do `pulse-host.json`. */
|
|
110
|
+
exports.ARQUIVO_DO_TRANSPORTE = 'noctum-host.json';
|
|
111
|
+
/** As contas fora, em palavras: o motivo tipado da supervisão vira o que aconteceu com a conta. */
|
|
112
|
+
const CONTA_FORA_EM_TEXTO = Object.freeze({ 'cota': 'cota esgotada', 'cota-sem-reset': 'cota esgotada, sem reset dito',
|
|
113
|
+
'limite-de-uso': 'limite de uso', 'auth': 'login perdido' });
|
|
114
|
+
/**
|
|
115
|
+
* RM-058, integração (T3, D7 da ork-rm058integra): os fatos da supervisão do arco, lidos do log dela no instante dado: os
|
|
116
|
+
* isolados com o motivo, as contas ainda fora, as retomadas autorizadas, as sessões paradas e a parada. Sem log, nada a
|
|
117
|
+
* dizer; o log que não se lê lança a recusa tipada (`RecusaDaSupervisao`), que quem chama diz como lacuna.
|
|
118
|
+
*/
|
|
119
|
+
function fatosDaSupervisao(raiz, arco, quando = new Date().toISOString()) {
|
|
120
|
+
const s = (0, noctum_supervisao_1.lerSupervisao)(raiz, arco);
|
|
121
|
+
return {
|
|
122
|
+
isolados: [...s.isolados.values()].map(i => ({ trabalho: i.trabalho, motivo: `${i.motivo}: ${i.detalhe}` })),
|
|
123
|
+
contasFora: [...s.contasFora.values()].filter(c => (0, noctum_supervisao_1.contaForaAgora)(s, c.conta, quando))
|
|
124
|
+
.map(c => ({ conta: c.conta, motivo: CONTA_FORA_EM_TEXTO[c.motivo] ?? c.motivo })),
|
|
125
|
+
retomadas: s.retomadas.map(r => `${r.trabalho} (thread ${r.thread}): retomada ${r.tentativa} na fase ${r.fase} (${r.motivo})`),
|
|
126
|
+
sessoesParadas: s.sessoesParadas.map(x => `sessão ${x.sessionId} (${x.trabalho}): ${x.confirmada ? 'parada confirmada' : 'parada não confirmada'} ` +
|
|
127
|
+
`(${x.motivo})`),
|
|
128
|
+
parada: s.parada ? `${s.parada.motivo}: ${s.parada.detalhe}` : null,
|
|
129
|
+
espera: s.espera,
|
|
130
|
+
recusaDoPasso: s.recusaDoPasso,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const sha = (s) => (0, node_crypto_1.createHash)('sha256').update(s).digest('hex');
|
|
134
|
+
/** O texto como frase: sem espaço sobrando e com um ponto só no fim. */
|
|
135
|
+
const frase = (s) => `${s.trim().replace(/[.\s]+$/u, '')}.`;
|
|
136
|
+
const MOTIVO_EM_TEXTO = Object.freeze({ revogado: 'revogação do dono', hora: 'hora final',
|
|
137
|
+
custo: 'teto do arco', 'fila-vazia': 'fila vazia', supervisao: 'parada da supervisão' });
|
|
138
|
+
/** Os dois fatos que desmentem uma entrega já informada; o resto (lacuna, thread ilegível) nunca vira correção. */
|
|
139
|
+
const CAUSA_REVERTIDA = 'entrega revertida na base';
|
|
140
|
+
const CHECKPOINT_SEM_PROVA = 'entrega registrada sem a prova do push no remoto';
|
|
141
|
+
/** Os bloqueios que valem para o arco inteiro nesta máquina, e não só para o trabalho. */
|
|
142
|
+
const BLOQUEIOS_DO_ARCO = ['reserva-sem-leitura'];
|
|
143
|
+
/** D10: o primeiro `HH:MM` do fuso da janela no instante do início ou depois. */
|
|
144
|
+
function vencimentoDoRelatorio(conteudo) {
|
|
145
|
+
const fuso = conteudo.janela.fuso;
|
|
146
|
+
const [hora, minuto] = conteudo.relatorio.horario.split(':').map(Number);
|
|
147
|
+
const inicio = Date.parse(conteudo.janela.inicio);
|
|
148
|
+
const p = (0, horario_1.partesLocais)(inicio, fuso);
|
|
149
|
+
let ms = (0, horario_1.instanteNoFuso)(Number(p.ano), Number(p.mes), Number(p.dia), hora, minuto, fuso);
|
|
150
|
+
if (!(ms >= inicio)) {
|
|
151
|
+
const seguinte = new Date(Date.UTC(Number(p.ano), Number(p.mes) - 1, Number(p.dia) + 1));
|
|
152
|
+
ms = (0, horario_1.instanteNoFuso)(seguinte.getUTCFullYear(), seguinte.getUTCMonth() + 1, seguinte.getUTCDate(), hora, minuto, fuso);
|
|
153
|
+
}
|
|
154
|
+
if (!Number.isFinite(ms))
|
|
155
|
+
throw new noctum_envelope_1.RecusaDoArco('noctum.envelope.invalido', 'horário do relatório sem instante no fuso da janela');
|
|
156
|
+
return new Date(ms).toISOString();
|
|
157
|
+
}
|
|
158
|
+
function eventosDe(raiz, thread, cache) {
|
|
159
|
+
if (!cache.has(thread)) {
|
|
160
|
+
let e;
|
|
161
|
+
try {
|
|
162
|
+
e = (0, ledger_1.lerLedger)((0, thread_1.dirThread)(raiz, thread));
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
e = null;
|
|
166
|
+
}
|
|
167
|
+
cache.set(thread, e);
|
|
168
|
+
}
|
|
169
|
+
return cache.get(thread);
|
|
170
|
+
}
|
|
171
|
+
/** A entrega provada: o último `ship_done` com o push verificado no remoto e sem `rollback_done` depois. */
|
|
172
|
+
function entregaDaThread(eventos) {
|
|
173
|
+
const ship = [...eventos].reverse().find(e => e.tipo === ledger_1.TIPOS_DE_EVENTO.shipConcluido) ?? null;
|
|
174
|
+
if (!ship)
|
|
175
|
+
return { ship: null, revertida: null, provada: false };
|
|
176
|
+
const revertida = eventos.find(e => e.tipo === ledger_1.TIPOS_DE_EVENTO.rollbackConcluido && e.ts >= ship.ts) ?? null;
|
|
177
|
+
return { ship, revertida, provada: ship.pushVerificado === true && !revertida };
|
|
178
|
+
}
|
|
179
|
+
function ultimaFaseConcluida(eventos) {
|
|
180
|
+
const r = [...eventos].reverse().find(e => e.tipo === 'phase_result' && e.classificacao === 'fase_concluida' && typeof e.fase === 'string');
|
|
181
|
+
return r ? { fase: String(r.fase), ts: r.ts } : null;
|
|
182
|
+
}
|
|
183
|
+
function tentativasDo(trabalho, estado) {
|
|
184
|
+
const ds = estado.despachos.filter(d => d.trabalho === trabalho);
|
|
185
|
+
if (!ds.length)
|
|
186
|
+
return 'nenhum despacho';
|
|
187
|
+
const recusados = ds.filter(d => d.desfecho === 'recusado');
|
|
188
|
+
const motivos = [...new Set(recusados.map(d => d.motivo).filter(Boolean))];
|
|
189
|
+
return `${ds.length} despacho(s)${recusados.length ? `, ${recusados.length} recusado(s) (${motivos.join(', ')})` : ''}`;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* A medição do relatório e do retrato: o relatório só mostra as unidades dos tetos, e com todos em `sessoes` nenhuma
|
|
193
|
+
* transcrição nem rollout precisa ser lido (as sessões vêm do log do arco).
|
|
194
|
+
*/
|
|
195
|
+
function medidorDoRelatorio(opcoes, conteudo) {
|
|
196
|
+
if (opcoes.medir)
|
|
197
|
+
return opcoes.medir;
|
|
198
|
+
if ((0, noctum_medicao_1.precisaMedir)(conteudo))
|
|
199
|
+
return (r, e, q) => (0, noctum_medicao_1.medirArco)(r, e, { quando: q });
|
|
200
|
+
return (r, e, q) => (0, noctum_medicao_1.medirArco)(r, e, { quando: q, fontes: { transcricoes: () => null, rollout: () => null } });
|
|
201
|
+
}
|
|
202
|
+
/** As linhas de custo: o teto do arco, de cada conta e de cada runtime, contra o consumo medido na unidade de cada um. */
|
|
203
|
+
function linhasDeCusto(conteudo, medicao) {
|
|
204
|
+
const o = conteudo.orcamento;
|
|
205
|
+
return [
|
|
206
|
+
{ escopo: 'do arco', unidade: o.arco.unidade, teto: o.arco.valor, consumo: (0, noctum_medicao_1.consumoDoEscopoNaUnidade)(medicao, 'arco', o.arco.unidade) },
|
|
207
|
+
...o.porConta.map(s => ({ escopo: `da conta ${s.conta}`, unidade: s.teto.unidade, teto: s.teto.valor,
|
|
208
|
+
consumo: (0, noctum_medicao_1.consumoDoEscopoNaUnidade)(medicao, { conta: s.conta }, s.teto.unidade) })),
|
|
209
|
+
...o.porRuntime.map(s => ({ escopo: `do runtime ${s.runtime}`, unidade: s.teto.unidade, teto: s.teto.valor,
|
|
210
|
+
consumo: (0, noctum_medicao_1.consumoDoEscopoNaUnidade)(medicao, { runtime: s.runtime }, s.teto.unidade) })),
|
|
211
|
+
];
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Os fatos do relatório no instante dado, relidos do disco. `null` sem envelope autorizado. Com `semCusto`, o custo fica
|
|
215
|
+
* vazio e nada é medido: quem compõe mede depois, só quando uma versão vai mesmo ser composta.
|
|
216
|
+
*/
|
|
217
|
+
function fatosDoRelatorio(carregado, arco, opcoes = {}, modo = {}) {
|
|
218
|
+
const quando = opcoes.quando ?? new Date().toISOString();
|
|
219
|
+
const { raiz } = carregado;
|
|
220
|
+
let estado;
|
|
221
|
+
try {
|
|
222
|
+
estado = (0, noctum_arco_1.lerArco)(raiz, arco, quando);
|
|
223
|
+
}
|
|
224
|
+
catch (e) {
|
|
225
|
+
if (e instanceof noctum_envelope_1.RecusaDoArco)
|
|
226
|
+
return null;
|
|
227
|
+
throw e;
|
|
228
|
+
}
|
|
229
|
+
if (!estado?.conteudo || !estado.autorizadoPor)
|
|
230
|
+
return null;
|
|
231
|
+
const conteudo = estado.conteudo;
|
|
232
|
+
const fuso = conteudo.janela.fuso;
|
|
233
|
+
const lacunas = [];
|
|
234
|
+
let fila = null;
|
|
235
|
+
try {
|
|
236
|
+
fila = (0, noctum_fechamento_1.trabalhosDoArco)((0, noctum_fila_1.fatosDaFila)(carregado, arco, { quando, maquina: opcoes.maquina, lerReservas: opcoes.lerReservas }));
|
|
237
|
+
}
|
|
238
|
+
catch (e) {
|
|
239
|
+
if (!(e instanceof noctum_envelope_1.RecusaDoArco))
|
|
240
|
+
throw e;
|
|
241
|
+
lacunas.push(`a fila do arco não pôde ser lida (${e.motivo})`);
|
|
242
|
+
}
|
|
243
|
+
if (fila)
|
|
244
|
+
lacunas.push(...fila.lacunas);
|
|
245
|
+
let fechamento = null;
|
|
246
|
+
try {
|
|
247
|
+
fechamento = (0, noctum_fechamento_1.lerFechamento)(raiz, arco);
|
|
248
|
+
}
|
|
249
|
+
catch (e) {
|
|
250
|
+
if (!(e instanceof noctum_registro_1.RecusaDoRegistro))
|
|
251
|
+
throw e;
|
|
252
|
+
lacunas.push(`o registro do fechamento não se lê (${e.motivo})`);
|
|
253
|
+
}
|
|
254
|
+
// RM-058, integração (T3): sem a interface, o log da supervisão do arco; o que não se lê vira lacuna com o motivo.
|
|
255
|
+
let supervisao = null;
|
|
256
|
+
try {
|
|
257
|
+
supervisao = (opcoes.supervisao ?? ((r, a) => fatosDaSupervisao(r, a, quando)))(raiz, arco);
|
|
258
|
+
}
|
|
259
|
+
catch (e) {
|
|
260
|
+
if (!(e instanceof noctum_supervisao_1.RecusaDaSupervisao))
|
|
261
|
+
throw e;
|
|
262
|
+
lacunas.push(`a supervisão do arco não se lê (${e.motivo})`);
|
|
263
|
+
}
|
|
264
|
+
if (opcoes.supervisao && !supervisao)
|
|
265
|
+
lacunas.push('a supervisão da N4 não respondeu');
|
|
266
|
+
const cache = new Map();
|
|
267
|
+
const fechado = estado.encerramento !== null || ['revogado', 'expirado', 'encerrado'].includes(estado.situacao);
|
|
268
|
+
const entregues = [], emCurso = [], impedidos = [];
|
|
269
|
+
const foraDoLote = [];
|
|
270
|
+
for (const t of fila?.trabalhos ?? []) {
|
|
271
|
+
const eventos = t.thread ? eventosDe(raiz, t.thread, cache) : null;
|
|
272
|
+
if (t.thread && eventos === null)
|
|
273
|
+
lacunas.push(`o ledger da thread ${t.thread} (${t.id}) não se lê`);
|
|
274
|
+
const pendente = estado.despachos.find(d => d.trabalho === t.id && d.desfecho === 'pendente');
|
|
275
|
+
const base = { trabalho: t.id, item: t.item, thread: t.thread };
|
|
276
|
+
const isolado = supervisao?.isolados.find(i => i.trabalho === t.id);
|
|
277
|
+
if (t.estado === 'entregue' && t.thread && eventos) {
|
|
278
|
+
const e = entregaDaThread(eventos);
|
|
279
|
+
if (e.provada && e.ship) {
|
|
280
|
+
const master = [...eventos].reverse().find(x => x.tipo === ledger_1.TIPOS_DE_EVENTO.masterConcluido);
|
|
281
|
+
entregues.push({ ...base, thread: t.thread, pr: typeof e.ship.pr === 'number' ? e.ship.pr : null,
|
|
282
|
+
merge: typeof e.ship.mergeSha === 'string' ? e.ship.mergeSha : null,
|
|
283
|
+
push: typeof e.ship.fonteDaProva === 'string' && e.ship.fonteDaProva ? e.ship.fonteDaProva : 'push verificado no remoto',
|
|
284
|
+
master: master && typeof master.score === 'number' ? `MASTER ${master.score}/5` : 'MASTER pendente' });
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
if (e.revertida) {
|
|
288
|
+
impedidos.push({ ...base, causa: CAUSA_REVERTIDA, alcance: 'trabalho', tentativas: tentativasDo(t.id, estado), hitl: null,
|
|
289
|
+
detalhe: `rollback_done às ${(0, horario_1.formatarHora)(e.revertida.ts, { fuso, agora: quando, locale: 'pt-BR' })}, depois do ship_done` });
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
emCurso.push({ ...base, estado: t.estado, checkpoint: CHECKPOINT_SEM_PROVA,
|
|
293
|
+
retomada: 'a entrega (N5), com a prova no SHA exato', proximo: 'provar o push e o merge', reserva: null });
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
if (isolado || t.estado === 'bloqueado' || t.estado === 'encerrado-sem-entrega') {
|
|
297
|
+
const doDono = t.motivo === 'decisao-no-lote' || t.motivo === 'dono';
|
|
298
|
+
const lote = doDono && t.thread ? estado.decisoes.find(d => d.thread === t.thread || d.dependentes.includes(t.thread))?.id ?? null : null;
|
|
299
|
+
// `#HITL` só com o id estável do lote; a pendência da própria thread vai ao grupo do dono sem ele.
|
|
300
|
+
if (doDono && !lote && t.thread)
|
|
301
|
+
foraDoLote.push({ trabalho: t.id, thread: t.thread, detalhe: t.detalhe });
|
|
302
|
+
impedidos.push({ ...base, causa: isolado ? 'isolado pela supervisão' : t.estado === 'encerrado-sem-entrega' ? 'fechou sem entrega' : String(t.motivo),
|
|
303
|
+
detalhe: isolado ? isolado.motivo : t.detalhe, alcance: !isolado && t.motivo && BLOQUEIOS_DO_ARCO.includes(t.motivo) ? 'arco' : 'trabalho',
|
|
304
|
+
tentativas: tentativasDo(t.id, estado), hitl: lote });
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
const concluida = eventos ? ultimaFaseConcluida(eventos) : null;
|
|
308
|
+
// No arco fechado, a sessão em curso recebeu o pedido de parada: "conduzida agora" seria o lease, não o trabalho.
|
|
309
|
+
const proximo = t.fase ? `fase ${t.fase}` : fechado && t.estado === 'em-curso'
|
|
310
|
+
? 'conferir a sessão que o fechamento mandou parar e retomar pela prova mais adiantada' : t.detalhe;
|
|
311
|
+
emCurso.push({ ...base, estado: t.estado,
|
|
312
|
+
checkpoint: t.estado === 'abrir' ? 'não começou' : concluida
|
|
313
|
+
? `fase ${concluida.fase} concluída às ${(0, horario_1.formatarHora)(concluida.ts, { fuso, agora: quando, locale: 'pt-BR' })}` : 'sem fase concluída ainda',
|
|
314
|
+
retomada: t.estado === 'aguardando-entrega' ? 'a entrega (N5, com a condutora)' : fechado ? 'um arco novo, com envelope vigente' : 'o passo do arco',
|
|
315
|
+
proximo, reserva: pendente ? `reserva ${pendente.seq} pendente` : null });
|
|
316
|
+
}
|
|
317
|
+
// O piso das decisões: a primeira reserva do arco ou a autorização vigente, a mais antiga. A emenda troca a
|
|
318
|
+
// autorização vigente, e as decisões da versão anterior continuam do envelope.
|
|
319
|
+
const autorizadoEm = [estado.autorizadoPor.recebidoEm, estado.despachos[0]?.reservadoEm]
|
|
320
|
+
.filter((x) => typeof x === 'string' && Number.isFinite(Date.parse(x)))
|
|
321
|
+
.reduce((a, b) => (Date.parse(b) < Date.parse(a) ? b : a));
|
|
322
|
+
const decisoes = [];
|
|
323
|
+
for (const thread of [...new Set((fila?.trabalhos ?? []).map(t => t.thread).filter((x) => !!x))]) {
|
|
324
|
+
for (const e of eventosDe(raiz, thread, cache) ?? []) {
|
|
325
|
+
const p = e.pedido;
|
|
326
|
+
if (e.tipo !== ledger_1.TIPOS_DE_EVENTO.decisaoAutonoma || p?.classe !== 'decidido' || e.ts < autorizadoEm)
|
|
327
|
+
continue;
|
|
328
|
+
decisoes.push({ thread, em: e.ts, decidido: String(p.decidido ?? ''), porque: String(p.porque ?? ''), comoMudar: String(p.comoMudar ?? ''),
|
|
329
|
+
evidencia: String(e.evidencia ?? '') });
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
decisoes.sort((a, b) => a.em.localeCompare(b.em));
|
|
333
|
+
const custo = modo.semCusto ? [] : linhasDeCusto(conteudo, medidorDoRelatorio(opcoes, conteudo)(raiz, estado, quando));
|
|
334
|
+
const candidato = fila?.candidatos[0];
|
|
335
|
+
return {
|
|
336
|
+
arco, quando, fuso, vencimentoEm: vencimentoDoRelatorio(conteudo), janela: { inicio: conteudo.janela.inicio, fim: conteudo.janela.fim },
|
|
337
|
+
situacao: estado.situacao, envelopeSha256: estado.envelopeSha256,
|
|
338
|
+
encerramento: estado.encerramento ? { motivo: estado.encerramento.motivo, detalhe: estado.encerramento.detalhe, em: estado.encerramento.em } : null,
|
|
339
|
+
entregues, emCurso, impedidos, decisoes, custo,
|
|
340
|
+
sessoesDespachadas: estado.despachos.filter(d => d.desfecho === 'concluido' && !d.idempotente).length,
|
|
341
|
+
reservasPendentes: estado.despachos.filter(d => d.desfecho === 'pendente').length,
|
|
342
|
+
paradas: (fechamento?.paradas ?? []).map(p => (0, noctum_fechamento_1.textoDaParada)(p, fechamento.conferencias)),
|
|
343
|
+
contasFora: (supervisao?.contasFora ?? []).map(c => `a conta ${c.conta} saiu do arco: ${c.motivo}`),
|
|
344
|
+
retomadas: supervisao?.retomadas ?? [], sessoesParadas: supervisao?.sessoesParadas ?? [], paradaDaSupervisao: supervisao?.parada ?? null,
|
|
345
|
+
esperaDoPasso: supervisao?.espera ?? null, recusaDoPasso: supervisao?.recusaDoPasso ?? null,
|
|
346
|
+
correcoes: [],
|
|
347
|
+
precisaDeVoce: estado.decisoes.map(d => ({ id: d.id, thread: d.thread, pergunta: d.pergunta, alternativas: d.alternativas })), foraDoLote,
|
|
348
|
+
proximo: candidato ? `${candidato.id} (${candidato.item}), ${candidato.estado === 'abrir' ? 'abre a thread pelo GOAL' : `fase ${candidato.fase}`}` +
|
|
349
|
+
(fechado ? ', num arco novo' : '') : null,
|
|
350
|
+
lacunas,
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
/** As linhas do relatório: os sete grupos, na ordem e com os ícones da página, iguais em todo canal. */
|
|
354
|
+
function linhasDoRelatorio(f, versao) {
|
|
355
|
+
const hora = (iso) => (0, horario_1.formatarHora)(iso, { fuso: f.fuso, agora: f.quando, locale: 'pt-BR' });
|
|
356
|
+
const ha = (iso) => (0, horario_1.duracaoRelativa)(Math.max(0, Math.floor((Date.parse(f.quando) - Date.parse(iso)) / 60_000)));
|
|
357
|
+
const ocupante = (o) => `${o.origem === 'runtime' ? 'sessão do runtime sem vínculo' :
|
|
358
|
+
o.origem === 'conducao' ? 'condução desta raiz' : 'despacho desta raiz'}${o.runtime ? ` no ${o.runtime}` : ''} ` +
|
|
359
|
+
`(${o.revisao ? 'revisão' : 'execução'})`;
|
|
360
|
+
const espera = f.esperaDoPasso;
|
|
361
|
+
const recusa = f.recusaDoPasso;
|
|
362
|
+
const textoDaEspera = espera ? `o passo do arco espera há ${ha(espera.desde)} por ${espera.motivo}; ocupação: ` +
|
|
363
|
+
(espera.ocupantes.length ? espera.ocupantes.map(ocupante).join(', ') : 'nenhum ocupante identificado') : null;
|
|
364
|
+
const textoDaRecusa = recusa ? `${recusa.trabalho}: o passo foi recusado há ${ha(recusa.desde)} por ${recusa.motivo} (${recusa.detalhe})` : null;
|
|
365
|
+
const nada = ['• nada'];
|
|
366
|
+
const l = [`Relatório da manhã do arco ${f.arco}, versão ${versao} (${(0, horario_1.formatarDataHora)(f.quando, { fuso: f.fuso, agora: f.quando, locale: 'pt-BR' })})`,
|
|
367
|
+
`Horários em ${f.fuso}.`];
|
|
368
|
+
if (f.encerramento)
|
|
369
|
+
l.push(`O arco fechou às ${hora(f.encerramento.em)} (${MOTIVO_EM_TEXTO[f.encerramento.motivo] ?? f.encerramento.motivo}): ${frase(f.encerramento.detalhe)}`);
|
|
370
|
+
else if (f.situacao === 'vigente')
|
|
371
|
+
l.push(`O arco segue até ${hora(f.janela.fim)}.`);
|
|
372
|
+
else
|
|
373
|
+
l.push(`O arco está ${f.situacao}.`);
|
|
374
|
+
if (f.lacunas.length)
|
|
375
|
+
l.push(`Não lido: ${f.lacunas.join('; ')}.`);
|
|
376
|
+
l.push('', '✅ Entregue', ...(f.entregues.length ? f.entregues.map(e => `• ${e.trabalho} (${e.item}): thread ${e.thread}` +
|
|
377
|
+
`${e.pr !== null ? `, PR #${e.pr}` : ''}${e.merge ? `, merge ${e.merge.slice(0, 8)}` : ''}, push provado (${e.push}), ${e.master}`) : nada));
|
|
378
|
+
const daSupervisao = [...f.retomadas.map(r => `• retomada autorizada pela supervisão: ${r}`),
|
|
379
|
+
...f.sessoesParadas.map(s => `• pela supervisão, ${s}`)];
|
|
380
|
+
l.push('', '🔄 Em curso', ...(f.emCurso.length || f.paradas.length || daSupervisao.length ? [...f.emCurso.map(e => `• ${e.trabalho} (${e.item}): ` +
|
|
381
|
+
`${e.thread ? `thread ${e.thread}, ` : ''}checkpoint: ${e.checkpoint}; retoma: ${e.retomada}; próximo passo: ${e.proximo}` +
|
|
382
|
+
`${e.reserva ? `; ${e.reserva}` : ''}`), ...f.paradas.map(p => `• no fechamento, ${p}`), ...daSupervisao] : nada));
|
|
383
|
+
// A parada da supervisão que ainda não fechou o arco (o fechamento a lê no tique seguinte) vai ao Impedido, com alcance do arco.
|
|
384
|
+
const parada = f.paradaDaSupervisao && f.encerramento?.motivo !== 'supervisao'
|
|
385
|
+
? [`• o arco: parado pela supervisão (${f.paradaDaSupervisao}); alcance: arco`] : [];
|
|
386
|
+
const linhaDaEspera = textoDaEspera ? [`• ${textoDaEspera}; alcance: arco`] : [];
|
|
387
|
+
const linhaDaRecusa = textoDaRecusa ? [`• ${textoDaRecusa}; alcance: trabalho`] : [];
|
|
388
|
+
l.push('', '⏸️ Impedido', ...(f.impedidos.length || parada.length || linhaDaEspera.length || linhaDaRecusa.length ? [...parada, ...linhaDaEspera, ...linhaDaRecusa,
|
|
389
|
+
...f.impedidos.map(i => `• ${i.trabalho} (${i.item}): ${i.causa} (${i.detalhe}); ` +
|
|
390
|
+
`alcance: ${i.alcance}; tentativas: ${i.tentativas}${i.hitl ? `; ${i.hitl} #HITL` : ''}`)] : nada));
|
|
391
|
+
const decisoes = f.decisoes.slice(0, 10);
|
|
392
|
+
l.push('', '🧭 Decidi no envelope', ...(decisoes.length ? [...decisoes.map(d => `• ${d.thread}: ${frase(d.decidido)} Porque: ${frase(d.porque)} ` +
|
|
393
|
+
`Como reverter: ${frase(d.comoMudar)} Evidência: ${frase(d.evidencia)}`), ...(f.decisoes.length > decisoes.length
|
|
394
|
+
? [`• e mais ${f.decisoes.length - decisoes.length}, no ledger de cada thread (ork decisao placar)`] : [])] : nada));
|
|
395
|
+
l.push('', '💰 Custo e duração', ...f.custo.map(c => `• Teto ${c.escopo}: ${(0, noctum_medicao_1.textoDoConsumo)(c.consumo)} de ${c.teto} ${c.unidade}`), `• Sessões abertas pelo arco: ${f.sessoesDespachadas}` +
|
|
396
|
+
(f.reservasPendentes ? `; reservas pendentes: ${f.reservasPendentes} (o runtime pode ter aberto a sessão, e o consumo dela fica desconhecido)` : ''), `• Janela de ${hora(f.janela.inicio)} a ${hora(f.janela.fim)}` +
|
|
397
|
+
(f.encerramento ? `; fechou às ${hora(f.encerramento.em)} (${MOTIVO_EM_TEXTO[f.encerramento.motivo] ?? f.encerramento.motivo})` : ''), ...f.contasFora.map(c => `• ${c}`), '• Assinatura não é uso ilimitado: o teto vale também para a conta de assinatura.');
|
|
398
|
+
l.push('', '📝 Correções', ...(f.correcoes.length ? f.correcoes.map(c => `• Foi informado: ${c.informado}. Fato confirmado agora: ` +
|
|
399
|
+
`${c.confirmado}. Fonte: ${c.fonte}. Impacto: ${c.impacto}.`) : nada));
|
|
400
|
+
const acaoDoDono = Object.freeze({
|
|
401
|
+
'capacidade.revisao': 'encerre uma sessão de execução que esteja trabalhando ou emende a capacidade do envelope',
|
|
402
|
+
'capacidade.maquina': 'encerre uma sessão que esteja trabalhando ou emende a capacidade do envelope',
|
|
403
|
+
'capacidade.conta': 'libere a conta ocupada ou emende seu limite no envelope',
|
|
404
|
+
'capacidade.runtime': 'libere o runtime ocupado ou emende seu limite no envelope',
|
|
405
|
+
'capacidade.projeto': 'libere uma condução do projeto ou ajuste a concorrência do projeto',
|
|
406
|
+
'capacidade.conta-nao-autorizada': 'corrija o login/perfil da conta ou emende as contas do envelope',
|
|
407
|
+
'capacidade.runtime-nao-autorizado': 'corrija o runtime da fase ou emende os runtimes do envelope',
|
|
408
|
+
'capacidade.fonte-indisponivel': 'restabeleça a leitura das sessões do runtime',
|
|
409
|
+
'fonte-indisponivel': 'restabeleça a fonte que o passo não conseguiu ler',
|
|
410
|
+
'vigia-ausente': 'restabeleça o tique do vigia do arco',
|
|
411
|
+
'sem-trava-do-kernel': 'instale ou restabeleça a trava flock exigida pelo núcleo',
|
|
412
|
+
'sem-autorizacao': 'autorize o envelope do arco pela pergunta pendente',
|
|
413
|
+
'noctum.coordenacao.exige-passo-da-maquina': 'restabeleça o passo da máquina que coordena este arco',
|
|
414
|
+
});
|
|
415
|
+
const esperaDoDono = espera && acaoDoDono[espera.motivo]
|
|
416
|
+
? [`• O passo não despacha por ${espera.motivo} há ${ha(espera.desde)}. Para destravar: ${acaoDoDono[espera.motivo]}.`] : [];
|
|
417
|
+
const acaoDaRecusaDoDono = (motivo) => {
|
|
418
|
+
if (motivo === 'noctum.origem.sem-prova') {
|
|
419
|
+
return 'configure no cron do condutor e do tique o ORK_RECEIPT_VERIFIERS com o material público gerado por publicHitlVerifiers; ' +
|
|
420
|
+
'nunca coloque a chave de ingresso no ambiente dos agentes';
|
|
421
|
+
}
|
|
422
|
+
if (['noctum.efeito.sem-autorizacao', 'noctum.efeito.hash-antigo', 'noctum.coordenacao.versao-nao-autorizada'].includes(motivo)) {
|
|
423
|
+
return 'responda à pergunta pendente para autorizar o envelope vigente';
|
|
424
|
+
}
|
|
425
|
+
if (motivo.startsWith('noctum.envelope.') || ['noctum.modo.sem-envelope', 'noctum.modo.lista-fechada',
|
|
426
|
+
'noctum.despacho.fora-do-envelope', 'noctum.coordenacao.fora-do-envelope'].includes(motivo)) {
|
|
427
|
+
return 'corrija o envelope, proponha a nova versão e autorize-a pela pergunta';
|
|
428
|
+
}
|
|
429
|
+
return null;
|
|
430
|
+
};
|
|
431
|
+
const acaoDaRecusa = recusa ? acaoDaRecusaDoDono(recusa.motivo) : null;
|
|
432
|
+
const recusaDoDono = recusa && acaoDaRecusa
|
|
433
|
+
? [`• ${recusa.trabalho}: o passo não despacha por ${recusa.motivo} há ${ha(recusa.desde)}. Motivo: ${frase(recusa.detalhe)} ` +
|
|
434
|
+
`Para destravar: ${acaoDaRecusa}.`] : [];
|
|
435
|
+
const doDono = [...f.precisaDeVoce.flatMap(p => [`• ${p.id} (${p.thread}): ${p.pergunta} #HITL`,
|
|
436
|
+
...p.alternativas.map(a => ` ${a.letra}) ${a.texto}: ${a.consequencia}${a.recomendada ? ` (Recomendação: ${a.porque})` : ''}`)]),
|
|
437
|
+
...f.foraDoLote.map(x => `• ${x.trabalho} (thread ${x.thread}) espera o dono fora do lote do arco: ${frase(x.detalhe)} A resposta vai pela pendência da própria thread.`),
|
|
438
|
+
...esperaDoDono, ...recusaDoDono];
|
|
439
|
+
l.push('', '👤 O que precisa de você', ...(doDono.length ? doDono : nada), ...(f.proximo ? [`• Próximo trabalho elegível: ${f.proximo}`] : []));
|
|
440
|
+
return l.map(x => (0, hitl_1.redigirSegredos)(x));
|
|
441
|
+
}
|
|
442
|
+
/** As páginas de até `TETO_DA_PAGINA` caracteres, cada uma com o arco, a versão e a página no cabeçalho. */
|
|
443
|
+
function paginar(arco, versao, linhas) {
|
|
444
|
+
const cabecalho = (i, n) => `Arco ${arco}, relatório v${versao}, página ${i}/${n}`;
|
|
445
|
+
const espaco = exports.TETO_DA_PAGINA - cabecalho(99, 99).length - 1;
|
|
446
|
+
const blocos = [[]];
|
|
447
|
+
let tamanho = 0;
|
|
448
|
+
for (const bruta of linhas) {
|
|
449
|
+
const linha = bruta.length > espaco ? `${bruta.slice(0, espaco - 1)}…` : bruta;
|
|
450
|
+
if (tamanho + linha.length + 1 > espaco && blocos.at(-1).length) {
|
|
451
|
+
blocos.push([]);
|
|
452
|
+
tamanho = 0;
|
|
453
|
+
}
|
|
454
|
+
blocos.at(-1).push(linha);
|
|
455
|
+
tamanho += linha.length + 1;
|
|
456
|
+
}
|
|
457
|
+
return blocos.map((b, i) => [cabecalho(i + 1, blocos.length), ...b].join('\n'));
|
|
458
|
+
}
|
|
459
|
+
/** O transporte do canal no arquivo local desta máquina, ou o motivo de não haver. */
|
|
460
|
+
function transporteDoCanal(raiz, canal) {
|
|
461
|
+
const arquivo = path.join((0, estado_thread_1.raizDoEstado)(raiz), '.orkastery', 'monitor', exports.ARQUIVO_DO_TRANSPORTE);
|
|
462
|
+
try {
|
|
463
|
+
(0, procedencia_1.exigirHostLocal)(raiz, arquivo);
|
|
464
|
+
}
|
|
465
|
+
catch (e) {
|
|
466
|
+
return { ausente: e.message.slice(0, 300) };
|
|
467
|
+
}
|
|
468
|
+
let bruto;
|
|
469
|
+
try {
|
|
470
|
+
bruto = JSON.parse(fs.readFileSync(arquivo, 'utf8'));
|
|
471
|
+
}
|
|
472
|
+
catch {
|
|
473
|
+
return { ausente: `transporte do canal ${canal} não configurado em .orkastery/monitor/${exports.ARQUIVO_DO_TRANSPORTE}` };
|
|
474
|
+
}
|
|
475
|
+
const t = bruto?.[canal];
|
|
476
|
+
if (!t || typeof t.executavel !== 'string' || !t.executavel || !Array.isArray(t.argumentos) ||
|
|
477
|
+
!t.argumentos.every(a => typeof a === 'string') || !t.argumentos.some(a => a.includes('{{mensagem}}'))) {
|
|
478
|
+
return { ausente: `transporte do canal ${canal} sem executável e argumentos com {{mensagem}} em .orkastery/monitor/${exports.ARQUIVO_DO_TRANSPORTE}` };
|
|
479
|
+
}
|
|
480
|
+
return { executavel: t.executavel, argumentos: t.argumentos };
|
|
481
|
+
}
|
|
482
|
+
/** Envia uma página pelo transporte: código 0 é recibo; prazo ou sinal é incerto; o resto é falha explícita. */
|
|
483
|
+
function enviarPeloTransporte(raiz, t, texto) {
|
|
484
|
+
// A mensagem entra como texto: `$&` e `$'` nela não são padrão de substituição (CHECK da RM-037, fatia 4).
|
|
485
|
+
const r = (0, node_child_process_1.spawnSync)(t.executavel, t.argumentos.map(a => a.replaceAll('{{mensagem}}', () => texto)), { cwd: (0, estado_thread_1.raizDoEstado)(raiz), encoding: 'utf8', input: texto, timeout: PRAZO_DO_TRANSPORTE_MS, maxBuffer: 1024 * 1024 });
|
|
486
|
+
const codigo = r.error?.code;
|
|
487
|
+
if (codigo === 'ENOENT' || codigo === 'EACCES')
|
|
488
|
+
return { estado: 'falhou', motivo: `o executável do transporte não roda (${codigo})` };
|
|
489
|
+
// O processo que nem subiu (EAGAIN, ENOMEM, E2BIG) não entregou nada: é falha explícita, que se repete, e não incerto.
|
|
490
|
+
if (r.error && !r.pid)
|
|
491
|
+
return { estado: 'falhou', motivo: `o transporte não subiu (${codigo ?? 'sem código'})` };
|
|
492
|
+
if (r.status === 0) {
|
|
493
|
+
let referencia = null;
|
|
494
|
+
try {
|
|
495
|
+
const j = JSON.parse(String(r.stdout ?? ''));
|
|
496
|
+
const id = j.message_id ?? j.messageId ?? j.id;
|
|
497
|
+
if ((typeof id === 'number' && Number.isFinite(id)) || (typeof id === 'string' && /^[\w:-]{1,80}$/.test(id)))
|
|
498
|
+
referencia = String(id);
|
|
499
|
+
}
|
|
500
|
+
catch {
|
|
501
|
+
referencia = null;
|
|
502
|
+
}
|
|
503
|
+
return { estado: 'confirmado', referencia };
|
|
504
|
+
}
|
|
505
|
+
if (r.status !== null && !r.signal)
|
|
506
|
+
return { estado: 'falhou', motivo: `o transporte saiu com o código ${r.status}` };
|
|
507
|
+
return { estado: 'incerto', motivo: `o transporte não terminou (${r.signal ?? codigo ?? 'sem código'}): a mensagem pode ter chegado` };
|
|
508
|
+
}
|
|
509
|
+
function estadoDaPagina(linhas, versao, pagina, canal) {
|
|
510
|
+
const st = { confirmada: false, referencia: null, incerta: false, falhas: 0, ultimaFalhaEm: null, tentativas: 0 };
|
|
511
|
+
let aberta = false;
|
|
512
|
+
for (const { evento: e, em } of linhas) {
|
|
513
|
+
if (!('pagina' in e) || e.versao !== versao || e.pagina !== pagina || e.canal !== canal)
|
|
514
|
+
continue;
|
|
515
|
+
if (e.tipo === 'envio_tentado') {
|
|
516
|
+
st.tentativas += 1;
|
|
517
|
+
aberta = true;
|
|
518
|
+
if (e.forcado)
|
|
519
|
+
st.incerta = false;
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
aberta = false;
|
|
523
|
+
if (e.tipo === 'envio_confirmado') {
|
|
524
|
+
st.confirmada = true;
|
|
525
|
+
st.referencia = e.referencia;
|
|
526
|
+
st.incerta = false;
|
|
527
|
+
}
|
|
528
|
+
else if (e.tipo === 'envio_falhou') {
|
|
529
|
+
st.falhas += 1;
|
|
530
|
+
st.ultimaFalhaEm = em;
|
|
531
|
+
}
|
|
532
|
+
else if (e.tipo === 'envio_incerto')
|
|
533
|
+
st.incerta = true;
|
|
534
|
+
}
|
|
535
|
+
// A tentativa sem desfecho, lida sob a trava do registro, é de um envio que morreu no meio: pode ter chegado.
|
|
536
|
+
if (aberta && !st.confirmada)
|
|
537
|
+
st.incerta = true;
|
|
538
|
+
return st;
|
|
539
|
+
}
|
|
540
|
+
const versoesDe = (linhas) => linhas.map(l => l.evento).filter((e) => e.tipo === 'composto');
|
|
541
|
+
/** A versão chegou ao dono: recibo de todas as páginas no canal, ou exibida num terminal interativo. */
|
|
542
|
+
function versaoEntregue(linhas, v) {
|
|
543
|
+
if (linhas.some(l => l.evento.tipo === 'exibido_no_terminal' && l.evento.versao === v.versao))
|
|
544
|
+
return true;
|
|
545
|
+
if (v.canal === 'terminal')
|
|
546
|
+
return false;
|
|
547
|
+
return v.paginas.every((_p, i) => estadoDaPagina(linhas, v.versao, i + 1, v.canal).confirmada);
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* As correções: o que uma versão entregue deu como entregue e os fatos de agora desmentem, e a versão anterior que ficou
|
|
551
|
+
* sem recibo. Cada correção diz o que foi informado, o fato confirmado agora, a fonte e o impacto, sem apagar o rastro.
|
|
552
|
+
*/
|
|
553
|
+
function correcoesDoRelatorio(linhas, fatos) {
|
|
554
|
+
const correcoes = [];
|
|
555
|
+
const hora = (iso) => (0, horario_1.formatarHora)(iso, { fuso: fatos.fuso, agora: fatos.quando, locale: 'pt-BR' });
|
|
556
|
+
const entregaDita = new Map();
|
|
557
|
+
for (const v of versoesDe(linhas)) {
|
|
558
|
+
const compostoEm = linhas.find(l => l.evento === v)?.em ?? fatos.quando;
|
|
559
|
+
if (!versaoEntregue(linhas, v)) {
|
|
560
|
+
correcoes.push({ trabalho: null, informado: `o relatório versão ${v.versao}, composto às ${hora(compostoEm)}`,
|
|
561
|
+
confirmado: `sem recibo de entrega no canal ${v.canal}`, fonte: 'o registro do relatório do arco',
|
|
562
|
+
impacto: 'o dono pode não ter recebido aquela versão; esta a substitui, e o terminal guarda todas' });
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
for (const e of v.informados.entregues)
|
|
566
|
+
if (!entregaDita.has(e.trabalho))
|
|
567
|
+
entregaDita.set(e.trabalho, { versao: v.versao, canal: v.canal, em: compostoEm });
|
|
568
|
+
}
|
|
569
|
+
for (const [trabalho, dita] of entregaDita) {
|
|
570
|
+
if (fatos.entregues.some(e => e.trabalho === trabalho))
|
|
571
|
+
continue;
|
|
572
|
+
// Só o fato que desmente a entrega vira correção: a entrega revertida, ou a que passou a não ter a prova do push.
|
|
573
|
+
// Lacuna (thread ilegível ou ambígua, ledger que não se lê, trabalho fora da lista) fica no cabeçalho do não lido.
|
|
574
|
+
const agora = fatos.impedidos.find(i => i.trabalho === trabalho && i.causa === CAUSA_REVERTIDA) ??
|
|
575
|
+
fatos.emCurso.find(i => i.trabalho === trabalho && i.checkpoint === CHECKPOINT_SEM_PROVA);
|
|
576
|
+
if (!agora)
|
|
577
|
+
continue;
|
|
578
|
+
const confirmado = 'causa' in agora ? `${agora.causa} (${agora.detalhe})` : `em curso: ${agora.checkpoint}`;
|
|
579
|
+
correcoes.push({ trabalho, informado: `${trabalho} entregue (versão ${dita.versao}, ${dita.canal}, ${hora(dita.em)})`, confirmado,
|
|
580
|
+
fonte: 'o ledger da thread do trabalho (ship_done e rollback_done)',
|
|
581
|
+
impacto: 'o trabalho não conta como entregue, e nada foi entregue de novo; ele volta para um arco novo' });
|
|
582
|
+
}
|
|
583
|
+
return correcoes;
|
|
584
|
+
}
|
|
585
|
+
function compor(fatos, versao, motivo, canal, corrigidos) {
|
|
586
|
+
const paginas = paginar(fatos.arco, versao, linhasDoRelatorio(fatos, versao));
|
|
587
|
+
return { tipo: 'composto', versao, motivo, vencimentoEm: fatos.vencimentoEm, canal, sha256: sha(paginas.join('\n')), paginas,
|
|
588
|
+
informados: { entregues: fatos.entregues.map(e => ({ trabalho: e.trabalho, thread: e.thread, merge: e.merge })), corrigidos } };
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Compõe e entrega o relatório quando ele vence, sob a trava do registro do relatório. Até o vencimento, espera; depois
|
|
592
|
+
* de 24 h, não tenta mais (a lacuna fica no retrato). Uma página com recibo nunca é enviada de novo, e a tentativa sem
|
|
593
|
+
* desfecho só se repete com `reenviar`, do dono.
|
|
594
|
+
*/
|
|
595
|
+
function entregarRelatorioSeDevido(carregado, arco, opcoes = {}) {
|
|
596
|
+
const quando = opcoes.quando ?? new Date().toISOString();
|
|
597
|
+
const { raiz } = carregado;
|
|
598
|
+
const sem = (acao, detalhe, o = {}) => ({ acao, vencimentoEm: null, versao: null, canal: null, enviadas: 0, detalhe, ...o });
|
|
599
|
+
let estado;
|
|
600
|
+
try {
|
|
601
|
+
estado = (0, noctum_arco_1.lerArco)(raiz, arco, quando);
|
|
602
|
+
}
|
|
603
|
+
catch (e) {
|
|
604
|
+
if (e instanceof noctum_envelope_1.RecusaDoArco)
|
|
605
|
+
return sem('sem-relatorio', e.message);
|
|
606
|
+
throw e;
|
|
607
|
+
}
|
|
608
|
+
if (!estado?.conteudo || !estado.autorizadoPor)
|
|
609
|
+
return sem('sem-relatorio', `o arco ${arco} não tem envelope autorizado`);
|
|
610
|
+
const conteudo = estado.conteudo;
|
|
611
|
+
const vencimentoEm = vencimentoDoRelatorio(conteudo), canal = conteudo.relatorio.canal;
|
|
612
|
+
if (Date.parse(quando) < Date.parse(vencimentoEm)) {
|
|
613
|
+
return sem('aguardar', `o relatório vence às ${(0, horario_1.formatarDataHora)(vencimentoEm, { fuso: conteudo.janela.fuso, agora: quando, locale: 'pt-BR' })}`, { vencimentoEm, canal });
|
|
614
|
+
}
|
|
615
|
+
if (Date.parse(quando) >= Date.parse(vencimentoEm) + exports.JANELA_DE_ENTREGA_MS) {
|
|
616
|
+
return sem('fora-da-janela', 'a janela de entrega de 24 h depois do vencimento acabou; o que ficou sem recibo segue no retrato do arco', { vencimentoEm, canal });
|
|
617
|
+
}
|
|
618
|
+
// Os fatos sem o custo: medir (transcrições e rollouts) só quando uma versão vai mesmo ser composta, nunca a cada tique.
|
|
619
|
+
const fatos = fatosDoRelatorio(carregado, arco, { ...opcoes, quando }, { semCusto: true });
|
|
620
|
+
if (!fatos)
|
|
621
|
+
return sem('sem-relatorio', `o arco ${arco} não tem envelope autorizado`);
|
|
622
|
+
const comCusto = (f) => ({ ...f, custo: linhasDeCusto(conteudo, medidorDoRelatorio(opcoes, conteudo)(raiz, estado, quando)) });
|
|
623
|
+
try {
|
|
624
|
+
return (0, noctum_registro_1.comRegistro)(raiz, arco, 'relatorio', (linhas, anexar) => {
|
|
625
|
+
const todas = [...linhas];
|
|
626
|
+
const gravar = (e) => { todas.push(anexar(e, quando)); };
|
|
627
|
+
const versoes = versoesDe(todas);
|
|
628
|
+
if (!versoes.length)
|
|
629
|
+
gravar(compor(comCusto(fatos), 1, 'horario', canal, []));
|
|
630
|
+
else {
|
|
631
|
+
const correcoes = correcoesDoRelatorio(todas, fatos);
|
|
632
|
+
const jaCorrigidos = new Set(versoes.flatMap(v => v.informados.corrigidos));
|
|
633
|
+
const novas = correcoes.filter(c => c.trabalho && !jaCorrigidos.has(c.trabalho));
|
|
634
|
+
if (novas.length && versoes.length < exports.MAXIMO_DE_VERSOES) {
|
|
635
|
+
gravar(compor({ ...comCusto(fatos), correcoes }, versoes.length + 1, 'correcao', canal, novas.map(c => c.trabalho)));
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
const atual = versoesDe(todas).at(-1);
|
|
639
|
+
// O canal é o da versão: a emenda que troca o canal compõe a versão seguinte nele, e a já composta segue no dela,
|
|
640
|
+
// com os recibos dela (nunca mandada de novo por outro canal).
|
|
641
|
+
const doCanal = atual.canal;
|
|
642
|
+
if (doCanal === 'terminal') {
|
|
643
|
+
return { acao: versaoEntregue(todas, atual) ? 'entregue' : 'pendente', vencimentoEm, versao: atual.versao, canal: doCanal, enviadas: 0,
|
|
644
|
+
detalhe: 'o relatório fica para consulta no terminal; o recibo vem quando o ork noctum relatorio o mostra num terminal interativo' };
|
|
645
|
+
}
|
|
646
|
+
const enviar = opcoes.enviar ?? ((c, texto) => {
|
|
647
|
+
const t = transporteDoCanal(raiz, c);
|
|
648
|
+
return 'ausente' in t ? { estado: 'falhou', motivo: `transporte.ausente: ${t.ausente}` } : enviarPeloTransporte(raiz, t, texto);
|
|
649
|
+
});
|
|
650
|
+
if (!opcoes.enviar) {
|
|
651
|
+
const t = transporteDoCanal(raiz, doCanal);
|
|
652
|
+
// Sem transporte, nada é tentado nem contado: a lacuna fica no retrato até o dono configurar o canal.
|
|
653
|
+
if ('ausente' in t)
|
|
654
|
+
return { acao: 'pendente', vencimentoEm, versao: atual.versao, canal: doCanal, enviadas: 0, detalhe: t.ausente };
|
|
655
|
+
}
|
|
656
|
+
let enviadas = 0;
|
|
657
|
+
for (const [i, texto] of atual.paginas.entries()) {
|
|
658
|
+
const pagina = i + 1;
|
|
659
|
+
const st = estadoDaPagina(todas, atual.versao, pagina, doCanal);
|
|
660
|
+
if (st.confirmada)
|
|
661
|
+
continue;
|
|
662
|
+
if (st.incerta || st.falhas >= exports.MAXIMO_DE_TENTATIVAS)
|
|
663
|
+
break;
|
|
664
|
+
if (st.ultimaFalhaEm && Date.parse(quando) - Date.parse(st.ultimaFalhaEm) < exports.INTERVALO_ENTRE_TENTATIVAS_MS)
|
|
665
|
+
break;
|
|
666
|
+
gravar({ tipo: 'envio_tentado', versao: atual.versao, pagina, canal: doCanal, tentativa: st.tentativas + 1, forcado: false });
|
|
667
|
+
let r;
|
|
668
|
+
try {
|
|
669
|
+
r = enviar(doCanal, texto);
|
|
670
|
+
}
|
|
671
|
+
catch (e) {
|
|
672
|
+
r = { estado: 'incerto', motivo: e.message.slice(0, 200) };
|
|
673
|
+
}
|
|
674
|
+
if (r.estado === 'confirmado') {
|
|
675
|
+
gravar({ tipo: 'envio_confirmado', versao: atual.versao, pagina, canal: doCanal, referencia: r.referencia });
|
|
676
|
+
enviadas += 1;
|
|
677
|
+
continue;
|
|
678
|
+
}
|
|
679
|
+
gravar(r.estado === 'falhou' ? { tipo: 'envio_falhou', versao: atual.versao, pagina, canal: doCanal, motivo: r.motivo.slice(0, 300) }
|
|
680
|
+
: { tipo: 'envio_incerto', versao: atual.versao, pagina, canal: doCanal, motivo: r.motivo.slice(0, 300) });
|
|
681
|
+
break;
|
|
682
|
+
}
|
|
683
|
+
const entregue = versaoEntregue(todas, atual);
|
|
684
|
+
const todasComRecibo = atual.paginas.every((_p, i) => estadoDaPagina(todas, atual.versao, i + 1, doCanal).confirmada);
|
|
685
|
+
return { acao: entregue ? 'entregue' : 'pendente', vencimentoEm, versao: atual.versao, canal: doCanal, enviadas,
|
|
686
|
+
detalhe: !entregue ? 'há página sem recibo; veja o retrato do arco'
|
|
687
|
+
: todasComRecibo ? `versão ${atual.versao} com recibo de todas as páginas no canal ${doCanal}`
|
|
688
|
+
: `versão ${atual.versao} mostrada num terminal interativo; o canal ${doCanal} ainda não tem recibo de todas as páginas` };
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
catch (e) {
|
|
692
|
+
if (e instanceof noctum_registro_1.RecusaDoRegistro && e.motivo === 'noctum.registro.ocupado') {
|
|
693
|
+
return sem('ocupado', 'outro processo entrega o relatório deste arco agora', { vencimentoEm, canal });
|
|
694
|
+
}
|
|
695
|
+
throw e;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* O reenvio que o dono pede para um canal: cada página da versão atual sem recibo vai uma vez, mesmo a incerta e a de
|
|
700
|
+
* reenvio esgotado. A página com recibo nunca vai de novo.
|
|
701
|
+
*/
|
|
702
|
+
function reenviarRelatorio(carregado, arco, canal, opcoes = {}) {
|
|
703
|
+
const quando = opcoes.quando ?? new Date().toISOString();
|
|
704
|
+
const { raiz } = carregado;
|
|
705
|
+
return (0, noctum_registro_1.comRegistro)(raiz, arco, 'relatorio', (linhas, anexar) => {
|
|
706
|
+
const todas = [...linhas];
|
|
707
|
+
const atual = versoesDe(todas).at(-1);
|
|
708
|
+
const base = { vencimentoEm: atual?.vencimentoEm ?? null, versao: atual?.versao ?? null, canal };
|
|
709
|
+
if (!atual)
|
|
710
|
+
return { acao: 'sem-relatorio', ...base, enviadas: 0, detalhe: 'nenhuma versão composta ainda' };
|
|
711
|
+
if (atual.canal !== canal)
|
|
712
|
+
return { acao: 'sem-relatorio', ...base, enviadas: 0, detalhe: `o canal do envelope é ${atual.canal}, não ${canal}` };
|
|
713
|
+
const enviar = opcoes.enviar ?? ((c, texto) => {
|
|
714
|
+
const t = transporteDoCanal(raiz, c);
|
|
715
|
+
return 'ausente' in t ? { estado: 'falhou', motivo: `transporte.ausente: ${t.ausente}` } : enviarPeloTransporte(raiz, t, texto);
|
|
716
|
+
});
|
|
717
|
+
let enviadas = 0;
|
|
718
|
+
for (const [i, texto] of atual.paginas.entries()) {
|
|
719
|
+
const pagina = i + 1;
|
|
720
|
+
const st = estadoDaPagina(todas, atual.versao, pagina, canal);
|
|
721
|
+
if (st.confirmada)
|
|
722
|
+
continue;
|
|
723
|
+
todas.push(anexar({ tipo: 'envio_tentado', versao: atual.versao, pagina, canal, tentativa: st.tentativas + 1, forcado: true }, quando));
|
|
724
|
+
let r;
|
|
725
|
+
try {
|
|
726
|
+
r = enviar(canal, texto);
|
|
727
|
+
}
|
|
728
|
+
catch (e) {
|
|
729
|
+
r = { estado: 'incerto', motivo: e.message.slice(0, 200) };
|
|
730
|
+
}
|
|
731
|
+
if (r.estado === 'confirmado') {
|
|
732
|
+
todas.push(anexar({ tipo: 'envio_confirmado', versao: atual.versao, pagina, canal, referencia: r.referencia }, quando));
|
|
733
|
+
enviadas += 1;
|
|
734
|
+
continue;
|
|
735
|
+
}
|
|
736
|
+
todas.push(anexar(r.estado === 'falhou' ? { tipo: 'envio_falhou', versao: atual.versao, pagina, canal, motivo: r.motivo.slice(0, 300) }
|
|
737
|
+
: { tipo: 'envio_incerto', versao: atual.versao, pagina, canal, motivo: r.motivo.slice(0, 300) }, quando));
|
|
738
|
+
break;
|
|
739
|
+
}
|
|
740
|
+
const entregue = versaoEntregue(todas, atual);
|
|
741
|
+
return { acao: entregue ? 'entregue' : 'pendente', ...base, enviadas,
|
|
742
|
+
detalhe: entregue ? `versão ${atual.versao} com recibo de todas as páginas` : 'o reenvio não confirmou todas as páginas' };
|
|
743
|
+
}, { esperarMs: 10_000 });
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* O relatório para o terminal: a versão atual, ou uma prévia dos fatos de agora quando nenhuma versão foi composta (a
|
|
747
|
+
* prévia não é registrada nem vale como entrega). Mostrado num terminal interativo, vira o recibo do canal `terminal`.
|
|
748
|
+
*/
|
|
749
|
+
function relatorioNoTerminal(carregado, arco, opcoes = {}) {
|
|
750
|
+
const quando = opcoes.quando ?? new Date().toISOString();
|
|
751
|
+
const { raiz } = carregado;
|
|
752
|
+
const atual = versoesDe((0, noctum_registro_1.lerRegistro)(raiz, arco, 'relatorio')).at(-1);
|
|
753
|
+
if (!atual) {
|
|
754
|
+
const fatos = fatosDoRelatorio(carregado, arco, { ...opcoes, quando });
|
|
755
|
+
if (!fatos)
|
|
756
|
+
return null;
|
|
757
|
+
return { versao: null, previa: true, paginas: paginar(arco, 0, ['Prévia: nenhuma versão foi composta ainda; isto não é a entrega.',
|
|
758
|
+
...linhasDoRelatorio(fatos, 0)]) };
|
|
759
|
+
}
|
|
760
|
+
if (opcoes.interativo) {
|
|
761
|
+
// Com a trava ocupada (o tique enviando), o dono vê as páginas assim mesmo; o recibo do terminal fica para a vez seguinte.
|
|
762
|
+
try {
|
|
763
|
+
(0, noctum_registro_1.comRegistro)(raiz, arco, 'relatorio', (linhas, anexar) => {
|
|
764
|
+
if (!linhas.some(l => l.evento.tipo === 'exibido_no_terminal' && l.evento.versao === atual.versao)) {
|
|
765
|
+
anexar({ tipo: 'exibido_no_terminal', versao: atual.versao }, quando);
|
|
766
|
+
}
|
|
767
|
+
}, { esperarMs: 10_000 });
|
|
768
|
+
}
|
|
769
|
+
catch (e) {
|
|
770
|
+
if (!(e instanceof noctum_registro_1.RecusaDoRegistro && e.motivo === 'noctum.registro.ocupado'))
|
|
771
|
+
throw e;
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
return { versao: atual.versao, previa: false, paginas: atual.paginas };
|
|
775
|
+
}
|
|
776
|
+
/** O retrato do relatório para o `ork noctum status`: as versões, os recibos e as lacunas, sem gravar nada. */
|
|
777
|
+
function retratoDoRelatorio(raiz, arco, conteudo, quando = new Date().toISOString()) {
|
|
778
|
+
let linhas;
|
|
779
|
+
try {
|
|
780
|
+
linhas = (0, noctum_registro_1.lerRegistro)(raiz, arco, 'relatorio');
|
|
781
|
+
}
|
|
782
|
+
catch (e) {
|
|
783
|
+
if (!(e instanceof noctum_registro_1.RecusaDoRegistro))
|
|
784
|
+
throw e;
|
|
785
|
+
return { vencimentoEm: null, canal: null, versoes: [], lacunas: [`o registro do relatório não se lê (${e.motivo})`] };
|
|
786
|
+
}
|
|
787
|
+
const vencimentoEm = conteudo ? vencimentoDoRelatorio(conteudo) : null;
|
|
788
|
+
const canal = conteudo?.relatorio.canal ?? null;
|
|
789
|
+
const fuso = conteudo?.janela.fuso;
|
|
790
|
+
const hora = (iso) => (0, horario_1.formatarDataHora)(iso, { fuso, agora: quando, locale: 'pt-BR' });
|
|
791
|
+
const versoes = versoesDe(linhas).map(v => ({ versao: v.versao, motivo: v.motivo, compostoEm: linhas.find(l => l.evento === v).em,
|
|
792
|
+
paginas: v.paginas.length, entregue: versaoEntregue(linhas, v),
|
|
793
|
+
recibos: v.canal === 'terminal' ? [] : v.paginas.map((_p, i) => ({ pagina: i + 1, st: estadoDaPagina(linhas, v.versao, i + 1, v.canal) }))
|
|
794
|
+
.filter(x => x.st.confirmada).map(x => ({ pagina: x.pagina, referencia: x.st.referencia })) }));
|
|
795
|
+
const lacunas = [];
|
|
796
|
+
const atual = versoesDe(linhas).at(-1);
|
|
797
|
+
if (!atual) {
|
|
798
|
+
if (vencimentoEm && Date.parse(quando) >= Date.parse(vencimentoEm))
|
|
799
|
+
lacunas.push(`o relatório venceu às ${hora(vencimentoEm)} e não foi composto`);
|
|
800
|
+
}
|
|
801
|
+
else if (atual.canal === 'terminal') {
|
|
802
|
+
if (!versaoEntregue(linhas, atual))
|
|
803
|
+
lacunas.push(`o relatório versão ${atual.versao} não foi mostrado num terminal interativo: rode ork noctum relatorio ${arco}`);
|
|
804
|
+
}
|
|
805
|
+
else {
|
|
806
|
+
const c = atual.canal;
|
|
807
|
+
for (const [i] of atual.paginas.entries()) {
|
|
808
|
+
const st = estadoDaPagina(linhas, atual.versao, i + 1, c);
|
|
809
|
+
if (st.confirmada)
|
|
810
|
+
continue;
|
|
811
|
+
const pagina = `página ${i + 1} da versão ${atual.versao} no canal ${c}`;
|
|
812
|
+
if (st.incerta)
|
|
813
|
+
lacunas.push(`envio incerto da ${pagina}: confira o canal e reenvie com ork noctum relatorio ${arco} --reenviar ${c}`);
|
|
814
|
+
else if (st.falhas >= exports.MAXIMO_DE_TENTATIVAS)
|
|
815
|
+
lacunas.push(`reenvio esgotado da ${pagina} (${st.falhas} tentativas): reenvie com ork noctum relatorio ${arco} --reenviar ${c}`);
|
|
816
|
+
else
|
|
817
|
+
lacunas.push(`sem recibo da ${pagina}${st.falhas ? ` (${st.falhas} tentativa(s) falharam)` : ''}`);
|
|
818
|
+
}
|
|
819
|
+
if (vencimentoEm && Date.parse(quando) >= Date.parse(vencimentoEm) + exports.JANELA_DE_ENTREGA_MS && !versaoEntregue(linhas, atual)) {
|
|
820
|
+
lacunas.push('a janela de entrega de 24 h acabou sem recibo de todas as páginas');
|
|
821
|
+
}
|
|
822
|
+
const t = transporteDoCanal(raiz, c);
|
|
823
|
+
if ('ausente' in t && !versaoEntregue(linhas, atual))
|
|
824
|
+
lacunas.push(t.ausente);
|
|
825
|
+
}
|
|
826
|
+
return { vencimentoEm, canal, versoes, lacunas };
|
|
827
|
+
}
|