@orkastery/cli 0.4.3 → 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/README.md +11 -0
- package/adapters/claude-code/.claude-plugin/plugin.json +3 -1
- package/adapters/claude-code/README.md +11 -2
- 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/onboarding.md +3 -2
- 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 +45 -2
- package/adapters/openclaw/dist/hitl-ingress.js +37 -3
- package/adapters/openclaw/dist/index.js +93 -21
- 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 +109 -21
- 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 +24 -7
- package/dist/auditrun.js +1 -1
- 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/claim-lint.js +3 -1
- 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 +135 -25
- package/dist/doctor.js +242 -32
- package/dist/entrega-pr.js +116 -5
- package/dist/experiencia-instalacao.js +256 -0
- package/dist/experiencia.js +72 -0
- package/dist/fabrica-estado.js +63 -12
- package/dist/fechamento.js +105 -0
- package/dist/fix.js +3 -3
- 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 +57 -13
- package/dist/hitl-texto-livre.js +150 -0
- package/dist/hosts.js +92 -3
- package/dist/index.js +637 -45
- package/dist/indice-vetorial.js +317 -0
- package/dist/init.js +24 -5
- package/dist/integracoes-locais.js +3 -1
- package/dist/intelligence-benchmark-contract.js +570 -0
- package/dist/intelligence-graph-cli.js +584 -0
- package/dist/intelligence-graph-contract.js +636 -0
- package/dist/intelligence-graph-extract-md.js +487 -0
- package/dist/intelligence-graph-extract-ts.js +980 -0
- package/dist/intelligence-graph-extract.js +546 -0
- package/dist/intelligence-graph-index.js +693 -0
- package/dist/intelligence-graph-parsers.js +327 -0
- package/dist/intelligence-graph-query.js +344 -0
- package/dist/intelligence-graph-repo.js +197 -0
- package/dist/leases.js +100 -0
- package/dist/ledger.js +23 -0
- package/dist/licoes.js +5 -4
- package/dist/liveness.js +2 -1
- package/dist/maestro-cli.js +10 -1
- package/dist/maestro-contract.js +5 -1
- package/dist/maestro-discovery.js +16 -2
- package/dist/maestro-runtime.js +3 -0
- package/dist/maestro-snapshot.js +6 -2
- package/dist/manifest.js +166 -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-artifacts.js +2 -0
- package/dist/mcp-experiencia.js +164 -0
- package/dist/mcp-git.js +64 -12
- package/dist/mcp-grafo-worker.js +91 -0
- package/dist/mcp-grafo.js +233 -0
- package/dist/mcp-install.js +5 -2
- package/dist/mcp-maestro.js +3 -2
- package/dist/mcp-server.js +93 -5
- package/dist/mcp-ship.js +2 -0
- package/dist/mcp-verify.js +3 -1
- package/dist/memoria.js +147 -1
- package/dist/network-roadmap.js +907 -0
- package/dist/ocupacao.js +43 -3
- package/dist/onboarding.js +122 -11
- package/dist/orkmind.js +165 -9
- package/dist/orquestracao.js +2 -1
- package/dist/parado-no-condutor.js +933 -0
- package/dist/phase.js +166 -13
- package/dist/policies.js +29 -2
- package/dist/projeto-alvo.js +551 -0
- package/dist/prompts.js +1 -1
- package/dist/prova-ativacao.js +345 -0
- package/dist/pulse-consentimento.js +4 -2
- package/dist/pulse-delivery.js +32 -6
- package/dist/pulse-resposta.js +674 -49
- package/dist/pulse.js +74 -3
- package/dist/retry.js +51 -8
- package/dist/roadmap-reservas.js +262 -20
- package/dist/roadmap-status.js +348 -0
- 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-close.js +3 -0
- package/dist/thread.js +21 -2
- package/dist/util.js +71 -4
- package/dist/verify.js +6 -5
- package/dist/worktree.js +29 -6
- package/eval/casos/onboarding.json +1 -1
- 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 +10 -2
- 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/skills/phases/ship-release/SKILL.md +6 -3
package/dist/mcp-maestro.js
CHANGED
|
@@ -9,7 +9,7 @@ const maestro_cli_1 = require("./maestro-cli");
|
|
|
9
9
|
exports.maestroInputSchema = zod_1.z.object({ threadId: zod_1.z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,79}$/).optional(),
|
|
10
10
|
section: zod_1.z.enum(maestro_contract_1.SECTION_NAMES).optional(), offset: zod_1.z.number().int().min(0).max(100000).optional() }).strict();
|
|
11
11
|
function registerMaestro(register, options) {
|
|
12
|
-
register('ork_maestro', { description: 'Panorama Maestro somente leitura, fixado ao projeto e ao escopo da sessão; cobertura, lacunas e próximas ações pelos endpoints existentes.',
|
|
12
|
+
register('ork_maestro', { description: 'Panorama Maestro somente leitura, fixado ao projeto e ao escopo da sessão; cobertura, lacunas e próximas ações pelos endpoints existentes. Não lê o roadmap, as reservas nem as outras máquinas (veja notConsulted): zero threads nunca é roadmap vazio; para o roadmap, com as threads de todas as máquinas, use ork_network_roadmap.',
|
|
13
13
|
inputSchema: exports.maestroInputSchema, annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false } }, async (args) => {
|
|
14
14
|
options.load();
|
|
15
15
|
if ((args.section === undefined) !== (args.offset === undefined))
|
|
@@ -17,7 +17,8 @@ function registerMaestro(register, options) {
|
|
|
17
17
|
const threadId = args.threadId ?? options.threadId;
|
|
18
18
|
if (threadId)
|
|
19
19
|
options.checkThread(threadId);
|
|
20
|
-
|
|
20
|
+
// RM-052 (D5): fixado no projeto servido; os outros projetos da maquina nao entram na conta.
|
|
21
|
+
const context = (0, maestro_discovery_1.discoverMaestro)({ cwd: options.root, pinned: options.root, countOtherProjects: false });
|
|
21
22
|
const result = (0, maestro_cli_1.readMaestro)(context, { threadId, native: true, offsets: args.section ? { [args.section]: args.offset } : undefined,
|
|
22
23
|
host: { tools: options.tools(), child: !!options.threadId } });
|
|
23
24
|
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
package/dist/mcp-server.js
CHANGED
|
@@ -56,6 +56,8 @@ const leases_1 = require("./leases");
|
|
|
56
56
|
const hitl_gates_1 = require("./hitl-gates");
|
|
57
57
|
const hitl_contract_1 = require("./hitl-contract");
|
|
58
58
|
const hitl_presentation_1 = require("./hitl-presentation");
|
|
59
|
+
const roadmap_status_1 = require("./roadmap-status");
|
|
60
|
+
const network_roadmap_1 = require("./network-roadmap");
|
|
59
61
|
const hitl_sessions_1 = require("./hitl-sessions");
|
|
60
62
|
const hitl_local_1 = require("./hitl-local");
|
|
61
63
|
const mcp_artifacts_1 = require("./mcp-artifacts");
|
|
@@ -65,6 +67,7 @@ const modos_1 = require("./modos");
|
|
|
65
67
|
const versao_1 = require("./versao");
|
|
66
68
|
const mcp_verify_1 = require("./mcp-verify");
|
|
67
69
|
const conducao_1 = require("./conducao");
|
|
70
|
+
const decisao_autonoma_1 = require("./decisao-autonoma");
|
|
68
71
|
const conducao_texto_1 = require("./conducao-texto");
|
|
69
72
|
const mcp_ship_1 = require("./mcp-ship");
|
|
70
73
|
const company_brain_mcp_1 = require("./company-brain-mcp");
|
|
@@ -72,6 +75,9 @@ const claude_bg_1 = require("./adapters/claude-bg");
|
|
|
72
75
|
const session_watcher_claude_1 = require("./session-watcher-claude");
|
|
73
76
|
const runtime_profiles_1 = require("./runtime-profiles");
|
|
74
77
|
const mcp_maestro_1 = require("./mcp-maestro");
|
|
78
|
+
const projeto_alvo_1 = require("./projeto-alvo");
|
|
79
|
+
const mcp_experiencia_1 = require("./mcp-experiencia");
|
|
80
|
+
const mcp_grafo_1 = require("./mcp-grafo");
|
|
75
81
|
const identidade = zod_1.z.string().min(1).max(80).regex(/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/);
|
|
76
82
|
const daThread = zod_1.z.object({ threadId: identidade }).strict();
|
|
77
83
|
const daDecisao = daThread.extend({ pedidoId: identidade }).strict();
|
|
@@ -291,21 +297,43 @@ function criarServidorMcp(opcoes) {
|
|
|
291
297
|
};
|
|
292
298
|
const livre = (id) => {
|
|
293
299
|
const nomes = ['main-tree', ...(id ? ['worktree-write:' + id] : [])];
|
|
300
|
+
// RM-037 (fatia 3, GO-FIX 2, achado 1 do CHECK): esta e a primeira conferencia de toda tool de escrita.
|
|
301
|
+
// Lease e fila de thread fechada sao orfaos e saem aqui, com registro no ledger dela; sem isso o
|
|
302
|
+
// main-tree de um SHIP que caiu antes do fechamento travava verify, commit, ship e artefato pelo MCP.
|
|
303
|
+
(0, leases_1.podarRegioesDeThreadsFechadas)(raiz, nomes, id ?? '');
|
|
294
304
|
for (const nome of nomes) {
|
|
295
305
|
const conflitos = (0, leases_1.leasesColidentes)(raiz, nome, id);
|
|
296
306
|
if (conflitos.length)
|
|
297
307
|
throw Error('lease.busy: ' + conflitos.map(l => l.nome + ' (' + l.thread + ')').join(', '));
|
|
298
308
|
}
|
|
299
309
|
};
|
|
300
|
-
const server = new index_js_1.Server({ name: 'orkastery', version: versao_1.VERSAO_DO_ORK }, { capabilities: { tools: {} }, instructions: 'Opere somente este projeto pelas ferramentas do nucleo. Decisoes humanas usam ork_request_decision e o dialogo do host; argumentos de ferramenta nunca sao respostas.' });
|
|
310
|
+
const server = new index_js_1.Server({ name: 'orkastery', version: versao_1.VERSAO_DO_ORK }, { capabilities: { tools: {} }, instructions: 'Opere somente este projeto pelas ferramentas do nucleo. O parametro opcional projeto so confere o projeto servido; nunca troca a raiz. O status do roadmap vem de ork_network_roadmap, transportado como vem. Decisoes humanas usam ork_request_decision e o dialogo do host; argumentos de ferramenta nunca sao respostas.' });
|
|
311
|
+
/**
|
|
312
|
+
* RM-052 (D5): toda tool aceita `projeto`, e ele so CONFERE o projeto fixado no startup. Pedido de
|
|
313
|
+
* outro projeto recusa tipado, com o projeto servido como unico candidato; a raiz nunca muda aqui.
|
|
314
|
+
*/
|
|
315
|
+
const conferirProjeto = (pedido) => {
|
|
316
|
+
const c = carregar(), nome = c.manifesto.project.name, abbrev = c.manifesto.project.abbrev, v = pedido.toLowerCase();
|
|
317
|
+
if (nome.toLowerCase() === v || (!!abbrev && abbrev.toLowerCase() === v))
|
|
318
|
+
return;
|
|
319
|
+
throw new projeto_alvo_1.ErroDeProjeto('projeto.fora-do-servidor', `este servidor MCP atende somente o projeto ${nome}; "${pedido}" nao e ele`, [{ nome, abbrev, raiz: (0, projeto_alvo_1.raizParaExibir)(raiz), remoto: (0, projeto_alvo_1.remotoDoProjeto)(raiz, c.manifesto.fabrica?.remoto ?? 'origin'), presente: true }], 'Use o servidor MCP instalado no outro projeto (ork mcp install --project <raiz>), ou o CLI com --projeto <nome>.');
|
|
320
|
+
};
|
|
321
|
+
const campoProjeto = zod_1.z.string().regex(projeto_alvo_1.PADRAO_DO_NOME_DE_PROJETO)
|
|
322
|
+
.describe('Nome ou abbrev do projeto pedido; este servidor atende somente o projeto fixado na instalacao');
|
|
301
323
|
const ferramentas = new Map();
|
|
302
324
|
function registrarTool(nome, config, executar) {
|
|
303
325
|
if (opcoes.threadId && ['ork_thread_new', 'ork_phase_run', 'ork_request_decision', 'ork_preflight'].includes(nome))
|
|
304
326
|
return;
|
|
305
|
-
const
|
|
327
|
+
const entrada = config.inputSchema.extend({ projeto: campoProjeto.optional() });
|
|
328
|
+
const schema = converterSchema(entrada, { $refStrategy: 'none' });
|
|
306
329
|
ferramentas.set(nome, { tool: { name: nome, description: config.description, annotations: config.annotations,
|
|
307
330
|
inputSchema: schema },
|
|
308
|
-
executar: async (raw, extra) =>
|
|
331
|
+
executar: async (raw, extra) => {
|
|
332
|
+
const { projeto, ...args } = entrada.parse(raw);
|
|
333
|
+
if (projeto !== undefined)
|
|
334
|
+
conferirProjeto(projeto);
|
|
335
|
+
return executar(args, extra);
|
|
336
|
+
} });
|
|
309
337
|
}
|
|
310
338
|
server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({ tools: [...ferramentas.values()].map(f => f.tool) }));
|
|
311
339
|
server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request, extra) => {
|
|
@@ -316,6 +344,8 @@ function criarServidorMcp(opcoes) {
|
|
|
316
344
|
return await f.executar(request.params.arguments ?? {}, extra);
|
|
317
345
|
}
|
|
318
346
|
catch (e) {
|
|
347
|
+
if (e instanceof projeto_alvo_1.ErroDeProjeto)
|
|
348
|
+
return { ...resposta(e.recusa), isError: true };
|
|
319
349
|
return { ...resposta({ erro: e.message }), isError: true };
|
|
320
350
|
}
|
|
321
351
|
});
|
|
@@ -344,6 +374,7 @@ function criarServidorMcp(opcoes) {
|
|
|
344
374
|
});
|
|
345
375
|
server.onclose = () => ingresso.fechar();
|
|
346
376
|
(0, company_brain_mcp_1.registerBrainTools)(registrarTool, carregar, thread, opcoes.brainWrites ?? []);
|
|
377
|
+
(0, mcp_experiencia_1.registrarConsultasExperiencia)(registrarTool, { raiz, carregar, transporte: opcoes.transporteShip ?? 'github-ssh' });
|
|
347
378
|
(0, mcp_maestro_1.registerMaestro)(registrarTool, { root: raiz, threadId: opcoes.threadId, load: carregar, checkThread: thread, tools: () => [...ferramentas.keys()] });
|
|
348
379
|
registrarTool('ork_preflight', { description: 'Preflight contextual por modo e bloco, sem inventário global/modelos. Não autoriza despacho nem comprova entrega.',
|
|
349
380
|
inputSchema: zod_1.z.object({ modo: modoDeConducao }).strict(), annotations: { readOnlyHint: true } }, async ({ modo }) => {
|
|
@@ -402,6 +433,35 @@ function criarServidorMcp(opcoes) {
|
|
|
402
433
|
livre(threadId);
|
|
403
434
|
return resposta((0, mcp_artifacts_1.escreverArtefatoMcp)(raiz, threadId, tipo, conteudo, expectedSha256));
|
|
404
435
|
});
|
|
436
|
+
// RM-037 (rm037defeito, defeito 1): a decisao que a sessao toma sem perguntar ao dono. O sandbox do
|
|
437
|
+
// codex nao grava o ledger e o `ork decisao registrar` morria em EROFS. Mesma `registrarDecisao` do CLI:
|
|
438
|
+
// contrato v2, criterio que resolve e rastro no mesmo evento. Nao pergunta ao dono e nao toca gate.
|
|
439
|
+
const campoDaDecisao = zod_1.z.string().min(1).max(4096);
|
|
440
|
+
registrarTool('ork_decision_record', { description: 'Registra decisao ja tomada pela sessao sem perguntar ao dono (classe decidido), com criterio que resolve e rastro. Nao pergunta, nao responde nem aprova gate.',
|
|
441
|
+
inputSchema: daThread.extend({ decidido: campoDaDecisao, porque: campoDaDecisao, comoMudar: campoDaDecisao,
|
|
442
|
+
custoAgora: campoDaDecisao, custoDepois: campoDaDecisao,
|
|
443
|
+
criterio: zod_1.z.object({ tipo: zod_1.z.enum(['manifesto', 'ledger', 'medicao']), referencia: campoDaDecisao }).strict(),
|
|
444
|
+
quemDecidiu: zod_1.z.string().min(1).max(200), evidencia: campoDaDecisao, razao: campoDaDecisao.optional(),
|
|
445
|
+
reverte: zod_1.z.string().min(1).max(80).optional() }).strict(),
|
|
446
|
+
annotations: { readOnlyHint: false, destructiveHint: false } }, async ({ threadId, ...e }) => {
|
|
447
|
+
thread(threadId);
|
|
448
|
+
livre(threadId);
|
|
449
|
+
// A sessao decide por ela: o rastro nao pode nascer assinado em nome do dono.
|
|
450
|
+
if (/^\s*(?:o\s+|a\s+)?(?:dono|owner|builder|maestro)\b/i.test(e.quemDecidiu))
|
|
451
|
+
throw Error('mcp.decision.author: quemDecidiu e a sessao que decidiu, nunca o dono');
|
|
452
|
+
let registro;
|
|
453
|
+
try {
|
|
454
|
+
registro = (0, decisao_autonoma_1.registrarDecisao)(raiz, threadId, { decidido: e.decidido, porque: e.porque, comoMudar: e.comoMudar,
|
|
455
|
+
custoDeReverter: { agora: e.custoAgora, depois: e.custoDepois }, criterio: e.criterio, quemDecidiu: e.quemDecidiu,
|
|
456
|
+
evidencia: e.evidencia, ...(e.razao ? { razao: e.razao } : {}), ...(e.reverte ? { reverte: e.reverte } : {}), origem: 'mcp', host: opcoes.host,
|
|
457
|
+
despacho: opcoes.dispatchId ?? null });
|
|
458
|
+
}
|
|
459
|
+
catch (erro) {
|
|
460
|
+
throw Error((0, hitl_contract_1.recusaNaSuperficie)(erro.message, hitl_contract_1.CAMPOS_DA_DECISAO_NO_MCP));
|
|
461
|
+
}
|
|
462
|
+
const { pedido, evento } = registro;
|
|
463
|
+
return resposta({ ok: true, pedidoId: pedido.id, eventId: evento.eventId, fase: pedido.fase, reciboOficial: false });
|
|
464
|
+
});
|
|
405
465
|
registrarTool('ork_claims_list', { description: 'Lista alegacoes e seu estado de verificacao registrado pelo nucleo.',
|
|
406
466
|
inputSchema: daThread, annotations: { readOnlyHint: true } }, async ({ threadId }) => {
|
|
407
467
|
thread(threadId);
|
|
@@ -423,6 +483,25 @@ function criarServidorMcp(opcoes) {
|
|
|
423
483
|
});
|
|
424
484
|
registrarTool('ork_hitl_pending', { description: 'Pedidos HITL atuais da thread, incluindo prazo; nao aprova nem abre pedido.',
|
|
425
485
|
inputSchema: daThread, annotations: { readOnlyHint: true } }, async ({ threadId }) => resposta({ threadId, pendencias: pendencias(threadId) }));
|
|
486
|
+
registrarTool('ork_roadmap_status', { description: 'Status report do roadmap SO desta maquina (o checkout local), no formato aprovado pelo dono: grupos com icones, #HITL no que espera o dono e o fecho. Somente leitura; transporte o texto como vem. O cabecalho diz o projeto consultado e o que nao foi lido (reservas, outras maquinas). Para o status do roadmap com as threads de todas as maquinas, as reservas, as fontes e as lacunas, use ork_network_roadmap; nunca conclua sobre ele a partir de ork_maestro ou de threads.',
|
|
487
|
+
inputSchema: zod_1.z.object({}).strict(), annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false } }, async () => {
|
|
488
|
+
const c = carregar();
|
|
489
|
+
// RM-052: origem `instalacao`; o servidor fixado nao revela os outros projetos da maquina.
|
|
490
|
+
const consulta = (0, projeto_alvo_1.consultaDoProjeto)(c, { origem: 'instalacao', outrosProjetos: false,
|
|
491
|
+
lido: ['roadmap (docs/roadmap)', projeto_alvo_1.FORA_DA_CONSULTA.threadsDaMaquina], naoLido: [projeto_alvo_1.FORA_DA_CONSULTA.reservas, 'threads de outras máquinas (ork network roadmap)'] });
|
|
492
|
+
const status = (0, roadmap_status_1.montarStatusDoRoadmap)(raiz, { projeto: c.manifesto.project.name, consulta });
|
|
493
|
+
return resposta({ texto: (0, roadmap_status_1.textoDoStatusDoRoadmap)(status), status });
|
|
494
|
+
});
|
|
495
|
+
/**
|
|
496
|
+
* RM-054 (fatia 2, D-G5): o roadmap da rede do projeto servido, em todas as maquinas dele. O servidor
|
|
497
|
+
* fixado nao le o registro nem revela os outros projetos (D5 da RM-052); o texto e o do CLI.
|
|
498
|
+
*/
|
|
499
|
+
registrarTool('ork_network_roadmap', { description: 'Roadmap da rede do projeto servido, somente leitura: o status report do roadmap (RM-048) com as threads de TODAS as maquinas, as reservas, as threads por maquina com a idade da batida, a fonte e a hora de cada parte e as lacunas tipadas. E a fonte do status do roadmap: transporte o texto como vem, sem reescrever nem resumir. Lacuna e "Nao consultado" sao o que nao foi lido: nunca conclua "roadmap vazio" nem "nenhuma maquina publicou" a partir deles. Este servidor le so o projeto fixado; os outros projetos vem do CLI ork network roadmap.',
|
|
500
|
+
inputSchema: zod_1.z.object({}).strict(), annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true } }, async () => {
|
|
501
|
+
carregar();
|
|
502
|
+
const panorama = (0, network_roadmap_1.montarPanoramaDaRede)({ fixado: raiz });
|
|
503
|
+
return resposta({ texto: (0, network_roadmap_1.textoDoPanoramaDaRede)(panorama), panorama });
|
|
504
|
+
});
|
|
426
505
|
registrarTool('ork_observe', { description: 'Observa uma vez o progresso canonico e pedidos da thread. Nao cria monitor ou despacho.',
|
|
427
506
|
inputSchema: daThread, annotations: { readOnlyHint: true } }, async ({ threadId }) => {
|
|
428
507
|
return resposta(observar(threadId));
|
|
@@ -439,14 +518,19 @@ function criarServidorMcp(opcoes) {
|
|
|
439
518
|
while (!fs.existsSync(existente))
|
|
440
519
|
existente = path.dirname(existente);
|
|
441
520
|
confinado(existente);
|
|
442
|
-
|
|
521
|
+
const criada = (0, thread_1.novaThread)(c, { nome, modo: modo ?? c.manifesto.conduction.default_mode, criarWorktree: true });
|
|
522
|
+
// RM-052 (D7): thread nova, projeto conhecido pelo nome nesta maquina; o registro nunca derruba a criacao.
|
|
523
|
+
if (criada.gravada)
|
|
524
|
+
(0, projeto_alvo_1.registrarProjetoEmSilencio)(raiz, 'mcp thread new');
|
|
525
|
+
return resposta(criada);
|
|
443
526
|
});
|
|
444
527
|
registrarTool('ork_phase_run', { description: 'Despacha fase autorizada pelo nucleo, preservando policies, estado e runtime/modelo. dryRun nao inicia modelo, mas pode registrar prompt/eventos.',
|
|
445
528
|
inputSchema: fase, annotations: { readOnlyHint: false, destructiveHint: false } }, async (args) => {
|
|
446
529
|
const t = thread(args.threadId);
|
|
447
530
|
livre(t.id);
|
|
448
531
|
const { threadId, fase: f, ...op } = args;
|
|
449
|
-
|
|
532
|
+
// RM-037 (A3): o MCP nao roda a suite para a baseline do despacho; a falta dela volta como pendencia.
|
|
533
|
+
return resposta((0, phase_1.rodarFase)(carregar(), threadId, { ...op, fase: (0, thread_1.exigirFase)(t, f), canal: conducao.canal, baselinePeloDespacho: false }));
|
|
450
534
|
});
|
|
451
535
|
registrarTool('ork_gate_request', { description: 'Abre pedido de gate somente quando pausa ou escalacao tipada esta comprovada no nucleo; nao aprova.',
|
|
452
536
|
inputSchema: daThread.extend({ motivo: zod_1.z.enum(['human.pending', 'policy.violation', 'cost.violation',
|
|
@@ -461,6 +545,10 @@ function criarServidorMcp(opcoes) {
|
|
|
461
545
|
thread(threadId);
|
|
462
546
|
return resposta(await ingresso.solicitar(threadId, pedidoId, extra.signal));
|
|
463
547
|
});
|
|
548
|
+
// RM-031 KG5 (D2): as tools do grafo so existem com a flag do manifesto da raiz no startup; sem ela, nada muda.
|
|
549
|
+
// CHECK rodada 1 (S8): o startup le so o manifesto, como antes; as conferencias de estado ficam na chamada.
|
|
550
|
+
if ((0, mcp_grafo_1.grafoLigado)((0, manifest_1.exigirManifesto)(raiz).manifesto))
|
|
551
|
+
(0, mcp_grafo_1.registrarConsultasDoGrafo)(registrarTool, { raiz, carregar, thread });
|
|
464
552
|
return server;
|
|
465
553
|
}
|
|
466
554
|
async function servirMcp(opcoes) {
|
package/dist/mcp-ship.js
CHANGED
|
@@ -324,6 +324,8 @@ function executar(f, p) {
|
|
|
324
324
|
if (comandos.length > 512 || comandos.reduce((n, s) => n + Buffer.byteLength(s), 0) > LIMITE)
|
|
325
325
|
erro('verify.limit');
|
|
326
326
|
const nome = `worktree-write:${t.id}`, nomes = [nome, `path:.orkastery/threads/${t.id}`];
|
|
327
|
+
// RM-037 (fatia 3, defeito 4): lease e fila de thread fechada sao orfaos e saem antes da conferencia.
|
|
328
|
+
(0, leases_1.podarRegioesDeThreadsFechadas)(raiz, [...nomes, 'main-tree'], t.id);
|
|
327
329
|
for (const n of [...nomes, 'main-tree'])
|
|
328
330
|
if (fs.lstatSync((0, leases_1.caminhoLease)(raiz, n), { throwIfNoEntry: false }) || (0, leases_1.leasesColidentes)(raiz, n).length)
|
|
329
331
|
erro('lease.busy');
|
package/dist/mcp-verify.js
CHANGED
|
@@ -97,7 +97,9 @@ function verificarMcp(raizEntrada, threadId, conducao = {}) {
|
|
|
97
97
|
const stFila = fs.lstatSync((0, leases_1.caminhoFila)(raiz), { throwIfNoEntry: false });
|
|
98
98
|
if (stFila && (!stFila.isFile() || stFila.nlink !== 1))
|
|
99
99
|
throw Error('mcp.verify.queue.unsafe');
|
|
100
|
-
//
|
|
100
|
+
// RM-037 (fatia 3, defeito 4): lease e fila de thread fechada são órfãos e saem antes da conferência.
|
|
101
|
+
(0, leases_1.podarRegioesDeThreadsFechadas)(raiz, [...nomes, 'main-tree'], threadId);
|
|
102
|
+
// Não herda nem remove lease existente de thread aberta, inclusive própria, expirada ou corrompida.
|
|
101
103
|
if (nomes.some(nome => fs.lstatSync((0, leases_1.caminhoLease)(raiz, nome), { throwIfNoEntry: false }) ||
|
|
102
104
|
(0, leases_1.leasesColidentes)(raiz, nome).length) || (0, leases_1.leasesColidentes)(raiz, 'main-tree').length) {
|
|
103
105
|
throw Error('lease.busy: verificação exige leases próprias livres');
|
package/dist/memoria.js
CHANGED
|
@@ -35,6 +35,8 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.sincronizarOnboarding = sincronizarOnboarding;
|
|
37
37
|
exports.abrirMemoria = abrirMemoria;
|
|
38
|
+
exports.estadoDeEmbeddings = estadoDeEmbeddings;
|
|
39
|
+
exports.sondarEmbeddings = sondarEmbeddings;
|
|
38
40
|
exports.tagsDoProjeto = tagsDoProjeto;
|
|
39
41
|
exports.situacaoDaThread = situacaoDaThread;
|
|
40
42
|
exports.situacaoDaFase = situacaoDaFase;
|
|
@@ -64,6 +66,7 @@ exports.sincronizarMemoria = sincronizarMemoria;
|
|
|
64
66
|
exports.publicar = publicar;
|
|
65
67
|
exports.publicarPropostas = publicarPropostas;
|
|
66
68
|
exports.textoDoEstado = textoDoEstado;
|
|
69
|
+
exports.textoDosEmbeddings = textoDosEmbeddings;
|
|
67
70
|
exports.textoDoSync = textoDoSync;
|
|
68
71
|
exports.parsePrioridade = parsePrioridade;
|
|
69
72
|
const write_activation_1 = require("./write-activation");
|
|
@@ -97,6 +100,7 @@ const memoria_humana_1 = require("./memoria-humana");
|
|
|
97
100
|
const master_1 = require("./master");
|
|
98
101
|
const modos_1 = require("./modos");
|
|
99
102
|
const orkmind_1 = require("./orkmind");
|
|
103
|
+
const indice_vetorial_1 = require("./indice-vetorial");
|
|
100
104
|
const ledger_1 = require("./ledger");
|
|
101
105
|
const thread_1 = require("./thread");
|
|
102
106
|
const util_1 = require("./util");
|
|
@@ -167,6 +171,10 @@ function abrirMemoria(carregado, opcoes = {}) {
|
|
|
167
171
|
const leituraRestrita = resolverLeituraRestrita(carregado.manifesto, opcoes.leituraRestrita);
|
|
168
172
|
const { estado, driver } = (0, orkmind_1.resolverRegime)(carregado.manifesto, opcoes.driver ?? null);
|
|
169
173
|
const ativo = estado.efetivo === 'orkmind' && driver !== null;
|
|
174
|
+
const config = (0, orkmind_1.configDoManifesto)(carregado.manifesto);
|
|
175
|
+
const embeddings = (universo) => estadoDeEmbeddings(carregado.manifesto, ativo ? driver?.disponivel().saude ?? null : null, carregado.raiz, estado.tenant, config.dsn, { universo });
|
|
176
|
+
if (estado.pedido === 'orkmind')
|
|
177
|
+
estado.embeddings = embeddings();
|
|
170
178
|
const consultarRestrito = (collection, tags) => {
|
|
171
179
|
if (!leituraRestrita)
|
|
172
180
|
throw Error('memory.query.invalid');
|
|
@@ -178,7 +186,7 @@ function abrirMemoria(carregado, opcoes = {}) {
|
|
|
178
186
|
throw Error('memory.query.unsupported');
|
|
179
187
|
return (0, orkmind_1.concluirConsultaDelimitada)(driver.consultar(q), q);
|
|
180
188
|
};
|
|
181
|
-
|
|
189
|
+
const memoria = {
|
|
182
190
|
configSource: context.source,
|
|
183
191
|
configDivergent: context.divergent,
|
|
184
192
|
get leituraRestrita() { return leituraRestrita; },
|
|
@@ -243,6 +251,114 @@ function abrirMemoria(carregado, opcoes = {}) {
|
|
|
243
251
|
return driver.exportar(colecao).find((e) => e.id === id) ?? null;
|
|
244
252
|
},
|
|
245
253
|
};
|
|
254
|
+
// A cobertura precisa do universo do tenant (um export por colecao): so quando pedida.
|
|
255
|
+
if (opcoes.embeddings === 'detalhado' && ativo && !leituraRestrita) {
|
|
256
|
+
estado.embeddings = embeddings((0, indice_vetorial_1.universoDoTenant)(memoria, estado.tenant));
|
|
257
|
+
}
|
|
258
|
+
return memoria;
|
|
259
|
+
}
|
|
260
|
+
// ---------------------------------------------------------------------------
|
|
261
|
+
// I-38 (T4, D6): estado sondado dos embeddings. Nunca derruba o regime.
|
|
262
|
+
// ---------------------------------------------------------------------------
|
|
263
|
+
/** Os indices locais do tenant nesta base, por modelo e dimensao. */
|
|
264
|
+
function indicesDoTenant(raiz, tenant, base, universo) {
|
|
265
|
+
const dir = (0, indice_vetorial_1.dirDosIndices)(raiz, tenant);
|
|
266
|
+
if (!fs.existsSync(dir))
|
|
267
|
+
return [];
|
|
268
|
+
const indices = [];
|
|
269
|
+
for (const nome of fs.readdirSync(dir).filter(n => n.endsWith('.json')).sort()) {
|
|
270
|
+
try {
|
|
271
|
+
const bruto = JSON.parse(fs.readFileSync(path.join(dir, nome), 'utf8'));
|
|
272
|
+
if (bruto.contrato !== indice_vetorial_1.CONTRATO_INDICE || bruto.tenant !== tenant || bruto.base !== base)
|
|
273
|
+
continue;
|
|
274
|
+
const { indice } = (0, indice_vetorial_1.lerIndice)(path.join(dir, nome), { tenant, base, modelo: bruto.modelo, dim: bruto.dim });
|
|
275
|
+
const vetores = Object.keys(indice.entradas).length;
|
|
276
|
+
const coerentes = (0, indice_vetorial_1.vetoresCoerentes)(indice, universo).size;
|
|
277
|
+
indices.push({ modelo: indice.modelo, dim: indice.dim, vetores, coerentes, desatualizados: vetores - coerentes });
|
|
278
|
+
}
|
|
279
|
+
catch { /* arquivo ilegivel nao e indice: `ork memory index` o reconstroi */ }
|
|
280
|
+
}
|
|
281
|
+
return indices;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Estado de embeddings: configuracao, presenca da chave (nome, nunca valor), a sonda `health`
|
|
285
|
+
* da ponte e os arquivos de indice. `saude` null quer dizer que a ponte nao foi sondada.
|
|
286
|
+
*/
|
|
287
|
+
function estadoDeEmbeddings(manifesto, saude, raiz, tenant, dsn, opcoes = {}) {
|
|
288
|
+
const config = (0, manifest_1.configDeEmbedding)(manifesto);
|
|
289
|
+
const env = opcoes.env ?? process.env;
|
|
290
|
+
const configurado = config.provider !== 'none';
|
|
291
|
+
const variavel = config.api_key_env;
|
|
292
|
+
const bruto = variavel ? (env[variavel] ?? '').trim() : '';
|
|
293
|
+
const chavePresente = !!variavel && (0, orkmind_1.chaveDeEmbeddingAceita)(bruto, dsn);
|
|
294
|
+
const recusada = bruto !== '' && !chavePresente;
|
|
295
|
+
const dimLocal = configurado && config.fallback_model ? (0, indice_vetorial_1.dimDoModeloLocal)(config.fallback_model, env) : null;
|
|
296
|
+
const dependencias = saude?.fallback.dependencias ?? false;
|
|
297
|
+
const fallback = { modelo: config.fallback_model || null, presente: dimLocal !== null, dependencias, dim: dimLocal };
|
|
298
|
+
const base = (0, indice_vetorial_1.impressaoDaBase)(dsn);
|
|
299
|
+
const existe = (modelo, dim) => dim !== null && fs.existsSync((0, indice_vetorial_1.arquivoDoIndice)(raiz, tenant, modelo, dim));
|
|
300
|
+
const estado = {
|
|
301
|
+
configurado, provider: config.provider, modelo: configurado ? config.model : null, dim: configurado ? config.dim : null,
|
|
302
|
+
variavelDaChave: variavel, chavePresente, fallback, indices: [], entradas: null, cobertura: null,
|
|
303
|
+
ativo: 'nenhum', sondado: saude !== null, motivo: null, detalhe: '', correcao: '',
|
|
304
|
+
};
|
|
305
|
+
if (!configurado) {
|
|
306
|
+
return { ...estado, motivo: 'embeddings.nao-configurado',
|
|
307
|
+
detalhe: 'memory.embedding ausente ou provider none: busca por significado desligada; o recall por tag segue identico',
|
|
308
|
+
correcao: 'declare memory.embedding no manifesto (provider openrouter e api_key_env com o NOME da variavel da chave)' };
|
|
309
|
+
}
|
|
310
|
+
const fallbackUsavel = saude !== null && fallback.presente && dependencias;
|
|
311
|
+
if (chavePresente) {
|
|
312
|
+
estado.ativo = 'primario';
|
|
313
|
+
if (!existe(config.model, config.dim)) {
|
|
314
|
+
estado.motivo = 'embeddings.indice-ausente';
|
|
315
|
+
estado.detalhe = `chave presente em ${variavel}; o indice local de ${config.model} ainda nao existe`;
|
|
316
|
+
estado.correcao = 'rode ork memory index (use --dry-run antes para ver tokens e custo)';
|
|
317
|
+
}
|
|
318
|
+
else
|
|
319
|
+
estado.detalhe = `primario ${config.model} (${config.dim} dim) pela chave em ${variavel}`;
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
estado.ativo = fallbackUsavel ? 'fallback' : 'nenhum';
|
|
323
|
+
estado.motivo = 'embeddings.chave-ausente';
|
|
324
|
+
const local = !fallback.modelo ? 'sem fallback local declarado: a busca por significado cai para FTS'
|
|
325
|
+
: !fallback.presente ? `fallback ${fallback.modelo} ausente do cache local: a busca cai para FTS`
|
|
326
|
+
: !saude ? 'ponte nao sondada: fallback local nao conferido'
|
|
327
|
+
: !dependencias ? `fallback ${fallback.modelo} sem torch/transformers no interpretador da ponte: a busca cai para FTS`
|
|
328
|
+
: `busca usa o fallback local ${fallback.modelo} (${fallback.dim} dim)` +
|
|
329
|
+
(existe(fallback.modelo, fallback.dim) ? '' : '; indice local ausente: rode ork memory index --modelo fallback');
|
|
330
|
+
estado.detalhe = `${variavel} ${recusada ? 'tem valor recusado (parece URL, DSN ou texto com espaco; nunca impresso)' : 'nao esta no ambiente (valor nunca impresso)'}; ${local}`;
|
|
331
|
+
estado.correcao = `exporte ${variavel} com a chave dedicada ao Orkastery; o valor nunca vai ao manifesto`;
|
|
332
|
+
}
|
|
333
|
+
if (opcoes.universo) {
|
|
334
|
+
const universo = opcoes.universo.filter(e => (e.tags.project ?? []).includes(tenant));
|
|
335
|
+
estado.indices = indicesDoTenant(raiz, tenant, base, universo);
|
|
336
|
+
estado.entradas = universo.length;
|
|
337
|
+
const referencia = estado.ativo === 'fallback' ? { modelo: fallback.modelo, dim: fallback.dim } : { modelo: config.model, dim: config.dim };
|
|
338
|
+
const indice = estado.indices.find(i => i.modelo === referencia.modelo && i.dim === referencia.dim);
|
|
339
|
+
estado.cobertura = universo.length === 0 ? 0 : Math.round(((indice?.coerentes ?? 0) / universo.length) * 10_000) / 10_000;
|
|
340
|
+
}
|
|
341
|
+
return estado;
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* `ork memory status --sondar`: UMA chamada real com texto fixo curto pelo caminho ativo, com
|
|
345
|
+
* a latencia medida (primario custa cerca de US$ 0,00000005; fallback local, so CPU).
|
|
346
|
+
*/
|
|
347
|
+
function sondarEmbeddings(manifesto, estado, embeddar, timeoutMs) {
|
|
348
|
+
if (estado.ativo === 'nenhum')
|
|
349
|
+
return { ok: false, alvo: null, latenciaMs: null, motivo: estado.motivo };
|
|
350
|
+
const config = (0, manifest_1.configDeEmbedding)(manifesto);
|
|
351
|
+
const primario = estado.ativo === 'primario';
|
|
352
|
+
const modelo = primario ? config.model : estado.fallback.modelo;
|
|
353
|
+
const dim = primario ? config.dim : estado.fallback.dim;
|
|
354
|
+
const inicio = Date.now();
|
|
355
|
+
try {
|
|
356
|
+
embeddar({ papel: 'consulta', alvo: estado.ativo, modelo, dim, textos: ['sonda de saude do ork'] }, { timeoutMs });
|
|
357
|
+
return { ok: true, alvo: estado.ativo, latenciaMs: Date.now() - inicio, motivo: null };
|
|
358
|
+
}
|
|
359
|
+
catch (erro) {
|
|
360
|
+
return { ok: false, alvo: estado.ativo, latenciaMs: Date.now() - inicio, motivo: (0, indice_vetorial_1.codigoDeEmbedding)(erro) };
|
|
361
|
+
}
|
|
246
362
|
}
|
|
247
363
|
// ---------------------------------------------------------------------------
|
|
248
364
|
// Tags: as 5 dimensoes, montadas em um lugar so.
|
|
@@ -968,8 +1084,38 @@ function textoDoEstado(estado) {
|
|
|
968
1084
|
linhas.push(` colecoes gravadas: ${orkmind_1.COLECOES_DO_ORK.join(', ')}`);
|
|
969
1085
|
linhas.push(' governanca: source=agent; decision/human exige human_gate comprovado; mandatory=false e regra nunca critical.');
|
|
970
1086
|
}
|
|
1087
|
+
if (estado.embeddings)
|
|
1088
|
+
linhas.push('', ...textoDosEmbeddings(estado.embeddings));
|
|
971
1089
|
return linhas.join('\n');
|
|
972
1090
|
}
|
|
1091
|
+
/** Secao de embeddings do `ork memory status` (I-38 D6): nome da variavel, nunca o valor. */
|
|
1092
|
+
function textoDosEmbeddings(e) {
|
|
1093
|
+
const linhas = ['Busca por significado (embeddings, nao deterministica; o recall por tag nao muda)', ''];
|
|
1094
|
+
linhas.push(` provider ${e.configurado ? `${e.provider} ${e.modelo} (${e.dim} dim)` : 'none (desligada)'}`);
|
|
1095
|
+
if (e.configurado) {
|
|
1096
|
+
linhas.push(` chave ${e.variavelDaChave} ${e.chavePresente ? 'presente' : 'ausente'} no ambiente (valor nunca impresso)`);
|
|
1097
|
+
linhas.push(` fallback local ${e.fallback.modelo ? `${e.fallback.modelo}: ${e.fallback.presente ? `no cache (${e.fallback.dim} dim)` : 'ausente do cache'}, ` +
|
|
1098
|
+
`dependencias ${e.fallback.dependencias ? 'ok' : e.sondado ? 'ausentes' : 'nao sondadas'}` : '(nao declarado)'}`);
|
|
1099
|
+
linhas.push(` caminho ativo ${e.ativo}${e.sondado ? '' : ' (ponte nao sondada)'}`);
|
|
1100
|
+
}
|
|
1101
|
+
for (const i of e.indices) {
|
|
1102
|
+
linhas.push(` indice ${i.modelo} / ${i.dim} dim: ${i.vetores} vetor(es), ${i.coerentes ?? '?'} coerente(s), ${i.desatualizados ?? '?'} desatualizado(s)`);
|
|
1103
|
+
}
|
|
1104
|
+
if (e.entradas !== null) {
|
|
1105
|
+
linhas.push(` cobertura ${e.cobertura === null ? '-' : `${Math.round(e.cobertura * 1000) / 10}%`} de ${e.entradas} entrada(s) do tenant`);
|
|
1106
|
+
}
|
|
1107
|
+
if (e.sonda) {
|
|
1108
|
+
linhas.push(` sonda ${e.sonda.ok ? `${e.sonda.alvo} respondeu em ${e.sonda.latenciaMs} ms` : `sem resposta (${e.sonda.motivo ?? 'sem caminho ativo'})`}`);
|
|
1109
|
+
}
|
|
1110
|
+
if (e.motivo) {
|
|
1111
|
+
linhas.push(` motivo ${e.motivo}`);
|
|
1112
|
+
linhas.push(` ${e.detalhe}`);
|
|
1113
|
+
linhas.push(` correcao: ${e.correcao}`);
|
|
1114
|
+
}
|
|
1115
|
+
else
|
|
1116
|
+
linhas.push(` ${e.detalhe}`);
|
|
1117
|
+
return linhas;
|
|
1118
|
+
}
|
|
973
1119
|
/** Texto de `ork memory sync`. */
|
|
974
1120
|
function textoDoSync(r) {
|
|
975
1121
|
const linhas = [];
|