@orkastery/cli 0.5.6 → 0.6.1
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 +123 -9
- 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/adapters/grok-events.js +227 -0
- package/dist/adapters/grok-mcp-servidor.js +612 -0
- package/dist/adapters/grok-mcp.js +178 -0
- package/dist/adapters/grok.js +686 -0
- package/dist/agendador-cpu.js +92 -0
- 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 +23 -10
- 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 +528 -0
- package/dist/check-modo.js +41 -0
- package/dist/check-paralelo.js +238 -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 +89 -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 +89 -89
- package/dist/doctor-forja.js +343 -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 +154 -54
- 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 +33 -21
- 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 +20 -1
- package/dist/hosts.js +104 -134
- package/dist/index.js +864 -554
- package/dist/indice-vetorial.js +14 -12
- package/dist/init.js +11 -2
- package/dist/integracoes-locais.js +4 -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 +228 -0
- package/dist/locale-adapter.js +291 -0
- package/dist/locale-ajuda.js +86 -10
- 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 +288 -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 +183 -6
- package/dist/locale-security-scan.js +69 -0
- package/dist/locale-setup.js +192 -0
- package/dist/locale-ship.js +177 -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 +293 -12
- 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 +90 -15
- package/dist/mcp-noctum.js +101 -0
- package/dist/mcp-server.js +116 -22
- 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/merge-da-entrega.js +85 -0
- package/dist/modos-migracao.js +8 -6
- package/dist/modos.js +17 -18
- package/dist/network-roadmap.js +94 -54
- package/dist/noctum-aceite.js +929 -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 +539 -0
- package/dist/noctum-coordenacao.js +1225 -0
- package/dist/noctum-entrega.js +1217 -0
- package/dist/noctum-envelope.js +253 -17
- package/dist/noctum-execucao.js +100 -24
- package/dist/noctum-fechamento.js +416 -0
- package/dist/noctum-fila.js +49 -10
- package/dist/noctum-guarda.js +333 -0
- package/dist/noctum-integracao.js +507 -0
- package/dist/noctum-maquinas.js +387 -0
- package/dist/noctum-medicao.js +450 -0
- package/dist/noctum-modo.js +390 -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 +594 -0
- package/dist/noctum-rede.js +102 -0
- package/dist/noctum-registro.js +215 -0
- package/dist/noctum-relatorio.js +861 -0
- package/dist/noctum-resposta.js +207 -0
- package/dist/noctum-retomada-rede.js +71 -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 +679 -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 +160 -139
- package/dist/phase.js +96 -42
- package/dist/policies.js +33 -0
- package/dist/postura-local.js +11 -6
- package/dist/preflight.js +2 -1
- 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 +13 -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 +326 -78
- package/dist/roadmap-reservas.js +32 -32
- package/dist/runtime-ambiente.js +5 -0
- package/dist/runtime-context.js +279 -85
- package/dist/runtime-escada.js +429 -0
- package/dist/runtime-profiles.js +130 -63
- package/dist/runtimes.js +42 -5
- package/dist/saida-segura.js +3 -2
- package/dist/security-scan/agil.js +366 -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 +196 -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-grok.js +425 -0
- package/dist/session-watcher.js +174 -13
- package/dist/sessoes-contas.js +42 -5
- package/dist/sessoes-inventario.js +1 -1
- package/dist/sessoes.js +25 -23
- package/dist/setup.js +67 -66
- 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 +500 -0
- package/dist/verify.js +491 -32
- 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 +10 -5
- 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
package/dist/noctum-arco.js
CHANGED
|
@@ -33,19 +33,27 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.PRAZO_PARA_AUTORIZAR_MIN = exports.TEXTO_DE_REVOGACAO = exports.textoDeAutorizacao = exports.enderecoDoArco = exports.ALVO_DO_NOCTUM = exports.CONTRATO_ARCO = void 0;
|
|
36
|
+
exports.MOTIVOS_DE_ENCERRAMENTO = exports.PRAZO_PARA_AUTORIZAR_MIN = exports.TEXTO_DE_REVOGACAO = exports.textoDeAutorizacao = exports.enderecoDoArco = exports.ALVO_DO_NOCTUM = exports.CONTRATO_ARCO = void 0;
|
|
37
37
|
exports.contextoDoArco = contextoDoArco;
|
|
38
38
|
exports.hashDaRevogacao = hashDaRevogacao;
|
|
39
39
|
exports.proporEnvelope = proporEnvelope;
|
|
40
|
+
exports.codigosDosArcos = codigosDosArcos;
|
|
41
|
+
exports.pedirRevogacao = pedirRevogacao;
|
|
42
|
+
exports.perguntasDoArco = perguntasDoArco;
|
|
40
43
|
exports.autorizarPeloIngresso = autorizarPeloIngresso;
|
|
41
44
|
exports.revogarPeloIngresso = revogarPeloIngresso;
|
|
45
|
+
exports.autorizarPelaResposta = autorizarPelaResposta;
|
|
46
|
+
exports.revogarPelaResposta = revogarPelaResposta;
|
|
42
47
|
exports.criarDialogoDoArco = criarDialogoDoArco;
|
|
43
48
|
exports.idDaDecisao = idDaDecisao;
|
|
44
49
|
exports.enfileirarDecisao = enfileirarDecisao;
|
|
45
50
|
exports.lerArco = lerArco;
|
|
51
|
+
exports.lerArcoComProposta = lerArcoComProposta;
|
|
46
52
|
exports.conferirAntesDoEfeito = conferirAntesDoEfeito;
|
|
47
53
|
exports.reservarDespacho = reservarDespacho;
|
|
54
|
+
exports.encerrarArco = encerrarArco;
|
|
48
55
|
exports.concluirDespacho = concluirDespacho;
|
|
56
|
+
exports.versoesAutorizadas = versoesAutorizadas;
|
|
49
57
|
/**
|
|
50
58
|
* RM-058, fatia N1: o estado do arco #Noctum e o ingresso do envelope.
|
|
51
59
|
*
|
|
@@ -82,6 +90,8 @@ const hitl_local_receipt_1 = require("./hitl-local-receipt");
|
|
|
82
90
|
const hitl_native_1 = require("./hitl-native");
|
|
83
91
|
const hitl_public_receipt_1 = require("./hitl-public-receipt");
|
|
84
92
|
const noctum_envelope_1 = require("./noctum-envelope");
|
|
93
|
+
const noctum_pergunta_1 = require("./noctum-pergunta");
|
|
94
|
+
const pulse_consentimento_1 = require("./pulse-consentimento");
|
|
85
95
|
const thread_1 = require("./thread");
|
|
86
96
|
exports.CONTRATO_ARCO = 'ork.noctum-arco/v1';
|
|
87
97
|
/**
|
|
@@ -100,6 +110,13 @@ exports.TEXTO_DE_REVOGACAO = 'revogar';
|
|
|
100
110
|
exports.PRAZO_PARA_AUTORIZAR_MIN = 60;
|
|
101
111
|
/** O diálogo do host espera no máximo isto, e nunca além do prazo da proposta. */
|
|
102
112
|
const ESPERA_DO_DIALOGO_MS = 300000;
|
|
113
|
+
/**
|
|
114
|
+
* RM-058 N6 (D2 e D8 da ork-rm058n6): por que o arco fechou. O fechamento é ato do núcleo, não do dono: ele só registra que
|
|
115
|
+
* a hora, o teto ou a fila acabaram, e que a revogação do dono já foi tratada. RM-058, integração (D7 da ork-rm058integra):
|
|
116
|
+
* `supervisao` é a parada da supervisão da N4 (o supervisor que falhou, o teto, a violação de custo ou o ambiente do
|
|
117
|
+
* despacho), que o fechamento lê do log dela depois do revogado e da hora e antes do custo.
|
|
118
|
+
*/
|
|
119
|
+
exports.MOTIVOS_DE_ENCERRAMENTO = ['revogado', 'hora', 'custo', 'fila-vazia', 'supervisao'];
|
|
103
120
|
const ZERO = '0'.repeat(64);
|
|
104
121
|
const ARQUIVO = 'arco.jsonl';
|
|
105
122
|
const sha = (s) => (0, node_crypto_1.createHash)('sha256').update(s).digest('hex');
|
|
@@ -266,11 +283,19 @@ async function comLockDoArcoAssincrono(raiz, arco, executar) {
|
|
|
266
283
|
}
|
|
267
284
|
}
|
|
268
285
|
}
|
|
286
|
+
/** A identidade de uma prova do dono: cada uma vale para um ato só. */
|
|
287
|
+
function chaveDaProva(i) {
|
|
288
|
+
if (i.origem === 'mcp-local')
|
|
289
|
+
return `mcp-local:${i.solicitacao}`;
|
|
290
|
+
if (i.origem === 'terminal')
|
|
291
|
+
return `terminal:${i.solicitacao}`;
|
|
292
|
+
return `${i.origem}:${i.mensagem}`;
|
|
293
|
+
}
|
|
269
294
|
function derivar(linhas) {
|
|
270
295
|
const d = { propostas: new Map(), ultima: null, autorizacao: null, revogacao: null, decisoes: [], usadas: new Set(),
|
|
271
|
-
despachos: new Map() };
|
|
296
|
+
despachos: new Map(), encerramento: null, revogacoesPedidas: [] };
|
|
272
297
|
const usar = (i) => {
|
|
273
|
-
const prova = i
|
|
298
|
+
const prova = chaveDaProva(i);
|
|
274
299
|
// Uma prova do dono vale para um ato só: reaproveitada noutra linha, o log foi emendado à mão.
|
|
275
300
|
if (d.usadas.has(prova))
|
|
276
301
|
throw adulterado('a mesma prova do dono aparece em dois atos do log');
|
|
@@ -294,8 +319,23 @@ function derivar(linhas) {
|
|
|
294
319
|
Object.assign(r, { desfecho: 'recusado', motivo: e.motivo, concluidoEm: em });
|
|
295
320
|
continue;
|
|
296
321
|
}
|
|
322
|
+
if (e.tipo === 'encerrado') {
|
|
323
|
+
// O fechamento é um só e vem depois da autorização: outra linha assim só existe num log emendado à mão.
|
|
324
|
+
if (!d.autorizacao || d.encerramento)
|
|
325
|
+
throw adulterado('encerramento sem autorização ou repetido');
|
|
326
|
+
d.encerramento = { motivo: e.motivo, detalhe: e.detalhe, em, versao: d.autorizacao.versao, envelopeSha256: d.autorizacao.envelopeSha256 };
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
if (e.tipo === 'revogacao_pedida') {
|
|
330
|
+
// Pergunta de revogação sem autorização antes dela só existe num log emendado à mão.
|
|
331
|
+
if (!d.autorizacao)
|
|
332
|
+
throw adulterado('pergunta de revogação sem autorização');
|
|
333
|
+
d.revogacoesPedidas.push({ codigo: e.codigo, prazo: e.prazo, em });
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
297
336
|
if (e.tipo === 'proposto') {
|
|
298
|
-
const p = { versao: e.versao, envelopeSha256: e.envelopeSha256, conteudo: e.conteudo, prazo: e.prazo
|
|
337
|
+
const p = { versao: e.versao, envelopeSha256: e.envelopeSha256, conteudo: e.conteudo, prazo: e.prazo, em,
|
|
338
|
+
...(typeof e.codigo === 'string' ? { codigo: e.codigo } : {}) };
|
|
299
339
|
if ((0, noctum_envelope_1.hashDoEnvelope)(p.conteudo) !== p.envelopeSha256)
|
|
300
340
|
throw adulterado(`proposta ${p.versao} com hash que não confere`);
|
|
301
341
|
d.propostas.set(p.versao, p);
|
|
@@ -304,6 +344,8 @@ function derivar(linhas) {
|
|
|
304
344
|
else if (e.tipo === 'autorizado') {
|
|
305
345
|
if (d.propostas.get(e.versao)?.envelopeSha256 !== e.envelopeSha256)
|
|
306
346
|
throw adulterado(`autorização da versão ${e.versao} sem proposta`);
|
|
347
|
+
if (d.encerramento)
|
|
348
|
+
throw adulterado(`autorização da versão ${e.versao} depois do encerramento`);
|
|
307
349
|
d.autorizacao = { versao: e.versao, envelopeSha256: e.envelopeSha256, ingresso: e.ingresso };
|
|
308
350
|
usar(e.ingresso);
|
|
309
351
|
}
|
|
@@ -321,6 +363,8 @@ function situacao(d, quando) {
|
|
|
321
363
|
return 'revogado';
|
|
322
364
|
if (!d.autorizacao)
|
|
323
365
|
return 'proposto';
|
|
366
|
+
if (d.encerramento)
|
|
367
|
+
return 'encerrado';
|
|
324
368
|
const janela = d.propostas.get(d.autorizacao.versao).conteudo.janela;
|
|
325
369
|
const agora = Date.parse(quando);
|
|
326
370
|
if (agora < Date.parse(janela.inicio))
|
|
@@ -329,13 +373,19 @@ function situacao(d, quando) {
|
|
|
329
373
|
}
|
|
330
374
|
function estadoDe(arco, d, quando) {
|
|
331
375
|
const vigente = d.autorizacao ? d.propostas.get(d.autorizacao.versao) : null;
|
|
332
|
-
const pendente = d.ultima && !d.revogacao && d.ultima.versao !== d.autorizacao?.versao &&
|
|
376
|
+
const pendente = d.ultima && !d.revogacao && !d.encerramento && d.ultima.versao !== d.autorizacao?.versao &&
|
|
333
377
|
Date.parse(quando) < Date.parse(d.ultima.prazo) ? d.ultima : null;
|
|
334
|
-
|
|
378
|
+
const sit = situacao(d, quando);
|
|
379
|
+
// RM-057 (resposta curta): só a última pergunta de revogação espera o dono, e só com o arco ainda de pé.
|
|
380
|
+
const rev = d.revogacoesPedidas.at(-1);
|
|
381
|
+
const revogacaoPedida = rev && (sit === 'vigente' || sit === 'agendado') && Date.parse(quando) < Date.parse(rev.prazo)
|
|
382
|
+
? { codigo: rev.codigo, prazo: rev.prazo, pedidaEm: rev.em } : null;
|
|
383
|
+
return { contrato: exports.CONTRATO_ARCO, arco, situacao: sit, versao: vigente?.versao ?? null,
|
|
335
384
|
envelopeSha256: vigente?.envelopeSha256 ?? null, conteudo: vigente?.conteudo ?? null,
|
|
336
385
|
autorizadoPor: d.autorizacao?.ingresso ?? null, revogadoPor: d.revogacao,
|
|
337
|
-
propostaPendente: pendente ? { versao: pendente.versao, envelopeSha256: pendente.envelopeSha256, prazo: pendente.prazo
|
|
338
|
-
|
|
386
|
+
propostaPendente: pendente ? { versao: pendente.versao, envelopeSha256: pendente.envelopeSha256, prazo: pendente.prazo,
|
|
387
|
+
...(pendente.codigo ? { codigo: pendente.codigo } : {}) } : null,
|
|
388
|
+
decisoes: d.decisoes, despachos: [...d.despachos.values()], consumo: consumoDe(d), encerramento: d.encerramento, revogacaoPedida };
|
|
339
389
|
}
|
|
340
390
|
/** D12 da ork-rm058n2: a reserva consome uma sessão até ser recusada; a concluída e a pendente contam. */
|
|
341
391
|
function consumoDe(d) {
|
|
@@ -355,7 +405,7 @@ function consumoDe(d) {
|
|
|
355
405
|
* A mesma proposta repetida devolve a que está pendente. Proposta nova sobre um envelope já
|
|
356
406
|
* autorizado é emenda: o vigente continua valendo até o dono autorizar a nova versão.
|
|
357
407
|
*/
|
|
358
|
-
function proporEnvelope(raiz, pedido, quando = new Date().toISOString()) {
|
|
408
|
+
function proporEnvelope(raiz, pedido, quando = new Date().toISOString(), emUso = []) {
|
|
359
409
|
const conteudo = (0, noctum_envelope_1.validarPedidoDeAbertura)(pedido);
|
|
360
410
|
const envelopeSha256 = (0, noctum_envelope_1.hashDoEnvelope)(conteudo);
|
|
361
411
|
if (!(Date.parse(quando) < Date.parse(conteudo.janela.fim))) {
|
|
@@ -365,6 +415,8 @@ function proporEnvelope(raiz, pedido, quando = new Date().toISOString()) {
|
|
|
365
415
|
const log = lerLog(raiz, conteudo.arco), d = derivar(log.linhas);
|
|
366
416
|
if (d.revogacao)
|
|
367
417
|
throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.revogado', 'arco revogado é terminal; abra outro arco');
|
|
418
|
+
if (d.encerramento)
|
|
419
|
+
throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.encerrado', `arco encerrado (${d.encerramento.motivo}) é terminal; abra outro arco`);
|
|
368
420
|
const resumo = (0, noctum_envelope_1.resumoDoEnvelope)(conteudo, envelopeSha256);
|
|
369
421
|
const pendente = estadoDe(conteudo.arco, d, quando).propostaPendente;
|
|
370
422
|
if (pendente?.envelopeSha256 === envelopeSha256)
|
|
@@ -376,9 +428,90 @@ function proporEnvelope(raiz, pedido, quando = new Date().toISOString()) {
|
|
|
376
428
|
exigirEmendaSemProrrogar(d, conteudo, quando);
|
|
377
429
|
const versao = (d.ultima?.versao ?? 0) + 1;
|
|
378
430
|
const prazo = new Date(Date.parse(quando) + exports.PRAZO_PARA_AUTORIZAR_MIN * 60000).toISOString();
|
|
379
|
-
|
|
380
|
-
|
|
431
|
+
// RM-057 (resposta curta, D2): a pergunta nasce com a proposta, com um código que nenhuma outra pergunta usa agora.
|
|
432
|
+
const codigo = (0, pulse_consentimento_1.gerarCodigo)([...emUso, ...codigosDosArcos(raiz)]);
|
|
433
|
+
anexar(raiz, conteudo.arco, log, { tipo: 'proposto', versao, envelopeSha256, conteudo, prazo, codigo }, quando);
|
|
434
|
+
return { arco: conteudo.arco, versao, envelopeSha256, prazo, resumo, codigo };
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* RM-057 (resposta curta): os códigos das perguntas de todos os arcos desta raiz, para um código novo nunca repetir um
|
|
439
|
+
* deles. Arco que não se lê não contribui (a resolução por código recusa o ambíguo de qualquer jeito).
|
|
440
|
+
*/
|
|
441
|
+
function codigosDosArcos(raiz) {
|
|
442
|
+
let nomes;
|
|
443
|
+
try {
|
|
444
|
+
nomes = fs.readdirSync(path.join((0, estado_thread_1.raizDoEstado)(raiz), '.orkastery', 'noctum', 'arcos')).filter(n => noctum_envelope_1.ID_DO_ARCO.test(n));
|
|
445
|
+
}
|
|
446
|
+
catch {
|
|
447
|
+
return [];
|
|
448
|
+
}
|
|
449
|
+
const codigos = [];
|
|
450
|
+
for (const arco of nomes) {
|
|
451
|
+
try {
|
|
452
|
+
const d = derivar(lerLog(raiz, arco).linhas);
|
|
453
|
+
for (const p of d.propostas.values())
|
|
454
|
+
if (p.codigo)
|
|
455
|
+
codigos.push(p.codigo);
|
|
456
|
+
for (const r of d.revogacoesPedidas)
|
|
457
|
+
codigos.push(r.codigo);
|
|
458
|
+
}
|
|
459
|
+
catch { /* arco ilegível não empresta código */ }
|
|
460
|
+
}
|
|
461
|
+
return codigos;
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* RM-057 (resposta curta, D10 da ork-rm057respost): abre a pergunta de revogação ao dono. Não revoga nada: quem revoga é
|
|
465
|
+
* a resposta dele, pela prova do canal ou do terminal. A pergunta que ainda espera o dono é devolvida, em vez de outra.
|
|
466
|
+
*/
|
|
467
|
+
function pedirRevogacao(raiz, arco, quando = new Date().toISOString(), emUso = []) {
|
|
468
|
+
return comLockDoArco(raiz, arco, false, () => {
|
|
469
|
+
const log = lerLog(raiz, arco), d = derivar(log.linhas);
|
|
470
|
+
if (d.revogacao)
|
|
471
|
+
throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.revogado', `o arco ${arco} já foi revogado`);
|
|
472
|
+
if (d.encerramento)
|
|
473
|
+
throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.encerrado', `arco ${arco} encerrado (${d.encerramento.motivo}) é terminal`);
|
|
474
|
+
if (!d.autorizacao) {
|
|
475
|
+
throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.sem-autorizacao', `o arco ${arco} nunca foi autorizado: não há o que revogar, e a proposta vence sozinha`);
|
|
476
|
+
}
|
|
477
|
+
const estado = estadoDe(arco, d, quando);
|
|
478
|
+
if (estado.situacao === 'expirado')
|
|
479
|
+
throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.expirado', `o arco ${arco} já terminou; não há o que revogar`);
|
|
480
|
+
const base = { arco, versao: d.autorizacao.versao, envelopeSha256: d.autorizacao.envelopeSha256 };
|
|
481
|
+
if (estado.revogacaoPedida)
|
|
482
|
+
return { ...base, codigo: estado.revogacaoPedida.codigo, prazo: estado.revogacaoPedida.prazo, nova: false };
|
|
483
|
+
const codigo = (0, pulse_consentimento_1.gerarCodigo)([...emUso, ...codigosDosArcos(raiz)]);
|
|
484
|
+
const prazo = new Date(Date.parse(quando) + exports.PRAZO_PARA_AUTORIZAR_MIN * 60000).toISOString();
|
|
485
|
+
anexar(raiz, arco, log, { tipo: 'revogacao_pedida', codigo, prazo }, quando);
|
|
486
|
+
return { ...base, codigo, prazo, nova: true };
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* RM-057 (resposta curta): as perguntas do arco ao dono, derivadas do log a cada leitura, com a situação de cada uma.
|
|
491
|
+
* Só a `pendente` aceita resposta; as outras existem para a resposta atrasada ouvir o porquê, nunca para valer.
|
|
492
|
+
*/
|
|
493
|
+
function perguntasDoArco(raiz, arco, quando = new Date().toISOString()) {
|
|
494
|
+
const d = derivar(lerLog(raiz, arco).linhas);
|
|
495
|
+
const estado = estadoDe(arco, d, quando);
|
|
496
|
+
const terminal = d.revogacao !== null || d.encerramento !== null;
|
|
497
|
+
const perguntas = [];
|
|
498
|
+
for (const p of d.propostas.values()) {
|
|
499
|
+
if (!p.codigo)
|
|
500
|
+
continue;
|
|
501
|
+
const situacao = d.autorizacao?.versao === p.versao ? 'respondida'
|
|
502
|
+
: d.ultima?.versao !== p.versao ? 'substituida' : terminal ? 'arco-terminal'
|
|
503
|
+
: estado.propostaPendente?.versao === p.versao && Date.parse(quando) < Date.parse(p.conteudo.janela.fim) ? 'pendente' : 'vencida';
|
|
504
|
+
perguntas.push({ contrato: noctum_pergunta_1.CONTRATO_PERGUNTA_DO_ARCO, arco, ato: 'autorizar', codigo: p.codigo, versao: p.versao,
|
|
505
|
+
envelopeSha256: p.envelopeSha256, criadaEm: p.em, prazo: p.prazo, conteudo: p.conteudo, situacao });
|
|
506
|
+
}
|
|
507
|
+
const vigente = d.autorizacao ? d.propostas.get(d.autorizacao.versao) ?? null : null;
|
|
508
|
+
d.revogacoesPedidas.forEach((r, i) => {
|
|
509
|
+
const situacao = d.revogacao ? 'respondida' : i < d.revogacoesPedidas.length - 1 ? 'substituida'
|
|
510
|
+
: d.encerramento ? 'arco-terminal' : estado.revogacaoPedida?.codigo === r.codigo ? 'pendente' : 'vencida';
|
|
511
|
+
perguntas.push({ contrato: noctum_pergunta_1.CONTRATO_PERGUNTA_DO_ARCO, arco, ato: 'revogar', codigo: r.codigo, versao: vigente?.versao ?? null,
|
|
512
|
+
envelopeSha256: vigente?.envelopeSha256 ?? null, criadaEm: r.em, prazo: r.prazo, conteudo: vigente?.conteudo ?? null, situacao });
|
|
381
513
|
});
|
|
514
|
+
return perguntas;
|
|
382
515
|
}
|
|
383
516
|
/**
|
|
384
517
|
* D7: emenda é versão nova com prova nova, mas não prorroga nada. Ela mantém o início, não passa do
|
|
@@ -394,11 +527,20 @@ function exigirEmendaSemProrrogar(d, conteudo, quando) {
|
|
|
394
527
|
if (Date.parse(conteudo.janela.inicio) !== Date.parse(janela.inicio) || Date.parse(conteudo.janela.fim) > Date.parse(janela.fim)) {
|
|
395
528
|
throw new noctum_envelope_1.RecusaDoArco('noctum.envelope.invalido', 'emenda mantém o início e não prorroga o fim do envelope autorizado');
|
|
396
529
|
}
|
|
530
|
+
// RM-058 N3 (A4 da revisão da ork-rm058n3): a emenda não troca o contrato entre a `v3` e as outras. O consumo e as sessões de
|
|
531
|
+
// uma máquina só não estão no ramo de coordenação, e a soma entre máquinas os perderia; o arco entre máquinas nasce assim.
|
|
532
|
+
const antes = d.propostas.get(d.autorizacao.versao).conteudo.contrato;
|
|
533
|
+
if ((antes === noctum_envelope_1.CONTRATO_ENVELOPE_V3) !== (conteudo.contrato === noctum_envelope_1.CONTRATO_ENVELOPE_V3)) {
|
|
534
|
+
throw new noctum_envelope_1.RecusaDoArco('noctum.envelope.invalido', `a emenda não troca o contrato de ${antes} para ${conteudo.contrato}: ` +
|
|
535
|
+
'o arco entre máquinas começa como tal, e o de uma máquina segue assim; abra outro arco');
|
|
536
|
+
}
|
|
397
537
|
}
|
|
398
538
|
/** A proposta que o dono pode autorizar agora, ou a recusa tipada de por que não. */
|
|
399
539
|
function propostaAutorizavel(d, envelopeSha256, quando) {
|
|
400
540
|
if (d.revogacao)
|
|
401
541
|
throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.revogado', 'arco revogado é terminal; abra outro arco');
|
|
542
|
+
if (d.encerramento)
|
|
543
|
+
throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.encerrado', `arco encerrado (${d.encerramento.motivo}) é terminal; abra outro arco`);
|
|
402
544
|
if (!d.ultima)
|
|
403
545
|
throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.sem-autorizacao', 'arco sem proposta');
|
|
404
546
|
if (d.ultima.envelopeSha256 !== envelopeSha256) {
|
|
@@ -448,7 +590,9 @@ function provarEnvelope(arco, texto, envelope, quando, amarra) {
|
|
|
448
590
|
function dadosDoRecibo(arco, ato, envelopeSha256, versao, i) {
|
|
449
591
|
return { contrato: 'ork.noctum-ingresso/v1', arco, endereco: (0, exports.enderecoDoArco)(arco), ato, envelopeSha256, versao,
|
|
450
592
|
origem: i.origem, canal: i.canal, conta: i.conta, por: i.por, mensagem: i.mensagem, recebidoEm: i.recebidoEm,
|
|
451
|
-
recibo: i.recibo, ...(i.origem === 'native' ? { native: i.native } : {})
|
|
593
|
+
recibo: i.recibo, ...(i.origem === 'native' ? { native: i.native } : {}),
|
|
594
|
+
// RM-057 (resposta curta, D7): a pergunta e o endereço que o gateway assinou entram no MAC do canal.
|
|
595
|
+
...(i.origem === 'telegram' && i.pergunta ? { pergunta: i.pergunta } : {}) };
|
|
452
596
|
}
|
|
453
597
|
function lerPrivado(file) {
|
|
454
598
|
const fd = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
|
|
@@ -509,6 +653,8 @@ function gravarRecibo(raiz, arco, ato, envelopeSha256, versao, i) {
|
|
|
509
653
|
function reciboConfere(raiz, arco, ato, envelopeSha256, versao, i) {
|
|
510
654
|
if (i.origem === 'mcp-local')
|
|
511
655
|
return 'ok';
|
|
656
|
+
if (i.origem === 'terminal')
|
|
657
|
+
return reciboDoTerminalConfere(raiz, arco, ato, envelopeSha256, versao, i);
|
|
512
658
|
try {
|
|
513
659
|
const nome = nomeDoRecibo(i);
|
|
514
660
|
if (i.evidencia !== `.orkastery/noctum/arcos/${arco}/ingresso/${nome}`)
|
|
@@ -561,15 +707,29 @@ function confirmado(raiz, arco, quando, vale, ato) {
|
|
|
561
707
|
throw adulterado(`a ${ato} não ficou gravada no log`);
|
|
562
708
|
return estado;
|
|
563
709
|
}
|
|
564
|
-
/**
|
|
565
|
-
|
|
710
|
+
/**
|
|
711
|
+
* Autoriza a proposta pendente pelo envelope assinado do dono. Sem prova, nada muda. `info.repetida` diz, decidido sob
|
|
712
|
+
* o lock do arco, que o ato já valia antes desta mensagem (o gateway não confirma de novo ao dono).
|
|
713
|
+
*/
|
|
714
|
+
function autorizarPeloIngresso(raiz, arco, envelopeSha256, envelope, quando = new Date().toISOString(), info) {
|
|
566
715
|
return comLockDoArco(raiz, arco, false, () => {
|
|
567
716
|
const log = lerLog(raiz, arco), d = derivar(log.linhas);
|
|
568
717
|
const versao = d.ultima?.envelopeSha256 === envelopeSha256 ? d.ultima.versao : 0;
|
|
569
718
|
const provado = provarEnvelope(arco, (0, exports.textoDeAutorizacao)(envelopeSha256), envelope, quando, { pedidoSha256: envelopeSha256, contexto: contextoDoArco(arco, versao), prazo: d.ultima?.prazo });
|
|
570
|
-
// A mesma autorização repetida
|
|
571
|
-
|
|
572
|
-
|
|
719
|
+
// A mesma autorização repetida devolve o que já vale; no arco que já acabou, a recusa tipada chega ao gateway, que
|
|
720
|
+
// responde ao dono, em vez de um ok mudo (RM-058 N6).
|
|
721
|
+
if (d.autorizacao?.envelopeSha256 === envelopeSha256 && !d.revogacao) {
|
|
722
|
+
const atual = estadoDe(arco, d, quando);
|
|
723
|
+
if (atual.situacao === 'encerrado') {
|
|
724
|
+
throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.encerrado', `arco ${arco} encerrado (${d.encerramento?.motivo ?? 'fechado'}) é terminal; abra outro arco`);
|
|
725
|
+
}
|
|
726
|
+
if (atual.situacao === 'expirado') {
|
|
727
|
+
throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.expirado', `o arco ${arco} terminou em ${atual.conteudo?.janela.fim ?? 'o fim da janela'}; abra outro arco`);
|
|
728
|
+
}
|
|
729
|
+
if (info)
|
|
730
|
+
info.repetida = true;
|
|
731
|
+
return atual;
|
|
732
|
+
}
|
|
573
733
|
if (d.usadas.has(`${provado.origem}:${provado.mensagem}`))
|
|
574
734
|
throw semProva('esta mensagem já provou outro ato neste arco');
|
|
575
735
|
const proposta = propostaAutorizavel(d, envelopeSha256, quando);
|
|
@@ -578,13 +738,16 @@ function autorizarPeloIngresso(raiz, arco, envelopeSha256, envelope, quando = ne
|
|
|
578
738
|
return confirmado(raiz, arco, quando, e => e.envelopeSha256 === envelopeSha256 && e.situacao !== 'revogado', 'autorização');
|
|
579
739
|
});
|
|
580
740
|
}
|
|
581
|
-
/** Revoga o arco pelo envelope assinado do dono. Terminal: nenhum efeito passa depois (D7). */
|
|
582
|
-
function revogarPeloIngresso(raiz, arco, envelope, quando = new Date().toISOString()) {
|
|
741
|
+
/** Revoga o arco pelo envelope assinado do dono. Terminal: nenhum efeito passa depois (D7). `info.repetida`: já valia. */
|
|
742
|
+
function revogarPeloIngresso(raiz, arco, envelope, quando = new Date().toISOString(), info) {
|
|
583
743
|
return comLockDoArco(raiz, arco, false, () => {
|
|
584
744
|
const log = lerLog(raiz, arco), d = derivar(log.linhas);
|
|
585
745
|
const provado = provarEnvelope(arco, exports.TEXTO_DE_REVOGACAO, envelope, quando, { pedidoSha256: hashDaRevogacao(arco), contexto: contextoDoArco(arco, 0) });
|
|
586
|
-
if (d.revogacao)
|
|
746
|
+
if (d.revogacao) {
|
|
747
|
+
if (info)
|
|
748
|
+
info.repetida = true;
|
|
587
749
|
return estadoDe(arco, d, quando);
|
|
750
|
+
}
|
|
588
751
|
if (d.usadas.has(`${provado.origem}:${provado.mensagem}`))
|
|
589
752
|
throw semProva('esta mensagem já provou outro ato neste arco');
|
|
590
753
|
// O recibo da revogação é auditoria e nunca é reconferido: falha nele não segura o ato de segurança, e o motivo fica na linha.
|
|
@@ -601,6 +764,274 @@ function revogarPeloIngresso(raiz, arco, envelope, quando = new Date().toISOStri
|
|
|
601
764
|
return confirmado(raiz, arco, quando, e => e.situacao === 'revogado', 'revogação');
|
|
602
765
|
}, true);
|
|
603
766
|
}
|
|
767
|
+
// ---------------------------------------------------------------------------
|
|
768
|
+
// RM-057 (resposta curta, ork-rm057respost): os atos do dono pela resposta curta à pergunta do arco.
|
|
769
|
+
// ---------------------------------------------------------------------------
|
|
770
|
+
/** O domínio da subchave local que assina o recibo do terminal: o mesmo segredo do recibo local, separado por domínio. */
|
|
771
|
+
const DOMINIO_DO_RECIBO_DO_TERMINAL = 'ork.noctum-terminal/v1';
|
|
772
|
+
/** A resposta curta já lida pelo núcleo: o código da pergunta, a letra e o hash do texto do dono. */
|
|
773
|
+
function validarRespostaAPergunta(r) {
|
|
774
|
+
if (!objeto(r) || typeof r.codigo !== 'string' || !pulse_consentimento_1.CODIGO_CURTO.test(r.codigo) || !['a', 'b', 'c'].includes(String(r.letra)) ||
|
|
775
|
+
typeof r.respostaSha256 !== 'string' || !/^[a-f0-9]{64}$/.test(r.respostaSha256) ||
|
|
776
|
+
(r.numero !== undefined && (!Number.isInteger(r.numero) || r.numero < 1 || r.numero > 99))) {
|
|
777
|
+
throw semProva('a resposta curta chegou malformada');
|
|
778
|
+
}
|
|
779
|
+
return { codigo: r.codigo, letra: r.letra, respostaSha256: r.respostaSha256,
|
|
780
|
+
...(r.numero !== undefined ? { numero: r.numero } : {}) };
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* A prova da resposta curta. Pelo canal, a MESMA `autenticarResposta` do HITL, no endereço do pulse que o gateway assinou
|
|
784
|
+
* (`resposta`, ou `resposta:<código>` no reply), no instante do envelope e no de agora, com o texto assinado igual ao que o
|
|
785
|
+
* núcleo leu. Pelo terminal, o atestado da origem da sessão, conferido por `noctum-terminal.ts` (D5): a fronteira dele é
|
|
786
|
+
* a conta do sistema, como a do diálogo nativo.
|
|
787
|
+
*/
|
|
788
|
+
function provarRespostaCurta(prova, quando) {
|
|
789
|
+
if (!objeto(prova))
|
|
790
|
+
throw semProva('a resposta curta precisa de prova');
|
|
791
|
+
const pergunta = validarRespostaAPergunta(prova.pergunta);
|
|
792
|
+
if (prova.via === 'pulse') {
|
|
793
|
+
const e = prova.envelope;
|
|
794
|
+
if (!objeto(e) || e.origem !== 'telegram')
|
|
795
|
+
throw semProva('a resposta curta pelo canal precisa do envelope assinado do Telegram');
|
|
796
|
+
// GO-FIX 1 (achado 4): o endereço assinado é o do pulse ou o do reply à MESMA pergunta, `resposta:<código dela>:<id>`;
|
|
797
|
+
// o recibo nunca diz uma pergunta e autoriza outra.
|
|
798
|
+
const ref = typeof prova.endereco === 'string' && prova.endereco.startsWith(`${pulse_consentimento_1.ENDERECO_DA_RESPOSTA}:`)
|
|
799
|
+
? (0, pulse_consentimento_1.lerReferenciaDoReply)(prova.endereco.slice(pulse_consentimento_1.ENDERECO_DA_RESPOSTA.length + 1)) : null;
|
|
800
|
+
if (prova.endereco !== pulse_consentimento_1.ENDERECO_DA_RESPOSTA && ref?.codigo !== pergunta.codigo) {
|
|
801
|
+
throw semProva('o endereço assinado não é o do pulse nem o do reply a esta pergunta');
|
|
802
|
+
}
|
|
803
|
+
try {
|
|
804
|
+
(0, hitl_gates_1.autenticarResposta)(pulse_consentimento_1.ALVO_DO_PULSE, prova.endereco, e, e.recebidoEm);
|
|
805
|
+
(0, hitl_gates_1.autenticarResposta)(pulse_consentimento_1.ALVO_DO_PULSE, prova.endereco, e, quando);
|
|
806
|
+
}
|
|
807
|
+
catch (x) {
|
|
808
|
+
throw semProva(`ingresso não autenticado (${x.message})`);
|
|
809
|
+
}
|
|
810
|
+
if (typeof e.resposta !== 'string' || sha(e.resposta) !== pergunta.respostaSha256) {
|
|
811
|
+
throw semProva('o texto assinado não é a resposta que o núcleo leu');
|
|
812
|
+
}
|
|
813
|
+
return { origem: 'telegram', canal: e.canal ?? null, conta: e.conta ?? null, por: e.por, mensagem: e.mensagem,
|
|
814
|
+
recebidoEm: e.recebidoEm, recibo: sha(e.prova), pergunta: { ...pergunta, enderecoAssinado: { alvo: pulse_consentimento_1.ALVO_DO_PULSE, endereco: prova.endereco } } };
|
|
815
|
+
}
|
|
816
|
+
if (prova.via === 'terminal') {
|
|
817
|
+
const a = prova.atestado;
|
|
818
|
+
const idade = objeto(a) ? Date.parse(quando) - Date.parse(String(a.recebidoEm)) : NaN;
|
|
819
|
+
if (!objeto(a) || a.host !== 'claude-code' || typeof a.sessao !== 'string' || !/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,127}$/.test(a.sessao) ||
|
|
820
|
+
(a.entrypoint !== null && (typeof a.entrypoint !== 'string' || a.entrypoint.length > 64)) || !Array.isArray(a.conferencias) ||
|
|
821
|
+
!a.conferencias.length || a.conferencias.some(c => typeof c !== 'string' || c.length > 80) ||
|
|
822
|
+
!Number.isFinite(idade) || idade < 0 || idade > 60000) {
|
|
823
|
+
throw semProva('o atestado do terminal chegou malformado ou fora da janela de 60 s');
|
|
824
|
+
}
|
|
825
|
+
return { origem: 'terminal', host: 'claude-code', sessao: a.sessao, entrypoint: a.entrypoint, conferencias: [...a.conferencias],
|
|
826
|
+
recebidoEm: a.recebidoEm, solicitacao: (0, node_crypto_1.randomUUID)(), por: 'terminal:claude-code', pergunta };
|
|
827
|
+
}
|
|
828
|
+
throw semProva('a resposta curta chegou por uma via desconhecida');
|
|
829
|
+
}
|
|
830
|
+
/** A subchave do recibo do terminal. */
|
|
831
|
+
function chaveDoTerminal(raiz, criar) {
|
|
832
|
+
return (0, node_crypto_1.createHmac)('sha256', (0, hitl_local_receipt_1.chaveLocal)(raiz, criar)).update(DOMINIO_DO_RECIBO_DO_TERMINAL).digest();
|
|
833
|
+
}
|
|
834
|
+
const nomeDoReciboDoTerminal = (i) => `${sha(`terminal:${i.solicitacao}`)}.json`;
|
|
835
|
+
function dadosDoReciboDoTerminal(arco, ato, envelopeSha256, versao, i) {
|
|
836
|
+
return { contrato: 'ork.noctum-ingresso/v1', arco, endereco: (0, exports.enderecoDoArco)(arco), ato, envelopeSha256, versao, origem: 'terminal',
|
|
837
|
+
host: i.host, sessao: i.sessao, entrypoint: i.entrypoint, conferencias: i.conferencias, solicitacao: i.solicitacao, por: i.por,
|
|
838
|
+
recebidoEm: i.recebidoEm, pergunta: i.pergunta };
|
|
839
|
+
}
|
|
840
|
+
/** O recibo do terminal, com a subchave local: a conferência antes do efeito o relê, como relê o do canal. */
|
|
841
|
+
function gravarReciboDoTerminal(raiz, arco, ato, envelopeSha256, versao, i) {
|
|
842
|
+
const dir = path.join(dirDoArco(raiz, arco), 'ingresso');
|
|
843
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
844
|
+
const nome = nomeDoReciboDoTerminal(i), file = path.join(dir, nome);
|
|
845
|
+
const dados = dadosDoReciboDoTerminal(arco, ato, envelopeSha256, versao, i);
|
|
846
|
+
const bytes = JSON.stringify({ dados, assinatura: (0, node_crypto_1.createHmac)('sha256', chaveDoTerminal(raiz, true)).update(JSON.stringify(dados)).digest('hex') });
|
|
847
|
+
const temp = path.join(dir, `${(0, node_crypto_1.randomUUID)()}.tmp`);
|
|
848
|
+
const fd = fs.openSync(temp, 'wx', 0o600);
|
|
849
|
+
try {
|
|
850
|
+
fs.writeFileSync(fd, bytes);
|
|
851
|
+
fs.fsyncSync(fd);
|
|
852
|
+
}
|
|
853
|
+
finally {
|
|
854
|
+
fs.closeSync(fd);
|
|
855
|
+
}
|
|
856
|
+
try {
|
|
857
|
+
fs.linkSync(temp, file);
|
|
858
|
+
}
|
|
859
|
+
catch (e) {
|
|
860
|
+
if (e.code !== 'EEXIST')
|
|
861
|
+
throw e;
|
|
862
|
+
}
|
|
863
|
+
finally {
|
|
864
|
+
fs.unlinkSync(temp);
|
|
865
|
+
}
|
|
866
|
+
if (lerPrivado(file).toString('utf8') !== bytes)
|
|
867
|
+
throw adulterado('recibo do terminal já existe com outro conteúdo');
|
|
868
|
+
return { evidencia: `.orkastery/noctum/arcos/${arco}/ingresso/${nome}`, evidenciaSha256: sha(bytes) };
|
|
869
|
+
}
|
|
870
|
+
/** Reconfere o recibo do terminal: o arquivo privado, o hash, os dados iguais aos da linha e o MAC da subchave local. */
|
|
871
|
+
function reciboDoTerminalConfere(raiz, arco, ato, envelopeSha256, versao, i) {
|
|
872
|
+
try {
|
|
873
|
+
const nome = nomeDoReciboDoTerminal(i);
|
|
874
|
+
if (i.evidencia !== `.orkastery/noctum/arcos/${arco}/ingresso/${nome}`)
|
|
875
|
+
return 'falhou';
|
|
876
|
+
const bytes = lerPrivado(path.join(dirDoArco(raiz, arco), 'ingresso', nome));
|
|
877
|
+
if (sha(bytes) !== i.evidenciaSha256)
|
|
878
|
+
return 'falhou';
|
|
879
|
+
const v = JSON.parse(bytes.toString('utf8'));
|
|
880
|
+
const { evidencia: _evidencia, evidenciaSha256: _sha, semRecibo: _sem, ...atestado } = i;
|
|
881
|
+
if (!objeto(v) || Object.keys(v).sort().join(',') !== 'assinatura,dados' || typeof v.assinatura !== 'string' ||
|
|
882
|
+
!/^[a-f0-9]{64}$/.test(v.assinatura) || (0, noctum_envelope_1.canonico)(v.dados) !== (0, noctum_envelope_1.canonico)(dadosDoReciboDoTerminal(arco, ato, envelopeSha256, versao, atestado))) {
|
|
883
|
+
return 'falhou';
|
|
884
|
+
}
|
|
885
|
+
return (0, node_crypto_1.timingSafeEqual)(Buffer.from(v.assinatura, 'hex'), (0, node_crypto_1.createHmac)('sha256', chaveDoTerminal(raiz, false)).update(JSON.stringify(v.dados)).digest())
|
|
886
|
+
? 'ok' : 'falhou';
|
|
887
|
+
}
|
|
888
|
+
catch {
|
|
889
|
+
return 'falhou';
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
/** GO-FIX 1 (achado 6 da ork-rm057respost): a resposta não é de antes da pergunta; a mensagem velha não responde à nova. */
|
|
893
|
+
function exigirDepoisDaPergunta(provado, criadaEm) {
|
|
894
|
+
if (!(Date.parse(provado.recebidoEm) >= Date.parse(criadaEm)))
|
|
895
|
+
throw semProva('a mensagem é de antes da pergunta que ela diz responder');
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* GO-FIX 1 (achado 6 da ork-rm057respost): uma mensagem assinada do dono responde a UMA pergunta. A conferência de "uma
|
|
899
|
+
* prova, um ato" do log vale por arco; esta vale para todos: a mensagem é reivindicada num arquivo privado por mensagem,
|
|
900
|
+
* criado de uma vez (link exclusivo) antes do ato. A mesma mensagem de novo, para a mesma pergunta e o mesmo ato, segue ao
|
|
901
|
+
* caminho de sempre (repetida); para outra pergunta, de qualquer arco, é recusada antes de gravar, mesmo dentro dos 60 s
|
|
902
|
+
* da assinatura (a reentrega do gateway depois de reiniciar, ou um processo que reapresente o envelope).
|
|
903
|
+
*/
|
|
904
|
+
function reivindicarMensagem(raiz, chave, alvo, quando) {
|
|
905
|
+
const principal = (0, estado_thread_1.raizDoEstado)(raiz);
|
|
906
|
+
(0, estado_thread_1.exigirEstadoSemLink)(principal, ['.orkastery', 'noctum', 'mensagens-usadas']);
|
|
907
|
+
const dir = path.join(principal, '.orkastery', 'noctum', 'mensagens-usadas');
|
|
908
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
909
|
+
const file = path.join(dir, `${sha(chave)}.json`);
|
|
910
|
+
const dados = JSON.stringify({ contrato: 'ork.noctum-mensagem-usada/v1', mensagemSha256: sha(chave), ...alvo, em: quando });
|
|
911
|
+
const temp = path.join(dir, `${(0, node_crypto_1.randomUUID)()}.tmp`);
|
|
912
|
+
const fd = fs.openSync(temp, 'wx', 0o600);
|
|
913
|
+
try {
|
|
914
|
+
fs.writeFileSync(fd, dados);
|
|
915
|
+
fs.fsyncSync(fd);
|
|
916
|
+
}
|
|
917
|
+
finally {
|
|
918
|
+
fs.closeSync(fd);
|
|
919
|
+
}
|
|
920
|
+
try {
|
|
921
|
+
fs.linkSync(temp, file);
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
catch (e) {
|
|
925
|
+
if (e.code !== 'EEXIST')
|
|
926
|
+
throw e;
|
|
927
|
+
}
|
|
928
|
+
finally {
|
|
929
|
+
fs.unlinkSync(temp);
|
|
930
|
+
}
|
|
931
|
+
let antes;
|
|
932
|
+
try {
|
|
933
|
+
antes = JSON.parse(lerPrivado(file).toString('utf8'));
|
|
934
|
+
}
|
|
935
|
+
catch {
|
|
936
|
+
throw semProva('o registro desta mensagem não se lê; nada foi gravado');
|
|
937
|
+
}
|
|
938
|
+
if (!objeto(antes) || antes.arco !== alvo.arco || antes.codigo !== alvo.codigo || antes.ato !== alvo.ato) {
|
|
939
|
+
throw semProva('esta mensagem já respondeu a outra pergunta; mande outra');
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
/** O ingresso do dono com o recibo durável da via (o do canal ou o do terminal). */
|
|
943
|
+
function comRecibo(raiz, arco, ato, envelopeSha256, versao, provado) {
|
|
944
|
+
return provado.origem === 'terminal'
|
|
945
|
+
? { ...provado, ...gravarReciboDoTerminal(raiz, arco, ato, envelopeSha256, versao, provado) }
|
|
946
|
+
: { ...provado, ...gravarRecibo(raiz, arco, ato, envelopeSha256, versao, provado) };
|
|
947
|
+
}
|
|
948
|
+
/**
|
|
949
|
+
* RM-057 (resposta curta): autoriza a proposta pela resposta curta do dono à pergunta dela. O hash vem da pergunta (a
|
|
950
|
+
* proposta com este código), nunca da resposta. Sob o lock do arco: a pergunta tem de ser a de agora (a proposta mais
|
|
951
|
+
* nova, no prazo e na janela), a prova não pode ter servido a outro ato, e o ato só volta com sucesso relido do disco.
|
|
952
|
+
*/
|
|
953
|
+
function autorizarPelaResposta(raiz, arco, codigo, prova, quando = new Date().toISOString(), info) {
|
|
954
|
+
return comLockDoArco(raiz, arco, false, () => {
|
|
955
|
+
const log = lerLog(raiz, arco), d = derivar(log.linhas);
|
|
956
|
+
const proposta = [...d.propostas.values()].find(p => p.codigo === codigo);
|
|
957
|
+
if (!proposta)
|
|
958
|
+
throw new noctum_envelope_1.RecusaDoArco('noctum.pergunta.desconhecida', `o arco ${arco} não tem a pergunta ${codigo}`);
|
|
959
|
+
const provado = provarRespostaCurta(prova, quando);
|
|
960
|
+
if (provado.pergunta?.codigo !== codigo)
|
|
961
|
+
throw semProva('a prova responde a outra pergunta');
|
|
962
|
+
exigirDepoisDaPergunta(provado, proposta.em);
|
|
963
|
+
if (provado.origem === 'telegram')
|
|
964
|
+
reivindicarMensagem(raiz, chaveDaProva(provado), { arco, codigo, ato: 'autorizar' }, quando);
|
|
965
|
+
if (d.autorizacao?.envelopeSha256 === proposta.envelopeSha256 && d.autorizacao.versao === proposta.versao && !d.revogacao) {
|
|
966
|
+
const atual = estadoDe(arco, d, quando);
|
|
967
|
+
if (atual.situacao === 'encerrado') {
|
|
968
|
+
throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.encerrado', `arco ${arco} encerrado (${d.encerramento?.motivo ?? 'fechado'}) é terminal; abra outro arco`);
|
|
969
|
+
}
|
|
970
|
+
if (atual.situacao === 'expirado') {
|
|
971
|
+
throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.expirado', `o arco ${arco} terminou em ${atual.conteudo?.janela.fim ?? 'o fim da janela'}; abra outro arco`);
|
|
972
|
+
}
|
|
973
|
+
if (info) {
|
|
974
|
+
info.repetida = true;
|
|
975
|
+
info.mesmaProva = chaveDaProva(d.autorizacao.ingresso) === chaveDaProva(provado);
|
|
976
|
+
}
|
|
977
|
+
return atual;
|
|
978
|
+
}
|
|
979
|
+
if (d.usadas.has(chaveDaProva(provado)))
|
|
980
|
+
throw semProva('esta mensagem já provou outro ato neste arco');
|
|
981
|
+
// A pergunta respondida é a de agora: a proposta mais nova, no prazo e na janela (a recusa diz por que não).
|
|
982
|
+
const autorizavel = propostaAutorizavel(d, proposta.envelopeSha256, quando);
|
|
983
|
+
const ingresso = comRecibo(raiz, arco, 'autorizar', autorizavel.envelopeSha256, autorizavel.versao, provado);
|
|
984
|
+
anexar(raiz, arco, log, { tipo: 'autorizado', versao: autorizavel.versao, envelopeSha256: autorizavel.envelopeSha256, ingresso }, quando);
|
|
985
|
+
return confirmado(raiz, arco, quando, e => e.envelopeSha256 === autorizavel.envelopeSha256 && e.situacao !== 'revogado', 'autorização');
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* RM-057 (resposta curta, D10): revoga o arco pela resposta curta do dono à pergunta de revogação. Terminal, como a
|
|
990
|
+
* revogação assinada. A pergunta tem de ser a última de revogação, no prazo; o recibo é auditoria e nunca segura o ato.
|
|
991
|
+
*/
|
|
992
|
+
function revogarPelaResposta(raiz, arco, codigo, prova, quando = new Date().toISOString(), info) {
|
|
993
|
+
return comLockDoArco(raiz, arco, false, () => {
|
|
994
|
+
const log = lerLog(raiz, arco), d = derivar(log.linhas);
|
|
995
|
+
const pedida = d.revogacoesPedidas.find(r => r.codigo === codigo);
|
|
996
|
+
if (!pedida)
|
|
997
|
+
throw new noctum_envelope_1.RecusaDoArco('noctum.pergunta.desconhecida', `o arco ${arco} não tem a pergunta ${codigo}`);
|
|
998
|
+
const provado = provarRespostaCurta(prova, quando);
|
|
999
|
+
if (provado.pergunta?.codigo !== codigo)
|
|
1000
|
+
throw semProva('a prova responde a outra pergunta');
|
|
1001
|
+
exigirDepoisDaPergunta(provado, pedida.em);
|
|
1002
|
+
if (provado.origem === 'telegram')
|
|
1003
|
+
reivindicarMensagem(raiz, chaveDaProva(provado), { arco, codigo, ato: 'revogar' }, quando);
|
|
1004
|
+
if (d.revogacao) {
|
|
1005
|
+
if (info) {
|
|
1006
|
+
info.repetida = true;
|
|
1007
|
+
info.mesmaProva = chaveDaProva(d.revogacao) === chaveDaProva(provado);
|
|
1008
|
+
}
|
|
1009
|
+
return estadoDe(arco, d, quando);
|
|
1010
|
+
}
|
|
1011
|
+
if (d.revogacoesPedidas.at(-1)?.codigo !== codigo) {
|
|
1012
|
+
throw new noctum_envelope_1.RecusaDoArco('noctum.pergunta.substituida', `a pergunta ${codigo} foi substituída por outra de revogação`);
|
|
1013
|
+
}
|
|
1014
|
+
if (!(Date.parse(quando) < Date.parse(pedida.prazo))) {
|
|
1015
|
+
throw new noctum_envelope_1.RecusaDoArco('noctum.pergunta.vencida', `a pergunta ${codigo} venceu em ${(0, horario_1.formatarDataHora)(pedida.prazo)}; peça a revogação de novo`);
|
|
1016
|
+
}
|
|
1017
|
+
if (d.usadas.has(chaveDaProva(provado)))
|
|
1018
|
+
throw semProva('esta mensagem já provou outro ato neste arco');
|
|
1019
|
+
let recibo;
|
|
1020
|
+
try {
|
|
1021
|
+
const comProva = comRecibo(raiz, arco, 'revogar', null, null, provado);
|
|
1022
|
+
recibo = { evidencia: comProva.evidencia, evidenciaSha256: comProva.evidenciaSha256 };
|
|
1023
|
+
}
|
|
1024
|
+
catch (e) {
|
|
1025
|
+
recibo = { evidencia: '', evidenciaSha256: '', semRecibo: e instanceof noctum_envelope_1.RecusaDoArco ? e.motivo : 'noctum.recibo.nao-gravado' };
|
|
1026
|
+
}
|
|
1027
|
+
anexar(raiz, arco, log, { tipo: 'revogado', ingresso: { ...provado, ...recibo } }, quando);
|
|
1028
|
+
return confirmado(raiz, arco, quando, e => e.situacao === 'revogado', 'revogação');
|
|
1029
|
+
}, true);
|
|
1030
|
+
}
|
|
1031
|
+
/** As opções do diálogo, uma por alternativa da pergunta, com o selo na recomendada. */
|
|
1032
|
+
function opcoesDoDialogo(ato) {
|
|
1033
|
+
return (0, noctum_pergunta_1.alternativasDaPergunta)({ ato }).map(a => `${a.letra}) ${a.texto}${a.porque ? ' (Recomendação)' : ''}`);
|
|
1034
|
+
}
|
|
604
1035
|
/**
|
|
605
1036
|
* O diálogo nativo do host para autorizar ou revogar, com o callback de elicitação fornecido pelo
|
|
606
1037
|
* servidor, nunca pelos argumentos de uma ferramenta. É a mesma fronteira de `criarIngressoLocal`:
|
|
@@ -621,6 +1052,7 @@ function criarDialogoDoArco(raiz, origem, elicitar) {
|
|
|
621
1052
|
try {
|
|
622
1053
|
const cancelar = new Promise(r => controller.signal.addEventListener('abort', () => r({ action: 'cancel' }), { once: true }));
|
|
623
1054
|
const r = await Promise.race([elicitar(JSON.parse(JSON.stringify(a)), controller.signal), cancelar]);
|
|
1055
|
+
// Só a primeira alternativa (a recomendada) age; adiar e mudar algo não fazem nada (D13 da ork-rm057respost).
|
|
624
1056
|
if (!objeto(r) || r.action !== 'accept' || !objeto(r.content) || Object.keys(r.content).length !== 1 ||
|
|
625
1057
|
r.content.opcao !== a.opcoes[0])
|
|
626
1058
|
throw semProva('o dono não confirmou pelo diálogo nativo do host');
|
|
@@ -641,8 +1073,11 @@ function criarDialogoDoArco(raiz, origem, elicitar) {
|
|
|
641
1073
|
const d = derivar(lerLog(raiz, arco).linhas);
|
|
642
1074
|
return propostaAutorizavel(d, envelopeSha256, new Date().toISOString());
|
|
643
1075
|
});
|
|
644
|
-
const
|
|
645
|
-
|
|
1076
|
+
const resumo = (0, noctum_envelope_1.resumoDoEnvelope)(antes.conteudo, envelopeSha256), quando = new Date().toISOString();
|
|
1077
|
+
const pergunta = { contrato: noctum_pergunta_1.CONTRATO_PERGUNTA_DO_ARCO, arco, ato: 'autorizar', codigo: antes.codigo ?? '',
|
|
1078
|
+
versao: antes.versao, envelopeSha256, criadaEm: antes.em, prazo: antes.prazo, conteudo: antes.conteudo };
|
|
1079
|
+
const a = { contrato: 'ork.noctum-dialogo/v1', arco, ato: 'autorizar', envelopeSha256, resumo,
|
|
1080
|
+
mensagem: [(0, noctum_pergunta_1.textoDoDialogoDoArco)(pergunta, quando), '', ...resumo].join('\n'), opcoes: opcoesDoDialogo('autorizar') };
|
|
646
1081
|
const { requestId, recebidoEm } = await perguntar(a, antes.prazo);
|
|
647
1082
|
// O mundo pode ter mudado enquanto o dono lia: tudo é conferido de novo sob o lock.
|
|
648
1083
|
return comLockDoArcoAssincrono(raiz, arco, () => {
|
|
@@ -660,9 +1095,13 @@ function criarDialogoDoArco(raiz, origem, elicitar) {
|
|
|
660
1095
|
const atual = lerArco(raiz, arco);
|
|
661
1096
|
if (!atual)
|
|
662
1097
|
throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.sem-autorizacao', `arco ${arco} sem proposta`);
|
|
663
|
-
const
|
|
664
|
-
|
|
665
|
-
|
|
1098
|
+
const resumo = [`Revogar o arco ${arco}: nenhum efeito novo passa depois disto, e o arco não reabre`];
|
|
1099
|
+
const quando = new Date().toISOString();
|
|
1100
|
+
const pergunta = { contrato: noctum_pergunta_1.CONTRATO_PERGUNTA_DO_ARCO, arco, ato: 'revogar', codigo: atual.revogacaoPedida?.codigo ?? '',
|
|
1101
|
+
versao: atual.versao, envelopeSha256: atual.envelopeSha256, criadaEm: quando,
|
|
1102
|
+
prazo: atual.revogacaoPedida?.prazo ?? new Date(Date.parse(quando) + ESPERA_DO_DIALOGO_MS).toISOString(), conteudo: atual.conteudo };
|
|
1103
|
+
const a = { contrato: 'ork.noctum-dialogo/v1', arco, ato: 'revogar', envelopeSha256: atual.envelopeSha256, resumo,
|
|
1104
|
+
mensagem: [(0, noctum_pergunta_1.textoDoDialogoDoArco)(pergunta, quando), '', ...resumo].join('\n'), opcoes: opcoesDoDialogo('revogar') };
|
|
666
1105
|
const { requestId, recebidoEm } = await perguntar(a, undefined);
|
|
667
1106
|
return comLockDoArcoAssincrono(raiz, arco, () => {
|
|
668
1107
|
const log = lerLog(raiz, arco), d = derivar(log.linhas);
|
|
@@ -745,6 +1184,17 @@ function lerArco(raiz, arco, quando = new Date().toISOString()) {
|
|
|
745
1184
|
const log = lerLog(raiz, arco);
|
|
746
1185
|
return log.linhas.length ? estadoDe(arco, derivar(log.linhas), quando) : null;
|
|
747
1186
|
}
|
|
1187
|
+
/**
|
|
1188
|
+
* RM-058, integração (D3 da ork-rm058integra): a guarda das threads do arco (`noctum-guarda.ts`) lê a lista de trabalhos
|
|
1189
|
+
* do envelope vigente e a da proposta no prazo; o estado só traz o hash e o prazo dela. `null` quando o arco não existe.
|
|
1190
|
+
*/
|
|
1191
|
+
function lerArcoComProposta(raiz, arco, quando = new Date().toISOString()) {
|
|
1192
|
+
const log = lerLog(raiz, arco);
|
|
1193
|
+
if (!log.linhas.length)
|
|
1194
|
+
return null;
|
|
1195
|
+
const d = derivar(log.linhas), estado = estadoDe(arco, d, quando);
|
|
1196
|
+
return { estado, proposta: estado.propostaPendente ? d.propostas.get(estado.propostaPendente.versao).conteudo : null };
|
|
1197
|
+
}
|
|
748
1198
|
/**
|
|
749
1199
|
* A conferência imediatamente antes de um efeito. Relê o disco, sem cache, e recusa na ordem:
|
|
750
1200
|
* ação excluída, estado adulterado (inclusive cauda incompleta), revogação, falta de autorização,
|
|
@@ -775,6 +1225,11 @@ function vigenteConferido(raiz, arco, envelopeSha256, log) {
|
|
|
775
1225
|
throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.revogado', `arco ${arco} revogado pelo dono`);
|
|
776
1226
|
if (!d.autorizacao)
|
|
777
1227
|
throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.sem-autorizacao', `arco ${arco} sem envelope autorizado`);
|
|
1228
|
+
// RM-058 N6 (D2): encerrado é terminal como o revogado; reserva devolvida depois do teto não o reabre.
|
|
1229
|
+
if (d.encerramento) {
|
|
1230
|
+
throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.encerrado', `arco ${arco} encerrado (${d.encerramento.motivo}) em ${(0, horario_1.formatarDataHora)(d.encerramento.em)}; ` +
|
|
1231
|
+
'nenhum despacho ou efeito novo passa, e o arco não reabre');
|
|
1232
|
+
}
|
|
778
1233
|
// A prova do dono é reconferida aqui, pelo recibo do canal, e não lida da linha do log (FX2 no gate).
|
|
779
1234
|
const prova = reciboConfere(raiz, arco, 'autorizar', d.autorizacao.envelopeSha256, d.autorizacao.versao, d.autorizacao.ingresso);
|
|
780
1235
|
if (prova === 'sem-verificador') {
|
|
@@ -824,8 +1279,9 @@ function threadsDoTrabalho(raiz, item, texto) {
|
|
|
824
1279
|
/**
|
|
825
1280
|
* D11 e D12 da ork-rm058n2: confere o envelope e reserva o despacho, as duas coisas sob o lock do arco, imediatamente
|
|
826
1281
|
* antes do efeito. A cadeia da N1 (`vigenteConferido`) mais a janela, o trabalho na lista `v2`, a conta e o runtime do
|
|
827
|
-
* envelope e o orçamento: cada despacho reserva uma sessão no teto do arco e nos subtetos da conta e do runtime
|
|
828
|
-
*
|
|
1282
|
+
* envelope e o orçamento: cada despacho reserva uma sessão no teto do arco e nos subtetos da conta e do runtime; teto em
|
|
1283
|
+
* outra unidade confere o consumo medido que o passo traz (`pedido.medido`, D5 da ork-rm058n6) e, sem ele ou com lacuna,
|
|
1284
|
+
* recusa (`sem-medicao`). O trabalho de pedido só despacha a thread que o arco abriu
|
|
829
1285
|
* para ele, e só abre uma pelo GOAL quando nenhuma casa ainda; SHIP e MASTER nunca saem do arco da N2. A revogação usa o
|
|
830
1286
|
* mesmo lock: ou ela vem antes e nada é reservado, ou vem depois e vale para o despacho seguinte.
|
|
831
1287
|
*/
|
|
@@ -869,13 +1325,24 @@ function reservarDespacho(raiz, pedido, quando = new Date().toISOString()) {
|
|
|
869
1325
|
throw fora(`a conta ${String(pedido.conta)} não está no envelope`);
|
|
870
1326
|
}
|
|
871
1327
|
const consumo = consumoDe(d);
|
|
872
|
-
const tetos = [['do arco', c.orcamento.arco, consumo.arco],
|
|
873
|
-
[`da conta ${pedido.conta}`, tetoDaConta, consumo.porConta[pedido.conta] ?? 0],
|
|
874
|
-
[`do runtime ${pedido.runtime}`, tetoDoRuntime, consumo.porRuntime[pedido.runtime] ?? 0]];
|
|
875
|
-
for (const [nome, teto, usado] of tetos) {
|
|
1328
|
+
const tetos = [['do arco', c.orcamento.arco, consumo.arco, 'arco'],
|
|
1329
|
+
[`da conta ${pedido.conta}`, tetoDaConta, consumo.porConta[pedido.conta] ?? 0, 'conta'],
|
|
1330
|
+
[`do runtime ${pedido.runtime}`, tetoDoRuntime, consumo.porRuntime[pedido.runtime] ?? 0, 'runtime']];
|
|
1331
|
+
for (const [nome, teto, usado, chave] of tetos) {
|
|
876
1332
|
if (teto.unidade !== 'sessoes') {
|
|
877
|
-
|
|
878
|
-
|
|
1333
|
+
// RM-058 N6 (D5): fora de `sessoes`, o consumo medido fora do lock decide; sem medida, ou com lacuna, recusa.
|
|
1334
|
+
const medido = pedido.medido?.[chave];
|
|
1335
|
+
if (!medido || medido.unidade !== teto.unidade || 'lacuna' in medido || !Number.isFinite(medido.valor)) {
|
|
1336
|
+
throw new noctum_envelope_1.RecusaDoArco('noctum.orcamento.sem-medicao', `o teto ${nome} está em ${teto.unidade}, e o consumo nessa unidade não foi ` +
|
|
1337
|
+
`medido para esta reserva${medido && 'lacuna' in medido ? ` (${medido.lacuna})` : ''}: sem medir, o teto não é garantido, e ` +
|
|
1338
|
+
'consumo desconhecido nunca vale zero');
|
|
1339
|
+
}
|
|
1340
|
+
if (medido.valor >= teto.valor) {
|
|
1341
|
+
const valor = teto.unidade === 'percentual-da-cota' ? `${Math.round(medido.valor * 10) / 10}%` : String(medido.valor);
|
|
1342
|
+
throw new noctum_envelope_1.RecusaDoArco('noctum.orcamento.esgotado', `o teto ${nome} é de ${teto.valor} ${teto.unidade} e o consumo medido é ` +
|
|
1343
|
+
(medido.tetoSuperior ? `até ${valor} (teto superior: inclui uso de fora do arco)` : valor));
|
|
1344
|
+
}
|
|
1345
|
+
continue;
|
|
879
1346
|
}
|
|
880
1347
|
if (usado + 1 > teto.valor) {
|
|
881
1348
|
throw new noctum_envelope_1.RecusaDoArco('noctum.orcamento.esgotado', `o teto ${nome} é de ${teto.valor} sessoes e ${usado} já foram reservadas`);
|
|
@@ -889,6 +1356,26 @@ function reservarDespacho(raiz, pedido, quando = new Date().toISOString()) {
|
|
|
889
1356
|
return { arco: pedido.arco, seq, versao: vigente.versao, envelopeSha256: vigente.envelopeSha256, fim: c.janela.fim, consumo: estado.consumo };
|
|
890
1357
|
}, true);
|
|
891
1358
|
}
|
|
1359
|
+
/**
|
|
1360
|
+
* RM-058 N6 (D2 e D8 da ork-rm058n6): fecha o arco, uma vez, sob o lock do arco (espera como a revogação, porque é ato de
|
|
1361
|
+
* segurança). Depois disto `vigenteConferido` recusa todo despacho e efeito com `noctum.efeito.encerrado`, a emenda é
|
|
1362
|
+
* recusada e a fila encerra. O segundo fechamento devolve o primeiro. Só fecha arco que o dono autorizou; o revogado
|
|
1363
|
+
* também fecha, para o registro do fechamento (a situação continua `revogado`).
|
|
1364
|
+
*/
|
|
1365
|
+
function encerrarArco(raiz, arco, fechamento, quando = new Date().toISOString()) {
|
|
1366
|
+
if (!exports.MOTIVOS_DE_ENCERRAMENTO.includes(fechamento?.motivo)) {
|
|
1367
|
+
throw new noctum_envelope_1.RecusaDoArco('noctum.envelope.invalido', `motivo de encerramento fora de ${exports.MOTIVOS_DE_ENCERRAMENTO.join(', ')}`);
|
|
1368
|
+
}
|
|
1369
|
+
return comLockDoArco(raiz, arco, false, () => {
|
|
1370
|
+
const log = lerLog(raiz, arco), d = derivar(log.linhas);
|
|
1371
|
+
if (!d.autorizacao)
|
|
1372
|
+
throw new noctum_envelope_1.RecusaDoArco('noctum.efeito.sem-autorizacao', `arco ${arco} nunca autorizado não encerra`);
|
|
1373
|
+
if (d.encerramento)
|
|
1374
|
+
return estadoDe(arco, d, quando);
|
|
1375
|
+
anexar(raiz, arco, log, { tipo: 'encerrado', motivo: fechamento.motivo, detalhe: String(fechamento.detalhe ?? '').slice(0, 500) }, quando);
|
|
1376
|
+
return confirmado(raiz, arco, quando, e => e.encerramento !== null, 'encerramento');
|
|
1377
|
+
}, true);
|
|
1378
|
+
}
|
|
892
1379
|
/**
|
|
893
1380
|
* O desfecho da reserva, uma vez só: o despacho que abriu sessão (ou que a sessão em andamento já atendia), ou o que o
|
|
894
1381
|
* núcleo recusou, que devolve a sessão ao orçamento. Vale também depois de revogação ou expiração: é a contabilidade do
|
|
@@ -909,3 +1396,23 @@ function concluirDespacho(raiz, arco, seq, desfecho, quando = new Date().toISOSt
|
|
|
909
1396
|
return confirmado(raiz, arco, quando, e => e.despachos.some(x => x.seq === seq && x.desfecho !== 'pendente'), 'conclusão do despacho');
|
|
910
1397
|
}, true);
|
|
911
1398
|
}
|
|
1399
|
+
/**
|
|
1400
|
+
* RM-058 N3 (D6 da ork-rm058n3): as versões do envelope que o dono autorizou neste arco, na ordem do log. O ramo de
|
|
1401
|
+
* coordenação guarda registros assinados sob versões anteriores (a vaga tomada antes de uma emenda continua contando), e a
|
|
1402
|
+
* leitura confere cada um com as máquinas da versão que ele diz. A vigente é a última; o recibo do dono continua conferido
|
|
1403
|
+
* por `vigenteConferido`, antes de cada efeito.
|
|
1404
|
+
*/
|
|
1405
|
+
function versoesAutorizadas(raiz, arco) {
|
|
1406
|
+
const log = lerLog(raiz, arco), d = derivar(log.linhas);
|
|
1407
|
+
const vistas = new Set();
|
|
1408
|
+
const versoes = [];
|
|
1409
|
+
for (const { evento: e } of log.linhas) {
|
|
1410
|
+
if (e.tipo !== 'autorizado' || vistas.has(e.versao))
|
|
1411
|
+
continue;
|
|
1412
|
+
vistas.add(e.versao);
|
|
1413
|
+
const p = d.propostas.get(e.versao);
|
|
1414
|
+
// RM-058, integração (T8, D12): o instante do ingresso de cada autorização, que o aceite do piloto lê.
|
|
1415
|
+
versoes.push({ versao: p.versao, envelopeSha256: p.envelopeSha256, conteudo: p.conteudo, autorizadoEm: e.ingresso.recebidoEm });
|
|
1416
|
+
}
|
|
1417
|
+
return versoes;
|
|
1418
|
+
}
|