@orkastery/cli 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +86 -65
  3. package/adapters/README.md +30 -15
  4. package/adapters/claude-code/.claude-plugin/plugin.json +11 -5
  5. package/adapters/claude-code/README.md +110 -8
  6. package/adapters/claude-code/commands/onboarding.md +21 -0
  7. package/adapters/claude-code/commands/ork.md +99 -27
  8. package/adapters/claude-code/hooks/hooks.json +6 -1
  9. package/adapters/claude-code/hooks/ork-sensor.js +66 -0
  10. package/adapters/codex/skills/ork/SKILL.md +57 -0
  11. package/adapters/hermes/README.md +163 -0
  12. package/adapters/hermes/bin/ork-abrir-thread.sh +2 -0
  13. package/adapters/hermes/bin/ork-brain.sh +4 -0
  14. package/adapters/hermes/bin/ork-hitl-answer.py +82 -0
  15. package/adapters/hermes/bin/ork-maestro.sh +6 -0
  16. package/adapters/hermes/bin/ork-master-enviar.py +84 -0
  17. package/adapters/hermes/bin/ork-objective-message.sh +17 -0
  18. package/adapters/hermes/bin/ork-objective-status.sh +12 -0
  19. package/adapters/hermes/bin/ork-pulse-enviar.py +47 -0
  20. package/adapters/hermes/hermes.plugin.json +26 -4
  21. package/adapters/hermes/hitl-ingress/__init__.py +356 -0
  22. package/adapters/hermes/hitl-ingress/plugin.yaml +4 -0
  23. package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +75 -22
  24. package/adapters/openclaw/README.md +135 -26
  25. package/adapters/openclaw/bin/ork-brain.sh +4 -0
  26. package/adapters/openclaw/construir.sh +19 -0
  27. package/adapters/openclaw/dist/hitl-ingress.js +199 -0
  28. package/adapters/openclaw/dist/index.js +437 -0
  29. package/adapters/openclaw/openclaw.plugin.json +39 -134
  30. package/adapters/openclaw/package.json +26 -0
  31. package/adapters/openclaw/src/hitl-ingress.ts +180 -0
  32. package/adapters/openclaw/src/index.ts +458 -0
  33. package/adapters/openclaw/src/tipos-openclaw.d.ts +58 -0
  34. package/adapters/openclaw/tsconfig.json +15 -0
  35. package/assets/docs/markdownlint-cli2.jsonc +22 -0
  36. package/assets/docs/padroes/documentacao-de-produto.md +133 -0
  37. package/assets/docs/padroes/roadmap-de-produto.md +98 -0
  38. package/assets/docs/produto/README.md +14 -0
  39. package/assets/docs/produto/_modelo-feature.md +59 -0
  40. package/assets/docs/roadmap/README.md +14 -0
  41. package/assets/docs/roadmap/_modelo-item.md +73 -0
  42. package/assets/orkmind-native-schema.json +32 -0
  43. package/assets/orkmind_bridge.py +357 -0
  44. package/assets/orkmind_fixture.py +67 -0
  45. package/assets/orkmind_prospective.py +138 -0
  46. package/assets/reference-tariffs-i07.json +23 -0
  47. package/dist/adapters/claude-bg.js +488 -39
  48. package/dist/adapters/codex-controller-sensor.js +449 -0
  49. package/dist/adapters/codex-controller-worker.js +361 -0
  50. package/dist/adapters/codex-controller.js +252 -0
  51. package/dist/adapters/codex-events.js +205 -0
  52. package/dist/adapters/codex-question.js +26 -0
  53. package/dist/adapters/codex-runner.js +133 -0
  54. package/dist/adapters/codex.js +394 -0
  55. package/dist/agents-md.js +84 -0
  56. package/dist/auditoria.js +11 -8
  57. package/dist/auditrun.js +5 -4
  58. package/dist/board.js +173 -22
  59. package/dist/branch-de-estado.js +136 -0
  60. package/dist/canarios-hitl.js +177 -0
  61. package/dist/canarios-i43.js +543 -0
  62. package/dist/canarios-pulse.js +147 -0
  63. package/dist/canarios-sensores.js +129 -0
  64. package/dist/canarios.js +111 -2
  65. package/dist/catalogo.js +9 -0
  66. package/dist/ci.js +216 -0
  67. package/dist/ciclos.js +2 -1
  68. package/dist/claim-lint.js +64 -0
  69. package/dist/claims.js +60 -0
  70. package/dist/company-brain-capture.js +195 -0
  71. package/dist/company-brain-cli.js +123 -0
  72. package/dist/company-brain-client.js +61 -0
  73. package/dist/company-brain-contract.js +166 -0
  74. package/dist/company-brain-journal.js +192 -0
  75. package/dist/company-brain-mcp.js +33 -0
  76. package/dist/company-brain-migration.js +60 -0
  77. package/dist/company-brain-source.js +177 -0
  78. package/dist/company-brain-worker.js +18 -0
  79. package/dist/conducao-texto.js +61 -0
  80. package/dist/conducao.js +876 -0
  81. package/dist/contrato-publico.js +39 -0
  82. package/dist/creation-operation-store.js +251 -0
  83. package/dist/creation-operation.js +148 -0
  84. package/dist/decisao-autonoma.js +183 -0
  85. package/dist/delegation.js +79 -0
  86. package/dist/demo.js +120 -0
  87. package/dist/docs.js +828 -0
  88. package/dist/doctor.js +183 -24
  89. package/dist/entrega-pr.js +117 -0
  90. package/dist/escopo-escrita.js +71 -0
  91. package/dist/estado-thread.js +222 -0
  92. package/dist/evalrunner.js +12 -0
  93. package/dist/fabrica-estado.js +329 -0
  94. package/dist/fabrica-publicar.js +76 -0
  95. package/dist/fix.js +21 -0
  96. package/dist/gates.js +55 -9
  97. package/dist/handoff.js +39 -23
  98. package/dist/hitl-canais.js +333 -0
  99. package/dist/hitl-classificacao.js +131 -0
  100. package/dist/hitl-contract.js +465 -0
  101. package/dist/hitl-estado.js +136 -0
  102. package/dist/hitl-gates.js +554 -0
  103. package/dist/hitl-ingress-receipt.js +382 -0
  104. package/dist/hitl-local-atestado.js +57 -0
  105. package/dist/hitl-local-receipt.js +328 -0
  106. package/dist/hitl-local.js +143 -0
  107. package/dist/hitl-lock.js +139 -0
  108. package/dist/hitl-lote.js +223 -0
  109. package/dist/hitl-native-offer.js +97 -0
  110. package/dist/hitl-native.js +65 -0
  111. package/dist/hitl-presentation.js +209 -0
  112. package/dist/hitl-public-receipt.js +176 -0
  113. package/dist/hitl-resumo.js +209 -0
  114. package/dist/hitl-sessions.js +475 -0
  115. package/dist/hitl.js +711 -0
  116. package/dist/horario.js +269 -0
  117. package/dist/hosts.js +198 -22
  118. package/dist/index.js +1736 -93
  119. package/dist/indice.js +99 -0
  120. package/dist/init.js +28 -3
  121. package/dist/integracoes-locais.js +17 -0
  122. package/dist/leases.js +65 -21
  123. package/dist/ledger-stats.js +272 -0
  124. package/dist/ledger.js +107 -2
  125. package/dist/licoes.js +215 -0
  126. package/dist/liveness.js +218 -0
  127. package/dist/maestro-actions.js +51 -0
  128. package/dist/maestro-authority.js +152 -0
  129. package/dist/maestro-cli.js +97 -0
  130. package/dist/maestro-contract.js +55 -0
  131. package/dist/maestro-discovery.js +132 -0
  132. package/dist/maestro-runtime.js +268 -0
  133. package/dist/maestro-snapshot.js +86 -0
  134. package/dist/maestro-sources.js +224 -0
  135. package/dist/manifest.js +165 -6
  136. package/dist/maquina.js +102 -0
  137. package/dist/master-audit.js +101 -0
  138. package/dist/master-batch.js +43 -0
  139. package/dist/master-digest.js +149 -0
  140. package/dist/master-migracao.js +155 -0
  141. package/dist/master.js +361 -77
  142. package/dist/mcp-artifacts.js +234 -0
  143. package/dist/mcp-git.js +433 -0
  144. package/dist/mcp-install.js +310 -0
  145. package/dist/mcp-maestro.js +25 -0
  146. package/dist/mcp-server.js +469 -0
  147. package/dist/mcp-ship.js +436 -0
  148. package/dist/mcp-verify.js +173 -0
  149. package/dist/memoria-humana.js +207 -0
  150. package/dist/memoria.js +314 -78
  151. package/dist/memory-migration.js +279 -0
  152. package/dist/memory-prospective.js +148 -0
  153. package/dist/modos-migracao.js +191 -0
  154. package/dist/modos.js +151 -15
  155. package/dist/monitor-lock.js +110 -0
  156. package/dist/objective.js +449 -0
  157. package/dist/ocupacao.js +267 -0
  158. package/dist/onboarding.js +303 -0
  159. package/dist/orkmind.js +389 -90
  160. package/dist/orquestracao.js +96 -69
  161. package/dist/phase.js +596 -150
  162. package/dist/playbook-capabilities.js +150 -0
  163. package/dist/playbook-contracts.js +188 -0
  164. package/dist/portfolio-context.js +61 -0
  165. package/dist/portfolio.js +180 -0
  166. package/dist/preflight.js +207 -0
  167. package/dist/process-audit.js +112 -0
  168. package/dist/project-state.js +93 -0
  169. package/dist/prompts.js +25 -5
  170. package/dist/prova-minima.js +112 -0
  171. package/dist/pulse-cadencia.js +163 -0
  172. package/dist/pulse-consentimento.js +260 -0
  173. package/dist/pulse-delivery.js +323 -0
  174. package/dist/pulse-resposta.js +542 -0
  175. package/dist/pulse.js +262 -0
  176. package/dist/ratelimit.js +35 -2
  177. package/dist/recall.js +80 -3
  178. package/dist/redacao-saida.js +45 -0
  179. package/dist/redacao-url.js +66 -0
  180. package/dist/retry.js +637 -115
  181. package/dist/roadmap-reservas.js +243 -0
  182. package/dist/runtime-ambiente.js +38 -0
  183. package/dist/runtime-context.js +215 -0
  184. package/dist/runtime-profiles.js +869 -0
  185. package/dist/runtimes.js +103 -0
  186. package/dist/sandbox.js +9 -0
  187. package/dist/session-events.js +203 -0
  188. package/dist/session-watcher-claude.js +637 -0
  189. package/dist/session-watcher.js +683 -0
  190. package/dist/sessoes-adopt.js +154 -0
  191. package/dist/sessoes-inventario.js +159 -0
  192. package/dist/sessoes.js +16 -35
  193. package/dist/setup.js +691 -0
  194. package/dist/ship.js +150 -15
  195. package/dist/slug.js +1 -1
  196. package/dist/thread-close.js +115 -0
  197. package/dist/thread.js +142 -28
  198. package/dist/tokens.js +1 -1
  199. package/dist/util.js +4 -2
  200. package/dist/verify-sandbox.js +265 -0
  201. package/dist/verify.js +270 -25
  202. package/dist/versao.js +60 -0
  203. package/dist/worktree.js +31 -2
  204. package/dist/write-activation.js +291 -0
  205. package/dist/yaml.js +2 -0
  206. package/eval/casos/onboarding.json +102 -0
  207. package/eval/casos/orkastery-bootstrap.json +35 -1
  208. package/eval/casos/scope-check-capability-map.json +45 -7
  209. package/eval/casos/ship-release.json +2 -2
  210. package/eval/fixtures/b0-slug-e-modos/caso.json +35 -22
  211. package/eval/fixtures/b2-master-log/caso.json +1 -1
  212. package/eval/fixtures/fx-adapter-editado-detectado/caso.json +19 -0
  213. package/eval/fixtures/fx-auto-quiet/caso.json +11 -0
  214. package/eval/fixtures/fx-blanket-approve/caso.json +10 -0
  215. package/eval/fixtures/fx-check-runtime-cruzado/caso.json +20 -0
  216. package/eval/fixtures/fx-codex-dry/caso.json +18 -0
  217. package/eval/fixtures/fx-donewhen-executavel/caso.json +15 -0
  218. package/eval/fixtures/fx-estado-dividido/caso.json +15 -0
  219. package/eval/fixtures/fx-fase-orfa/caso.json +116 -0
  220. package/eval/fixtures/fx-happy/caso.json +6 -4
  221. package/eval/fixtures/fx-hitl-latency/caso.json +20 -0
  222. package/eval/fixtures/fx-indice-reversao/caso.json +25 -0
  223. package/eval/fixtures/fx-listagem-abertas/caso.json +16 -0
  224. package/eval/fixtures/fx-maestro-bootstrap/caso.json +21 -0
  225. package/eval/fixtures/fx-modo-aposentado-escritor/caso.json +21 -0
  226. package/eval/fixtures/fx-modo-aposentado-leitor/caso.json +16 -0
  227. package/eval/fixtures/fx-objective-oscillation/caso.json +14 -0
  228. package/eval/fixtures/fx-omnicanal/caso.json +17 -0
  229. package/eval/fixtures/fx-sensores-runtime/caso.json +17 -0
  230. package/monitor/company-brain.cjs +17 -0
  231. package/monitor/pulse-scope.cjs +45 -0
  232. package/monitor/pulse.cron +18 -0
  233. package/monitor/varredura-pulse.sh +27 -0
  234. package/package.json +25 -4
  235. package/schemas/claims.schema.json +25 -0
  236. package/schemas/company-brain.schema.json +1158 -0
  237. package/schemas/creation-operation.schema.json +365 -0
  238. package/schemas/maestro-snapshot.schema.json +2716 -0
  239. package/skills/core/onboarding/SKILL.md +50 -0
  240. package/skills/core/orkastery-bootstrap/SKILL.md +95 -65
  241. package/skills/core/thread-state/SKILL.md +3 -1
  242. package/skills/governance/scope-check-capability-map/SKILL.md +17 -7
