auroq-os 2.8.8 → 2.8.9

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.
@@ -1,7 +1,7 @@
1
1
  project:
2
2
  name: "Auroq OS"
3
3
  type: greenfield
4
- version: 2.8.8
4
+ version: 2.8.9
5
5
  installedAt: '2026-03-16'
6
6
 
7
7
  ide:
package/CHANGELOG.md CHANGED
@@ -1,5 +1,6 @@
1
1
  ## Unreleased
2
2
 
3
+ - 2.8.9 — 1Password no Mac sem Homebrew (teste real num usuário novo do macOS, 06/11/2026): o Mac de aluno não tem Homebrew, e o `conectar-1password` parava em `app-nao-instalou` e `sem-op`. Agora, sem Homebrew (ou se ele falhar), o comando baixa o app oficial do 1Password (`1Password-latest`, assinatura da AgileBits conferida com `codesign --deep` e Team ID 2BUA8C4S2C) e copia para a pasta Aplicativos, e instala o 1Password CLI 2.41.1 oficial (SHA-256 fixo, o mesmo do app Auroq OS, mais a assinatura) em `~/.auroq/op/bin`, sem senha de administrador. `segredo` acha o `op` nessa pasta mesmo num terminal que não a tem no PATH.
3
4
  - 1Password no Windows: o CLI do 1Password passa pelo PowerShell, como no teste real do Marcos; isso evita a falha de sessão que ocorria quando o Node o iniciava diretamente. `--estado` usa o mesmo caminho para detectar o CLI, e a lista de contas deixa de depender do shell em que o aluno abriu o comando. Os três ganchos do Claude Code usam o caminho do projeto mesmo depois de entrar numa subpasta. A skill do cofre orienta o diagnóstico quando a aprovação se repete. Catálogo `auroq-core` 2.3.55.
4
5
 
5
6
  - Conteúdo ao vivo (catálogo `auroq-core` 2.3.54, 04/11): a peça do 1Password da skill do cofre avisa o Companion de que, no Windows, o app pode estar instalado só como pacote MSIX (é o que o `winget` instala hoje) e não aparece nas pastas de sempre: antes de dizer "o app não está instalado" (resultado `instalar-app`, ou o aluno dizendo que não acha) ele confere a lista de pacotes do Windows, e, se o app estiver lá, vai direto para entrar na conta e ligar a chave. Também descreve o instalador de reserva com limite de 3 minutos; o mesmo aviso entra no pré-gate da skill `configurar-computador`. Precisa do auroq-os 2.8.6 no npm (a detecção e o limite vivem no comando).
package/README.md CHANGED
@@ -290,13 +290,13 @@ a parte tecnica e com o Companion.
290
290
 
291
291
  ## Observabilidade e integridade do motor
292
292
 
293
- Apos publicacao, `npx -y auroq-os@2.8.8 doctor` mostra a fila local segura e os comandos pinados de
294
- manutencao/diagnostico. `npx -y auroq-os@2.8.8 manutencao` preserva o caminho existente
293
+ Apos publicacao, `npx -y auroq-os@2.8.9 doctor` mostra a fila local segura e os comandos pinados de
294
+ manutencao/diagnostico. `npx -y auroq-os@2.8.9 manutencao` preserva o caminho existente
295
295
  de consentimento legado, studentFiles, customizacoes e dados do negocio. Nao ha
296
296
  migracao forcada. O registro MCP aponta para `arcane-mcp@0.7.1` — o pin mora no campo
297
297
  `arcaneMcpPin` do package.json (fonte unica; o servidor le o mesmo campo no npm).
298
298
  `npm run release:verify` recusa publicar com um pin que o npm nao resolva.
299
- Os instaladores do repositorio ficam preparados para 2.8.8; as copias publicas so recebem
299
+ Os instaladores do repositorio ficam preparados para 2.8.9; as copias publicas so recebem
300
300
  a versao apos os gates nativos e a verificacao do tarball publicado.
301
301
  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.
302
302
 
