@orkastery/cli 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/adapters/claude-code/README.md +11 -2
- package/adapters/claude-code/commands/onboarding.md +3 -2
- package/adapters/openclaw/README.md +11 -0
- package/adapters/openclaw/dist/index.js +1 -1
- package/adapters/openclaw/openclaw.plugin.json +1 -1
- package/adapters/openclaw/package.json +1 -1
- package/adapters/openclaw/src/index.ts +1 -1
- package/dist/adapters/claude-bg.js +20 -6
- package/dist/auditrun.js +1 -1
- package/dist/claim-lint.js +3 -1
- package/dist/docs.js +71 -3
- package/dist/doctor.js +209 -32
- package/dist/entrega-pr.js +7 -0
- package/dist/fabrica-estado.js +21 -3
- package/dist/fix.js +3 -3
- package/dist/hitl-resumo.js +24 -1
- package/dist/hosts.js +7 -3
- package/dist/index.js +42 -14
- package/dist/init.js +11 -5
- package/dist/integracoes-locais.js +2 -0
- package/dist/intelligence-graph-cli.js +181 -27
- package/dist/intelligence-graph-contract.js +71 -12
- package/dist/intelligence-graph-extract-md.js +41 -23
- package/dist/intelligence-graph-extract-ts.js +181 -11
- package/dist/intelligence-graph-extract.js +186 -9
- package/dist/intelligence-graph-index.js +205 -24
- package/dist/intelligence-graph-repo.js +10 -0
- package/dist/leases.js +16 -0
- package/dist/ledger.js +9 -0
- package/dist/licoes.js +5 -4
- package/dist/liveness.js +2 -1
- package/dist/maestro-runtime.js +3 -0
- package/dist/manifest.js +24 -0
- package/dist/mcp-artifacts.js +2 -0
- package/dist/mcp-git.js +60 -12
- package/dist/mcp-grafo-worker.js +91 -0
- package/dist/mcp-grafo.js +233 -0
- package/dist/mcp-install.js +2 -1
- package/dist/mcp-server.js +9 -0
- package/dist/mcp-ship.js +2 -0
- package/dist/mcp-verify.js +3 -1
- package/dist/network-roadmap.js +5 -5
- package/dist/ocupacao.js +6 -2
- package/dist/onboarding.js +9 -4
- package/dist/orquestracao.js +2 -1
- package/dist/parado-no-condutor.js +933 -0
- package/dist/policies.js +27 -2
- package/dist/prova-ativacao.js +345 -0
- package/dist/pulse-delivery.js +27 -6
- package/dist/pulse.js +53 -3
- package/dist/roadmap-status.js +142 -7
- package/dist/runtime-context.js +5 -1
- package/dist/sessoes-inventario.js +7 -1
- package/dist/sessoes.js +4 -1
- package/dist/ship.js +25 -1
- package/dist/thread.js +21 -2
- package/dist/util.js +71 -4
- package/dist/verify.js +2 -2
- package/dist/worktree.js +29 -6
- package/eval/casos/onboarding.json +1 -1
- package/package.json +1 -1
- package/skills/core/onboarding/SKILL.md +3 -2
- package/skills/phases/ship-release/SKILL.md +6 -3
|
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.USO_DO_GRAFO = exports.AMOSTRA_SCHEMA = exports.STATUS_SCHEMA = void 0;
|
|
36
|
+
exports.CORRECAO_DO_INDICE = exports.USO_DO_GRAFO = exports.AMOSTRA_SCHEMA = exports.STATUS_SCHEMA = void 0;
|
|
37
37
|
exports.lerPedido = lerPedido;
|
|
38
38
|
exports.executarGrafo = executarGrafo;
|
|
39
39
|
/**
|
|
@@ -46,6 +46,10 @@ exports.executarGrafo = executarGrafo;
|
|
|
46
46
|
* O argv chega cru e o parser e estrito: subcomando conhecido, opcao conhecida uma vez so, valor
|
|
47
47
|
* onde a opcao pede valor e bandeira sem valor, numero exato de posicionais. Resposta sai 0, mesmo
|
|
48
48
|
* vazia; erro tipado sai 1 e, com `--json`, vem como objeto.
|
|
49
|
+
*
|
|
50
|
+
* RM-031 KG5 (D4, D5): `--teto-bytes N` limita a resposta em bytes (JSON compacto), para quem a leva
|
|
51
|
+
* ao contexto de um agente, como as tools `ork_grafo_*` do MCP; sem a opcao, a saida e a de antes. Sem
|
|
52
|
+
* o indice do HEAD, a recusa diz se ha indice de outra revisao ou de outro extrator, e a correcao.
|
|
49
53
|
*/
|
|
50
54
|
const node_crypto_1 = require("node:crypto");
|
|
51
55
|
const fs = __importStar(require("node:fs"));
|
|
@@ -60,20 +64,29 @@ exports.AMOSTRA_SCHEMA = 'ork.graph-edge-audit-sample/v0';
|
|
|
60
64
|
exports.USO_DO_GRAFO = [
|
|
61
65
|
'uso: ork grafo indexar [--verificar] [--forcar] [--json]',
|
|
62
66
|
' ork grafo status [--json]',
|
|
63
|
-
' ork grafo vizinhos <no> [--profundidade N] [--sentido entrada|saida|ambos] [--tipo T,...] [--limite N] [--json]',
|
|
64
|
-
' ork grafo chamadores <simbolo> [--profundidade N] [--limite N] [--json]',
|
|
65
|
-
' ork grafo importadores <arquivo|simbolo> [--profundidade N] [--limite N] [--json]',
|
|
66
|
-
' ork grafo caminho <de> <para> [--sentido saida|entrada|ambos] [--tipo T,...] [--json]',
|
|
67
|
+
' ork grafo vizinhos <no> [--profundidade N] [--sentido entrada|saida|ambos] [--tipo T,...] [--limite N] [--json [--teto-bytes N]]',
|
|
68
|
+
' ork grafo chamadores <simbolo> [--profundidade N] [--limite N] [--json [--teto-bytes N]]',
|
|
69
|
+
' ork grafo importadores <arquivo|simbolo> [--profundidade N] [--limite N] [--json [--teto-bytes N]]',
|
|
70
|
+
' ork grafo caminho <de> <para> [--sentido saida|entrada|ambos] [--tipo T,...] [--json [--teto-bytes N]]',
|
|
67
71
|
' ork grafo amostra [--por-estrato N] | ork grafo amostra --conferir ARQ [--json]',
|
|
68
72
|
' ork grafo limpar [--tudo] [--json]',
|
|
69
73
|
].join('\n');
|
|
74
|
+
/** KG5 (D5): a correcao das recusas de indice que a proxima indexacao resolve. */
|
|
75
|
+
exports.CORRECAO_DO_INDICE = 'ork grafo indexar';
|
|
76
|
+
/** KG5 (D5): o estado do indice do HEAD que cada recusa declara no JSON, ao lado da correcao. */
|
|
77
|
+
const ESTADO_DO_INDICE = Object.freeze({
|
|
78
|
+
'grafo.indice.ausente': 'nao-indexado',
|
|
79
|
+
'grafo.indice.outra-revisao': 'outra-revisao',
|
|
80
|
+
'grafo.indice.outro-extrator': 'outro-extrator',
|
|
81
|
+
'grafo.indice.corrompido': 'corrompido',
|
|
82
|
+
});
|
|
70
83
|
const SUBCOMANDOS = Object.freeze({
|
|
71
84
|
indexar: { posicionais: 0, bandeiras: ['verificar', 'forcar', 'json'], valores: [] },
|
|
72
85
|
status: { posicionais: 0, bandeiras: ['json'], valores: [] },
|
|
73
|
-
vizinhos: { posicionais: 1, bandeiras: ['json'], valores: ['profundidade', 'sentido', 'tipo', 'limite'] },
|
|
74
|
-
chamadores: { posicionais: 1, bandeiras: ['json'], valores: ['profundidade', 'limite'] },
|
|
75
|
-
importadores: { posicionais: 1, bandeiras: ['json'], valores: ['profundidade', 'limite'] },
|
|
76
|
-
caminho: { posicionais: 2, bandeiras: ['json'], valores: ['sentido', 'tipo'] },
|
|
86
|
+
vizinhos: { posicionais: 1, bandeiras: ['json'], valores: ['profundidade', 'sentido', 'tipo', 'limite', 'teto-bytes'] },
|
|
87
|
+
chamadores: { posicionais: 1, bandeiras: ['json'], valores: ['profundidade', 'limite', 'teto-bytes'] },
|
|
88
|
+
importadores: { posicionais: 1, bandeiras: ['json'], valores: ['profundidade', 'limite', 'teto-bytes'] },
|
|
89
|
+
caminho: { posicionais: 2, bandeiras: ['json'], valores: ['sentido', 'tipo', 'teto-bytes'] },
|
|
77
90
|
amostra: { posicionais: 0, bandeiras: ['json'], valores: ['por-estrato', 'conferir'] },
|
|
78
91
|
limpar: { posicionais: 0, bandeiras: ['tudo', 'json'], valores: [] },
|
|
79
92
|
});
|
|
@@ -141,8 +154,40 @@ function sentido(p) {
|
|
|
141
154
|
uso(`--sentido desconhecido: ${v}`);
|
|
142
155
|
return v;
|
|
143
156
|
}
|
|
157
|
+
/** KG5 (D4): o teto vale para o JSON, que e o que vai ao contexto de quem consulta. */
|
|
158
|
+
function tetoDeBytes(p) {
|
|
159
|
+
const teto = inteiro(p, 'teto-bytes');
|
|
160
|
+
if (teto === undefined)
|
|
161
|
+
return undefined;
|
|
162
|
+
if (!p.bandeiras.has('json'))
|
|
163
|
+
uso('--teto-bytes exige --json');
|
|
164
|
+
if (teto < 1)
|
|
165
|
+
uso('--teto-bytes precisa ser ao menos 1');
|
|
166
|
+
return teto;
|
|
167
|
+
}
|
|
144
168
|
const mb = (bytes) => `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
145
169
|
const contagem = (o) => Object.entries(o).map(([k, v]) => `${k}=${v}`).join(' ');
|
|
170
|
+
/** Ate cinco caminhos e quantos ficaram de fora. */
|
|
171
|
+
const alguns = (lista) => (lista.length ? ` (${lista.slice(0, 5).join(', ')}${lista.length > 5 ? ` e mais ${lista.length - 5}` : ''})` : '');
|
|
172
|
+
/** KG4: como a extracao rodou, a base e o que veio dela, ou por que foi completa. */
|
|
173
|
+
function linhasDaExtracao(r) {
|
|
174
|
+
if (r.modo === null)
|
|
175
|
+
return [];
|
|
176
|
+
const a = r.reaproveitamento;
|
|
177
|
+
if (r.modo === 'incremental' && r.base && a) {
|
|
178
|
+
// Desestruturado: o lint de horario le o campo de nome curto do TypeScript como instante.
|
|
179
|
+
const { ts: doTs, md: doMd, mudanca } = a;
|
|
180
|
+
const ts = doTs.modo === 'inteiro' ? `TypeScript inteiro (${doTs.motivo})`
|
|
181
|
+
: doTs.modo === 'parcial' ? `TypeScript parcial, ${doTs.reextraidos.length} reextraido(s)${alguns(doTs.reextraidos)} num programa de ${doTs.programa}, ${doTs.reaproveitados} da base`
|
|
182
|
+
: `TypeScript todo da base (${doTs.reaproveitados})`;
|
|
183
|
+
return [
|
|
184
|
+
` extracao incremental a partir do indice de ${r.base.revision.slice(0, 12)}: ${mudanca.alterados} alterado(s), ${mudanca.novos} novo(s), ${mudanca.removidos} removido(s)`,
|
|
185
|
+
` ${ts}`,
|
|
186
|
+
` Markdown: ${doMd.reextraidos.length} reextraido(s)${alguns(doMd.reextraidos)}, ${doMd.reaproveitados} da base`,
|
|
187
|
+
];
|
|
188
|
+
}
|
|
189
|
+
return [` extracao completa${r.motivo_completo ? `: ${r.motivo_completo}` : ''}`];
|
|
190
|
+
}
|
|
146
191
|
function indexar(ctx, p) {
|
|
147
192
|
const r = (0, intelligence_graph_index_1.construirIndice)(ctx, { verificar: p.bandeiras.has('verificar'), forcar: p.bandeiras.has('forcar') });
|
|
148
193
|
if (p.bandeiras.has('json')) {
|
|
@@ -159,12 +204,18 @@ function indexar(ctx, p) {
|
|
|
159
204
|
` conferencia conferirFontes verificada: ${m.conferencia.fontes} fontes e ${m.conferencia.evidencias} evidencias contra os bytes`,
|
|
160
205
|
` nos ${contagem(m.contagens.nos)}`,
|
|
161
206
|
` arestas ${contagem(m.contagens.arestas)}`,
|
|
162
|
-
` lugar ${r.dir} (${mb(m.graph_bytes + m.report_bytes)})`,
|
|
207
|
+
` lugar ${r.dir} (${mb(m.graph_bytes + m.report_bytes + m.units_bytes)})`,
|
|
208
|
+
...linhasDaExtracao(r),
|
|
163
209
|
];
|
|
164
210
|
if (r.motivo)
|
|
165
211
|
linhas.push(` motivo ${r.motivo}`);
|
|
166
|
-
for (const d of r.determinismo ?? [])
|
|
167
|
-
linhas.push(` determinismo ${d.ordem}: digest ${d.digest.slice(0, 16)}, relatorio ${d.relatorio.slice(0, 16)}, ${d.igual ? 'igual' : 'DIFERENTE'}`);
|
|
212
|
+
for (const d of r.determinismo ?? []) {
|
|
213
|
+
linhas.push(` determinismo ${d.ordem}: digest ${d.digest.slice(0, 16)}, relatorio ${d.relatorio.slice(0, 16)}, unidades ${d.unidades.slice(0, 16)}, ${d.igual ? 'igual' : 'DIFERENTE'}`);
|
|
214
|
+
}
|
|
215
|
+
if (r.incremental)
|
|
216
|
+
linhas.push(` incremental a partir do indice de ${r.incremental.base.slice(0, 12)}: ${r.incremental.igual ? 'igual a completa nos quatro arquivos' : 'DIFERENTE'}`);
|
|
217
|
+
else if (r.determinismo)
|
|
218
|
+
linhas.push(` incremental nao conferido: ${r.motivo_completo ?? 'sem base'}`);
|
|
168
219
|
linhas.push(` tempo ${r.ms} ms`);
|
|
169
220
|
if (r.determinismo)
|
|
170
221
|
linhas.push('aprovado');
|
|
@@ -211,9 +262,65 @@ function status(ctx, p) {
|
|
|
211
262
|
ctx.escrever(linhas.join('\n'));
|
|
212
263
|
return 0;
|
|
213
264
|
}
|
|
265
|
+
/**
|
|
266
|
+
* KG5 (CHECK rodada 1, A1): o que difere entre o extrator do indice guardado e o de quem consulta. A
|
|
267
|
+
* versao do Node entra no rotulo do analisador de JavaScript, entao outra instalacao ou outro Node dao
|
|
268
|
+
* outra chave com o mesmo conteudo.
|
|
269
|
+
*/
|
|
270
|
+
function diferencasDoExtrator(guardado, atual) {
|
|
271
|
+
const r = [];
|
|
272
|
+
for (const k of Object.keys(atual.analisadores).sort(intelligence_graph_contract_1.compararUtf8)) {
|
|
273
|
+
const antes = guardado.analisadores?.[k];
|
|
274
|
+
if (antes !== atual.analisadores[k])
|
|
275
|
+
r.push(`${k} ${antes ?? '?'} no indice, ${atual.analisadores[k]} aqui`);
|
|
276
|
+
}
|
|
277
|
+
if ((0, intelligence_graph_contract_1.canonico)(guardado.pacotes) !== (0, intelligence_graph_contract_1.canonico)(atual.pacotes))
|
|
278
|
+
r.push('pacotes dos analisadores');
|
|
279
|
+
if (guardado.codigo !== atual.codigo)
|
|
280
|
+
r.push('codigo do extrator');
|
|
281
|
+
return r;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* KG5 (D5): sem o indice do HEAD, diz o que ha no lugar. Indice guardado do mesmo repositorio e da
|
|
285
|
+
* revisao do HEAD com outra chave e de outro extrator (instalacao, Node, analisadores ou codigo do
|
|
286
|
+
* extrator mudaram), e a recusa diz o que mudou; de outra revisao, e indice velho ou de outra arvore.
|
|
287
|
+
* Nos dois casos a correcao e indexar de novo, com a instalacao de quem consulta; sem nenhum, fica a
|
|
288
|
+
* recusa original (nao indexado). Nunca responde por um indice que nao e o do HEAD.
|
|
289
|
+
*/
|
|
290
|
+
function semIndiceDoHead(ctx, original) {
|
|
291
|
+
const arvore = (0, intelligence_graph_repo_1.revisaoDaArvore)(ctx.raiz);
|
|
292
|
+
if (!arvore.head)
|
|
293
|
+
throw original;
|
|
294
|
+
const perfil = (0, intelligence_graph_index_1.perfilDoIndice)(arvore.raiz, undefined, ctx.repositorio ? { repository_id: ctx.repositorio } : {});
|
|
295
|
+
const chave = (0, intelligence_graph_index_1.chaveDoIndice)(arvore.head, perfil), head = arvore.head.slice(0, 12);
|
|
296
|
+
const guardados = (0, intelligence_graph_index_1.estadoDosIndices)(ctx).indices
|
|
297
|
+
.filter((i) => i.problema === null && i.chave !== chave && i.repository_id === perfil.repository_id && i.revision !== null);
|
|
298
|
+
const doHead = guardados.find((i) => i.revision === arvore.head);
|
|
299
|
+
if (doHead) {
|
|
300
|
+
const diferencas = diferencasDoExtrator((0, intelligence_graph_index_1.lerIndice)(ctx, doHead.chave, { grafo: false }).manifesto, perfil);
|
|
301
|
+
throw new Error(`grafo.indice.outro-extrator: ha indice do HEAD ${head} de outro extrator (${diferencas.join('; ') || 'perfil diferente'}); `
|
|
302
|
+
+ `rode ${exports.CORRECAO_DO_INDICE} com a mesma instalacao do ork e o mesmo Node de quem consulta (o servidor MCP consulta com a dele)`);
|
|
303
|
+
}
|
|
304
|
+
if (guardados.length) {
|
|
305
|
+
const revisoes = [...new Set(guardados.map((i) => i.revision.slice(0, 12)))].sort(intelligence_graph_contract_1.compararUtf8);
|
|
306
|
+
// CHECK rodada 2 (N7): a ressalva da instalacao ja na primeira recusa, a de cada commit do GO.
|
|
307
|
+
throw new Error(`grafo.indice.outra-revisao: o HEAD ${head} nao tem indice; o guardado e de outra revisao${alguns(revisoes)}; `
|
|
308
|
+
+ `rode ${exports.CORRECAO_DO_INDICE} com a mesma instalacao do ork e o mesmo Node de quem consulta`);
|
|
309
|
+
}
|
|
310
|
+
throw original;
|
|
311
|
+
}
|
|
214
312
|
/** O indice do HEAD pronto para consulta, com a concessao local e o cabecalho; `grafo` ja vem filtrado por ela. */
|
|
215
313
|
function consultavel(ctx) {
|
|
216
|
-
|
|
314
|
+
let doHead;
|
|
315
|
+
try {
|
|
316
|
+
doHead = (0, intelligence_graph_index_1.indiceDoHead)(ctx);
|
|
317
|
+
}
|
|
318
|
+
catch (e) {
|
|
319
|
+
if (String(e.message).startsWith('grafo.indice.ausente'))
|
|
320
|
+
semIndiceDoHead(ctx, e);
|
|
321
|
+
throw e;
|
|
322
|
+
}
|
|
323
|
+
const { arvore, perfil, chave, indice } = doHead;
|
|
217
324
|
const concessao = (0, intelligence_graph_index_1.concessaoLocal)(arvore.raiz, perfil);
|
|
218
325
|
const grafo = (0, intelligence_graph_query_1.filtrarGrafo)(indice.grafo, concessao), m = indice.manifesto;
|
|
219
326
|
const cabecalho = {
|
|
@@ -227,18 +334,59 @@ function responder(ctx, p, r) {
|
|
|
227
334
|
ctx.escrever(p.bandeiras.has('json') ? (0, intelligence_graph_query_1.jsonDaResposta)(r) : (0, intelligence_graph_query_1.textoDaResposta)(r));
|
|
228
335
|
return 0;
|
|
229
336
|
}
|
|
337
|
+
/**
|
|
338
|
+
* KG5 (D4): a resposta em JSON compacto que cabe em `teto` bytes. Se a pedida nao cabe, vale o maior
|
|
339
|
+
* limite que cabe: as arestas mais longe do alvo saem primeiro, pela mesma ordem do `--limite`, e
|
|
340
|
+
* `consulta.limite` passa a ser o efetivo. O tamanho cresce com o limite (cada aresta a mais so soma
|
|
341
|
+
* bytes), entao a busca binaria acha o maior que cabe, sempre o mesmo. Aresta nunca sai sem toda a
|
|
342
|
+
* evidencia; o caminho nao se corta.
|
|
343
|
+
*/
|
|
344
|
+
function respostaComTeto(perguntar, limite, teto) {
|
|
345
|
+
const pedida = perguntar(limite);
|
|
346
|
+
const limitePedido = pedida.consulta.limite;
|
|
347
|
+
const comTeto = (r, cortado) => JSON.stringify({ ...r, teto: { bytes: teto, limite_pedido: limitePedido, cortado } });
|
|
348
|
+
const inteira = comTeto(pedida, false);
|
|
349
|
+
if (Buffer.byteLength(inteira) <= teto)
|
|
350
|
+
return inteira;
|
|
351
|
+
const ehCaminho = pedida.consulta.tipo === 'caminho';
|
|
352
|
+
// So chega aqui o que nao cabe nem com a aresta mais perto do alvo: estreitar a consulta nao ajuda.
|
|
353
|
+
const excedido = (resposta, bytes) => {
|
|
354
|
+
throw new intelligence_graph_query_1.ErroDeConsulta('grafo.consulta.teto-excedido', `${resposta} tem ${bytes} bytes, acima do teto de ${teto}; ${ehCaminho ? 'o caminho nao se corta: ' : ''}use --teto-bytes maior`);
|
|
355
|
+
};
|
|
356
|
+
if (ehCaminho)
|
|
357
|
+
excedido(`o caminho de ${pedida.arestas.length} passo(s)`, Buffer.byteLength(inteira));
|
|
358
|
+
if (pedida.arestas.length <= 1)
|
|
359
|
+
excedido(`a resposta com ${pedida.arestas.length} aresta(s)`, Buffer.byteLength(inteira));
|
|
360
|
+
let menor = 1, maior = pedida.arestas.length - 1, melhor = null;
|
|
361
|
+
while (menor <= maior) {
|
|
362
|
+
const k = Math.floor((menor + maior) / 2), texto = comTeto(perguntar(k), true);
|
|
363
|
+
if (Buffer.byteLength(texto) <= teto) {
|
|
364
|
+
melhor = texto;
|
|
365
|
+
menor = k + 1;
|
|
366
|
+
}
|
|
367
|
+
else
|
|
368
|
+
maior = k - 1;
|
|
369
|
+
}
|
|
370
|
+
return melhor ?? excedido('a resposta com 1 aresta', Buffer.byteLength(comTeto(perguntar(1), true)));
|
|
371
|
+
}
|
|
230
372
|
function consultar(ctx, p) {
|
|
231
373
|
// As opcoes sao conferidas antes de carregar o indice: erro de uso nao custa a leitura do grafo.
|
|
232
374
|
const [a, b] = p.posicionais;
|
|
233
|
-
const profundidade = inteiro(p, 'profundidade'), limite = inteiro(p, 'limite'), s = sentido(p), t = tipos(p);
|
|
375
|
+
const profundidade = inteiro(p, 'profundidade'), limite = inteiro(p, 'limite'), s = sentido(p), t = tipos(p), teto = tetoDeBytes(p);
|
|
234
376
|
const { g, cabecalho } = consultavel(ctx);
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
377
|
+
const perguntar = (lim) => {
|
|
378
|
+
if (p.sub === 'vizinhos')
|
|
379
|
+
return (0, intelligence_graph_query_1.vizinhos)(g, cabecalho, a, { profundidade, limite: lim, sentido: s, tipos: t });
|
|
380
|
+
if (p.sub === 'chamadores')
|
|
381
|
+
return (0, intelligence_graph_query_1.chamadores)(g, cabecalho, a, { profundidade, limite: lim });
|
|
382
|
+
if (p.sub === 'importadores')
|
|
383
|
+
return (0, intelligence_graph_query_1.importadores)(g, cabecalho, a, { profundidade, limite: lim });
|
|
384
|
+
return (0, intelligence_graph_query_1.caminho)(g, cabecalho, a, b, { sentido: s, tipos: t });
|
|
385
|
+
};
|
|
386
|
+
if (teto === undefined)
|
|
387
|
+
return responder(ctx, p, perguntar(limite));
|
|
388
|
+
ctx.escrever(respostaComTeto(perguntar, limite, teto));
|
|
389
|
+
return 0;
|
|
242
390
|
}
|
|
243
391
|
const sha256 = (b) => (0, node_crypto_1.createHash)('sha256').update(b).digest('hex');
|
|
244
392
|
/**
|
|
@@ -392,15 +540,21 @@ function limpar(ctx, p) {
|
|
|
392
540
|
ctx.escrever([`grafo limpar: ${r.removidos.length} removido(s), ${mb(r.bytes)}`, ...r.removidos.map((x) => ` ${x.nome} ${mb(x.bytes)}`)].join('\n'));
|
|
393
541
|
return 0;
|
|
394
542
|
}
|
|
395
|
-
|
|
543
|
+
/** KG5 (D4, D5): com `--teto-bytes` o erro tambem sai compacto; recusa de indice traz o estado e a correcao. */
|
|
544
|
+
const erroEmJson = (ctx, e, compacto) => {
|
|
396
545
|
const [codigo, ...detalhe] = String(e.message).split('\n')[0].split(': ');
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
546
|
+
const doIndice = Object.prototype.hasOwnProperty.call(ESTADO_DO_INDICE, codigo)
|
|
547
|
+
? { estado_do_indice: ESTADO_DO_INDICE[codigo], correcao: exports.CORRECAO_DO_INDICE } : {};
|
|
548
|
+
const r = {
|
|
549
|
+
schema: intelligence_graph_query_1.CONSULTA_SCHEMA,
|
|
550
|
+
erro: { codigo, detalhe: detalhe.join(': ') || null, candidatos: e instanceof intelligence_graph_query_1.ErroDeConsulta ? e.candidatos : [], ...doIndice },
|
|
551
|
+
};
|
|
552
|
+
ctx.escrever(compacto ? JSON.stringify(r) : JSON.stringify(r, null, 2));
|
|
400
553
|
return 1;
|
|
401
554
|
};
|
|
402
555
|
/** `ork grafo <subcomando> ...`: devolve o codigo de saida; sem `--json`, o erro de uso ou tipado e lancado. */
|
|
403
556
|
function executarGrafo(argv, ctx) {
|
|
557
|
+
const compacto = argv.some((a) => a === '--teto-bytes' || a.startsWith('--teto-bytes='));
|
|
404
558
|
let p;
|
|
405
559
|
try {
|
|
406
560
|
p = lerPedido(argv);
|
|
@@ -408,7 +562,7 @@ function executarGrafo(argv, ctx) {
|
|
|
408
562
|
catch (e) {
|
|
409
563
|
// Com `--json` no argv, tambem o erro de uso sai como objeto, como os demais.
|
|
410
564
|
if (argv.includes('--json'))
|
|
411
|
-
return erroEmJson(ctx, e);
|
|
565
|
+
return erroEmJson(ctx, e, compacto);
|
|
412
566
|
throw e;
|
|
413
567
|
}
|
|
414
568
|
try {
|
|
@@ -425,6 +579,6 @@ function executarGrafo(argv, ctx) {
|
|
|
425
579
|
catch (e) {
|
|
426
580
|
if (!p.bandeiras.has('json'))
|
|
427
581
|
throw e;
|
|
428
|
-
return erroEmJson(ctx, e);
|
|
582
|
+
return erroEmJson(ctx, e, compacto);
|
|
429
583
|
}
|
|
430
584
|
}
|
|
@@ -10,6 +10,7 @@ exports.derivarIds = derivarIds;
|
|
|
10
10
|
exports.caminhoValido = caminhoValido;
|
|
11
11
|
exports.validarGrafo = validarGrafo;
|
|
12
12
|
exports.conferirFontes = conferirFontes;
|
|
13
|
+
exports.conferirFontesDoGrafoValidado = conferirFontesDoGrafoValidado;
|
|
13
14
|
/**
|
|
14
15
|
* I-31 KG1 (D1 a D5, D9, D10): contrato `ork.code-artifact-graph/v1` do grafo deterministico
|
|
15
16
|
* de codigo e artefatos.
|
|
@@ -134,19 +135,70 @@ function compararUtf8(a, b) {
|
|
|
134
135
|
}
|
|
135
136
|
return a.length - b.length;
|
|
136
137
|
}
|
|
137
|
-
|
|
138
|
+
const FORMAS = new Map();
|
|
139
|
+
const TETO_DE_FORMAS = 4096;
|
|
140
|
+
let formasGuardadas = 0;
|
|
141
|
+
function formaDe(chaves) {
|
|
142
|
+
const primeira = chaves.length ? chaves[0] : '', candidatas = FORMAS.get(primeira);
|
|
143
|
+
if (candidatas) {
|
|
144
|
+
for (const f of candidatas) {
|
|
145
|
+
if (f.chaves.length !== chaves.length)
|
|
146
|
+
continue;
|
|
147
|
+
let igual = true;
|
|
148
|
+
for (let i = 1; i < chaves.length && igual; i++)
|
|
149
|
+
igual = f.chaves[i] === chaves[i];
|
|
150
|
+
if (igual)
|
|
151
|
+
return f.ordem;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
const ordem = [...chaves].sort(compararUtf8).map((k) => [k, `${JSON.stringify(k)}:`]);
|
|
155
|
+
if (formasGuardadas < TETO_DE_FORMAS) {
|
|
156
|
+
formasGuardadas++;
|
|
157
|
+
if (candidatas)
|
|
158
|
+
candidatas.push({ chaves: [...chaves], ordem });
|
|
159
|
+
else
|
|
160
|
+
FORMAS.set(primeira, [{ chaves: [...chaves], ordem }]);
|
|
161
|
+
}
|
|
162
|
+
return ordem;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* D2: JSON canonico. Chaves na ordem de bytes UTF-8, sem espaco, so numero finito. KG4 (D6): mesma
|
|
166
|
+
* saida da forma original (`map` e `join`, com o buraco de array esparso vazio), sem ordenar as
|
|
167
|
+
* chaves a cada objeto; a ordem de visita, e com ela o primeiro erro, e a mesma.
|
|
168
|
+
*/
|
|
138
169
|
function canonico(v) {
|
|
139
|
-
if (v === null
|
|
140
|
-
return
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
170
|
+
if (v === null)
|
|
171
|
+
return 'null';
|
|
172
|
+
switch (typeof v) {
|
|
173
|
+
case 'string':
|
|
174
|
+
return JSON.stringify(v);
|
|
175
|
+
case 'boolean':
|
|
176
|
+
return v ? 'true' : 'false';
|
|
177
|
+
case 'number':
|
|
178
|
+
return Number.isFinite(v) ? JSON.stringify(v) : falha('grafo.canonico.numero-invalido');
|
|
179
|
+
case 'object': {
|
|
180
|
+
if (Array.isArray(v)) {
|
|
181
|
+
let s = '[';
|
|
182
|
+
for (let i = 0; i < v.length; i++) {
|
|
183
|
+
if (i > 0)
|
|
184
|
+
s += ',';
|
|
185
|
+
if (i in v)
|
|
186
|
+
s += canonico(v[i]);
|
|
187
|
+
}
|
|
188
|
+
return `${s}]`;
|
|
189
|
+
}
|
|
190
|
+
const o = v, forma = formaDe(Object.keys(o));
|
|
191
|
+
let s = '{';
|
|
192
|
+
for (let i = 0; i < forma.length; i++) {
|
|
193
|
+
if (i > 0)
|
|
194
|
+
s += ',';
|
|
195
|
+
s += forma[i][1] + canonico(o[forma[i][0]]);
|
|
196
|
+
}
|
|
197
|
+
return `${s}}`;
|
|
198
|
+
}
|
|
199
|
+
default:
|
|
200
|
+
return falha('grafo.canonico.tipo-invalido');
|
|
148
201
|
}
|
|
149
|
-
return falha('grafo.canonico.tipo-invalido');
|
|
150
202
|
}
|
|
151
203
|
/** SHA-256 sobre os bytes UTF-8 do conteudo canonico. */
|
|
152
204
|
const sha256DoCanonico = (v) => (0, node_crypto_1.createHash)('sha256').update(canonico(v), 'utf8').digest('hex');
|
|
@@ -508,7 +560,14 @@ function linhaDoByte(quebras, o) {
|
|
|
508
560
|
* parser interpretou a relacao certo; essa prova vem da auditoria de arestas do benchmark.
|
|
509
561
|
*/
|
|
510
562
|
function conferirFontes(entrada, fontes) {
|
|
511
|
-
|
|
563
|
+
return conferirFontesDoGrafoValidado(validarGrafo(entrada), fontes);
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* RM-031 KG4 (D6): a conferencia de `conferirFontes` sobre o grafo que `validarGrafo` acabou de
|
|
567
|
+
* devolver, sem validar de novo. So para o produtor que tem esse retorno em maos (a construcao do
|
|
568
|
+
* indice, logo depois da extracao); qualquer outra entrada passa por `conferirFontes`.
|
|
569
|
+
*/
|
|
570
|
+
function conferirFontesDoGrafoValidado(g, fontes) {
|
|
512
571
|
const manifesto = new Map(g.snapshot.source_manifest.map((m) => [m.path, m]));
|
|
513
572
|
for (const [p, f] of fontes) {
|
|
514
573
|
if (!manifesto.has(p))
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.TETO_DE_LINHAS_DE_TABELA = exports.TOKENS_SEM_MENCAO = exports.CHAVES_DO_FRONTMATTER = exports.PADRAO_DE_ID = void 0;
|
|
4
4
|
exports.slugDeTexto = slugDeTexto;
|
|
5
|
+
exports.estruturarMarkdown = estruturarMarkdown;
|
|
5
6
|
exports.extrairMarkdown = extrairMarkdown;
|
|
7
|
+
exports.ligarMarkdown = ligarMarkdown;
|
|
6
8
|
const yaml_1 = require("./yaml");
|
|
7
9
|
/** D6: ID de artefato governado (`RM-031`, `FEAT-018`). */
|
|
8
10
|
exports.PADRAO_DE_ID = /^[A-Z][A-Z0-9]*-[0-9]+$/;
|
|
@@ -329,7 +331,25 @@ const decodificar = (t) => {
|
|
|
329
331
|
return null;
|
|
330
332
|
}
|
|
331
333
|
};
|
|
334
|
+
function estruturarMarkdown(fonte, e) {
|
|
335
|
+
const s = estruturar(fonte, e.aceitaFragmento, e.analisar, e.referencia);
|
|
336
|
+
const mencoes = [];
|
|
337
|
+
for (const l of s.textoDeMencao)
|
|
338
|
+
for (const m of l.texto.matchAll(MENCAO))
|
|
339
|
+
mencoes.push({ inicio: l.inicio + m.index, id: m[0] });
|
|
340
|
+
const d = s.frontmatter?.dados;
|
|
341
|
+
return {
|
|
342
|
+
path: fonte.path, secoes: s.secoes, links: s.links, mencoes,
|
|
343
|
+
frontmatter: s.frontmatter && d
|
|
344
|
+
? { id: typeof d.id === 'string' ? d.id : null, tipo: typeof d.tipo === 'string' && d.tipo !== '', valores: s.frontmatter.valores }
|
|
345
|
+
: null,
|
|
346
|
+
frontmatterInvalido: s.frontmatterInvalido, tabelaGrande: s.tabelaGrande,
|
|
347
|
+
};
|
|
348
|
+
}
|
|
332
349
|
function extrairMarkdown(e) {
|
|
350
|
+
return ligarMarkdown({ ...e, estruturas: e.fontes.map((f) => estruturarMarkdown(f, e)) });
|
|
351
|
+
}
|
|
352
|
+
function ligarMarkdown(e) {
|
|
333
353
|
const saida = { nos: [], arestas: [], diagnosticos: [], lacunas: [] };
|
|
334
354
|
const arquivos = new Set(e.arquivos), diretorios = new Set();
|
|
335
355
|
for (const p of e.arquivos) {
|
|
@@ -337,8 +357,8 @@ function extrairMarkdown(e) {
|
|
|
337
357
|
for (let i = 1; i < partes.length; i++)
|
|
338
358
|
diretorios.add(partes.slice(0, i).join('/'));
|
|
339
359
|
}
|
|
340
|
-
const estruturas = e.
|
|
341
|
-
const slugs = new Map(estruturas.map((s) => [s.
|
|
360
|
+
const estruturas = e.estruturas;
|
|
361
|
+
const slugs = new Map(estruturas.map((s) => [s.path, new Set(s.secoes.map((x) => x.slug).filter((x) => x !== null))]));
|
|
342
362
|
const aresta = (kind, from, to, extrator, metodo, t) => {
|
|
343
363
|
saida.arestas.push({ kind, from, to, extrator, metodo, trecho: t });
|
|
344
364
|
};
|
|
@@ -349,17 +369,17 @@ function extrairMarkdown(e) {
|
|
|
349
369
|
const donos = new Map();
|
|
350
370
|
for (const s of estruturas) {
|
|
351
371
|
if (s.frontmatterInvalido)
|
|
352
|
-
lacuna('frontmatter-invalido', s.
|
|
372
|
+
lacuna('frontmatter-invalido', s.path, 0, null);
|
|
353
373
|
if (s.tabelaGrande)
|
|
354
|
-
lacuna('markdown-tabela-grande', s.
|
|
355
|
-
const nome = s.
|
|
356
|
-
if (!d || nome.startsWith('_') ||
|
|
374
|
+
lacuna('markdown-tabela-grande', s.path, null, null);
|
|
375
|
+
const nome = s.path.slice(s.path.lastIndexOf('/') + 1), d = s.frontmatter;
|
|
376
|
+
if (!d || nome.startsWith('_') || d.id === null || !exports.PADRAO_DE_ID.test(d.id) || !d.tipo)
|
|
357
377
|
continue;
|
|
358
378
|
if (!e.aceitaFragmento(d.id)) {
|
|
359
|
-
lacuna('artefato-recusado', s.
|
|
379
|
+
lacuna('artefato-recusado', s.path, 0, null);
|
|
360
380
|
continue;
|
|
361
381
|
}
|
|
362
|
-
donos.set(d.id, [...(donos.get(d.id) ?? []), s.
|
|
382
|
+
donos.set(d.id, [...(donos.get(d.id) ?? []), s.path]);
|
|
363
383
|
}
|
|
364
384
|
const artefatos = new Map();
|
|
365
385
|
for (const [id, caminhos] of donos) {
|
|
@@ -385,20 +405,15 @@ function extrairMarkdown(e) {
|
|
|
385
405
|
const atual = baixo > 0 ? s.secoes[baixo - 1] : null;
|
|
386
406
|
return atual && atual.slug !== null ? { kind: 'section', path: arquivo.path, fragment: atual.slug } : arquivo;
|
|
387
407
|
};
|
|
388
|
-
const
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
continue;
|
|
395
|
-
}
|
|
396
|
-
aresta('references', origem(inicio), alvo, e.extratorId, 'text-location', { path: fonte.path, inicio, fim: inicio + m[0].length });
|
|
397
|
-
}
|
|
398
|
-
}
|
|
408
|
+
const mencao = (path, inicio, id, origem) => {
|
|
409
|
+
const alvo = artefatos.get(id);
|
|
410
|
+
if (!alvo)
|
|
411
|
+
lacuna('id-sem-artefato', path, inicio, id);
|
|
412
|
+
else
|
|
413
|
+
aresta('references', origem(inicio), alvo, e.extratorId, 'text-location', { path, inicio, fim: inicio + id.length });
|
|
399
414
|
};
|
|
400
415
|
for (const s of estruturas) {
|
|
401
|
-
const path = s.
|
|
416
|
+
const path = s.path, arquivo = { kind: 'file', path, fragment: null };
|
|
402
417
|
for (const x of s.secoes) {
|
|
403
418
|
if (x.slug === null) {
|
|
404
419
|
lacuna('secao-recusada', path, x.inicio, null);
|
|
@@ -444,7 +459,7 @@ function extrairMarkdown(e) {
|
|
|
444
459
|
continue;
|
|
445
460
|
aresta('references', origem, { kind: 'file', path: alvo, fragment: null }, e.extratorMd, 'explicit-link', t);
|
|
446
461
|
}
|
|
447
|
-
const artefato = s.frontmatter &&
|
|
462
|
+
const artefato = s.frontmatter && s.frontmatter.id !== null ? artefatos.get(s.frontmatter.id) : undefined;
|
|
448
463
|
if (artefato && artefato.path === path && s.frontmatter) {
|
|
449
464
|
for (const v of s.frontmatter.valores) {
|
|
450
465
|
const [kind, destino] = exports.CHAVES_DO_FRONTMATTER[v.chave].split(':');
|
|
@@ -459,11 +474,14 @@ function extrairMarkdown(e) {
|
|
|
459
474
|
lacuna('frontmatter-sem-alvo', path, v.inicio, `${v.chave}: ${v.valor}`);
|
|
460
475
|
}
|
|
461
476
|
}
|
|
462
|
-
|
|
477
|
+
for (const m of s.mencoes)
|
|
478
|
+
mencao(path, m.inicio, m.id, (o) => secaoEm(s, arquivo, o));
|
|
463
479
|
}
|
|
464
480
|
for (const f of e.codigo) {
|
|
465
481
|
const arquivo = { kind: 'file', path: f.path, fragment: null };
|
|
466
|
-
|
|
482
|
+
for (const l of linhasDe(f.texto))
|
|
483
|
+
for (const m of l.texto.matchAll(MENCAO))
|
|
484
|
+
mencao(f.path, l.inicio + m.index, m[0], () => arquivo);
|
|
467
485
|
}
|
|
468
486
|
return saida;
|
|
469
487
|
}
|