@orkastery/cli 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/adapters/claude-code/README.md +11 -2
- package/adapters/claude-code/commands/onboarding.md +3 -2
- package/adapters/openclaw/README.md +11 -0
- package/adapters/openclaw/dist/index.js +1 -1
- package/adapters/openclaw/openclaw.plugin.json +1 -1
- package/adapters/openclaw/package.json +1 -1
- package/adapters/openclaw/src/index.ts +1 -1
- package/dist/adapters/claude-bg.js +20 -6
- package/dist/auditrun.js +1 -1
- package/dist/claim-lint.js +3 -1
- package/dist/docs.js +71 -3
- package/dist/doctor.js +209 -32
- package/dist/entrega-pr.js +7 -0
- package/dist/fabrica-estado.js +21 -3
- package/dist/fix.js +3 -3
- package/dist/hitl-resumo.js +24 -1
- package/dist/hosts.js +7 -3
- package/dist/index.js +42 -14
- package/dist/init.js +11 -5
- package/dist/integracoes-locais.js +2 -0
- package/dist/intelligence-graph-cli.js +181 -27
- package/dist/intelligence-graph-contract.js +71 -12
- package/dist/intelligence-graph-extract-md.js +41 -23
- package/dist/intelligence-graph-extract-ts.js +181 -11
- package/dist/intelligence-graph-extract.js +186 -9
- package/dist/intelligence-graph-index.js +205 -24
- package/dist/intelligence-graph-repo.js +10 -0
- package/dist/leases.js +16 -0
- package/dist/ledger.js +9 -0
- package/dist/licoes.js +5 -4
- package/dist/liveness.js +2 -1
- package/dist/maestro-runtime.js +3 -0
- package/dist/manifest.js +24 -0
- package/dist/mcp-artifacts.js +2 -0
- package/dist/mcp-git.js +60 -12
- package/dist/mcp-grafo-worker.js +91 -0
- package/dist/mcp-grafo.js +233 -0
- package/dist/mcp-install.js +2 -1
- package/dist/mcp-server.js +9 -0
- package/dist/mcp-ship.js +2 -0
- package/dist/mcp-verify.js +3 -1
- package/dist/network-roadmap.js +5 -5
- package/dist/ocupacao.js +6 -2
- package/dist/onboarding.js +9 -4
- package/dist/orquestracao.js +2 -1
- package/dist/parado-no-condutor.js +933 -0
- package/dist/policies.js +27 -2
- package/dist/prova-ativacao.js +345 -0
- package/dist/pulse-delivery.js +27 -6
- package/dist/pulse.js +53 -3
- package/dist/roadmap-status.js +142 -7
- package/dist/runtime-context.js +5 -1
- package/dist/sessoes-inventario.js +7 -1
- package/dist/sessoes.js +4 -1
- package/dist/ship.js +25 -1
- package/dist/thread.js +21 -2
- package/dist/util.js +71 -4
- package/dist/verify.js +2 -2
- package/dist/worktree.js +29 -6
- package/eval/casos/onboarding.json +1 -1
- package/package.json +1 -1
- package/skills/core/onboarding/SKILL.md +3 -2
- package/skills/phases/ship-release/SKILL.md +6 -3
|
@@ -50,6 +50,15 @@ exige Linux, Python 3/pexpect e OAuth de assinatura em `ANTHROPIC_TOKEN` no arqu
|
|
|
50
50
|
permanece fora da fixture. As sessões e os ledgers temporários são descartados;
|
|
51
51
|
guarde a saída JSON como recibo antes de declarar a prova concluída.
|
|
52
52
|
|
|
53
|
+
**Prova de ativação do Maestro (RM-032).** `node core/scripts/prova-ativacao.cjs claude-code`
|
|
54
|
+
instala o plugin e o `.mcp.json` desta cópia num projeto descartável e abre uma sessão nova
|
|
55
|
+
`claude -p "orkastery maestro"` com `--plugin-dir`, `--mcp-config --strict-mcp-config`,
|
|
56
|
+
`--setting-sources project` e `--no-session-persistence`. Usa o login nativo do CLI, sem copiar
|
|
57
|
+
credencial e sem gravar no registro global de plugins. Aprova só quando o modelo chama
|
|
58
|
+
`mcp__orkastery__ork_maestro` (o `ork maestro` pelo Bash é desvio) e o snapshot é do projeto
|
|
59
|
+
descartável; o recibo traz o sha256 dos arquivos globais antes e depois. Modelo padrão: `sonnet`
|
|
60
|
+
(`--modelo` troca). Detalhes no [contrato do snapshot](../../docs/referencia/contratos/maestro-i32.md).
|
|
61
|
+
|
|
53
62
|
## O que voce ganha
|
|
54
63
|
|
|
55
64
|
| Voce digita | Voce recebe |
|
|
@@ -165,8 +174,8 @@ tres e o `--dry-run` mostra o que ele vai fazer sobre cada um.
|
|
|
165
174
|
(`core/`, `phases/`, `reviewers/`, ...) que este catalogo usa. Por isso `plugin.json` declara os
|
|
166
175
|
caminhos um a um. O teste compara esses caminhos com o catálogo fonte e exige
|
|
167
176
|
`onboarding`, acrescentada por I15. Neste catálogo, `claude plugin details orkastery`
|
|
168
|
-
deve reportar
|
|
169
|
-
separadamente skills e comandos.
|
|
177
|
+
deve reportar 28 entradas: 20 skills e 8 comandos (a linha `Skills (28)`). O startup registra
|
|
178
|
+
separadamente skills e comandos. As provas anteriores, com 17 e 18 skills, são históricas.
|
|
170
179
|
Confira também os seis agentes e seis eventos de hooks, sem erros de carregamento.
|
|
171
180
|
|
|
172
181
|
2. **Duas copias do catalogo divergem, e a divergencia so aparece quando ja custou uma entrega.**
|
|
@@ -12,8 +12,9 @@ etapas ou validação neste host e não invente respostas em nome do builder.
|
|
|
12
12
|
|
|
13
13
|
Grave a resposta pública solicitada com `ork onboarding set <etapa> --conteudo <JSON> --por <quem>`.
|
|
14
14
|
Transporte o JSON como argumento, com escaping de shell adequado; nunca execute o texto recebido.
|
|
15
|
-
Peça somente nomes de variáveis para credenciais. Valores secretos ficam
|
|
16
|
-
não leia
|
|
15
|
+
Peça somente nomes de variáveis para credenciais. Valores secretos ficam no ambiente do processo
|
|
16
|
+
ou no cofre do host (no Hermes, `~/.hermes/.env`); não os leia nem transporte valores para a
|
|
17
|
+
conversa, onboarding ou ledger.
|
|
17
18
|
|
|
18
19
|
Reset solicitado usa `ork onboarding reset [etapa]`. Publicação opcional usa
|
|
19
20
|
`ork onboarding sync --json`; apresente o motivo tipado de degradação sem bloquear a entrevista.
|
|
@@ -97,6 +97,17 @@ no incidente de 29/09. As outras tools continuam sob escolha do operador:
|
|
|
97
97
|
`tools.alsoAllow: ["orkastery"]` no `openclaw.json` libera todas; allowlist e deny do operador
|
|
98
98
|
sempre valem.
|
|
99
99
|
|
|
100
|
+
## Prova de ativação do Maestro (RM-032)
|
|
101
|
+
|
|
102
|
+
`node core/scripts/prova-ativacao.cjs openclaw` instala esta extensão por
|
|
103
|
+
`ork adapter install openclaw --dir <estado temporário>`, a raiz global de uma cópia descartável
|
|
104
|
+
(`OPENCLAW_STATE_DIR` e `OPENCLAW_CONFIG_PATH` temporários, segredo do provedor só por SecretRef,
|
|
105
|
+
nada em `~/.openclaw`), e roda `openclaw agent --local` com `orkastery maestro` num workspace sem
|
|
106
|
+
manifesto, o cenário do incidente de 29/09. Aprova quando o modelo chama `ork_network_roadmap`
|
|
107
|
+
(ou `ork_maestro` com projeto) e o resultado é o do projeto descartável. A prova não roda
|
|
108
|
+
`openclaw plugins install --force` nem mexe em `plugins.allow`: o aviso de procedência vai ao
|
|
109
|
+
recibo, e a revisão fica com o dono.
|
|
110
|
+
|
|
100
111
|
## A #TAG de conducao
|
|
101
112
|
|
|
102
113
|
O manifesto novo do OpenClaw nao tem campo proprio para isso, entao a regra vive aqui e
|
|
@@ -205,7 +205,7 @@ const FERRAMENTAS = [
|
|
|
205
205
|
},
|
|
206
206
|
{
|
|
207
207
|
name: 'ork_onboarding',
|
|
208
|
-
description: 'Consulta a pauta do núcleo e registra respostas públicas. Credenciais somente como nomes de variáveis; valores ficam
|
|
208
|
+
description: 'Consulta a pauta do núcleo e registra respostas públicas. Credenciais somente como nomes de variáveis; valores ficam no ambiente do processo ou no cofre do host (no Hermes, ~/.hermes/.env). Sync publica opcionalmente e relata degradação.',
|
|
209
209
|
parameters: {
|
|
210
210
|
type: 'object', additionalProperties: false, required: ['acao'],
|
|
211
211
|
properties: {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "orkastery",
|
|
3
3
|
"name": "Orkastery",
|
|
4
4
|
"description": "Conducao de looping threads em 6 fases pelo nucleo `ork`, exposta ao OpenClaw como tools `ork_*`. Zero regra de negocio no host: cada tool e uma chamada de CLI.",
|
|
5
|
-
"version": "0.5.
|
|
5
|
+
"version": "0.5.1",
|
|
6
6
|
"configSchema": {
|
|
7
7
|
"type": "object",
|
|
8
8
|
"properties": {},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orkastery/openclaw-plugin",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Plugin Orkastery para OpenClaw: 27 tools ork_* que traduzem intencao em chamada de CLI do nucleo `ork`. Zero regra de negocio no host.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": true,
|
|
@@ -220,7 +220,7 @@ const FERRAMENTAS: FerramentaOrk[] = [
|
|
|
220
220
|
},
|
|
221
221
|
{
|
|
222
222
|
name: 'ork_onboarding',
|
|
223
|
-
description: 'Consulta a pauta do núcleo e registra respostas públicas. Credenciais somente como nomes de variáveis; valores ficam
|
|
223
|
+
description: 'Consulta a pauta do núcleo e registra respostas públicas. Credenciais somente como nomes de variáveis; valores ficam no ambiente do processo ou no cofre do host (no Hermes, ~/.hermes/.env). Sync publica opcionalmente e relata degradação.',
|
|
224
224
|
parameters: {
|
|
225
225
|
type: 'object', additionalProperties: false, required: ['acao'],
|
|
226
226
|
properties: {
|
|
@@ -71,6 +71,7 @@ const runtime_ambiente_1 = require("../runtime-ambiente");
|
|
|
71
71
|
const path = __importStar(require("node:path"));
|
|
72
72
|
const runtime_context_1 = require("../runtime-context");
|
|
73
73
|
const runtime_profiles_1 = require("../runtime-profiles");
|
|
74
|
+
const mcp_grafo_1 = require("../mcp-grafo");
|
|
74
75
|
function exec(cmd, args, cwd, timeoutMs, ambiente = (0, runtime_ambiente_1.ambienteDeAssinatura)()) {
|
|
75
76
|
return (0, util_1.exec)(cmd, args, cwd, timeoutMs, ambiente);
|
|
76
77
|
}
|
|
@@ -107,8 +108,14 @@ const MUTACOES_WORKTREE = [
|
|
|
107
108
|
'mcp__orkastery__ork_artifact_write', 'mcp__orkastery__ork_claim_add', 'mcp__orkastery__ork_decision_record',
|
|
108
109
|
'mcp__orkastery__ork_git_commit', 'mcp__orkastery__ork_verify', 'mcp__orkastery__ork_ship',
|
|
109
110
|
];
|
|
111
|
+
/**
|
|
112
|
+
* RM-031 KG5 (D7): as consultas do grafo, de leitura, so com a flag do manifesto da raiz; sem ela, nada muda.
|
|
113
|
+
* Montada na chamada: o `mcp-grafo` chega a este modulo pelo `mcp-git`, e no ciclo de imports o valor pode
|
|
114
|
+
* ainda nao existir quando este modulo carrega.
|
|
115
|
+
*/
|
|
116
|
+
const consultasDoGrafoMcp = () => mcp_grafo_1.TOOLS_DO_GRAFO.map((nome) => `mcp__orkastery__${nome}`);
|
|
110
117
|
/** Regras nativas de arquivo, nao sandbox de processos. Edit tambem cobre Write. */
|
|
111
|
-
function permissoesDaWorktree(cwd, permiteEditarProduto) {
|
|
118
|
+
function permissoesDaWorktree(cwd, permiteEditarProduto, consultas) {
|
|
112
119
|
if (!path.isAbsolute(cwd) || cwd === '/' || path.normalize(cwd) !== cwd || !/^[A-Za-z0-9_./-]+$/.test(cwd))
|
|
113
120
|
throw Error('runtime.context.permissions: worktree contem caracteres ambiguos para regras nativas; use caminho sem glob, espacos ou delimitadores');
|
|
114
121
|
const absoluto = '/' + cwd; // Claude exige // para caminho absoluto.
|
|
@@ -117,7 +124,7 @@ function permissoesDaWorktree(cwd, permiteEditarProduto) {
|
|
|
117
124
|
for (const prefixo of [`${absoluto}/${nome}`, `${absoluto}/**/${nome}`])
|
|
118
125
|
deny.push(`Edit(${prefixo})`, `Edit(${prefixo}/**)`);
|
|
119
126
|
}
|
|
120
|
-
return { allow: [...
|
|
127
|
+
return { allow: [...consultas, 'mcp__orkastery__ork_git_status', ...MUTACOES_WORKTREE, ...(permiteEditarProduto ? [`Edit(${absoluto}/**)`] : [])], deny };
|
|
121
128
|
}
|
|
122
129
|
/** O runtime esta disponivel nesta maquina? */
|
|
123
130
|
function disponivel() {
|
|
@@ -152,13 +159,14 @@ function montarComando(pedido) {
|
|
|
152
159
|
if (pedido.contextoRuntime.host !== 'claude-code')
|
|
153
160
|
throw Error('runtime.context.invalid: host Claude esperado');
|
|
154
161
|
const contexto = (0, runtime_context_1.validarContextoRuntime)(pedido.contextoRuntime, pedido.cwd);
|
|
162
|
+
const consultas = [...CONSULTAS_MCP, ...(contexto.grafoMcp ? consultasDoGrafoMcp() : [])];
|
|
155
163
|
// Somente o servidor gerado: nao mesclar MCP de outros escopos nesta sessao filha.
|
|
156
164
|
const permissoes = contexto.permissoesFilho === 'worktree'
|
|
157
|
-
? permissoesDaWorktree(pedido.cwd, contexto.permiteEditarProduto && !plano) : null;
|
|
165
|
+
? permissoesDaWorktree(pedido.cwd, contexto.permiteEditarProduto && !plano, consultas) : null;
|
|
158
166
|
// PLAN não implementa: nem commit, nem claim, nem verify, nem ship, em nenhum perfil. Decidir não é
|
|
159
167
|
// implementar, e o PLAN é onde as decisões nascem (RM-037, achado S7 do CHECK): a decisão autônoma fica.
|
|
160
|
-
const allow = plano ? [...
|
|
161
|
-
: permissoes?.allow ??
|
|
168
|
+
const allow = plano ? [...consultas, 'mcp__orkastery__ork_artifact_write', 'mcp__orkastery__ork_decision_record']
|
|
169
|
+
: permissoes?.allow ?? consultas;
|
|
162
170
|
const deny = [...(plano ? ESCRITA_DE_ARQUIVO : []), ...(permissoes?.deny ?? [])];
|
|
163
171
|
args.push('--plugin-dir', contexto.instalacao, '--strict-mcp-config', '--mcp-config', JSON.stringify({ mcpServers: { orkastery: contexto.servidor } }), '--allowedTools', allow.join(','));
|
|
164
172
|
if (deny.length)
|
|
@@ -189,12 +197,18 @@ function extrairSessionId(saida) {
|
|
|
189
197
|
function listarSessoes(cwd, todas = false, ambiente = (0, runtime_ambiente_1.ambienteDeAssinatura)()) {
|
|
190
198
|
return consultarSessoes(cwd, todas, ambiente).sessoes;
|
|
191
199
|
}
|
|
192
|
-
/**
|
|
200
|
+
/**
|
|
201
|
+
* Falha de consulta não pode ser confundida com uma máquina sem sessões. O binário ausente segue
|
|
202
|
+
* falha aqui (o observador e o pulse não concluem nada dele); só o inventário o trata como fonte
|
|
203
|
+
* ausente, com a correção.
|
|
204
|
+
*/
|
|
193
205
|
function consultarSessoes(cwd, todas = false, ambiente = (0, runtime_ambiente_1.ambienteDeAssinatura)()) {
|
|
194
206
|
const args = ['agents', '--json'];
|
|
195
207
|
if (todas)
|
|
196
208
|
args.push('--all');
|
|
197
209
|
const r = exec('claude', args, undefined, 60000, ambiente);
|
|
210
|
+
if (r.error === 'ENOENT')
|
|
211
|
+
return { ok: false, ausente: true, sessoes: [], detalhe: 'binário `claude` fora do PATH' };
|
|
198
212
|
if (!r.ok)
|
|
199
213
|
return { ok: false, sessoes: [], detalhe: `claude agents falhou (código ${r.code})` };
|
|
200
214
|
let dados;
|
package/dist/auditrun.js
CHANGED
|
@@ -798,7 +798,7 @@ function textoDaRodada(raiz, id) {
|
|
|
798
798
|
L.push('');
|
|
799
799
|
if (r.veredito) {
|
|
800
800
|
L.push(` Claims do auditor: ${r.veredito.ok ? 'SUSTENTADAS' : 'REPROVADAS'} ` +
|
|
801
|
-
`(motivos: ${r.veredito.motivos.join(', ') || 'nenhum'}) no HEAD ${r.veredito.commit
|
|
801
|
+
`(motivos: ${r.veredito.motivos.join(', ') || 'nenhum'}) no HEAD ${(0, util_1.shaCurto)(r.veredito.commit)}`);
|
|
802
802
|
}
|
|
803
803
|
else {
|
|
804
804
|
L.push(` Claims do auditor: NAO verificadas. Rode: ork audit verify ${r.id}`);
|
package/dist/claim-lint.js
CHANGED
|
@@ -20,7 +20,9 @@ exports.analisarComando = analisarComando;
|
|
|
20
20
|
exports.analisarComandos = analisarComandos;
|
|
21
21
|
exports.linhaDoLintDeClaim = linhaDoLintDeClaim;
|
|
22
22
|
const CORRECAO = {
|
|
23
|
-
|
|
23
|
+
// Fatia 2 do ensaio da 0.5.0 (P6): a correcao vale para qualquer projeto; o script do Orkastery saiu daqui.
|
|
24
|
+
'suite-inteira': 'rode só o teste da claim (`node --test` no arquivo do teste, ou o comando do projeto que roda um arquivo) ' +
|
|
25
|
+
'ou um script hermético do projeto',
|
|
24
26
|
'sha-intermediario': 'ancore na base (`git diff "$(git merge-base origin/main HEAD)" HEAD`) ou prove ancestralidade com `git merge-base --is-ancestor <sha> HEAD`',
|
|
25
27
|
'contagem-de-commits': 'use `git merge-base --is-ancestor <commit> HEAD`: contagem de commits quebra com rebase e merge',
|
|
26
28
|
};
|
package/dist/docs.js
CHANGED
|
@@ -46,6 +46,7 @@ exports.renderizarEstado = renderizarEstado;
|
|
|
46
46
|
exports.textoDaVerificacao = textoDaVerificacao;
|
|
47
47
|
exports.escreverYaml = escreverYaml;
|
|
48
48
|
exports.mergeDaThread = mergeDaThread;
|
|
49
|
+
exports.mergesDaThreadNoGit = mergesDaThreadNoGit;
|
|
49
50
|
exports.escopoPadraoDoSync = escopoPadraoDoSync;
|
|
50
51
|
exports.sincronizarDocs = sincronizarDocs;
|
|
51
52
|
exports.tabelaDoRoadmap = tabelaDoRoadmap;
|
|
@@ -262,9 +263,12 @@ function verificarDocs(raiz, opcoes = {}) {
|
|
|
262
263
|
verificarFontes(raiz, d, comandos, achados);
|
|
263
264
|
if (d.tipo === 'roadmap') {
|
|
264
265
|
verificarEstadoDoRoadmap(raiz, d, base, achados);
|
|
266
|
+
if (base)
|
|
267
|
+
verificarMergeDaThread(raiz, d, base, achados, opcoes.pr === true);
|
|
265
268
|
verificarTabelasDeEstado(d, achados);
|
|
266
269
|
}
|
|
267
270
|
}
|
|
271
|
+
verificarIndices(raiz, docs, achados, opcoes.pr === true);
|
|
268
272
|
return { docs, achados };
|
|
269
273
|
}
|
|
270
274
|
function verificarChaves(d, achados) {
|
|
@@ -564,6 +568,57 @@ function verificarEstadoDoRoadmap(raiz, d, base, achados) {
|
|
|
564
568
|
}
|
|
565
569
|
}
|
|
566
570
|
}
|
|
571
|
+
/**
|
|
572
|
+
* RM-037 (fatia 3, defeito 1): o outro sentido da paridade com o git. Depois do merge do PR, o
|
|
573
|
+
* frontmatter da RM-049 (#26) e da RM-051 (#33) seguiu em "Branch criada", e o verificador dizia OK:
|
|
574
|
+
* ele so conferia que `Mesclado` tem o commit na base. O merge desta fabrica e pelo GitHub, sem o
|
|
575
|
+
* `ork ship` no caminho, entao a prova mora aqui, no job que o CI obrigatorio roda. A regra usa o
|
|
576
|
+
* mesmo `ship(<thread>)` do `sincronizarDocs`: tudo o que ela acusa o `sincronizar` corrige.
|
|
577
|
+
*/
|
|
578
|
+
function verificarMergeDaThread(raiz, d, base, achados, pr = false) {
|
|
579
|
+
const sdlc = ehMapa(d.dados.sdlc) ? d.dados.sdlc : null;
|
|
580
|
+
const thread = sdlc && typeof sdlc.thread === 'string' ? sdlc.thread.trim() : '';
|
|
581
|
+
if (!thread)
|
|
582
|
+
return;
|
|
583
|
+
const merges = mergesDaThreadNoGit(raiz, thread, base);
|
|
584
|
+
if (merges.length === 0)
|
|
585
|
+
return;
|
|
586
|
+
const codigo = String((ehMapa(d.dados.estado) ? d.dados.estado.codigo : null) ?? '');
|
|
587
|
+
if (codigo === 'Mesclado')
|
|
588
|
+
return;
|
|
589
|
+
achados.push((pr ? aviso : erro)(d.arquivo, d.id, 'docs.paridade.merge', `a thread ${thread} entrou na ${base} pelo merge ${merges[0].slice(0, 7)} (ship(${thread})), e o estado.codigo diz "${codigo || 'vazio'}"` +
|
|
590
|
+
(pr ? ' (no PR, aviso: o push da main reprova)' : ''), `abra um PR de docs sobre a ${base} atualizada com ork docs sincronizar --escrever --so ${d.id} (o item e os índices); ` +
|
|
591
|
+
`se o item tem outra fatia em curso, aponte sdlc.thread para a thread dela`));
|
|
592
|
+
}
|
|
593
|
+
/** Os indices gerados (`docs/roadmap/README.md`, `docs/produto/README.md`) e o que o frontmatter diz. */
|
|
594
|
+
const INDICES_GERADOS = [
|
|
595
|
+
[`${exports.DIR_ROADMAP}/README.md`, (docs) => tabelaDoRoadmap(docs)],
|
|
596
|
+
[`${exports.DIR_PRODUTO}/README.md`, (docs) => tabelaDoProduto(docs)],
|
|
597
|
+
];
|
|
598
|
+
/**
|
|
599
|
+
* RM-037 (fatia 3, defeito 1): o indice e uma vista do frontmatter, como as tabelas do corpo, e mentia
|
|
600
|
+
* do mesmo jeito: sem a RM-051 e com a RM-031 de antes. Divergente reprova; vazio (o modelo recem-copiado
|
|
601
|
+
* pelo `ork docs init`) so avisa; sem os marcadores, nao ha indice gerado para conferir.
|
|
602
|
+
*/
|
|
603
|
+
function verificarIndices(raiz, docs, achados, pr = false) {
|
|
604
|
+
for (const [arquivo, gerar] of INDICES_GERADOS) {
|
|
605
|
+
const abs = path.join(raiz, arquivo);
|
|
606
|
+
if (!fs.existsSync(abs))
|
|
607
|
+
continue;
|
|
608
|
+
// Checkout com CRLF (Windows com core.autocrlf) nao e divergencia: as paginas tambem normalizam.
|
|
609
|
+
const atual = blocoEntre(fs.readFileSync(abs, 'utf8').replace(/\r\n/g, '\n'), INICIO, FIM);
|
|
610
|
+
if (atual === null)
|
|
611
|
+
continue;
|
|
612
|
+
if (atual === '') {
|
|
613
|
+
if (docs.length > 0) {
|
|
614
|
+
achados.push(aviso(arquivo, undefined, 'docs.paridade.indice', 'o índice ainda não foi gerado', 'rode ork docs sincronizar --escrever'));
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
else if (atual !== gerar(docs)) {
|
|
618
|
+
achados.push((pr ? aviso : erro)(arquivo, undefined, 'docs.paridade.indice', `o índice diverge do frontmatter das páginas${pr ? ' (no PR, aviso: o push da main reprova)' : ''}`, 'o índice é gerado do frontmatter: rode ork docs sincronizar --escrever e commite o índice'));
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
567
622
|
// --------------------------------------------------------------------------------------------
|
|
568
623
|
// Saida para humano (TDAH) e para agente
|
|
569
624
|
function textoDaVerificacao(docs, achados) {
|
|
@@ -666,13 +721,26 @@ function mergeDaThread(raiz, thread, base) {
|
|
|
666
721
|
return { sha: String(ship.mergeSha).slice(0, 7), fonte: 'ledger ship_done' };
|
|
667
722
|
}
|
|
668
723
|
catch { /* thread sem estado nesta maquina: cai para o git */ }
|
|
669
|
-
|
|
670
|
-
const log = git(raiz, ['log', base, '--reverse', '--format=%H', '--fixed-strings', `--grep=ship(${thread})`]);
|
|
671
|
-
const primeiro = log.ok ? log.saida.split('\n')[0] : '';
|
|
724
|
+
const primeiro = mergesDaThreadNoGit(raiz, thread, base)[0];
|
|
672
725
|
if (primeiro)
|
|
673
726
|
return { sha: primeiro.slice(0, 7), fonte: `git log ${base}` };
|
|
674
727
|
return null;
|
|
675
728
|
}
|
|
729
|
+
/**
|
|
730
|
+
* Os commits `ship(<thread>)` da base, do mais velho ao mais novo, so pelo git (o CI nao tem ledger).
|
|
731
|
+
* RM-037 (fatia 3): um lugar so para o `sincronizarDocs` e o `verificarDocs` acharem o mesmo merge.
|
|
732
|
+
*/
|
|
733
|
+
function mergesDaThreadNoGit(raiz, thread, base) {
|
|
734
|
+
// Sem `-n 1`: o git corta antes de inverter, e `--reverse -n 1` devolveria o mais recente.
|
|
735
|
+
// GO-FIX 2 (achado 4 do CHECK): so a linha de primeiro pai da base e o assunto que COMECA com
|
|
736
|
+
// `ship(<thread>)`, como no `mergeDaEntrega`. Commit que cita o merge no corpo, ou o Revert dele, nao e merge.
|
|
737
|
+
const log = git(raiz, ['log', base, '--first-parent', '--reverse', '--format=%H%x09%s', '--fixed-strings', `--grep=ship(${thread})`]);
|
|
738
|
+
if (!log.ok)
|
|
739
|
+
return [];
|
|
740
|
+
return log.saida.split('\n').map((l) => l.split('\t'))
|
|
741
|
+
.filter(([sha, assunto]) => !!sha && typeof assunto === 'string' && assunto.startsWith(`ship(${thread})`))
|
|
742
|
+
.map(([sha]) => sha);
|
|
743
|
+
}
|
|
676
744
|
function temBranch(raiz, thread) {
|
|
677
745
|
const r = git(raiz, ['for-each-ref', '--format=%(refname:short)', `refs/heads/ork/${thread}-*`, `refs/remotes/origin/ork/${thread}-*`]);
|
|
678
746
|
return r.ok && r.saida.length > 0;
|
package/dist/doctor.js
CHANGED
|
@@ -39,9 +39,14 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
39
39
|
};
|
|
40
40
|
})();
|
|
41
41
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.TETO_DO_DONO_DO_GIT = void 0;
|
|
42
43
|
exports.checarContas = checarContas;
|
|
43
44
|
exports.checarChaveDeEmbedding = checarChaveDeEmbedding;
|
|
45
|
+
exports.checarDonoDoGit = checarDonoDoGit;
|
|
44
46
|
exports.checarOnboarding = checarOnboarding;
|
|
47
|
+
exports.blocosDoRuntime = blocosDoRuntime;
|
|
48
|
+
exports.checarRuntimeClaude = checarRuntimeClaude;
|
|
49
|
+
exports.checarDespachoPeloCodex = checarDespachoPeloCodex;
|
|
45
50
|
exports.checar = checar;
|
|
46
51
|
exports.relatorio = relatorio;
|
|
47
52
|
exports.doctor = doctor;
|
|
@@ -63,6 +68,7 @@ const project_state_1 = require("./project-state");
|
|
|
63
68
|
const runtime_ambiente_1 = require("./runtime-ambiente");
|
|
64
69
|
const runtime_profiles_1 = require("./runtime-profiles");
|
|
65
70
|
const preflight_1 = require("./preflight");
|
|
71
|
+
const setup_1 = require("./setup");
|
|
66
72
|
/**
|
|
67
73
|
* I-33 (D7): check "contas por runtime". Cada perfil ativo tem o login conferido pelo proprio
|
|
68
74
|
* CLI com o env dele (`claude auth status`, `codex login status`); o doctor so le e relata, sem
|
|
@@ -148,6 +154,81 @@ function checarChaveDeEmbedding(carregado, env = process.env) {
|
|
|
148
154
|
return { nome, nivel: 'warn', detalhe: `${variavel} ausente do ambiente: a busca por significado usa o fallback local ou cai para FTS; o regime nao muda`,
|
|
149
155
|
correcao: `exporte ${variavel} com a chave dedicada ao Orkastery (com limite de credito no painel do provider)` };
|
|
150
156
|
}
|
|
157
|
+
/** Acima disso a conferencia do dono do `.git` fica parcial (aviso), para o doctor nao pesar. */
|
|
158
|
+
exports.TETO_DO_DONO_DO_GIT = 200_000;
|
|
159
|
+
/**
|
|
160
|
+
* RM-037 (fatia 3, defeito 7): git rodado como root no repositorio do dono deixa objeto, pasta e
|
|
161
|
+
* `FETCH_HEAD` com outro dono, e o git do dono deixa de gravar neles: o fetch travou em 29/09 e em
|
|
162
|
+
* 01/10. O dono do repositorio e o dono do diretorio comum do git. O check acusa com a contagem, ate
|
|
163
|
+
* tres exemplos e o `chown` exato; o doctor nunca roda nada. Fora de repositorio git, `null`.
|
|
164
|
+
*/
|
|
165
|
+
function checarDonoDoGit(dirInicial, opcoes = {}) {
|
|
166
|
+
const nome = 'dono do .git';
|
|
167
|
+
const comum = (0, util_1.exec)('git', ['rev-parse', '--git-common-dir'], dirInicial);
|
|
168
|
+
if (!comum.ok || !comum.stdout.trim())
|
|
169
|
+
return null;
|
|
170
|
+
const dirComum = path.resolve(dirInicial, comum.stdout.trim());
|
|
171
|
+
const lstat = opcoes.lstat ?? ((p) => fs.lstatSync(p));
|
|
172
|
+
let raiz;
|
|
173
|
+
try {
|
|
174
|
+
raiz = lstat(dirComum);
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
const teto = opcoes.teto ?? exports.TETO_DO_DONO_DO_GIT;
|
|
180
|
+
const relativo = (f) => path.relative(path.dirname(dirComum), f);
|
|
181
|
+
// A correcao sai pronta para copiar: caminho com caractere fora do conjunto seguro vai entre aspas simples.
|
|
182
|
+
const noShell = (s) => /^[A-Za-z0-9._/-]+$/.test(s) ? s : `'${s.replace(/'/g, "'\\''")}'`;
|
|
183
|
+
const estranhos = [], uids = new Set();
|
|
184
|
+
let total = 0, vistos = 0, parcial = false;
|
|
185
|
+
const pilha = [dirComum];
|
|
186
|
+
while (pilha.length > 0 && !parcial) {
|
|
187
|
+
const atual = pilha.pop();
|
|
188
|
+
let nomes;
|
|
189
|
+
// Pasta que o dono nao consegue listar ja aparece pelo dono dela, na entrada de cima.
|
|
190
|
+
try {
|
|
191
|
+
nomes = fs.readdirSync(atual);
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
for (const n of nomes) {
|
|
197
|
+
if (++vistos > teto) {
|
|
198
|
+
parcial = true;
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
const f = path.join(atual, n);
|
|
202
|
+
let st;
|
|
203
|
+
try {
|
|
204
|
+
st = lstat(f);
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (st.uid !== raiz.uid) {
|
|
210
|
+
total++;
|
|
211
|
+
uids.add(st.uid);
|
|
212
|
+
if (estranhos.length < 3)
|
|
213
|
+
estranhos.push(relativo(f));
|
|
214
|
+
}
|
|
215
|
+
if (st.isDirectory())
|
|
216
|
+
pilha.push(f);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if (total > 0) {
|
|
220
|
+
return { nome, nivel: 'fail',
|
|
221
|
+
detalhe: `${total} entrada(s) de ${relativo(dirComum)} com outro dono (uid ${[...uids].sort((a, b) => a - b).join(', ')}; o do ` +
|
|
222
|
+
`repositorio e ${raiz.uid}): ${estranhos.join(', ')}${total > estranhos.length ? ', ...' : ''}; o git do dono nao grava nelas, ` +
|
|
223
|
+
`e o fetch e o commit falham${parcial ? ` (conferencia parcial: mais de ${teto} entradas)` : ''}`,
|
|
224
|
+
correcao: `sudo chown -R ${raiz.uid}:${raiz.gid} ${noShell(dirComum)} (o ork nao roda isso sozinho)` };
|
|
225
|
+
}
|
|
226
|
+
if (parcial) {
|
|
227
|
+
return { nome, nivel: 'warn', detalhe: `mais de ${teto} entradas em ${relativo(dirComum)}: conferencia parcial, sem outro dono ate aqui`,
|
|
228
|
+
correcao: `confira o resto com: find ${noShell(dirComum)} -not -uid ${raiz.uid}` };
|
|
229
|
+
}
|
|
230
|
+
return { nome, nivel: 'ok', detalhe: `${vistos} entradas de ${relativo(dirComum)} com o dono do repositorio (uid ${raiz.uid})` };
|
|
231
|
+
}
|
|
151
232
|
/** Checks locais; ler a entrevista não abre driver, banco, runtime ou rede. */
|
|
152
233
|
function checarOnboarding(carregado) {
|
|
153
234
|
const estado = (0, onboarding_1.lerOnboarding)(carregado.raiz);
|
|
@@ -163,9 +244,15 @@ function checarOnboarding(carregado) {
|
|
|
163
244
|
checks.push({ nome: 'onboarding memoria', nivel: 'warn', detalhe: `entrevista escolheu ${modo}; manifesto declara ${carregado.manifesto.memory.mode}`,
|
|
164
245
|
correcao: `revise memory.mode: ${modo} em orkastery.yaml; a entrevista não altera o manifesto` });
|
|
165
246
|
}
|
|
166
|
-
// I-35: o fuso respondido na etapa maestro orienta owner.timezone, sem editar o manifesto.
|
|
247
|
+
// I-35: o fuso respondido na etapa maestro orienta owner.timezone, sem editar o manifesto. Com
|
|
248
|
+
// `owner.timezone` na mesma resposta (gravado no manifesto pelo `onboarding set`), vale ele: no
|
|
249
|
+
// ensaio da 0.5.0, o `fuso` legado da resposta anterior mandava desfazer a escolha explicita.
|
|
167
250
|
const maestro = estado.etapas.maestro?.conteudo;
|
|
168
|
-
const
|
|
251
|
+
const owner = maestro && typeof maestro === 'object' && !Array.isArray(maestro) ? maestro.owner : undefined;
|
|
252
|
+
const fusoDoOwner = owner && typeof owner === 'object' && !Array.isArray(owner) ? owner.timezone : undefined;
|
|
253
|
+
// Owner invalido (so por edicao a mao: o `onboarding set` recusa) nao cala o fuso legado.
|
|
254
|
+
const fusoDaEntrevista = maestro && typeof maestro === 'object' && !Array.isArray(maestro)
|
|
255
|
+
? (0, horario_1.normalizarFuso)(fusoDoOwner) ?? (0, horario_1.normalizarFuso)(maestro.fuso) : undefined;
|
|
169
256
|
if (fusoDaEntrevista && fusoDaEntrevista !== carregado.manifesto.owner?.timezone) {
|
|
170
257
|
checks.push({ nome: 'onboarding fuso', nivel: 'warn',
|
|
171
258
|
detalhe: `entrevista informou ${fusoDaEntrevista}; manifesto declara ${carregado.manifesto.owner?.timezone ?? `${horario_1.CHAVE_DO_FUSO} ausente`}`,
|
|
@@ -173,6 +260,97 @@ function checarOnboarding(carregado) {
|
|
|
173
260
|
}
|
|
174
261
|
return checks;
|
|
175
262
|
}
|
|
263
|
+
/**
|
|
264
|
+
* Fatia 2 do ensaio da 0.5.0 (P1): por qual runtime cada bloco dos modos de
|
|
265
|
+
* `conduction.allowed_modes` despacha, pelo mesmo criterio do despacho sem opcao de CLI
|
|
266
|
+
* (`resolverDespacho`: o bloco do setup vence `runtime.adapter`). Quem passou os blocos para o
|
|
267
|
+
* Codex nao precisa do `claude`. Setup ilegivel devolve `null`: quem chama fica no lado seguro.
|
|
268
|
+
*/
|
|
269
|
+
function blocosDoRuntime(carregado, runtime) {
|
|
270
|
+
const { manifesto } = carregado;
|
|
271
|
+
const r = { principal: [], fallback: [] };
|
|
272
|
+
try {
|
|
273
|
+
const setup = (0, setup_1.lerSetup)(carregado.raiz);
|
|
274
|
+
for (const modo of manifesto.conduction.allowed_modes) {
|
|
275
|
+
modos_1.MODOS[modo].blocos.forEach((b, i) => {
|
|
276
|
+
const doBloco = (0, setup_1.configDoBloco)(setup, modo, b.fases[0]);
|
|
277
|
+
const bloco = { tag: modos_1.MODOS[modo].tag, bloco: i + 1 };
|
|
278
|
+
if ((doBloco?.runtime ?? manifesto.runtime.adapter) === runtime)
|
|
279
|
+
r.principal.push(bloco);
|
|
280
|
+
else if (doBloco?.fallback?.some((f) => f.split(':')[0] === runtime))
|
|
281
|
+
r.fallback.push(bloco);
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
catch {
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
return r;
|
|
289
|
+
}
|
|
290
|
+
/** `#Classic 1, 2; #Auto 1`: os blocos agrupados por modo, na ordem dos modos permitidos. */
|
|
291
|
+
function textoDosBlocos(blocos) {
|
|
292
|
+
const porModo = new Map();
|
|
293
|
+
for (const b of blocos)
|
|
294
|
+
porModo.set(b.tag, [...(porModo.get(b.tag) ?? []), b.bloco]);
|
|
295
|
+
return [...porModo].map(([tag, numeros]) => `${tag} ${numeros.join(', ')}`).join('; ');
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Fatia 2 do ensaio da 0.5.0 (P1): o `claude` so e obrigatorio quando algum bloco de modo permitido
|
|
299
|
+
* despacha por ele. Sem manifesto, com manifesto invalido ou com setup ilegivel vale o padrao (todo
|
|
300
|
+
* bloco no claude-bg), e a falta reprova como antes.
|
|
301
|
+
*/
|
|
302
|
+
function checarRuntimeClaude(carregado, claude, versao) {
|
|
303
|
+
const nome = 'runtime claude-bg';
|
|
304
|
+
if (claude)
|
|
305
|
+
return { nome, nivel: 'ok', detalhe: `${claude} (${versao ?? 'versao desconhecida'})` };
|
|
306
|
+
const blocos = carregado && carregado.erros.length === 0 ? blocosDoRuntime(carregado, 'claude-bg') : null;
|
|
307
|
+
if (blocos && blocos.principal.length === 0) {
|
|
308
|
+
// CHECK, rodada 1 (S6): as fases nao precisam dele, mas o `ork audit run` despacha sempre pelo claude-bg.
|
|
309
|
+
return { nome, nivel: 'warn',
|
|
310
|
+
detalhe: 'binario `claude` fora do PATH (opcional para as fases: nenhum bloco dos modos permitidos despacha pelo claude-bg' +
|
|
311
|
+
(blocos.fallback.length
|
|
312
|
+
? `; o fallback de ${blocos.fallback.length} bloco(s) cai nele e falharia: ${textoDosBlocos(blocos.fallback)}` : '') +
|
|
313
|
+
'; o ork audit run ainda despacha por ele)',
|
|
314
|
+
correcao: 'para despachar pelo claude-bg (e rodar ork audit run), instale o Claude Code e garanta `claude` no PATH' };
|
|
315
|
+
}
|
|
316
|
+
return { nome, nivel: 'fail',
|
|
317
|
+
detalhe: 'binario `claude` fora do PATH' + (blocos
|
|
318
|
+
? `; ${blocos.principal.length} bloco(s) dos modos permitidos despacham por ele (${textoDosBlocos(blocos.principal)})` : ''),
|
|
319
|
+
correcao: 'instale o Claude Code e garanta `claude` no PATH; so com o Codex, passe cada bloco para ele ' +
|
|
320
|
+
'(ork setup <modo> --bloco N --runtime codex --model <modelo>) ou tire o modo de conduction.allowed_modes ' +
|
|
321
|
+
'(mantenha o conduction.default_mode entre os permitidos)' };
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* O despacho pelo Codex quando o projeto o elege: por `runtime.adapter: codex`, como antes, ou, desde
|
|
325
|
+
* a fatia 2 do ensaio da 0.5.0 (P1), por bloco de modo permitido. A falta do `codex`, ou um sandbox
|
|
326
|
+
* que nao executa, deixa de ser aviso: e exatamente o que bloquearia a fase. Sem nenhum dos dois, o
|
|
327
|
+
* check nao sai.
|
|
328
|
+
*/
|
|
329
|
+
function checarDespachoPeloCodex(carregado, codex, sonda) {
|
|
330
|
+
const pelaAdapter = carregado.manifesto.runtime.adapter === 'codex';
|
|
331
|
+
const blocos = blocosDoRuntime(carregado, 'codex')?.principal ?? [];
|
|
332
|
+
if (!pelaAdapter && blocos.length === 0)
|
|
333
|
+
return null;
|
|
334
|
+
const origem = [pelaAdapter ? 'runtime.adapter: codex' : '',
|
|
335
|
+
blocos.length ? `${blocos.length} bloco(s) dos modos permitidos no codex (${textoDosBlocos(blocos)})` : '']
|
|
336
|
+
.filter(Boolean).join(' e ');
|
|
337
|
+
const sandboxConfigurado = carregado.manifesto.runtime.sandbox;
|
|
338
|
+
const sandboxQuebrado = sonda !== null && !sonda.ok && sandboxConfigurado !== 'danger-full-access';
|
|
339
|
+
return {
|
|
340
|
+
nome: 'despacho pelo codex',
|
|
341
|
+
nivel: !codex ? 'fail' : sandboxQuebrado ? 'fail' : 'ok',
|
|
342
|
+
detalhe: !codex
|
|
343
|
+
? `${origem}, mas o binario \`codex\` esta fora do PATH`
|
|
344
|
+
: sandboxQuebrado
|
|
345
|
+
? `${origem} com sandbox "${sandboxConfigurado}", mas o sandbox nao executa nesta maquina (o agente narraria sucesso sem rodar nada)`
|
|
346
|
+
: `${origem} com sandbox "${sandboxConfigurado}"`,
|
|
347
|
+
correcao: !codex
|
|
348
|
+
? 'instale o Codex CLI e autentique com `codex login` (assinatura, nunca API key)'
|
|
349
|
+
: sandboxQuebrado
|
|
350
|
+
? 'instale o bubblewrap do sistema, ou declare runtime.sandbox: danger-full-access ciente do risco'
|
|
351
|
+
: undefined,
|
|
352
|
+
};
|
|
353
|
+
}
|
|
176
354
|
/** Roda todos os checks a partir do diretorio informado. */
|
|
177
355
|
function checar(dirInicial = process.cwd(), nomesHerdados = (0, runtime_ambiente_1.nomesDeProviderAtivos)()) {
|
|
178
356
|
const checks = [];
|
|
@@ -192,31 +370,43 @@ function checar(dirInicial = process.cwd(), nomesHerdados = (0, runtime_ambiente
|
|
|
192
370
|
});
|
|
193
371
|
const repo = (0, util_1.exec)('git', ['rev-parse', '--is-inside-work-tree'], dirInicial);
|
|
194
372
|
const dentroDeRepo = repo.ok && repo.stdout.trim() === 'true';
|
|
195
|
-
|
|
373
|
+
// Ensaio da 0.5.0: sem commit, o `rev-parse --abbrev-ref` respondia "HEAD"; a branch vem do
|
|
374
|
+
// `symbolic-ref`, e a falta de commit fica dita.
|
|
375
|
+
const semCommit = dentroDeRepo && !(0, util_1.exec)('git', ['rev-parse', '--verify', '--quiet', 'HEAD'], dirInicial).ok;
|
|
376
|
+
const branch = dentroDeRepo ? (0, util_1.branchDoHead)(dirInicial) ?? (0, util_1.exec)('git', ['rev-parse', '--abbrev-ref', 'HEAD'], dirInicial).stdout.trim() : '';
|
|
196
377
|
checks.push({
|
|
197
378
|
nome: 'repositorio',
|
|
198
379
|
nivel: dentroDeRepo ? 'ok' : 'fail',
|
|
199
|
-
detalhe: dentroDeRepo ? `branch ${branch
|
|
380
|
+
detalhe: dentroDeRepo ? `branch ${branch}${semCommit ? ' (sem commit)' : ''}` : 'fora de um repositorio git',
|
|
200
381
|
correcao: dentroDeRepo ? undefined : 'rode o ork dentro de um repositorio git',
|
|
201
382
|
});
|
|
383
|
+
if (dentroDeRepo) {
|
|
384
|
+
const dono = checarDonoDoGit(dirInicial);
|
|
385
|
+
if (dono)
|
|
386
|
+
checks.push(dono);
|
|
387
|
+
}
|
|
388
|
+
// Fatia 2 do ensaio da 0.5.0 (P1): o manifesto e lido antes dos runtimes, porque e o setup dos
|
|
389
|
+
// modos permitidos que diz se o `claude` e obrigatorio; a ordem das linhas do relatorio nao muda.
|
|
390
|
+
const carregado = (0, manifest_1.carregarManifesto)(dirInicial);
|
|
202
391
|
const claude = adapter.disponivel();
|
|
203
392
|
const v = claude ? adapter.versao() : null;
|
|
204
|
-
checks.push(
|
|
205
|
-
nome: 'runtime claude-bg',
|
|
206
|
-
nivel: claude ? 'ok' : 'fail',
|
|
207
|
-
detalhe: claude ? `${claude} (${v ?? 'versao desconhecida'})` : 'binario `claude` fora do PATH',
|
|
208
|
-
correcao: claude ? undefined : 'instale o Claude Code e garanta `claude` no PATH',
|
|
209
|
-
});
|
|
393
|
+
checks.push(checarRuntimeClaude(carregado, claude, v));
|
|
210
394
|
// Segundo runtime homologado. Ausente e `warn`, nao `fail`: o claude-bg segue sendo o
|
|
211
395
|
// padrao, e um projeto que nunca pediu codex nao pode ficar bloqueado por ele.
|
|
212
396
|
const codex = codexAdapter.disponivel();
|
|
213
397
|
const codexVersao = codex ? codexAdapter.versao() : null;
|
|
398
|
+
// CHECK, rodada 1 (S6): com o projeto despachando pelo codex, a falta dele nao e "opcional"; quem
|
|
399
|
+
// reprova e o check `despacho pelo codex`, mais abaixo.
|
|
400
|
+
const usaCodex = !!carregado && carregado.erros.length === 0 && (carregado.manifesto.runtime.adapter === 'codex' ||
|
|
401
|
+
(blocosDoRuntime(carregado, 'codex')?.principal.length ?? 0) > 0);
|
|
214
402
|
checks.push({
|
|
215
403
|
nome: 'runtime codex',
|
|
216
404
|
nivel: codex ? 'ok' : 'warn',
|
|
217
405
|
detalhe: codex
|
|
218
406
|
? `${codex} (${codexVersao ?? 'versao desconhecida'})`
|
|
219
|
-
:
|
|
407
|
+
: usaCodex
|
|
408
|
+
? 'binario `codex` fora do PATH (o projeto despacha por ele: veja despacho pelo codex)'
|
|
409
|
+
: 'binario `codex` fora do PATH (opcional: claude-bg e o runtime padrao)',
|
|
220
410
|
correcao: codex
|
|
221
411
|
? undefined
|
|
222
412
|
: 'para despachar pelo codex, instale o Codex CLI e autentique com `codex login`',
|
|
@@ -238,7 +428,6 @@ function checar(dirInicial = process.cwd(), nomesHerdados = (0, runtime_ambiente
|
|
|
238
428
|
'nesta maquina) ou declare runtime.sandbox: danger-full-access ciente do risco',
|
|
239
429
|
});
|
|
240
430
|
}
|
|
241
|
-
const carregado = (0, manifest_1.carregarManifesto)(dirInicial);
|
|
242
431
|
if (!carregado) {
|
|
243
432
|
checks.push({
|
|
244
433
|
nome: 'manifesto',
|
|
@@ -285,26 +474,11 @@ function checar(dirInicial = process.cwd(), nomesHerdados = (0, runtime_ambiente
|
|
|
285
474
|
: `${fuso.fuso} (fuso do sistema; ${horario_1.CHAVE_DO_FUSO} nao configurado)`),
|
|
286
475
|
correcao: fuso.aviso ? `corrija ${horario_1.CHAVE_DO_FUSO} com um nome IANA` : undefined,
|
|
287
476
|
});
|
|
288
|
-
// Quando o manifesto
|
|
477
|
+
// Quando o manifesto ou um bloco de modo permitido ELEGE o codex, a ausencia dele (ou um
|
|
289
478
|
// sandbox que nao executa) deixa de ser aviso: e exatamente o que bloquearia a fase.
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
checks.push({
|
|
294
|
-
nome: 'despacho pelo codex',
|
|
295
|
-
nivel: !codex ? 'fail' : sandboxQuebrado ? 'fail' : 'ok',
|
|
296
|
-
detalhe: !codex
|
|
297
|
-
? 'runtime.adapter: codex, mas o binario `codex` esta fora do PATH'
|
|
298
|
-
: sandboxQuebrado
|
|
299
|
-
? `runtime.adapter: codex com sandbox "${sandboxConfigurado}", mas o sandbox nao executa nesta maquina (o agente narraria sucesso sem rodar nada)`
|
|
300
|
-
: `runtime.adapter: codex com sandbox "${sandboxConfigurado}"`,
|
|
301
|
-
correcao: !codex
|
|
302
|
-
? 'instale o Codex CLI e autentique com `codex login` (assinatura, nunca API key)'
|
|
303
|
-
: sandboxQuebrado
|
|
304
|
-
? 'instale o bubblewrap do sistema, ou declare runtime.sandbox: danger-full-access ciente do risco'
|
|
305
|
-
: undefined,
|
|
306
|
-
});
|
|
307
|
-
}
|
|
479
|
+
const despachoPeloCodex = checarDespachoPeloCodex(carregado, codex, sondaDoCodex);
|
|
480
|
+
if (despachoPeloCodex)
|
|
481
|
+
checks.push(despachoPeloCodex);
|
|
308
482
|
const politica = carregado.manifesto.runtime.provider_policy;
|
|
309
483
|
const soAssinatura = politica === 'subscription-only';
|
|
310
484
|
const redirecionam = ENVS_QUE_REDIRECIONAM.filter(e => nomesHerdados.includes(e));
|
|
@@ -385,12 +559,15 @@ function checar(dirInicial = process.cwd(), nomesHerdados = (0, runtime_ambiente
|
|
|
385
559
|
checks.push(...(0, preflight_1.sondasDeAmbiente)(carregado.raiz));
|
|
386
560
|
{
|
|
387
561
|
const inventario = (0, sessoes_inventario_1.inventariarSessoes)(carregado.raiz, { global: true, todas: true });
|
|
562
|
+
// Fatia 2 do ensaio da 0.5.0 (P1): binario ausente e fonte ausente, dita no detalhe.
|
|
563
|
+
const ausentes = inventario.fontes.filter((f) => f.ausente).map((f) => f.origem);
|
|
388
564
|
checks.push({
|
|
389
565
|
nome: 'governanca de sessoes',
|
|
390
566
|
nivel: inventario.ok && inventario.semThread === 0 ? 'ok' : 'warn',
|
|
391
567
|
detalhe: `${inventario.escopo.usuario}, Claude e Codex, global com histórico: ${inventario.total} sessões; ` +
|
|
392
568
|
`${inventario.semThread} sem thread; ${inventario.ambiguas} ambíguas; ` +
|
|
393
|
-
`inventário ${inventario.ok ? 'válido' : 'INCOMPLETO (zero não comprovado)'}
|
|
569
|
+
`inventário ${inventario.ok ? 'válido' : 'INCOMPLETO (zero não comprovado)'}` +
|
|
570
|
+
(ausentes.length ? `; fonte ausente: ${ausentes.join(', ')}` : ''),
|
|
394
571
|
correcao: inventario.ok && inventario.semThread === 0 ? undefined :
|
|
395
572
|
'ork sessions --global --all --json; adote identidades sem vínculo com ork sessions adopt <id>',
|
|
396
573
|
});
|