@@ -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.8.8 check-access
592
+ & (Get-AuroqNpx) -y auroq-os@2.8.9 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.8.8 instalar
680
+ & (Get-AuroqNpx) -y auroq-os@2.8.9 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.'
@@ -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.8.8 "$@"
222
+ npx -y auroq-os@2.8.9 "$@"
223
223
  else
224
- npx -y auroq-os@2.8.8 "$@" < /dev/tty
224
+ npx -y auroq-os@2.8.9 "$@" < /dev/tty
225
225
  fi
226
226
  }
227
227
 
package/lib/cofre.js CHANGED
@@ -88,6 +88,10 @@ function ehWindows(d) {
88
88
  return ((d && d.platform) || process.platform) === 'win32';
89
89
  }
90
90
 
91
+ function ehMac(d) {
92
+ return ((d && d.platform) || process.platform) === 'darwin';
93
+ }
94
+
91
95
  function nowOf(d) {
92
96
  return d && typeof d.now === 'function' ? d.now() : Date.now();
93
97
  }
@@ -501,14 +505,14 @@ function makeOpBackend(d) {
501
505
  }
502
506
 
503
507
  // Windows: o `op` instalado DEPOIS de o programa-pai abrir (winget so muda o PATH no registro) nao esta no PATH deste
504
- // processo: o spawn da ENOENT. Recompoe o PATH UMA vez por backend (cada recomposicao abre o PowerShell) e tenta o
508
+ // processo: o spawn da ENOENT. Mac sem Homebrew: o `op` mora em ~/.auroq/op/bin, que um terminal comum nao tem no PATH. Recompoe o PATH UMA vez por backend (cada recomposicao abre o PowerShell) e tenta o
505
509
  // comando de novo; chamadas em paralelo esperam a mesma recomposicao. Devolve false se nao ha como recompor.
506
510
  let recomposicao = null;
