@orkastery/cli 0.5.3 → 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/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/hermes/skills/orkastery-devmaster/SKILL.md +1 -1
- package/adapters/openclaw/README.md +2 -1
- package/adapters/openclaw/openclaw.plugin.json +1 -1
- package/adapters/openclaw/package.json +1 -1
- 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/dist/adapters/claude-bg.js +77 -13
- package/dist/adapters/codex.js +10 -1
- package/dist/agents-md.js +24 -15
- package/dist/auditrun.js +17 -2
- package/dist/board.js +4 -3
- package/dist/branch-de-estado.js +23 -1
- package/dist/catalogo.js +33 -0
- package/dist/ci.js +13 -7
- package/dist/claims-conferir.js +342 -0
- package/dist/claims.js +3 -1
- package/dist/company-brain-client.js +36 -1
- 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 +22 -8
- package/dist/doctor-pulse-cron.js +182 -27
- package/dist/doctor-rede.js +18 -17
- package/dist/doctor.js +115 -111
- package/dist/entrega-pr.js +22 -21
- package/dist/escopo-escrita.js +10 -1
- package/dist/estado-thread.js +23 -1
- package/dist/experiencia-instalacao.js +45 -25
- package/dist/fabrica-batida.js +310 -0
- package/dist/fabrica-estado.js +38 -8
- package/dist/fabrica-publicar.js +3 -1
- package/dist/fila-pesada.js +444 -0
- package/dist/fix.js +3 -2
- package/dist/forja.js +1 -1
- package/dist/gates.js +3 -28
- package/dist/handoff.js +10 -6
- package/dist/hitl-local-receipt.js +2 -0
- package/dist/hitl-resumo.js +29 -3
- package/dist/hitl-tempo-parado.js +4 -4
- package/dist/hitl.js +29 -33
- package/dist/horario.js +14 -7
- package/dist/hosts.js +52 -1
- package/dist/impedimento.js +108 -14
- package/dist/index.js +623 -521
- package/dist/indice.js +7 -1
- package/dist/init.js +43 -47
- package/dist/instabilidade.js +144 -0
- package/dist/integracoes-locais.js +2 -0
- package/dist/intelligence-graph-cli.js +92 -21
- package/dist/intelligence-graph-contexto.js +56 -17
- 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-query.js +66 -19
- package/dist/ledger.js +15 -1
- 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/manifest.js +129 -3
- package/dist/maquina.js +23 -4
- package/dist/master-digest.js +6 -1
- package/dist/master-nota.js +4 -6
- package/dist/master.js +21 -24
- package/dist/mcp-artifacts.js +11 -2
- package/dist/mcp-git.js +23 -6
- package/dist/mcp-grafo.js +7 -4
- package/dist/mcp-server.js +8 -2
- package/dist/mcp-ship.js +3 -1
- package/dist/mcp-verify.js +6 -1
- package/dist/modos.js +3 -6
- package/dist/network-roadmap.js +5 -2
- 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 +29 -2
- package/dist/onboarding.js +40 -21
- package/dist/orkmind.js +9 -2
- package/dist/orquestracao.js +13 -12
- package/dist/parado-no-condutor.js +34 -9
- package/dist/phase.js +94 -39
- package/dist/policies.js +4 -4
- package/dist/portfolio.js +4 -2
- package/dist/postura-local.js +234 -0
- package/dist/preflight.js +11 -10
- package/dist/procedencia.js +225 -0
- package/dist/projeto-alvo.js +127 -4
- package/dist/prompts.js +6 -2
- package/dist/pulse-delivery.js +13 -2
- package/dist/pulse.js +52 -19
- package/dist/ratelimit.js +4 -2
- package/dist/redacao-saida.js +63 -0
- package/dist/rede-status.js +3 -2
- package/dist/retry.js +232 -148
- package/dist/roadmap-reservas.js +20 -3
- package/dist/roadmap-status.js +129 -33
- package/dist/runtime-context.js +181 -3
- package/dist/runtimes.js +2 -1
- package/dist/session-events.js +7 -3
- package/dist/session-watcher-claude.js +7 -1
- package/dist/session-watcher.js +185 -33
- package/dist/sessoes-contas.js +1 -1
- package/dist/ship-publicar.js +800 -0
- package/dist/ship.js +84 -81
- package/dist/steal.js +77 -0
- package/dist/thread-close.js +3 -2
- package/dist/thread.js +153 -82
- 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 +20 -0
- package/dist/yaml.js +27 -8
- package/package.json +1 -1
- package/schemas/code-artifact-graph.v1.schema.json +2 -1
package/dist/indice.js
CHANGED
|
@@ -59,13 +59,19 @@ exports.INSUMOS_DO_INDICE = [
|
|
|
59
59
|
* reversao e `ship.ts`, que e quem sabe de merge e de base; aqui so se conta o evento
|
|
60
60
|
* que ele gravou.
|
|
61
61
|
*/
|
|
62
|
+
function pausaPrevistaDoFast(e) {
|
|
63
|
+
return e.tipo === ledger_1.TIPOS_DE_EVENTO.shipBloqueado && e.modo === 'fast' && e.motivo === 'human.pending';
|
|
64
|
+
}
|
|
62
65
|
function calcularIndice(eventos) {
|
|
63
66
|
const parcelas = [];
|
|
64
67
|
let valor = exports.INDICE_BASE;
|
|
65
68
|
for (const insumo of exports.INSUMOS_DO_INDICE) {
|
|
66
69
|
// Ensaio de 03/10/2026 (RM-049): o `ork ship --dry-run` barrado grava `ship_blocked` com `dryRun: true`;
|
|
67
70
|
// ensaio nao e tropeco da entrega, como ja nao e pausa no board (fatia 2 do ensaio da 0.5.0, P2).
|
|
68
|
-
|
|
71
|
+
// EN8 (a), decisao do dono de 11/10/2026: no #Fast, o ship sem autorizacao de push e a pausa prevista
|
|
72
|
+
// do modo, e o `ship_blocked` com `human.pending` tambem nao conta.
|
|
73
|
+
const ocorrencias = eventos.filter((e) => e.tipo === insumo.evento && e.dryRun !== true &&
|
|
74
|
+
!pausaPrevistaDoFast(e)).length;
|
|
69
75
|
if (ocorrencias === 0)
|
|
70
76
|
continue;
|
|
71
77
|
const bruto = insumo.peso * ocorrencias;
|
package/dist/init.js
CHANGED
|
@@ -48,6 +48,7 @@ exports.detectar = detectar;
|
|
|
48
48
|
exports.manifestoYaml = manifestoYaml;
|
|
49
49
|
exports.exigirRepositorioParaInit = exigirRepositorioParaInit;
|
|
50
50
|
exports.init = init;
|
|
51
|
+
const locale_1 = require("./locale");
|
|
51
52
|
const fs = __importStar(require("node:fs"));
|
|
52
53
|
const path = __importStar(require("node:path"));
|
|
53
54
|
const manifest_1 = require("./manifest");
|
|
@@ -55,7 +56,8 @@ const slug_1 = require("./slug");
|
|
|
55
56
|
const util_1 = require("./util");
|
|
56
57
|
const horario_1 = require("./horario");
|
|
57
58
|
const modos_1 = require("./modos");
|
|
58
|
-
|
|
59
|
+
/** O proximo passo do `ork init` em pt-BR; o texto do CLI sai pelo catalogo (`msg().init.proximoPasso`). */
|
|
60
|
+
exports.PROXIMO_PASSO_INIT = (0, locale_1.msg)('pt-BR').init.proximoPasso;
|
|
59
61
|
/** Raiz do repositorio git, resolvendo tambem o caso de worktree. */
|
|
60
62
|
function raizDoRepo(dirInicial) {
|
|
61
63
|
const r = (0, util_1.exec)('git', ['rev-parse', '--show-toplevel'], dirInicial);
|
|
@@ -144,57 +146,57 @@ function detectar(dirInicial, nomeForcado, abbrevForcada) {
|
|
|
144
146
|
verify: verifyDetectado(raiz),
|
|
145
147
|
};
|
|
146
148
|
}
|
|
147
|
-
/**
|
|
148
|
-
function
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
149
|
+
/** Cada linha do texto vira comentario do YAML, com o recuo da chave em que ele fica. */
|
|
150
|
+
function comentario(texto, recuo = ' ') {
|
|
151
|
+
return texto.split('\n').map((linha) => `${recuo}# ${linha}`).join('\n');
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Serializa o manifesto v1 a partir da deteccao. RM-061 (F3a, D1 a D3): os comentarios saem na lingua pedida, pela
|
|
155
|
+
* familia `gerados` do catalogo; chaves, valores e exemplos comentados sao este esqueleto, o mesmo nas duas linguas.
|
|
156
|
+
* Sem locale, pt-BR: so o `ork init` passa a lingua do dono (D2).
|
|
157
|
+
*/
|
|
158
|
+
function manifestoYaml(d, locale = locale_1.LOCALE_PADRAO) {
|
|
159
|
+
const c = (0, locale_1.msg)(locale).gerados.manifesto;
|
|
160
|
+
const linhaVerify = (chave, valor) => valor ? ` ${chave}: "${valor}"` : ` # ${chave}: ${c.naoDetectado}`;
|
|
161
|
+
return `${comentario(c.cabecalho, '')}
|
|
153
162
|
|
|
154
163
|
project:
|
|
155
164
|
name: "${d.nome}"
|
|
156
|
-
|
|
165
|
+
${comentario(c.abbrev)}
|
|
157
166
|
abbrev: "${d.abbrev}"
|
|
158
167
|
stage: nascente
|
|
159
168
|
|
|
160
169
|
owner:
|
|
161
|
-
|
|
162
|
-
# Ausente: fuso do sistema. Exemplo:
|
|
170
|
+
${comentario(c.fuso)}
|
|
163
171
|
# timezone: "${horario_1.FUSO_DE_BRASILIA}"
|
|
164
|
-
|
|
165
|
-
# language:
|
|
172
|
+
${comentario(c.preferencias(`ork onboarding set maestro --conteudo '{"owner":{"experience":true}}'`))}
|
|
173
|
+
# language: ${c.exemploDeIdioma} # ${c.idiomaAusente}
|
|
166
174
|
# depth: curta # curta | detalhada
|
|
167
|
-
# experience: true #
|
|
175
|
+
# experience: true # ${c.experienciaFalse}
|
|
168
176
|
|
|
169
177
|
board:
|
|
170
178
|
adapter: hermes-kanban
|
|
171
179
|
default: default
|
|
172
180
|
|
|
173
181
|
runtime:
|
|
174
|
-
|
|
175
|
-
# claude-bg (padrao, mais maduro): despacha \`claude --bg\`, sessao visivel no app
|
|
176
|
-
# e em \`claude agents --json\`
|
|
177
|
-
# codex: despacha \`codex exec\` headless na sessao autenticada do Codex CLI,
|
|
178
|
-
# verificado no rollout de $CODEX_HOME/sessions (requer \`codex login\`)
|
|
182
|
+
${comentario(c.runtimes)}
|
|
179
183
|
adapter: claude-bg
|
|
180
184
|
model: opus
|
|
181
185
|
effort: high
|
|
182
|
-
|
|
186
|
+
${comentario(c.providerPolicy)}
|
|
183
187
|
provider_policy: subscription-only
|
|
184
|
-
|
|
185
|
-
# (danger-full-access SO onde o bubblewrap nao roda; \`ork doctor\` tem a sonda)
|
|
188
|
+
${comentario(c.sandbox)}
|
|
186
189
|
sandbox: workspace-write
|
|
187
190
|
|
|
188
191
|
conduction:
|
|
189
|
-
|
|
192
|
+
${comentario(c.modoPadrao)}
|
|
190
193
|
default_mode: classic
|
|
191
194
|
allowed_modes: [${modos_1.ORDEM_DOS_MODOS.join(', ')}]
|
|
192
195
|
|
|
193
196
|
worktree:
|
|
194
197
|
base_branch: "${d.baseBranch}"
|
|
195
198
|
dir: ".claude/worktrees"
|
|
196
|
-
|
|
197
|
-
# false ou ausente: só com --worktree auto
|
|
199
|
+
${comentario(c.porThread)}
|
|
198
200
|
por_thread: true
|
|
199
201
|
|
|
200
202
|
verify:
|
|
@@ -203,54 +205,46 @@ ${linhaVerify('test', d.verify.test)}
|
|
|
203
205
|
${linhaVerify('typecheck', d.verify.typecheck)}
|
|
204
206
|
|
|
205
207
|
ci:
|
|
206
|
-
|
|
208
|
+
${comentario(c.ci)}
|
|
207
209
|
required_for_ship: false
|
|
208
210
|
context: ork-verify
|
|
209
|
-
# command: "npm test" #
|
|
211
|
+
# command: "npm test" # ${c.ciComando}
|
|
210
212
|
|
|
211
213
|
concurrency:
|
|
212
214
|
max_parallel_threads: 3
|
|
213
|
-
|
|
214
|
-
# isto NAO ocupa vaga do escalonador: a vaga volta para quem esta pronto para rodar
|
|
215
|
+
${comentario(c.staleAfter)}
|
|
215
216
|
stale_after_min: 240
|
|
216
217
|
|
|
217
218
|
handoff:
|
|
218
|
-
|
|
219
|
+
${comentario(c.handoff)}
|
|
219
220
|
rotate_above: 0.70
|
|
220
221
|
force_rotate_above: 0.85
|
|
221
222
|
|
|
222
223
|
retry:
|
|
223
|
-
|
|
224
|
-
# max_tentativas e o LIMITE DE ESCALACAO: estourado, pausa qualquer modo, ate #Auto
|
|
224
|
+
${comentario(c.retry)}
|
|
225
225
|
max_tentativas: 3
|
|
226
|
-
|
|
227
|
-
# (fica marcada como estimativa na fila; o ork nunca inventa horario medido)
|
|
226
|
+
${comentario(c.janelaPadrao)}
|
|
228
227
|
janela_padrao_min: 60
|
|
229
228
|
escalar_esforco: true
|
|
230
229
|
|
|
231
230
|
memory:
|
|
232
|
-
|
|
233
|
-
# orkmind = camada semantica do bloco B6 por cima, com degradacao honesta para files
|
|
231
|
+
${comentario(c.memoriaModo)}
|
|
234
232
|
mode: files
|
|
235
|
-
|
|
236
|
-
# Vazio = o regime orkmind nao liga (o ork nao adivinha base: base alheia e pior que
|
|
237
|
-
# base nenhuma, precedente do incidente memory-orkmind).
|
|
233
|
+
${comentario(c.databaseUrl)}
|
|
238
234
|
database_url_env: ""
|
|
239
235
|
cli: orkmind
|
|
240
236
|
timeout_ms: 15000
|
|
241
|
-
|
|
242
|
-
# api_key_env recebe o NOME da variavel com a chave dedicada, nunca o valor.
|
|
237
|
+
${comentario(c.embedding)}
|
|
243
238
|
# embedding:
|
|
244
239
|
# provider: "none" # none | openrouter
|
|
245
240
|
# model: "qwen/qwen3-embedding-8b"
|
|
246
241
|
# dim: 1024
|
|
247
242
|
# api_key_env: "ORKASTERY_EMBEDDING_API_KEY"
|
|
248
|
-
# fallback_model: "" #
|
|
243
|
+
# fallback_model: "" # ${c.modeloOffline}
|
|
249
244
|
# max_tokens_por_execucao: 1000000
|
|
250
245
|
|
|
251
246
|
audit:
|
|
252
|
-
|
|
253
|
-
# janela ociosa da assinatura: fora dela a rodada so anda com --agora <quem>
|
|
247
|
+
${comentario(c.audit)}
|
|
254
248
|
janela_ociosa: "22:00-06:00"
|
|
255
249
|
effort: eco
|
|
256
250
|
since_padrao: 7d
|
|
@@ -273,10 +267,12 @@ function exigirRepositorioParaInit(dir) {
|
|
|
273
267
|
const r = (0, util_1.exec)('git', ['rev-parse', '--is-inside-work-tree'], dir);
|
|
274
268
|
if (r.ok && r.stdout.trim() === 'true')
|
|
275
269
|
return;
|
|
276
|
-
throw new Error(
|
|
277
|
-
'Entre no repositório do projeto (ou crie um com git init e o primeiro commit) e rode ork init de novo.');
|
|
270
|
+
throw new Error((0, locale_1.msg)().init.foraDoRepositorio(path.resolve(dir)));
|
|
278
271
|
}
|
|
279
|
-
/**
|
|
272
|
+
/**
|
|
273
|
+
* Gera o manifesto e o esqueleto de `.orkastery/`. Nao sobrescreve sem `force`. Os comentarios do manifesto saem
|
|
274
|
+
* no `locale` pedido (RM-061, F3a), pt-BR por omissao.
|
|
275
|
+
*/
|
|
280
276
|
function init(dirInicial, opcoes = {}) {
|
|
281
277
|
const deteccao = detectar(dirInicial, opcoes.nome, opcoes.abbrev);
|
|
282
278
|
const caminho = path.join(deteccao.raiz, manifest_1.NOME_MANIFESTO);
|
|
@@ -284,7 +280,7 @@ function init(dirInicial, opcoes = {}) {
|
|
|
284
280
|
if (existe && !opcoes.force) {
|
|
285
281
|
return { caminho, criado: false, deteccao };
|
|
286
282
|
}
|
|
287
|
-
(0, util_1.gravar)(caminho, manifestoYaml(deteccao));
|
|
283
|
+
(0, util_1.gravar)(caminho, manifestoYaml(deteccao, opcoes.locale));
|
|
288
284
|
const estado = path.join(deteccao.raiz, manifest_1.DIR_ESTADO);
|
|
289
285
|
fs.mkdirSync(path.join(estado, 'threads'), { recursive: true });
|
|
290
286
|
// Fatia 2 do ensaio da 0.5.0 (P3): o estado e da maquina; fica fora do git sem mexer no `.gitignore` do usuario.
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* RM-037 (fatia 6, P6/T15): o registro de instabilidade, contrato `ork.instabilidade/v1`.
|
|
4
|
+
*
|
|
5
|
+
* Arquivo `core/instabilidade.json` na arvore verificada. Nasce vazio. Uma entrada diz que um teste
|
|
6
|
+
* depende do relogio e por isso reprova sob steal alto; ela so entra com a taxa MEDIDA (falhas em
|
|
7
|
+
* rodadas, com o comando que mediu e a data) e com revalidacao em ate 30 dias da medida. Vencida a
|
|
8
|
+
* revalidacao, a entrada deixa de valer ate alguem medir de novo.
|
|
9
|
+
*
|
|
10
|
+
* O registro nunca atenua uma falha sozinho: ele so diz ao verify que aquele teste e de relogio, e
|
|
11
|
+
* a reprovacao so vira `verify.timeout` com o steal medido acima de 40% na janela do comando. Sem
|
|
12
|
+
* steal, o teste registrado reprova como qualquer outro.
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.CAUSAS_DE_INSTABILIDADE = exports.REVALIDACAO_MAXIMA_DIAS = exports.ARQUIVO_INSTABILIDADE = exports.CONTRATO_INSTABILIDADE = void 0;
|
|
16
|
+
exports.validarRegistro = validarRegistro;
|
|
17
|
+
exports.vigente = vigente;
|
|
18
|
+
exports.lerRegistroDeInstabilidade = lerRegistroDeInstabilidade;
|
|
19
|
+
exports.testesDeRelogioVigentes = testesDeRelogioVigentes;
|
|
20
|
+
const node_fs_1 = require("node:fs");
|
|
21
|
+
const node_path_1 = require("node:path");
|
|
22
|
+
exports.CONTRATO_INSTABILIDADE = 'ork.instabilidade/v1';
|
|
23
|
+
exports.ARQUIVO_INSTABILIDADE = 'core/instabilidade.json';
|
|
24
|
+
/** Prazo maximo entre a medida e a revalidacao. */
|
|
25
|
+
exports.REVALIDACAO_MAXIMA_DIAS = 30;
|
|
26
|
+
exports.CAUSAS_DE_INSTABILIDADE = ['relogio'];
|
|
27
|
+
const DIA_MS = 24 * 60 * 60 * 1000;
|
|
28
|
+
const CHAVES_DA_ENTRADA = new Set(['teste', 'arquivo', 'causa', 'taxa', 'revalidarAte', 'nota']);
|
|
29
|
+
const CHAVES_DA_TAXA = new Set(['falhas', 'rodadas', 'comando', 'medidaEm']);
|
|
30
|
+
function data(valor) {
|
|
31
|
+
if (typeof valor !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(valor))
|
|
32
|
+
return null;
|
|
33
|
+
const ms = Date.parse(`${valor}T00:00:00Z`);
|
|
34
|
+
return Number.isFinite(ms) && new Date(ms).toISOString().slice(0, 10) === valor ? ms : null;
|
|
35
|
+
}
|
|
36
|
+
const texto = (v) => typeof v === 'string' && v.trim() !== '';
|
|
37
|
+
const inteiro = (v) => typeof v === 'number' && Number.isInteger(v);
|
|
38
|
+
/** Os erros de contrato de um registro ja desserializado. Lista vazia: o registro vale. */
|
|
39
|
+
function validarRegistro(bruto) {
|
|
40
|
+
const erros = [];
|
|
41
|
+
if (!bruto || typeof bruto !== 'object' || Array.isArray(bruto))
|
|
42
|
+
return ['o registro precisa ser um objeto JSON'];
|
|
43
|
+
const r = bruto;
|
|
44
|
+
if (r.contrato !== exports.CONTRATO_INSTABILIDADE)
|
|
45
|
+
erros.push(`contrato precisa ser "${exports.CONTRATO_INSTABILIDADE}"`);
|
|
46
|
+
for (const chave of Object.keys(r)) {
|
|
47
|
+
if (chave !== 'contrato' && chave !== 'entradas')
|
|
48
|
+
erros.push(`chave desconhecida no registro: ${chave}`);
|
|
49
|
+
}
|
|
50
|
+
if (!Array.isArray(r.entradas))
|
|
51
|
+
return [...erros, 'entradas precisa ser uma lista (vazia no nascimento)'];
|
|
52
|
+
const vistos = new Set();
|
|
53
|
+
r.entradas.forEach((e, i) => {
|
|
54
|
+
const onde = `entradas[${i}]`;
|
|
55
|
+
if (!e || typeof e !== 'object' || Array.isArray(e)) {
|
|
56
|
+
erros.push(`${onde}: precisa ser um objeto`);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const entrada = e;
|
|
60
|
+
for (const chave of Object.keys(entrada)) {
|
|
61
|
+
if (!CHAVES_DA_ENTRADA.has(chave))
|
|
62
|
+
erros.push(`${onde}: chave desconhecida ${chave}`);
|
|
63
|
+
}
|
|
64
|
+
if (!texto(entrada.teste))
|
|
65
|
+
erros.push(`${onde}.teste: nome do teste como o runner reporta`);
|
|
66
|
+
else if (vistos.has(entrada.teste))
|
|
67
|
+
erros.push(`${onde}.teste: "${entrada.teste}" repetido`);
|
|
68
|
+
else
|
|
69
|
+
vistos.add(entrada.teste);
|
|
70
|
+
if (!texto(entrada.arquivo))
|
|
71
|
+
erros.push(`${onde}.arquivo: caminho do teste`);
|
|
72
|
+
if (!exports.CAUSAS_DE_INSTABILIDADE.includes(entrada.causa)) {
|
|
73
|
+
erros.push(`${onde}.causa: ${exports.CAUSAS_DE_INSTABILIDADE.join(' ou ')}`);
|
|
74
|
+
}
|
|
75
|
+
if (entrada.nota !== undefined && typeof entrada.nota !== 'string')
|
|
76
|
+
erros.push(`${onde}.nota: texto`);
|
|
77
|
+
const taxa = entrada.taxa;
|
|
78
|
+
let medida = null;
|
|
79
|
+
if (!taxa || typeof taxa !== 'object' || Array.isArray(taxa)) {
|
|
80
|
+
erros.push(`${onde}.taxa: sem taxa medida a entrada nao entra (falhas, rodadas, comando, medidaEm)`);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
for (const chave of Object.keys(taxa)) {
|
|
84
|
+
if (!CHAVES_DA_TAXA.has(chave))
|
|
85
|
+
erros.push(`${onde}.taxa: chave desconhecida ${chave}`);
|
|
86
|
+
}
|
|
87
|
+
if (!inteiro(taxa.rodadas) || taxa.rodadas < 1)
|
|
88
|
+
erros.push(`${onde}.taxa.rodadas: inteiro de rodadas medidas, 1 ou mais`);
|
|
89
|
+
if (!inteiro(taxa.falhas) || taxa.falhas < 1)
|
|
90
|
+
erros.push(`${onde}.taxa.falhas: inteiro de reprovacoes medidas, 1 ou mais`);
|
|
91
|
+
else if (inteiro(taxa.rodadas) && taxa.falhas > taxa.rodadas)
|
|
92
|
+
erros.push(`${onde}.taxa.falhas: maior que rodadas`);
|
|
93
|
+
if (!texto(taxa.comando))
|
|
94
|
+
erros.push(`${onde}.taxa.comando: o comando que mediu, reproduzivel`);
|
|
95
|
+
medida = data(taxa.medidaEm);
|
|
96
|
+
if (medida === null)
|
|
97
|
+
erros.push(`${onde}.taxa.medidaEm: data AAAA-MM-DD valida`);
|
|
98
|
+
}
|
|
99
|
+
const revalidar = data(entrada.revalidarAte);
|
|
100
|
+
if (revalidar === null)
|
|
101
|
+
erros.push(`${onde}.revalidarAte: data AAAA-MM-DD valida`);
|
|
102
|
+
else if (medida !== null) {
|
|
103
|
+
if (revalidar < medida)
|
|
104
|
+
erros.push(`${onde}.revalidarAte: antes da medida`);
|
|
105
|
+
else if (revalidar - medida > exports.REVALIDACAO_MAXIMA_DIAS * DIA_MS) {
|
|
106
|
+
erros.push(`${onde}.revalidarAte: no maximo ${exports.REVALIDACAO_MAXIMA_DIAS} dias depois de taxa.medidaEm`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
return erros;
|
|
111
|
+
}
|
|
112
|
+
/** A entrada vale hoje? Vale ate o fim do dia de `revalidarAte`, em UTC. */
|
|
113
|
+
function vigente(entrada, agoraMs = Date.now()) {
|
|
114
|
+
const ate = data(entrada.revalidarAte);
|
|
115
|
+
return ate !== null && agoraMs < ate + DIA_MS;
|
|
116
|
+
}
|
|
117
|
+
/** Le o registro da arvore `cwd`. Ausente: vazio. Ilegivel ou fora do contrato: erros e nada vale. */
|
|
118
|
+
function lerRegistroDeInstabilidade(cwd, agoraMs = Date.now()) {
|
|
119
|
+
const arquivo = (0, node_path_1.join)(cwd, exports.ARQUIVO_INSTABILIDADE);
|
|
120
|
+
if (!(0, node_fs_1.existsSync)(arquivo))
|
|
121
|
+
return { arquivo: exports.ARQUIVO_INSTABILIDADE, presente: false, erros: [], vigentes: [], vencidas: [] };
|
|
122
|
+
let bruto;
|
|
123
|
+
try {
|
|
124
|
+
bruto = JSON.parse((0, node_fs_1.readFileSync)(arquivo, 'utf8'));
|
|
125
|
+
}
|
|
126
|
+
catch (e) {
|
|
127
|
+
return { arquivo: exports.ARQUIVO_INSTABILIDADE, presente: true, erros: [`JSON ilegivel: ${e.message}`], vigentes: [], vencidas: [] };
|
|
128
|
+
}
|
|
129
|
+
const erros = validarRegistro(bruto);
|
|
130
|
+
if (erros.length > 0)
|
|
131
|
+
return { arquivo: exports.ARQUIVO_INSTABILIDADE, presente: true, erros, vigentes: [], vencidas: [] };
|
|
132
|
+
const entradas = bruto.entradas;
|
|
133
|
+
return {
|
|
134
|
+
arquivo: exports.ARQUIVO_INSTABILIDADE,
|
|
135
|
+
presente: true,
|
|
136
|
+
erros: [],
|
|
137
|
+
vigentes: entradas.filter((e) => vigente(e, agoraMs)),
|
|
138
|
+
vencidas: entradas.filter((e) => !vigente(e, agoraMs)),
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
/** Os nomes dos testes de relogio que o verify pode considerar nesta rodada. */
|
|
142
|
+
function testesDeRelogioVigentes(registro) {
|
|
143
|
+
return new Set(registro.vigentes.filter((e) => e.causa === 'relogio').map((e) => e.teste));
|
|
144
|
+
}
|
|
@@ -18,4 +18,6 @@ exports.TESTES_DE_INTEGRACAO_LOCAL = new Set([
|
|
|
18
18
|
'rm037-fatia3-hard-link-mcp-git.test.js', 'rm037-fatia3-lease-mcp-git.test.js',
|
|
19
19
|
// RM-038 (fatia de correcao): a ponte com o GovernedStore real da biblioteca instalada.
|
|
20
20
|
'rm038-universo-ponte.test.js',
|
|
21
|
+
// RM-059 (T0.10): a conferencia pontual com o sandbox real do codex, como o mcp-verify.test.js.
|
|
22
|
+
'claims-conferir-sandbox.test.js',
|
|
21
23
|
]);
|
|
@@ -33,11 +33,12 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.CORRECAO_DO_INDICE = exports.USO_DO_GRAFO = exports.AMOSTRA_SCHEMA = exports.STATUS_SCHEMA = void 0;
|
|
36
|
+
exports.CORRECAO_DO_INDICE = exports.USO_DO_GRAFO = exports.AMOSTRA_SCHEMA = exports.REDACAO_DOS_ANALISADORES = exports.STATUS_SCHEMA = void 0;
|
|
37
37
|
exports.descreverVersoes = descreverVersoes;
|
|
38
38
|
exports.correcaoDosAnalisadores = correcaoDosAnalisadores;
|
|
39
39
|
exports.checarAnalisadoresDoGrafo = checarAnalisadoresDoGrafo;
|
|
40
40
|
exports.lerPedido = lerPedido;
|
|
41
|
+
exports.situacaoNaArvore = situacaoNaArvore;
|
|
41
42
|
exports.executarGrafo = executarGrafo;
|
|
42
43
|
/**
|
|
43
44
|
* RM-031 KG3 (D7, D8): `ork grafo`, o CLI do indice e da consulta do grafo de codigo.
|
|
@@ -78,17 +79,21 @@ const RECUSA_DOS_ANALISADORES = 'grafo.parser.indisponivel';
|
|
|
78
79
|
* de `MODULOS_DO_EXTRATOR`, para a redacao nao entrar na chave do indice; a versao vem de quem chama
|
|
79
80
|
* (o `index.ts`), porque o CLI do grafo so importa a propria familia (fronteira do KG3).
|
|
80
81
|
*/
|
|
81
|
-
function correcaoDosAnalisadores(motivo, versao) {
|
|
82
|
-
if (/ERR_REQUIRE_ESM/.test(motivo))
|
|
83
|
-
return
|
|
84
|
-
'sem a opcao --no-experimental-require-module (na linha de comando ou no NODE_OPTIONS)';
|
|
85
|
-
}
|
|
82
|
+
function correcaoDosAnalisadores(motivo, versao, redacao = exports.REDACAO_DOS_ANALISADORES) {
|
|
83
|
+
if (/ERR_REQUIRE_ESM/.test(motivo))
|
|
84
|
+
return redacao.soEsm(process.version);
|
|
86
85
|
// Sem a versao do pacote (`0.0.0-desconhecida` quando o package.json dele nao e achado), o comando
|
|
87
86
|
// sai sem `@<versao>` e o npm instala a ultima publicada.
|
|
88
87
|
const fixada = versao && /^\d+\.\d+\.\d+/.test(versao) && !versao.startsWith('0.0.0') ? `@${versao}` : '';
|
|
89
|
-
return
|
|
90
|
-
' (instalado dentro de um projeto ou pelo npx, o npm deixa os analisadores fora do pacote do ork, e o grafo os recusa)';
|
|
88
|
+
return redacao.reinstale(fixada);
|
|
91
89
|
}
|
|
90
|
+
exports.REDACAO_DOS_ANALISADORES = {
|
|
91
|
+
recusa: (motivo) => `${motivo}: o ork grafo indexar recusa nesta instalacao, e sem o indice as consultas e as tools ork_grafo_* tambem`,
|
|
92
|
+
soEsm: (versaoDoNode) => `o micromark e so ESM, e este Node (${versaoDoNode}) nao o carrega por require: use Node 20.19, 22.12 ou mais novo, ` +
|
|
93
|
+
'sem a opcao --no-experimental-require-module (na linha de comando ou no NODE_OPTIONS)',
|
|
94
|
+
reinstale: (fixada) => `reinstale o ork global, que traz os analisadores como dependencias: npm install -g @orkastery/cli${fixada}` +
|
|
95
|
+
' (instalado dentro de um projeto ou pelo npx, o npm deixa os analisadores fora do pacote do ork, e o grafo os recusa)',
|
|
96
|
+
};
|
|
92
97
|
/**
|
|
93
98
|
* RM-031 (pacote do npm): o check "analisadores do grafo" do `ork doctor`. Carrega o que o
|
|
94
99
|
* `ork grafo indexar` carrega (`carregarAnalisadores`), entao diz o que o indexar faria, inclusive no
|
|
@@ -96,7 +101,7 @@ function correcaoDosAnalisadores(motivo, versao) {
|
|
|
96
101
|
* fase. Fora da familia do grafo so o `index.ts` e o worker do MCP a abrem (fronteira do KG1), entao o
|
|
97
102
|
* `index.ts` passa este check ao doctor, que nao importa o grafo.
|
|
98
103
|
*/
|
|
99
|
-
function checarAnalisadoresDoGrafo(versao, carregar = intelligence_graph_parsers_1.carregarAnalisadores, versoes = intelligence_graph_parsers_1.versoesDosAnalisadores) {
|
|
104
|
+
function checarAnalisadoresDoGrafo(versao, carregar = intelligence_graph_parsers_1.carregarAnalisadores, versoes = intelligence_graph_parsers_1.versoesDosAnalisadores, redacao = exports.REDACAO_DOS_ANALISADORES) {
|
|
100
105
|
const nome = 'analisadores do grafo';
|
|
101
106
|
try {
|
|
102
107
|
carregar();
|
|
@@ -104,9 +109,7 @@ function checarAnalisadoresDoGrafo(versao, carregar = intelligence_graph_parsers
|
|
|
104
109
|
}
|
|
105
110
|
catch (e) {
|
|
106
111
|
const motivo = e.message;
|
|
107
|
-
return { nome, nivel: 'warn',
|
|
108
|
-
detalhe: `${motivo}: o ork grafo indexar recusa nesta instalacao, e sem o indice as consultas e as tools ork_grafo_* tambem`,
|
|
109
|
-
correcao: correcaoDosAnalisadores(motivo, versao) };
|
|
112
|
+
return { nome, nivel: 'warn', detalhe: redacao.recusa(motivo), correcao: correcaoDosAnalisadores(motivo, versao, redacao) };
|
|
110
113
|
}
|
|
111
114
|
}
|
|
112
115
|
/** O mesmo schema da amostra do KG2: as amostras auditadas continuam validas. */
|
|
@@ -366,7 +369,10 @@ function semIndiceDoHead(ctx, original) {
|
|
|
366
369
|
}
|
|
367
370
|
throw original;
|
|
368
371
|
}
|
|
369
|
-
/**
|
|
372
|
+
/**
|
|
373
|
+
* O indice do HEAD pronto para consulta, com a concessao local e o cabecalho; `grafo` ja vem filtrado por ela.
|
|
374
|
+
* KG5 fatia 5 (D3): `situacao` so le a arvore quando o cabecalho diz `modificada`; limpa, tudo vale `igual`.
|
|
375
|
+
*/
|
|
370
376
|
function consultavel(ctx) {
|
|
371
377
|
let doHead;
|
|
372
378
|
try {
|
|
@@ -385,7 +391,8 @@ function consultavel(ctx) {
|
|
|
385
391
|
arvore: arvore.motivo === null ? 'limpa' : 'modificada',
|
|
386
392
|
extratores: grafo.snapshot.extractors.map((e) => ({ extractor_id: e.extractor_id, extractor_version: e.extractor_version })),
|
|
387
393
|
};
|
|
388
|
-
|
|
394
|
+
const situacao = cabecalho.arvore === 'limpa' ? intelligence_graph_query_1.TUDO_IGUAL : situacaoNaArvore(arvore.raiz, grafo.snapshot.source_manifest);
|
|
395
|
+
return { g: (0, intelligence_graph_query_1.prepararConsulta)(grafo, concessao), cabecalho, grafo, raiz: arvore.raiz, situacao };
|
|
389
396
|
}
|
|
390
397
|
function responder(ctx, p, r) {
|
|
391
398
|
ctx.escrever(p.bandeiras.has('json') ? (0, intelligence_graph_query_1.jsonDaResposta)(r) : (0, intelligence_graph_query_1.textoDaResposta)(r));
|
|
@@ -430,15 +437,16 @@ function consultar(ctx, p) {
|
|
|
430
437
|
// As opcoes sao conferidas antes de carregar o indice: erro de uso nao custa a leitura do grafo.
|
|
431
438
|
const [a, b] = p.posicionais;
|
|
432
439
|
const profundidade = inteiro(p, 'profundidade'), limite = inteiro(p, 'limite'), s = sentido(p), t = tipos(p), teto = tetoDeBytes(p);
|
|
433
|
-
const { g, cabecalho } = consultavel(ctx);
|
|
440
|
+
const { g, cabecalho, situacao } = consultavel(ctx);
|
|
441
|
+
// A busca binaria do teto pergunta de novo com outro limite; a situacao guarda o que ja leu da arvore.
|
|
434
442
|
const perguntar = (lim) => {
|
|
435
443
|
if (p.sub === 'vizinhos')
|
|
436
|
-
return (0, intelligence_graph_query_1.vizinhos)(g, cabecalho, a, { profundidade, limite: lim, sentido: s, tipos: t });
|
|
444
|
+
return (0, intelligence_graph_query_1.vizinhos)(g, cabecalho, a, { profundidade, limite: lim, sentido: s, tipos: t }, situacao);
|
|
437
445
|
if (p.sub === 'chamadores')
|
|
438
|
-
return (0, intelligence_graph_query_1.chamadores)(g, cabecalho, a, { profundidade, limite: lim });
|
|
446
|
+
return (0, intelligence_graph_query_1.chamadores)(g, cabecalho, a, { profundidade, limite: lim }, situacao);
|
|
439
447
|
if (p.sub === 'importadores')
|
|
440
|
-
return (0, intelligence_graph_query_1.importadores)(g, cabecalho, a, { profundidade, limite: lim });
|
|
441
|
-
return (0, intelligence_graph_query_1.caminho)(g, cabecalho, a, b, { sentido: s, tipos: t });
|
|
448
|
+
return (0, intelligence_graph_query_1.importadores)(g, cabecalho, a, { profundidade, limite: lim }, situacao);
|
|
449
|
+
return (0, intelligence_graph_query_1.caminho)(g, cabecalho, a, b, { sentido: s, tipos: t }, situacao);
|
|
442
450
|
};
|
|
443
451
|
if (teto === undefined)
|
|
444
452
|
return responder(ctx, p, perguntar(limite));
|
|
@@ -452,18 +460,21 @@ function contexto(ctx, p) {
|
|
|
452
460
|
if (!ctx.contextoDaThread)
|
|
453
461
|
throw new intelligence_graph_query_1.ErroDeConsulta('grafo.contexto.indisponivel', 'borda sem leitor da thread');
|
|
454
462
|
const lido = ctx.contextoDaThread(p.posicionais[0]);
|
|
455
|
-
const { grafo, cabecalho } = consultavel({ ...ctx, raiz: lido.raiz });
|
|
463
|
+
const { grafo, cabecalho, situacao } = consultavel({ ...ctx, raiz: lido.raiz });
|
|
456
464
|
if (lido.head !== cabecalho.revision)
|
|
457
465
|
throw new intelligence_graph_query_1.ErroDeConsulta('grafo.contexto.revisao-mudou', 'HEAD mudou durante a leitura; repita a consulta');
|
|
458
|
-
const json = (0, intelligence_graph_contexto_1.pacoteDeContexto)(grafo, cabecalho, lido.entrada, teto);
|
|
466
|
+
const json = (0, intelligence_graph_contexto_1.pacoteDeContexto)(grafo, cabecalho, lido.entrada, teto, situacao);
|
|
459
467
|
if (p.bandeiras.has('json'))
|
|
460
468
|
ctx.escrever(json);
|
|
461
469
|
else {
|
|
462
470
|
const r = JSON.parse(json);
|
|
471
|
+
// KG5 fatia 5 (D9): o resumo conta as ligacoes cuja fonte mudou na arvore, como o JSON marca.
|
|
472
|
+
const mudadas = r.arestas.filter((a) => a.arvore === 'modificada').length;
|
|
463
473
|
ctx.escrever([`Contexto ${r.thread} @ ${r.indice.revision}`,
|
|
464
474
|
` ${r.sementes.length}/${r.total_sementes} sementes, ${r.arestas.length}/${r.total_ligacoes} ligacoes agregadas; ${r.medida.pacote_bytes} bytes JSON`,
|
|
465
475
|
` tamanho dos arquivos no pacote (nao descoberta): ${r.medida.leitura_crua_bytes} bytes (revisao indexada); tokens: unavailable`,
|
|
466
476
|
` diff: ${r.fontes.diff}`,
|
|
477
|
+
...(mudadas ? [` ligacoes com fonte modificada na arvore: ${mudadas}`] : []),
|
|
467
478
|
` truncado: ${r.truncado}; arvore: ${r.indice.arvore}; use --json para nos e evidencias`, json].join('\n'));
|
|
468
479
|
}
|
|
469
480
|
return 0;
|
|
@@ -507,6 +518,66 @@ function leitorDaArvore(raiz, grafo) {
|
|
|
507
518
|
return bytes;
|
|
508
519
|
};
|
|
509
520
|
}
|
|
521
|
+
/**
|
|
522
|
+
* KG5 fatia 5 (D3, D6): a situacao de cada fonte do indice na arvore, pelos bytes. So caminho do manifesto;
|
|
523
|
+
* sem seguir link, nem no ultimo nome nem numa pasta do caminho: o caminho real tem de ser o proprio caminho
|
|
524
|
+
* indexado dentro da raiz (CHECK rodada 1, A1). So arquivo regular; tamanho diferente do indexado e
|
|
525
|
+
* `modificada` sem ler, e o sha256 dos bytes decide o resto, lidos ate o tamanho indexado mais um. O arquivo
|
|
526
|
+
* aberto tem de ser o mesmo do `lstat` (dispositivo e inode, CHECK rodada 1, S3): troca entre a conferencia e
|
|
527
|
+
* a abertura vira `ausente`; duas trocas no intervalo ainda passam, e revelariam so se os bytes sao os
|
|
528
|
+
* indexados. Qualquer falha e `ausente`: nao ha arquivo regular legivel no caminho. Cada caminho e lido uma
|
|
529
|
+
* vez, sob demanda, e nada e escrito.
|
|
530
|
+
*/
|
|
531
|
+
function situacaoNaArvore(raiz, manifesto) {
|
|
532
|
+
const porCaminho = new Map(manifesto.map((m) => [m.path, m]));
|
|
533
|
+
const vistas = new Map();
|
|
534
|
+
let raizReal = null;
|
|
535
|
+
const ler = (p) => {
|
|
536
|
+
const m = porCaminho.get(p);
|
|
537
|
+
if (!m)
|
|
538
|
+
return 'ausente';
|
|
539
|
+
const absoluto = path.join(raiz, p);
|
|
540
|
+
try {
|
|
541
|
+
raizReal ??= fs.realpathSync(raiz);
|
|
542
|
+
const st = fs.lstatSync(absoluto, { throwIfNoEntry: false });
|
|
543
|
+
if (!st || !st.isFile())
|
|
544
|
+
return 'ausente';
|
|
545
|
+
// CHECK rodada 2 (R2-S1): caminho de manifesto adulterado com `..` coincidiria com o real fora da raiz.
|
|
546
|
+
const real = fs.realpathSync(absoluto);
|
|
547
|
+
if (real !== path.join(raizReal, p) || !real.startsWith(raizReal + path.sep))
|
|
548
|
+
return 'ausente';
|
|
549
|
+
if (st.size !== m.size_bytes)
|
|
550
|
+
return 'modificada';
|
|
551
|
+
const fd = fs.openSync(absoluto, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK);
|
|
552
|
+
try {
|
|
553
|
+
const aberto = fs.fstatSync(fd);
|
|
554
|
+
if (!aberto.isFile() || aberto.dev !== st.dev || aberto.ino !== st.ino)
|
|
555
|
+
return 'ausente';
|
|
556
|
+
if (aberto.size !== m.size_bytes)
|
|
557
|
+
return 'modificada';
|
|
558
|
+
const bytes = Buffer.alloc(m.size_bytes + 1);
|
|
559
|
+
let lidos = 0;
|
|
560
|
+
for (let n = 1; n > 0 && lidos < bytes.length; lidos += n)
|
|
561
|
+
n = fs.readSync(fd, bytes, lidos, bytes.length - lidos, null);
|
|
562
|
+
return lidos === m.size_bytes && sha256(bytes.subarray(0, lidos)) === m.source_hash ? 'igual' : 'modificada';
|
|
563
|
+
}
|
|
564
|
+
finally {
|
|
565
|
+
fs.closeSync(fd);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
catch {
|
|
569
|
+
return 'ausente';
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
return (p) => {
|
|
573
|
+
const vista = vistas.get(p);
|
|
574
|
+
if (vista !== undefined)
|
|
575
|
+
return vista;
|
|
576
|
+
const s = ler(p);
|
|
577
|
+
vistas.set(p, s);
|
|
578
|
+
return s;
|
|
579
|
+
};
|
|
580
|
+
}
|
|
510
581
|
const localizadorDe = (g) => new Map(g.nodes.map((n) => [n.node_id, { kind: n.kind, path: n.locator.path, fragment: n.locator.fragment }]));
|
|
511
582
|
const rotulo = (l) => `${l.kind}:${l.path}${l.fragment === null ? '' : `#${l.fragment}`}`;
|
|
512
583
|
const chaveDaAresta = (kind, de, para) => `${kind} ${rotulo(de)} -> ${rotulo(para)}`;
|