auroq-os 2.8.5 → 2.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.auroq-core/core-config.yaml +1 -1
- package/AGENTS.md +4 -0
- package/CHANGELOG.md +12 -0
- package/README.md +4 -4
- package/installer/install.ps1 +2 -2
- package/installer/install.sh +2 -2
- package/lib/onepassword.js +173 -10
- package/lib/runtime-integrity.json +4 -4
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -76,6 +76,10 @@ nunca como fonte do método.
|
|
|
76
76
|
- Pedido de explicar, revisar ou diagnosticar: olhe e responda, sem mudar nada.
|
|
77
77
|
- Pedido de fazer, mudar, corrigir ou publicar: faça até o resultado conferido. A ordem vale para
|
|
78
78
|
todos os passos de rotina daquele pedido.
|
|
79
|
+
- **Vai e faz até o final, sempre que der.** Só pare para perguntar diante de um impeditivo real: uma
|
|
80
|
+
das travas abaixo, senha pessoal, código do celular, CAPTCHA ou decisão de negócio que só o aluno toma.
|
|
81
|
+
Com a resposta, continue até o fim sem pedir outro OK. Passo de rotina do pedido não pede licença:
|
|
82
|
+
quanto mais a IA faz sem o aluno precisar fazer, melhor.
|
|
79
83
|
- Antes de perguntar, procure no arquivo, na memória, no histórico ou na ferramenta. Decidiu sozinho:
|
|
80
84
|
diga a suposição em uma linha.
|
|
81
85
|
- Resolva antes de devolver: ferramentas, CLIs, MCPs e logins já configurados primeiro. Credencial vem
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
## Unreleased
|
|
2
2
|
|
|
3
|
+
- 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).
|
|
3
4
|
- Conteúdo ao vivo (catálogo `auroq-core` 2.3.53, 29/10): **a skill do cofre de senhas foi dividida em seis peças** (entrada, 1Password, Infisical grátis com os resultados à parte, passar do grátis para o 1Password, e uso do dia a dia). Antes era um texto só de 62 mil caracteres, grande demais para o Claude Code ler de uma vez (o Marcos viu isso no teste de 27/10). O Companion continua entrando pelo mesmo gatilho (`conectar-cofre`) e a entrada diz qual peça abrir. O Companion agora pede o OK do aluno em uma frase antes de criar a chave do agente, de instalar o app e de passar as chaves (a proteção do Claude Code barrava esses passos sem aviso), explica a Chave Secreta e o Kit de Emergência do 1Password (guardar o PDF fora do computador e fora do GitHub), ensina a ligar o Windows Hello ou o Touch ID quando a digital não aparece, e não manda mais ligar a chave do app de novo quando ela já está ligada. Precisa do auroq-os 2.8.5 no npm (comando `--instalar-app` e códigos novos).
|
|
4
5
|
- Conteúdo ao vivo (catálogo `auroq-core` 2.3.52, 27/10): **documentação revisada para o jeito de hoje.** O guia do sistema que o Companion usa com o aluno, a fundação do Companion e as fichas do Consultor não apresentam mais o Ops como técnico nem o nome personalizado do Companion; ensinam a abrir uma conversa nova dentro do projeto e chamar o Companion (quantas quiser, uma por assunto, nunca um chat por squad) e dizem que, com o app aberto, o salvamento na internet é automático a cada 15 minutos e ao abrir e fechar. A skill `configurar-computador` e o guia longo do Ops dizem que as contas são conectadas pelo Companion na primeira conversa, não na instalação.
|
|
5
6
|
- Motor: README com o app Auroq OS como caminho novo e o terminal como o caminho que a plataforma ensina hoje; segunda máquina pelo app ("Trazer projeto que já existe"); atualizar, primeiro uso e agentes core sem o Ops; endereço da plataforma `arcane.arka.education`. `AGENTS.md` e `.auroq-core/como-funciona.md` dizem que, com o app aberto, salvar e entregar são automáticos a cada 15 minutos.
|
|
@@ -17,6 +18,17 @@
|
|
|
17
18
|
- Conteúdo ao vivo (catálogo `auroq-core` 2.3.40, 26/10): **as contas do nível 1 (GitHub, Vercel e Supabase) passam a ser conectadas pelo Companion na primeira conversa.** Decisão do Euriler: conectar na instalação do app não funciona bem para o aluno iniciante. Skill nova `conexoes-essenciais`: o Companion explica cada conta em uma linha, abre as páginas (GitHub primeiro, com a conta criada junto se o aluno não tiver; a Vercel e o Supabase entram com o GitHub), confere e segue; o aluno só cria a conta, entra e autoriza, sem senha nem código na conversa, sempre no plano grátis. Usa o comando `auroq conexoes` do app quando existir; antes disso, a tela Configurações → Conexões. O `*primeiro-uso` ficou: conferência e instalação do que faltar (o aluno nunca baixa nem instala programa), material, perguntas, Mapa, conexões (P6), organizar (P7) e fecho (P8).
|
|
18
19
|
- Conteúdo ao vivo (catálogo `auroq-core` 2.3.39, 26/10): **o cofre de senhas saiu da primeira conversa.** Decisão do Euriler: é técnico demais para o aluno iniciante e é conexão do nível 2 (com domínio próprio e Google, que o app chama de "conecte quando precisar"). No `*primeiro-uso` o Companion confere só o sistema e as contas do nível 1 (GitHub, Vercel e Supabase, que o app conecta na tela "Conectar suas contas") e vai para a entrevista; o cofre entra quando a primeira tarefa precisar de uma senha, chave ou token (skill `conectar-cofre`).
|
|
19
20
|
- **Regra de projetos do aluno sem o nome do Euriler.** `project-tracker.md` mandava "avisar o Euriler" quando faltava o acompanhamento de um projeto e dizia que só "o Euriler/Jarvis" mudam objetivo e fases (cópia da regra pessoal dele). Agora o Companion cria o acompanhamento pelo modelo e avisa o aluno em uma linha; quem decide objetivo e fases é o aluno (o Companion propõe). Chega pelo npm.
|
|
21
|
+
## 2.8.6 — 2026-11-04
|
|
22
|
+
|
|
23
|
+
Correção do re-teste real do Marcos (03/11, Windows 11, auroq-os 2.8.5). O resto da rota A (aprovação, cofre `Claude`, chave do agente, teste e migração) passou liso e não foi mexido. Nenhuma máquina de aluno foi tocada; o que dá para provar sem Windows foi provado com PowerShell, winget e instalador simulados (veja a story `AUROQ-1password-automatico`, seção "Re-teste do Marcos 03/11").
|
|
24
|
+
|
|
25
|
+
- **O app do 1Password instalado pelo `winget` agora é reconhecido.** O `winget` instala o 1Password como pacote MSIX (`C:\Program Files\WindowsApps\Agilebits.1Password_...`), pasta que o comando não olhava e que um usuário comum nem consegue listar. Agora, além das pastas de sempre, ele pergunta ao Windows pela lista de pacotes do próprio usuário (`Get-AppxPackage`, sem admin, em texto claro). Se o Windows não responder, o comando diz "não sei" e nunca "ausente".
|
|
26
|
+
- **Saída 0 do `winget` = confia.** Antes, como o app não aparecia nas pastas, o comando achava que o `winget` tinha falhado, avisava isso (não era verdade) e rodava o instalador oficial por cima, que travou escondido por 6 minutos no teste. Agora saída 0 encerra a instalação; o instalador de reserva só roda se o `winget` falhou **e** o app de fato não está no computador.
|
|
27
|
+
- **Instalador de reserva mais seguro.** Limite de 3 minutos (era 15): passou disso, o comando interrompe o instalador e os processos filhos, diz a verdade ("passou de 3 minutos sem terminar e eu o interrompi; não sei dizer por que") e devolve `app-nao-instalou`. Roda sem janela escondida (a hipótese, não provada, é que o travamento vinha de uma janela invisível esperando resposta; rodado à vista ele terminou em 13 segundos). A pasta temporária é apagada ao terminar, no prazo estourado e também com Ctrl+C ou terminal fechado; pasta velha de uma execução morta à força é varrida na próxima vez.
|
|
28
|
+
- **O comando principal não manda mais reinstalar o app que já está instalado.** Com o app só como MSIX e a conta ainda não listada, devolvia `instalar-app` de novo (risco de laço). Agora devolve `ligar-integracao`.
|
|
29
|
+
- Skill do Companion: aviso do pacote MSIX antes de dizer "não instalado" (veja a nota do catálogo `auroq-core` 2.3.54 acima).
|
|
30
|
+
- Instaladores fixam `auroq-os@2.8.6`.
|
|
31
|
+
|
|
20
32
|
## 2.8.5 — 2026-10-29
|
|
21
33
|
|
|
22
34
|
Correções do teste real do Marcos (27/10, Windows 11 zerado). O cofre grátis (Infisical) rodou liso; o 1Password travou em quatro pontos, e o Companion girava em círculo. Nenhuma máquina de aluno foi tocada; o que dá para provar sem Windows foi provado com um 1Password de mentira que agora se comporta como o de verdade (veja a story `AUROQ-1password-automatico`, seção "Teste real do Marcos 27/10").
|
package/README.md
CHANGED
|
@@ -57,7 +57,7 @@ nem atualizar Pack.
|
|
|
57
57
|
| `auroq-os doctor` | Diagnostico do suporte: arquivos, motor do sistema (e se o programa aberto pelo icone enxerga ele), registro do conteudo ao vivo, login e servidor |
|
|
58
58
|
| `auroq-os register-mcp` | Reescreve o registro do conteudo ao vivo no projeto (use se o `doctor` apontar que ele falta) |
|
|
59
59
|
| `auroq-os sync-codex` | Regenera e verifica as skills locais do Codex |
|
|
60
|
-
| `auroq-os conectar-1password` | Liga o 1Password ao Auroq sozinho (Mac/Windows): instala o `op` se faltar, cria o cofre `Claude` e uma conta de servico que so enxerga esse cofre (sem data de validade), guarda a chave no computador e testa. Voce so aprova no app do 1Password. O Companion roda isto por voce (`--agente`). Opcoes: `--conta "<e-mail>"` (escolhe a conta quando o app tem mais de uma), `--rotacionar` (cria uma chave nova), `--instalar-app` (instala o app do 1Password: Windows pelo winget e, se ele falhar, pelo instalador oficial depois de conferir a assinatura; Mac pelo Homebrew), `--estado` (mostra em que etapa voce parou, sem falar com o 1Password; com `--agente` imprime a linha `AUROQ_ESTADO ...`). Pode ser retomado a qualquer hora: ele reconfere cada etapa e continua de onde parou. O Companion roda isto sozinho quando falta o cofre (skill `conectar-cofre`, que pergunta qual cofre) |
|
|
60
|
+
| `auroq-os conectar-1password` | Liga o 1Password ao Auroq sozinho (Mac/Windows): instala o `op` se faltar, cria o cofre `Claude` e uma conta de servico que so enxerga esse cofre (sem data de validade), guarda a chave no computador e testa. Voce so aprova no app do 1Password. O Companion roda isto por voce (`--agente`). Opcoes: `--conta "<e-mail>"` (escolhe a conta quando o app tem mais de uma), `--rotacionar` (cria uma chave nova), `--instalar-app` (instala o app do 1Password: Windows pelo winget, que se terminar bem encerra o assunto, e, so se ele falhar e o app nao estiver la, pelo instalador oficial depois de conferir a assinatura, com limite de 3 minutos; Mac pelo Homebrew), `--estado` (mostra em que etapa voce parou, sem falar com o 1Password; com `--agente` imprime a linha `AUROQ_ESTADO ...`). Pode ser retomado a qualquer hora: ele reconfere cada etapa e continua de onde parou. O Companion roda isto sozinho quando falta o cofre (skill `conectar-cofre`, que pergunta qual cofre) |
|
|
61
61
|
| `auroq-os conectar-cofre` | Escolhe e liga o cofre de senhas: **1Password** (pago, uns US$ 3 por mes, confira o preco na tela, recomendado; preenche senha no navegador) ou **Infisical** (gratis, so guarda as chaves que o agente usa). Opcoes: `--cofre 1password\|infisical` (sem ela, pergunta qual), `--regiao us\|eu` (Infisical), `--estado` (em que etapa voce parou, sem falar com o cofre; com `--agente` imprime a linha `AUROQ_ESTADO ...`), `--rotacionar` (chave nova) e `--migrar` (passa as chaves do Infisical para o 1Password, sem apagar nada; so depois que todas passaram o 1Password vira o cofre ativo, e `--ignorar-fora-do-formato` deixa no Infisical as chaves com nome que o Auroq nao usa). Ligar o 1Password sozinho nao troca o cofre de quem ja usa o Infisical; ligar o Infisical com o 1Password ativo troca, e as chaves do 1Password nao sao levadas (continuam la, `segredo ler NOME --cofre 1password`). O Companion roda isto por voce (`--agente`) |
|
|
62
62
|
| `auroq-os segredo` | Le e guarda credenciais no cofre que voce escolheu, igual nos dois: `segredo ler\|gravar\|listar\|apagar\|qual <referencia>`. O valor de `gravar` entra por stdin, nunca como argumento; `ler` imprime o valor, entao use sempre dentro do comando que o usa (`X="$(auroq-os segredo ler Item/campo)" && comando "$X"`), nunca sozinho; a referencia e `op://Claude/<Item>/<campo>` ou `cofre://<Item>/<campo>`. Com 1Password, `op read` direto continua valendo. Squads, skills e agentes usam este comando |
|
|
63
63
|
|
|
@@ -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.
|
|
294
|
-
manutencao/diagnostico. `npx -y auroq-os@2.8.
|
|
293
|
+
Apos publicacao, `npx -y auroq-os@2.8.6 doctor` mostra a fila local segura e os comandos pinados de
|
|
294
|
+
manutencao/diagnostico. `npx -y auroq-os@2.8.6 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.
|
|
299
|
+
Os instaladores do repositorio ficam preparados para 2.8.6; 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
|
|
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.8.
|
|
592
|
+
& (Get-AuroqNpx) -y auroq-os@2.8.6 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.
|
|
680
|
+
& (Get-AuroqNpx) -y auroq-os@2.8.6 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.8.
|
|
222
|
+
npx -y auroq-os@2.8.6 "$@"
|
|
223
223
|
else
|
|
224
|
-
npx -y auroq-os@2.8.
|
|
224
|
+
npx -y auroq-os@2.8.6 "$@" < /dev/tty
|
|
225
225
|
fi
|
|
226
226
|
}
|
|
227
227
|
|
package/lib/onepassword.js
CHANGED
|
@@ -798,9 +798,41 @@ const WINGET_APP_ID = 'AgileBits.1Password';
|
|
|
798
798
|
const APP_INSTALLER_URL = 'https://downloads.1password.com/win/1PasswordSetup-latest.exe';
|
|
799
799
|
// Assinante do instalador oficial: "Valid | CN=Agilebits, O=Agilebits, L=Toronto, S=Ontario, C=CA".
|
|
800
800
|
const APP_SIGNER_RE = /(?:^|,\s*)CN=Agilebits(?:,|$)/i;
|
|
801
|
+
// Limite do instalador oficial (reserva): o winget leva ~1 min; o limite antigo de 15 min deixava o aluno esperando sem aviso.
|
|
802
|
+
const APP_SETUP_TIMEOUT_MS = 3 * 60 * 1000;
|
|
801
803
|
|
|
802
|
-
|
|
803
|
-
|
|
804
|
+
// O winget (e a loja da Microsoft) instalam o 1Password como pacote MSIX: a pasta fica em
|
|
805
|
+
// C:\Program Files\WindowsApps\Agilebits.1Password_<versao>_x64__<id>, que um usuario comum nem consegue listar.
|
|
806
|
+
const MSIX_PACKAGE_NAME = 'Agilebits.1Password';
|
|
807
|
+
const MSIX_QUERY_TIMEOUT_MS = 60 * 1000;
|
|
808
|
+
|
|
809
|
+
/**
|
|
810
|
+
* Windows: o 1Password esta instalado como pacote MSIX para ESTE usuario? A lista de pacotes do proprio usuario
|
|
811
|
+
* (`Get-AppxPackage`) responde sem admin. true | false | null (o PowerShell nao respondeu: nao da para saber).
|
|
812
|
+
* Comando em texto claro, nunca codificado (antivirus le comando escondido como dropper).
|
|
813
|
+
*/
|
|
814
|
+
function msixInstalled(run = spawnSync) {
|
|
815
|
+
const script = `$p = @(Get-AppxPackage -Name '${MSIX_PACKAGE_NAME}' -ErrorAction Stop); Write-Output ('AUROQ_MSIX=' + $p.Count)`;
|
|
816
|
+
let r;
|
|
817
|
+
try {
|
|
818
|
+
r = run('powershell', ['-NoProfile', '-NonInteractive', '-Command', script], {
|
|
819
|
+
encoding: 'utf8', windowsHide: true, timeout: MSIX_QUERY_TIMEOUT_MS, stdio: ['ignore', 'pipe', 'pipe'], env: opEnv(),
|
|
820
|
+
});
|
|
821
|
+
} catch {
|
|
822
|
+
return null;
|
|
823
|
+
}
|
|
824
|
+
if (!r || r.error || r.status !== 0) return null;
|
|
825
|
+
const m = /AUROQ_MSIX=(\d+)/.exec(String(r.stdout || ''));
|
|
826
|
+
return m ? Number(m[1]) > 0 : null;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
/**
|
|
830
|
+
* O app do 1Password esta neste computador? true | false | null (nao sabe: sistema sem instalacao automatica, ou
|
|
831
|
+
* o Windows nao deixou conferir o pacote MSIX). O fluxo principal so manda instalar com `=== false`; o `installApp`
|
|
832
|
+
* trata `null` como "nao esta aqui" e segue (a reserva se protege com limite de tempo).
|
|
833
|
+
* `run` so existe para os testes simularem o PowerShell.
|
|
834
|
+
*/
|
|
835
|
+
function appInstalled(platform = process.platform, env = process.env, run = spawnSync) {
|
|
804
836
|
// AUROQ_APP_1PASSWORD=instalado|ausente: so para os testes de processo real, que rodam em Mac/Windows de verdade
|
|
805
837
|
// (onde o app nao esta) e nao podem depender de o app existir na maquina.
|
|
806
838
|
const forcado = String(env.AUROQ_APP_1PASSWORD || '').toLowerCase();
|
|
@@ -812,7 +844,7 @@ function appInstalled(platform = process.platform, env = process.env) {
|
|
|
812
844
|
if (platform === 'win32') {
|
|
813
845
|
// Instalador do aluno: %LOCALAPPDATA%\1Password\app\8\1Password.exe; instalacao para a maquina: Arquivos de Programas.
|
|
814
846
|
const roots = [env.LOCALAPPDATA, env.ProgramFiles, env['ProgramFiles(x86)']].filter(Boolean);
|
|
815
|
-
|
|
847
|
+
const classico = roots.some((root) => {
|
|
816
848
|
const appDir = path.join(root, '1Password', 'app');
|
|
817
849
|
try {
|
|
818
850
|
return fs.readdirSync(appDir).some((v) => fs.existsSync(path.join(appDir, v, '1Password.exe')));
|
|
@@ -820,6 +852,16 @@ function appInstalled(platform = process.platform, env = process.env) {
|
|
|
820
852
|
return false;
|
|
821
853
|
}
|
|
822
854
|
});
|
|
855
|
+
if (classico) return true;
|
|
856
|
+
// Pacote MSIX (o que o winget instala hoje). A lista de pacotes do usuario manda; a pasta WindowsApps so entra
|
|
857
|
+
// se o PowerShell nao respondeu (so quem e admin consegue listar essa pasta).
|
|
858
|
+
const msix = msixInstalled(run);
|
|
859
|
+
if (msix !== null) return msix;
|
|
860
|
+
try {
|
|
861
|
+
const base = env.ProgramFiles || 'C:\\Program Files';
|
|
862
|
+
if (fs.readdirSync(path.join(base, 'WindowsApps')).some((n) => /^Agilebits\.1Password_/i.test(n))) return true;
|
|
863
|
+
} catch { /* sem permissao para listar: nao e prova de ausencia */ }
|
|
864
|
+
return null;
|
|
823
865
|
}
|
|
824
866
|
return null;
|
|
825
867
|
}
|
|
@@ -877,6 +919,92 @@ function runProgram(cmd, args, { timeoutMs = 15 * 60 * 1000 } = {}) {
|
|
|
877
919
|
};
|
|
878
920
|
}
|
|
879
921
|
|
|
922
|
+
/** Mata o processo e os filhos dele (o instalador oficial lanca outros processos; matar so o pai nao basta). Nunca lanca. */
|
|
923
|
+
function killProcessTree(pid) {
|
|
924
|
+
if (!pid) return;
|
|
925
|
+
try {
|
|
926
|
+
if (process.platform === 'win32') spawnSync('taskkill', ['/PID', String(pid), '/T', '/F'], { stdio: 'ignore', windowsHide: true, timeout: 15000 });
|
|
927
|
+
else process.kill(pid, 'SIGKILL');
|
|
928
|
+
} catch { /* ja tinha saido */ }
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
/**
|
|
932
|
+
* Roda o instalador oficial do app: ASSINCRONO (o Node continua vendo Ctrl+C e o fechar do terminal; com `spawnSync`
|
|
933
|
+
* parado ele morre sem rodar limpeza nenhuma), com limite de tempo que mata o instalador e os filhos dele, e SEM
|
|
934
|
+
* janela escondida: no teste real de 03/11 o mesmo instalador travou escondido e terminou em 13 s rodado a vista. Se
|
|
935
|
+
* ele abrir uma janela, o aluno a ve (e pode responder). Nunca lanca. `signal` (AbortController) cancela de fora.
|
|
936
|
+
*/
|
|
937
|
+
function runInstallerProgram(cmd, args, { timeoutMs = APP_SETUP_TIMEOUT_MS, signal, spawnFn = spawn } = {}) {
|
|
938
|
+
return new Promise((resolve) => {
|
|
939
|
+
let settled = false;
|
|
940
|
+
let timedOut = false;
|
|
941
|
+
let timer = null;
|
|
942
|
+
let child = null;
|
|
943
|
+
const finish = (extra) => {
|
|
944
|
+
if (settled) return;
|
|
945
|
+
settled = true;
|
|
946
|
+
if (timer) clearTimeout(timer);
|
|
947
|
+
if (signal) signal.removeEventListener('abort', onAbort);
|
|
948
|
+
resolve({ ok: false, status: null, stdout: '', stderr: '', timedOut, missing: false, interrupted: false, ...extra });
|
|
949
|
+
};
|
|
950
|
+
const onAbort = () => { killProcessTree(child && child.pid); finish({ interrupted: true }); };
|
|
951
|
+
try {
|
|
952
|
+
child = spawnFn(cmd, args, { stdio: 'ignore', windowsHide: false, env: opEnv() });
|
|
953
|
+
} catch (e) {
|
|
954
|
+
finish({ missing: !!(e && e.code === 'ENOENT') });
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
child.on('error', (e) => finish({ missing: !!(e && e.code === 'ENOENT') }));
|
|
958
|
+
child.on('close', (code) => finish({ ok: code === 0, status: code }));
|
|
959
|
+
timer = setTimeout(() => { timedOut = true; killProcessTree(child.pid); finish({}); }, timeoutMs);
|
|
960
|
+
if (signal) {
|
|
961
|
+
if (signal.aborted) onAbort();
|
|
962
|
+
else signal.addEventListener('abort', onAbort, { once: true });
|
|
963
|
+
}
|
|
964
|
+
});
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
/**
|
|
968
|
+
* Ctrl+C, terminal fechado ou encerramento: roda `fn` (limpeza) e sai. Devolve a funcao que desliga o gancho.
|
|
969
|
+
* So vale com o Node livre para ver o sinal, por isso a parte demorada da instalacao e assincrona.
|
|
970
|
+
*/
|
|
971
|
+
function onInterruptCleanup(fn) {
|
|
972
|
+
const sinais = ['SIGINT', 'SIGTERM', 'SIGHUP', 'SIGBREAK']; // SIGBREAK so existe no Windows (o Node ignora o nome nos outros)
|
|
973
|
+
const handler = (sinal) => {
|
|
974
|
+
try { fn(); } catch { /* sai do mesmo jeito */ }
|
|
975
|
+
process.exit(sinal === 'SIGINT' ? 130 : 143);
|
|
976
|
+
};
|
|
977
|
+
const ligados = [];
|
|
978
|
+
for (const sinal of sinais) {
|
|
979
|
+
try { process.on(sinal, handler); ligados.push(sinal); } catch { /* sinal que este sistema nao tem */ }
|
|
980
|
+
}
|
|
981
|
+
return () => { for (const sinal of ligados) process.removeListener(sinal, handler); };
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
const APP_TEMP_PREFIX = 'auroq-1p-app-';
|
|
985
|
+
|
|
986
|
+
/**
|
|
987
|
+
* Apaga pastas temporarias do instalador (`auroq-1p-app-*`) que ficaram de uma execucao anterior morta a forca (o
|
|
988
|
+
* `finally` nao roda se o processo e morto). So mexe em pasta com esse prefixo e mais velha que `maxAgeMs`. Nunca lanca.
|
|
989
|
+
* Devolve quantas apagou.
|
|
990
|
+
*/
|
|
991
|
+
function sweepStaleAppDirs(root = os.tmpdir(), { maxAgeMs = 60 * 60 * 1000, now = Date.now() } = {}) {
|
|
992
|
+
let apagadas = 0;
|
|
993
|
+
let nomes = [];
|
|
994
|
+
try { nomes = fs.readdirSync(root); } catch { return 0; }
|
|
995
|
+
for (const nome of nomes) {
|
|
996
|
+
if (!nome.startsWith(APP_TEMP_PREFIX)) continue;
|
|
997
|
+
const full = path.join(root, nome);
|
|
998
|
+
try {
|
|
999
|
+
const st = fs.lstatSync(full);
|
|
1000
|
+
if (!st.isDirectory() || now - st.mtimeMs < maxAgeMs) continue;
|
|
1001
|
+
fs.rmSync(full, { recursive: true, force: true, maxRetries: 3, retryDelay: 200 });
|
|
1002
|
+
apagadas += 1;
|
|
1003
|
+
} catch { /* em uso ou sem permissao: fica para a proxima */ }
|
|
1004
|
+
}
|
|
1005
|
+
return apagadas;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
880
1008
|
function makeAppDeps() {
|
|
881
1009
|
return {
|
|
882
1010
|
platform: process.platform,
|
|
@@ -885,9 +1013,12 @@ function makeAppDeps() {
|
|
|
885
1013
|
appInstalled: () => appInstalled(),
|
|
886
1014
|
refreshPath: refreshWindowsPath,
|
|
887
1015
|
run: runProgram,
|
|
1016
|
+
runInstaller: runInstallerProgram,
|
|
1017
|
+
onInterrupt: onInterruptCleanup,
|
|
1018
|
+
sweepStale: () => sweepStaleAppDirs(),
|
|
888
1019
|
download: downloadToFile,
|
|
889
1020
|
checkSignature: checkInstallerSignature,
|
|
890
|
-
mkdtemp: () => fs.mkdtempSync(path.join(os.tmpdir(),
|
|
1021
|
+
mkdtemp: () => fs.mkdtempSync(path.join(os.tmpdir(), APP_TEMP_PREFIX)),
|
|
891
1022
|
out: { log, step, ok, warn, err },
|
|
892
1023
|
};
|
|
893
1024
|
}
|
|
@@ -930,19 +1061,36 @@ async function installApp(deps = {}) {
|
|
|
930
1061
|
}
|
|
931
1062
|
|
|
932
1063
|
// Windows 1: winget (rapido, mas falhou no teste real com 0x80070002 mesmo depois de conferir o hash)
|
|
1064
|
+
const saidaDe = (r) => (r.timedOut ? 'demorou demais' : `codigo de saida ${r.status === null || r.status === undefined ? 'desconhecido' : r.status}`);
|
|
933
1065
|
if (d.hasCommand('winget')) {
|
|
934
1066
|
out.log(`${DIM}Instalando via winget (pode levar um minuto)...${RESET}`);
|
|
935
1067
|
const w = d.run('winget', ['install', '--id', WINGET_APP_ID, '-e', '--source', 'winget', '--accept-source-agreements', '--accept-package-agreements', '--silent']);
|
|
936
1068
|
d.refreshPath();
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
if (
|
|
1069
|
+
// Saida 0 = o winget instalou: confia. Ele instala o 1Password como pacote MSIX, que nenhuma checagem de pasta
|
|
1070
|
+
// enxerga (teste real de 03/11); desconfiar dele mandava rodar o instalador de reserva por cima, e ele travava.
|
|
1071
|
+
if (w.ok) return pronto('winget');
|
|
1072
|
+
// O winget falhou. So vale a reserva se o app de fato nao esta aqui (o erro pode ter vindo depois de instalar).
|
|
1073
|
+
if (d.appInstalled()) {
|
|
1074
|
+
out.ok(`O winget saiu com erro (${saidaDe(w)}), mas o app do 1Password ja esta instalado.`);
|
|
1075
|
+
return pronto('winget');
|
|
1076
|
+
}
|
|
1077
|
+
out.warn(`O winget nao conseguiu instalar o app (${saidaDe(w)}). Vou usar o instalador oficial do 1Password.`);
|
|
940
1078
|
} else {
|
|
941
1079
|
out.warn('O winget nao esta disponivel. Vou usar o instalador oficial do 1Password.');
|
|
942
1080
|
}
|
|
943
1081
|
|
|
944
1082
|
// Windows 2: instalador oficial, com a assinatura conferida ANTES de rodar
|
|
1083
|
+
const minutos = Math.round(APP_SETUP_TIMEOUT_MS / 60000);
|
|
1084
|
+
try { d.sweepStale(); } catch { /* faxina e so um extra */ }
|
|
945
1085
|
let dir = null;
|
|
1086
|
+
const limpar = () => {
|
|
1087
|
+
if (!dir) return;
|
|
1088
|
+
try { fs.rmSync(dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 }); } catch { /* fica na pasta temporaria do usuario; a proxima execucao varre */ }
|
|
1089
|
+
dir = null;
|
|
1090
|
+
};
|
|
1091
|
+
const cancelar = new AbortController();
|
|
1092
|
+
// Ctrl+C ou terminal fechado no meio: mata o instalador, apaga a pasta temporaria e solta a trava
|
|
1093
|
+
const desligar = d.onInterrupt(() => { cancelar.abort(); limpar(); d.lock.release(); });
|
|
946
1094
|
try {
|
|
947
1095
|
dir = d.mkdtemp();
|
|
948
1096
|
const file = path.join(dir, '1PasswordSetup.exe');
|
|
@@ -955,11 +1103,21 @@ async function installApp(deps = {}) {
|
|
|
955
1103
|
const sig = d.checkSignature(file);
|
|
956
1104
|
if (!sig.ok) return falhou(`Nao executei o instalador baixado: ${sig.motivo}.`);
|
|
957
1105
|
out.ok(`Assinatura conferida: ${sig.assunto || 'Agilebits'}`);
|
|
958
|
-
|
|
959
|
-
|
|
1106
|
+
out.log(`${DIM}Rodando o instalador oficial (ele tem ate ${minutos} minutos). Se uma janela do 1Password abrir, deixe terminar.${RESET}`);
|
|
1107
|
+
const r = await d.runInstaller(file, ['--silent'], { timeoutMs: APP_SETUP_TIMEOUT_MS, signal: cancelar.signal });
|
|
1108
|
+
if (r.timedOut) {
|
|
1109
|
+
// Nao terminou no prazo: foi interrompido (ele e os filhos). Pode ter instalado antes de travar; confere.
|
|
1110
|
+
if (d.appInstalled()) {
|
|
1111
|
+
out.ok('O instalador oficial passou do prazo e foi interrompido, mas o app do 1Password ja esta instalado.');
|
|
1112
|
+
return pronto('instalador oficial');
|
|
1113
|
+
}
|
|
1114
|
+
return falhou(`O instalador oficial passou de ${minutos} minutos sem terminar e eu o interrompi (nao sei dizer por que: pode ter ficado esperando uma resposta numa janela, ou a internet estar lenta).`);
|
|
1115
|
+
}
|
|
1116
|
+
if (!r.ok) return falhou(`O instalador oficial nao terminou bem (${saidaDe(r)}).`);
|
|
960
1117
|
return pronto('instalador oficial');
|
|
961
1118
|
} finally {
|
|
962
|
-
|
|
1119
|
+
desligar();
|
|
1120
|
+
limpar();
|
|
963
1121
|
}
|
|
964
1122
|
} finally {
|
|
965
1123
|
d.lock.release();
|
|
@@ -1847,7 +2005,12 @@ module.exports = {
|
|
|
1847
2005
|
requestApproval,
|
|
1848
2006
|
biometricSteps,
|
|
1849
2007
|
appInstalled,
|
|
2008
|
+
msixInstalled,
|
|
1850
2009
|
installApp,
|
|
2010
|
+
runInstallerProgram,
|
|
2011
|
+
onInterruptCleanup,
|
|
2012
|
+
makeAppDeps,
|
|
2013
|
+
sweepStaleAppDirs,
|
|
1851
2014
|
checkInstallerSignature,
|
|
1852
2015
|
writeTemplateFile,
|
|
1853
2016
|
APP_INSTALLER_URL,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schema": 1,
|
|
3
3
|
"algorithm": "sha256",
|
|
4
4
|
"scope": "packaged-runtime-and-cascas",
|
|
5
|
-
"version": "2.8.
|
|
5
|
+
"version": "2.8.6",
|
|
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": "51edca56ab55af77a40dc2fa1290022ee3d608916994a518da779f2e138284da"
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
16
|
"path": ".auroq-core/core/synapse/context/context-builder.js",
|
|
@@ -282,7 +282,7 @@
|
|
|
282
282
|
},
|
|
283
283
|
{
|
|
284
284
|
"path": "lib/onepassword.js",
|
|
285
|
-
"sha256": "
|
|
285
|
+
"sha256": "64d449d0d8f7fdc30d912dd7163d44ab235e83d495e0ad9573addf574f010ec4"
|
|
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": "
|
|
301
|
+
"sha256": "5d4c499b938bf1426747f0f7f5b3c7f760a5b1cbbf926f0523d00bdb34fbca91"
|
|
302
302
|
},
|
|
303
303
|
{
|
|
304
304
|
"path": "scripts/build-pack.mjs",
|