507
511
  function recomporPathUmaVez() {
508
512
  if (!recomposicao) {
509
513
  recomposicao = (async () => {
510
514
  const o = mod(d, 'onepassword');
511
- const recompor = typeof d.refreshPath === 'function' ? d.refreshPath : o.refreshWindowsPath;
515
+ const recompor = typeof d.refreshPath === 'function' ? d.refreshPath : (ehWindows(d) ? o.refreshWindowsPath : o.refreshPath);
512
516
  if (typeof recompor !== 'function') return false;
513
517
  try { await recompor(); } catch { /* melhor esforco: o comando decide se achou */ }
514
518
  return true;
@@ -531,7 +535,7 @@ function makeOpBackend(d) {
531
535
  return run(args, { env, input, timeoutMs: OP_TIMEOUT_MS });
532
536
  };
533
537
  let r = await tentar();
534
- if (r && r.missing && ehWindows(d) && (await recomporPathUmaVez())) r = await tentar();
538
+ if (r && r.missing && (ehWindows(d) || ehMac(d)) && (await recomporPathUmaVez())) r = await tentar();
535
539
  return r;
536
540
  }
537
541
 
@@ -84,20 +84,102 @@ function opVersion() {
84
84
  return r.ok ? r.stdout.trim() : null;
85
85
  }
86
86
 
87
+ // 1Password CLI oficial para Mac SEM Homebrew (teste real num Mac novo, 06/11/2026: aluno nao tem `brew`, e instalar o
88
+ // Homebrew pede a senha de admin e as ferramentas da Apple). Mesma versao, mesmo endereco e mesmo SHA-256 que o app
89
+ // Auroq OS usa (auroq-os-app, auroq/primeira-execucao/instalador-do-op.ts), na mesma pasta: ~/.auroq/op/bin.
90
+ const OP_DIRETO = {
91
+ versao: '2.41.1',
92
+ pacotes: {
93
+ arm64: '2289771b68b521ee3dc1d63a013a6a1c5691e0c53e4c4fe79af8042b6ad3c7f5',
94
+ x64: 'e77fc9a8ebb23c176fed7c56ea6e4a7184d2b89957e9a5d9a2feb724e7e0574b',
95
+ },
96
+ };
97
+ // Team ID da AgileBits (dona do 1Password) na assinatura da Apple: o `op` e o app so entram se vierem assinados por ela.
98
+ const AGILEBITS_TEAM_ID = '2BUA8C4S2C';
99
+
100
+ /** Pasta do `op` instalado sem Homebrew (a mesma do app Auroq OS, que ja a poe no PATH dos terminais dele). */
101
+ function auroqOpBin(home = os.homedir()) {
102
+ return path.join(home, '.auroq', 'op', 'bin');
103
+ }
104
+
105
+ function opDiretoUrl(arch) {
106
+ const pacote = arch === 'arm64' ? 'arm64' : 'amd64';
107
+ return `https://cache.agilebits.com/dist/1P/op2/pkg/v${OP_DIRETO.versao}/op_darwin_${pacote}_v${OP_DIRETO.versao}.zip`;
108
+ }
109
+
110
+ // Requisito de assinatura conferido pelo proprio macOS: certificado emitido pela Apple (Developer ID) para a equipe
111
+ // da AgileBits. So o texto "TeamIdentifier=" nao basta: uma assinatura propria poderia imitar o identificador.
112
+ const AGILEBITS_REQUISITO = `anchor apple generic and certificate leaf[subject.OU] = "${AGILEBITS_TEAM_ID}"`;
113
+
114
+ /** O programa tem assinatura valida, emitida pela Apple para a AgileBits (`codesign -R`)? Nunca lanca. */
115
+ function assinadoPelaAgilebits(alvo, run, { deep = false } = {}) {
116
+ const v = run('/usr/bin/codesign', ['--verify', '--strict', ...(deep ? ['--deep'] : []), `-R=${AGILEBITS_REQUISITO}`, alvo]);
117
+ if (!v || !v.ok) return false;
118
+ const info = run('/usr/bin/codesign', ['-dv', '--verbose=2', alvo]);
119
+ const texto = `${(info && info.stdout) || ''}\n${(info && info.stderr) || ''}`;
120
+ return new RegExp(`^TeamIdentifier=${AGILEBITS_TEAM_ID}$`, 'm').test(texto);
121
+ }
122
+
123
+ /**
124
+ * Mac sem Homebrew: baixa o `op` oficial (versao fixa), confere o SHA-256 e a assinatura da AgileBits ANTES de por no
125
+ * lugar e instala em ~/.auroq/op/bin, sem senha. Poe a pasta no PATH deste processo. Nunca lanca; devolve true/false.
126
+ * `deps` so existe para os testes.
127
+ */
128
+ function installOpDirect(deps = {}) {
129
+ const d = {
130
+ arch: process.arch,
131
+ home: os.homedir(),
132
+ run: (cmd, args, o) => runProgram(cmd, args, o),
133
+ pacotes: OP_DIRETO.pacotes,
134
+ tmpdir: os.tmpdir(),
135
+ out: { log, err },
136
+ ...deps,
137
+ };
138
+ const esperado = d.pacotes[d.arch === 'arm64' ? 'arm64' : 'x64'];
139
+ const destino = auroqOpBin(d.home);
140
+ let tmp = null;
141
+ try {
142
+ tmp = fs.mkdtempSync(path.join(d.tmpdir, 'auroq-op-'));
143
+ const zip = path.join(tmp, 'op.zip');
144
+ const aberto = path.join(tmp, 'aberto');
145
+ d.out.log(`${DIM}Baixando o 1Password CLI oficial ${OP_DIRETO.versao} (sem Homebrew)...${RESET}`);
146
+ const baixou = d.run('/usr/bin/curl', ['-fL', '--proto', '=https', '--proto-redir', '=https', '--silent', '--show-error', '--connect-timeout', '30', '--max-time', '900', '-o', zip, opDiretoUrl(d.arch)]);
147
+ if (!baixou.ok || !fs.existsSync(zip)) { d.out.err('Nao consegui baixar o 1Password CLI oficial (internet?).'); return false; }
148
+ const obtido = crypto.createHash('sha256').update(fs.readFileSync(zip)).digest('hex');
149
+ if (obtido !== esperado) { d.out.err('O arquivo baixado do 1Password CLI nao confere (SHA-256 diferente); nao instalei nada.'); return false; }
150
+ fs.mkdirSync(aberto, { recursive: true });
151
+ const abriu = d.run('/usr/bin/ditto', ['-x', '-k', zip, aberto]);
152
+ const novo = path.join(aberto, 'op');
153
+ if (!abriu.ok || !fs.existsSync(novo)) { d.out.err('Nao consegui abrir o pacote do 1Password CLI.'); return false; }
154
+ if (!assinadoPelaAgilebits(novo, d.run)) { d.out.err('O 1Password CLI baixado nao tem a assinatura da AgileBits; nao instalei.'); return false; }
155
+ fs.mkdirSync(destino, { recursive: true });
156
+ const final = path.join(destino, 'op');
157
+ const provisorio = path.join(destino, `.op.auroq-${process.pid}.tmp`);
158
+ fs.copyFileSync(novo, provisorio);
159
+ fs.chmodSync(provisorio, 0o755);
160
+ fs.renameSync(provisorio, final);
161
+ d.run('/usr/bin/xattr', ['-d', 'com.apple.quarantine', final]); // melhor esforco: curl nao marca quarentena
162
+ refreshPath({ platform: 'darwin', home: d.home });
163
+ return true;
164
+ } catch (e) {
165
+ d.out.err(`Nao consegui instalar o 1Password CLI em ${destino} (${oneLine(e && e.message, 120)}).`);
166
+ return false;
167
+ } finally {
168
+ if (tmp) { try { fs.rmSync(tmp, { recursive: true, force: true }); } catch { /* fica no temp do sistema */ } }
169
+ }
170
+ }
171
+
87
172
  function installOp({ upgrade = false } = {}) {
88
173
  if (process.platform === 'darwin') {
89
- if (!hasCommand('brew')) {
90
- err('Homebrew nao encontrado. Instale em https://brew.sh e rode este comando de novo.');
91
- return false;
92
- }
174
+ if (!hasCommand('brew')) return installOpDirect();
93
175
  log(`${DIM}${upgrade ? 'Atualizando' : 'Instalando'} via Homebrew (pode levar um minuto)...${RESET}`);
94
176
  const brewCmd = upgrade ? 'brew upgrade --cask 1password-cli' : 'brew install --cask 1password-cli';
95
177
  try {
96
178
  execSync(brewCmd, { stdio: 'pipe', maxBuffer: 64 * 1024 * 1024, env: opEnv() });
97
179
  return true;
98
180
  } catch {
99
- err(`${upgrade ? 'Atualizacao' : 'Instalacao'} via Homebrew falhou. Tente manualmente: ${brewCmd}`);
100
- return false;
181
+ warn(`${upgrade ? 'Atualizacao' : 'Instalacao'} via Homebrew falhou; vou usar o pacote oficial do 1Password.`);
182
+ return installOpDirect();
101
183
  }
102
184
  }
103
185
 
@@ -392,6 +474,7 @@ async function connectManual() {
392
474
 
393
475
  // 1. Garantir que o op existe
394
476
  step('Verificando 1Password CLI...');
477
+ refreshPath();
395
478
  let version = opVersion();
396
479
  if (!version) {
397
480
  warn('1Password CLI (op) nao encontrado — instalando...');
@@ -786,6 +869,17 @@ function openUrl(url) {
786
869
  * Windows: o winget so muda o PATH no registro, nao no processo que ja esta rodando. Recompoe o
787
870
  * PATH deste processo (maquina + usuario + pasta de links do winget) para achar o `op` recem-instalado.
788
871
  */
872
+ /**
873
+ * Recompõe o PATH deste processo para achar um `op` instalado depois que ele abriu. Mac/Linux: a pasta do `op`
874
+ * instalado sem Homebrew (~/.auroq/op/bin), se existir. Windows: o PATH do registro (o winget so muda o registro).
875
+ */
876
+ function refreshPath({ platform = process.platform, home = os.homedir() } = {}) {
877
+ if (platform === 'win32') return refreshWindowsPath();
878
+ const bin = auroqOpBin(home);
879
+ const atual = String(process.env.PATH || '').split(':');
880
+ if (!atual.includes(bin) && fs.existsSync(path.join(bin, 'op'))) process.env.PATH = [bin, ...atual.filter(Boolean)].join(':');
881
+ }
882
+
789
883
  function refreshWindowsPath() {
790
884
  if (process.platform !== 'win32') return;
791
885
  const parts = [];
@@ -1020,23 +1114,82 @@ function makeAppDeps() {
1020
1114
  lock: makeLock(),
1021
1115
  hasCommand,
1022
1116
  appInstalled: () => appInstalled(),
1023
- refreshPath: refreshWindowsPath,
1117
+ refreshPath,
1024
1118
  run: runProgram,
1025
1119
  runInstaller: runInstallerProgram,
1026
1120
  onInterrupt: onInterruptCleanup,
1027
1121
  sweepStale: () => sweepStaleAppDirs(),
1028
1122
  download: downloadToFile,
1029
1123
  checkSignature: checkInstallerSignature,
1124
+ macAppDirect: () => installMacAppDirect(),
1030
1125
  mkdtemp: () => fs.mkdtempSync(path.join(os.tmpdir(), APP_TEMP_PREFIX)),
1031
1126
  out: { log, step, ok, warn, err },
1032
1127
  };
1033
1128
  }
1034
1129
 
1130
+ // App do 1Password para Mac: o zip oficial do app inteiro (o mesmo arquivo que o Homebrew baixa, sem fixar versao).
1131
+ const MAC_APP_URL = (arch) => `https://downloads.1password.com/mac/1Password-latest-${arch === 'arm64' ? 'aarch64' : 'x86_64'}.zip`;
1132
+
1133
+ /**
1134
+ * Mac sem Homebrew: baixa o app oficial, confere a assinatura da AgileBits (Team ID e `codesign --deep`) e copia para
1135
+ * /Applications (o aluno administrador do Mac grava ali sem senha) ou, sem permissao, para ~/Applications.
1136
+ * Nunca lanca. { ok, motivo, destino }. `deps` so existe para os testes.
1137
+ */
1138
+ async function installMacAppDirect(deps = {}) {
1139
+ const d = {
1140
+ arch: process.arch,
1141
+ home: os.homedir(),
1142
+ run: (cmd, args, o) => runProgram(cmd, args, o),
1143
+ download: downloadToFile,
1144
+ mkdtemp: () => fs.mkdtempSync(path.join(os.tmpdir(), APP_TEMP_PREFIX)),
1145
+ gravavel: (dir) => { try { fs.accessSync(dir, fs.constants.W_OK); return true; } catch { return false; } },
1146
+ aplicativos: '/Applications',
1147
+ out: { log },
1148
+ ...deps,
1149
+ };
1150
+ let dir = null;
1151
+ let provisorio = null;
1152
+ try {
1153
+ dir = d.mkdtemp();
1154
+ const zip = path.join(dir, '1Password.zip');
1155
+ const aberto = path.join(dir, 'aberto');
1156
+ d.out.log(`${DIM}Baixando o app oficial do 1Password (uns 220 MB, pode levar alguns minutos)...${RESET}`);
1157
+ try {
1158
+ await d.download(MAC_APP_URL(d.arch), zip);
1159
+ } catch (e) {
1160
+ return { ok: false, motivo: `Nao consegui baixar o app oficial (${oneLine(e && e.message, 120)}).` };
1161
+ }
1162
+ fs.mkdirSync(aberto, { recursive: true });
1163
+ const abriu = d.run('/usr/bin/ditto', ['-x', '-k', zip, aberto]);
1164
+ const app = path.join(aberto, '1Password.app');
1165
+ if (!abriu.ok || !fs.existsSync(app)) return { ok: false, motivo: 'Nao consegui abrir o pacote do app.' };
1166
+ if (!assinadoPelaAgilebits(app, d.run, { deep: true })) return { ok: false, motivo: 'O app baixado nao tem a assinatura da AgileBits; nao instalei.' };
1167
+ const pasta = d.gravavel(d.aplicativos) ? d.aplicativos : path.join(d.home, 'Applications');
1168
+ fs.mkdirSync(pasta, { recursive: true });
1169
+ const destino = path.join(pasta, '1Password.app');
1170
+ // copia para um nome provisorio na mesma pasta, confere a COPIA e so entao poe o nome final: uma copia pela metade
1171
+ // nunca fica com cara de app instalado (o appInstalled olha so se a pasta existe)
1172
+ provisorio = path.join(pasta, `.1Password.app.auroq-${process.pid}.tmp`);
1173
+ const copiou = d.run('/usr/bin/ditto', [app, provisorio]);
1174
+ if (!copiou.ok) return { ok: false, motivo: `Nao consegui copiar o app para ${pasta}.` };
1175
+ if (!assinadoPelaAgilebits(provisorio, d.run, { deep: true })) return { ok: false, motivo: `A copia do app em ${pasta} nao passou na conferencia da assinatura; nao instalei.` };
1176
+ fs.renameSync(provisorio, destino);
1177
+ provisorio = null;
1178
+ return { ok: true, destino };
1179
+ } catch (e) {
1180
+ return { ok: false, motivo: `Nao consegui instalar o app (${oneLine(e && e.message, 120)}).` };
1181
+ } finally {
1182
+ if (provisorio) { try { fs.rmSync(provisorio, { recursive: true, force: true }); } catch { /* sobra so uma pasta oculta */ } }
1183
+ if (dir) { try { fs.rmSync(dir, { recursive: true, force: true }); } catch { /* fica no temp; a proxima execucao varre */ } }
1184
+ }
1185
+ }
1186
+
1035
1187
  const APP_NEXT_STEPS = 'Agora abra o app do 1Password, entre na sua conta (ou crie uma: guarde a Secret Key e o Kit de Emergencia que ele oferece) e ligue "Integrar com a CLI do 1Password" em Configuracoes > Desenvolvedor. Depois rode o conectar-1password de novo.';
1036
1188
 
1037
1189
  /**
1038
1190
  * Instala o app do 1Password (`conectar-1password --instalar-app`). Windows: winget e, se ele falhar, o instalador
1039
- * oficial, so rodado depois de conferir a assinatura (valida e do publicador Agilebits). Mac: Homebrew (cask).
1191
+ * oficial, so rodado depois de conferir a assinatura (valida e do publicador Agilebits). Mac: Homebrew (cask), se o aluno
1192
+ * tiver; senao (ou se ele falhar), o pacote oficial com a assinatura da AgileBits conferida (installMacAppDirect).
1040
1193
  * O app e um programa novo no computador do aluno: quem chama pede o OK dele antes.
1041
1194
  */
1042
1195
  async function installApp(deps = {}) {
@@ -1062,11 +1215,16 @@ async function installApp(deps = {}) {
1062
1215
  };
1063
1216
 
1064
1217
  if (d.platform === 'darwin') {
1065
- if (!d.hasCommand('brew')) return falhou('Homebrew nao encontrado, entao nao consigo instalar o app daqui.');
1066
- out.log(`${DIM}Instalando via Homebrew (pode levar um minuto)...${RESET}`);
1067
- const r = d.run('brew', ['install', '--cask', '1password']);
1068
- if (r.ok || d.appInstalled()) return pronto('Homebrew');
1069
- return falhou(`A instalacao via Homebrew falhou (${r.timedOut ? 'demorou demais' : `codigo de saida ${r.status}`}).`);
1218
+ if (d.hasCommand('brew')) {
1219
+ out.log(`${DIM}Instalando via Homebrew (pode levar um minuto)...${RESET}`);
1220
+ const r = d.run('brew', ['install', '--cask', '1password']);
1221
+ if (r.ok || d.appInstalled()) return pronto('Homebrew');
1222
+ out.warn(`A instalacao via Homebrew falhou (${r.timedOut ? 'demorou demais' : `codigo de saida ${r.status}`}). Vou usar o pacote oficial do 1Password.`);
1223
+ }
1224
+ // Mac de aluno nao tem Homebrew (e instalar o Homebrew pede senha de admin): pacote oficial, assinatura conferida.
1225
+ const m = await d.macAppDirect();
1226
+ if (m.ok) return pronto('pacote oficial do 1Password');
1227
+ return falhou(m.motivo || 'Nao consegui instalar o app pelo pacote oficial.');
1070
1228
  }
1071
1229
 
1072
1230
  // Windows 1: winget (rapido, mas falhou no teste real com 0x80070002 mesmo depois de conferir o hash)
@@ -1270,7 +1428,7 @@ function makeDefaultDeps() {
1270
1428
  return {
1271
1429
  platform: process.platform,
1272
1430
  openUrl,
1273
- refreshPath: refreshWindowsPath,
1431
+ refreshPath,
1274
1432
  appInstalled: () => appInstalled(),
1275
1433
  lock: makeLock(),
1276
1434
  state: { read: readState, write: writeState },
@@ -1884,6 +2042,7 @@ async function finishWithToken(d, out, account, token, prog, { reaproveitada, ol
1884
2042
  function describeState(d) {
1885
2043
  const saved = d.state.read();
1886
2044
  const etapas = saved ? { ...saved.etapas } : emptyEtapas();
2045
+ if (typeof d.refreshPath === 'function') d.refreshPath(); // Mac sem Homebrew: o op mora em ~/.auroq/op/bin
1887
2046
  const version = d.opVersion();
1888
2047
  const opOk = Boolean(version && isVersionAtLeast(version));
1889
2048
  etapas.op = opOk ? 'feito' : 'pendente';
@@ -2036,6 +2195,11 @@ module.exports = {
2036
2195
  auroqDir,
2037
2196
  openUrl,
2038
2197
  refreshWindowsPath,
2198
+ refreshPath,
2199
+ installOpDirect,
2200
+ installMacAppDirect,
2201
+ auroqOpBin,
2202
+ OP_DIRETO,
2039
2203
  runOp,
2040
2204
  TOKEN_VAR,
2041
2205
  MIN_OP_VERSION,
@@ -2,7 +2,7 @@
2
2
  "schema": 1,
3
3
  "algorithm": "sha256",
4
4
  "scope": "packaged-runtime-and-cascas",
5
- "version": "2.8.8",
5
+ "version": "2.8.9",
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": "f8d396a2a696af94b4a96822d5eec348510fa1e0e0fb6f71107118dd15e2adc1"
13
+ "sha256": "7eeba357e5203bb8d0d9ade5d4a53ecfa99d7edd05212a5625b59d6425f641f4"
14
14
  },
15
15
  {
16
16
  "path": ".auroq-core/core/synapse/context/context-builder.js",
@@ -254,7 +254,7 @@
254
254
  },
255
255
  {
256
256
  "path": "lib/cofre.js",
257
- "sha256": "8dff20e576fda737f5039ceaca64e8e18fe79e19460c3ce0bac1262f7fa490df"
257
+ "sha256": "a7142a3a49e7cb71f4927d567fa834b838c19987f12bd1d7fe02e1cb744378c1"
258
258
  },
259
259
  {
260
260
  "path": "lib/credentials.js",
@@ -282,7 +282,7 @@
282
282
  },
283
283
  {
284
284
  "path": "lib/onepassword.js",
285
- "sha256": "d1bf61b459f81dfb7e13e2c694c1ead1d3146ba8057ff74fcf5d46beb6558cdf"
285
+ "sha256": "34ce88cb11b2fc9bd52905acc19903430df88db5c94b3ed9bc1000bfb1f4a666"
286
286
  },
287
287
  {
288
288
  "path": "lib/supabase.js",
@@ -298,7 +298,7 @@
298
298
  },
299
299
  {
300
300
  "path": "package.json",
301
- "sha256": "e8a718a7f9f7345742d5cf7a894deeee0cf9308bfaf49dc3635c530ff0e4a3bd"
301
+ "sha256": "0f06b1ac3cb3201dd3f21c3ea980b47ba637fd40a300534d07610de091f54e6b"
302
302
  },
303
303
  {
304
304
  "path": "scripts/build-pack.mjs",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "auroq-os",
3
- "version": "2.8.8",
3
+ "version": "2.8.9",
4
4
  "arcaneMcpPin": "0.7.1",
5
5
  "description": "Auroq OS — Sistema Operacional de IA para o negócio do aluno da Arcane",
6
6
  "bin": {