@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
|
@@ -0,0 +1,612 @@
|
|
|
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.LIMITE_DE_RECUSAS_NO_LOG = exports.INTERVALO_DA_VIGIA_MS = exports.PRAZO_DO_FECHO_MS = exports.LIMITE_DE_SESSOES_MCP = exports.LIMITE_DO_CORPO_BYTES = void 0;
|
|
37
|
+
exports.validarPedidoServidorGrok = validarPedidoServidorGrok;
|
|
38
|
+
exports.recusaDaRequisicao = recusaDaRequisicao;
|
|
39
|
+
exports.conferirArvore = conferirArvore;
|
|
40
|
+
exports.servirGrok = servirGrok;
|
|
41
|
+
/**
|
|
42
|
+
* RM-067 (F4c, thread ork-rm067f4cmcpd): o servidor MCP do `ork` da sessão filha do `grok`, num processo do `ork`, fora do
|
|
43
|
+
* sandbox do `grok`. O despacho o lança antes do `grok` (`iniciarServidorDoGrok`, em `grok-mcp.ts`); o `grok` se conecta
|
|
44
|
+
* pela forma `url` do guia oficial (G16), e o sandbox dele, que vale para ele e os filhos (G17), não chega aqui.
|
|
45
|
+
*
|
|
46
|
+
* - D1: Streamable HTTP do SDK, com sessão e resposta JSON, só em 127.0.0.1, porta efêmera, caminho `/mcp`;
|
|
47
|
+
* - D2: token de 256 bits do despacho, que chega só no ambiente; e, com o `grok` no sandbox, a conexão tem de vir da
|
|
48
|
+
* árvore de processos do `grok` deste despacho (GO-FIX 2, R1): o token vazado pela saída de uma ferramenta não serve
|
|
49
|
+
* a outro processo;
|
|
50
|
+
* - D3: endereço, caminho, `Host`, `Origin`, token e método conferidos antes de tudo (`recusaDaRequisicao`);
|
|
51
|
+
* - D4: só as ferramentas do `--allow` deste despacho, pela opção `ferramentas` de `criarServidorMcp`;
|
|
52
|
+
* - D5: o servidor morre com a sessão (o recibo do supervisor, o processo do `grok`, o prazo da partida ou um sinal) e,
|
|
53
|
+
* no fecho, para de aceitar conexão e espera a requisição e a ferramenta em andamento, com teto.
|
|
54
|
+
*/
|
|
55
|
+
const http = __importStar(require("node:http"));
|
|
56
|
+
const path = __importStar(require("node:path"));
|
|
57
|
+
const fs = __importStar(require("node:fs"));
|
|
58
|
+
const node_crypto_1 = require("node:crypto");
|
|
59
|
+
const zod_1 = require("zod");
|
|
60
|
+
const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
61
|
+
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
62
|
+
const mcp_server_1 = require("../mcp-server");
|
|
63
|
+
const mcp_transportes_1 = require("../mcp-transportes");
|
|
64
|
+
const codex_runner_1 = require("./codex-runner");
|
|
65
|
+
const grok_mcp_1 = require("./grok-mcp");
|
|
66
|
+
/** D3: o maior corpo de `POST` aceito; o `ork_artifact_write` leva até 131072 caracteres. */
|
|
67
|
+
exports.LIMITE_DO_CORPO_BYTES = 4 * 1024 * 1024;
|
|
68
|
+
/**
|
|
69
|
+
* D3: sessões MCP abertas ao mesmo tempo. A sessão do `grok` usa uma, e os subagentes herdam a dela (G16). Passado o
|
|
70
|
+
* limite, o `initialize` novo despeja a sessão ociosa usada há mais tempo (GO-FIX 1, achado 4), depois que a nova
|
|
71
|
+
* inicializa, e nunca a que tem pedido ou ferramenta em curso (GO-FIX 2, R2); sem ociosa, 503.
|
|
72
|
+
*/
|
|
73
|
+
exports.LIMITE_DE_SESSOES_MCP = 4;
|
|
74
|
+
/** D5: o teto da espera pela requisição e pela ferramenta em andamento no fecho (o `ork_verify` tem prazo de três minutos). */
|
|
75
|
+
exports.PRAZO_DO_FECHO_MS = 10 * 60_000;
|
|
76
|
+
/** D5: o intervalo da vigia sobre os recibos do supervisor. */
|
|
77
|
+
exports.INTERVALO_DA_VIGIA_MS = 250;
|
|
78
|
+
/**
|
|
79
|
+
* D2 (GO-FIX 3): a conexão do `grok` fica aberta entre os turnos do modelo, para a conferência da árvore não se repetir a
|
|
80
|
+
* cada ferramenta (o padrão do Node, 5 s, a derrubava).
|
|
81
|
+
*/
|
|
82
|
+
const CONEXAO_OCIOSA_MS = 60_000;
|
|
83
|
+
/** D2 (GO-FIX 4): as recusas da conferência da árvore que vão ao stderr por minuto; depois delas, uma linha só diz que há mais. */
|
|
84
|
+
exports.LIMITE_DE_RECUSAS_NO_LOG = 20;
|
|
85
|
+
const JANELA_DO_LOG_MS = 60_000;
|
|
86
|
+
/** D2 (GO-FIX 4): a varredura dos donos legíveis cede o event loop a cada tantos processos lidos. */
|
|
87
|
+
const PROCESSOS_POR_VEZ = 16;
|
|
88
|
+
const absoluto = zod_1.z.string().min(1).max(4096).refine((v) => path.isAbsolute(v), 'caminho absoluto');
|
|
89
|
+
const esquemaDoPedido = zod_1.z.object({
|
|
90
|
+
schema: zod_1.z.literal(grok_mcp_1.CONTRATO_DO_SERVIDOR_GROK),
|
|
91
|
+
projeto: absoluto,
|
|
92
|
+
threadId: zod_1.z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,79}$/),
|
|
93
|
+
dispatchId: zod_1.z.string().regex(/^[a-f0-9-]{36}$/),
|
|
94
|
+
permissoesFilho: zod_1.z.enum(['interactive', 'worktree']),
|
|
95
|
+
transporteShip: zod_1.z.string().refine(mcp_transportes_1.ehTransporteShipMcp, 'transporte SHIP desconhecido'),
|
|
96
|
+
ferramentas: zod_1.z.array(zod_1.z.string().regex(/^[a-z][a-z0-9_]{0,63}$/)).min(1).max(64),
|
|
97
|
+
conferirProcesso: zod_1.z.boolean(),
|
|
98
|
+
logPath: absoluto,
|
|
99
|
+
reciboPath: absoluto,
|
|
100
|
+
prazoDaPartidaMs: zod_1.z.number().int().min(1000).max(600_000).optional(),
|
|
101
|
+
}).strict();
|
|
102
|
+
/** O pedido do despacho, conferido; fora do contrato recusa. */
|
|
103
|
+
function validarPedidoServidorGrok(bruto) {
|
|
104
|
+
return esquemaDoPedido.parse(bruto);
|
|
105
|
+
}
|
|
106
|
+
/** O token do despacho: 32 bytes em hexadecimal. */
|
|
107
|
+
const TOKEN = /^[a-f0-9]{64}$/;
|
|
108
|
+
/**
|
|
109
|
+
* D3: a conferência de cada requisição, antes de qualquer leitura de corpo ou sessão, nesta ordem: endereço remoto do
|
|
110
|
+
* loopback (403), caminho exato (404), `Host` exato (403, contra DNS rebinding), nenhum `Origin` (403, o navegador não
|
|
111
|
+
* entra), o `Authorization` inteiro em tempo constante (401) e o método (405). `GET` fica de fora: o servidor não abre
|
|
112
|
+
* fluxo SSE próprio, e a especificação do Streamable HTTP manda responder 405 nesse caso.
|
|
113
|
+
*/
|
|
114
|
+
function recusaDaRequisicao(req, porta, esperado) {
|
|
115
|
+
const remoto = req.socket?.remoteAddress ?? '';
|
|
116
|
+
if (remoto !== grok_mcp_1.ENDERECO_DO_SERVIDOR_GROK && remoto !== `::ffff:${grok_mcp_1.ENDERECO_DO_SERVIDOR_GROK}`)
|
|
117
|
+
return { status: 403, motivo: 'mcp.grok.loopback: o servidor do grok so atende conexao local' };
|
|
118
|
+
if (req.url !== grok_mcp_1.CAMINHO_DO_SERVIDOR_GROK)
|
|
119
|
+
return { status: 404, motivo: 'mcp.grok.path: caminho fora do servidor do grok' };
|
|
120
|
+
if (req.headers.host !== `${grok_mcp_1.ENDERECO_DO_SERVIDOR_GROK}:${porta}`)
|
|
121
|
+
return { status: 403, motivo: 'mcp.grok.host: Host fora do servidor local do grok' };
|
|
122
|
+
if (req.headers.origin !== undefined)
|
|
123
|
+
return { status: 403, motivo: 'mcp.grok.origin: pedido de navegador recusado' };
|
|
124
|
+
const dado = Buffer.from(String(req.headers.authorization ?? ''), 'utf8');
|
|
125
|
+
if (dado.length !== esperado.length || !(0, node_crypto_1.timingSafeEqual)(dado, esperado))
|
|
126
|
+
return { status: 401, motivo: 'mcp.grok.token: token ausente ou de outra sessao' };
|
|
127
|
+
if (req.method !== 'POST' && req.method !== 'DELETE')
|
|
128
|
+
return { status: 405, motivo: 'mcp.grok.method: o servidor do grok aceita POST e DELETE' };
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
/** O endereço `127.0.0.1:<porta>` como o `/proc/net/tcp` o escreve, nas duas ordens de byte. */
|
|
132
|
+
function enderecoDoProc(porta) {
|
|
133
|
+
const p = porta.toString(16).toUpperCase().padStart(4, '0');
|
|
134
|
+
return [`0100007F:${p}`, `7F000001:${p}`];
|
|
135
|
+
}
|
|
136
|
+
/** O endereço v4-mapeado `::ffff:127.0.0.1:<porta>` como o `/proc/net/tcp6` o escreve, nas duas ordens de byte. */
|
|
137
|
+
function enderecoMapeadoDoProc(porta) {
|
|
138
|
+
const p = porta.toString(16).toUpperCase().padStart(4, '0');
|
|
139
|
+
return [`0000000000000000FFFF00000100007F:${p}`, `00000000000000000000FFFF7F000001:${p}`];
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* O inode do socket do lado do cliente de uma conexão local: a linha cujo endereço local é a porta do cliente, no
|
|
143
|
+
* `/proc/net/tcp` ou, para o cliente IPv6 que conecta ao endereço v4-mapeado, no `/proc/net/tcp6` (GO-FIX 3).
|
|
144
|
+
*/
|
|
145
|
+
function inodeDoCliente(portaDoCliente, portaDoServidor) {
|
|
146
|
+
const tabelas = [
|
|
147
|
+
['/proc/net/tcp', enderecoDoProc(portaDoCliente), enderecoDoProc(portaDoServidor)],
|
|
148
|
+
['/proc/net/tcp6', enderecoMapeadoDoProc(portaDoCliente), enderecoMapeadoDoProc(portaDoServidor)],
|
|
149
|
+
];
|
|
150
|
+
for (const [tabela, local, remoto] of tabelas) {
|
|
151
|
+
let texto;
|
|
152
|
+
try {
|
|
153
|
+
texto = fs.readFileSync(tabela, 'utf8');
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
for (const linha of texto.split('\n').slice(1)) {
|
|
159
|
+
const c = linha.trim().split(/\s+/);
|
|
160
|
+
if (local.includes(c[1]) && remoto.includes(c[2]) && /^[1-9]\d*$/.test(c[9] ?? ''))
|
|
161
|
+
return c[9];
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Os descendentes do processo (GO-FIX 3): pelos `children` de cada tarefa, que o kernel mantém; sem eles, pela varredura
|
|
168
|
+
* do pai de cada processo em `/proc/<pid>/stat`.
|
|
169
|
+
*/
|
|
170
|
+
function descendentes(raiz) {
|
|
171
|
+
const achados = [];
|
|
172
|
+
const pelasTarefas = (pid) => {
|
|
173
|
+
try {
|
|
174
|
+
return fs.readdirSync(`/proc/${pid}/task`).flatMap((tarefa) => fs.readFileSync(`/proc/${pid}/task/${tarefa}/children`, 'utf8').trim().split(/\s+/).filter(Boolean).map(Number));
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
if (pelasTarefas(raiz) !== null) {
|
|
181
|
+
const fila = [raiz];
|
|
182
|
+
while (fila.length)
|
|
183
|
+
for (const filho of pelasTarefas(fila.shift()) ?? []) {
|
|
184
|
+
achados.push(filho);
|
|
185
|
+
fila.push(filho);
|
|
186
|
+
}
|
|
187
|
+
return achados;
|
|
188
|
+
}
|
|
189
|
+
const filhos = new Map();
|
|
190
|
+
for (const nome of fs.readdirSync('/proc')) {
|
|
191
|
+
if (!/^\d+$/.test(nome))
|
|
192
|
+
continue;
|
|
193
|
+
try {
|
|
194
|
+
const stat = fs.readFileSync(`/proc/${nome}/stat`, 'utf8');
|
|
195
|
+
const pai = Number(stat.slice(stat.lastIndexOf(')') + 2).split(' ')[1]);
|
|
196
|
+
filhos.set(pai, [...(filhos.get(pai) ?? []), Number(nome)]);
|
|
197
|
+
}
|
|
198
|
+
catch { /* o processo saiu durante a leitura */ }
|
|
199
|
+
}
|
|
200
|
+
const fila = [raiz];
|
|
201
|
+
while (fila.length)
|
|
202
|
+
for (const filho of filhos.get(fila.shift()) ?? []) {
|
|
203
|
+
achados.push(filho);
|
|
204
|
+
fila.push(filho);
|
|
205
|
+
}
|
|
206
|
+
return achados;
|
|
207
|
+
}
|
|
208
|
+
/** O processo segura este socket entre os descritores dele? `null` quando os descritores não se leem (EACCES, EPERM). */
|
|
209
|
+
function seguraOSocket(pid, inode) {
|
|
210
|
+
const alvo = `socket:[${inode}]`;
|
|
211
|
+
let descritores;
|
|
212
|
+
try {
|
|
213
|
+
descritores = fs.readdirSync(`/proc/${pid}/fd`);
|
|
214
|
+
}
|
|
215
|
+
catch (e) {
|
|
216
|
+
const codigo = e.code;
|
|
217
|
+
return codigo === 'EACCES' || codigo === 'EPERM' ? null : false;
|
|
218
|
+
}
|
|
219
|
+
return descritores.some((fd) => { try {
|
|
220
|
+
return fs.readlinkSync(`/proc/${pid}/fd/${fd}`) === alvo;
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
return false;
|
|
224
|
+
} });
|
|
225
|
+
}
|
|
226
|
+
const FORA_DA_ARVORE = { aceita: false, motivo: 'conexao de processo fora da arvore do grok' };
|
|
227
|
+
/**
|
|
228
|
+
* D2 (GO-FIX 2, R1; GO-FIX 3 e 4): a conexão vem da árvore de processos do `grok` deste despacho? O `grok` vivo sai do
|
|
229
|
+
* `.process.json` do supervisor, já lido por quem chama (pid, início e boot); o socket do cliente, do `/proc/net/tcp` ou do
|
|
230
|
+
* `tcp6`; e o dono dele, dos descritores do `grok` primeiro (o caso esperado: a forma `url` não abre subprocesso, G16, e o
|
|
231
|
+
* agente com sandbox roda no próprio processo, G17) e depois dos descendentes. O token vazado pela saída de uma ferramenta
|
|
232
|
+
* da sessão chega a outro processo, mas a conexão dele não sai dessa árvore. Se o próprio `grok` não deixa ler os
|
|
233
|
+
* descritores (não despejável), o socket se procura entre os outros processos que se leem (GO-FIX 4): o de fora da árvore
|
|
234
|
+
* que o segura é recusado; sem dono legível, vale só o token, e o veredito diz isso para o log.
|
|
235
|
+
*/
|
|
236
|
+
async function conferirArvore(processo, portaDoCliente, portaDoServidor) {
|
|
237
|
+
if (!processo.filho || (0, codex_runner_1.estadoProcesso)(processo.filho) !== 'vivo')
|
|
238
|
+
return { aceita: false, motivo: 'processo do grok ausente' };
|
|
239
|
+
const inode = inodeDoCliente(portaDoCliente, portaDoServidor);
|
|
240
|
+
if (!inode)
|
|
241
|
+
return { aceita: false, motivo: 'socket do cliente fora do /proc/net/tcp e do tcp6' };
|
|
242
|
+
const grok = processo.filho.pid;
|
|
243
|
+
const doGrok = seguraOSocket(grok, inode);
|
|
244
|
+
if (doGrok)
|
|
245
|
+
return { aceita: true };
|
|
246
|
+
const arvore = descendentes(grok);
|
|
247
|
+
if (arvore.some((pid) => seguraOSocket(pid, inode)))
|
|
248
|
+
return { aceita: true };
|
|
249
|
+
if (doGrok === false)
|
|
250
|
+
return FORA_DA_ARVORE;
|
|
251
|
+
return (await donoLegivelDeFora(inode, new Set([grok, ...arvore]))) ? FORA_DA_ARVORE : { aceita: true, soToken: true };
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* D2 (GO-FIX 4): com o `grok` não despejável, algum processo do mesmo usuário, fora da árvore, segura o socket? A varredura
|
|
255
|
+
* cede o event loop a cada `PROCESSOS_POR_VEZ` processos lidos, para a conexão nova não travar as respostas da sessão, e
|
|
256
|
+
* pula sem ler o processo de outro dono (o não despejável tem o `/proc/<pid>` do root, e os descritores dele não se leem).
|
|
257
|
+
*/
|
|
258
|
+
async function donoLegivelDeFora(inode, naArvore) {
|
|
259
|
+
const usuario = process.getuid?.();
|
|
260
|
+
let lidos = 0;
|
|
261
|
+
for (const nome of fs.readdirSync('/proc')) {
|
|
262
|
+
if (!/^\d+$/.test(nome) || naArvore.has(Number(nome)))
|
|
263
|
+
continue;
|
|
264
|
+
try {
|
|
265
|
+
if (usuario !== undefined && fs.statSync(`/proc/${nome}`).uid !== usuario)
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
catch {
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
if (seguraOSocket(Number(nome), inode))
|
|
272
|
+
return true;
|
|
273
|
+
if (++lidos % PROCESSOS_POR_VEZ === 0)
|
|
274
|
+
await new Promise((r) => setImmediate(r));
|
|
275
|
+
}
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
function responder(res, status, motivo, cabecalhos = {}) {
|
|
279
|
+
if (res.destroyed || res.socket?.destroyed)
|
|
280
|
+
return;
|
|
281
|
+
if (res.headersSent) {
|
|
282
|
+
res.end();
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
res.writeHead(status, { 'Content-Type': 'application/json', ...cabecalhos });
|
|
286
|
+
res.end(JSON.stringify({ jsonrpc: '2.0', error: { code: -32000, message: motivo }, id: null }));
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* O corpo inteiro até o limite; acima dele, `null` (o pedido recebe 413). O excesso é drenado sem guardar, para a resposta
|
|
290
|
+
* chegar a quem ainda envia; acima de quatro vezes o limite, a conexão cai. Só a sessão com o token chega aqui.
|
|
291
|
+
*/
|
|
292
|
+
function lerCorpo(req, limite) {
|
|
293
|
+
return new Promise((resolve, reject) => {
|
|
294
|
+
const partes = [];
|
|
295
|
+
let total = 0;
|
|
296
|
+
req.on('data', (parte) => {
|
|
297
|
+
total += parte.length;
|
|
298
|
+
if (total > limite * 4) {
|
|
299
|
+
req.destroy();
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
if (total <= limite)
|
|
303
|
+
partes.push(parte);
|
|
304
|
+
});
|
|
305
|
+
req.on('end', () => resolve(total > limite ? null : Buffer.concat(partes)));
|
|
306
|
+
req.on('close', () => resolve(null));
|
|
307
|
+
req.on('error', reject);
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
/** O recibo do supervisor no disco, sem seguir link. */
|
|
311
|
+
function existe(arquivo) {
|
|
312
|
+
return fs.lstatSync(arquivo, { throwIfNoEntry: false }) !== undefined;
|
|
313
|
+
}
|
|
314
|
+
/** Roda um passo do fecho sem deixar a exceção dele impedir os seguintes. */
|
|
315
|
+
function semFalhar(passo) {
|
|
316
|
+
try {
|
|
317
|
+
passo();
|
|
318
|
+
}
|
|
319
|
+
catch { /* o fecho segue: a porta e o recibo vêm depois */ }
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Sobe o servidor do pedido com o token do despacho: confere o projeto, a thread e a lista criando um servidor MCP antes de
|
|
323
|
+
* abrir a porta, escuta no loopback e grava o recibo `pronto`. Cada sessão MCP (`initialize`) ganha o próprio servidor do
|
|
324
|
+
* núcleo, vinculado à thread e ao despacho; a vigia fecha tudo quando a sessão do `grok` termina.
|
|
325
|
+
*/
|
|
326
|
+
async function servirGrok(pedido, token) {
|
|
327
|
+
if (!TOKEN.test(token))
|
|
328
|
+
throw new Error('mcp.grok.token: token do despacho ausente ou fora do formato');
|
|
329
|
+
// D5 (GO-FIX 1, achado 2): conta a requisição até a resposta fechar (enviada ou com a conexão caída) e, à parte, a
|
|
330
|
+
// ferramenta até ela assentar; a cancelada que nunca responde não segura o fecho, e o commit em curso não é cortado.
|
|
331
|
+
let emAndamento = 0, fechando = false;
|
|
332
|
+
const fabricar = (sessao) => (0, mcp_server_1.criarServidorMcp)({ projeto: pedido.projeto, host: 'grok',
|
|
333
|
+
threadId: pedido.threadId, dispatchId: pedido.dispatchId, permissoesFilho: pedido.permissoesFilho,
|
|
334
|
+
transporteShip: pedido.transporteShip, ferramentas: pedido.ferramentas,
|
|
335
|
+
emCurso: (delta) => { emAndamento += delta; if (sessao)
|
|
336
|
+
sessao.emCurso += delta; } });
|
|
337
|
+
await fabricar(null).close();
|
|
338
|
+
const sessoes = new Map();
|
|
339
|
+
const esperado = Buffer.from(`Bearer ${token}`, 'utf8');
|
|
340
|
+
// D2 (GO-FIX 2, R1; GO-FIX 3): a conferência da árvore por socket. Só o aceite fica guardado; a recusa só vem depois de
|
|
341
|
+
// uma conferência que viu o `.process.json`, que o supervisor grava logo depois do `grok`, esperado até o prazo da partida.
|
|
342
|
+
const aceitas = new WeakMap();
|
|
343
|
+
let avisouSoToken = false;
|
|
344
|
+
// GO-FIX 4: o log das recusas tem teto por minuto, para quem tem o token vazado não encher o stderr abrindo conexões
|
|
345
|
+
// nem calar de vez a recusa que vier depois.
|
|
346
|
+
let recusas = 0, janelaDesde = Date.now();
|
|
347
|
+
const anotarRecusa = (motivo) => {
|
|
348
|
+
if (Date.now() - janelaDesde >= JANELA_DO_LOG_MS) {
|
|
349
|
+
recusas = 0;
|
|
350
|
+
janelaDesde = Date.now();
|
|
351
|
+
}
|
|
352
|
+
recusas++;
|
|
353
|
+
if (recusas <= exports.LIMITE_DE_RECUSAS_NO_LOG)
|
|
354
|
+
process.stderr.write(`grok-mcp: conexao recusada: ${motivo}\n`);
|
|
355
|
+
else if (recusas === exports.LIMITE_DE_RECUSAS_NO_LOG + 1)
|
|
356
|
+
process.stderr.write('grok-mcp: mais conexoes recusadas neste minuto, sem log\n');
|
|
357
|
+
};
|
|
358
|
+
const conferirConexao = (socket) => {
|
|
359
|
+
const pendente = aceitas.get(socket);
|
|
360
|
+
if (pendente)
|
|
361
|
+
return pendente;
|
|
362
|
+
// GO-FIX 4: a conferência nunca rejeita; o erro dela (um EMFILE na leitura do `/proc`) recusa só esta conexão, sem a
|
|
363
|
+
// exceção não tratada derrubar o servidor da sessão.
|
|
364
|
+
const veredito = (async () => {
|
|
365
|
+
try {
|
|
366
|
+
const limite = Date.now() + (pedido.prazoDaPartidaMs ?? grok_mcp_1.PRAZO_DA_PARTIDA_MS);
|
|
367
|
+
let processo = (0, grok_mcp_1.lerJsonPequeno)(`${pedido.logPath}.process.json`);
|
|
368
|
+
while (!processo && Date.now() < limite) {
|
|
369
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
370
|
+
processo = (0, grok_mcp_1.lerJsonPequeno)(`${pedido.logPath}.process.json`);
|
|
371
|
+
}
|
|
372
|
+
if (!processo) {
|
|
373
|
+
anotarRecusa('sem o .process.json do supervisor no prazo');
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
376
|
+
const v = await conferirArvore(processo, socket.remotePort ?? 0, socket.localPort ?? 0);
|
|
377
|
+
if (!v.aceita) {
|
|
378
|
+
anotarRecusa(v.motivo);
|
|
379
|
+
return false;
|
|
380
|
+
}
|
|
381
|
+
if (v.soToken && !avisouSoToken) {
|
|
382
|
+
avisouSoToken = true;
|
|
383
|
+
process.stderr.write('grok-mcp: o grok nao deixa ler os descritores dele; a conexao vale so pelo token\n');
|
|
384
|
+
}
|
|
385
|
+
return true;
|
|
386
|
+
}
|
|
387
|
+
catch (e) {
|
|
388
|
+
const erro = e;
|
|
389
|
+
anotarRecusa(`erro na conferencia (${erro?.code ?? String(erro?.message ?? e).split('\n')[0].slice(0, 200)})`);
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
})();
|
|
393
|
+
aceitas.set(socket, veredito);
|
|
394
|
+
void veredito.then((aceita) => { if (!aceita)
|
|
395
|
+
aceitas.delete(socket); }, () => { aceitas.delete(socket); });
|
|
396
|
+
return veredito;
|
|
397
|
+
};
|
|
398
|
+
// GO-FIX 5: um ouvinte de `close` por socket, que solta os pedidos pendentes dele, e não um por pedido: com dez ou mais
|
|
399
|
+
// pedidos em pipeline, o aviso de ouvintes demais do Node iria ao log por fora do teto das recusas.
|
|
400
|
+
const pendentesPorSocket = new WeakMap();
|
|
401
|
+
const aoFecharOSocket = (socket, soltar) => {
|
|
402
|
+
let pendentes = pendentesPorSocket.get(socket);
|
|
403
|
+
if (!pendentes) {
|
|
404
|
+
const novos = new Set();
|
|
405
|
+
pendentesPorSocket.set(socket, novos);
|
|
406
|
+
socket.once('close', () => { for (const f of [...novos])
|
|
407
|
+
f(); });
|
|
408
|
+
pendentes = novos;
|
|
409
|
+
}
|
|
410
|
+
const doSocket = pendentes;
|
|
411
|
+
doSocket.add(soltar);
|
|
412
|
+
return () => { doSocket.delete(soltar); };
|
|
413
|
+
};
|
|
414
|
+
let porta = 0;
|
|
415
|
+
const servidor = http.createServer((req, res) => { void atender(req, res); });
|
|
416
|
+
servidor.keepAliveTimeout = CONEXAO_OCIOSA_MS;
|
|
417
|
+
servidor.headersTimeout = CONEXAO_OCIOSA_MS + 5000;
|
|
418
|
+
async function atender(req, res) {
|
|
419
|
+
const recusa = recusaDaRequisicao(req, porta, esperado);
|
|
420
|
+
if (recusa) {
|
|
421
|
+
responder(res, recusa.status, recusa.motivo, recusa.status === 401 ? { 'WWW-Authenticate': 'Bearer' }
|
|
422
|
+
: recusa.status === 405 ? { Allow: 'POST, DELETE' } : {});
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
if (fechando) {
|
|
426
|
+
responder(res, 503, 'mcp.grok.closing: a sessao do grok terminou');
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
const id = req.headers['mcp-session-id'];
|
|
430
|
+
// GO-FIX 4: o pedido conta até a resposta fechar ou, no pipeline cuja resposta nunca ganha o socket (depois de um
|
|
431
|
+
// `Connection: close`, ou com o cliente caindo no meio), até o socket fechar; uma vez só, na sessão também.
|
|
432
|
+
let contada;
|
|
433
|
+
let descontado = false;
|
|
434
|
+
let largarDoSocket = () => undefined;
|
|
435
|
+
const descontar = () => {
|
|
436
|
+
if (descontado)
|
|
437
|
+
return;
|
|
438
|
+
descontado = true;
|
|
439
|
+
emAndamento--;
|
|
440
|
+
if (contada)
|
|
441
|
+
contada.emCurso--;
|
|
442
|
+
largarDoSocket();
|
|
443
|
+
};
|
|
444
|
+
emAndamento++;
|
|
445
|
+
res.once('close', descontar);
|
|
446
|
+
largarDoSocket = aoFecharOSocket(req.socket, descontar);
|
|
447
|
+
try {
|
|
448
|
+
// GO-FIX 4: a recusa fecha a conexão, e cada nova tentativa custa outra conexão e outra conferência.
|
|
449
|
+
if (pedido.conferirProcesso && !(await conferirConexao(req.socket))) {
|
|
450
|
+
responder(res, 403, 'mcp.grok.process: conexao de processo fora da sessao do grok deste despacho', { Connection: 'close' });
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
// GO-FIX 3: a sessão só conta o pedido que passou pela conferência; o recusado não mexe no despejo dela.
|
|
454
|
+
const sessao = typeof id === 'string' ? sessoes.get(id) : undefined;
|
|
455
|
+
if (sessao && !descontado) {
|
|
456
|
+
contada = sessao;
|
|
457
|
+
sessao.emCurso++;
|
|
458
|
+
sessao.usadaEm = Date.now();
|
|
459
|
+
}
|
|
460
|
+
let corpo;
|
|
461
|
+
if (req.method === 'POST') {
|
|
462
|
+
const lido = await lerCorpo(req, exports.LIMITE_DO_CORPO_BYTES);
|
|
463
|
+
if (lido === null) {
|
|
464
|
+
responder(res, 413, 'mcp.grok.body: corpo acima do limite', { Connection: 'close' });
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
try {
|
|
468
|
+
corpo = JSON.parse(lido.toString('utf8'));
|
|
469
|
+
}
|
|
470
|
+
catch {
|
|
471
|
+
responder(res, 400, 'mcp.grok.body: corpo nao e JSON');
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
if (typeof id === 'string') {
|
|
476
|
+
if (!sessao) {
|
|
477
|
+
responder(res, 404, 'mcp.grok.session: sessao MCP desconhecida');
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
await sessao.transporte.handleRequest(req, res, corpo);
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
if (req.method !== 'POST' || !(0, types_js_1.isInitializeRequest)(corpo)) {
|
|
484
|
+
responder(res, 400, 'mcp.grok.session: pedido sem sessao MCP');
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
// D3 (GO-FIX 2, R2): a vítima é a ociosa usada há mais tempo, e o despejo só vem depois que a nova inicializa.
|
|
488
|
+
let vitima = null;
|
|
489
|
+
if (sessoes.size >= exports.LIMITE_DE_SESSOES_MCP) {
|
|
490
|
+
const ociosas = [...sessoes.entries()].filter(([, s]) => s.emCurso === 0).sort((a, b) => a[1].usadaEm - b[1].usadaEm);
|
|
491
|
+
if (!ociosas.length) {
|
|
492
|
+
responder(res, 503, 'mcp.grok.session: limite de sessoes MCP, todas com pedido em curso');
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
vitima = ociosas[0][0];
|
|
496
|
+
}
|
|
497
|
+
const transporte = new streamableHttp_js_1.StreamableHTTPServerTransport({
|
|
498
|
+
sessionIdGenerator: () => (0, node_crypto_1.randomUUID)(), enableJsonResponse: true,
|
|
499
|
+
onsessioninitialized: (novo) => {
|
|
500
|
+
sessoes.set(novo, nova);
|
|
501
|
+
const despejada = vitima === null ? undefined : sessoes.get(vitima);
|
|
502
|
+
if (vitima !== null && despejada && despejada.emCurso === 0) {
|
|
503
|
+
sessoes.delete(vitima);
|
|
504
|
+
void despejada.transporte.close().catch(() => undefined);
|
|
505
|
+
}
|
|
506
|
+
},
|
|
507
|
+
});
|
|
508
|
+
const nova = { transporte, usadaEm: Date.now(), emCurso: 0 };
|
|
509
|
+
transporte.onclose = () => { if (transporte.sessionId && sessoes.get(transporte.sessionId) === nova)
|
|
510
|
+
sessoes.delete(transporte.sessionId); };
|
|
511
|
+
await fabricar(nova).connect(transporte);
|
|
512
|
+
await transporte.handleRequest(req, res, corpo);
|
|
513
|
+
}
|
|
514
|
+
catch {
|
|
515
|
+
responder(res, 500, 'mcp.grok.internal: erro no servidor do grok');
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
await new Promise((resolve, reject) => {
|
|
519
|
+
servidor.once('error', reject);
|
|
520
|
+
servidor.listen(0, grok_mcp_1.ENDERECO_DO_SERVIDOR_GROK, () => { servidor.off('error', reject); resolve(); });
|
|
521
|
+
});
|
|
522
|
+
porta = servidor.address().port;
|
|
523
|
+
const url = `http://${grok_mcp_1.ENDERECO_DO_SERVIDOR_GROK}:${porta}${grok_mcp_1.CAMINHO_DO_SERVIDOR_GROK}`;
|
|
524
|
+
const recibo = { schema: grok_mcp_1.CONTRATO_DO_SERVIDOR_GROK, estado: 'pronto', url, porta,
|
|
525
|
+
processo: (0, codex_runner_1.identidadeProcesso)(process.pid), pai: process.ppid, threadId: pedido.threadId, dispatchId: pedido.dispatchId,
|
|
526
|
+
conferirProcesso: pedido.conferirProcesso, iniciadoEm: new Date().toISOString() };
|
|
527
|
+
(0, codex_runner_1.gravarAtomico)(pedido.reciboPath, recibo);
|
|
528
|
+
let encerrado = () => undefined;
|
|
529
|
+
const fechado = new Promise((resolve) => { encerrado = resolve; });
|
|
530
|
+
const inicio = Date.now(), prazoDaPartida = pedido.prazoDaPartidaMs ?? grok_mcp_1.PRAZO_DA_PARTIDA_MS;
|
|
531
|
+
// GO-FIX 1 (achado 3): cada passo do fecho sem lançar, e a porta, o recibo e o fim sempre alcançados.
|
|
532
|
+
const fechar = (motivo) => {
|
|
533
|
+
if (fechando)
|
|
534
|
+
return;
|
|
535
|
+
fechando = true;
|
|
536
|
+
clearInterval(vigia);
|
|
537
|
+
semFalhar(() => servidor.close());
|
|
538
|
+
const limite = Date.now() + exports.PRAZO_DO_FECHO_MS;
|
|
539
|
+
const terminar = () => {
|
|
540
|
+
if (emAndamento > 0 && Date.now() < limite) {
|
|
541
|
+
setTimeout(terminar, 100);
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
try {
|
|
545
|
+
for (const { transporte } of sessoes.values())
|
|
546
|
+
void transporte.close().catch(() => undefined);
|
|
547
|
+
semFalhar(() => servidor.closeAllConnections());
|
|
548
|
+
semFalhar(() => (0, codex_runner_1.gravarAtomico)(pedido.reciboPath, { ...recibo, estado: 'encerrado', encerradoEm: new Date().toISOString(), motivo }));
|
|
549
|
+
}
|
|
550
|
+
finally {
|
|
551
|
+
encerrado(motivo);
|
|
552
|
+
}
|
|
553
|
+
};
|
|
554
|
+
terminar();
|
|
555
|
+
};
|
|
556
|
+
const vigiar = () => {
|
|
557
|
+
if (existe(`${pedido.logPath}.exit.json`)) {
|
|
558
|
+
fechar('recibo');
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
const processo = (0, grok_mcp_1.lerJsonPequeno)(`${pedido.logPath}.process.json`);
|
|
562
|
+
if (!processo) {
|
|
563
|
+
if (Date.now() - inicio > prazoDaPartida)
|
|
564
|
+
fechar('sem-partida');
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
if (!processo.filho || (0, codex_runner_1.estadoProcesso)(processo.filho) === 'ausente')
|
|
568
|
+
fechar('grok-ausente');
|
|
569
|
+
};
|
|
570
|
+
const vigia = setInterval(vigiar, exports.INTERVALO_DA_VIGIA_MS);
|
|
571
|
+
return { porta, url, fechar, fechado };
|
|
572
|
+
}
|
|
573
|
+
/** O processo lançado por `iniciarServidorDoGrok`: o pedido vem no descritor 0, o token no ambiente, e o fim é o do servidor. */
|
|
574
|
+
async function principal() {
|
|
575
|
+
// D2: o token sai do `process.env` antes de tudo, para nenhum processo que este servidor lance (o worker do commit, o
|
|
576
|
+
// verify) o herdar. O `/proc/<pid>/environ` deste processo guarda o valor de partida, que o Landlock nega à sessão irmã.
|
|
577
|
+
const token = process.env[grok_mcp_1.ENV_DO_TOKEN_DO_SERVIDOR_GROK] ?? '';
|
|
578
|
+
delete process.env[grok_mcp_1.ENV_DO_TOKEN_DO_SERVIDOR_GROK];
|
|
579
|
+
let emCurso = null;
|
|
580
|
+
const sair = (motivo) => {
|
|
581
|
+
if (emCurso) {
|
|
582
|
+
emCurso.fechar(motivo);
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
process.exit(1);
|
|
586
|
+
};
|
|
587
|
+
for (const sinal of ['SIGTERM', 'SIGINT', 'SIGHUP'])
|
|
588
|
+
process.on(sinal, () => sair('sinal'));
|
|
589
|
+
process.on('uncaughtException', (e) => {
|
|
590
|
+
process.stderr.write(`grok-mcp: ${String(e?.message ?? e).split('\n')[0]}\n`);
|
|
591
|
+
sair('erro');
|
|
592
|
+
});
|
|
593
|
+
let pedido;
|
|
594
|
+
try {
|
|
595
|
+
pedido = validarPedidoServidorGrok(JSON.parse(fs.readFileSync(0, 'utf8')));
|
|
596
|
+
}
|
|
597
|
+
catch (e) {
|
|
598
|
+
process.stderr.write(`grok-mcp: pedido invalido (${e.message.split('\n')[0]})\n`);
|
|
599
|
+
process.exit(2);
|
|
600
|
+
}
|
|
601
|
+
try {
|
|
602
|
+
emCurso = await servirGrok(pedido, token);
|
|
603
|
+
}
|
|
604
|
+
catch (e) {
|
|
605
|
+
process.stderr.write(`grok-mcp: ${e.message.split('\n')[0]}\n`);
|
|
606
|
+
process.exit(1);
|
|
607
|
+
}
|
|
608
|
+
await emCurso.fechado;
|
|
609
|
+
process.exit(0);
|
|
610
|
+
}
|
|
611
|
+
if (require.main === module && process.argv[2] === '--run')
|
|
612
|
+
void principal();
|