@orkastery/cli 0.5.0 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/adapters/claude-code/README.md +11 -2
- package/adapters/claude-code/agents/ork-master.md +2 -0
- package/adapters/claude-code/commands/master.md +8 -0
- package/adapters/claude-code/commands/onboarding.md +3 -2
- package/adapters/claude-code/commands/ork.md +9 -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 +6 -5
- package/adapters/openclaw/README.md +35 -4
- package/adapters/openclaw/dist/hitl-ingress.js +2 -2
- package/adapters/openclaw/dist/index.js +47 -12
- 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 +50 -12
- package/assets/orkmind_bridge.py +117 -9
- package/dist/adapters/claude-bg.js +59 -11
- package/dist/adapters/codex.js +4 -3
- package/dist/agents-md.js +1 -0
- package/dist/auditrun.js +6 -2
- package/dist/board.js +16 -14
- package/dist/branch-de-estado.js +66 -10
- package/dist/busca-semantica.js +52 -12
- package/dist/canarios-rm057.js +149 -0
- package/dist/canarios.js +2 -0
- package/dist/ci.js +8 -1
- package/dist/claim-lint.js +3 -1
- package/dist/claims.js +39 -1
- package/dist/company-brain-context.js +158 -22
- package/dist/docs.js +110 -3
- package/dist/doctor-pulse-cron.js +194 -0
- package/dist/doctor-rede.js +149 -0
- package/dist/doctor.js +294 -37
- package/dist/entrega-pr.js +27 -8
- package/dist/esgotamentos.js +181 -0
- package/dist/experiencia.js +12 -2
- package/dist/fabrica-estado.js +35 -9
- package/dist/fabrica-publicar.js +6 -0
- package/dist/fechamento.js +20 -66
- package/dist/fix.js +3 -3
- package/dist/forja.js +31 -7
- package/dist/gates.js +5 -1
- package/dist/hitl-contract.js +59 -4
- package/dist/hitl-curto.js +11 -6
- package/dist/hitl-gates.js +5 -0
- package/dist/hitl-lote.js +5 -2
- package/dist/hitl-resumo.js +33 -1
- package/dist/hitl-tempo-parado.js +201 -0
- package/dist/hitl-texto-livre.js +5 -5
- package/dist/horario.js +27 -0
- package/dist/hosts.js +59 -13
- package/dist/impedimento.js +205 -0
- package/dist/index.js +433 -88
- package/dist/indice-vetorial.js +87 -10
- package/dist/indice.js +3 -1
- package/dist/init.js +28 -5
- package/dist/integracoes-locais.js +4 -0
- package/dist/intelligence-graph-cli.js +267 -30
- package/dist/intelligence-graph-contexto.js +215 -0
- package/dist/intelligence-graph-contract.js +71 -12
- package/dist/intelligence-graph-extract-md.js +41 -23
- package/dist/intelligence-graph-extract-ts.js +181 -11
- package/dist/intelligence-graph-extract.js +186 -9
- package/dist/intelligence-graph-index.js +205 -24
- package/dist/intelligence-graph-parsers.js +6 -4
- package/dist/intelligence-graph-repo.js +10 -0
- package/dist/leases.js +680 -87
- package/dist/ledger-stats.js +15 -1
- package/dist/ledger.js +12 -0
- package/dist/licoes.js +6 -4
- package/dist/liveness.js +2 -1
- package/dist/maestro-runtime.js +7 -1
- package/dist/manifest.js +44 -1
- package/dist/master-nota.js +14 -5
- package/dist/master.js +129 -12
- package/dist/mcp-artifacts.js +36 -3
- package/dist/mcp-git.js +80 -15
- package/dist/mcp-grafo-worker.js +93 -0
- package/dist/mcp-grafo.js +279 -0
- package/dist/mcp-install.js +2 -1
- package/dist/mcp-server.js +23 -1
- package/dist/mcp-ship.js +2 -0
- package/dist/mcp-verify.js +3 -1
- package/dist/memoria.js +48 -6
- package/dist/network-roadmap.js +195 -20
- package/dist/ocupacao.js +17 -5
- package/dist/onboarding.js +9 -4
- package/dist/orkmind.js +90 -5
- package/dist/orquestracao.js +14 -11
- package/dist/parado-no-condutor.js +1089 -0
- package/dist/phase.js +68 -15
- package/dist/policies.js +79 -3
- package/dist/portfolio.js +4 -1
- package/dist/projeto-alvo.js +5 -1
- package/dist/prompts.js +36 -1
- package/dist/prova-ativacao.js +414 -0
- package/dist/pulse-delivery.js +60 -7
- package/dist/pulse.js +99 -3
- package/dist/ratelimit.js +20 -4
- package/dist/recall.js +3 -1
- 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 +336 -0
- package/dist/rede.js +980 -0
- package/dist/retry.js +69 -2
- package/dist/roadmap-status.js +162 -7
- package/dist/runtime-context.js +5 -1
- package/dist/runtime-profiles.js +22 -1
- package/dist/saida-segura.js +58 -0
- package/dist/session-watcher-claude.js +113 -23
- package/dist/sessoes-contas.js +187 -0
- package/dist/sessoes-inventario.js +6 -12
- package/dist/sessoes.js +87 -3
- package/dist/ship.js +110 -20
- package/dist/thread.js +212 -14
- package/dist/util.js +71 -4
- package/dist/verify.js +2 -2
- package/dist/worktree.js +34 -8
- package/eval/casos/onboarding.json +1 -1
- package/eval/fixtures/fx-pedido-colado/caso.json +20 -0
- package/package.json +7 -3
- package/references/definition-of-done.md +1 -1
- package/skills/README.md +1 -1
- package/skills/core/onboarding/SKILL.md +3 -2
- package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
- package/skills/phases/master-metrics/SKILL.md +8 -3
- package/skills/phases/ship-release/SKILL.md +6 -3
package/dist/retry.js
CHANGED
|
@@ -97,6 +97,7 @@ const modos_1 = require("./modos");
|
|
|
97
97
|
const thread_1 = require("./thread");
|
|
98
98
|
const tokens_1 = require("./tokens");
|
|
99
99
|
const util_1 = require("./util");
|
|
100
|
+
const impedimento_1 = require("./impedimento");
|
|
100
101
|
const worktree_1 = require("./worktree");
|
|
101
102
|
const delegation_1 = require("./delegation");
|
|
102
103
|
const gates_2 = require("./gates");
|
|
@@ -163,6 +164,14 @@ exports.POLITICA_DE_RETRY = {
|
|
|
163
164
|
porque: 'repetir o despacho no MESMO runtime daria o mesmo resultado: o que falta nao e tentativa, e um validador que nao seja quem executou. Escolher o outro runtime e decisao de conducao, nao retry',
|
|
164
165
|
correcao: 'despache o CHECK em outro runtime (`ork phase run <thread> CHECK --runtime <outro>`), ou tire a exigencia de validacao cruzada da thread se ela nao se justifica',
|
|
165
166
|
},
|
|
167
|
+
// RM-056 (D1): o perfil pedido nao existe ou e de outro runtime. Quem corrige e quem pediu.
|
|
168
|
+
'runtime.profile-invalid': {
|
|
169
|
+
motivo: 'runtime.profile-invalid',
|
|
170
|
+
acao: 'escalar-humano',
|
|
171
|
+
automatica: false,
|
|
172
|
+
porque: 'o perfil foi pedido pelo dono (--perfil) e nao existe no store ou e de outro runtime: repetir o pedido daria a mesma recusa, e trocar de perfil sozinho desobedeceria o pedido',
|
|
173
|
+
correcao: 'ork accounts list mostra os perfis de cada runtime; repita o despacho com um perfil do runtime certo ou sem --perfil',
|
|
174
|
+
},
|
|
166
175
|
'cost.violation': {
|
|
167
176
|
motivo: 'cost.violation',
|
|
168
177
|
acao: 'sem-retry',
|
|
@@ -251,6 +260,22 @@ exports.POLITICA_DE_RETRY = {
|
|
|
251
260
|
porque: 'o modelo pedido nao existe ou a conta nao tem acesso a ele: o MESMO prompt, com o mesmo sha256, segue no proximo perfil do mesmo runtime com o mesmo modelo e depois no fallback do bloco, nunca de novo no par que ja recusou',
|
|
252
261
|
correcao: 'ork retry run <thread> troca o destino; sem destino, ork setup <modo> --bloco N --model <modelo acessivel> ou --fallback runtime:modelo',
|
|
253
262
|
},
|
|
263
|
+
// RM-055: so o dono destrava, no terminal. O retry nao repete o despacho as cegas: confere antes se o
|
|
264
|
+
// impedimento saiu (a confianca do diretorio tem prova local) e, sem prova, deixa o runtime decidir.
|
|
265
|
+
'runtime.workspace-untrusted': {
|
|
266
|
+
motivo: 'runtime.workspace-untrusted',
|
|
267
|
+
acao: 'reexecutar',
|
|
268
|
+
automatica: true,
|
|
269
|
+
porque: 'o runtime recusou o diretorio da worktree antes de rodar qualquer coisa: depois que o dono aceita a confianca no terminal, o MESMO prompt, com o mesmo sha256, volta a mesma fase; enquanto a confianca nao aparece no .claude.json da conta, nada e despachado',
|
|
270
|
+
correcao: 'o dono roda o comando da pausa (cd <worktree> && claude, aceita a confianca); ork retry run <thread> re-despacha o prompt ja gravado',
|
|
271
|
+
},
|
|
272
|
+
'runtime.consent-pending': {
|
|
273
|
+
motivo: 'runtime.consent-pending',
|
|
274
|
+
acao: 'reexecutar',
|
|
275
|
+
automatica: true,
|
|
276
|
+
porque: 'o runtime espera o dono aceitar termos novos antes de rodar qualquer coisa: depois do aceite, o MESMO prompt, com o mesmo sha256, volta a mesma fase; se o runtime recusar de novo, a mesma pausa do dono volta',
|
|
277
|
+
correcao: 'o dono abre o CLI do runtime e aceita os termos; ork retry run <thread> re-despacha o prompt ja gravado',
|
|
278
|
+
},
|
|
254
279
|
'tree.blocked': {
|
|
255
280
|
motivo: 'tree.blocked',
|
|
256
281
|
acao: 'sincronizar-worktree',
|
|
@@ -258,6 +283,13 @@ exports.POLITICA_DE_RETRY = {
|
|
|
258
283
|
porque: 'a arvore de destino andou por baixo da thread: rebasar a worktree contra a base e o passo que desbloqueia, e reexecutar antes disso so repete o conflito',
|
|
259
284
|
correcao: 'ork worktree sync <thread> e so entao repetir o passo',
|
|
260
285
|
},
|
|
286
|
+
'lease.resume-unavailable': {
|
|
287
|
+
motivo: 'lease.resume-unavailable',
|
|
288
|
+
acao: 'escalar-humano',
|
|
289
|
+
automatica: false,
|
|
290
|
+
porque: 'sem inode seguro, a remocao forcada exige avaliar a posse antes de retomar',
|
|
291
|
+
correcao: 'ork lease release <nome> --forcar; depois repita a aquisicao',
|
|
292
|
+
},
|
|
261
293
|
'lease.busy': {
|
|
262
294
|
motivo: 'lease.busy',
|
|
263
295
|
acao: 'reexecutar',
|
|
@@ -677,8 +709,19 @@ tomadaPrevia) {
|
|
|
677
709
|
if (!resultado.ok || !resultado.sessionId) {
|
|
678
710
|
(0, ledger_1.registrar)((0, thread_1.dirThread)(raiz, thread.id), thread.id, 'phase_dispatch_failed', { fase, runtime, slug, cwd,
|
|
679
711
|
promptPath: absoluto, promptSha256: sha, controlador: resultado.controlador, model, effort,
|
|
680
|
-
erro: resultado.erro ?? 'runtime.unavailable: sem sessionId',
|
|
712
|
+
erro: resultado.erro ?? 'runtime.unavailable: sem sessionId', stderr: (resultado.stderr ?? '').slice(0, 500),
|
|
713
|
+
origem: 'retry', ...(perfil ? { perfil } : {}) });
|
|
681
714
|
const sinal = resultado.rateLimit ?? null;
|
|
715
|
+
// RM-055: o redespacho recusado pelo que so o dono resolve reabre a pausa dele, com o mesmo comando.
|
|
716
|
+
const impedimento = !resultado.falhaDeConta && !sinal
|
|
717
|
+
? (0, impedimento_1.registrarImpedimentoDoDono)((0, thread_1.dirThread)(raiz, thread.id), thread, { fase, slug, runtime, cwd, origem: 'retry',
|
|
718
|
+
promptPath: absoluto, promptSha256: sha, perfil, saida: `${resultado.stderr ?? ''}\n${resultado.erro ?? ''}` })
|
|
719
|
+
: null;
|
|
720
|
+
if (impedimento) {
|
|
721
|
+
return { ok: false, slug, sessionId: null, verificada: false, motivo: impedimento.motivo,
|
|
722
|
+
detalhe: `${impedimento.motivo}: ${impedimento.trecho}`, sinal: null, falhaDeConta: null, perfil: perfil ?? null,
|
|
723
|
+
comando: resultado.comando, controlador: resultado.controlador, dryRun: false };
|
|
724
|
+
}
|
|
682
725
|
// I-33 (D5): a conta recusou o redespacho; o perfil sai do rodizio aqui, onde a evidencia esta.
|
|
683
726
|
if (resultado.falhaDeConta) {
|
|
684
727
|
try {
|
|
@@ -1121,6 +1164,17 @@ function jaMarcado(raiz, perfil, falha, agoraMs = Date.now()) {
|
|
|
1121
1164
|
? atual.estado === 'esgotado' && atual.esgotadoAte !== null && Date.parse(atual.esgotadoAte) > agoraMs
|
|
1122
1165
|
: atual.estado === 'sem-auth';
|
|
1123
1166
|
}
|
|
1167
|
+
/**
|
|
1168
|
+
* RM-037 (fatia 5, aviso da rodada 1 do CHECK): a cota que o observador viu ao vivo (`runtime_quota_detected`, da mesma
|
|
1169
|
+
* sessao e do mesmo despacho, com o mesmo prazo dito) ja tirou o perfil do rodizio ate a hora da mensagem. Marcar de
|
|
1170
|
+
* novo aqui, depois dessa hora, pela janela padrao (A10), tiraria do rodizio uma conta que ja voltou.
|
|
1171
|
+
*/
|
|
1172
|
+
function cotaJaVistaAoVivo(eventos, gate, falha) {
|
|
1173
|
+
if (falha.motivo !== 'runtime.quota-exhausted' || typeof gate?.sessionId !== 'string')
|
|
1174
|
+
return false;
|
|
1175
|
+
return eventos.some((e) => e.tipo === ledger_1.TIPOS_DE_EVENTO.cotaVistaNaTranscricao && e.sessionId === gate.sessionId &&
|
|
1176
|
+
e.despachoEm === gate.despachoEm && (e.resetEm ?? null) === falha.resetEm);
|
|
1177
|
+
}
|
|
1124
1178
|
function sinalDoGate(gate, motivo) {
|
|
1125
1179
|
const f = gate?.falhaDeConta;
|
|
1126
1180
|
return { motivo: motivo === 'runtime.auth-missing' ? 'runtime.auth-missing' : 'runtime.quota-exhausted',
|
|
@@ -1282,6 +1336,18 @@ function executarRetry(carregado, threadId, opcoes = {}) {
|
|
|
1282
1336
|
registrarTentativa(false, detalhe);
|
|
1283
1337
|
return { ...vazio, detalhe };
|
|
1284
1338
|
}
|
|
1339
|
+
// RM-055: o impedimento do dono com prova local (confianca do diretorio) so volta ao runtime depois de
|
|
1340
|
+
// resolvido. Sem a prova, nada e despachado e a tentativa nao conta: a pausa do dono continua aberta.
|
|
1341
|
+
if (plano.motivo && (0, impedimento_1.ehImpedimentoDoDono)(plano.motivo) && typeof falho.cwd === 'string') {
|
|
1342
|
+
const perfilFalho = perfilDoEvento(falho);
|
|
1343
|
+
const resolvido = (0, impedimento_1.impedimentoResolvido)(plano.motivo, { cwd: falho.cwd,
|
|
1344
|
+
runtime: typeof falho.runtime === 'string' ? falho.runtime : '', configDir: perfilFalho?.configDir ?? null });
|
|
1345
|
+
if (resolvido === false) {
|
|
1346
|
+
const detalhe = `${plano.motivo}: o runtime ainda nao confia em ${falho.cwd}; nada foi despachado. ` +
|
|
1347
|
+
`Rode \`cd ${falho.cwd} && claude\`, aceite a confianca e repita \`ork retry run ${threadId}\``;
|
|
1348
|
+
return { ...vazio, detalhe };
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1285
1351
|
const redespacho = redespachar(carregado, thread, plano.fase, falho.promptPath, falho.promptSha256, {
|
|
1286
1352
|
effort: plano.effort,
|
|
1287
1353
|
runtime: typeof falho.runtime === 'string' ? falho.runtime : null,
|
|
@@ -1349,7 +1415,8 @@ function rotacionarConta(carregado, thread, plano, opcoes, vazio, registrarTenta
|
|
|
1349
1415
|
const politica = (0, runtime_profiles_1.politicaDeRotacao)(manifesto);
|
|
1350
1416
|
const falha = sinalDoGate(gate, motivo);
|
|
1351
1417
|
// I-33 (D12): o observador ja marca o perfil no caminho terminal; remarcar empurraria o prazo a cada retry.
|
|
1352
|
-
|
|
1418
|
+
// RM-037 (fatia 5, aviso da rodada 1 do CHECK): a cota que ele viu ao vivo ja marcou ate a hora da mensagem.
|
|
1419
|
+
if (!opcoes.dryRun && perfil && !jaMarcado(raiz, perfil, falha) && !cotaJaVistaAoVivo(eventos, gate, falha)) {
|
|
1353
1420
|
try {
|
|
1354
1421
|
(0, ratelimit_1.marcarContaDaFalha)(carregado, perfil, falha);
|
|
1355
1422
|
}
|
package/dist/roadmap-status.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.TETO_DO_FECHO = exports.GRUPO_DO_CICLO = exports.GRUPOS_DO_ROADMAP = exports.CONTRATO_STATUS_DO_ROADMAP = void 0;
|
|
4
4
|
exports.esperaDoDono = esperaDoDono;
|
|
5
5
|
exports.fatosLocais = fatosLocais;
|
|
6
|
+
exports.batidaDaFabrica = batidaDaFabrica;
|
|
6
7
|
exports.montarStatusDoRoadmap = montarStatusDoRoadmap;
|
|
7
8
|
exports.montarStatusDeFatos = montarStatusDeFatos;
|
|
8
9
|
exports.textoDoStatusDoRoadmap = textoDoStatusDoRoadmap;
|
|
@@ -34,6 +35,13 @@ const hitl_gates_1 = require("./hitl-gates");
|
|
|
34
35
|
const hitl_classificacao_1 = require("./hitl-classificacao");
|
|
35
36
|
const horario_1 = require("./horario");
|
|
36
37
|
const projeto_alvo_1 = require("./projeto-alvo");
|
|
38
|
+
const fabrica_estado_1 = require("./fabrica-estado");
|
|
39
|
+
const hitl_resumo_1 = require("./hitl-resumo");
|
|
40
|
+
const hitl_tempo_parado_1 = require("./hitl-tempo-parado");
|
|
41
|
+
const manifest_1 = require("./manifest");
|
|
42
|
+
const maquina_1 = require("./maquina");
|
|
43
|
+
const parado_no_condutor_1 = require("./parado-no-condutor");
|
|
44
|
+
const util_1 = require("./util");
|
|
37
45
|
exports.CONTRATO_STATUS_DO_ROADMAP = 'ork.roadmap-status/v1';
|
|
38
46
|
/** Os grupos, na ordem e com os icones que o dono aprovou. */
|
|
39
47
|
exports.GRUPOS_DO_ROADMAP = Object.freeze([
|
|
@@ -79,7 +87,9 @@ function esperaDoDono(raiz, t, quando) {
|
|
|
79
87
|
const escalacoes = [...new Set(eventos.filter(e => e.tipo === 'gate_blocked' && e.fase === t.faseAtual &&
|
|
80
88
|
hitl_gates_1.MOTIVOS_DE_ESCALACAO_HUMANA.includes(String(e.motivo)) && (0, hitl_classificacao_1.quemDecide)(String(e.motivo)) === 'dono')
|
|
81
89
|
.map(e => String(e.motivo)))];
|
|
82
|
-
|
|
90
|
+
// RM-037 (fatia 4): o fim de turno sem pergunta e do condutor, nunca "O que precisa de voce".
|
|
91
|
+
const doCondutor = (0, parado_no_condutor_1.esperaDoCondutor)(t, eventos, quando) !== null;
|
|
92
|
+
for (const motivo of [...(doCondutor ? [] : ['human.pending']), ...escalacoes]) {
|
|
83
93
|
try {
|
|
84
94
|
const preparado = (0, hitl_gates_1.prepararPedidoGate)(raiz, t.id, motivo, quando);
|
|
85
95
|
const pedido = 'aberto' in preparado ? preparado.aberto : preparado.novo;
|
|
@@ -114,6 +124,30 @@ function esperaDoDono(raiz, t, quando) {
|
|
|
114
124
|
function fatosLocais(raiz, quando, fuso) {
|
|
115
125
|
const hoje = (0, horario_1.dataLocal)(quando, fuso);
|
|
116
126
|
const fatos = [];
|
|
127
|
+
// RM-037 (fatia 4): o estado das entregas sai uma vez, sob demanda, do git local e do retrato de PRs do pulse.
|
|
128
|
+
let entregas;
|
|
129
|
+
const lerEntregas = () => {
|
|
130
|
+
if (entregas !== undefined)
|
|
131
|
+
return entregas;
|
|
132
|
+
try {
|
|
133
|
+
const carregado = (0, manifest_1.exigirManifesto)(raiz);
|
|
134
|
+
// RM-037 (fatia 5, A5): a forja sem leitura que o pulse ja disse vale sobre o retrato, que e de antes dela (a
|
|
135
|
+
// leitura boa apaga a marca), e a linha nao diz "PR nao lido".
|
|
136
|
+
const aviso = (0, parado_no_condutor_1.forjaSemLeituraDoRemoto)(raiz, carregado.manifesto.fabrica.remoto);
|
|
137
|
+
const retrato = aviso ? null : (0, parado_no_condutor_1.lerRetratoDePrs)(raiz);
|
|
138
|
+
entregas = (0, parado_no_condutor_1.entregasDoProjeto)(carregado, { quando, lerPrs: () => (retrato ? { ok: true, retrato }
|
|
139
|
+
: aviso ? { ok: false, lidoEm: aviso.ditoEm, erro: aviso.motivo, semLeitura: { remoto: aviso.remoto, host: aviso.host } } : null) });
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
entregas = null;
|
|
143
|
+
}
|
|
144
|
+
return entregas;
|
|
145
|
+
};
|
|
146
|
+
// A leitura que falhou e dita, e nao vira o "sigo" de sempre.
|
|
147
|
+
const entrega = (id) => {
|
|
148
|
+
const lidas = lerEntregas();
|
|
149
|
+
return lidas ? entregaNoStatus(lidas.estados.find(e => e.thread === id)) : { estado: 'estado da entrega não lido' };
|
|
150
|
+
};
|
|
117
151
|
for (const id of (0, thread_1.listarIds)(raiz)) {
|
|
118
152
|
let t;
|
|
119
153
|
try {
|
|
@@ -126,14 +160,85 @@ function fatosLocais(raiz, quando, fuso) {
|
|
|
126
160
|
id: t.id, roadmap: t.roadmap ?? null, aberta: t.status !== 'fechada', fase: t.faseAtual,
|
|
127
161
|
entregueHoje: () => (0, ledger_1.lerLedger)((0, thread_1.dirThread)(raiz, t.id)).some(e => e.tipo === 'ship_done' && (0, horario_1.dataLocal)(e.ts, fuso) === hoje),
|
|
128
162
|
espera: () => esperaDoDono(raiz, t, quando),
|
|
163
|
+
entrega: () => (t.status === 'fechada' ? undefined : entrega(t.id)),
|
|
129
164
|
});
|
|
130
165
|
}
|
|
131
166
|
return fatos;
|
|
132
167
|
}
|
|
168
|
+
/** O estado da entrega como o status o diz: so quando ha o que dizer. */
|
|
169
|
+
function entregaNoStatus(e) {
|
|
170
|
+
if (!e || (!e.resumo && !e.parado))
|
|
171
|
+
return undefined;
|
|
172
|
+
return { estado: e.resumo, ...(e.conduzidaAgora ? { conduzida: true } : {}),
|
|
173
|
+
...(e.parado ? { parado: { desdeEm: e.parado.desdeEm, proximoPasso: e.parado.proximoPasso,
|
|
174
|
+
...(e.parado.prLidoEm ? { prLidoEm: e.parado.prLidoEm } : {}) } } : {}),
|
|
175
|
+
...(e.prLidoEm ? { prLidoEm: e.prLidoEm } : {}) };
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* RM-037 (fatia 4, D8): as outras maquinas da fabrica sem batida alem do limiar da rede, lidas da copia
|
|
179
|
+
* local de `ork/fabrica-estado`, sem fetch. Sem fabrica compartilhada, nada; sem copia local, "nao lido".
|
|
180
|
+
*/
|
|
181
|
+
function batidaDaFabrica(carregado, quando) {
|
|
182
|
+
if (!(0, maquina_1.fabricaCompartilhada)(carregado.manifesto))
|
|
183
|
+
return undefined;
|
|
184
|
+
// Sem rede escondida: num clone parcial, o `git show` dos retratos buscaria o objeto no remoto. O git anterior
|
|
185
|
+
// a 2.45 ignora o GIT_NO_LAZY_FETCH, entao o clone parcial nem e lido aqui (N4 da seguranca, rodada 2).
|
|
186
|
+
// N4 da seguranca (rodada 3): o git novo registra o clone parcial no `remote.<r>.promisor`, o velho no `extensions`.
|
|
187
|
+
if ((0, util_1.exec)('git', ['config', '--get-regexp', '^(extensions\\.partialclone|remote\\..+\\.promisor)$'], carregado.raiz).stdout.trim()) {
|
|
188
|
+
return { lido: false, motivo: `clone parcial: a cópia local de ${fabrica_estado_1.BRANCH_DA_FABRICA} não é lida sem rede`, semBatida: [] };
|
|
189
|
+
}
|
|
190
|
+
const antes = process.env.GIT_NO_LAZY_FETCH;
|
|
191
|
+
process.env.GIT_NO_LAZY_FETCH = '1';
|
|
192
|
+
let painel;
|
|
193
|
+
try {
|
|
194
|
+
painel = (0, fabrica_estado_1.lerFabrica)(carregado.raiz, { remoto: carregado.manifesto.fabrica.remoto, semRemoto: true });
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
return { lido: false, motivo: `a cópia local de ${fabrica_estado_1.BRANCH_DA_FABRICA} está ilegível`, semBatida: [] };
|
|
198
|
+
}
|
|
199
|
+
finally {
|
|
200
|
+
if (antes === undefined)
|
|
201
|
+
delete process.env.GIT_NO_LAZY_FETCH;
|
|
202
|
+
else
|
|
203
|
+
process.env.GIT_NO_LAZY_FETCH = antes;
|
|
204
|
+
}
|
|
205
|
+
if (!painel.ponta)
|
|
206
|
+
return { lido: false, semBatida: [] };
|
|
207
|
+
const eu = (0, maquina_1.nomeDaMaquina)(), agora = Date.parse(quando);
|
|
208
|
+
// A copia so diz quem esta sem batida quando esta maquina publicou nela ha pouco: e o fetch daqui que a atualiza.
|
|
209
|
+
const { publicadoEm: minha } = painel.maquinas.find(m => m.maquina === eu) ?? { publicadoEm: null };
|
|
210
|
+
if (!minha || !(agora - Date.parse(minha) <= fabrica_estado_1.LIMIAR_SEM_BATIDA_MS)) {
|
|
211
|
+
return { lido: true, copiaVelha: { ultimoDestaMaquina: minha }, semBatida: [] };
|
|
212
|
+
}
|
|
213
|
+
const semBatida = painel.maquinas.filter(m => m.maquina !== eu).map(m => {
|
|
214
|
+
const { publicadoEm: publicado } = m;
|
|
215
|
+
return { maquina: m.maquina, publicadoEm: publicado, idadeMs: agora - Date.parse(publicado) };
|
|
216
|
+
}).filter(m => Number.isFinite(m.idadeMs) && m.idadeMs > fabrica_estado_1.LIMIAR_SEM_BATIDA_MS)
|
|
217
|
+
.map(m => ({ maquina: m.maquina, publicadoEm: m.publicadoEm, idadeMin: Math.floor(m.idadeMs / 60000) }));
|
|
218
|
+
return { lido: true, semBatida };
|
|
219
|
+
}
|
|
133
220
|
/** Monta o relatorio. Leitura pura: nada e escrito, nenhum pedido e aberto. */
|
|
134
221
|
function montarStatusDoRoadmap(raiz, opcoes = {}) {
|
|
135
222
|
const quando = opcoes.quando ?? new Date().toISOString();
|
|
136
|
-
|
|
223
|
+
let fabrica;
|
|
224
|
+
try {
|
|
225
|
+
fabrica = batidaDaFabrica((0, manifest_1.exigirManifesto)(raiz), quando);
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
fabrica = undefined;
|
|
229
|
+
}
|
|
230
|
+
// RM-057 (fatia 3): leitura pura do ledger; sem ela o relatorio sai como antes.
|
|
231
|
+
let hitlDeConducao;
|
|
232
|
+
try {
|
|
233
|
+
const h = (0, hitl_tempo_parado_1.lerHitlDeConducaoAgora)(raiz, quando);
|
|
234
|
+
if (h.abertas.length || h.seteDias.pedidos || h.seteDias.respondidos)
|
|
235
|
+
hitlDeConducao = h;
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
hitlDeConducao = undefined;
|
|
239
|
+
}
|
|
240
|
+
return montarStatusDeFatos((0, docs_1.carregarDocs)(raiz).docs, fatosLocais(raiz, quando), { quando, projeto: opcoes.projeto, consulta: opcoes.consulta,
|
|
241
|
+
...(fabrica ? { fabrica } : {}), ...(hitlDeConducao ? { hitlDeConducao } : {}) });
|
|
137
242
|
}
|
|
138
243
|
/** O relatorio a partir das paginas do roadmap e dos fatos das threads, de onde quer que venham. */
|
|
139
244
|
function montarStatusDeFatos(docs, fatos, opcoes) {
|
|
@@ -157,7 +262,10 @@ function montarStatusDeFatos(docs, fatos, opcoes) {
|
|
|
157
262
|
if (hitl)
|
|
158
263
|
break;
|
|
159
264
|
}
|
|
160
|
-
|
|
265
|
+
// RM-037 (fatia 4): a thread que diz o estado real vem primeiro: a parada no condutor, depois a entrega em curso.
|
|
266
|
+
const entregas = new Map(abertas.map(f => [f.id, f.entrega?.()]));
|
|
267
|
+
const conduz = abertas.find(f => entregas.get(f.id)?.parado) ?? abertas.find(f => entregas.get(f.id)?.estado) ?? abertas[0];
|
|
268
|
+
const entrega = conduz ? entregas.get(conduz.id) : undefined;
|
|
161
269
|
const emAberto = exports.GRUPO_DO_CICLO[ciclo] === 'disponiveis' ? [
|
|
162
270
|
...(texto(estado.exposicao) && texto(estado.exposicao) !== 'Geral' ? [`exposição ${texto(estado.exposicao)}`] : []),
|
|
163
271
|
...(texto(estado.habilitacao) && texto(estado.habilitacao) !== 'Concluída' ? [`habilitação ${texto(estado.habilitacao)}`] : []),
|
|
@@ -166,7 +274,8 @@ function montarStatusDeFatos(docs, fatos, opcoes) {
|
|
|
166
274
|
id: d.id, titulo: nomeCurto(texto(d.dados.titulo) || d.id), ciclo,
|
|
167
275
|
grupo: entregueHoje ? 'hoje' : exports.GRUPO_DO_CICLO[ciclo] ?? 'proposto',
|
|
168
276
|
emAberto, threads: ligadas,
|
|
169
|
-
...(conduz ? { conduzindo: { thread: conduz.id, fase: conduz.fase, ...(conduz.maquina ? { maquina: conduz.maquina } : {})
|
|
277
|
+
...(conduz ? { conduzindo: { thread: conduz.id, fase: conduz.fase, ...(conduz.maquina ? { maquina: conduz.maquina } : {}),
|
|
278
|
+
...(entrega ? { entrega } : {}) } } : {}),
|
|
170
279
|
...(hitl ? { hitl } : {}),
|
|
171
280
|
});
|
|
172
281
|
}
|
|
@@ -177,17 +286,58 @@ function montarStatusDeFatos(docs, fatos, opcoes) {
|
|
|
177
286
|
precisaDeVoce: itens.filter(i => i.hitl).map(i => ({ item: i.id, espera: i.hitl })),
|
|
178
287
|
emSeguida: itens.filter(i => i.conduzindo && !i.hitl).map(i => ({ item: i.id, ...i.conduzindo })),
|
|
179
288
|
...(opcoes.consulta ? { consulta: opcoes.consulta } : {}),
|
|
289
|
+
...(opcoes.fabrica ? { fabrica: opcoes.fabrica } : {}),
|
|
290
|
+
...(opcoes.hitlDeConducao ? { hitlDeConducao: opcoes.hitlDeConducao } : {}),
|
|
180
291
|
};
|
|
181
292
|
}
|
|
182
293
|
/** "orkastery" vira "Orkastery": o titulo diz o nome do projeto como gente escreve. */
|
|
183
294
|
const capitalizar = (s) => s ? s[0].toUpperCase() + s.slice(1) : s;
|
|
184
295
|
/** A maquina entre parenteses, so quando o fato a traz (visao da rede, D7 da RM-054). */
|
|
185
296
|
const naMaquina = (maquina) => maquina ? ` (${maquina})` : '';
|
|
297
|
+
/**
|
|
298
|
+
* RM-037 (fatia 4): a linha do que vem a seguir diz o estado real. Parada no condutor sai com o desde e o
|
|
299
|
+
* proximo passo; entrega em curso, com o estado do PR e a hora em que ele foi lido; sem nada disso, a fase.
|
|
300
|
+
*/
|
|
301
|
+
function linhaDoQueVem(x, agora, fuso) {
|
|
302
|
+
const e = x.entrega;
|
|
303
|
+
if (e?.parado) {
|
|
304
|
+
const { prLidoEm: lido } = e.parado;
|
|
305
|
+
return `• ${x.item}: ${(0, hitl_resumo_1.linhaDoParadoNoCondutor)({ thread: x.thread, ...e.parado }, { agora, fuso })}${naMaquina(x.maquina)}` +
|
|
306
|
+
`${lido ? ` (PR lido às ${(0, horario_1.formatarHora)(lido, { agora, fuso })})` : ''}.`;
|
|
307
|
+
}
|
|
308
|
+
if (e?.estado) {
|
|
309
|
+
const { prLidoEm: lido } = e;
|
|
310
|
+
const daLeitura = lido ? ` (PR lido às ${(0, horario_1.formatarHora)(lido, { agora, fuso })})` : '';
|
|
311
|
+
return e.conduzida ? `• ${x.item}: sigo ${x.thread} na fase ${x.fase}${naMaquina(x.maquina)}; ${e.estado}${daLeitura}.`
|
|
312
|
+
: `• ${x.item}: ${x.thread} na fase ${x.fase}${naMaquina(x.maquina)}, ${e.estado}${daLeitura}.`;
|
|
313
|
+
}
|
|
314
|
+
return `• ${x.item}: sigo ${x.thread} na fase ${x.fase}${naMaquina(x.maquina)}.`;
|
|
315
|
+
}
|
|
316
|
+
/** RM-037 (fatia 4, D8): uma linha so para a fabrica, e so quando ha o que avisar. */
|
|
317
|
+
function linhaDaFabrica(f, agora, fuso) {
|
|
318
|
+
if (!f)
|
|
319
|
+
return [];
|
|
320
|
+
if (!f.lido)
|
|
321
|
+
return [`Fábrica: não lido (${f.motivo ?? `esta máquina não tem cópia local de ${fabrica_estado_1.BRANCH_DA_FABRICA}`}).`];
|
|
322
|
+
if (f.copiaVelha) {
|
|
323
|
+
const { ultimoDestaMaquina: ultimo } = f.copiaVelha;
|
|
324
|
+
return [`Fábrica: a cópia local de ${fabrica_estado_1.BRANCH_DA_FABRICA} está velha (${ultimo ? `esta máquina publicou nela por último em ` +
|
|
325
|
+
`${(0, horario_1.formatarDataHora)(ultimo, { agora, fuso })}` : 'esta máquina não aparece nela'}); não dá para dizer quem está sem batida.`];
|
|
326
|
+
}
|
|
327
|
+
if (!f.semBatida.length)
|
|
328
|
+
return [];
|
|
329
|
+
return [`Fábrica: ${f.semBatida.map(m => `${m.maquina} sem batida há ${(0, horario_1.duracaoCurta)(m.idadeMin)} ` +
|
|
330
|
+
`(último retrato ${(0, horario_1.formatarDataHora)(m.publicadoEm, { agora, fuso })})`).join('; ')}, pela cópia local de ${fabrica_estado_1.BRANCH_DA_FABRICA}.`];
|
|
331
|
+
}
|
|
186
332
|
/**
|
|
187
333
|
* O texto do relatorio, igual em todo canal: os icones sao parte do formato aprovado. `fuso`: o do
|
|
188
334
|
* projeto consultado, na visao da rede; sem ele, o do dono deste processo.
|
|
335
|
+
*
|
|
336
|
+
* Fatia 2 do ensaio da 0.5.0 (P7): a hora do titulo vem com o fuso dito logo abaixo dele, na linha
|
|
337
|
+
* 2, sem mudar o titulo aprovado. `legenda: false` e para quem ja diz o fuso na mesma mensagem (o
|
|
338
|
+
* panorama da rede diz no fim).
|
|
189
339
|
*/
|
|
190
|
-
function textoDoStatusDoRoadmap(s, fuso) {
|
|
340
|
+
function textoDoStatusDoRoadmap(s, fuso, opcoes = {}) {
|
|
191
341
|
if (s.contrato !== exports.CONTRATO_STATUS_DO_ROADMAP)
|
|
192
342
|
throw new Error('status do roadmap: contrato inválido');
|
|
193
343
|
const p = (0, horario_1.partesLocais)(s.consultadoEm, fuso);
|
|
@@ -199,15 +349,20 @@ function textoDoStatusDoRoadmap(s, fuso) {
|
|
|
199
349
|
const cortar = (lista, f) => [...lista.slice(0, exports.TETO_DO_FECHO).map(f), ...(lista.length > exports.TETO_DO_FECHO ? [`• e mais ${lista.length - exports.TETO_DO_FECHO}`] : [])];
|
|
200
350
|
return [
|
|
201
351
|
`Roadmap do ${capitalizar(s.projeto)} (${p.dia}/${p.mes}, ${p.hora}:${p.minuto})`,
|
|
202
|
-
|
|
352
|
+
...(opcoes.legenda === false ? [] : [(0, horario_1.legendaDoFuso)(fuso, s.consultadoEm)]),
|
|
353
|
+
// RM-052: logo abaixo do titulo aprovado (e do fuso), qual projeto foi lido e o que nao foi.
|
|
203
354
|
...(s.consulta ? (0, projeto_alvo_1.linhasDaConsulta)(s.consulta) : []),
|
|
355
|
+
...linhaDaFabrica(s.fabrica, s.consultadoEm, fuso),
|
|
204
356
|
...s.grupos.filter(g => g.itens.length).flatMap(g => ['', `${g.icone} ${g.titulo}`, ...g.itens.map(linhaDoItem)]),
|
|
205
357
|
'', 'O que precisa de você',
|
|
206
358
|
...(s.precisaDeVoce.length ? cortar(s.precisaDeVoce, x => `• ${x.item}${naMaquina(x.espera.maquina)}: ${x.espera.pergunta}` +
|
|
207
359
|
(x.espera.codigo ? ` Responda ${x.espera.codigo} ${x.espera.recomendada ?? 'a'} (ou outra letra).` : ' A pergunta chega no próximo resumo.'))
|
|
208
360
|
: ['• Nada agora.']),
|
|
361
|
+
// RM-057 (fatia 3): ha quanto tempo cada pergunta de conducao parou a thread, e a mediana da semana.
|
|
362
|
+
...(s.hitlDeConducao ? [`• HITL de condução: ${s.hitlDeConducao.abertas.length} aberta(s); ${(0, hitl_tempo_parado_1.textoDaMediana)(s.hitlDeConducao)}.`,
|
|
363
|
+
...cortar(s.hitlDeConducao.abertas, a => ` ${(0, hitl_tempo_parado_1.linhaDaPerguntaParada)(a, { agora: s.consultadoEm, fuso })}.`)] : []),
|
|
209
364
|
'', 'O que eu faço em seguida',
|
|
210
|
-
...(s.emSeguida.length ? cortar(s.emSeguida, x =>
|
|
365
|
+
...(s.emSeguida.length ? cortar(s.emSeguida, x => linhaDoQueVem(x, s.consultadoEm, fuso))
|
|
211
366
|
: ['• Nada em andamento; sigo o que você priorizar.']),
|
|
212
367
|
].join('\n');
|
|
213
368
|
}
|
package/dist/runtime-context.js
CHANGED
|
@@ -46,6 +46,7 @@ const thread_1 = require("./thread");
|
|
|
46
46
|
const hosts_1 = require("./hosts");
|
|
47
47
|
const worktree_1 = require("./worktree");
|
|
48
48
|
const mcp_install_1 = require("./mcp-install");
|
|
49
|
+
const mcp_grafo_1 = require("./mcp-grafo");
|
|
49
50
|
const hitl_public_receipt_1 = require("./hitl-public-receipt");
|
|
50
51
|
/** Correlação interna; nenhuma identidade de despacho concede poder de gate. */
|
|
51
52
|
function novaIdentidadeDeDespacho(threadId, fase) {
|
|
@@ -207,9 +208,12 @@ function validarContextoRuntime(contexto, cwd) {
|
|
|
207
208
|
throw Error('runtime.context.installation: catalogo ou plugin incompleto');
|
|
208
209
|
// Capacidade derivada do bloco atual, nunca aceita como entrada do modelo.
|
|
209
210
|
const permiteEditarProduto = permissoesFilho === 'worktree' && (0, thread_1.blocoDaThread)(thread, thread.faseAtual).fases.includes('GO');
|
|
211
|
+
// RM-031 KG5 (D2, D7): as tools do grafo seguem a flag do manifesto da raiz, que o servidor do filho tambem le;
|
|
212
|
+
// o manifesto da worktree da thread e o pedido do modelo nao ligam nada.
|
|
213
|
+
const grafoMcp = (0, mcp_grafo_1.grafoLigado)((0, manifest_1.exigirManifesto)(raiz).manifesto);
|
|
210
214
|
const publicVerifiers = (0, hitl_public_receipt_1.publicHitlVerifiers)();
|
|
211
215
|
const servidor = { ...(0, mcp_install_1.configuracaoServidorMcp)(raiz, contexto.host, contexto.threadId, transporteShip, permissoesFilho, contexto.identidade?.dispatchId),
|
|
212
216
|
...(publicVerifiers ? { env: { [hitl_public_receipt_1.ENV_HITL_VERIFIERS]: publicVerifiers } } : {}),
|
|
213
217
|
...(contexto.host === 'codex' && permissoesFilho === 'worktree' ? (0, mcp_install_1.politicasFilhoCodex)(perfil.politicasCodex ?? {}) : {}) };
|
|
214
|
-
return { projeto: raiz, instalacao, bootstrap, permissoesFilho, permiteEditarProduto, servidor };
|
|
218
|
+
return { projeto: raiz, instalacao, bootstrap, permissoesFilho, permiteEditarProduto, grafoMcp, servidor };
|
|
215
219
|
}
|
package/dist/runtime-profiles.js
CHANGED
|
@@ -67,6 +67,7 @@ exports.perfisDoRuntime = perfisDoRuntime;
|
|
|
67
67
|
exports.perfilDisponivel = perfilDisponivel;
|
|
68
68
|
exports.politicaDeRotacao = politicaDeRotacao;
|
|
69
69
|
exports.proximoPerfilDisponivel = proximoPerfilDisponivel;
|
|
70
|
+
exports.proximoPerfilPorCarga = proximoPerfilPorCarga;
|
|
70
71
|
exports.prazoDoRuntime = prazoDoRuntime;
|
|
71
72
|
exports.prazoDaFila = prazoDaFila;
|
|
72
73
|
exports.menorEsgotadoAte = menorEsgotadoAte;
|
|
@@ -794,7 +795,8 @@ exports.POLITICA_PADRAO = { mesmoRuntimePorCota: true, mesmoRuntimePorAuth: true
|
|
|
794
795
|
function politicaDeRotacao(manifesto) {
|
|
795
796
|
const r = manifesto.runtime_profiles;
|
|
796
797
|
return { mesmoRuntimePorCota: r?.rotate_same_runtime_on_quota !== false,
|
|
797
|
-
mesmoRuntimePorAuth: r?.rotate_same_runtime_on_auth !== false
|
|
798
|
+
mesmoRuntimePorAuth: r?.rotate_same_runtime_on_auth !== false,
|
|
799
|
+
...(r?.distribuir === 'carga' ? { distribuir: 'carga' } : {}) };
|
|
798
800
|
}
|
|
799
801
|
/**
|
|
800
802
|
* D14: o perfil da vez do runtime, na ordem do store. `desativado` e `provider-pago` nunca foram
|
|
@@ -819,6 +821,25 @@ function proximoPerfilDisponivel(store, runtime, politica, opcoes = {}) {
|
|
|
819
821
|
}
|
|
820
822
|
return null;
|
|
821
823
|
}
|
|
824
|
+
/**
|
|
825
|
+
* RM-056 (D3): a escolha por carga. Respeita a politica da D14 (sem perfil da vez pela ordem, sem
|
|
826
|
+
* perfil nenhum: a mesma retencao do runtime), e entre os perfis disponiveis vence o de menor
|
|
827
|
+
* carga (sessoes vivas da conta nesta maquina); empate pelo `ultimoUso` mais antigo (nunca usado
|
|
828
|
+
* primeiro), depois a ordem do store. O `ultimoUso` e gravado no despacho, entao dois despachos
|
|
829
|
+
* seguidos com carga igual vao a perfis diferentes mesmo antes de o runtime listar a sessao.
|
|
830
|
+
*/
|
|
831
|
+
function proximoPerfilPorCarga(store, runtime, politica, carga, opcoes = {}) {
|
|
832
|
+
const daVez = proximoPerfilDisponivel(store, runtime, politica, opcoes);
|
|
833
|
+
if (!daVez)
|
|
834
|
+
return null;
|
|
835
|
+
const agoraMs = opcoes.agoraMs ?? Date.now();
|
|
836
|
+
const candidatos = perfisDoRuntime(store, runtime)
|
|
837
|
+
.map((p, i) => ({ p, i }))
|
|
838
|
+
.filter(({ p }) => !(opcoes.excluir ?? []).includes(p.id) && perfilDisponivel(p, agoraMs));
|
|
839
|
+
const uso = (p) => p.ultimoUso === null ? -Infinity : Date.parse(p.ultimoUso);
|
|
840
|
+
candidatos.sort((x, y) => (carga.get(x.p.id) ?? 0) - (carga.get(y.p.id) ?? 0) || uso(x.p) - uso(y.p) || x.i - y.i);
|
|
841
|
+
return candidatos[0]?.p ?? daVez;
|
|
842
|
+
}
|
|
822
843
|
/**
|
|
823
844
|
* D14: quando o runtime volta a ter perfil da vez, pela mesma regra da escolha. Com a troca por
|
|
824
845
|
* cota desligada, e o prazo do perfil que segura o runtime; ligada, o menor prazo entre os
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* RM-053 (fatia 2): o saneador de saida comum ao nucleo.
|
|
4
|
+
*
|
|
5
|
+
* Texto que vem de fora (o retrato de outra maquina, a branch `ork/fabrica-estado` do remoto de um
|
|
6
|
+
* projeto, onde outras pessoas escrevem) chega a tela do dono. A rede ja limpava o que imprime desde
|
|
7
|
+
* a revisao 4; o `ork fabrica` imprimia o `por` e o `projeto` legados crus. Este modulo e a regra
|
|
8
|
+
* unica: quem imprime valor alheio passa por aqui.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.emUmaLinha = exports.INVISIVEL = exports.CF_VISIVEL = void 0;
|
|
12
|
+
exports.valoresEmUmaLinha = valoresEmUmaLinha;
|
|
13
|
+
exports.jsonSemInvisivel = jsonSemInvisivel;
|
|
14
|
+
/**
|
|
15
|
+
* Y4 do CHECK 7: as marcas de `Cf` que se veem, lista fechada (todo `Prepended_Concatenation_Mark=Yes`
|
|
16
|
+
* do Unicode 17: os sinais numericos arabes U+0600 a U+0605, o fim de aya U+06DD, a abreviacao siriaca
|
|
17
|
+
* U+070F, as marcas de libra e piastra U+0890 e U+0891, o sanah U+08E2 e as marcas de numero kaithi
|
|
18
|
+
* U+110BD e U+110CD). Elas tem glifo e nao movem o texto, ao contrario das bidi, de largura zero e das
|
|
19
|
+
* tags, que seguem invisiveis. O regex do JS nao aceita `\p{Prepended_Concatenation_Mark}`; por isso a
|
|
20
|
+
* lista literal, presa ao `INVISIVEL` e conferida ponto a ponto no teste.
|
|
21
|
+
*/
|
|
22
|
+
exports.CF_VISIVEL = Object.freeze([
|
|
23
|
+
0x0600, 0x0601, 0x0602, 0x0603, 0x0604, 0x0605, 0x06dd, 0x070f, 0x0890, 0x0891, 0x08e2, 0x110bd, 0x110cd,
|
|
24
|
+
]);
|
|
25
|
+
/**
|
|
26
|
+
* V2 da revisao 4, W11 da revisao 5 e X3 da revisao 6: caractere que o terminal executa ou que
|
|
27
|
+
* ninguem ve, pelas classes do Unicode: controles (`Cc`: ESC, BEL, CSI, quebra de linha), formato
|
|
28
|
+
* (`Cf`: bidi, largura zero, tags), separadores de linha e todo `Default_Ignorable_Code_Point`
|
|
29
|
+
* (preenchimentos Hangul, seletores de variacao, os de musica e de estenografia), mais o braile
|
|
30
|
+
* vazio. Ficam de fora o ZWJ (U+200D) e os seletores U+FE0E e U+FE0F: eles montam emoji comuns, e as
|
|
31
|
+
* marcas visiveis de `Cf` (`CF_VISIVEL`, Y4 do CHECK 7).
|
|
32
|
+
*/
|
|
33
|
+
exports.INVISIVEL = /(?![\u200d\ufe0e\ufe0f\u0600-\u0605\u06dd\u070f\u0890\u0891\u08e2\u{110bd}\u{110cd}])[\p{Cc}\p{Cf}\p{Zl}\p{Zp}\p{Default_Ignorable_Code_Point}\u2800]/u;
|
|
34
|
+
const INVISIVEIS = new RegExp(exports.INVISIVEL.source, 'gu');
|
|
35
|
+
/**
|
|
36
|
+
* W4 da revisao 5: o valor numa linha so, sem nada invisivel. A quebra de linha (e a tabulacao) vira
|
|
37
|
+
* espaco: um valor que vem de fora nunca abre uma linha propria na saida, como se fosse do `ork`.
|
|
38
|
+
*/
|
|
39
|
+
const emUmaLinha = (texto) => texto.replace(INVISIVEIS, (c) => (/[\t\n\v\f\r\u0085\u2028\u2029]/.test(c) ? ' ' : ''));
|
|
40
|
+
exports.emUmaLinha = emUmaLinha;
|
|
41
|
+
/**
|
|
42
|
+
* Cada texto de uma estrutura (objeto, lista, aninhados) em uma linha, sem invisivel; numero,
|
|
43
|
+
* booleano e `null` ficam como estao (X5 da revisao 6: a ida e volta em JSON trocava `NaN` por `null`).
|
|
44
|
+
*/
|
|
45
|
+
function valoresEmUmaLinha(valor) {
|
|
46
|
+
const limpar = (v) => typeof v === 'string' ? (0, exports.emUmaLinha)(v)
|
|
47
|
+
: Array.isArray(v) ? v.map(limpar)
|
|
48
|
+
: v && typeof v === 'object' ? Object.fromEntries(Object.entries(v).map(([k, x]) => [k, limpar(x)])) : v;
|
|
49
|
+
return limpar(valor);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* O JSON para a tela: o mesmo valor, com os caracteres invisiveis escritos como `\uXXXX` (quem le o
|
|
53
|
+
* JSON recebe o mesmo texto; so o terminal deixa de executa-lo). A quebra de linha da indentacao fica.
|
|
54
|
+
*/
|
|
55
|
+
function jsonSemInvisivel(valor) {
|
|
56
|
+
return JSON.stringify(valor, null, 2).replace(INVISIVEIS, (c) => c === '\n' ? c
|
|
57
|
+
: Array.from({ length: c.length }, (_, i) => `\\u${c.charCodeAt(i).toString(16).padStart(4, '0')}`).join(''));
|
|
58
|
+
}
|