auroq-os 2.6.2 → 2.6.4
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/.auroq-core/core-config.yaml +1 -1
- package/CHANGELOG.md +25 -0
- package/README.md +3 -3
- package/bin/auroq-os.js +67 -4
- package/installer/install.ps1 +2 -2
- package/installer/install.sh +2 -2
- package/lib/ahi/install.cjs +9 -1
- package/lib/codex-windows.cjs +99 -0
- package/lib/runtime-integrity.json +10 -6
- package/package.json +2 -2
- package/scripts/sync-codex-skills.mjs +67 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,28 @@
|
|
|
1
|
+
## 2.6.4 — 2026-09-29
|
|
2
|
+
|
|
3
|
+
### Companion com o nome do aluno nao trava mais a atualizacao
|
|
4
|
+
|
|
5
|
+
- Quem deu nome ao Companion no Bootstrap (ex.: "Atlas" → `/atlas-companion`) podia ficar preso em "Sync de skills do Codex falhou" → "arquivo faltando: Codex Companion (.agents/skills/companion/SKILL.md)" → `AUROQ_RESULTADO status=erro`. Tres alunos em 29/09.
|
|
6
|
+
- Causa: o sync do Codex morria inteiro na primeira pasta de skill que nao reconhecia — pasta do negocio movida, copiada ou vinda de outra maquina (marcador com outro caminho), `companion.md` antigo colidindo com `auroq-companion.md`, ou skill feita a mao pelo agente. Como o `--clean` ja tinha apagado a skill `companion` quando o comando base sumiu, ela nunca voltava e a verificacao final derrubava a atualizacao.
|
|
7
|
+
- O sync nao para mais por isso: pasta de skill com o nosso marcador dentro do proprio negocio e reconhecida mesmo depois de mover a pasta; colisao de nome usa o comando oficial e segue; skill sem marcador feita pelo aluno fica como esta, com aviso. A pasta global `~/.agents/skills` continua recusando o que e de outro projeto.
|
|
8
|
+
- `$companion` sempre existe no Codex: sem o comando base, vira atalho do comando com o nome do aluno. `$atlas-companion` continua.
|
|
9
|
+
- A verificacao de integridade aceita o comando com o nome do aluno como o Companion (so ele; os outros arquivos seguem exigidos pelo caminho exato). Quando o sync falha, a manutencao mostra o motivo.
|
|
10
|
+
- Ops (auroq-core 2.3.20): a FASE 10 cria o comando com o nome e deixa `/auroq-companion` no lugar (antes mandava apagar um `companion.md` que nao existe mais).
|
|
11
|
+
- Quem esta travado: "atualiza o sistema" termina `status=ok` e mantem o nome, o comando e o conteudo do Companion.
|
|
12
|
+
- Mantem `arcane-mcp@0.7.0`. Instaladores do repositorio preparados para 2.6.4.
|
|
13
|
+
|
|
14
|
+
## 2.6.3 — 2026-09-29
|
|
15
|
+
|
|
16
|
+
### Codex no Windows volta a ler e gravar na pasta (EMB-063)
|
|
17
|
+
|
|
18
|
+
- Desde o Codex 0.157 (25/09) o sandbox padrao do Windows ("elevated") refaz as permissoes da pasta antes de cada comando e aborta tudo no primeiro erro: `helper_unknown_error: setup refresh had errors`. O agente (Clone incluido) nao le nem grava nada; o MCP conecta normal porque nao passa pelo sandbox. Bug aberto na OpenAI ate o 0.159 (openai/codex#48517, #48979, #49017, #49234; dono da pasta #46441).
|
|
19
|
+
- `register-mcp`, `instalar`, `manutencao` e `clone` gravam no `.codex/config.toml` da pasta `[windows] sandbox = "unelevated"`: o modo do proprio Codex que aplica a mesma regra (escreve so na pasta do negocio) sem o passo que quebra. Escolha que a pasta ja tinha fica. Fora do Windows o Codex ignora a chave.
|
|
20
|
+
- No Windows grava tambem `[features] daemon_auto_start = false`: o servidor de fundo que o Codex 0.157 liga por padrao abre janelas pretas vazias a cada hook e comando (#47179, #44768, #48074).
|
|
21
|
+
- `doctor` no Windows mostra o modo do sandbox, ajusta a pasta se estiver no padrao e traduz a ultima falha do log `~/.codex/.sandbox`.
|
|
22
|
+
- Hooks de fim de turno do Codex (`Stop`, `SubagentStop`) passam de 3 s para 10 s: Node frio + janela de console passavam de 3 s e o aluno via "Hook failed: timed out after 3s". `SessionEnd`/`Interrupt` seguem em 3 s; Claude Code sem mudanca.
|
|
23
|
+
- Workflow manual `codex-windows-sandbox.yml` prova no windows-latest com o Codex 0.157.0.
|
|
24
|
+
- Mantem `arcane-mcp@0.7.0`.
|
|
25
|
+
|
|
1
26
|
## 2.6.2 — 2026-09-24
|
|
2
27
|
|
|
3
28
|
- Atalho `/euriler` (e a skill do Codex): o Clone Euriler se apresenta como o clone do Euriler, nunca como ele em pessoa ou "ao vivo", e chama `squad_ativar` antes de responder qualquer coisa, inclusive perguntas diretas do aluno, e antes de abrir arquivos ou configurações. Sem isso, o modelo às vezes respondia como assistente genérico antes de o clone ser ativado.
|
package/README.md
CHANGED
|
@@ -241,13 +241,13 @@ agents/ → Seu exercito (companion, workers, minds, squads)
|
|
|
241
241
|
|
|
242
242
|
## Observabilidade e integridade do motor
|
|
243
243
|
|
|
244
|
-
Apos publicacao, `npx -y auroq-os@2.6.
|
|
245
|
-
manutencao/diagnostico. `npx -y auroq-os@2.6.
|
|
244
|
+
Apos publicacao, `npx -y auroq-os@2.6.4 doctor` mostra a fila local segura e os comandos pinados de
|
|
245
|
+
manutencao/diagnostico. `npx -y auroq-os@2.6.4 manutencao` preserva o caminho existente
|
|
246
246
|
de consentimento legado, studentFiles, customizacoes e dados do negocio. Nao ha
|
|
247
247
|
migracao forcada. O registro MCP aponta para `arcane-mcp@0.7.0` — o pin mora no campo
|
|
248
248
|
`arcaneMcpPin` do package.json (fonte unica; o servidor le o mesmo campo no npm).
|
|
249
249
|
`npm run release:verify` recusa publicar com um pin que o npm nao resolva.
|
|
250
|
-
Os instaladores do repositorio ficam preparados para 2.6.
|
|
250
|
+
Os instaladores do repositorio ficam preparados para 2.6.4; as copias publicas so recebem
|
|
251
251
|
a versao apos os gates nativos e a verificacao do tarball publicado.
|
|
252
252
|
O pacote inclui `lib/runtime-integrity.json`: inventario SHA-256 gerado depois da montagem da casca, verificavel com `npm run integrity:verify` dentro do pacote extraido. Ele comprova consistencia do conteudo; nao e uma assinatura de autoria.
|
|
253
253
|
|
package/bin/auroq-os.js
CHANGED
|
@@ -398,6 +398,32 @@ const HEALTH_CHECKS = [
|
|
|
398
398
|
['scripts/validate-hybrid.mjs', 'Validador hibrido'],
|
|
399
399
|
];
|
|
400
400
|
|
|
401
|
+
/**
|
|
402
|
+
* O aluno da um nome ao Companion no bootstrap (FASE 10 do Ops): `/atlas-companion`. Um
|
|
403
|
+
* comando com esse nome que ainda aponta pro Companion (recurso `companion` do MCP ou a
|
|
404
|
+
* pasta agents/companion/) vale como a casca dele — um clone do negocio que so tem o
|
|
405
|
+
* comando com o nome do aluno nao pode ser acusado de "arquivo faltando". So o Companion
|
|
406
|
+
* tem essa folga: os outros arquivos continuam exigidos pelo caminho exato.
|
|
407
|
+
*/
|
|
408
|
+
async function companionComNomeDoAluno(targetDir) {
|
|
409
|
+
const dir = path.join(targetDir, '.claude', 'commands');
|
|
410
|
+
let nomes = [];
|
|
411
|
+
try { nomes = await fs.readdir(dir); } catch { return null; }
|
|
412
|
+
for (const nome of nomes.filter(n => /^[a-z0-9-]+-companion\.md$/.test(n)).sort()) {
|
|
413
|
+
try {
|
|
414
|
+
const texto = await fs.readFile(path.join(dir, nome), 'utf8');
|
|
415
|
+
if (/recurso_id\s*`?companion`?/.test(texto) || /agents\/companion\//.test(texto)) return `.claude/commands/${nome}`;
|
|
416
|
+
} catch { /* ilegivel: nao conta */ }
|
|
417
|
+
}
|
|
418
|
+
return null;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
async function checagemPresente(targetDir, file) {
|
|
422
|
+
if (await fs.pathExists(path.join(targetDir, file))) return true;
|
|
423
|
+
if (file === '.claude/commands/auroq-companion.md') return Boolean(await companionComNomeDoAluno(targetDir));
|
|
424
|
+
return false;
|
|
425
|
+
}
|
|
426
|
+
|
|
401
427
|
/**
|
|
402
428
|
* Registro pro suporte (PRD "Embarque em 2 passos" §5.7).
|
|
403
429
|
*
|
|
@@ -442,6 +468,7 @@ const ETIQUETAS_DOCTOR = [
|
|
|
442
468
|
['Confira sua internet', 'servidor-fora'],
|
|
443
469
|
['whoami', 'mcp-nao-abre'],
|
|
444
470
|
['tela INTEIRA', 'codex-sem-conteudo'],
|
|
471
|
+
['setup refresh had errors', 'codex-sandbox-windows'],
|
|
445
472
|
];
|
|
446
473
|
function rotulo(pendencia) {
|
|
447
474
|
const texto = String(pendencia || '');
|
|
@@ -493,7 +520,7 @@ async function runHealthChecks(targetDir) {
|
|
|
493
520
|
let passed = 0;
|
|
494
521
|
const missing = [];
|
|
495
522
|
for (const [file, label] of HEALTH_CHECKS) {
|
|
496
|
-
if (await
|
|
523
|
+
if (await checagemPresente(targetDir, file)) {
|
|
497
524
|
passed++;
|
|
498
525
|
} else {
|
|
499
526
|
missing.push(`${label} (${file})`);
|
|
@@ -530,7 +557,7 @@ async function runDoctor(targetDir) {
|
|
|
530
557
|
// 1. arquivos de autoload
|
|
531
558
|
let faltando = 0;
|
|
532
559
|
for (const [file] of HEALTH_CHECKS) {
|
|
533
|
-
if (!(await
|
|
560
|
+
if (!(await checagemPresente(targetDir, file))) faltando++;
|
|
534
561
|
}
|
|
535
562
|
if (faltando === 0) {
|
|
536
563
|
success(`Arquivos do sistema: ${HEALTH_CHECKS.length}/${HEALTH_CHECKS.length} no lugar`);
|
|
@@ -714,6 +741,28 @@ async function runDoctor(targetDir) {
|
|
|
714
741
|
}
|
|
715
742
|
if (r.stderr) for (const l of r.stderr.split('\n').slice(-4)) log(` ${DIM} stderr: ${l.slice(0, 160)}${RESET}`);
|
|
716
743
|
|
|
744
|
+
// EMB-063: sandbox do Codex no Windows. "setup refresh had errors" = modo elevated.
|
|
745
|
+
if (process.platform === 'win32') {
|
|
746
|
+
const cw = require('../lib/codex-windows.cjs');
|
|
747
|
+
let modo = cw.modoDoSandbox(tomlTexto);
|
|
748
|
+
if (!modo) {
|
|
749
|
+
try {
|
|
750
|
+
await fs.writeFile(tomlPath, cw.ajustarConfigCodex(tomlTexto, { semJanelas: true }));
|
|
751
|
+
modo = 'unelevated';
|
|
752
|
+
warn('Codex estava no sandbox padrao, que no Windows da "setup refresh had errors". Ajustei esta pasta; feche e abra o Codex.');
|
|
753
|
+
} catch (e) {
|
|
754
|
+
warn(`Nao consegui ajustar o sandbox do Codex (${e.code || e.message})`);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
(modo === 'unelevated' ? success : warn)(`Sandbox do Codex nesta pasta: ${modo || 'elevated (padrao)'}`);
|
|
758
|
+
const d = cw.diagnosticoDoLog();
|
|
759
|
+
if (d.arquivo && d.causa) {
|
|
760
|
+
warn(`Log do sandbox (${d.arquivo}): ${d.causa === 'dono-da-pasta' ? 'o Windows negou permissao na pasta' : d.causa === 'caminho-longo' ? 'caminho longo demais no Codex' : 'erro do sandbox'}`);
|
|
761
|
+
for (const l of d.linhas.slice(-3)) log(` ${DIM} ${l.slice(0, 160)}${RESET}`);
|
|
762
|
+
}
|
|
763
|
+
if (modo !== 'unelevated') problemas.push(`Codex no Windows: esta pasta pede o sandbox "${modo || 'elevated'}", que da "setup refresh had errors". Troque para "unelevated" em .codex/config.toml ([windows] sandbox) ou mande este print pro suporte.`);
|
|
764
|
+
}
|
|
765
|
+
|
|
717
766
|
const rastro = await rastroDoCodex();
|
|
718
767
|
if (rastro.length) {
|
|
719
768
|
log(` ${DIM}Ultimas linhas do log do Codex sobre o arcane:${RESET}`);
|
|
@@ -1187,7 +1236,9 @@ async function acertarVersaoNoRegistro(targetDir, versao) {
|
|
|
1187
1236
|
const novoDentro = PAR_VERSAO_TOML.test(dentro) ? dentro.replace(PAR_VERSAO_TOML, par) : (dentro ? `${dentro}, ${par}` : par);
|
|
1188
1237
|
blocoNovo = bloco.replace(linha[0], `env = { ${novoDentro} }`);
|
|
1189
1238
|
} else {
|
|
1190
|
-
|
|
1239
|
+
// O que vinha depois do bloco (linha em branco antes da proxima tabela, ou nada) fica.
|
|
1240
|
+
const depoisDoBloco = bloco.match(/\s*$/)[0] || (toml.endsWith(bloco) ? '\n' : '');
|
|
1241
|
+
blocoNovo = `${bloco.replace(/\s*$/, '')}\nenv = { ${par} }${depoisDoBloco}`;
|
|
1191
1242
|
}
|
|
1192
1243
|
novo = toml.replace(bloco, blocoNovo);
|
|
1193
1244
|
}
|
|
@@ -1256,6 +1307,15 @@ async function writeArcaneMcpConfig(targetDir, { versaoMotor = null } = {}) {
|
|
|
1256
1307
|
const next = (toml.trimEnd() ? `${toml.trimEnd()}\n\n` : '') + block;
|
|
1257
1308
|
await fs.writeFile(tomlPath, next);
|
|
1258
1309
|
}
|
|
1310
|
+
// Codex no Windows (EMB-063): sandbox "unelevated" e sem servidor de fundo abrindo janela preta.
|
|
1311
|
+
// Escolha que a pasta ja tem (aluno/suporte) fica; fora do Windows o Codex ignora [windows].
|
|
1312
|
+
try {
|
|
1313
|
+
const atual = await fs.readFile(tomlPath, 'utf8');
|
|
1314
|
+
const ajustado = require('../lib/codex-windows.cjs').ajustarConfigCodex(atual, { semJanelas: process.platform === 'win32' });
|
|
1315
|
+
if (ajustado !== atual) await fs.writeFile(tomlPath, ajustado);
|
|
1316
|
+
} catch (e) {
|
|
1317
|
+
warn(`Nao consegui ajustar o sandbox do Codex em .codex/config.toml (${e.code || e.message}).`);
|
|
1318
|
+
}
|
|
1259
1319
|
// Codex reads its own native hooks.json; do not alter its trust decisions or other hooks.
|
|
1260
1320
|
const codexHooksPath = path.join(targetDir, '.codex', 'hooks.json');
|
|
1261
1321
|
try {
|
|
@@ -1934,8 +1994,11 @@ async function init({ pasta = null, maintenance = false, modoAgente = false, mig
|
|
|
1934
1994
|
execSync('node scripts/sync-codex-skills.mjs --clean', { cwd: targetDir, stdio: 'pipe' });
|
|
1935
1995
|
execSync('node scripts/sync-codex-skills.mjs --check', { cwd: targetDir, stdio: 'pipe' });
|
|
1936
1996
|
success('Codex CLI pronto — skills locais verificadas ($companion, $ops, etc.)');
|
|
1937
|
-
} catch {
|
|
1997
|
+
} catch (e) {
|
|
1938
1998
|
warn('Sync de skills do Codex falhou — rode "npx auroq-os sync-codex" para diagnosticar');
|
|
1999
|
+
// Sem o motivo, o suporte so via o efeito ("Codex Companion faltando") e nunca a causa.
|
|
2000
|
+
const motivo = String(e && (e.stderr || e.message) || '').split(/\r?\n/).find(l => /Error:|Skill |Drift|Ownership|Recusando/.test(l));
|
|
2001
|
+
if (motivo) warn(` ${motivo.trim().slice(0, 300)}`);
|
|
1939
2002
|
}
|
|
1940
2003
|
|
|
1941
2004
|
// ─── Fase 5: npm install
|
package/installer/install.ps1
CHANGED
|
@@ -589,7 +589,7 @@ function Invoke-AuroqLogin {
|
|
|
589
589
|
# sucesso. Usado como pergunta de sim/nao, ele dizia "voce ja estava logado"
|
|
590
590
|
# pra quem nunca logou, pulava o portao e deixava a conta sem acesso chegar ate
|
|
591
591
|
# a criacao da pasta.
|
|
592
|
-
& (Get-AuroqNpx) -y auroq-os@2.6.
|
|
592
|
+
& (Get-AuroqNpx) -y auroq-os@2.6.4 check-access
|
|
593
593
|
if ($LASTEXITCODE -ne 0) {
|
|
594
594
|
Write-AuroqLog 'login' 'recusado'
|
|
595
595
|
Write-Host ''
|
|
@@ -677,7 +677,7 @@ function Invoke-AuroqInit {
|
|
|
677
677
|
if ($env:AUROQ_INSTALL_DRY_RUN) { Write-AuroqOk 'Sistema - seria montado aqui'; return }
|
|
678
678
|
|
|
679
679
|
Set-Location $script:AuroqPastaAlvo
|
|
680
|
-
& (Get-AuroqNpx) -y auroq-os@2.6.
|
|
680
|
+
& (Get-AuroqNpx) -y auroq-os@2.6.4 instalar
|
|
681
681
|
if ($LASTEXITCODE -ne 0) {
|
|
682
682
|
Write-AuroqLog 'init' 'falhou'
|
|
683
683
|
Write-AuroqErro 'A montagem do sistema parou no meio.' 'Rode o mesmo comando de novo. Se repetir, mande pro suporte o arquivo install.log da pasta .auroq.'
|
package/installer/install.sh
CHANGED
|
@@ -219,9 +219,9 @@ auroq_tem_terminal() {
|
|
|
219
219
|
# recebem pedaco do script no lugar da resposta dele.
|
|
220
220
|
auroq_npx_aluno() {
|
|
221
221
|
if [ -t 0 ]; then
|
|
222
|
-
npx -y auroq-os@2.6.
|
|
222
|
+
npx -y auroq-os@2.6.4 "$@"
|
|
223
223
|
else
|
|
224
|
-
npx -y auroq-os@2.6.
|
|
224
|
+
npx -y auroq-os@2.6.4 "$@" < /dev/tty
|
|
225
225
|
fi
|
|
226
226
|
}
|
|
227
227
|
|
package/lib/ahi/install.cjs
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
const path = require('node:path');
|
|
3
3
|
const TERMINAL = ['Stop', 'SubagentStop', 'StopFailure', 'SessionEnd'];
|
|
4
4
|
const EVENTS = ['SessionStart', 'UserPromptSubmit', 'PreToolUse', 'PostToolUse', 'Stop', 'SubagentStop', 'SessionEnd'];
|
|
5
|
+
// Codex no Windows (EMB-063): o Node frio + a janela de console que o Codex 0.157 abre para cada
|
|
6
|
+
// hook passam de 3 s e o aluno ve "Hook failed: timed out after 3s" no fim de toda resposta. O
|
|
7
|
+
// hook so grava na fila local e sai; 10 s e folga, nao espera. SessionEnd/Interrupt ficam em 3.
|
|
8
|
+
const CODEX_FIM_DE_TURNO = ['Stop', 'SubagentStop'];
|
|
9
|
+
function timeoutDoHook(event, host) {
|
|
10
|
+
if (host === 'codex' && CODEX_FIM_DE_TURNO.includes(event)) return 10;
|
|
11
|
+
return [...TERMINAL, 'Interrupt'].includes(event) ? 3 : 10;
|
|
12
|
+
}
|
|
5
13
|
function mergeHooks(settings, project, host) {
|
|
6
14
|
const original = JSON.stringify(settings);
|
|
7
15
|
if (!settings.hooks || typeof settings.hooks !== 'object' || Array.isArray(settings.hooks)) settings.hooks = {};
|
|
@@ -12,7 +20,7 @@ function mergeHooks(settings, project, host) {
|
|
|
12
20
|
for (const event of events) {
|
|
13
21
|
const groups = Array.isArray(settings.hooks[event]) ? settings.hooks[event] : [];
|
|
14
22
|
const kept = groups.map(group => ({ ...group, hooks: (Array.isArray(group.hooks) ? group.hooks : []).filter(h => !String(h.command || '').includes('ahi-capture.cjs')) })).filter(g => g.hooks.length);
|
|
15
|
-
kept.push({ matcher: '', hooks: [{ type: 'command', command: `node "${script}" --host ${host}`, ...(TERMINAL.includes(event) ? {} : { async: true }), timeout:
|
|
23
|
+
kept.push({ matcher: '', hooks: [{ type: 'command', command: `node "${script}" --host ${host}`, ...(TERMINAL.includes(event) ? {} : { async: true }), timeout: timeoutDoHook(event, host) }] });
|
|
16
24
|
settings.hooks[event] = kept;
|
|
17
25
|
}
|
|
18
26
|
return JSON.stringify(settings) !== original;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* Codex no Windows: "helper_unknown_error: setup refresh had errors" (EMB-063).
|
|
4
|
+
*
|
|
5
|
+
* Desde o Codex 0.157 (25/09/2026) o sandbox padrao do Windows ("elevated") refaz, antes de
|
|
6
|
+
* CADA comando, as permissoes da pasta para as contas do sandbox ("setup refresh"). Esse
|
|
7
|
+
* passo roda sem administrador e aborta tudo no primeiro erro de permissao: pasta com dono
|
|
8
|
+
* BUILTIN\Administradores (criada num PowerShell de administrador), caminho longo no runtime
|
|
9
|
+
* do Codex, provisionamento que falha. Sintoma: o agente nao le nem grava nada na pasta; o MCP
|
|
10
|
+
* continua conectando (ele nao passa pelo sandbox). O Claude Code nao usa esse sandbox, por isso
|
|
11
|
+
* na mesma pasta ele funciona. Referencias: openai/codex#31414, #46441, #46388, #47892, #48517,
|
|
12
|
+
* #48979, #49017, #49234 (abertas no 0.159).
|
|
13
|
+
*
|
|
14
|
+
* O modo "unelevated" do proprio Codex aplica a mesma regra (escreve so na pasta do projeto),
|
|
15
|
+
* sem o passo que quebra, e ignora a falha de uma permissao isolada. Este modulo grava esse modo
|
|
16
|
+
* no .codex/config.toml da pasta e le o log do sandbox para o doctor dizer a causa.
|
|
17
|
+
*/
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const os = require('os');
|
|
20
|
+
const path = require('path');
|
|
21
|
+
|
|
22
|
+
/** Modo do sandbox num config.toml do Codex: 'elevated' | 'unelevated' | 'mxc' | null (padrao = elevated). */
|
|
23
|
+
function modoDoSandbox(toml) {
|
|
24
|
+
const texto = String(toml || '');
|
|
25
|
+
const tabela = texto.match(/^\[windows\][^\n]*\n([\s\S]*?)(?=^\[|(?![\s\S]))/m);
|
|
26
|
+
const dentro = tabela ? tabela[1].match(/^\s*sandbox\s*=\s*["']([a-z]+)["']/m) : null;
|
|
27
|
+
if (dentro) return dentro[1];
|
|
28
|
+
const pontilhado = texto.match(/^\s*windows\.sandbox\s*=\s*["']([a-z]+)["']/m);
|
|
29
|
+
return pontilhado ? pontilhado[1] : null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Config do Codex desta pasta com o sandbox que funciona nessas maquinas.
|
|
34
|
+
* - [windows] sandbox = "unelevated" (so se a pasta ainda nao escolheu um modo: escolha do
|
|
35
|
+
* aluno ou do suporte fica). Fora do Windows o Codex ignora a chave.
|
|
36
|
+
* - [features] daemon_auto_start = false quando `semJanelas`: no 0.157 o Codex passou a ligar um
|
|
37
|
+
* servidor de fundo por padrao (#47179) e, no Windows, ele abre uma janela preta vazia para
|
|
38
|
+
* cada hook e comando (#44768, #48074). Mesma regra: valor que ja existe fica.
|
|
39
|
+
* O resto do arquivo (o registro do arcane, o que for do aluno) fica como esta.
|
|
40
|
+
*/
|
|
41
|
+
function ajustarConfigCodex(toml, { semJanelas = false } = {}) {
|
|
42
|
+
let texto = String(toml || '');
|
|
43
|
+
const garantir = (tabela, chave, valorToml) => {
|
|
44
|
+
if (new RegExp(`^\\s*${tabela}\\.${chave}\\s*=`, 'm').test(texto)) return; // pontilhado na raiz
|
|
45
|
+
const m = texto.match(new RegExp(`^\\[${tabela}\\][^\\n]*\\n?`, 'm'));
|
|
46
|
+
if (!m) {
|
|
47
|
+
texto = `${texto.replace(/\s*$/, '')}${texto.trim() ? '\n\n' : ''}[${tabela}]\n${chave} = ${valorToml}\n`;
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const inicio = m.index + m[0].length;
|
|
51
|
+
const resto = texto.slice(inicio);
|
|
52
|
+
const fim = resto.search(/^\[/m);
|
|
53
|
+
const corpo = fim === -1 ? resto : resto.slice(0, fim);
|
|
54
|
+
if (new RegExp(`^\\s*${chave}\\s*=`, 'm').test(corpo)) return;
|
|
55
|
+
texto = `${texto.slice(0, inicio)}${m[0].endsWith('\n') ? '' : '\n'}${chave} = ${valorToml}\n${resto}`;
|
|
56
|
+
};
|
|
57
|
+
garantir('windows', 'sandbox', '"unelevated"');
|
|
58
|
+
if (semJanelas) garantir('features', 'daemon_auto_start', 'false');
|
|
59
|
+
return texto.replace(/\s*$/, '\n');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Le as ultimas linhas de erro do log do sandbox do Codex e diz a causa em linguagem de gente.
|
|
64
|
+
* causa: 'dono-da-pasta' | 'caminho-longo' | 'outro' | null (sem erro no log).
|
|
65
|
+
*/
|
|
66
|
+
function causaNoLog(texto) {
|
|
67
|
+
const linhas = String(texto || '').split(/\r?\n/).filter(l => /fail|error|denied/i.test(l));
|
|
68
|
+
const ultimas = linhas.slice(-6);
|
|
69
|
+
if (!ultimas.length) return { causa: null, linhas: [] };
|
|
70
|
+
const junto = ultimas.join('\n');
|
|
71
|
+
let causa = 'outro';
|
|
72
|
+
if (/SetNamedSecurityInfoW failed[^\n]*:\s*5\b|(write|deny) ACE (grant )?failed|os error 5\b/i.test(junto)) causa = 'dono-da-pasta';
|
|
73
|
+
else if (/runtime read\/execute validation failed|CreateFileW failed|cua_node|long path/i.test(junto)) causa = 'caminho-longo';
|
|
74
|
+
return { causa, linhas: ultimas.map(l => l.trim().slice(0, 300)) };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** O log mais recente em <CODEX_HOME>/.sandbox (sandbox*.log). */
|
|
78
|
+
function logMaisRecente(codexHome = process.env.CODEX_HOME || path.join(os.homedir(), '.codex')) {
|
|
79
|
+
const dir = path.join(codexHome, '.sandbox');
|
|
80
|
+
let nomes = [];
|
|
81
|
+
try { nomes = fs.readdirSync(dir).filter(n => /^sandbox.*\.log$/i.test(n)); } catch { return null; }
|
|
82
|
+
if (!nomes.length) return null;
|
|
83
|
+
return nomes.map(n => path.join(dir, n)).map(f => ({ f, t: fs.statSync(f).mtimeMs })).sort((a, b) => b.t - a.t)[0].f;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function diagnosticoDoLog(codexHome) {
|
|
87
|
+
const arquivo = logMaisRecente(codexHome);
|
|
88
|
+
if (!arquivo) return { arquivo: null, causa: null, linhas: [] };
|
|
89
|
+
try {
|
|
90
|
+
const st = fs.statSync(arquivo);
|
|
91
|
+
const tam = Math.min(64 * 1024, st.size);
|
|
92
|
+
const buf = Buffer.alloc(tam);
|
|
93
|
+
const fd = fs.openSync(arquivo, 'r');
|
|
94
|
+
try { fs.readSync(fd, buf, 0, tam, st.size - tam); } finally { fs.closeSync(fd); }
|
|
95
|
+
return { arquivo, ...causaNoLog(buf.toString('utf8')) };
|
|
96
|
+
} catch { return { arquivo, causa: null, linhas: [] }; }
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
module.exports = { ajustarConfigCodex, modoDoSandbox, causaNoLog, logMaisRecente, diagnosticoDoLog };
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schema": 1,
|
|
3
3
|
"algorithm": "sha256",
|
|
4
4
|
"scope": "packaged-runtime-and-cascas",
|
|
5
|
-
"version": "2.6.
|
|
5
|
+
"version": "2.6.4",
|
|
6
6
|
"files": [
|
|
7
7
|
{
|
|
8
8
|
"path": ".auroq-core/constitution.md",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
{
|
|
12
12
|
"path": ".auroq-core/core-config.yaml",
|
|
13
|
-
"sha256": "
|
|
13
|
+
"sha256": "3052aab9e21a7b36788f5141a7d34ce6baa87c482ac18762c306b84b24ffc5c9"
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
16
|
"path": ".auroq-core/core/synapse/context/context-builder.js",
|
|
@@ -206,7 +206,7 @@
|
|
|
206
206
|
},
|
|
207
207
|
{
|
|
208
208
|
"path": "bin/auroq-os.js",
|
|
209
|
-
"sha256": "
|
|
209
|
+
"sha256": "7a9fd719e1ddc2fb18000b9f237ec2fdb0b605ed111c6e58dccef890f2d68cb1"
|
|
210
210
|
},
|
|
211
211
|
{
|
|
212
212
|
"path": "lib/ahi/client.mjs",
|
|
@@ -218,12 +218,16 @@
|
|
|
218
218
|
},
|
|
219
219
|
{
|
|
220
220
|
"path": "lib/ahi/install.cjs",
|
|
221
|
-
"sha256": "
|
|
221
|
+
"sha256": "bc06e1dd61bd5bbfe3346d439960b7201b75547b7ed96e25b69d682174f0d696"
|
|
222
222
|
},
|
|
223
223
|
{
|
|
224
224
|
"path": "lib/auth.js",
|
|
225
225
|
"sha256": "5f1ddefbf2d4b9005e562380b40525e5bf823641e05a7bdb71568e15a5179869"
|
|
226
226
|
},
|
|
227
|
+
{
|
|
228
|
+
"path": "lib/codex-windows.cjs",
|
|
229
|
+
"sha256": "d6307d0106bad3e69142052d6450cef3f6252544049d3f3340aec55fc99005d1"
|
|
230
|
+
},
|
|
227
231
|
{
|
|
228
232
|
"path": "lib/credentials.js",
|
|
229
233
|
"sha256": "93f810df94db4f9c64e0e48308c10f2696a3af850fe6b9820cb4c4d5cf3e2fd5"
|
|
@@ -254,7 +258,7 @@
|
|
|
254
258
|
},
|
|
255
259
|
{
|
|
256
260
|
"path": "package.json",
|
|
257
|
-
"sha256": "
|
|
261
|
+
"sha256": "9c2cafc77bfc679534da059fbcb7a847ca284da5d9074e138308d329aa6aef76"
|
|
258
262
|
},
|
|
259
263
|
{
|
|
260
264
|
"path": "scripts/build-pack.mjs",
|
|
@@ -278,7 +282,7 @@
|
|
|
278
282
|
},
|
|
279
283
|
{
|
|
280
284
|
"path": "scripts/sync-codex-skills.mjs",
|
|
281
|
-
"sha256": "
|
|
285
|
+
"sha256": "889394bcbf80aefeba49adf2f19202b47382324f3b1357063ca04b87ce46ddb2"
|
|
282
286
|
},
|
|
283
287
|
{
|
|
284
288
|
"path": "scripts/validate-hybrid.mjs",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "auroq-os",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.4",
|
|
4
4
|
"arcaneMcpPin": "0.7.0",
|
|
5
5
|
"description": "Auroq OS — Sistema Operacional de IA para Experts",
|
|
6
6
|
"bin": {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"auroq:sync:codex:check": "npm run sync:codex:check",
|
|
20
20
|
"auroq:validate": "npm run validate:hybrid",
|
|
21
21
|
"validate:hybrid": "node scripts/validate-hybrid.mjs",
|
|
22
|
-
"lint": "node --check bin/auroq-os.js && node --check lib/auth.js && node --check lib/diagnostico.js && node --check lib/credentials.js && node --check lib/onepassword.js && node --check lib/supabase.js && node --check scripts/sync-codex-skills.mjs && node --check scripts/validate-hybrid.mjs && node --check scripts/prepack-casca.mjs && node --check scripts/postpack-casca.mjs && node --check scripts/assert-mcp-pin.mjs && node --check lib/telemetry.mjs && node --check scripts/synapse-hashes.mjs && node --check lib/ahi/historico.mjs && node --check .claude/hooks/ahi-capture.cjs && node --check .claude/hooks/ahi/security.cjs && node --check .claude/hooks/ahi/runtime.cjs && node --check .claude/hooks/ahi/worker.cjs && node --check lib/ahi/install.cjs && node --check .claude/hooks/ahi/auth-config.cjs && node --check lib/ahi/client.mjs && node --check scripts/build-pack.mjs && node --check scripts/assert-native-release.mjs && node --check scripts/runtime-integrity.mjs && node --check scripts/pack-state.mjs",
|
|
22
|
+
"lint": "node --check bin/auroq-os.js && node --check lib/auth.js && node --check lib/diagnostico.js && node --check lib/credentials.js && node --check lib/onepassword.js && node --check lib/supabase.js && node --check scripts/sync-codex-skills.mjs && node --check scripts/validate-hybrid.mjs && node --check scripts/prepack-casca.mjs && node --check scripts/postpack-casca.mjs && node --check scripts/assert-mcp-pin.mjs && node --check lib/telemetry.mjs && node --check scripts/synapse-hashes.mjs && node --check lib/ahi/historico.mjs && node --check .claude/hooks/ahi-capture.cjs && node --check .claude/hooks/ahi/security.cjs && node --check .claude/hooks/ahi/runtime.cjs && node --check .claude/hooks/ahi/worker.cjs && node --check lib/ahi/install.cjs && node --check lib/codex-windows.cjs && node --check .claude/hooks/ahi/auth-config.cjs && node --check lib/ahi/client.mjs && node --check scripts/build-pack.mjs && node --check scripts/assert-native-release.mjs && node --check scripts/runtime-integrity.mjs && node --check scripts/pack-state.mjs",
|
|
23
23
|
"typecheck": "npm run validate:hybrid",
|
|
24
24
|
"test": "node --test tests/*.test.mjs",
|
|
25
25
|
"release:verify": "node scripts/assert-mcp-pin.mjs && node scripts/assert-native-release.mjs",
|
|
@@ -61,12 +61,48 @@ function summaryFromMarkdown(file) {
|
|
|
61
61
|
return output.join(' ').replace(/\s+/g, ' ').replaceAll('"', "'") || 'Agente do Auroq OS.';
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
const avisos = [];
|
|
65
|
+
|
|
66
|
+
// Comando oficial = `auroq-<nome>.md` ou o Ops. Numa colisao de nome ele vence; o
|
|
67
|
+
// outro comando continua valendo no Claude/Grok e so fica sem skill no Codex.
|
|
68
|
+
const oficial = (file) => /^auroq-/.test(path.basename(file)) || relative(file).startsWith('.claude/commands/AuroqOS/');
|
|
69
|
+
|
|
64
70
|
function registerSkill(name, commandFile) {
|
|
65
|
-
if (!/^[a-z0-9-]+$/.test(name))
|
|
66
|
-
|
|
71
|
+
if (!/^[a-z0-9-]+$/.test(name)) {
|
|
72
|
+
avisos.push(`Comando ${relative(commandFile)} ignorado no Codex: nome de skill invalido '${name}'.`);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (skills.has(name)) {
|
|
76
|
+
// Uma colisao (ex: companion.md de antes do prefixo auroq- ao lado de auroq-companion.md)
|
|
77
|
+
// derrubava o sync inteiro — e junto a skill $companion, que a manutencao exige.
|
|
78
|
+
const atual = skills.get(name);
|
|
79
|
+
const [fica, sai] = oficial(commandFile) && !oficial(atual) ? [commandFile, atual] : [atual, commandFile];
|
|
80
|
+
skills.set(name, fica);
|
|
81
|
+
avisos.push(`Colisao de nome '${name}': usando ${relative(fica)}; ${relative(sai)} continua valendo no Claude, sem skill propria no Codex.`);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
67
84
|
skills.set(name, commandFile);
|
|
68
85
|
}
|
|
69
86
|
|
|
87
|
+
// O aluno da um nome ao Companion no bootstrap (FASE 10 do Ops: /atlas-companion).
|
|
88
|
+
// Se o comando base sumiu, $companion continua existindo, apontando pro comando dele.
|
|
89
|
+
function comandoDoCompanion(file) {
|
|
90
|
+
try {
|
|
91
|
+
const texto = fs.readFileSync(file, 'utf8');
|
|
92
|
+
return /recurso_id\s*`?companion`?/.test(texto) || /agents\/companion\//.test(texto);
|
|
93
|
+
} catch {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function garantirAliasCompanion() {
|
|
99
|
+
if (skills.has('companion')) return;
|
|
100
|
+
const candidatos = [...skills.entries()]
|
|
101
|
+
.filter(([name, file]) => name.endsWith('-companion') && comandoDoCompanion(file))
|
|
102
|
+
.sort(([a], [b]) => a.localeCompare(b));
|
|
103
|
+
if (candidatos.length) skills.set('companion', candidatos[0][1]);
|
|
104
|
+
}
|
|
105
|
+
|
|
70
106
|
function collectSkills() {
|
|
71
107
|
const commandRoot = path.join(root, '.claude', 'commands');
|
|
72
108
|
if (!fs.existsSync(commandRoot)) return;
|
|
@@ -88,6 +124,7 @@ function collectSkills() {
|
|
|
88
124
|
registerSkill(kebab(file.replace(/\.md$/, '')), path.join(coreAgents, file));
|
|
89
125
|
}
|
|
90
126
|
}
|
|
127
|
+
garantirAliasCompanion();
|
|
91
128
|
}
|
|
92
129
|
|
|
93
130
|
function skillContent(name, commandFile, outRoot) {
|
|
@@ -139,13 +176,20 @@ function readOwner(dir) {
|
|
|
139
176
|
// Mesma pasta de projeto = mesmo dono, mesmo que o name do package tenha
|
|
140
177
|
// mudado depois (ex: marcador gerado antes do package.json existir).
|
|
141
178
|
if (data.sourceRoot && path.resolve(data.sourceRoot) === root) return { kind: 'owned', data };
|
|
179
|
+
// .agents/skills mora DENTRO do projeto: marcador do nosso gerador com outro caminho
|
|
180
|
+
// e a mesma pasta de negocio movida, copiada ou vinda de outra maquina. Tratar como
|
|
181
|
+
// estranho travava o sync inteiro (e com ele a skill $companion) pra sempre.
|
|
182
|
+
if (path.dirname(dir) === localOut && data.schema === 2 && data.projectId) return { kind: 'owned', data, movido: true };
|
|
142
183
|
return { kind: 'foreign', data };
|
|
143
184
|
} catch {
|
|
144
185
|
return { kind: 'foreign', data: { invalidMarker: true } };
|
|
145
186
|
}
|
|
146
187
|
}
|
|
147
188
|
|
|
148
|
-
|
|
189
|
+
// Local: pasta de skill sem o nosso marcador e do aluno — fica como esta, com aviso,
|
|
190
|
+
// e o resto do sync segue. Global (~/.agents/skills, dividido entre projetos): recusa tudo.
|
|
191
|
+
function collisions(outRoot) {
|
|
192
|
+
const preservadas = new Set();
|
|
149
193
|
for (const [name, commandFile] of skills) {
|
|
150
194
|
const dir = path.join(outRoot, name);
|
|
151
195
|
if (!fs.existsSync(dir)) continue;
|
|
@@ -155,8 +199,15 @@ function assertNoCollisions(outRoot) {
|
|
|
155
199
|
if (adoptLegacy && owner.kind === 'foreign' && owner.data?.projectName === projectName) continue;
|
|
156
200
|
const existing = path.join(dir, 'SKILL.md');
|
|
157
201
|
if (owner.kind === 'legacy' && fs.existsSync(existing) && fs.readFileSync(existing, 'utf8') === skillContent(name, commandFile, outRoot)) continue;
|
|
202
|
+
if (outRoot === localOut && owner.kind === 'legacy') continue;
|
|
203
|
+
if (outRoot === localOut) {
|
|
204
|
+
preservadas.add(name);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
158
207
|
throw new Error(`Recusando sobrescrever ${dir}; ownership: ${owner.kind}.`);
|
|
159
208
|
}
|
|
209
|
+
for (const name of preservadas) avisos.push(`Skill ${name} em ${relative(path.join(outRoot, name))} nao foi gerada por este sync: mantida como esta.`);
|
|
210
|
+
return preservadas;
|
|
160
211
|
}
|
|
161
212
|
|
|
162
213
|
function inspect(outRoot) {
|
|
@@ -168,7 +219,9 @@ function inspect(outRoot) {
|
|
|
168
219
|
issues.push(`Skill ausente: ${name}`);
|
|
169
220
|
continue;
|
|
170
221
|
}
|
|
171
|
-
|
|
222
|
+
const owner = readOwner(dir);
|
|
223
|
+
if (outRoot === localOut && owner.kind === 'unmanaged') continue; // skill do aluno, preservada
|
|
224
|
+
if (owner.kind !== 'owned' || owner.movido) issues.push(`Ownership invalido: ${name}`);
|
|
172
225
|
const file = path.join(dir, 'SKILL.md');
|
|
173
226
|
if (!fs.existsSync(file) || fs.readFileSync(file, 'utf8') !== skillContent(name, commandFile, outRoot)) {
|
|
174
227
|
issues.push(`Drift de conteudo: ${name}`);
|
|
@@ -183,16 +236,18 @@ function inspect(outRoot) {
|
|
|
183
236
|
|
|
184
237
|
function sync(outRoot) {
|
|
185
238
|
ensureDir(outRoot);
|
|
186
|
-
|
|
239
|
+
const preservadas = collisions(outRoot);
|
|
187
240
|
const stage = fs.mkdtempSync(path.join(outRoot, '.auroq-stage-'));
|
|
188
241
|
try {
|
|
189
242
|
for (const [name, commandFile] of skills) {
|
|
243
|
+
if (preservadas.has(name)) continue;
|
|
190
244
|
const dir = path.join(stage, name);
|
|
191
245
|
ensureDir(dir);
|
|
192
246
|
fs.writeFileSync(path.join(dir, markerFile), marker, 'utf8');
|
|
193
247
|
fs.writeFileSync(path.join(dir, 'SKILL.md'), skillContent(name, commandFile, outRoot), 'utf8');
|
|
194
248
|
}
|
|
195
249
|
for (const name of skills.keys()) {
|
|
250
|
+
if (preservadas.has(name)) continue;
|
|
196
251
|
const target = path.join(outRoot, name);
|
|
197
252
|
fs.rmSync(target, { recursive: true, force: true });
|
|
198
253
|
fs.renameSync(path.join(stage, name), target);
|
|
@@ -237,6 +292,11 @@ Padrao gera skills locais em .agents/skills (recomendado)
|
|
|
237
292
|
collectSkills();
|
|
238
293
|
if (targets.includes(localOut)) cleanLegacyLocalLocation();
|
|
239
294
|
|
|
295
|
+
const mostrarAvisos = () => {
|
|
296
|
+
for (const aviso of new Set(avisos)) console.error(`Aviso: ${aviso}`);
|
|
297
|
+
avisos.length = 0;
|
|
298
|
+
};
|
|
299
|
+
|
|
240
300
|
for (const target of targets) {
|
|
241
301
|
if (check) {
|
|
242
302
|
const issues = inspect(target);
|
|
@@ -247,10 +307,11 @@ for (const target of targets) {
|
|
|
247
307
|
console.log(`Auroq: ${skills.size} skills verificadas em ${target}`);
|
|
248
308
|
}
|
|
249
309
|
} else if (dryRun) {
|
|
250
|
-
if (fs.existsSync(target))
|
|
310
|
+
if (fs.existsSync(target)) collisions(target);
|
|
251
311
|
console.log(`Auroq: ${skills.size} skills validadas para ${target}`);
|
|
252
312
|
} else {
|
|
253
313
|
sync(target);
|
|
254
314
|
console.log(`Auroq: ${skills.size} skills sincronizadas em ${target}`);
|
|
255
315
|
}
|
|
316
|
+
mostrarAvisos();
|
|
256
317
|
}
|