@orkastery/cli 0.4.3 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/adapters/README.md +11 -0
- package/adapters/claude-code/.claude-plugin/plugin.json +3 -1
- package/adapters/claude-code/agents/ork-check.md +1 -1
- package/adapters/claude-code/agents/ork-go.md +2 -2
- package/adapters/claude-code/agents/ork-goal.md +1 -1
- package/adapters/claude-code/agents/ork-master.md +1 -1
- package/adapters/claude-code/agents/ork-plan.md +1 -1
- package/adapters/claude-code/agents/ork-ship.md +1 -1
- package/adapters/claude-code/commands/master.md +11 -8
- package/adapters/claude-code/commands/ork.md +8 -0
- package/adapters/claude-code/commands/ship.md +2 -2
- package/adapters/codex/skills/ork/SKILL.md +12 -1
- package/adapters/hermes/README.md +3 -0
- package/adapters/hermes/bin/ork-abrir-thread.sh +17 -2
- package/adapters/hermes/bin/ork-brain.sh +3 -0
- package/adapters/hermes/bin/ork-maestro.sh +4 -1
- package/adapters/hermes/bin/ork-network-roadmap.sh +11 -0
- package/adapters/hermes/bin/ork-roadmap-status.sh +9 -0
- package/adapters/hermes/hermes.plugin.json +2 -0
- package/adapters/hermes/hitl-ingress/__init__.py +36 -3
- package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +19 -20
- package/adapters/openclaw/README.md +34 -2
- package/adapters/openclaw/dist/hitl-ingress.js +37 -3
- package/adapters/openclaw/dist/index.js +92 -20
- package/adapters/openclaw/openclaw.plugin.json +10 -1
- package/adapters/openclaw/package.json +2 -2
- package/adapters/openclaw/src/hitl-ingress.ts +32 -3
- package/adapters/openclaw/src/index.ts +108 -20
- package/assets/docs/padroes/documentacao-de-produto.md +2 -2
- package/assets/docs/padroes/roadmap-de-produto.md +1 -1
- package/assets/docs/produto/_modelo-feature.md +2 -0
- package/assets/orkmind_bridge.py +214 -1
- package/dist/adapters/claude-bg.js +6 -3
- package/dist/board.js +74 -0
- package/dist/branch-de-estado.js +5 -4
- package/dist/busca-semantica.js +161 -0
- package/dist/ci.js +141 -10
- package/dist/company-brain-cli.js +27 -2
- package/dist/company-brain-context.js +132 -0
- package/dist/company-brain-dossie.js +376 -0
- package/dist/company-brain-mcp.js +4 -1
- package/dist/conducao.js +71 -18
- package/dist/decisao-autonoma.js +42 -0
- package/dist/docs.js +64 -22
- package/dist/doctor.js +33 -0
- package/dist/entrega-pr.js +109 -5
- package/dist/experiencia-instalacao.js +256 -0
- package/dist/experiencia.js +72 -0
- package/dist/fabrica-estado.js +43 -10
- package/dist/fechamento.js +105 -0
- package/dist/forja.js +435 -0
- package/dist/gates.js +9 -1
- package/dist/hitl-classificacao.js +11 -1
- package/dist/hitl-contract.js +50 -7
- package/dist/hitl-curto.js +158 -0
- package/dist/hitl-gates.js +67 -1
- package/dist/hitl-lote.js +30 -15
- package/dist/hitl-presentation.js +10 -7
- package/dist/hitl-resumo.js +33 -12
- package/dist/hitl-texto-livre.js +150 -0
- package/dist/hosts.js +86 -1
- package/dist/index.js +602 -38
- package/dist/indice-vetorial.js +317 -0
- package/dist/init.js +13 -0
- package/dist/integracoes-locais.js +1 -1
- package/dist/intelligence-benchmark-contract.js +570 -0
- package/dist/intelligence-graph-cli.js +430 -0
- package/dist/intelligence-graph-contract.js +577 -0
- package/dist/intelligence-graph-extract-md.js +469 -0
- package/dist/intelligence-graph-extract-ts.js +810 -0
- package/dist/intelligence-graph-extract.js +369 -0
- package/dist/intelligence-graph-index.js +512 -0
- package/dist/intelligence-graph-parsers.js +327 -0
- package/dist/intelligence-graph-query.js +344 -0
- package/dist/intelligence-graph-repo.js +187 -0
- package/dist/leases.js +84 -0
- package/dist/ledger.js +14 -0
- package/dist/maestro-cli.js +10 -1
- package/dist/maestro-contract.js +5 -1
- package/dist/maestro-discovery.js +16 -2
- package/dist/maestro-snapshot.js +6 -2
- package/dist/manifest.js +142 -5
- package/dist/master-batch.js +3 -2
- package/dist/master-digest.js +2 -2
- package/dist/master-nota.js +203 -0
- package/dist/master.js +4 -0
- package/dist/mcp-experiencia.js +164 -0
- package/dist/mcp-git.js +4 -0
- package/dist/mcp-install.js +3 -1
- package/dist/mcp-maestro.js +3 -2
- package/dist/mcp-server.js +84 -5
- package/dist/memoria.js +147 -1
- package/dist/network-roadmap.js +907 -0
- package/dist/ocupacao.js +38 -2
- package/dist/onboarding.js +113 -7
- package/dist/orkmind.js +165 -9
- package/dist/phase.js +166 -13
- package/dist/policies.js +2 -0
- package/dist/projeto-alvo.js +551 -0
- package/dist/prompts.js +1 -1
- package/dist/pulse-consentimento.js +4 -2
- package/dist/pulse-delivery.js +5 -0
- package/dist/pulse-resposta.js +674 -49
- package/dist/pulse.js +21 -0
- package/dist/retry.js +51 -8
- package/dist/roadmap-reservas.js +262 -20
- package/dist/roadmap-status.js +213 -0
- package/dist/thread-close.js +3 -0
- package/dist/verify.js +4 -3
- package/eval/casos/orchestration-experience-pt-br.json +48 -0
- package/eval/casos/orchestration-experience.json +48 -0
- package/package.json +1 -1
- package/schemas/code-artifact-graph.v1.schema.json +581 -0
- package/schemas/graph-benchmark.v1.schema.json +2240 -0
- package/schemas/maestro-snapshot.schema.json +23 -0
- package/skills/README.md +7 -5
- package/skills/core/onboarding/SKILL.md +7 -0
- package/skills/core/orchestration-experience/SKILL.md +113 -0
- package/skills/core/orchestration-experience-pt-br/SKILL.md +112 -0
- package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
- package/skills/phases/go-implementation/SKILL.md +2 -0
package/dist/ci.js
CHANGED
|
@@ -33,14 +33,21 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.BUNDLE_LEGADO = exports.DIR_DO_BUNDLE = void 0;
|
|
36
37
|
exports.motivoDiferimentoCi = motivoDiferimentoCi;
|
|
37
38
|
exports.repositorioGitHub = repositorioGitHub;
|
|
38
39
|
exports.consultarCi = consultarCi;
|
|
40
|
+
exports.consultarCiDoRepositorio = consultarCiDoRepositorio;
|
|
39
41
|
exports.executarCi = executarCi;
|
|
42
|
+
exports.arquivoDoBundle = arquivoDoBundle;
|
|
40
43
|
exports.lintDoBundle = lintDoBundle;
|
|
44
|
+
exports.destinoDoBundle = destinoDoBundle;
|
|
41
45
|
exports.prepararBundleCi = prepararBundleCi;
|
|
46
|
+
exports.bundleDaBranch = bundleDaBranch;
|
|
47
|
+
exports.executarCiDaBranch = executarCiDaBranch;
|
|
42
48
|
exports.executarBundleCi = executarBundleCi;
|
|
43
49
|
const util_1 = require("./util");
|
|
50
|
+
const worktree_1 = require("./worktree");
|
|
44
51
|
const verify_1 = require("./verify");
|
|
45
52
|
const verify_2 = require("./verify");
|
|
46
53
|
const claims_1 = require("./claims");
|
|
@@ -64,6 +71,21 @@ const CASOS_DE_VERIFICADOR_LOCAIS = [
|
|
|
64
71
|
];
|
|
65
72
|
const CASOS_DE_VERIFICADOR_COM_INTEGRACAO_LOCAL = [
|
|
66
73
|
/verify-check-c2-b3\.cjs\s+channel-offer(?:\s|$)/,
|
|
74
|
+
// I-38: a prova da busca por significado le a memoria do tenant pela DSN do manifesto.
|
|
75
|
+
/prova-busca-semantica\.sh(?:\s|$)/,
|
|
76
|
+
// RM-051: o ensaio instala o tarball com npm --offline e resolve as dependencias pelo cache do npm
|
|
77
|
+
// da estacao; o runner hospedado so tem os tarballs do npm ci, sem os metadados do registro.
|
|
78
|
+
/testar-experiencia-e2e\.cjs(?:\s|$)/,
|
|
79
|
+
];
|
|
80
|
+
/**
|
|
81
|
+
* I-38: o runner hospedado nao oferece OrkMind nem a base do tenant. Claim que chama a memoria
|
|
82
|
+
* pelo CLI (a DSN vem do manifesto), que exige o OrkMind instalado ou que roda a suite inteira
|
|
83
|
+
* (ela inclui `TESTES_DE_INTEGRACAO_LOCAL`; o runner roda `test:ci`) fica para a estacao.
|
|
84
|
+
*/
|
|
85
|
+
const USA_A_ESTACAO = [
|
|
86
|
+
/(?:^|\s)memory\s+(?:status|index|search|sync|migrate|inventory)(?=\s|$)/,
|
|
87
|
+
/command -v orkmind(?=[\s)"']|$)/,
|
|
88
|
+
/npm\s+--prefix\s+core\s+test(?![:\w-])/,
|
|
67
89
|
];
|
|
68
90
|
function exigeIntegracaoLocal(command) {
|
|
69
91
|
return [...command.matchAll(/(?:^|[\s/])([a-z0-9-]+\.test\.js)(?=$|[\s;&|])/g)]
|
|
@@ -90,7 +112,8 @@ function motivoDiferimentoCi(claim, raiz) {
|
|
|
90
112
|
return 'host-runtime-required';
|
|
91
113
|
}
|
|
92
114
|
if (claim.verificar.some((command) => exigeIntegracaoLocal(command)
|
|
93
|
-
|| CASOS_DE_VERIFICADOR_COM_INTEGRACAO_LOCAL.some((caso) => caso.test(command))
|
|
115
|
+
|| CASOS_DE_VERIFICADOR_COM_INTEGRACAO_LOCAL.some((caso) => caso.test(command))
|
|
116
|
+
|| USA_A_ESTACAO.some((caso) => caso.test(command)))) {
|
|
94
117
|
return 'local-integration-required';
|
|
95
118
|
}
|
|
96
119
|
return null;
|
|
@@ -114,6 +137,13 @@ function consultarCi(carregado, sha, remoto = 'origin', executor = executorPadra
|
|
|
114
137
|
const repository = remote.ok ? repositorioGitHub(remote.stdout) : null;
|
|
115
138
|
if (!repository)
|
|
116
139
|
return { schema: 'ork.ci-status/v1', required: true, ok: false, provider: 'github', repository: null, sha, context, state: 'unavailable', url: null, detail: `remoto ${remoto} não é um repositório GitHub reconhecível` };
|
|
140
|
+
return consultarCiDoRepositorio(repository, sha, context, executor);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* O check `context` no `sha` de um repositorio GitHub ja resolvido: o do remoto do projeto ou, desde a
|
|
144
|
+
* RM-037 (rm037defeito, defeito 5), um repositorio externo declarado em `ci.external_repositories`.
|
|
145
|
+
*/
|
|
146
|
+
function consultarCiDoRepositorio(repository, sha, context, executor = executorPadrao) {
|
|
117
147
|
const result = executor({ repository, sha, context });
|
|
118
148
|
if (!result.ok)
|
|
119
149
|
return { schema: 'ork.ci-status/v1', required: true, ok: false, provider: 'github', repository, sha, context, state: 'unavailable', url: null, detail: (result.stderr || result.stdout || 'consulta ao GitHub falhou').trim().slice(0, 400) };
|
|
@@ -148,6 +178,28 @@ function executarCi(carregado, threadId) {
|
|
|
148
178
|
verify,
|
|
149
179
|
};
|
|
150
180
|
}
|
|
181
|
+
/**
|
|
182
|
+
* RM-037 (rm037noite, defeito 1): um bundle por thread. O `.ork-ci/bundle.json` era o mesmo caminho em
|
|
183
|
+
* toda thread, entao toda PR conflitava com todas as outras e cada merge pedia merge da main, `ork ci
|
|
184
|
+
* prepare` e CI de novo na proxima. Cada thread grava agora o proprio `.ork-ci/<thread>.json`, e o CI
|
|
185
|
+
* acha o da thread pelo nome da branch (`ork ci run --branch`). O bundle nao nasce no CI porque as
|
|
186
|
+
* claims ficam no `.orkastery/` da maquina que conduz, fora do git.
|
|
187
|
+
*/
|
|
188
|
+
exports.DIR_DO_BUNDLE = '.ork-ci';
|
|
189
|
+
/** O caminho unico de antes: so vale quando o `thread` dele bate com a branch (PR aberta antes da mudanca). */
|
|
190
|
+
exports.BUNDLE_LEGADO = 'bundle.json';
|
|
191
|
+
const THREAD_DO_BUNDLE = /^ork-[a-z0-9-]+$/;
|
|
192
|
+
/** O nome do arquivo do bundle da thread, dentro de `.ork-ci/`. */
|
|
193
|
+
function arquivoDoBundle(threadId) {
|
|
194
|
+
if (!THREAD_DO_BUNDLE.test(threadId))
|
|
195
|
+
throw new Error(`ci.bundle: "${threadId}" nao e id de thread`);
|
|
196
|
+
return `${threadId}.json`;
|
|
197
|
+
}
|
|
198
|
+
function comandosDoBundle(carregado) {
|
|
199
|
+
return carregado.manifesto.ci.command
|
|
200
|
+
? [{ name: 'ci', command: carregado.manifesto.ci.command }]
|
|
201
|
+
: (0, verify_2.comandosDoManifesto)(carregado.manifesto).map(({ nome, comando }) => ({ name: nome, command: comando }));
|
|
202
|
+
}
|
|
151
203
|
/**
|
|
152
204
|
* I-53 (RM-037, P6): o lint no `ci prepare`. Claim nascida sob a regra (com o campo `lint`) que
|
|
153
205
|
* roda a suite inteira e recusada: o bundle vai para o runner hospedado e reprovaria la, depois de
|
|
@@ -166,10 +218,20 @@ function lintDoBundle(claims) {
|
|
|
166
218
|
}
|
|
167
219
|
return { recusas, avisos };
|
|
168
220
|
}
|
|
221
|
+
/**
|
|
222
|
+
* RM-037 (rm037defeito, defeito 6): onde o bundle nasce. Ele e artefato da branch da thread (o commit
|
|
223
|
+
* `ci(<thread>)` vai no PR dela), entao o destino e a worktree da thread, rodando o `ork` da raiz ou da
|
|
224
|
+
* worktree. Gravado na raiz, ele sujava o checkout compartilhado com o bundle de outra thread, e o
|
|
225
|
+
* `git ls-files` da raiz adiava claim de arquivo que so existe na branch da thread.
|
|
226
|
+
*/
|
|
227
|
+
function destinoDoBundle(carregado, threadId) {
|
|
228
|
+
const worktree = (0, thread_1.lerThread)(carregado.raiz, threadId).worktree;
|
|
229
|
+
return worktree && fs.existsSync(worktree) ? worktree : carregado.raiz;
|
|
230
|
+
}
|
|
169
231
|
function prepararBundleCi(carregado, threadId, opcoes = {}) {
|
|
170
|
-
const commands = carregado
|
|
171
|
-
|
|
172
|
-
|
|
232
|
+
const commands = comandosDoBundle(carregado);
|
|
233
|
+
const nomeDoArquivo = arquivoDoBundle(threadId);
|
|
234
|
+
const branch = (0, worktree_1.branchDaWorktree)((0, thread_1.lerThread)(carregado.raiz, threadId));
|
|
173
235
|
const activeClaims = (0, claims_1.lerClaims)(carregado.raiz, threadId).filter((claim) => claim.estado !== 'retirada');
|
|
174
236
|
const lint = lintDoBundle(activeClaims);
|
|
175
237
|
if (lint.recusas.length || lint.avisos.length) {
|
|
@@ -181,13 +243,20 @@ function prepararBundleCi(carregado, threadId, opcoes = {}) {
|
|
|
181
243
|
throw new Error(`claims.lint: o bundle nao foi gerado; retire a claim e registre de novo com o comando focado:\n ` +
|
|
182
244
|
lint.recusas.join('\n '));
|
|
183
245
|
}
|
|
246
|
+
const destino = destinoDoBundle(carregado, threadId);
|
|
247
|
+
// Achado S8 do CHECK: a worktree registrada que sumiu nao e silencio; o bundle na raiz vem com aviso.
|
|
248
|
+
const registrada = (0, thread_1.lerThread)(carregado.raiz, threadId).worktree;
|
|
249
|
+
if (registrada && destino !== registrada) {
|
|
250
|
+
opcoes.aoAvisar?.(`a worktree ${registrada} da thread nao existe mais; o bundle vai para a raiz do projeto`);
|
|
251
|
+
}
|
|
184
252
|
const classified = activeClaims.map((claim) => ({
|
|
185
253
|
claim,
|
|
186
|
-
reason: motivoDiferimentoCi(claim,
|
|
254
|
+
reason: motivoDiferimentoCi(claim, destino),
|
|
187
255
|
}));
|
|
188
256
|
const bundle = {
|
|
189
257
|
schema: 'ork.ci-bundle/v1',
|
|
190
258
|
thread: threadId,
|
|
259
|
+
branch,
|
|
191
260
|
base: carregado.manifesto.worktree.base_branch,
|
|
192
261
|
claims: classified.filter((item) => item.reason === null).map((item) => item.claim),
|
|
193
262
|
deferredClaims: classified
|
|
@@ -195,20 +264,82 @@ function prepararBundleCi(carregado, threadId, opcoes = {}) {
|
|
|
195
264
|
.map((item) => ({ id: item.claim.id, reason: item.reason })),
|
|
196
265
|
commands,
|
|
197
266
|
};
|
|
198
|
-
const dir = path.join(
|
|
267
|
+
const dir = path.join(destino, '.ork-ci');
|
|
199
268
|
fs.mkdirSync(dir, { recursive: true });
|
|
200
|
-
const file = path.join(dir,
|
|
269
|
+
const file = path.join(dir, nomeDoArquivo);
|
|
201
270
|
fs.writeFileSync(file, JSON.stringify(bundle, null, 2) + '\n', 'utf8');
|
|
202
271
|
return file;
|
|
203
272
|
}
|
|
204
|
-
function
|
|
205
|
-
const absolute = path.resolve(
|
|
273
|
+
function lerBundle(raiz, file) {
|
|
274
|
+
const absolute = path.resolve(raiz, file);
|
|
206
275
|
const stat = fs.statSync(absolute);
|
|
207
276
|
if (!stat.isFile() || stat.size > 1024 * 1024)
|
|
208
277
|
throw new Error('bundle de CI ausente ou acima de 1 MiB');
|
|
209
278
|
const bundle = JSON.parse(fs.readFileSync(absolute, 'utf8'));
|
|
210
|
-
if (bundle.schema !== 'ork.ci-bundle/v1' ||
|
|
279
|
+
if (bundle.schema !== 'ork.ci-bundle/v1' || !THREAD_DO_BUNDLE.test(bundle.thread) || !Array.isArray(bundle.claims) || !Array.isArray(bundle.commands))
|
|
211
280
|
throw new Error('bundle de CI inválido');
|
|
281
|
+
return bundle;
|
|
282
|
+
}
|
|
283
|
+
const casaComBranch = (thread, branch) => branch === `ork/${thread}` || branch.startsWith(`ork/${thread}-`);
|
|
284
|
+
/**
|
|
285
|
+
* O bundle da branch, entre os `.ork-ci/*.json` do checkout. Vale o de `branch` igual. O bundle sem
|
|
286
|
+
* `branch` (o `bundle.json` legado, de antes da RM-037) vale pela thread, quando a branch e
|
|
287
|
+
* `ork/<thread>` ou `ork/<thread>-*` (a mais longa ganha: `ork-a` nao leva a branch da `ork-ab`).
|
|
288
|
+
* Bundle novo de outra branch nunca vale, nem da mesma thread (achado A4 do CHECK 1), e o arquivo da
|
|
289
|
+
* thread da branch que nao abre reprova em vez de ceder a vez a outro. `null` quando nenhum bate.
|
|
290
|
+
*/
|
|
291
|
+
function bundleDaBranch(raiz, branch) {
|
|
292
|
+
const dir = path.join(raiz, exports.DIR_DO_BUNDLE);
|
|
293
|
+
if (!fs.existsSync(dir))
|
|
294
|
+
return null;
|
|
295
|
+
let melhor = null;
|
|
296
|
+
let quebrado = null;
|
|
297
|
+
for (const nome of fs.readdirSync(dir).filter((n) => n.endsWith('.json')).sort()) {
|
|
298
|
+
const arquivo = path.posix.join(exports.DIR_DO_BUNDLE, nome);
|
|
299
|
+
let bundle;
|
|
300
|
+
try {
|
|
301
|
+
bundle = lerBundle(raiz, arquivo);
|
|
302
|
+
}
|
|
303
|
+
catch (e) {
|
|
304
|
+
if (!quebrado && nome !== exports.BUNDLE_LEGADO && casaComBranch(nome.slice(0, -'.json'.length), branch)) {
|
|
305
|
+
quebrado = new Error(`ci.bundle.invalido: ${arquivo} e o bundle da branch ${branch} e nao abre ` +
|
|
306
|
+
`(${e.message}); rode ork ci prepare <thread> de novo e versione o arquivo`);
|
|
307
|
+
}
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
if (nome !== exports.BUNDLE_LEGADO && nome !== `${bundle.thread}.json`)
|
|
311
|
+
continue;
|
|
312
|
+
const peso = bundle.branch === branch ? Number.MAX_SAFE_INTEGER
|
|
313
|
+
: !bundle.branch && casaComBranch(bundle.thread, branch) ? bundle.thread.length : 0;
|
|
314
|
+
if (peso > 0 && (!melhor || peso > melhor.peso))
|
|
315
|
+
melhor = { arquivo, bundle, peso };
|
|
316
|
+
}
|
|
317
|
+
// Sugestao 4 do CHECK 2: o arquivo quebrado so reprova se nenhum bundle valido for da branch exata.
|
|
318
|
+
if (quebrado && melhor?.peso !== Number.MAX_SAFE_INTEGER)
|
|
319
|
+
throw quebrado;
|
|
320
|
+
return melhor ? { arquivo: melhor.arquivo, bundle: melhor.bundle } : null;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* `ork ci run --branch <branch>`: o CHECK do CI pela branch. Branch de thread (`ork/*`) roda o bundle
|
|
324
|
+
* dela e reprova sem ele; outra branch (a `main`, por exemplo) nao tem thread e roda so os comandos
|
|
325
|
+
* do manifesto.
|
|
326
|
+
*/
|
|
327
|
+
function executarCiDaBranch(carregado, branch) {
|
|
328
|
+
const achado = bundleDaBranch(carregado.raiz, branch);
|
|
329
|
+
if (achado)
|
|
330
|
+
return { ...rodarBundle(carregado, achado.bundle), branch, bundle: achado.arquivo };
|
|
331
|
+
if (branch.startsWith('ork/')) {
|
|
332
|
+
throw new Error(`ci.bundle.ausente: a branch ${branch} e de thread e nao tem bundle em ${exports.DIR_DO_BUNDLE}/; ` +
|
|
333
|
+
'rode ork ci prepare <thread> na worktree e versione o arquivo');
|
|
334
|
+
}
|
|
335
|
+
const semThread = { schema: 'ork.ci-bundle/v1', thread: '', base: carregado.manifesto.worktree.base_branch,
|
|
336
|
+
claims: [], commands: comandosDoBundle(carregado) };
|
|
337
|
+
return { ...rodarBundle(carregado, semThread), thread: null, branch, bundle: null };
|
|
338
|
+
}
|
|
339
|
+
function executarBundleCi(carregado, file = path.posix.join(exports.DIR_DO_BUNDLE, exports.BUNDLE_LEGADO)) {
|
|
340
|
+
return rodarBundle(carregado, lerBundle(carregado.raiz, file));
|
|
341
|
+
}
|
|
342
|
+
function rodarBundle(carregado, bundle) {
|
|
212
343
|
// O CI roda o mesmo preparo do `ork verify` local (I-54), uma vez e antes das claims: a claim
|
|
213
344
|
// que passa na maquina de quem entrega passa aqui pelo mesmo caminho. Como no verify, o
|
|
214
345
|
// preparo nao e condicao de claim; ele so deixa a compilacao pronta e sai no resultado.
|
|
@@ -48,7 +48,9 @@ const company_brain_source_1 = require("./company-brain-source");
|
|
|
48
48
|
const company_brain_migration_1 = require("./company-brain-migration");
|
|
49
49
|
const company_brain_journal_1 = require("./company-brain-journal");
|
|
50
50
|
const company_brain_contract_1 = require("./company-brain-contract");
|
|
51
|
-
|
|
51
|
+
const company_brain_context_1 = require("./company-brain-context");
|
|
52
|
+
const company_brain_dossie_1 = require("./company-brain-dossie");
|
|
53
|
+
exports.BRAIN_READ = ['status', 'inventory', 'get', 'query', 'receipts', 'reconcile', 'context', 'dossie'];
|
|
52
54
|
exports.BRAIN_WRITE = ['sync', 'apply', 'rollback', 'bind'];
|
|
53
55
|
function runBrain(c, sub, options, positionals = [], transport) {
|
|
54
56
|
const context = (0, project_state_1.memoryState)(c);
|
|
@@ -56,7 +58,7 @@ function runBrain(c, sub, options, positionals = [], transport) {
|
|
|
56
58
|
const config = c.manifesto.memory;
|
|
57
59
|
if (![...exports.BRAIN_READ, ...exports.BRAIN_WRITE].includes(sub))
|
|
58
60
|
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']);
|
|
61
|
+
const allowed = new Set(['thread', 'json', 'id', 'ids', 'kinds', 'workspaces', 'limit', 'offset', 'batch', 'dry-run', 'plan', 'expected-sha256', 'project', 'initiatives', 'decisao']);
|
|
60
62
|
if (Object.keys(options).some(k => !allowed.has(k)))
|
|
61
63
|
throw Error('brain.argument.invalid');
|
|
62
64
|
const value = (name) => typeof options[name] === 'string' ? options[name] : undefined;
|
|
@@ -80,6 +82,29 @@ function runBrain(c, sub, options, positionals = [], transport) {
|
|
|
80
82
|
throw Error('brain.migration.dry-run-required');
|
|
81
83
|
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
84
|
}
|
|
85
|
+
// B4.1: só leitura, por isso fica antes da ativação. Sem --ids, vale o escopo vinculado da thread.
|
|
86
|
+
if (sub === 'context') {
|
|
87
|
+
let ids = (value('ids') ?? '').split(',').map(s => s.trim()).filter(Boolean);
|
|
88
|
+
const bindingFile = path.join((0, thread_1.dirThread)(c.raiz, thread), 'brain-scope.json');
|
|
89
|
+
if (!ids.length && fs.existsSync(bindingFile)) {
|
|
90
|
+
let binding;
|
|
91
|
+
try {
|
|
92
|
+
binding = JSON.parse(fs.readFileSync(bindingFile, 'utf8'));
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
throw Error('brain.scope.invalid');
|
|
96
|
+
}
|
|
97
|
+
// Mesma conferência do sync: o escopo vinculado vale só se for desta thread e do formato gravado pelo bind.
|
|
98
|
+
if (binding?.schema !== 'ork.brain-cycle-scope/v1' || binding.version !== 1 || binding.thread !== thread || typeof binding.projectId !== 'string' ||
|
|
99
|
+
!Array.isArray(binding.initiativeIds) || binding.initiativeIds.some((i) => typeof i !== 'string'))
|
|
100
|
+
throw Error('brain.scope.invalid');
|
|
101
|
+
ids = [binding.projectId, ...binding.initiativeIds];
|
|
102
|
+
}
|
|
103
|
+
return (0, company_brain_context_1.buildContext)(c, ids, client, thread);
|
|
104
|
+
}
|
|
105
|
+
// K3.1: dossiê de decisão, só leitura como o contexto; decidir continua no registro e no ingresso.
|
|
106
|
+
if (sub === 'dossie')
|
|
107
|
+
return (0, company_brain_dossie_1.buildDossie)(c, thread, options.decisao === undefined ? undefined : value('decisao') ?? '', client);
|
|
83
108
|
// Existing reviewed activation plus dedicated Brain DB grants: neither grants the other.
|
|
84
109
|
(0, write_activation_1.exigirAtivacao)(c, thread, 'memory');
|
|
85
110
|
const dir = (0, thread_1.dirThread)(c.raiz, thread);
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CONTEXT_SCHEMA = void 0;
|
|
4
|
+
exports.buildContext = buildContext;
|
|
5
|
+
const portfolio_1 = require("./portfolio");
|
|
6
|
+
const company_brain_client_1 = require("./company-brain-client");
|
|
7
|
+
const company_brain_contract_1 = require("./company-brain-contract");
|
|
8
|
+
const company_brain_source_1 = require("./company-brain-source");
|
|
9
|
+
exports.CONTEXT_SCHEMA = 'ork.brain-context/v1';
|
|
10
|
+
const ID = /^(prod|proj|init)-[a-z0-9][a-z0-9-]{2,47}$/;
|
|
11
|
+
const ORDEM = { prod: 0, proj: 1, init: 2 };
|
|
12
|
+
const LIMITE = 1000;
|
|
13
|
+
/** Teto de `get` por pacote: só o caso misto (alguns retidos, alguns ausentes) precisa deles. */
|
|
14
|
+
const LIMITE_DE_GET = 100;
|
|
15
|
+
const kindDe = (id) => id.slice(0, 4);
|
|
16
|
+
/** Por code point: o digest é identidade citável e não pode depender do locale do processo. */
|
|
17
|
+
const comparar = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
18
|
+
// Além do que o D7 lista, `conflict` também encerra: resposta de conflito não é conteúdo citável.
|
|
19
|
+
const falha = (state) => ['forbidden', 'unavailable', 'conflict'].includes(state);
|
|
20
|
+
/** A citação só vale inteira: sem um dos campos, o item sai do conteúdo e vira lacuna. */
|
|
21
|
+
function citacaoDe(source) {
|
|
22
|
+
if (!source || typeof source !== 'object')
|
|
23
|
+
return null;
|
|
24
|
+
const { instance, source_ref, source_hash, source_version, location } = source;
|
|
25
|
+
if ([instance, source_ref, location].some(v => typeof v !== 'string' || !v) || typeof source_hash !== 'string' ||
|
|
26
|
+
!/^[a-f0-9]{64}$/.test(source_hash) || !Number.isInteger(source_version))
|
|
27
|
+
return null;
|
|
28
|
+
return { instance, source_ref, source_hash, source_version, location };
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Monta o pacote dos ids pedidos mais a cadeia de pais. A fonte canônica é o portfólio local;
|
|
32
|
+
* o Brain é projeção, e a diferença entre os dois sai como frescor e lacuna, nunca é corrigida aqui.
|
|
33
|
+
*/
|
|
34
|
+
function buildContext(c, ids, transport, thread) {
|
|
35
|
+
const tenant = c.manifesto.memory.tenant;
|
|
36
|
+
const pedido = [...new Set(ids)].sort(comparar);
|
|
37
|
+
if (!pedido.length)
|
|
38
|
+
throw Error('brain.context.ids-required');
|
|
39
|
+
if (pedido.some(id => !ID.test(id)))
|
|
40
|
+
throw Error('brain.context.id-invalid');
|
|
41
|
+
if (pedido.length > LIMITE)
|
|
42
|
+
throw Error('brain.context.too-many');
|
|
43
|
+
const fonte = new Map((0, company_brain_source_1.portfolioEntities)((0, portfolio_1.readPortfolio)(c.raiz), { tenant, instance: c.manifesto.project.name, thread, aclRef: 'ork-factory' }).map(e => [e.id, e]));
|
|
44
|
+
const base = { schema: exports.CONTEXT_SCHEMA, tenant, contrato: company_brain_contract_1.CONTRACT_HASH, thread, pedido };
|
|
45
|
+
const encerrar = (r) => ({ ...base, state: r.state, ...(r.error ? { error: r.error } : {}), itens: [], lacunas: [], digest: null, consultadoEm: new Date().toISOString() });
|
|
46
|
+
const escopo = new Set(pedido), brain = new Map(), retidos = new Set();
|
|
47
|
+
// O pai só entra por um filho visível e tem kind acima dele: init, proj e prod fecham em três voltas.
|
|
48
|
+
const pai = (id, parent) => typeof parent === 'string' && ID.test(parent) && ORDEM[kindDe(parent)] < ORDEM[kindDe(id)] ? parent : null;
|
|
49
|
+
let gets = 0;
|
|
50
|
+
for (let pendentes = pedido; pendentes.length;) {
|
|
51
|
+
for (let inicio = 0; inicio < pendentes.length; inicio += LIMITE) {
|
|
52
|
+
const lote = pendentes.slice(inicio, inicio + LIMITE), doLote = new Set(lote);
|
|
53
|
+
const r = transport({ schema: company_brain_client_1.BRAIN_API, operation: 'query', payload: (0, company_brain_contract_1.validateContract)({ schema: 'orkmind.company-brain-selection/v1',
|
|
54
|
+
tenant_id: tenant, facets: { ids: lote, kinds: [], workspace_ids: [], source_instances: [] }, mode: 'selection',
|
|
55
|
+
limit: lote.length, offset: 0 }) });
|
|
56
|
+
if (falha(r.state))
|
|
57
|
+
return encerrar(r);
|
|
58
|
+
const itens = Array.isArray(r.items) ? r.items : [];
|
|
59
|
+
for (const item of itens)
|
|
60
|
+
if (item?.state === 'ok' && doLote.has(item.entity?.id))
|
|
61
|
+
brain.set(item.entity.id, item.entity);
|
|
62
|
+
// Retido chega sem id na seleção. Sem retido no lote, o que faltou é desconhecido; com todos os
|
|
63
|
+
// faltantes retidos, não há o que atribuir. Só no caso misto o `get` diz qual id é qual.
|
|
64
|
+
const faltantes = lote.filter(id => !brain.has(id)), retidosNoLote = itens.filter((i) => i?.state === 'withheld').length;
|
|
65
|
+
if (retidosNoLote && retidosNoLote === faltantes.length)
|
|
66
|
+
faltantes.forEach(id => retidos.add(id));
|
|
67
|
+
else if (retidosNoLote)
|
|
68
|
+
for (const id of faltantes) {
|
|
69
|
+
if (++gets > LIMITE_DE_GET)
|
|
70
|
+
return encerrar({ state: 'unavailable', error: 'brain.context.get-limit' });
|
|
71
|
+
const g = transport({ schema: company_brain_client_1.BRAIN_API, operation: 'get', payload: { tenant_id: tenant, id } });
|
|
72
|
+
if (falha(g.state))
|
|
73
|
+
return encerrar(g);
|
|
74
|
+
if (g.state === 'withheld')
|
|
75
|
+
retidos.add(id);
|
|
76
|
+
else if (g.state === 'ok' && g.entity?.id === id)
|
|
77
|
+
brain.set(id, g.entity);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const novos = [];
|
|
81
|
+
for (const id of pendentes)
|
|
82
|
+
if (!retidos.has(id))
|
|
83
|
+
for (const parent of [pai(id, fonte.get(id)?.parent_id), pai(id, brain.get(id)?.parent_id)])
|
|
84
|
+
if (parent && !escopo.has(parent)) {
|
|
85
|
+
escopo.add(parent);
|
|
86
|
+
novos.push(parent);
|
|
87
|
+
}
|
|
88
|
+
pendentes = novos.sort(comparar);
|
|
89
|
+
}
|
|
90
|
+
const itens = [], lacunas = [];
|
|
91
|
+
const lacuna = (id, codigo) => lacunas.push({ id, codigo });
|
|
92
|
+
for (const id of escopo) {
|
|
93
|
+
if (retidos.has(id)) {
|
|
94
|
+
itens.push({ id, estado: 'retido', frescor: 'retido', origem: 'brain' });
|
|
95
|
+
lacuna(id, 'brain.retido');
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const b = brain.get(id), f = fonte.get(id);
|
|
99
|
+
if (!b && !f) {
|
|
100
|
+
lacuna(id, 'entidade.desconhecida');
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const entidade = b ?? f, citacao = citacaoDe(entidade.source);
|
|
104
|
+
if (!citacao) {
|
|
105
|
+
lacuna(id, 'citacao.incompleta');
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const frescor = !b ? 'ausente-no-brain' : !f ? 'ausente-na-fonte'
|
|
109
|
+
: b.source?.source_hash === f.source.source_hash && b.version === f.version ? 'confere' : 'divergente';
|
|
110
|
+
itens.push({ id, kind: entidade.kind, estado: 'ok', frescor, origem: b ? 'brain' : 'fonte', titulo: entidade.title ?? '',
|
|
111
|
+
status: entidade.status ?? '', parent_id: entidade.parent_id ?? null, depends_on: entidade.depends_on ?? [],
|
|
112
|
+
owner: { raw: entidade.owner?.raw ?? null, state: entidade.owner?.state ?? 'unknown' }, versao: entidade.version, citacao,
|
|
113
|
+
fonte: f ? { source_hash: f.source.source_hash, version: f.version } : null });
|
|
114
|
+
if (frescor === 'ausente-no-brain')
|
|
115
|
+
lacuna(id, 'brain.ausente');
|
|
116
|
+
if (frescor === 'ausente-na-fonte')
|
|
117
|
+
lacuna(id, 'fonte.ausente');
|
|
118
|
+
if (frescor === 'divergente')
|
|
119
|
+
lacuna(id, 'fonte.divergente');
|
|
120
|
+
if ((entidade.owner?.principal ?? null) === null)
|
|
121
|
+
lacuna(id, 'dono.sem-principal');
|
|
122
|
+
if ((entidade.observed_at ?? null) === null)
|
|
123
|
+
lacuna(id, 'observado.desconhecido');
|
|
124
|
+
if ((entidade.recorded_at ?? null) === null)
|
|
125
|
+
lacuna(id, 'registrado.desconhecido');
|
|
126
|
+
}
|
|
127
|
+
itens.sort((a, b) => ORDEM[kindDe(a.id)] - ORDEM[kindDe(b.id)] || comparar(a.id, b.id));
|
|
128
|
+
lacunas.sort((a, b) => comparar(a.id, b.id) || comparar(a.codigo, b.codigo));
|
|
129
|
+
// O digest cobre o que foi dito e de onde veio; o horário da consulta fica fora para o pacote ser reproduzível.
|
|
130
|
+
const corpo = { ...base, state: (itens.length ? 'ok' : 'empty'), itens, lacunas };
|
|
131
|
+
return { ...corpo, digest: (0, company_brain_contract_1.digest)(corpo), consultadoEm: new Date().toISOString() };
|
|
132
|
+
}
|