@@ -0,0 +1,436 @@
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.criarPerfilShipMcp = criarPerfilShipMcp;
37
+ exports.shipMcp = shipMcp;
38
+ /** SHIP instalado: o cliente escolhe identidade/HEADs, nunca transporte ou autorização.
39
+ * SSH do dono é infraestrutura confiável explícita; não é uma promessa de SSH sem helpers.
40
+ * Escritores hostis concorrentes fora do sandbox não pertencem a este contrato. */
41
+ const fs = __importStar(require("node:fs"));
42
+ const path = __importStar(require("node:path"));
43
+ const node_crypto_1 = require("node:crypto");
44
+ const node_child_process_1 = require("node:child_process");
45
+ const manifest_1 = require("./manifest");
46
+ const thread_1 = require("./thread");
47
+ const worktree_1 = require("./worktree");
48
+ const mcp_artifacts_1 = require("./mcp-artifacts");
49
+ const mcp_git_1 = require("./mcp-git");
50
+ const leases_1 = require("./leases");
51
+ const ship_1 = require("./ship");
52
+ const verify_1 = require("./verify");
53
+ const claims_1 = require("./claims");
54
+ const verify_sandbox_1 = require("./verify-sandbox");
55
+ // Mesmo mecanismo subreaper do executor C17: também cobre Git/SSH e o worker inteiro.
56
+ const SUPERVISOR_SHIP = String.raw `
57
+ import ctypes, errno, json, os, selectors, signal, subprocess, sys, time
58
+ p = json.load(sys.stdin)
59
+ libc = ctypes.CDLL(None, use_errno=True)
60
+ if libc.prctl(36, 1, 0, 0, 0) != 0:
61
+ raise OSError(ctypes.get_errno(), 'PR_SET_CHILD_SUBREAPER falhou')
62
+ state = {'failure': None}
63
+ def interrupted(_sig, _frame): state['failure'] = 'supervisor interrompido'
64
+ signal.signal(signal.SIGTERM, interrupted)
65
+ def cleanup():
66
+ deadline = time.monotonic() + 3
67
+ while True:
68
+ with open('/proc/self/task/' + str(os.getpid()) + '/children') as f:
69
+ children = [int(x) for x in f.read().split()]
70
+ # Filhos vivos ou zombies ainda não reaped: seus PIDs não podem ser reutilizados.
71
+ for pid in children:
72
+ try: os.kill(pid, signal.SIGKILL)
73
+ except ProcessLookupError: pass
74
+ try:
75
+ while True:
76
+ pid, _status = os.waitpid(-1, os.WNOHANG)
77
+ if pid == 0: break
78
+ except ChildProcessError: return True
79
+ if time.monotonic() >= deadline: return False
80
+ time.sleep(.005)
81
+ stdout = bytearray(); stderr = bytearray(); code = -1
82
+ try:
83
+ child = subprocess.Popen([p['bin'], *p['args']], cwd=p['cwd'], env=p['env'],
84
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, start_new_session=True)
85
+ child.stdin.write(p['input'].encode()); child.stdin.close()
86
+ selector = selectors.DefaultSelector()
87
+ selector.register(child.stdout, selectors.EVENT_READ, stdout)
88
+ selector.register(child.stderr, selectors.EVENT_READ, stderr)
89
+ deadline = time.monotonic() + p['timeoutMs']/1000
90
+ while True:
91
+ if state['failure']: break
92
+ if time.monotonic() >= deadline:
93
+ state['failure'] = 'timeout do sandbox'; break
94
+ if child.poll() is not None and not selector.get_map():
95
+ code = child.returncode; break
96
+ for key, _ in selector.select(.025):
97
+ data = os.read(key.fileobj.fileno(), 65536)
98
+ if not data: selector.unregister(key.fileobj); continue
99
+ if len(stdout)+len(stderr)+len(data) > p['maxOutput']:
100
+ state['failure'] = 'limite de saída excedido'; break
101
+ key.data.extend(data)
102
+ except Exception as e: state['failure'] = 'sandbox indisponível: ' + str(e)
103
+ finally:
104
+ cleaned = cleanup()
105
+ if not cleaned: state['failure'] = 'descendente do sandbox não encerrou no prazo'
106
+ print(json.dumps({'code': -1 if state['failure'] else code,
107
+ 'stdout': stdout.decode(errors='replace'), 'stderr': stderr.decode(errors='replace'), 'failure': state['failure'], 'cleanupConfirmed': cleaned}))
108
+ `;
109
+ const PRAZO = 240000, RESERVA = 15000, LIMITE = 1048576;
110
+ const perfis = new WeakMap();
111
+ function erro(m) { throw Error('mcp.ship.' + m); }
112
+ function digest(s) { return (0, node_crypto_1.createHash)('sha256').update(s).digest('hex'); }
113
+ /** Somente SSH fixado/bare físico: ambos nunca usam credential helpers HTTP. HTTPS é recusado na factory. */
114
+ function config(raiz) {
115
+ const raw = (0, mcp_git_1.gitPassivoMcp)(raiz, ['config', '--null', '--list']);
116
+ for (const item of raw.split('\0').filter(Boolean)) {
117
+ const i = item.indexOf('\n');
118
+ if (i < 0 || !(0, mcp_git_1.configuracaoPassivaGitMcp)(item.slice(0, i).toLowerCase(), item.slice(i + 1), true))
119
+ erro('config.unsupported');
120
+ }
121
+ return digest(raw);
122
+ }
123
+ /** Push usa todas as URLs configuradas; nunca fixar somente a primeira. */
124
+ function originUnico(raiz) {
125
+ const ler = (push) => {
126
+ const raw = (0, mcp_git_1.gitPassivoMcp)(raiz, ['remote', 'get-url', ...(push ? ['--push'] : []), '--all', 'origin']);
127
+ const urls = raw.split('\n');
128
+ if (urls[urls.length - 1] === '')
129
+ urls.pop();
130
+ if (urls.length !== 1 || !urls[0] || /[\x00-\x1f\x7f]/.test(urls[0]))
131
+ erro('transport.urls: origin exige uma URL efetiva');
132
+ return urls[0];
133
+ };
134
+ const fetch = ler(false), push = ler(true);
135
+ if (fetch !== push)
136
+ erro('transport.pushurl');
137
+ return fetch;
138
+ }
139
+ function guardarBare(url) {
140
+ if (!path.isAbsolute(url))
141
+ erro('transport.bare.invalid');
142
+ (0, mcp_git_1.caminhoFisicoGitMcp)(url, url);
143
+ const st = fs.statSync(url);
144
+ if (st.uid !== process.getuid?.())
145
+ erro('transport.bare.owner');
146
+ for (const n of ['objects', 'refs', 'logs', 'info', 'HEAD', 'config', 'packed-refs', 'hooks'])
147
+ (0, mcp_git_1.metadadosGitMcp)(url, path.join(url, n));
148
+ const hooks = path.join(url, 'hooks');
149
+ if (fs.existsSync(hooks))
150
+ for (const n of fs.readdirSync(hooks)) {
151
+ if (!n.endsWith('.sample') && (fs.statSync(path.join(hooks, n)).mode & 0o111))
152
+ erro('transport.bare.hooks');
153
+ }
154
+ const alternates = path.join(url, 'objects/info/alternates');
155
+ if (fs.existsSync(alternates) && fs.statSync(alternates).size)
156
+ erro('transport.bare.alternates');
157
+ const raw = (0, mcp_git_1.gitPassivoMcp)(url, ['config', '--null', '--list']);
158
+ let bare = false;
159
+ for (const item of raw.split('\0').filter(Boolean)) {
160
+ const i = item.indexOf('\n'), k = item.slice(0, i).toLowerCase(), v = item.slice(i + 1);
161
+ if (k === 'core.bare' && v === 'true') {
162
+ bare = true;
163
+ continue;
164
+ }
165
+ if (i < 0 || !(0, mcp_git_1.configuracaoPassivaGitMcp)(k, v, true))
166
+ erro('transport.bare.config');
167
+ }
168
+ if (!bare)
169
+ erro('transport.bare.invalid');
170
+ return { dev: st.dev, ino: st.ino };
171
+ }
172
+ /** Somente instalação/startup confiável chama esta factory. O objeto é uma capacidade privada. */
173
+ function criarPerfilShipMcp(raiz, transporte = 'github-ssh') {
174
+ if (process.platform !== 'linux' || !path.isAbsolute(raiz))
175
+ erro('project.invalid');
176
+ (0, mcp_git_1.caminhoFisicoGitMcp)(raiz, raiz);
177
+ const c = (0, manifest_1.exigirManifesto)(raiz);
178
+ if (c.raiz !== raiz)
179
+ erro('project.invalid');
180
+ const destino = c.manifesto.worktree.base_branch;
181
+ if (!/^[A-Za-z0-9][A-Za-z0-9._/-]*$/.test(destino) || destino.includes('..') || destino.endsWith('/'))
182
+ erro('destination.invalid');
183
+ const cfg = config(raiz), url = originUnico(raiz);
184
+ let bare;
185
+ if (transporte === 'github-ssh') {
186
+ if (!/^git@github\.com:[A-Za-z0-9][A-Za-z0-9-]*\/[A-Za-z0-9][A-Za-z0-9._-]*\.git$/.test(url))
187
+ erro('transport.unsupported: GitHub SSH exigido; HTTPS requer autenticacao instalada auditada');
188
+ }
189
+ else if (transporte === 'bare-local')
190
+ bare = guardarBare(url);
191
+ else
192
+ erro('transport.invalid');
193
+ const perfil = Object.freeze({ transporte, destino }), st = fs.statSync(raiz);
194
+ const env = { HOME: process.env.HOME, USER: process.env.USER, LOGNAME: process.env.LOGNAME, XDG_CONFIG_HOME: process.env.XDG_CONFIG_HOME,
195
+ SSH_AUTH_SOCK: process.env.SSH_AUTH_SOCK, PATH: '/usr/bin:/bin', LANG: 'C.UTF-8', TMPDIR: '/tmp' };
196
+ perfis.set(perfil, { raiz, dev: st.dev, ino: st.ino, url, config: cfg, perfil, env, ...(bare ? { bare } : {}) });
197
+ return perfil;
198
+ }
199
+ function validar(p) {
200
+ if (!p || Object.keys(p).sort().join(',') !== 'expectedDestination,expectedSource,threadId' ||
201
+ typeof p.threadId !== 'string' || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,79}$/.test(p.threadId) ||
202
+ !/^[a-f0-9]{40}$/.test(p.expectedSource) || !/^[a-f0-9]{40}$/.test(p.expectedDestination))
203
+ erro('request.invalid');
204
+ }
205
+ function gitdir(raiz, wt) {
206
+ if (wt === raiz)
207
+ return path.join(raiz, '.git');
208
+ const f = path.join(wt, '.git');
209
+ (0, mcp_git_1.caminhoFisicoGitMcp)(wt, f, true);
210
+ const m = /^gitdir: (.+)\s*$/.exec(fs.readFileSync(f, 'utf8'));
211
+ if (!m)
212
+ erro('worktree.invalid');
213
+ const d = path.resolve(wt, m[1].trim());
214
+ (0, mcp_git_1.caminhoFisicoGitMcp)(path.join(raiz, '.git'), d);
215
+ return d;
216
+ }
217
+ function arvore(raiz, ref) {
218
+ const raw = (0, mcp_git_1.gitPassivoMcp)(raiz, ['ls-tree', '-rz', ref]);
219
+ const nomes = [], attrs = [];
220
+ for (const item of raw.split('\0').filter(Boolean)) {
221
+ if (item.startsWith('160000 '))
222
+ erro('tree.gitlink.unsupported');
223
+ const i = item.indexOf('\t'), nome = item.slice(i + 1);
224
+ if (i < 0 || !nome || nomes.length >= 4096)
225
+ erro('tree.unsupported');
226
+ nomes.push(nome);
227
+ if (path.posix.basename(nome) === '.gitattributes')
228
+ attrs.push(item);
229
+ }
230
+ return { paths: nomes, attrs: attrs.sort().join('\0') };
231
+ }
232
+ function atributos(raiz, refs, destino) {
233
+ const trees = refs.map(r => arvore(raiz, r));
234
+ if (trees.some(t => t.attrs !== trees[0].attrs))
235
+ erro('attributes.transition.unsupported');
236
+ const nomes = [...new Set(trees.flatMap(t => t.paths))].sort();
237
+ if (!nomes.length || nomes.length > 4096)
238
+ erro('tree.unsupported');
239
+ const input = nomes.join('\0') + '\0';
240
+ const conferir = (cwd, args) => {
241
+ const raw = (0, mcp_git_1.gitPassivoMcp)(cwd, ['check-attr', ...args, '-z', '--stdin', 'filter', 'merge'], input), parts = raw.split('\0');
242
+ if (parts.pop() !== '' || parts.length !== nomes.length * 6)
243
+ erro('attributes.inconclusive');
244
+ for (let i = 0; i < nomes.length; i++) {
245
+ const v = parts.slice(i * 6, i * 6 + 6);
246
+ if (v[0] !== nomes[i] || v[1] !== 'filter' || !['unspecified', 'unset'].includes(v[2]) ||
247
+ v[3] !== nomes[i] || v[4] !== 'merge' || !['unspecified', 'unset', 'set', 'text', 'binary', 'union'].includes(v[5]))
248
+ erro('attributes.active-driver');
249
+ }
250
+ };
251
+ refs.forEach(r => conferir(raiz, ['--source=' + r]));
252
+ conferir(destino, []);
253
+ conferir(destino, ['--cached']);
254
+ // O destino não pode manter atributos locais divergentes dos blobs fixados.
255
+ for (const item of trees[0].attrs.split('\0').filter(Boolean)) {
256
+ const i = item.indexOf('\t'), nome = item.slice(i + 1), blob = item.slice(0, i).split(' ')[2], f = path.join(destino, nome);
257
+ (0, mcp_git_1.caminhoFisicoGitMcp)(destino, f, true);
258
+ const bytes = fs.readFileSync(f);
259
+ const hash = (0, node_crypto_1.createHash)('sha1').update(`blob ${bytes.length}\0`).update(bytes).digest('hex');
260
+ if (hash !== blob)
261
+ erro('attributes.worktree.changed');
262
+ }
263
+ }
264
+ function executar(f, p) {
265
+ validar(p);
266
+ const raiz = f.raiz, deadline = Date.now() + PRAZO;
267
+ const guardEstado = () => {
268
+ (0, mcp_git_1.caminhoFisicoGitMcp)(raiz, raiz);
269
+ const st = fs.statSync(raiz);
270
+ if (st.dev !== f.dev || st.ino !== f.ino)
271
+ erro('project.changed');
272
+ for (const dir of ['.orkastery', '.orkastery/threads', `.orkastery/threads/${p.threadId}`])
273
+ (0, mcp_git_1.caminhoFisicoGitMcp)(raiz, path.join(raiz, dir));
274
+ (0, mcp_artifacts_1.validarArquivosEstadoMcp)(raiz, p.threadId);
275
+ for (const dir of ['.orkastery/leases', '.orkastery/tmp', '.orkastery/state-backups', '.git/worktrees']) {
276
+ const alvo = path.join(raiz, dir);
277
+ if (fs.existsSync(alvo))
278
+ (0, mcp_git_1.caminhoFisicoGitMcp)(raiz, alvo);
279
+ }
280
+ (0, mcp_git_1.metadadosGitMcp)(path.join(raiz, '.orkastery'), path.join(raiz, '.orkastery/leases'));
281
+ (0, mcp_git_1.metadadosGitMcp)(path.join(raiz, '.orkastery'), (0, leases_1.caminhoFila)(raiz));
282
+ };
283
+ guardEstado();
284
+ const c = (0, manifest_1.exigirManifesto)(raiz), t = (0, thread_1.lerThread)(raiz, p.threadId), wt = t.worktree;
285
+ if (!wt || t.id !== p.threadId || t.status === 'fechada' || !(0, thread_1.blocoDaThread)(t, t.faseAtual).fases.includes('SHIP'))
286
+ erro('thread.invalid');
287
+ (0, mcp_git_1.caminhoFisicoGitMcp)(raiz, wt);
288
+ const de = (0, worktree_1.branchDaWorktree)(t), para = f.perfil.destino;
289
+ if (c.manifesto.worktree.base_branch !== para)
290
+ erro('destination.changed');
291
+ // Primeira fatia requer destino já registrado: não cria checkout com attrs ainda não inspecionados.
292
+ const destino = (0, ship_1.arvoreComBranch)(raiz, para);
293
+ if (!destino)
294
+ erro('destination.worktree.required');
295
+ (0, mcp_git_1.caminhoFisicoGitMcp)(raiz, destino);
296
+ const refs = [p.expectedSource, p.expectedDestination, (0, mcp_git_1.gitPassivoMcp)(raiz, ['merge-base', p.expectedSource, p.expectedDestination]).trim()];
297
+ refs.forEach(ref => arvore(raiz, ref)); // Antes de qualquer diff, inclusive índice/WT.
298
+ const guard = (etapa, dir) => {
299
+ guardEstado();
300
+ if (Date.now() > deadline - RESERVA)
301
+ erro('deadline');
302
+ if (originUnico(raiz) !== f.url || config(raiz) !== f.config)
303
+ erro('transport.changed');
304
+ if (f.bare) {
305
+ const b = guardarBare(f.url);
306
+ if (b.dev !== f.bare.dev || b.ino !== f.bare.ino)
307
+ erro('transport.changed');
308
+ }
309
+ if ((0, mcp_git_1.gitPassivoMcp)(raiz, ['rev-parse', `refs/heads/${de}`]).trim() !== p.expectedSource || (0, mcp_git_1.gitPassivoMcp)(wt, ['rev-parse', 'HEAD']).trim() !== p.expectedSource)
310
+ erro('source.stale');
311
+ if (etapa !== 'push' && (0, mcp_git_1.gitPassivoMcp)(raiz, ['rev-parse', `refs/heads/${para}`]).trim() !== p.expectedDestination)
312
+ erro('destination.stale');
313
+ if ((0, ship_1.arvoreComBranch)(raiz, para) !== destino || (dir && dir !== destino))
314
+ erro('destination.changed');
315
+ for (const a of [wt, destino])
316
+ (0, mcp_git_1.perfilGitMcp)(a, path.join(raiz, '.git'), gitdir(raiz, a), t.id, []);
317
+ for (const a of [wt, destino])
318
+ if ((0, mcp_git_1.gitPassivoMcp)(a, ['diff', '--cached', '--name-only', '-z']).length || (0, mcp_git_1.gitPassivoMcp)(a, ['diff', '--name-only', '-z']).length)
319
+ erro('tracked.dirty');
320
+ atributos(raiz, refs, destino);
321
+ };
322
+ guard('inicio');
323
+ const comandos = (0, claims_1.lerClaims)(raiz, t.id).filter(x => x.estado !== 'retirada').flatMap(x => x.verificar).concat((0, verify_1.comandosDoManifesto)(c.manifesto).map(x => x.comando));
324
+ if (comandos.length > 512 || comandos.reduce((n, s) => n + Buffer.byteLength(s), 0) > LIMITE)
325
+ erro('verify.limit');
326
+ const nome = `worktree-write:${t.id}`, nomes = [nome, `path:.orkastery/threads/${t.id}`];
327
+ for (const n of [...nomes, 'main-tree'])
328
+ if (fs.lstatSync((0, leases_1.caminhoLease)(raiz, n), { throwIfNoEntry: false }) || (0, leases_1.leasesColidentes)(raiz, n).length)
329
+ erro('lease.busy');
330
+ const proprias = [];
331
+ const leasesIntactas = () => proprias.every(l => JSON.stringify((0, leases_1.lerLease)(raiz, l.nome)) === JSON.stringify(l));
332
+ try {
333
+ for (const n of nomes) {
334
+ const a = (0, leases_1.adquirirRegiao)(raiz, n, { thread: t.id, motivo: 'MCP SHIP ' + (0, node_crypto_1.randomUUID)(), ttlMs: PRAZO + RESERVA, retomarVencido: false });
335
+ if (!a.ok || !a.lease)
336
+ erro('lease.busy');
337
+ proprias.push(a.lease);
338
+ }
339
+ const executor = (nomeComando, comando, cwd) => {
340
+ guardEstado();
341
+ if (cwd !== wt || !leasesIntactas())
342
+ erro('verify.scope');
343
+ const restante = deadline - Date.now() - RESERVA;
344
+ if (restante < 100)
345
+ return { nome: nomeComando, comando, ok: false, code: -1, resumo: 'prazo SHIP esgotado' };
346
+ const sandbox = (0, verify_sandbox_1.criarExecutorSandbox)({ worktree: wt, timeoutMs: Math.min(60000, restante) });
347
+ try {
348
+ return sandbox.executar(nomeComando, comando, cwd);
349
+ }
350
+ finally {
351
+ sandbox.fechar();
352
+ }
353
+ };
354
+ return (0, ship_1.ship)(c, t.id, { de, para, remoto: 'origin', executorVerify: executor, ttlLeaseMs: PRAZO + RESERVA, retomarLeaseVencido: false,
355
+ revalidarGit: (etapa, dir) => { if (!leasesIntactas())
356
+ erro('lease.changed'); guard(etapa, dir); } });
357
+ }
358
+ finally {
359
+ for (const l of proprias.reverse())
360
+ if (JSON.stringify((0, leases_1.lerLease)(raiz, l.nome)) === JSON.stringify(l))
361
+ (0, leases_1.liberar)(raiz, l.nome, t.id);
362
+ }
363
+ }
364
+ /** Perfil não pode ser fabricado por argumentos JSON. Retorno interrompido exige reconciliação. */
365
+ async function shipMcp(perfil, pedido, signal) {
366
+ const f = perfis.get(perfil);
367
+ if (!f)
368
+ erro('profile.invalid');
369
+ validar(pedido);
370
+ if (signal?.aborted)
371
+ return { ok: false, erro: 'mcp.ship.cancelled' };
372
+ return new Promise(resolve => {
373
+ const child = (0, node_child_process_1.spawn)('/usr/bin/python3', ['-c', SUPERVISOR_SHIP], { env: f.env, stdio: ['pipe', 'pipe', 'pipe'] });
374
+ let out = '', falhou = false, fechou = false, inicio;
375
+ const identity = () => { try {
376
+ const raw = fs.readFileSync(`/proc/${child.pid}/stat`, 'utf8');
377
+ return raw.slice(raw.lastIndexOf(')') + 2).split(' ')[19];
378
+ }
379
+ catch {
380
+ return undefined;
381
+ } };
382
+ const cancelar = () => { falhou = true; if (!fechou && inicio && identity() === inicio)
383
+ try {
384
+ child.kill('SIGTERM');
385
+ }
386
+ catch { } };
387
+ const timer = setTimeout(cancelar, PRAZO + 5000);
388
+ signal?.addEventListener('abort', cancelar, { once: true });
389
+ if (signal?.aborted)
390
+ cancelar();
391
+ child.once('spawn', () => { inicio = identity(); if (!inicio || falhou) {
392
+ falhou = true;
393
+ child.stdin.end();
394
+ return;
395
+ } child.stdin.end(JSON.stringify({ bin: process.execPath, args: [__filename, '--worker'], cwd: f.raiz, env: f.env, timeoutMs: PRAZO, maxOutput: LIMITE, input: JSON.stringify({ f, pedido }) })); });
396
+ child.stdin.on('error', () => { falhou = true; });
397
+ child.on('error', () => { falhou = true; });
398
+ child.stdout.on('data', (b) => { if (Buffer.byteLength(out) + b.length > LIMITE * 4)
399
+ cancelar();
400
+ else
401
+ out += b.toString(); });
402
+ child.stderr.on('data', () => { falhou = true; });
403
+ child.on('close', (code, sig) => {
404
+ fechou = true;
405
+ clearTimeout(timer);
406
+ signal?.removeEventListener('abort', cancelar);
407
+ let cleanupConfirmado = false;
408
+ if (code === 0 && !sig)
409
+ try {
410
+ const envelope = JSON.parse(out);
411
+ cleanupConfirmado = envelope.cleanupConfirmed === true;
412
+ if (!falhou && cleanupConfirmado && envelope.code === 0 && envelope.failure === null && !envelope.stderr) {
413
+ const r = JSON.parse(envelope.stdout);
414
+ if (typeof r.ok === 'boolean')
415
+ return resolve({ ...r, cleanupConfirmado });
416
+ }
417
+ }
418
+ catch { }
419
+ resolve({ ok: false, cleanupConfirmado, erro: 'mcp.ship.worker.incomplete: conferir processos, leases, HEAD e remoto antes de repetir' });
420
+ });
421
+ });
422
+ }
423
+ if (require.main === module && process.argv[2] === '--worker') {
424
+ try {
425
+ const raw = fs.readFileSync(0, 'utf8');
426
+ if (Buffer.byteLength(raw) > LIMITE)
427
+ erro('request.invalid');
428
+ const { f, pedido } = JSON.parse(raw);
429
+ const resultado = executar(f, pedido);
430
+ process.stdout.write(JSON.stringify({ ok: resultado.ok, resultado }));
431
+ }
432
+ catch (e) {
433
+ const m = e.message;
434
+ process.stdout.write(JSON.stringify({ ok: false, erro: m.startsWith('mcp.') ? m : 'mcp.ship.operation.failed: conferir estado antes de repetir' }));
435
+ }
436
+ }
@@ -0,0 +1,173 @@
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.verificarMcp = verificarMcp;
37
+ /** Operação estruturada: argumentos só identificam a thread; execução sempre confinada. */
38
+ const fs = __importStar(require("node:fs"));
39
+ const path = __importStar(require("node:path"));
40
+ const node_crypto_1 = require("node:crypto");
41
+ const manifest_1 = require("./manifest");
42
+ const thread_1 = require("./thread");
43
+ const claims_1 = require("./claims");
44
+ const leases_1 = require("./leases");
45
+ const mcp_artifacts_1 = require("./mcp-artifacts");
46
+ const verify_1 = require("./verify");
47
+ const verify_sandbox_1 = require("./verify-sandbox");
48
+ const worktree_1 = require("./worktree");
49
+ const PRAZO_TOTAL_MS = 180_000;
50
+ const RESERVA_MS = 10_000;
51
+ // I-37 (D4): o teto por comando cabe o `test:ci` medido (196 s); quem limita e o prazo total.
52
+ const COMANDO_MAX_MS = 300_000;
53
+ const MAX_COMANDOS = 512;
54
+ const MAX_CLAIMS = 512;
55
+ const LIMITE_RETORNO = 40;
56
+ function identidade(st) { return `${st.dev}:${st.ino}`; }
57
+ function diretorioFixo(p) {
58
+ const real = fs.realpathSync(p);
59
+ if (real !== path.resolve(p) || !fs.lstatSync(p).isDirectory())
60
+ throw Error('mcp.verify.scope');
61
+ return real;
62
+ }
63
+ function dentro(raiz, p) { return p.startsWith(raiz + path.sep); }
64
+ function mesmoLease(a, b) {
65
+ return !!a && a.nome === b.nome && a.thread === b.thread && a.pid === b.pid &&
66
+ a.adquiridoEm === b.adquiridoEm && a.expiraEm === b.expiraEm && a.motivo === b.motivo;
67
+ }
68
+ /** Síncrono: prazo total/per-command, não cancelamento reativo do event loop. */
69
+ function verificarMcp(raizEntrada, threadId, conducao = {}) {
70
+ const deadline = Date.now() + PRAZO_TOTAL_MS;
71
+ if (!path.isAbsolute(raizEntrada) || !/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,79}$/.test(threadId))
72
+ throw Error('mcp.verify.invalid');
73
+ const raiz = diretorioFixo(raizEntrada);
74
+ const carregado = (0, manifest_1.exigirManifesto)(raiz);
75
+ if (fs.realpathSync(carregado.raiz) !== raiz)
76
+ throw Error('mcp.verify.project');
77
+ // Nenhum componente de estado pode redirecionar as APIs legadas que usam caminhos.
78
+ for (const p of [path.join(raiz, '.orkastery'), path.join(raiz, '.orkastery', 'threads'), (0, thread_1.dirThread)(raiz, threadId)]) {
79
+ if (!dentro(raiz, diretorioFixo(p)))
80
+ throw Error('mcp.verify.scope');
81
+ }
82
+ (0, mcp_artifacts_1.validarArquivosEstadoMcp)(raiz, threadId);
83
+ const t = (0, thread_1.lerThread)(raiz, threadId);
84
+ if (t.id !== threadId || t.status === 'fechada' || !t.worktree)
85
+ throw Error('mcp.verify.thread');
86
+ const worktree = diretorioFixo(t.worktree);
87
+ if (!dentro(raiz, worktree))
88
+ throw Error('mcp.verify.worktree');
89
+ if (!(0, worktree_1.worktreesDoGit)(raiz).some(w => w.dir === worktree && w.branch === (0, worktree_1.branchDaWorktree)(t)))
90
+ throw Error('mcp.verify.worktree.unregistered');
91
+ const fixacaoRaiz = identidade(fs.statSync(raiz)), fixacaoWt = identidade(fs.statSync(worktree));
92
+ const nomes = [`worktree-write:${threadId}`, `path:.orkastery/threads/${threadId}`];
93
+ const leasesDir = (0, leases_1.dirLeases)(raiz);
94
+ const stDir = fs.lstatSync(leasesDir, { throwIfNoEntry: false });
95
+ if (stDir && (!stDir.isDirectory() || !dentro(raiz, diretorioFixo(leasesDir))))
96
+ throw Error('mcp.verify.scope');
97
+ const stFila = fs.lstatSync((0, leases_1.caminhoFila)(raiz), { throwIfNoEntry: false });
98
+ if (stFila && (!stFila.isFile() || stFila.nlink !== 1))
99
+ throw Error('mcp.verify.queue.unsafe');
100
+ // Não herda nem remove lease existente, inclusive própria, expirada ou corrompida.
101
+ if (nomes.some(nome => fs.lstatSync((0, leases_1.caminhoLease)(raiz, nome), { throwIfNoEntry: false }) ||
102
+ (0, leases_1.leasesColidentes)(raiz, nome).length) || (0, leases_1.leasesColidentes)(raiz, 'main-tree').length) {
103
+ throw Error('lease.busy: verificação exige leases próprias livres');
104
+ }
105
+ const tempoParaLease = deadline - Date.now();
106
+ if (tempoParaLease <= RESERVA_MS)
107
+ throw Error('mcp.verify.deadline: prazo esgotado no preflight');
108
+ const adquiridos = [];
109
+ const conservaLeases = () => adquiridos.length === nomes.length &&
110
+ adquiridos.every(lease => mesmoLease((0, leases_1.lerLease)(raiz, lease.nome), lease));
111
+ try {
112
+ for (const nome of nomes) {
113
+ const adquirido = (0, leases_1.adquirirRegiao)(raiz, nome, { thread: threadId, motivo: 'MCP VERIFY ' + (0, node_crypto_1.randomUUID)(),
114
+ ttlMs: tempoParaLease + RESERVA_MS, retomarVencido: false });
115
+ if (!adquirido.ok || !adquirido.lease)
116
+ throw Error('lease.busy: verificação não adquiriu região');
117
+ adquiridos.push(adquirido.lease);
118
+ }
119
+ (0, mcp_artifacts_1.validarArquivosEstadoMcp)(raiz, threadId);
120
+ const atual = (0, thread_1.lerThread)(raiz, threadId);
121
+ if (atual.id !== threadId || atual.status === 'fechada' || atual.worktree !== t.worktree)
122
+ throw Error('mcp.verify.thread.changed');
123
+ const claims = (0, claims_1.lerClaims)(raiz, threadId).filter(c => c.estado !== 'retirada');
124
+ const comandos = claims.flatMap(c => c.verificar)
125
+ .concat((0, verify_1.comandosDoManifesto)(carregado.manifesto).map(c => c.comando));
126
+ if (claims.length > MAX_CLAIMS || comandos.length > MAX_COMANDOS || comandos.reduce((n, c) => n + Buffer.byteLength(c), 0) > 1024 * 1024) {
127
+ throw Error('mcp.verify.limit: plano de execução excede limite');
128
+ }
129
+ const executor = (nomeComando, comando, cwd) => {
130
+ if (diretorioFixo(raiz) !== raiz || identidade(fs.statSync(raiz)) !== fixacaoRaiz ||
131
+ diretorioFixo(worktree) !== worktree || identidade(fs.statSync(worktree)) !== fixacaoWt || cwd !== worktree) {
132
+ throw Error('mcp.verify.scope.changed');
133
+ }
134
+ (0, mcp_artifacts_1.validarArquivosEstadoMcp)(raiz, threadId);
135
+ if ((0, thread_1.lerThread)(raiz, threadId).worktree !== t.worktree || !conservaLeases())
136
+ throw Error('mcp.verify.lease-or-scope.changed');
137
+ const restante = deadline - Date.now() - RESERVA_MS;
138
+ if (restante < 100)
139
+ return { nome: nomeComando, comando, ok: false, code: -1,
140
+ resumo: 'não executado: prazo total de VERIFY esgotado', executado: false };
141
+ const prazo = Math.min(COMANDO_MAX_MS, (0, verify_1.prazoDoComando)(carregado.manifesto, nomeComando));
142
+ const confinado = (0, verify_sandbox_1.criarExecutorSandbox)({ worktree, timeoutMs: Math.min(prazo, restante) });
143
+ try {
144
+ const resultado = confinado.executar(nomeComando, comando, cwd);
145
+ if (!conservaLeases())
146
+ throw Error('mcp.verify.lease.changed');
147
+ (0, mcp_artifacts_1.validarArquivosEstadoMcp)(raiz, threadId);
148
+ return { ...resultado, executado: true };
149
+ }
150
+ finally {
151
+ confinado.fechar();
152
+ }
153
+ };
154
+ // I-36 (T8): o MESMO lease de execucao do CLI; a sessao filha reentra pela identidade do despacho.
155
+ const r = (0, verify_1.verificar)(carregado, threadId, { executor, operacao: 'mcp.verify', conducao });
156
+ const execucoes = r.claims.flatMap(c => c.execucoes).concat(r.comandos);
157
+ return {
158
+ threadId, ok: r.ok, commit: r.commit, motivos: r.motivos,
159
+ claims: { total: r.claims.length, verificadas: r.claims.filter(c => c.verificado).length },
160
+ execucoes: execucoes.slice(0, LIMITE_RETORNO).map(c => ({ nome: c.nome, ok: c.ok, code: c.code,
161
+ executado: c.executado === true, resumo: c.resumo })),
162
+ totalExecucoes: execucoes.length, omitidas: Math.max(0, execucoes.length - LIMITE_RETORNO),
163
+ regressoes: r.regressoes.map(c => c.nome), preExistentes: r.preExistentes.map(c => c.nome),
164
+ executor: 'codex-sandbox', prazoTotalMs: PRAZO_TOTAL_MS, limitePorComandoMs: COMANDO_MAX_MS,
165
+ };
166
+ }
167
+ finally {
168
+ for (const lease of adquiridos.reverse()) {
169
+ if (mesmoLease((0, leases_1.lerLease)(raiz, lease.nome), lease))
170
+ (0, leases_1.liberar)(raiz, lease.nome, threadId);
171
+ }
172
+ }
173
+ }