@orkastery/cli 0.5.1 → 0.5.6
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 +158 -0
- package/adapters/claude-code/agents/ork-master.md +2 -0
- package/adapters/claude-code/commands/master.md +8 -0
- package/adapters/claude-code/commands/ork.md +9 -0
- package/adapters/claude-code/mods/autoridade.json +56 -0
- package/adapters/claude-code/mods/ork-clima/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-clima/hooks/contas.ts +283 -0
- package/adapters/claude-code/mods/ork-clima/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-clima/hooks/previsao.ts +393 -0
- package/adapters/claude-code/mods/ork-clima/hooks/register.ts +205 -0
- package/adapters/claude-code/mods/ork-clima/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-clima/types/index.d.ts +80 -0
- package/adapters/claude-code/mods/ork-freio/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-freio/hooks/carga.ts +160 -0
- package/adapters/claude-code/mods/ork-freio/hooks/conferencia.ts +309 -0
- package/adapters/claude-code/mods/ork-freio/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-freio/hooks/lote.ts +270 -0
- package/adapters/claude-code/mods/ork-freio/hooks/register.ts +285 -0
- package/adapters/claude-code/mods/ork-freio/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-freio/types/index.d.ts +127 -0
- package/adapters/claude-code/mods/ork-mesa/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/cartoes.tsx +246 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/decidir.ts +110 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/fila.ts +253 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/obvia.ts +98 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/register.tsx +493 -0
- package/adapters/claude-code/mods/ork-mesa/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-mesa/types/index.d.ts +131 -0
- package/adapters/claude-code/mods/ork-pista/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-pista/hooks/acoes.ts +71 -0
- package/adapters/claude-code/mods/ork-pista/hooks/ci.ts +117 -0
- package/adapters/claude-code/mods/ork-pista/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-pista/hooks/pista.tsx +356 -0
- package/adapters/claude-code/mods/ork-pista/hooks/register.tsx +439 -0
- package/adapters/claude-code/mods/ork-pista/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-pista/types/index.d.ts +85 -0
- package/adapters/claude-code/mods/ork-ponte/.claude-plugin/plugin.json +13 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/faixa.tsx +57 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/guarda.ts +410 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/mostrado.ts +55 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/nucleo.ts +157 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/papel.ts +42 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/register.ts +331 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/vigia.ts +76 -0
- package/adapters/claude-code/mods/ork-ponte/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-ponte/types/index.d.ts +178 -0
- package/adapters/claude-code/mods/ork-portao/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-portao/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-portao/hooks/lease.ts +201 -0
- package/adapters/claude-code/mods/ork-portao/hooks/plano.ts +304 -0
- package/adapters/claude-code/mods/ork-portao/hooks/register.ts +339 -0
- package/adapters/claude-code/mods/ork-portao/hooks/verify.ts +152 -0
- package/adapters/claude-code/mods/ork-portao/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-portao/types/index.d.ts +60 -0
- package/adapters/claude-code/mods/ork-radar/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-radar/hooks/colisao.ts +420 -0
- package/adapters/claude-code/mods/ork-radar/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-radar/hooks/mapa.tsx +322 -0
- package/adapters/claude-code/mods/ork-radar/hooks/register.tsx +470 -0
- package/adapters/claude-code/mods/ork-radar/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-radar/types/index.d.ts +72 -0
- package/adapters/claude-code/mods/ork-sentinela/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/alertas.ts +85 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/despacho.ts +156 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/register.ts +511 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/vigia.ts +186 -0
- package/adapters/claude-code/mods/ork-sentinela/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-sentinela/types/index.d.ts +65 -0
- package/adapters/claude-code/mods/ork-torre/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-torre/hooks/agenda.ts +60 -0
- package/adapters/claude-code/mods/ork-torre/hooks/detalhe.tsx +236 -0
- package/adapters/claude-code/mods/ork-torre/hooks/frases.ts +42 -0
- package/adapters/claude-code/mods/ork-torre/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-torre/hooks/painel.tsx +344 -0
- package/adapters/claude-code/mods/ork-torre/hooks/rede.ts +97 -0
- package/adapters/claude-code/mods/ork-torre/hooks/register.tsx +437 -0
- package/adapters/claude-code/mods/ork-torre/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-torre/types/index.d.ts +108 -0
- package/adapters/codex/skills/ork/SKILL.md +18 -3
- package/adapters/hermes/README.md +1 -0
- package/adapters/hermes/bin/ork-network-status.sh +10 -0
- package/adapters/hermes/hermes.plugin.json +1 -0
- package/adapters/hermes/hitl-ingress/__init__.py +2 -2
- package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +7 -6
- package/adapters/openclaw/README.md +26 -5
- package/adapters/openclaw/dist/hitl-ingress.js +2 -2
- package/adapters/openclaw/dist/index.js +46 -11
- package/adapters/openclaw/openclaw.plugin.json +5 -1
- package/adapters/openclaw/package.json +2 -2
- package/adapters/openclaw/src/hitl-ingress.ts +2 -2
- package/adapters/openclaw/src/index.ts +49 -11
- package/assets/docs-en/markdownlint-cli2.jsonc +22 -0
- package/assets/docs-en/padroes/documentacao-de-produto.md +135 -0
- package/assets/docs-en/padroes/roadmap-de-produto.md +100 -0
- package/assets/docs-en/produto/README.md +14 -0
- package/assets/docs-en/produto/_modelo-feature.md +62 -0
- package/assets/docs-en/roadmap/README.md +14 -0
- package/assets/docs-en/roadmap/_modelo-item.md +75 -0
- package/assets/orkmind_bridge.py +117 -9
- package/dist/adapters/claude-bg.js +116 -18
- package/dist/adapters/codex.js +14 -4
- package/dist/agents-md.js +24 -14
- package/dist/auditrun.js +22 -3
- package/dist/board.js +20 -17
- package/dist/branch-de-estado.js +88 -10
- package/dist/busca-semantica.js +52 -12
- package/dist/canarios-rm057.js +149 -0
- package/dist/canarios.js +2 -0
- package/dist/catalogo.js +33 -0
- package/dist/ci.js +21 -8
- package/dist/claims-conferir.js +342 -0
- package/dist/claims.js +41 -1
- package/dist/company-brain-client.js +36 -1
- package/dist/company-brain-context.js +158 -22
- package/dist/company-brain-journal.js +2 -1
- package/dist/company-brain-migration.js +36 -1
- package/dist/conducao-sentinela.js +388 -0
- package/dist/conducao-texto.js +12 -18
- package/dist/conducao.js +122 -30
- package/dist/conhecimento.js +490 -0
- package/dist/docs.js +70 -17
- package/dist/doctor-pulse-cron.js +349 -0
- package/dist/doctor-rede.js +150 -0
- package/dist/doctor.js +195 -111
- package/dist/entrega-pr.js +42 -29
- package/dist/escopo-escrita.js +10 -1
- package/dist/esgotamentos.js +181 -0
- package/dist/estado-thread.js +23 -1
- package/dist/experiencia-instalacao.js +45 -25
- package/dist/experiencia.js +12 -2
- package/dist/fabrica-batida.js +310 -0
- package/dist/fabrica-estado.js +52 -14
- package/dist/fabrica-publicar.js +9 -1
- package/dist/fechamento.js +20 -66
- package/dist/fila-pesada.js +444 -0
- package/dist/fix.js +3 -2
- package/dist/forja.js +31 -7
- package/dist/gates.js +3 -24
- package/dist/handoff.js +10 -6
- package/dist/hitl-contract.js +59 -4
- package/dist/hitl-curto.js +11 -6
- package/dist/hitl-gates.js +5 -0
- package/dist/hitl-local-receipt.js +2 -0
- package/dist/hitl-lote.js +5 -2
- package/dist/hitl-resumo.js +38 -3
- package/dist/hitl-tempo-parado.js +201 -0
- package/dist/hitl-texto-livre.js +5 -5
- package/dist/hitl.js +29 -33
- package/dist/horario.js +41 -7
- package/dist/hosts.js +104 -11
- package/dist/impedimento.js +299 -0
- package/dist/index.js +961 -542
- package/dist/indice-vetorial.js +87 -10
- package/dist/indice.js +9 -1
- package/dist/init.js +56 -43
- package/dist/instabilidade.js +144 -0
- package/dist/integracoes-locais.js +4 -0
- package/dist/intelligence-graph-cli.js +170 -16
- package/dist/intelligence-graph-contexto.js +254 -0
- package/dist/intelligence-graph-contract.js +2 -1
- package/dist/intelligence-graph-extract-md.js +56 -4
- package/dist/intelligence-graph-extract-ts.js +71 -3
- package/dist/intelligence-graph-parsers.js +6 -4
- package/dist/intelligence-graph-query.js +66 -19
- package/dist/leases.js +664 -87
- package/dist/ledger-stats.js +15 -1
- package/dist/ledger.js +18 -1
- package/dist/licoes.js +1 -0
- package/dist/liveness.js +6 -3
- package/dist/locale-ajuda.js +761 -0
- package/dist/locale-gerados.js +160 -0
- package/dist/locale-master.js +99 -0
- package/dist/locale-onboarding.js +92 -0
- package/dist/locale-phase.js +151 -0
- package/dist/locale-pulse.js +211 -0
- package/dist/locale-retry.js +385 -0
- package/dist/locale-roadmap.js +102 -0
- package/dist/locale-ship.js +494 -0
- package/dist/locale-thread.js +277 -0
- package/dist/locale-verify.js +125 -0
- package/dist/locale.js +714 -0
- package/dist/maestro-runtime.js +4 -1
- package/dist/manifest.js +149 -4
- package/dist/maquina.js +23 -4
- package/dist/master-digest.js +6 -1
- package/dist/master-nota.js +18 -11
- package/dist/master.js +145 -31
- package/dist/mcp-artifacts.js +43 -3
- package/dist/mcp-git.js +42 -8
- package/dist/mcp-grafo-worker.js +5 -3
- package/dist/mcp-grafo.js +55 -6
- package/dist/mcp-server.js +22 -3
- package/dist/mcp-ship.js +3 -1
- package/dist/mcp-verify.js +6 -1
- package/dist/memoria.js +48 -6
- package/dist/modos.js +3 -6
- package/dist/network-roadmap.js +196 -18
- package/dist/noctum-arco.js +911 -0
- package/dist/noctum-capacidade.js +141 -0
- package/dist/noctum-envelope.js +810 -0
- package/dist/noctum-execucao.js +304 -0
- package/dist/noctum-fila.js +280 -0
- package/dist/ocupacao.js +40 -5
- package/dist/onboarding.js +40 -21
- package/dist/orkmind.js +99 -7
- package/dist/orquestracao.js +24 -21
- package/dist/parado-no-condutor.js +219 -38
- package/dist/phase.js +160 -52
- package/dist/policies.js +56 -5
- package/dist/portfolio.js +8 -3
- package/dist/postura-local.js +234 -0
- package/dist/preflight.js +11 -10
- package/dist/procedencia.js +225 -0
- package/dist/projeto-alvo.js +132 -5
- package/dist/prompts.js +40 -1
- package/dist/prova-ativacao.js +75 -6
- package/dist/pulse-delivery.js +47 -4
- package/dist/pulse.js +98 -19
- package/dist/ratelimit.js +24 -6
- package/dist/recall.js +3 -1
- package/dist/redacao-saida.js +63 -0
- package/dist/rede-adesao.js +399 -0
- package/dist/rede-forja.js +289 -0
- package/dist/rede-projetos.js +239 -0
- package/dist/rede-status.js +337 -0
- package/dist/rede.js +980 -0
- package/dist/retry.js +291 -140
- package/dist/roadmap-reservas.js +20 -3
- package/dist/roadmap-status.js +150 -34
- package/dist/runtime-context.js +181 -3
- package/dist/runtime-profiles.js +22 -1
- package/dist/runtimes.js +2 -1
- package/dist/saida-segura.js +58 -0
- package/dist/session-events.js +7 -3
- package/dist/session-watcher-claude.js +120 -24
- package/dist/session-watcher.js +185 -33
- package/dist/sessoes-contas.js +187 -0
- package/dist/sessoes-inventario.js +6 -18
- package/dist/sessoes.js +83 -2
- package/dist/ship-publicar.js +800 -0
- package/dist/ship.js +157 -88
- package/dist/steal.js +77 -0
- package/dist/thread-close.js +3 -2
- package/dist/thread.js +299 -49
- package/dist/util.js +8 -1
- package/dist/verify.js +141 -52
- package/dist/vida-da-thread.js +254 -0
- package/dist/worktree-local.js +165 -0
- package/dist/worktree.js +25 -2
- package/dist/yaml.js +27 -8
- package/eval/fixtures/fx-pedido-colado/caso.json +20 -0
- package/package.json +7 -3
- package/references/definition-of-done.md +1 -1
- package/schemas/code-artifact-graph.v1.schema.json +2 -1
- package/skills/README.md +1 -1
- package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
- package/skills/phases/master-metrics/SKILL.md +8 -3
package/dist/mcp-server.js
CHANGED
|
@@ -57,7 +57,9 @@ 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
59
|
const roadmap_status_1 = require("./roadmap-status");
|
|
60
|
+
const pulse_1 = require("./pulse");
|
|
60
61
|
const network_roadmap_1 = require("./network-roadmap");
|
|
62
|
+
const rede_status_1 = require("./rede-status");
|
|
61
63
|
const hitl_sessions_1 = require("./hitl-sessions");
|
|
62
64
|
const hitl_local_1 = require("./hitl-local");
|
|
63
65
|
const mcp_artifacts_1 = require("./mcp-artifacts");
|
|
@@ -67,6 +69,7 @@ const modos_1 = require("./modos");
|
|
|
67
69
|
const versao_1 = require("./versao");
|
|
68
70
|
const mcp_verify_1 = require("./mcp-verify");
|
|
69
71
|
const conducao_1 = require("./conducao");
|
|
72
|
+
const fila_pesada_1 = require("./fila-pesada");
|
|
70
73
|
const decisao_autonoma_1 = require("./decisao-autonoma");
|
|
71
74
|
const conducao_texto_1 = require("./conducao-texto");
|
|
72
75
|
const mcp_ship_1 = require("./mcp-ship");
|
|
@@ -107,7 +110,9 @@ const nova = zod_1.z.object({ nome: zod_1.z.string().trim().min(1).max(200),
|
|
|
107
110
|
const fase = daThread.extend({ fase: zod_1.z.enum(['GOAL', 'PLAN', 'GO', 'CHECK', 'SHIP', 'MASTER']),
|
|
108
111
|
prompt: zod_1.z.string().min(1).max(65536), runtime: zod_1.z.enum(['claude-bg', 'codex']).optional(),
|
|
109
112
|
model: zod_1.z.string().trim().min(1).max(200).optional(),
|
|
110
|
-
effort: zod_1.z.enum(['low', 'medium', 'high', 'xhigh', 'max']).optional(),
|
|
113
|
+
effort: zod_1.z.enum(['low', 'medium', 'high', 'xhigh', 'max']).optional(),
|
|
114
|
+
// RM-056 (D1): o perfil pedido, validado contra o store pelo mesmo caminho do CLI.
|
|
115
|
+
perfil: zod_1.z.string().trim().regex(/^[a-z0-9][a-z0-9._-]{0,62}$/i).optional(), dryRun: zod_1.z.boolean().optional() }).strict();
|
|
111
116
|
// Limite de tipos do conversor externo: validacao continua no schema Zod strict.
|
|
112
117
|
const converterSchema = zod_to_json_schema_1.zodToJsonSchema;
|
|
113
118
|
const resposta = (valor) => ({ content: [{ type: 'text', text: JSON.stringify(valor) }] });
|
|
@@ -408,6 +413,9 @@ function criarServidorMcp(opcoes) {
|
|
|
408
413
|
// I-36 (T18): a mesma recusa tipada do CLI, com o mesmo texto, sem reescrever nada no host.
|
|
409
414
|
if (e instanceof conducao_1.ErroDeConducao)
|
|
410
415
|
return { ...resposta(e.recusa), isError: true };
|
|
416
|
+
// RM-058 N2 (A6 do CHECK): a fila pesada que nao deu a vez no prazo responde como a conducao.
|
|
417
|
+
if (e instanceof fila_pesada_1.ErroDaFilaPesada)
|
|
418
|
+
return { ...resposta(e.recusa), isError: true };
|
|
411
419
|
throw e;
|
|
412
420
|
}
|
|
413
421
|
});
|
|
@@ -417,7 +425,7 @@ function criarServidorMcp(opcoes) {
|
|
|
417
425
|
annotations: { readOnlyHint: false, destructiveHint: false } }, async (args, extra) => {
|
|
418
426
|
thread(args.threadId);
|
|
419
427
|
livre(args.threadId);
|
|
420
|
-
const r = await (0, mcp_git_1.commitMcp)(raiz, args, extra.signal);
|
|
428
|
+
const r = await (0, mcp_git_1.commitMcp)(raiz, args, extra.signal, opcoes.dispatchId);
|
|
421
429
|
return { ...resposta(r), ...(r.ok ? {} : { isError: true }) };
|
|
422
430
|
});
|
|
423
431
|
const documento = daThread.extend({ tipo: zod_1.z.enum(['goal', 'plan', 'check']) }).strict();
|
|
@@ -489,7 +497,8 @@ function criarServidorMcp(opcoes) {
|
|
|
489
497
|
// RM-052: origem `instalacao`; o servidor fixado nao revela os outros projetos da maquina.
|
|
490
498
|
const consulta = (0, projeto_alvo_1.consultaDoProjeto)(c, { origem: 'instalacao', outrosProjetos: false,
|
|
491
499
|
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
|
-
|
|
500
|
+
// RM-063 (F2): a mesma fila do `ork pulse`; a consulta que falha e dita no fecho, nunca "Nada agora".
|
|
501
|
+
const status = (0, roadmap_status_1.montarStatusDoRoadmap)(raiz, { projeto: c.manifesto.project.name, consulta, fila: (quando) => (0, pulse_1.filaDoDono)(c, { quando }) });
|
|
493
502
|
return resposta({ texto: (0, roadmap_status_1.textoDoStatusDoRoadmap)(status), status });
|
|
494
503
|
});
|
|
495
504
|
/**
|
|
@@ -502,6 +511,16 @@ function criarServidorMcp(opcoes) {
|
|
|
502
511
|
const panorama = (0, network_roadmap_1.montarPanoramaDaRede)({ fixado: raiz });
|
|
503
512
|
return resposta({ texto: (0, network_roadmap_1.textoDoPanoramaDaRede)(panorama), panorama });
|
|
504
513
|
});
|
|
514
|
+
/**
|
|
515
|
+
* RM-054 (fatia 3): as maquinas da pessoa (RM-053, `ork.rede-status/v1`), com a fonte, a batida e as
|
|
516
|
+
* lacunas. O retrato de cada maquina fica so com o projeto servido (D5 da RM-052).
|
|
517
|
+
*/
|
|
518
|
+
registrarTool('ork_network_status', { description: 'Status da Orkastery Network, somente leitura: as maquinas da pessoa (casa privada na forja), com a batida de cada uma, a forja e o login, os runtimes e hosts com versao e o projeto servido em cada uma, mais a fonte e as lacunas. Transporte o texto como vem. Lacuna e "Nao consultado" sao o que nao foi lido: nunca conclua "nenhuma maquina" nem "rede vazia" a partir deles. Nao le roadmap nem threads: para isso, ork_network_roadmap.',
|
|
519
|
+
inputSchema: zod_1.z.object({}).strict(), annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true } }, async () => {
|
|
520
|
+
carregar();
|
|
521
|
+
const status = (0, network_roadmap_1.redeDoProjetoFixado)(raiz);
|
|
522
|
+
return resposta({ texto: (0, rede_status_1.textoDaRede)(status), status: JSON.parse((0, rede_status_1.jsonDaRede)(status)) });
|
|
523
|
+
});
|
|
505
524
|
registrarTool('ork_observe', { description: 'Observa uma vez o progresso canonico e pedidos da thread. Nao cria monitor ou despacho.',
|
|
506
525
|
inputSchema: daThread, annotations: { readOnlyHint: true } }, async ({ threadId }) => {
|
|
507
526
|
return resposta(observar(threadId));
|
package/dist/mcp-ship.js
CHANGED
|
@@ -353,7 +353,9 @@ function executar(f, p) {
|
|
|
353
353
|
sandbox.fechar();
|
|
354
354
|
}
|
|
355
355
|
};
|
|
356
|
+
// RM-058 N2 (B1 do CHECK): a fila pesada espera so o que cabe no prazo do worker, para o finally soltar as leases.
|
|
356
357
|
return (0, ship_1.ship)(c, t.id, { de, para, remoto: 'origin', executorVerify: executor, ttlLeaseMs: PRAZO + RESERVA, retomarLeaseVencido: false,
|
|
358
|
+
esperarFilaPesadaMs: Math.max(0, Math.min(20000, deadline - Date.now() - RESERVA)),
|
|
357
359
|
revalidarGit: (etapa, dir) => { if (!leasesIntactas())
|
|
358
360
|
erro('lease.changed'); guard(etapa, dir); } });
|
|
359
361
|
}
|
|
@@ -433,6 +435,6 @@ if (require.main === module && process.argv[2] === '--worker') {
|
|
|
433
435
|
}
|
|
434
436
|
catch (e) {
|
|
435
437
|
const m = e.message;
|
|
436
|
-
process.stdout.write(JSON.stringify({ ok: false, erro: m.startsWith('mcp.') ? m : 'mcp.ship.operation.failed: conferir estado antes de repetir' }));
|
|
438
|
+
process.stdout.write(JSON.stringify({ ok: false, erro: m.startsWith('mcp.') || m.startsWith('fila-pesada.') ? m : 'mcp.ship.operation.failed: conferir estado antes de repetir' }));
|
|
437
439
|
}
|
|
438
440
|
}
|
package/dist/mcp-verify.js
CHANGED
|
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.ESPERA_DA_FILA_PESADA_MCP_MS = void 0;
|
|
36
37
|
exports.verificarMcp = verificarMcp;
|
|
37
38
|
/** Operação estruturada: argumentos só identificam a thread; execução sempre confinada. */
|
|
38
39
|
const fs = __importStar(require("node:fs"));
|
|
@@ -46,6 +47,8 @@ const mcp_artifacts_1 = require("./mcp-artifacts");
|
|
|
46
47
|
const verify_1 = require("./verify");
|
|
47
48
|
const verify_sandbox_1 = require("./verify-sandbox");
|
|
48
49
|
const worktree_1 = require("./worktree");
|
|
50
|
+
/** RM-058 N2 (D10 da ork-rm058n2): a espera maxima do MCP pela fila de recursos pesados, dentro do prazo total. */
|
|
51
|
+
exports.ESPERA_DA_FILA_PESADA_MCP_MS = 20_000;
|
|
49
52
|
const PRAZO_TOTAL_MS = 180_000;
|
|
50
53
|
const RESERVA_MS = 10_000;
|
|
51
54
|
// I-37 (D4): o teto por comando cabe o `test:ci` medido (196 s); quem limita e o prazo total.
|
|
@@ -154,7 +157,9 @@ function verificarMcp(raizEntrada, threadId, conducao = {}) {
|
|
|
154
157
|
}
|
|
155
158
|
};
|
|
156
159
|
// I-36 (T8): o MESMO lease de execucao do CLI; a sessao filha reentra pela identidade do despacho.
|
|
157
|
-
|
|
160
|
+
// RM-058 N2 (D10): o MCP tem prazo total curto; espera a fila pesada no maximo ESPERA_DA_FILA_PESADA_MCP_MS.
|
|
161
|
+
const r = (0, verify_1.verificar)(carregado, threadId, { executor, operacao: 'mcp.verify',
|
|
162
|
+
conducao: { ...conducao, filaPesada: { esperarMs: Math.min(exports.ESPERA_DA_FILA_PESADA_MCP_MS, conducao.filaPesada?.esperarMs ?? exports.ESPERA_DA_FILA_PESADA_MCP_MS) } } });
|
|
158
163
|
const execucoes = r.claims.flatMap(c => c.execucoes).concat(r.comandos);
|
|
159
164
|
return {
|
|
160
165
|
threadId, ok: r.ok, commit: r.commit, motivos: r.motivos,
|
package/dist/memoria.js
CHANGED
|
@@ -172,7 +172,7 @@ function abrirMemoria(carregado, opcoes = {}) {
|
|
|
172
172
|
const { estado, driver } = (0, orkmind_1.resolverRegime)(carregado.manifesto, opcoes.driver ?? null);
|
|
173
173
|
const ativo = estado.efetivo === 'orkmind' && driver !== null;
|
|
174
174
|
const config = (0, orkmind_1.configDoManifesto)(carregado.manifesto);
|
|
175
|
-
const embeddings = (
|
|
175
|
+
const embeddings = (leitura = {}) => estadoDeEmbeddings(carregado.manifesto, ativo ? driver?.disponivel().saude ?? null : null, carregado.raiz, estado.tenant, config.dsn, leitura);
|
|
176
176
|
if (estado.pedido === 'orkmind')
|
|
177
177
|
estado.embeddings = embeddings();
|
|
178
178
|
const consultarRestrito = (collection, tags) => {
|
|
@@ -250,10 +250,30 @@ function abrirMemoria(carregado, opcoes = {}) {
|
|
|
250
250
|
return null;
|
|
251
251
|
return driver.exportar(colecao).find((e) => e.id === id) ?? null;
|
|
252
252
|
},
|
|
253
|
+
universo(tenant) {
|
|
254
|
+
// A fronteira do manifesto vale antes de qualquer leitura: tenant e so o configurado.
|
|
255
|
+
if (tenant !== estado.tenant)
|
|
256
|
+
throw new Error('memory.tenant.mismatch');
|
|
257
|
+
if (leituraRestrita)
|
|
258
|
+
throw new Error('memory.query.invalid');
|
|
259
|
+
if (!ativo || !driver)
|
|
260
|
+
throw new Error('memory.universo.indisponivel');
|
|
261
|
+
if (typeof driver.universo !== 'function')
|
|
262
|
+
throw new Error('memory.universo.unsupported');
|
|
263
|
+
return driver.universo(tenant);
|
|
264
|
+
},
|
|
253
265
|
};
|
|
254
|
-
//
|
|
266
|
+
// RM-038: a cobertura precisa do universo da busca (uma leitura pela ponte): so quando pedida. Falha
|
|
267
|
+
// de leitura (janela saturada, fronteira violada) vira motivo tipado no estado, nunca cobertura inventada.
|
|
255
268
|
if (opcoes.embeddings === 'detalhado' && ativo && !leituraRestrita) {
|
|
256
|
-
|
|
269
|
+
let leitura;
|
|
270
|
+
try {
|
|
271
|
+
leitura = { universo: (0, indice_vetorial_1.universoDaBusca)(memoria, estado.tenant) };
|
|
272
|
+
}
|
|
273
|
+
catch (erro) {
|
|
274
|
+
leitura = { falhaDoUniverso: (0, indice_vetorial_1.codigoDaFalha)(erro) };
|
|
275
|
+
}
|
|
276
|
+
estado.embeddings = embeddings(leitura);
|
|
257
277
|
}
|
|
258
278
|
return memoria;
|
|
259
279
|
}
|
|
@@ -300,6 +320,7 @@ function estadoDeEmbeddings(manifesto, saude, raiz, tenant, dsn, opcoes = {}) {
|
|
|
300
320
|
const estado = {
|
|
301
321
|
configurado, provider: config.provider, modelo: configurado ? config.model : null, dim: configurado ? config.dim : null,
|
|
302
322
|
variavelDaChave: variavel, chavePresente, fallback, indices: [], entradas: null, cobertura: null,
|
|
323
|
+
universo: null, aviso: null, falhaDoUniverso: null,
|
|
303
324
|
ativo: 'nenhum', sondado: saude !== null, motivo: null, detalhe: '', correcao: '',
|
|
304
325
|
};
|
|
305
326
|
if (!configurado) {
|
|
@@ -331,13 +352,24 @@ function estadoDeEmbeddings(manifesto, saude, raiz, tenant, dsn, opcoes = {}) {
|
|
|
331
352
|
estado.correcao = `exporte ${variavel} com a chave dedicada ao Orkastery; o valor nunca vai ao manifesto`;
|
|
332
353
|
}
|
|
333
354
|
if (opcoes.universo) {
|
|
334
|
-
|
|
355
|
+
// RM-038: o universo vem de universoDaBusca; a fronteira vale de novo antes de contar.
|
|
356
|
+
if (opcoes.universo.tenant !== tenant)
|
|
357
|
+
throw new Error('memory.query.scope-violation');
|
|
358
|
+
(0, indice_vetorial_1.conferirUniverso)(opcoes.universo.entradas, tenant, opcoes.universo.lidoEm);
|
|
359
|
+
const universo = opcoes.universo.entradas;
|
|
335
360
|
estado.indices = indicesDoTenant(raiz, tenant, base, universo);
|
|
336
361
|
estado.entradas = universo.length;
|
|
362
|
+
estado.universo = { porColecao: { ...opcoes.universo.porColecao }, foraDaBusca: opcoes.universo.foraDaBusca,
|
|
363
|
+
...(opcoes.universo.latenciaMs === undefined ? {} : { latenciaMs: opcoes.universo.latenciaMs }) };
|
|
337
364
|
const referencia = estado.ativo === 'fallback' ? { modelo: fallback.modelo, dim: fallback.dim } : { modelo: config.model, dim: config.dim };
|
|
338
365
|
const indice = estado.indices.find(i => i.modelo === referencia.modelo && i.dim === referencia.dim);
|
|
339
|
-
|
|
366
|
+
const coerentes = indice?.coerentes ?? 0;
|
|
367
|
+
estado.cobertura = universo.length === 0 ? 0 : Math.round((coerentes / universo.length) * 10_000) / 10_000;
|
|
368
|
+
// RM-038 (D7): o indice que a busca usa cobre menos do que ela enxerga: dito, nunca escondido.
|
|
369
|
+
estado.aviso = (0, indice_vetorial_1.avisoDeCobertura)(coerentes, universo, estado.ativo);
|
|
340
370
|
}
|
|
371
|
+
else if (opcoes.falhaDoUniverso)
|
|
372
|
+
estado.falhaDoUniverso = opcoes.falhaDoUniverso;
|
|
341
373
|
return estado;
|
|
342
374
|
}
|
|
343
375
|
/**
|
|
@@ -1098,12 +1130,22 @@ function textoDosEmbeddings(e) {
|
|
|
1098
1130
|
`dependencias ${e.fallback.dependencias ? 'ok' : e.sondado ? 'ausentes' : 'nao sondadas'}` : '(nao declarado)'}`);
|
|
1099
1131
|
linhas.push(` caminho ativo ${e.ativo}${e.sondado ? '' : ' (ponte nao sondada)'}`);
|
|
1100
1132
|
}
|
|
1133
|
+
if (e.universo) {
|
|
1134
|
+
const colecoes = Object.entries(e.universo.porColecao).map(([c, n]) => `${c} ${n}`).join(', ');
|
|
1135
|
+
linhas.push(` universo da busca ${e.entradas} entrada(s) do tenant: ${colecoes}`);
|
|
1136
|
+
linhas.push(` fora da busca ${(0, indice_vetorial_1.textoForaDaBusca)(e.universo.foraDaBusca)}`);
|
|
1137
|
+
}
|
|
1138
|
+
else if (e.falhaDoUniverso) {
|
|
1139
|
+
linhas.push(` universo da busca nao lido (${e.falhaDoUniverso}); cobertura nao calculada`);
|
|
1140
|
+
}
|
|
1101
1141
|
for (const i of e.indices) {
|
|
1102
1142
|
linhas.push(` indice ${i.modelo} / ${i.dim} dim: ${i.vetores} vetor(es), ${i.coerentes ?? '?'} coerente(s), ${i.desatualizados ?? '?'} desatualizado(s)`);
|
|
1103
1143
|
}
|
|
1104
1144
|
if (e.entradas !== null) {
|
|
1105
|
-
linhas.push(` cobertura ${e.cobertura === null ? '-' : `${Math.round(e.cobertura * 1000) / 10}%`} de ${e.entradas} entrada(s) do
|
|
1145
|
+
linhas.push(` cobertura ${e.cobertura === null ? '-' : `${Math.round(e.cobertura * 1000) / 10}%`} de ${e.entradas} entrada(s) do universo da busca`);
|
|
1106
1146
|
}
|
|
1147
|
+
if (e.aviso)
|
|
1148
|
+
linhas.push(` aviso ${e.aviso}`);
|
|
1107
1149
|
if (e.sonda) {
|
|
1108
1150
|
linhas.push(` sonda ${e.sonda.ok ? `${e.sonda.alvo} respondeu em ${e.sonda.latenciaMs} ms` : `sem resposta (${e.sonda.motivo ?? 'sem caminho ativo'})`}`);
|
|
1109
1151
|
}
|
package/dist/modos.js
CHANGED
|
@@ -24,6 +24,7 @@ exports.blocoDaFase = blocoDaFase;
|
|
|
24
24
|
exports.fasePausa = fasePausa;
|
|
25
25
|
exports.tabelaDeModos = tabelaDeModos;
|
|
26
26
|
const types_1 = require("./types");
|
|
27
|
+
const locale_1 = require("./locale");
|
|
27
28
|
/** Invariantes que valem em TODOS os modos, inclusive `#Auto`. */
|
|
28
29
|
exports.INVARIANTES = [
|
|
29
30
|
'Claims, verify e policies rodam identicos em todos os modos.',
|
|
@@ -190,10 +191,7 @@ exports.MOTIVO_MODO_APOSENTADO = 'modo.aposentado';
|
|
|
190
191
|
* O texto e DERIVADO de `ORDEM_DOS_MODOS` (ver `substitutosVivos`), nunca escrito a mao.
|
|
191
192
|
*/
|
|
192
193
|
function recusaDeModoAposentado(modo) {
|
|
193
|
-
|
|
194
|
-
return `${exports.MOTIVO_MODO_APOSENTADO}: ${tag} foi aposentado e nao abre trabalho novo. `
|
|
195
|
-
+ `Use ${substitutosVivos().join(' ou ')}. `
|
|
196
|
-
+ `Thread ja gravada em ${tag} continua abrindo normalmente.`;
|
|
194
|
+
return (0, locale_1.msg)().thread.modoAposentado(exports.MOTIVO_MODO_APOSENTADO, exports.MODOS[modo].tag, substitutosVivos());
|
|
197
195
|
}
|
|
198
196
|
/**
|
|
199
197
|
* O portao de ESCRITA de modo, com recusa tipada em vez de silencio.
|
|
@@ -209,8 +207,7 @@ function exigirModoVivo(entrada) {
|
|
|
209
207
|
const cru = (entrada ?? '').trim().replace(/^--?modo[= ]/i, '').replace(/^#/, '').toLowerCase();
|
|
210
208
|
if (modoAposentado(cru))
|
|
211
209
|
throw new Error(recusaDeModoAposentado(cru));
|
|
212
|
-
throw new Error(
|
|
213
|
-
+ `Use um de: ${exports.ORDEM_DOS_MODOS.map((m) => exports.MODOS[m].tag).join(', ')}.`);
|
|
210
|
+
throw new Error((0, locale_1.msg)().thread.modoDesconhecido(entrada ?? '', exports.ORDEM_DOS_MODOS.map((m) => exports.MODOS[m].tag).join(', ')));
|
|
214
211
|
}
|
|
215
212
|
/**
|
|
216
213
|
* Parseia a #TAG de modo de conducao.
|
package/dist/network-roadmap.js
CHANGED
|
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.ErroDoPedidoDeProjeto = exports.SAIDA_DO_PEDIDO = exports.LIMIAR_SEM_BATIDA_MS = exports.CONTRATO_REGISTRO_DE_PROJETOS = exports.CONTRATO_PANORAMA_DA_REDE = void 0;
|
|
37
37
|
exports.projetosConhecidos = projetosConhecidos;
|
|
38
38
|
exports.resolverProjeto = resolverProjeto;
|
|
39
|
+
exports.redeDoProjetoFixado = redeDoProjetoFixado;
|
|
39
40
|
exports.montarPanoramaDaRede = montarPanoramaDaRede;
|
|
40
41
|
exports.textoDoPanoramaDaRede = textoDoPanoramaDaRede;
|
|
41
42
|
/**
|
|
@@ -62,6 +63,11 @@ exports.textoDoPanoramaDaRede = textoDoPanoramaDaRede;
|
|
|
62
63
|
*
|
|
63
64
|
* Na fatia 2, os hosts chegam aqui por dois modos: `host` (gateway sem cwd de projeto, a regra da
|
|
64
65
|
* RM-052 no `--projeto` proprio do `network`) e `fixado` (o servidor MCP, que le so o projeto dele).
|
|
66
|
+
*
|
|
67
|
+
* Na fatia 3, a rede por pessoa da RM-053 (`ork.rede-status/v1`) entra como fonte: as maquinas da
|
|
68
|
+
* casa, com a batida de cada uma, os projetos que elas declaram (um projeto sem clone nem registro
|
|
69
|
+
* nesta maquina passa a ser conhecido pelo nome) e a maquina que esta na rede mas nao publicou na
|
|
70
|
+
* fabrica do projeto, dita como lacuna e nunca como "0 threads".
|
|
65
71
|
*/
|
|
66
72
|
const node_child_process_1 = require("node:child_process");
|
|
67
73
|
const fs = __importStar(require("node:fs"));
|
|
@@ -78,8 +84,12 @@ const manifest_1 = require("./manifest");
|
|
|
78
84
|
const maquina_1 = require("./maquina");
|
|
79
85
|
const modos_1 = require("./modos");
|
|
80
86
|
const projeto_alvo_1 = require("./projeto-alvo");
|
|
87
|
+
const rede_1 = require("./rede");
|
|
88
|
+
const rede_projetos_1 = require("./rede-projetos");
|
|
89
|
+
const rede_status_1 = require("./rede-status");
|
|
81
90
|
const roadmap_reservas_1 = require("./roadmap-reservas");
|
|
82
91
|
const roadmap_status_1 = require("./roadmap-status");
|
|
92
|
+
const vida_da_thread_1 = require("./vida-da-thread");
|
|
83
93
|
const thread_1 = require("./thread");
|
|
84
94
|
const versao_1 = require("./versao");
|
|
85
95
|
const yaml_1 = require("./yaml");
|
|
@@ -118,9 +128,8 @@ const lacuna = (tipo, parte, alvo, detalhe, correcao) => ({ tipo, parte, ...(alv
|
|
|
118
128
|
/**
|
|
119
129
|
* Nome de remoto do git, como `origin`. O valor vem do manifesto versionado (`fabrica.remoto`), e um
|
|
120
130
|
* valor que comece com `-` viraria opcao do `git fetch` (`--upload-pack=...`): so este formato chega
|
|
121
|
-
* ao git (achado 4 do CHECK).
|
|
131
|
+
* ao git (achado 4 do CHECK). O validador e o mesmo da branch de estado (RM-047).
|
|
122
132
|
*/
|
|
123
|
-
const REMOTO = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
124
133
|
/**
|
|
125
134
|
* Nome de branch base, como `main`. Tambem vem do manifesto (`worktree.base_branch`), e o `git log`
|
|
126
135
|
* da base o recebia cru: `--output=<arquivo>` gravava um arquivo (rodada 2 do CHECK).
|
|
@@ -128,7 +137,7 @@ const REMOTO = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
|
128
137
|
const baseValida = (base) => typeof base === 'string' && /^[A-Za-z0-9][A-Za-z0-9._/-]{0,99}$/.test(base) && !base.includes('..');
|
|
129
138
|
function projetoDoClone(c, origem) {
|
|
130
139
|
const remoto = c.manifesto.fabrica.remoto;
|
|
131
|
-
const url =
|
|
140
|
+
const url = (0, branch_de_estado_1.remotoValido)(remoto) ? (0, branch_de_estado_1.git)(c.raiz, ['remote', 'get-url', '--', remoto]) : null;
|
|
132
141
|
return { nome: c.manifesto.project.name, forja: url?.ok ? (0, forja_1.identidadeDaForja)(url.stdout.trim()) : null, raiz: c.raiz,
|
|
133
142
|
base: c.manifesto.worktree.base_branch, remoto, origem };
|
|
134
143
|
}
|
|
@@ -400,12 +409,14 @@ function retratoLegivel(v) {
|
|
|
400
409
|
textoOuNulo(t.roadmap) && textoOuNulo(t.pergunta) && textoOuNulo(t.entregue) && textoOuNulo(t.paradaDesde) &&
|
|
401
410
|
textoOuNulo(t.runtime) && textoOuNulo(t.modelo) && textoOuNulo(t.esforco));
|
|
402
411
|
}
|
|
403
|
-
function fatosDoRetrato(m, entregues, itemDaReserva) {
|
|
412
|
+
function fatosDoRetrato(m, entregues, itemDaReserva, quando) {
|
|
404
413
|
return m.threads.map((t) => ({
|
|
405
414
|
id: t.id, roadmap: t.roadmap ?? itemDaReserva.get(t.id) ?? null, aberta: t.status !== 'fechada', fase: t.fase,
|
|
406
415
|
entregueHoje: () => entregues.has(t.id),
|
|
407
416
|
espera: () => t.esperaVoce ? { thread: t.id, pergunta: pontuar(t.pergunta ?? 'o veredito da pausa'), maquina: m.maquina } : undefined,
|
|
408
417
|
maquina: m.maquina,
|
|
418
|
+
// RM-063 (F2): a sessao que a outra maquina verificou ao publicar; retrato sem o campo, ou velho, nao diz "sigo".
|
|
419
|
+
vida: () => (0, vida_da_thread_1.vidaDoRetrato)(t.sessao, m.publicadoEm, quando),
|
|
409
420
|
}));
|
|
410
421
|
}
|
|
411
422
|
function fatosDaMaquinaLocal(raiz, quando, fuso, maquina, entregues, itemDaReserva) {
|
|
@@ -429,12 +440,12 @@ function maquinasDoPanorama(retratos, local, ctx, lacunas) {
|
|
|
429
440
|
}
|
|
430
441
|
saida.push({ maquina: m.maquina, por: m.por, publicadoEm: m.publicadoEm, idadeMin: Math.floor(idadeMs / 60000), semBatida,
|
|
431
442
|
estaMaquina: m.maquina === ctx.maquina, origem: 'retrato', versaoOrk: m.versaoOrk ?? null,
|
|
432
|
-
ativas: m.threads.filter((t) => !t.entregue), entreguesSemMaster: m.threads.filter((t) => t.entregue).length });
|
|
443
|
+
ativas: m.threads.filter((t) => !t.entregue), entreguesSemMaster: m.threads.filter((t) => t.entregue).length, threadsLidas: true });
|
|
433
444
|
}
|
|
434
445
|
if (local) {
|
|
435
446
|
saida.push({ maquina: local.maquina, por: local.por, publicadoEm: local.publicadoEm, idadeMin: 0, semBatida: false, estaMaquina: true,
|
|
436
447
|
origem: 'estado-local', versaoOrk: local.versaoOrk, ativas: local.threads.filter((t) => !t.entregue),
|
|
437
|
-
entreguesSemMaster: local.threads.filter((t) => t.entregue).length });
|
|
448
|
+
entreguesSemMaster: local.threads.filter((t) => t.entregue).length, threadsLidas: true });
|
|
438
449
|
}
|
|
439
450
|
return saida.sort((a, b) => a.maquina.localeCompare(b.maquina));
|
|
440
451
|
}
|
|
@@ -450,7 +461,7 @@ function montarProjeto(p, d, ctx, fontes, lacunas) {
|
|
|
450
461
|
fatosDaMaquinaLocal(d.local.raiz, ctx.quando, ctx.fuso, d.local.retrato.maquina, entregues, itemDaReserva).forEach(somar);
|
|
451
462
|
for (const m of d.retratos ?? [])
|
|
452
463
|
if (!d.local || m.maquina !== d.local.retrato.maquina)
|
|
453
|
-
fatosDoRetrato(m, entregues, itemDaReserva).forEach(somar);
|
|
464
|
+
fatosDoRetrato(m, entregues, itemDaReserva, ctx.quando).forEach(somar);
|
|
454
465
|
// Entregue hoje e ja fechada: nao esta em retrato nenhum, mas o merge na base e o fato do dia.
|
|
455
466
|
for (const id of entregues)
|
|
456
467
|
somar({ id, roadmap: itemDaReserva.get(id) ?? null, aberta: false, fase: 'SHIP', entregueHoje: () => true,
|
|
@@ -571,8 +582,8 @@ function lerProjetoDoClone(p, geral) {
|
|
|
571
582
|
const carregado = (0, manifest_1.carregarManifesto)(raiz);
|
|
572
583
|
// O fuso do dono deste projeto vale para o dia e os horarios dele (achado 5 do CHECK).
|
|
573
584
|
const ctx = { ...geral, fuso: carregado?.manifesto.owner?.timezone ?? geral.fuso };
|
|
574
|
-
if (!
|
|
575
|
-
lacunas.push(lacuna('projeto.remoto-invalido', 'projeto', raizParaExibir(raiz), `fabrica.remoto do manifesto (${
|
|
585
|
+
if (!(0, branch_de_estado_1.remotoValido)(remoto)) {
|
|
586
|
+
lacunas.push(lacuna('projeto.remoto-invalido', 'projeto', raizParaExibir(raiz), `fabrica.remoto do manifesto (${(0, branch_de_estado_1.remotoRedigido)(remoto)}) não é nome de remoto do git: nada foi lido pelo git`, 'corrija fabrica.remoto no orkastery.yaml (padrão: origin)'));
|
|
576
587
|
return montarProjeto(p, { nome: p.nome, docs: null, reservas: null, retratos: null, local: null, commits: [] }, ctx, fontes, lacunas);
|
|
577
588
|
}
|
|
578
589
|
if (!baseValida(p.base ?? 'main') || (carregado && !baseValida(carregado.manifesto.worktree.base_branch))) {
|
|
@@ -784,6 +795,134 @@ function lerProjeto(p, ctx) {
|
|
|
784
795
|
return vazio(lacuna('projeto.sem-leitura', 'projeto', p.raiz ? raizParaExibir(p.raiz) : p.nome, `a leitura de ${p.nome} parou: ${(0, forja_1.detalheSeguro)(e?.message ?? String(e))}`, 'confira o clone e o estado dele no próprio projeto; os outros projetos seguiram'));
|
|
785
796
|
}
|
|
786
797
|
}
|
|
798
|
+
// ---------------------------------------------------------------------------
|
|
799
|
+
// A rede por pessoa (fatia 3).
|
|
800
|
+
// ---------------------------------------------------------------------------
|
|
801
|
+
/** As lacunas do `ork.rede-status/v1` que sao da casa (forja, casa, retrato), e nao da fabrica de um projeto. */
|
|
802
|
+
const lacunaDaCasa = (tipo) => tipo.startsWith('forja.') || tipo.startsWith('rede.') || tipo === 'retrato.invalido' || tipo === 'maquina.nome-em-uso';
|
|
803
|
+
const CORRECAO_DA_CASA = {
|
|
804
|
+
'forja.ausente': 'instale o gh (ou o glab) nesta máquina',
|
|
805
|
+
'forja.sem-login': 'gh auth login (ou glab auth login) nesta máquina',
|
|
806
|
+
'rede.sem-repositorio': 'ork network entrar numa máquina da pessoa',
|
|
807
|
+
'maquina.nome-em-uso': 'ork network entrar --maquina NOME ou --forcar',
|
|
808
|
+
};
|
|
809
|
+
/** A rede lida para o panorama, ou o motivo de nao ter lido (vai ao `naoConsultado`). */
|
|
810
|
+
function lerRedeDoPanorama(opcoes, ctx, cwd) {
|
|
811
|
+
if (opcoes.rede === false)
|
|
812
|
+
return { status: null, motivo: 'não lida nesta chamada' };
|
|
813
|
+
if (opcoes.rede)
|
|
814
|
+
return { status: opcoes.rede, motivo: null };
|
|
815
|
+
if ((process.env.ORK_REDE_LER ?? '').trim() === '0')
|
|
816
|
+
return { status: null, motivo: 'leitura desligada (ORK_REDE_LER=0)' };
|
|
817
|
+
try {
|
|
818
|
+
// D-G6 da fatia 2: no host, o diretorio do gateway nao e projeto; a rede le so o registro e os retratos.
|
|
819
|
+
return { status: (0, rede_status_1.lerRede)({ semRemoto: ctx.semRemoto, maquina: opcoes.maquina, agora: ctx.quando, diretorio: opcoes.host ? null : cwd }),
|
|
820
|
+
motivo: null };
|
|
821
|
+
}
|
|
822
|
+
catch (e) {
|
|
823
|
+
return { status: null, motivo: `a leitura parou: ${(0, forja_1.detalheSeguro)(e?.message ?? String(e))}` };
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
/** O projeto que um retrato declara casa com o projeto do panorama: pela forja, ou pelo nome quando falta a forja. */
|
|
827
|
+
function declara(q, p, nome) {
|
|
828
|
+
const forja = q.remoto ? (0, forja_1.identidadeDaForja)(q.remoto) : null;
|
|
829
|
+
if (forja && p.forja)
|
|
830
|
+
return (0, forja_1.mesmaForja)(forja, p.forja);
|
|
831
|
+
return (0, rede_projetos_1.emUmaLinha)(q.nome) === nome;
|
|
832
|
+
}
|
|
833
|
+
/** Os projetos que as maquinas da rede declaram com remoto de forja e que esta maquina nao conhece. */
|
|
834
|
+
function projetosDaRede(status, conhecidos) {
|
|
835
|
+
const saida = [];
|
|
836
|
+
for (const m of status.membros) {
|
|
837
|
+
if (m.origem !== 'rede')
|
|
838
|
+
continue;
|
|
839
|
+
for (const q of m.projetos) {
|
|
840
|
+
const nome = (0, rede_projetos_1.emUmaLinha)(q.nome);
|
|
841
|
+
const forja = q.remoto ? (0, forja_1.identidadeDaForja)(q.remoto) : null;
|
|
842
|
+
if (!forja || !projeto_alvo_1.PADRAO_DO_NOME_DE_PROJETO.test(nome))
|
|
843
|
+
continue;
|
|
844
|
+
const p = { nome, forja, raiz: null, base: null, remoto: 'origin', origem: 'rede' };
|
|
845
|
+
if (![...conhecidos, ...saida].some((x) => mesmoProjeto(x, p)))
|
|
846
|
+
saida.push(p);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
return saida;
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
852
|
+
* Suspeitas da revisao de 03/10 (mesma raiz do defeito 3 da revisao): o retrato com o nome desta
|
|
853
|
+
* maquina e de outra instalacao quando o status diz `nomeEmUso`; entao nenhum retrato da rede e "esta
|
|
854
|
+
* maquina". `null` nesse caso; senao, o nome saneado desta maquina.
|
|
855
|
+
*/
|
|
856
|
+
function euNaRede(status, ctx) {
|
|
857
|
+
const eu = (0, rede_1.nomeSeguro)(ctx.maquina);
|
|
858
|
+
return status.estaMaquina.maquina === eu && status.estaMaquina.nomeEmUso ? null : eu;
|
|
859
|
+
}
|
|
860
|
+
/** A rede no panorama; no MCP fixado, os projetos de cada maquina ficam so o servido (D5 da RM-052). */
|
|
861
|
+
function redeNoPanorama(status, ctx, servido) {
|
|
862
|
+
const agora = Date.parse(ctx.quando);
|
|
863
|
+
const fonte = status.fontes.find((f) => f.fonte === 'rede');
|
|
864
|
+
const eu = euNaRede(status, ctx);
|
|
865
|
+
const membros = status.membros.filter((m) => m.origem === 'rede').map((m) => {
|
|
866
|
+
const idadeMs = agora - Date.parse(m.publicadoEm);
|
|
867
|
+
const idadeMin = Number.isFinite(idadeMs) ? Math.floor(Math.max(0, idadeMs) / 60000) : null;
|
|
868
|
+
const projetos = m.projetos.filter((q) => !servido || declara(q, servido.p, servido.nome)).map((q) => (0, rede_projetos_1.emUmaLinha)(q.nome));
|
|
869
|
+
return { maquina: (0, rede_projetos_1.emUmaLinha)(m.maquina), estaMaquina: m.maquina === eu, publicadoEm: m.publicadoEm, idadeMin,
|
|
870
|
+
semBatida: idadeMin === null || idadeMin * 60000 > fabrica_estado_1.LIMIAR_SEM_BATIDA_MS, versaoOrk: m.versaoOrk ? (0, rede_projetos_1.emUmaLinha)(m.versaoOrk) : null,
|
|
871
|
+
projetos: [...new Set(projetos)] };
|
|
872
|
+
});
|
|
873
|
+
return { casa: status.casa ? (0, rede_1.refDaCasa)(status.casa) : null, ponta: fonte?.ponta ?? null, atualizado: fonte ? fonte.atualizado : null,
|
|
874
|
+
lidoEm: status.consultadoEm, membros };
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* A maquina da rede que declara o projeto e nao publicou na fabrica dele entra na lista com as
|
|
878
|
+
* threads NAO lidas e uma lacuna: antes ela sumia, e a lista parecia completa.
|
|
879
|
+
*/
|
|
880
|
+
function completarComARede(x, p, status, ctx) {
|
|
881
|
+
if (x.maquinas === null)
|
|
882
|
+
return;
|
|
883
|
+
const agora = Date.parse(ctx.quando);
|
|
884
|
+
for (const m of status.membros) {
|
|
885
|
+
if (m.origem !== 'rede' || !m.projetos.some((q) => declara(q, p, x.projeto.nome)))
|
|
886
|
+
continue;
|
|
887
|
+
if (x.maquinas.some((y) => (0, rede_1.nomeSeguro)(y.maquina) === m.maquina))
|
|
888
|
+
continue;
|
|
889
|
+
const maquina = (0, rede_projetos_1.emUmaLinha)(m.maquina);
|
|
890
|
+
const idadeMs = agora - Date.parse(m.publicadoEm);
|
|
891
|
+
const legivel = Number.isFinite(idadeMs);
|
|
892
|
+
const idadeMin = legivel ? Math.floor(Math.max(0, idadeMs) / 60000) : 0;
|
|
893
|
+
const semBatida = !legivel || idadeMin * 60000 > fabrica_estado_1.LIMIAR_SEM_BATIDA_MS;
|
|
894
|
+
x.maquinas.push({ maquina, por: 'rede por pessoa', publicadoEm: m.publicadoEm, idadeMin, semBatida, estaMaquina: m.maquina === euNaRede(status, ctx),
|
|
895
|
+
origem: 'rede', versaoOrk: m.versaoOrk ? (0, rede_projetos_1.emUmaLinha)(m.versaoOrk) : null, ativas: [], entreguesSemMaster: 0, threadsLidas: false });
|
|
896
|
+
x.lacunas.push(lacuna('maquina.sem-fabrica', 'fabrica', maquina, `${maquina} está na rede por pessoa e declara ${x.projeto.nome}, mas não tem retrato em ${fabrica_estado_1.BRANCH_DA_FABRICA}: as threads dela neste projeto não foram lidas`, `na ${maquina}, rode ork fabrica entrar no clone de ${x.projeto.nome} (ou atualize o ork dela)`));
|
|
897
|
+
if (semBatida) {
|
|
898
|
+
x.lacunas.push(lacuna('maquina.sem-batida', 'rede', maquina, `${maquina} sem retrato novo na rede ${legivel
|
|
899
|
+
? `há ${(0, horario_1.duracaoCurta)(idadeMin)}` : '(batida ilegível)'}`, `confira a ${maquina}: ela publica na batida do pulse`));
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
x.maquinas.sort((a, b) => a.maquina.localeCompare(b.maquina));
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* Fatia 3: o `ork network status` do servidor MCP fixado. A rede e da pessoa, mas o retrato de cada
|
|
906
|
+
* maquina lista todos os projetos dela: aqui fica so o projeto servido (D5 da RM-052), e o resto vai
|
|
907
|
+
* ao "nao consultado". Com `ORK_REDE_LER=0`, a casa e lida so da ultima copia desta maquina.
|
|
908
|
+
*/
|
|
909
|
+
function redeDoProjetoFixado(raiz, opcoes = {}) {
|
|
910
|
+
const c = (0, manifest_1.carregarManifesto)(raiz);
|
|
911
|
+
if (!c) {
|
|
912
|
+
throw new ErroDoPedidoDeProjeto('projeto.sem-manifesto', `a raiz fixada não tem ${manifest_1.NOME_MANIFESTO}`, [], 'confira a raiz fixada na instalação do servidor MCP (ork mcp install --project <raiz>)');
|
|
913
|
+
}
|
|
914
|
+
const p = projetoDoClone(c, 'instalacao');
|
|
915
|
+
const s = opcoes.status ?? (0, rede_status_1.lerRede)({ semRemoto: opcoes.semRemoto === true || (process.env.ORK_REDE_LER ?? '').trim() === '0', diretorio: c.raiz,
|
|
916
|
+
soRaiz: c.raiz });
|
|
917
|
+
const membros = s.membros.map((m) => ({ ...m, projetos: m.projetos.filter((q) => declara(q, p, p.nome)) }))
|
|
918
|
+
.filter((m) => m.origem === 'rede' || m.projetos.length > 0);
|
|
919
|
+
// Revisao de 03/10 (D5): a lacuna de uma maquina que saiu dos membros (vista so em outro projeto) sai junto.
|
|
920
|
+
const nomes = new Set(membros.map((m) => m.maquina));
|
|
921
|
+
return { ...s, membros, fontes: s.fontes.filter((f) => !f.projeto || f.projeto === p.nome),
|
|
922
|
+
lacunas: s.lacunas.filter((l) => (!l.projeto || l.projeto === p.nome) &&
|
|
923
|
+
(!l.maquina || l.tipo === 'maquina.nome-em-uso' || nomes.has(l.maquina))),
|
|
924
|
+
naoConsultado: [...s.naoConsultado, `outros projetos das máquinas (este servidor MCP mostra só ${p.nome}; os outros vêm do CLI ork network status)`] };
|
|
925
|
+
}
|
|
787
926
|
/**
|
|
788
927
|
* `ork network roadmap`: os projetos pedidos (ou todos os conhecidos), cada um com roadmap, reservas,
|
|
789
928
|
* threads por maquina, fontes e lacunas. Recusa o `--projeto` ambiguo ou desconhecido.
|
|
@@ -793,11 +932,21 @@ function montarPanoramaDaRede(opcoes = {}) {
|
|
|
793
932
|
const cwd = opcoes.cwd ?? process.cwd();
|
|
794
933
|
const ctx = { quando, maquina: (0, maquina_1.nomeDaMaquina)(opcoes.maquina), semRemoto: opcoes.semRemoto === true, executor: opcoes.executor,
|
|
795
934
|
fuso: (0, horario_1.fusoDoDono)().fuso };
|
|
796
|
-
const naoConsultado = [
|
|
797
|
-
'rede por pessoa (RM-053, ork.rede-status/v1): não lida nesta versão; as máquinas vêm da branch ork/fabrica-estado de cada projeto',
|
|
798
|
-
];
|
|
935
|
+
const naoConsultado = [];
|
|
799
936
|
const lacunas = [];
|
|
937
|
+
const lida = lerRedeDoPanorama(opcoes, ctx, cwd);
|
|
938
|
+
const status = lida.status;
|
|
939
|
+
if (!status)
|
|
940
|
+
naoConsultado.push(`rede por pessoa (RM-053, ork.rede-status/v1): ${lida.motivo}; as máquinas vêm da branch ${fabrica_estado_1.BRANCH_DA_FABRICA} de cada projeto`);
|
|
941
|
+
else {
|
|
942
|
+
for (const l of status.lacunas) {
|
|
943
|
+
if (!lacunaDaCasa(l.tipo) || l.projeto)
|
|
944
|
+
continue;
|
|
945
|
+
lacunas.push(lacuna(l.tipo, 'rede', l.maquina, (0, forja_1.detalheSeguro)((0, rede_projetos_1.emUmaLinha)(l.detalhe)), CORRECAO_DA_CASA[l.tipo] ?? 'confira com ork network status'));
|
|
946
|
+
}
|
|
947
|
+
}
|
|
800
948
|
let alvos;
|
|
949
|
+
let servido = null;
|
|
801
950
|
if (opcoes.fixado !== undefined) {
|
|
802
951
|
// D-G5 (fatia 2): o servidor MCP le so o projeto dele, em todas as maquinas; o registro nao e lido.
|
|
803
952
|
const c = (0, manifest_1.carregarManifesto)(opcoes.fixado);
|
|
@@ -805,6 +954,7 @@ function montarPanoramaDaRede(opcoes = {}) {
|
|
|
805
954
|
throw new ErroDoPedidoDeProjeto('projeto.sem-manifesto', `a raiz fixada não tem ${manifest_1.NOME_MANIFESTO}`, [], 'confira a raiz fixada na instalação do servidor MCP (ork mcp install --project <raiz>)');
|
|
806
955
|
}
|
|
807
956
|
alvos = [projetoDoClone(c, 'instalacao')];
|
|
957
|
+
servido = { p: alvos[0], nome: alvos[0].nome };
|
|
808
958
|
naoConsultado.push('outros projetos desta máquina: este servidor MCP lê só o projeto fixado na instalação; ' +
|
|
809
959
|
'os outros vêm do CLI `ork network roadmap`');
|
|
810
960
|
}
|
|
@@ -812,17 +962,19 @@ function montarPanoramaDaRede(opcoes = {}) {
|
|
|
812
962
|
const conhecidos = projetosConhecidos({ cwd, registro: opcoes.registro, host: opcoes.host });
|
|
813
963
|
naoConsultado.push(...conhecidos.naoConsultado);
|
|
814
964
|
lacunas.push(...conhecidos.lacunas);
|
|
965
|
+
// Fatia 3: o projeto que uma maquina da rede declara passa a ser conhecido, lido pela forja.
|
|
966
|
+
const todos = [...conhecidos.projetos, ...(status ? projetosDaRede(status, conhecidos.projetos) : [])];
|
|
815
967
|
if (opcoes.pedido !== undefined) {
|
|
816
968
|
if (opcoes.host)
|
|
817
|
-
exigirPedidoDoHost(opcoes.pedido,
|
|
818
|
-
const alvo = resolverProjeto(opcoes.pedido,
|
|
969
|
+
exigirPedidoDoHost(opcoes.pedido, todos);
|
|
970
|
+
const alvo = resolverProjeto(opcoes.pedido, todos, cwd);
|
|
819
971
|
alvos = [alvo];
|
|
820
|
-
for (const p of
|
|
972
|
+
for (const p of todos)
|
|
821
973
|
if (!mesmoProjeto(p, alvo))
|
|
822
974
|
naoConsultado.push(`projeto ${p.nome}: fora do pedido (--projeto)`);
|
|
823
975
|
}
|
|
824
976
|
else {
|
|
825
|
-
alvos =
|
|
977
|
+
alvos = todos;
|
|
826
978
|
}
|
|
827
979
|
}
|
|
828
980
|
if (alvos.length === 0) {
|
|
@@ -831,9 +983,15 @@ function montarPanoramaDaRede(opcoes = {}) {
|
|
|
831
983
|
: 'nenhum projeto conhecido nesta máquina: sem manifesto no diretório atual e sem registro', opcoes.host ? 'peça o projeto pela forja (--projeto github:dono/repo) ou registre o clone com `ork projetos registrar <caminho>`'
|
|
832
984
|
: 'peça o projeto: --projeto <caminho do clone> ou --projeto github:dono/repo'));
|
|
833
985
|
}
|
|
834
|
-
const projetos = alvos.map((p) =>
|
|
986
|
+
const projetos = alvos.map((p) => {
|
|
987
|
+
const x = lerProjeto(p, ctx);
|
|
988
|
+
if (status)
|
|
989
|
+
completarComARede(x, p, status, ctx);
|
|
990
|
+
return x;
|
|
991
|
+
});
|
|
835
992
|
return { contrato: exports.CONTRATO_PANORAMA_DA_REDE, consultadoEm: quando, maquina: ctx.maquina, pedido: opcoes.pedido ?? null,
|
|
836
|
-
limiarSemBatidaMin: fabrica_estado_1.LIMIAR_SEM_BATIDA_MS / 60000, fuso: projetos[0]?.projeto.fuso ?? ctx.fuso, projetos,
|
|
993
|
+
limiarSemBatidaMin: fabrica_estado_1.LIMIAR_SEM_BATIDA_MS / 60000, fuso: projetos[0]?.projeto.fuso ?? ctx.fuso, projetos,
|
|
994
|
+
rede: status ? redeNoPanorama(status, ctx, servido) : null, naoConsultado, lacunas };
|
|
837
995
|
}
|
|
838
996
|
// ---------------------------------------------------------------------------
|
|
839
997
|
// O texto.
|
|
@@ -859,6 +1017,11 @@ function linhasDasMaquinas(x, fuso) {
|
|
|
859
1017
|
return ['• nenhuma máquina publicou em ork/fabrica-estado'];
|
|
860
1018
|
return x.maquinas.flatMap((m) => {
|
|
861
1019
|
const quem = `${m.maquina}${m.estaMaquina ? ' (esta máquina)' : ''}`;
|
|
1020
|
+
// Fatia 3: a maquina so da rede por pessoa; as threads dela neste projeto nao foram lidas.
|
|
1021
|
+
if (m.origem === 'rede') {
|
|
1022
|
+
return [`• ${quem}: threads não lidas, na rede por pessoa (retrato de ${(0, horario_1.formatarDataHora)(m.publicadoEm, { fuso })}, ` +
|
|
1023
|
+
`há ${(0, horario_1.duracaoCurta)(m.idadeMin)}${m.semBatida ? ', SEM BATIDA' : ''}) e sem retrato em ${fabrica_estado_1.BRANCH_DA_FABRICA}`];
|
|
1024
|
+
}
|
|
862
1025
|
const batida = m.origem === 'estado-local' ? 'estado local lido agora'
|
|
863
1026
|
: `retrato de ${(0, horario_1.formatarDataHora)(m.publicadoEm, { fuso })} (há ${(0, horario_1.duracaoCurta)(m.idadeMin)})${m.semBatida ? ', SEM BATIDA' : ''}`;
|
|
864
1027
|
const cabeca = `• ${quem}: ${m.ativas.length} ativa(s)${m.entreguesSemMaster ? `, ${m.entreguesSemMaster} entregue(s) sem MASTER` : ''}, ${batida}`;
|
|
@@ -875,6 +1038,19 @@ function linhasDasReservas(x, fuso) {
|
|
|
875
1038
|
return ['• nenhuma reserva em ork/roadmap-reservas'];
|
|
876
1039
|
return x.reservas.map((r) => ` ${r.item} · ${r.maquina} · ${r.thread ?? 'sem thread'} · desde ${(0, horario_1.formatarDataHora)(r.desdeEm, { fuso })}`);
|
|
877
1040
|
}
|
|
1041
|
+
/** Fatia 3: a casa da rede por pessoa e as maquinas dela, com a batida e os projetos que declaram. */
|
|
1042
|
+
function linhasDaRede(r, quando, fuso) {
|
|
1043
|
+
const lido = r.lidoEm === quando ? 'lida agora' : `lida ${(0, horario_1.formatarDataHora)(r.lidoEm, { fuso })}`;
|
|
1044
|
+
const casa = !r.casa ? '• casa: nenhuma achada (veja as lacunas)'
|
|
1045
|
+
: r.atualizado === null ? `• casa: ${r.casa}, não lida (veja as lacunas)`
|
|
1046
|
+
: `• casa: ${r.casa} @ ${r.ponta ? r.ponta.slice(0, 7) : 'sem commit'}, ${r.atualizado ? lido : 'última cópia desta máquina, sem leitura nova'}`;
|
|
1047
|
+
if (r.membros.length === 0)
|
|
1048
|
+
return [casa, '• nenhum retrato lido na casa; isso não quer dizer que não há máquinas: veja as lacunas'];
|
|
1049
|
+
return [casa, ...r.membros.map((m) => `• ${m.maquina}${m.estaMaquina ? ' (esta máquina)' : ''}: retrato de ` +
|
|
1050
|
+
`${(0, horario_1.formatarDataHora)(m.publicadoEm, { fuso })}${m.idadeMin === null ? ' (batida ilegível)' : ` (há ${(0, horario_1.duracaoCurta)(m.idadeMin)})`}` +
|
|
1051
|
+
`${m.semBatida ? ', SEM BATIDA' : ''}${m.versaoOrk ? `, ork ${m.versaoOrk}` : ''}, ` +
|
|
1052
|
+
(m.projetos.length ? `projetos: ${m.projetos.join(', ')}` : 'nenhum projeto declarado aqui'))];
|
|
1053
|
+
}
|
|
878
1054
|
const linhaDaLacuna = (l) => `• ${l.tipo}${l.alvo ? ` (${l.alvo})` : ''}: ${l.detalhe}. O que fazer: ${l.correcao}.`;
|
|
879
1055
|
/** O texto de `ork network roadmap`: o consultado e o nao consultado no alto, e cada parte com a fonte e a hora. */
|
|
880
1056
|
function textoDoPanoramaDaRede(p) {
|
|
@@ -886,6 +1062,8 @@ function textoDoPanoramaDaRede(p) {
|
|
|
886
1062
|
p.projetos.length ? `Consultado: ${p.projetos.map((x) => descreverProjeto(x.projeto)).join('; ')}` : 'Consultado: nenhum projeto.',
|
|
887
1063
|
'Não consultado:', ...(p.naoConsultado.length ? p.naoConsultado.map((n) => `• ${n}`) : ['• nada fora do pedido']),
|
|
888
1064
|
];
|
|
1065
|
+
if (p.rede)
|
|
1066
|
+
linhas.push('', 'Rede por pessoa (RM-053)', ...linhasDaRede(p.rede, p.consultadoEm, p.fuso));
|
|
889
1067
|
if (p.lacunas.length)
|
|
890
1068
|
linhas.push('', 'Lacunas da consulta', ...p.lacunas.map(linhaDaLacuna));
|
|
891
1069
|
for (const x of p.projetos) {
|