@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,123 @@
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.BRAIN_WRITE = exports.BRAIN_READ = void 0;
37
+ exports.runBrain = runBrain;
38
+ const fs = __importStar(require("node:fs"));
39
+ const path = __importStar(require("node:path"));
40
+ const project_state_1 = require("./project-state");
41
+ const portfolio_1 = require("./portfolio");
42
+ const thread_1 = require("./thread");
43
+ const write_activation_1 = require("./write-activation");
44
+ const ledger_1 = require("./ledger");
45
+ const company_brain_client_1 = require("./company-brain-client");
46
+ const company_brain_capture_1 = require("./company-brain-capture");
47
+ const company_brain_source_1 = require("./company-brain-source");
48
+ const company_brain_migration_1 = require("./company-brain-migration");
49
+ const company_brain_journal_1 = require("./company-brain-journal");
50
+ const company_brain_contract_1 = require("./company-brain-contract");
51
+ exports.BRAIN_READ = ['status', 'inventory', 'get', 'query', 'receipts', 'reconcile'];
52
+ exports.BRAIN_WRITE = ['sync', 'apply', 'rollback', 'bind'];
53
+ function runBrain(c, sub, options, positionals = [], transport) {
54
+ const context = (0, project_state_1.memoryState)(c);
55
+ c = context.loaded;
56
+ const config = c.manifesto.memory;
57
+ if (![...exports.BRAIN_READ, ...exports.BRAIN_WRITE].includes(sub))
58
+ throw Error('brain.operation.invalid');
59
+ const allowed = new Set(['thread', 'json', 'id', 'ids', 'kinds', 'workspaces', 'limit', 'offset', 'batch', 'dry-run', 'plan', 'expected-sha256', 'project', 'initiatives']);
60
+ if (Object.keys(options).some(k => !allowed.has(k)))
61
+ throw Error('brain.argument.invalid');
62
+ const value = (name) => typeof options[name] === 'string' ? options[name] : undefined;
63
+ const thread = value('thread');
64
+ let scope = { tenant: config.tenant, instance: c.manifesto.project.name, thread: thread ?? '', aclRef: 'ork-factory' };
65
+ const client = transport ?? (0, company_brain_client_1.brainClient)(c);
66
+ if (sub === 'status')
67
+ return { ...client({ schema: company_brain_client_1.BRAIN_API, operation: 'capabilities' }), tenant: config.tenant, configSource: context.source };
68
+ if (sub === 'get' || sub === 'receipts')
69
+ return client({ schema: company_brain_client_1.BRAIN_API, operation: sub, payload: { tenant_id: config.tenant, [sub === 'get' ? 'id' : 'event_id']: value('id') ?? positionals[0] ?? '' } });
70
+ if (sub === 'query' || sub === 'inventory') {
71
+ const split = (name) => (value(name) ?? '').split(',').filter(Boolean);
72
+ return client({ schema: company_brain_client_1.BRAIN_API, operation: 'query', payload: (0, company_brain_contract_1.validateContract)({ schema: 'orkmind.company-brain-selection/v1', tenant_id: config.tenant,
73
+ facets: { ids: split('ids'), kinds: split('kinds'), workspace_ids: split('workspaces'), source_instances: [] }, mode: 'selection', limit: Number(value('limit') ?? 50), offset: Number(value('offset') ?? 0) }) });
74
+ }
75
+ if (!thread)
76
+ throw Error('brain.thread.required');
77
+ (0, thread_1.lerThread)(c.raiz, thread);
78
+ if (sub === 'reconcile') {
79
+ if (options['dry-run'] !== true)
80
+ throw Error('brain.migration.dry-run-required');
81
+ return { state: 'ok', ...(0, company_brain_migration_1.reconcileBrain)(c, scope, value('batch') ?? 'inventory-' + (0, company_brain_contract_1.digest)((0, portfolio_1.readPortfolio)(c.raiz)).slice(0, 20)) };
82
+ }
83
+ // Existing reviewed activation plus dedicated Brain DB grants: neither grants the other.
84
+ (0, write_activation_1.exigirAtivacao)(c, thread, 'memory');
85
+ const dir = (0, thread_1.dirThread)(c.raiz, thread);
86
+ if (sub === 'bind') {
87
+ const selection = (0, portfolio_1.assertCycleScope)(c.raiz, { projectId: value('project') ?? '', delivery: 'initiatives', initiativeIds: (value('initiatives') ?? '').split(',').filter(Boolean) });
88
+ const binding = { schema: 'ork.brain-cycle-scope/v1', version: 1, thread, ...selection };
89
+ const file = path.join(dir, 'brain-scope.json');
90
+ if (fs.existsSync(file)) {
91
+ if ((0, company_brain_contract_1.digest)(JSON.parse(fs.readFileSync(file, 'utf8'))) !== (0, company_brain_contract_1.digest)(binding))
92
+ throw Error('brain.scope.immutable');
93
+ return { state: 'ok', binding };
94
+ }
95
+ (0, company_brain_journal_1.atomicJson)(file, binding);
96
+ (0, ledger_1.registrar)(dir, thread, 'brain_scope_recorded', { binding, sha256: (0, company_brain_contract_1.digest)(binding) });
97
+ return { state: 'ok', binding };
98
+ }
99
+ if (sub === 'sync') {
100
+ const bindingFile = path.join(dir, 'brain-scope.json');
101
+ if (fs.existsSync(bindingFile)) {
102
+ const binding = JSON.parse(fs.readFileSync(bindingFile, 'utf8'));
103
+ if (binding.schema !== 'ork.brain-cycle-scope/v1' || binding.version !== 1 || binding.thread !== thread)
104
+ throw Error('brain.scope.invalid');
105
+ const selection = (0, portfolio_1.assertCycleScope)(c.raiz, { projectId: binding.projectId, delivery: binding.delivery, initiativeIds: binding.initiativeIds });
106
+ scope = { ...scope, projectId: selection.projectId, initiativeIds: selection.initiativeIds };
107
+ }
108
+ return (0, company_brain_capture_1.capture)(scope, path.join(dir, 'brain'), (0, portfolio_1.readPortfolio)(c.raiz), (0, company_brain_source_1.readSourceFile)(path.join(dir, 'ledger.jsonl')), client, undefined, (0, company_brain_journal_1.readJournal)((0, project_state_1.stateFile)(c.raiz, 'portfolio.json') + '.journal.jsonl'));
109
+ }
110
+ if (sub === 'apply') {
111
+ const filename = value('plan');
112
+ if (!filename)
113
+ throw Error('brain.migration.plan-required');
114
+ const file = path.resolve(c.raiz, filename);
115
+ if (!file.startsWith(path.resolve(c.raiz) + path.sep))
116
+ throw Error('brain.migration.path-invalid');
117
+ const plan = JSON.parse((0, company_brain_source_1.readSourceFile)(file, 2 * 1024 * 1024).toString('utf8'));
118
+ return (0, company_brain_migration_1.applyBrain)(c, plan, value('expected-sha256') ?? '');
119
+ }
120
+ if (sub === 'rollback')
121
+ return (0, company_brain_migration_1.brainAdmin)(c)('rollback', { batch_id: value('batch') ?? '' });
122
+ throw Error('brain.operation.invalid');
123
+ }
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BRAIN_API = void 0;
4
+ exports.validateRequest = validateRequest;
5
+ exports.brainClient = brainClient;
6
+ const node_child_process_1 = require("node:child_process");
7
+ const project_state_1 = require("./project-state");
8
+ const company_brain_contract_1 = require("./company-brain-contract");
9
+ exports.BRAIN_API = 'orkmind.company-brain-api/v1';
10
+ function validateRequest(value) {
11
+ if (!value || typeof value !== 'object' || Array.isArray(value) || value.schema !== exports.BRAIN_API ||
12
+ Object.keys(value).some(k => !['schema', 'operation', 'payload'].includes(k)) ||
13
+ !['capabilities', 'ingest', 'get', 'query', 'receipts', 'head'].includes(value.operation))
14
+ throw Error('brain.api.invalid');
15
+ if (['ingest', 'query'].includes(value.operation)) {
16
+ const body = (0, company_brain_contract_1.validateContract)(value.payload);
17
+ if (body.schema !== `orkmind.company-brain-${value.operation === 'ingest' ? 'event' : 'selection'}/v1`)
18
+ throw Error('brain.api.invalid');
19
+ }
20
+ if (['get', 'receipts', 'head'].includes(value.operation)) {
21
+ const keys = ['tenant_id', value.operation === 'receipts' ? 'event_id' : 'id'];
22
+ if (!value.payload || Object.keys(value.payload).length !== 2 || keys.some(k => typeof value.payload[k] !== 'string' ||
23
+ !/^[a-zA-Z0-9][a-zA-Z0-9._:/-]{0,159}$/.test(value.payload[k])))
24
+ throw Error('brain.api.invalid');
25
+ }
26
+ return JSON.parse(JSON.stringify(value));
27
+ }
28
+ function brainClient(loaded, execute = node_child_process_1.spawnSync) {
29
+ const context = (0, project_state_1.memoryState)(loaded), config = context.loaded.manifesto.memory;
30
+ return raw => {
31
+ const request = validateRequest(raw);
32
+ const unavailable = (error) => ({ schema: exports.BRAIN_API, state: 'unavailable', error });
33
+ const dsn = config.database_url_env ? process.env[config.database_url_env] : undefined;
34
+ if (config.mode !== 'orkmind' || (!dsn && request.operation !== 'capabilities'))
35
+ return unavailable('brain.configuration.missing');
36
+ const payload = request.payload;
37
+ if (payload?.tenant_id !== undefined && payload.tenant_id !== config.tenant)
38
+ return { schema: exports.BRAIN_API, state: 'forbidden', error: 'brain.tenant.mismatch' };
39
+ const result = execute(config.cli, ['brain', 'request'], { cwd: loaded.raiz, encoding: 'utf8', timeout: Math.min(config.timeout_ms, 15000),
40
+ maxBuffer: 2 * 1024 * 1024, input: JSON.stringify(request), shell: false,
41
+ env: { PATH: process.env.PATH ?? '/usr/bin:/bin', PYTHONDONTWRITEBYTECODE: '1',
42
+ ORKMIND_DATABASE_URL: dsn ?? '', ORKMIND_BRAIN_TENANT: config.tenant } });
43
+ if (result.error || result.signal)
44
+ return unavailable('brain.transport.unavailable');
45
+ try {
46
+ const response = JSON.parse(String(result.stdout));
47
+ if (response.schema !== exports.BRAIN_API || !['ok', 'empty', 'unknown', 'unavailable', 'withheld', 'forbidden', 'conflict'].includes(response.state))
48
+ throw Error();
49
+ if (response.state === 'ok' && result.status !== 0)
50
+ throw Error();
51
+ if (request.operation === 'capabilities' && response.state === 'ok' && response.contract_hash !== company_brain_contract_1.CONTRACT_HASH)
52
+ return unavailable('brain.contract.mismatch');
53
+ if (response.error && !/^brain\.[a-z0-9.-]+$/.test(response.error))
54
+ return unavailable('brain.transport.invalid');
55
+ return response;
56
+ }
57
+ catch {
58
+ return unavailable('brain.transport.invalid');
59
+ }
60
+ };
61
+ }
@@ -0,0 +1,166 @@
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.digest = exports.canonical = exports.CONTRACT_HASH = void 0;
37
+ exports.validateContract = validateContract;
38
+ exports.validateCatalog = validateCatalog;
39
+ /** D6: consume o schema canônico OrkMind; transporte não concede autoridade. */
40
+ const fs = __importStar(require("node:fs"));
41
+ const path = __importStar(require("node:path"));
42
+ const node_crypto_1 = require("node:crypto");
43
+ const file = path.resolve(__dirname, __dirname.endsWith(`${path.sep}src`) ? '../../schemas/company-brain.schema.json' : '../schemas/company-brain.schema.json');
44
+ const schema = JSON.parse(fs.readFileSync(file, 'utf8'));
45
+ exports.CONTRACT_HASH = (0, node_crypto_1.createHash)('sha256').update(fs.readFileSync(file)).digest('hex');
46
+ const canonical = (v) => Array.isArray(v) ? `[${v.map(exports.canonical).join(',')}]`
47
+ : v !== null && typeof v === 'object' ? `{${Object.keys(v).sort().map(k => `${JSON.stringify(k)}:${(0, exports.canonical)(v[k])}`).join(',')}}`
48
+ : JSON.stringify(v);
49
+ exports.canonical = canonical;
50
+ const digest = (v) => (0, node_crypto_1.createHash)('sha256').update((0, exports.canonical)(v)).digest('hex');
51
+ exports.digest = digest;
52
+ const fail = () => { throw Error('brain.contract.invalid'); };
53
+ /** Closed vocabulary emitted by Pydantic. Unknown validation keywords fail closed. */
54
+ function matches(v, rule, depth = 0) {
55
+ if (depth > 32)
56
+ return false;
57
+ const known = ['$ref', 'anyOf', 'type', 'const', 'enum', 'properties', 'required', 'additionalProperties', 'items',
58
+ 'minItems', 'maxItems', 'minLength', 'maxLength', 'pattern', 'minimum', 'maximum', 'title', 'description'];
59
+ if (Object.keys(rule).some(k => !known.includes(k)))
60
+ return false;
61
+ if (rule.$ref)
62
+ return rule.$ref.startsWith('#/$defs/') && matches(v, schema.$defs[rule.$ref.slice(8)], depth + 1);
63
+ if (rule.anyOf)
64
+ return rule.anyOf.some((r) => matches(v, r, depth + 1));
65
+ if ('const' in rule && v !== rule.const || rule.enum && !rule.enum.includes(v))
66
+ return false;
67
+ switch (rule.type) {
68
+ case 'null': return v === null;
69
+ case 'boolean': return typeof v === 'boolean';
70
+ case 'integer': return Number.isSafeInteger(v) && (rule.minimum === undefined || v >= rule.minimum) && (rule.maximum === undefined || v <= rule.maximum);
71
+ case 'string': return typeof v === 'string' && (rule.minLength === undefined || [...v].length >= rule.minLength) &&
72
+ (rule.maxLength === undefined || [...v].length <= rule.maxLength) && (!rule.pattern || new RegExp(rule.pattern, 'u').test(v));
73
+ case 'array': return Array.isArray(v) && (rule.minItems === undefined || v.length >= rule.minItems) &&
74
+ (rule.maxItems === undefined || v.length <= rule.maxItems) && v.every(i => matches(i, rule.items, depth + 1));
75
+ case 'object': return v !== null && typeof v === 'object' && !Array.isArray(v) &&
76
+ (!rule.required || rule.required.every((k) => Object.hasOwn(v, k))) &&
77
+ Object.keys(v).every(k => Object.hasOwn(rule.properties ?? {}, k) ? matches(v[k], rule.properties[k], depth + 1) : rule.additionalProperties !== false);
78
+ default: return false;
79
+ }
80
+ }
81
+ function semantic(v) {
82
+ for (const [key, value] of Object.entries(v))
83
+ if ((key.endsWith('_at') || ['valid_from', 'valid_to'].includes(key)) && value !== null) {
84
+ const time = new Date(value);
85
+ if (!Number.isFinite(time.getTime()) || time.toISOString().slice(0, 19) !== value.slice(0, 19))
86
+ fail();
87
+ }
88
+ if (v.schema.endsWith('entity/v1')) {
89
+ if (!v.id.startsWith(v.kind + '-'))
90
+ fail();
91
+ if (v.kind === 'prod' ? v.parent_id !== null : !v.parent_id?.startsWith(v.kind === 'proj' ? 'prod-' : 'proj-'))
92
+ fail();
93
+ if (v.kind !== 'init' && v.depends_on.length || v.kind !== 'proj' && v.workspace_ids.length)
94
+ fail();
95
+ if (v.depends_on.some((d) => !d.startsWith('init-') || d === v.id))
96
+ fail();
97
+ if ((v.owner.raw === null) !== (v.owner.state === 'unknown'))
98
+ fail();
99
+ }
100
+ if (v.schema.endsWith('event/v1')) {
101
+ if ((0, exports.digest)(v.payload) !== v.payload_hash || (v.operation === 'tombstone') !== (v.payload === null))
102
+ fail();
103
+ if (v.payload !== null) {
104
+ validateContract(v.payload);
105
+ if (v.payload.tenant_id !== v.tenant_id || v.payload.id !== v.aggregate_id || v.payload.acl_ref !== v.acl_ref ||
106
+ (0, exports.canonical)(v.payload.source) !== (0, exports.canonical)(v.source) ||
107
+ v.operation !== (v.payload.schema.endsWith('entity/v1') ? 'upsert' : 'assert'))
108
+ fail();
109
+ }
110
+ }
111
+ if (v.schema.endsWith('assertion/v1')) {
112
+ if (v.object_id !== null && v.value !== null || v.valid_from && v.valid_to && v.valid_from >= v.valid_to)
113
+ fail();
114
+ }
115
+ if (v.schema.endsWith('migration/v1'))
116
+ for (const op of v.operations) {
117
+ validateContract(op.after);
118
+ if (op.id !== op.after.id || op.after.tenant_id !== v.tenant_id)
119
+ fail();
120
+ }
121
+ }
122
+ function validateContract(value) {
123
+ try {
124
+ if (!schema.anyOf.some((r) => matches(value, r)))
125
+ fail();
126
+ semantic(value);
127
+ return JSON.parse(JSON.stringify(value));
128
+ }
129
+ catch {
130
+ return fail();
131
+ }
132
+ }
133
+ function validateCatalog(values, scope) {
134
+ const entities = values.map(v => validateContract(v));
135
+ if (entities.some(e => e.schema !== 'orkmind.company-brain-entity/v1'))
136
+ fail();
137
+ const ids = new Map(entities.map(e => [e.id, e]));
138
+ if (ids.size !== entities.length || new Set(entities.map(e => e.tenant_id)).size > 1)
139
+ throw Error('brain.catalog.conflict');
140
+ for (const e of entities) {
141
+ if (e.parent_id !== null && !ids.has(e.parent_id))
142
+ throw Error('brain.catalog.parent-missing');
143
+ if (e.depends_on.some(d => !ids.has(d) || ids.get(d).parent_id !== e.parent_id))
144
+ throw Error('brain.catalog.dependency-invalid');
145
+ }
146
+ const visiting = new Set(), done = new Set();
147
+ const visit = (id) => {
148
+ if (visiting.has(id))
149
+ throw Error('brain.catalog.cycle');
150
+ if (done.has(id))
151
+ return;
152
+ visiting.add(id);
153
+ ids.get(id).depends_on.forEach(visit);
154
+ visiting.delete(id);
155
+ done.add(id);
156
+ };
157
+ entities.forEach(e => visit(e.id));
158
+ if (scope) {
159
+ const project = ids.get(scope.project_id), selected = scope.initiative_ids;
160
+ if (project?.kind !== 'proj' || !Array.isArray(selected) || !['project', 'initiatives'].includes(scope.delivery) ||
161
+ (scope.delivery === 'project' ? selected.length !== 0 : selected.length === 0) ||
162
+ selected.some(id => ids.get(id)?.parent_id !== project.id))
163
+ throw Error('brain.scope.invalid');
164
+ }
165
+ return entities;
166
+ }
@@ -0,0 +1,192 @@
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.bytesHash = void 0;
37
+ exports.withProcessLock = withProcessLock;
38
+ exports.durableAppend = durableAppend;
39
+ exports.atomicJson = atomicJson;
40
+ exports.readJournal = readJournal;
41
+ exports.recoverPortfolio = recoverPortfolio;
42
+ exports.replaceWithJournal = replaceWithJournal;
43
+ exports.portfolioMutation = portfolioMutation;
44
+ /** D9: journal recuperável para fontes em arquivo, sem reescrever o ledger. */
45
+ const fs = __importStar(require("node:fs"));
46
+ const path = __importStar(require("node:path"));
47
+ const node_crypto_1 = require("node:crypto");
48
+ const bytesHash = (v) => (0, node_crypto_1.createHash)('sha256').update(v).digest('hex');
49
+ exports.bytesHash = bytesHash;
50
+ function processIdentity(pid) {
51
+ try {
52
+ const stat = fs.readFileSync(`/proc/${pid}/stat`, 'utf8');
53
+ const fields = stat.slice(stat.lastIndexOf(')') + 2).split(' ');
54
+ if (fields[0] === 'Z')
55
+ return null;
56
+ return fs.readFileSync('/proc/sys/kernel/random/boot_id', 'utf8').trim() + ':' + fields[19];
57
+ }
58
+ catch (error) {
59
+ if (error.code === 'ENOENT')
60
+ return null;
61
+ throw Error('brain.lock.identity-unavailable');
62
+ }
63
+ }
64
+ /** Bakery lock with unique contender files. Reaping never unlinks a reusable lock name.
65
+ * Atomic publication + ticket order exclude live writers; PID start/boot identity handles reuse.
66
+ * Linux process identity is required; unidentifiable legacy locks fail closed. */
67
+ function withProcessLock(dir, busy, run) {
68
+ if (fs.existsSync(dir) && !fs.lstatSync(dir).isDirectory())
69
+ throw Error('brain.lock.recovery-required');
70
+ fs.mkdirSync(dir, { recursive: true });
71
+ const identity = processIdentity(process.pid);
72
+ if (!identity)
73
+ throw Error('brain.lock.identity-unavailable');
74
+ const name = (0, node_crypto_1.randomUUID)() + '.json', file = path.join(dir, name);
75
+ const owner = { pid: process.pid, identity, ticket: 0 };
76
+ const contenders = () => {
77
+ const result = [];
78
+ for (const entry of fs.readdirSync(dir).filter(n => n.endsWith('.json'))) {
79
+ let value;
80
+ try {
81
+ value = JSON.parse(fs.readFileSync(path.join(dir, entry), 'utf8'));
82
+ }
83
+ catch (error) {
84
+ if (error.code === 'ENOENT')
85
+ continue;
86
+ throw Error('brain.lock.corrupt');
87
+ }
88
+ if (!Number.isSafeInteger(value.pid) || value.pid < 1 || typeof value.identity !== 'string' ||
89
+ !Number.isSafeInteger(value.ticket) || value.ticket < 0)
90
+ throw Error('brain.lock.corrupt');
91
+ if (processIdentity(value.pid) !== value.identity) {
92
+ // UUID names are never reused: a concurrent reaper cannot remove a new owner.
93
+ try {
94
+ fs.unlinkSync(path.join(dir, entry));
95
+ }
96
+ catch (error) {
97
+ if (error.code !== 'ENOENT')
98
+ throw error;
99
+ }
100
+ }
101
+ else
102
+ result.push({ name: entry, ticket: value.ticket });
103
+ }
104
+ return result;
105
+ };
106
+ atomicJson(file, owner);
107
+ try {
108
+ owner.ticket = Math.max(0, ...contenders().map(c => c.ticket)) + 1;
109
+ atomicJson(file, owner);
110
+ if (contenders().some(c => c.name !== name && (c.ticket === 0 || c.ticket < owner.ticket ||
111
+ c.ticket === owner.ticket && c.name < name)))
112
+ throw Error(busy);
113
+ return run();
114
+ }
115
+ finally {
116
+ fs.unlinkSync(file);
117
+ }
118
+ }
119
+ function durableAppend(file, value) {
120
+ fs.mkdirSync(path.dirname(file), { recursive: true });
121
+ const fd = fs.openSync(file, 'a', 0o600);
122
+ try {
123
+ fs.writeFileSync(fd, JSON.stringify(value) + '\n');
124
+ fs.fsyncSync(fd);
125
+ }
126
+ finally {
127
+ fs.closeSync(fd);
128
+ }
129
+ syncDirectory(path.dirname(file));
130
+ }
131
+ function syncDirectory(dir) {
132
+ const fd = fs.openSync(dir, 'r');
133
+ try {
134
+ fs.fsyncSync(fd);
135
+ }
136
+ finally {
137
+ fs.closeSync(fd);
138
+ }
139
+ }
140
+ function atomicJson(file, value) {
141
+ fs.mkdirSync(path.dirname(file), { recursive: true });
142
+ const tmp = file + '.' + (0, node_crypto_1.randomUUID)() + '.tmp';
143
+ const fd = fs.openSync(tmp, 'wx', 0o600);
144
+ try {
145
+ fs.writeFileSync(fd, JSON.stringify(value, null, 2) + '\n');
146
+ fs.fsyncSync(fd);
147
+ }
148
+ finally {
149
+ fs.closeSync(fd);
150
+ }
151
+ fs.renameSync(tmp, file);
152
+ syncDirectory(path.dirname(file));
153
+ }
154
+ function readJournal(file) {
155
+ if (!fs.existsSync(file))
156
+ return [];
157
+ const text = fs.readFileSync(file, 'utf8');
158
+ if (!text.endsWith('\n'))
159
+ throw Error('brain.journal.incomplete');
160
+ try {
161
+ return text.trimEnd().split('\n').map(l => JSON.parse(l));
162
+ }
163
+ catch {
164
+ throw Error('brain.journal.corrupt');
165
+ }
166
+ }
167
+ function recoverPortfolio(file) {
168
+ const journal = file + '.journal.jsonl', entries = readJournal(journal);
169
+ const finished = new Set(entries.filter(e => e.type === 'confirmed' || e.type === 'aborted').map(e => e.id));
170
+ for (const intent of entries.filter(e => e.type === 'intent' && !finished.has(e.id))) {
171
+ const current = fs.existsSync(file) ? (0, exports.bytesHash)(fs.readFileSync(file)) : null;
172
+ if (current !== intent.beforeHash && current !== intent.afterHash)
173
+ throw Error('brain.journal.source-conflict');
174
+ durableAppend(journal, { type: current === intent.afterHash ? 'confirmed' : 'aborted', id: intent.id, recovered: true });
175
+ }
176
+ }
177
+ function replaceWithJournal(file, value, fault = () => { }) {
178
+ const beforeValue = fs.existsSync(file) ? JSON.parse(fs.readFileSync(file, 'utf8')) : null;
179
+ const beforeHash = fs.existsSync(file) ? (0, exports.bytesHash)(fs.readFileSync(file)) : null;
180
+ const afterHash = (0, exports.bytesHash)(JSON.stringify(value, null, 2) + '\n'), id = (0, node_crypto_1.randomUUID)();
181
+ durableAppend(file + '.journal.jsonl', { schema: 'ork.brain-source-intent/v1', type: 'intent', id,
182
+ observedAt: new Date().toISOString(), beforeHash, afterHash, beforeValue, value });
183
+ fault('intent');
184
+ atomicJson(file, value);
185
+ fault('source');
186
+ durableAppend(file + '.journal.jsonl', { type: 'confirmed', id });
187
+ fault('confirmed');
188
+ }
189
+ function portfolioMutation(file, run) {
190
+ fs.mkdirSync(path.dirname(file), { recursive: true });
191
+ return withProcessLock(file + '.lock', 'brain.journal.busy', () => { recoverPortfolio(file); return run(); });
192
+ }
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BRAIN_WRITE_TOOLS = exports.BRAIN_READ_TOOLS = void 0;
4
+ exports.registerBrainTools = registerBrainTools;
5
+ const zod_1 = require("zod");
6
+ const company_brain_cli_1 = require("./company-brain-cli");
7
+ exports.BRAIN_READ_TOOLS = ['ork_brain_status', 'ork_brain_inventory', 'ork_brain_get', 'ork_brain_query', 'ork_brain_receipts'];
8
+ exports.BRAIN_WRITE_TOOLS = ['ork_brain_sync', 'ork_brain_apply', 'ork_brain_rollback', 'ork_brain_bind'];
9
+ /** Installation supplies exact grants; presence of a tool is never activation or human identity. */
10
+ function registerBrainTools(register, load, checkThread, writeGrants = []) {
11
+ if (writeGrants.some(n => !exports.BRAIN_WRITE_TOOLS.includes(n)))
12
+ throw Error('brain.mcp.grant-invalid');
13
+ const id = zod_1.z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,79}$/), base = zod_1.z.object({ threadId: id }).strict();
14
+ const definitions = [['status', base], ['inventory', base], ['get', base.extend({ id })], ['receipts', base.extend({ id })],
15
+ ['query', base.extend({ ids: zod_1.z.array(id).max(1000).optional(), kinds: zod_1.z.array(zod_1.z.enum(['prod', 'proj', 'init', 'assertion'])).optional(), limit: zod_1.z.number().int().min(1).max(1000).optional() })],
16
+ ['sync', base], ['apply', base.extend({ plan: zod_1.z.string().min(1).max(512), expectedSha256: zod_1.z.string().regex(/^[a-f0-9]{64}$/) })],
17
+ ['rollback', base.extend({ batch: id })], ['bind', base.extend({ project: id, initiatives: zod_1.z.array(id).min(1).max(1000) })]];
18
+ for (const [operation, inputSchema] of definitions) {
19
+ const name = 'ork_brain_' + operation, writing = exports.BRAIN_WRITE_TOOLS.includes(name);
20
+ if (writing && !writeGrants.includes(name))
21
+ continue;
22
+ register(name, { description: `Company Brain ${operation}: estado e fontes pelo contrato canônico; identidade humana nunca vem de argumento.`, inputSchema: inputSchema.strict(), annotations: { readOnlyHint: !writing, destructiveHint: writing } }, async (args) => {
23
+ const c = load();
24
+ checkThread(args.threadId);
25
+ const options = { thread: args.threadId, json: true };
26
+ for (const [key, value] of Object.entries(args))
27
+ if (key !== 'threadId')
28
+ options[key === 'expectedSha256' ? 'expected-sha256' : key] = Array.isArray(value) ? value.join(',') : String(value);
29
+ const result = (0, company_brain_cli_1.runBrain)(c, operation, options);
30
+ return { content: [{ type: 'text', text: JSON.stringify(result) }], ...(['unavailable', 'forbidden', 'conflict'].includes(result.state) ? { isError: true } : {}) };
31
+ });
32
+ }
33
+ }
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.brainAdmin = brainAdmin;
4
+ exports.reconcileBrain = reconcileBrain;
5
+ exports.applyBrain = applyBrain;
6
+ const node_child_process_1 = require("node:child_process");
7
+ const project_state_1 = require("./project-state");
8
+ const portfolio_1 = require("./portfolio");
9
+ const company_brain_source_1 = require("./company-brain-source");
10
+ const company_brain_journal_1 = require("./company-brain-journal");
11
+ const company_brain_contract_1 = require("./company-brain-contract");
12
+ function brainAdmin(c) {
13
+ const config = (0, project_state_1.memoryState)(c).loaded.manifesto.memory;
14
+ return (operation, payload) => {
15
+ const dsn = config.database_url_env ? process.env[config.database_url_env] : undefined;
16
+ if (config.mode !== 'orkmind' || !dsn)
17
+ throw Error('brain.configuration.missing');
18
+ const r = (0, node_child_process_1.spawnSync)(config.cli, ['brain', 'migration'], { cwd: c.raiz, shell: false, encoding: 'utf8', timeout: Math.min(config.timeout_ms, 15000), maxBuffer: 2 * 1024 * 1024,
19
+ input: JSON.stringify({ schema: 'orkmind.company-brain-admin/v1', operation, payload }),
20
+ env: { PATH: process.env.PATH ?? '/usr/bin:/bin', PYTHONDONTWRITEBYTECODE: '1', ORKMIND_DATABASE_URL: dsn, ORKMIND_BRAIN_TENANT: config.tenant } });
21
+ try {
22
+ const response = JSON.parse(r.stdout);
23
+ if (r.status !== 0 || response.state !== 'ok')
24
+ throw Error();
25
+ return response.result;
26
+ }
27
+ catch {
28
+ throw Error('brain.migration.unconfirmed');
29
+ }
30
+ };
31
+ }
32
+ function reconcileBrain(c, scope, batch, transport = brainAdmin(c)) {
33
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,79}$/.test(batch))
34
+ throw Error('brain.migration.batch-invalid');
35
+ const file = (0, project_state_1.stateFile)(c.raiz, 'portfolio.json'), before = (0, company_brain_journal_1.bytesHash)((0, company_brain_source_1.readSourceFile)(file));
36
+ const catalog = (0, portfolio_1.readPortfolio)(c.raiz);
37
+ const latest = new Map();
38
+ for (const entity of (0, company_brain_source_1.portfolioHistory)(catalog, scope, (0, company_brain_journal_1.readJournal)(file + '.journal.jsonl')))
39
+ latest.set(entity.id, entity);
40
+ const currentIds = new Set([...catalog.products, ...catalog.projects, ...catalog.initiatives].map(e => e.id));
41
+ const entities = [...latest.values()].filter(e => currentIds.has(e.id));
42
+ if ((0, company_brain_journal_1.bytesHash)((0, company_brain_source_1.readSourceFile)(file)) !== before)
43
+ throw Error('brain.migration.source-changed');
44
+ const plan = (0, company_brain_contract_1.validateContract)(transport('plan', { entities, batch_id: batch, source_hash: before, observed_at: catalog.updatedAt }));
45
+ if (plan.schema !== 'orkmind.company-brain-migration/v1' || plan.source_hash !== before || plan.tenant_id !== scope.tenant ||
46
+ plan.operations.length !== entities.length || new Set(plan.operations.map((o) => o.id)).size !== entities.length ||
47
+ entities.some(e => !plan.operations.some((o) => o.id === e.id && (0, company_brain_contract_1.digest)(o.after) === (0, company_brain_contract_1.digest)(e))))
48
+ throw Error('brain.migration.plan-invalid');
49
+ return { plan, sha256: (0, company_brain_contract_1.digest)(plan) };
50
+ }
51
+ function applyBrain(c, plan, expected, transport = brainAdmin(c)) {
52
+ const validated = (0, company_brain_contract_1.validateContract)(plan);
53
+ const config = (0, project_state_1.memoryState)(c).loaded.manifesto.memory;
54
+ if (validated.schema !== 'orkmind.company-brain-migration/v1' || validated.tenant_id !== config.tenant || (0, company_brain_contract_1.digest)(plan) !== expected)
55
+ throw Error('brain.migration.stale');
56
+ const current = (0, company_brain_journal_1.bytesHash)((0, company_brain_source_1.readSourceFile)((0, project_state_1.stateFile)(c.raiz, 'portfolio.json')));
57
+ if (current !== validated.source_hash)
58
+ throw Error('brain.migration.source-changed');
59
+ return transport('apply', { plan: validated, expected_hash: expected, current_source_hash: current });
60
+ }