auroq-os 2.8.4 → 2.8.5
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/.claude/rules/operacao-autonoma.md +2 -1
- package/CHANGELOG.md +12 -0
- package/README.md +4 -4
- package/bin/auroq-os.js +1 -1
- package/installer/install.ps1 +2 -2
- package/installer/install.sh +2 -2
- package/lib/cofre.js +111 -22
- package/lib/onepassword.js +318 -23
- package/lib/runtime-integrity.json +6 -6
- package/package.json +1 -1
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
dentro do comando que usa o valor, numa variável (`X="$(npx -y auroq-os@latest segredo ler Item/campo)" && comando "$X"`),
|
|
14
14
|
para o valor não voltar na conversa; para guardar, ligue por pipe a saída de quem gerou o valor. Nunca exponha segredo no chat ou em logs; `business/vault/` guarda só
|
|
15
15
|
status e referência `op://...`, nunca o valor. Cofre ainda não conectado, ou `op`/`segredo` com erro de
|
|
16
|
-
autenticação: conecte você mesmo pela skill `conectar-cofre` (`agents/companion/tasks/conectar-cofre.md`
|
|
16
|
+
autenticação: conecte você mesmo pela skill `conectar-cofre` (`agents/companion/tasks/conectar-cofre.md`; MCP `arcane`, `squad_passo`
|
|
17
|
+
com recurso_id `companion` e passo `conectar-cofre`, cuja entrada diz qual das peças carregar) e
|
|
17
18
|
retome a tarefa. Leia cada credencial uma vez por conversa e reaproveite o valor (a chave tem teto de leituras
|
|
18
19
|
por hora, por minuto ou por dia, conforme o cofre); nunca rode `env`/`printenv` nem abra o arquivo de ambiente
|
|
19
20
|
onde a chave fica.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
## Unreleased
|
|
2
2
|
|
|
3
|
+
- 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).
|
|
3
4
|
- 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.
|
|
4
5
|
- 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.
|
|
5
6
|
- Conteúdo ao vivo (catálogo `auroq-core` 2.3.51, 27/10): a renovação diz o salvamento certo: o app guarda a cópia na internet sozinho a cada 15 minutos e ao abrir e fechar (antes dizia 3 vezes por dia).
|
|
@@ -16,6 +17,17 @@
|
|
|
16
17
|
- 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).
|
|
17
18
|
- 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`).
|
|
18
19
|
- **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.
|
|
20
|
+
## 2.8.5 — 2026-10-29
|
|
21
|
+
|
|
22
|
+
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").
|
|
23
|
+
|
|
24
|
+
- **O laço "liga a chave do app" acabou.** O comando pedia a digital com um comando (`op whoami`) que o 1Password não usa para isso: ele responde "não está conectado" na hora, sem abrir a janela. O Companion lia isso como "a chave do app está desligada" e mandava o aluno ligar de novo, mesmo ligada. Agora a digital é pedida do jeito certo (`op vault list`, e se o app disser "sem sessão", `op signin` e mais uma tentativa), a aprovação vale para o resto da execução, e "a conta aparece mas o app não liberou" vira o aviso `aprovar-no-app`, com a receita do Windows Hello ou do Touch ID. O aviso `ligar-integracao` só sai quando o app não mostra conta nenhuma. Vale no Mac e no Windows.
|
|
25
|
+
- **Instalar o app do 1Password.** Novo `conectar-1password --instalar-app`: no Windows tenta o winget e, se ele falhar (no teste deu o erro 0x80070002), baixa o instalador oficial do 1Password, confere que a assinatura é válida e do 1Password (Agilebits) e só então instala; no Mac usa o Homebrew. O comando também percebe quando o app nem está no computador (`instalar-app`) em vez de mandar ligar uma chave que não existe.
|
|
26
|
+
- **Cópia da chave no cofre pessoal, no Windows.** O jeito antigo (mandar o molde por "pipe") falhava no PowerShell 5.1 com "invalid JSON in piped input". Agora o molde vai num arquivo temporário, só do usuário, e é apagado logo depois, com sucesso ou erro. A chave continua fora de argumento de comando e fora de log.
|
|
27
|
+
- **`--migrar` e `segredo` quando o programa do 1Password acabou de ser instalado.** No Windows, o programa `op` recém-instalado não aparece para a janela que já estava aberta, e o comando respondia "não enxerga o cofre Claude". Agora o comando atualiza a lista de programas e tenta de novo; se o `op` continuar sem aparecer, diz a verdade (`op-nao-achado`: "não achei o programa nesta janela, feche e abra") e não manda refazer a conexão à toa. As mensagens do `--migrar` também dizem qual foi o caso (chave não achada, chave recusada, cofre `Claude` invisível) e mostram a linha que o 1Password devolveu. Aviso honesto: ler a chave gravada já era feito (a hipótese do relatório do Marcos não bate com o código); a causa exata do caso dele não foi provada e só um novo teste no Windows mostra.
|
|
28
|
+
- Skill do Companion: dividida em seis peças, com o OK do aluno antes dos passos que a proteção do Claude Code barrava, Chave Secreta e Kit de Emergência, receita do Windows Hello e do Touch ID (veja a nota do catálogo `auroq-core` 2.3.53 acima).
|
|
29
|
+
- Instaladores fixam `auroq-os@2.8.5`.
|
|
30
|
+
|
|
19
31
|
## 2.8.4 — 2026-10-27
|
|
20
32
|
|
|
21
33
|
- **O freio "trabalhar até o fim" parou de mostrar as instruções dele na tela do aluno.** Ele era um gancho de IA (`type: "prompt"`), e quando segurava uma resposta o Claude Code mostrava ao aluno o texto inteiro da conferência ("Você confere a última resposta de uma IA para um aluno leigo..."), sem como esconder (alunos relataram em 27/10). Agora é um gancho de comando (`.claude/hooks/auroq-ate-o-fim.cjs`): segura só quando a resposta termina pedindo licença para passo de rotina ("quer que eu...?", "posso seguir?") ou manda o aluno rodar comando que ele não pediu para rodar sozinho, e o aluno vê só uma linha curta ("Auroq: faça você mesmo..."). Libera o que é do aluno (senha, código, pagamento, apagar, mensagem, publicar, GitHub e entregar), "Decide: A ou B?" e IA da equipe. Saiu a conferência de "disse que está pronto sem conferir", que precisava de IA; essa regra segue no `AGENTS.md`. A manutenção troca o freio antigo pelo novo sozinha. Provado num Claude Code de verdade e em `tests/ate-o-fim.test.mjs`.
|
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), `--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 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) |
|
|
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.5 doctor` mostra a fila local segura e os comandos pinados de
|
|
294
|
+
manutencao/diagnostico. `npx -y auroq-os@2.8.5 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.5; 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/bin/auroq-os.js
CHANGED
|
@@ -2573,7 +2573,7 @@ if (['init', 'install', 'update'].includes(command)) {
|
|
|
2573
2573
|
log(` ${CYAN}sync-codex${RESET} Gerar e verificar as skills locais do Codex CLI ($nome)`);
|
|
2574
2574
|
log(` ${CYAN}historico${RESET} Estado da captura do trabalho (prevista nos Termos de Uso; desliga em Minha Conta)`);
|
|
2575
2575
|
log(` ${CYAN}conectar-cofre${RESET} Escolher e conectar o cofre de senhas: 1Password (pago, recomendado) ou Infisical (gratis)`);
|
|
2576
|
-
log(` ${CYAN}conectar-1password${RESET} Conectar o 1Password (cria o cofre e a chave sozinho; voce so aprova uma vez no app)`);
|
|
2576
|
+
log(` ${CYAN}conectar-1password${RESET} Conectar o 1Password (cria o cofre e a chave sozinho; voce so aprova uma vez no app; --instalar-app instala o app)`);
|
|
2577
2577
|
log(` ${CYAN}segredo${RESET} Ler, gravar, listar ou apagar uma credencial no cofre do aluno (vale para 1Password e Infisical)`);
|
|
2578
2578
|
log('');
|
|
2579
2579
|
log(`${DIM}Primeira instalacao: https://arcane.arka.education/auroq${RESET}`);
|
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.5 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.5 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.5 "$@"
|
|
223
223
|
else
|
|
224
|
-
npx -y auroq-os@2.8.
|
|
224
|
+
npx -y auroq-os@2.8.5 "$@" < /dev/tty
|
|
225
225
|
fi
|
|
226
226
|
}
|
|
227
227
|
|
package/lib/cofre.js
CHANGED
|
@@ -50,9 +50,11 @@ const ESCOLHA_MSG = 'Quer o 1Password (uns 3 dolares por mes, confira o preco na
|
|
|
50
50
|
|
|
51
51
|
// Codigos do comando `segredo` (stderr: AUROQ_ERRO codigo=<c> mensagem="...").
|
|
52
52
|
// `uso` = uso errado da linha de comando (ref faltando, valor em argumento, opcao desconhecida).
|
|
53
|
+
// `op-nao-achado` = Windows: o programa `op` do 1Password nao foi achado nesta janela nem depois de recompor o PATH.
|
|
54
|
+
// Sai 1 (erro geral, como `credencial-indisponivel`), NAO 4: com 4 o agente roda a rota A de novo e cai no laco.
|
|
53
55
|
const SEGREDO_CODIGOS = ['nome-invalido', 'cofre-nao-configurado', 'nao-encontrado', 'sem-valor', 'ja-existe-confirmar',
|
|
54
|
-
'credencial-invalida', 'credencial-indisponivel', 'sem-permissao', 'rede', 'limite', 'bloqueada', 'falha', 'uso'];
|
|
55
|
-
// Saida: 0 ok, 1 erro geral, 2 uso errado, 3 nao encontrado, 4 cofre nao configurado.
|
|
56
|
+
'credencial-invalida', 'credencial-indisponivel', 'op-nao-achado', 'sem-permissao', 'rede', 'limite', 'bloqueada', 'falha', 'uso'];
|
|
57
|
+
// Saida: 0 ok, 1 erro geral (inclui op-nao-achado), 2 uso errado, 3 nao encontrado, 4 cofre nao configurado.
|
|
56
58
|
const SEGREDO_EXIT = Object.freeze({ ok: 0, erro: 1, uso: 2, 'nao-encontrado': 3, 'cofre-nao-configurado': 4 });
|
|
57
59
|
const CODIGOS_DE_USO = new Set(['uso', 'nome-invalido', 'sem-valor', 'ja-existe-confirmar']);
|
|
58
60
|
|
|
@@ -81,6 +83,11 @@ function envOf(d) {
|
|
|
81
83
|
return (d && d.env) || process.env;
|
|
82
84
|
}
|
|
83
85
|
|
|
86
|
+
/** Windows? (`d.platform` troca nos testes.) */
|
|
87
|
+
function ehWindows(d) {
|
|
88
|
+
return ((d && d.platform) || process.platform) === 'win32';
|
|
89
|
+
}
|
|
90
|
+
|
|
84
91
|
function nowOf(d) {
|
|
85
92
|
return d && typeof d.now === 'function' ? d.now() : Date.now();
|
|
86
93
|
}
|
|
@@ -387,6 +394,10 @@ function opReference(ref) {
|
|
|
387
394
|
|
|
388
395
|
// ─── Mensagens por codigo ────────────────────────────────
|
|
389
396
|
|
|
397
|
+
// Windows, `op` fora do PATH desta janela: primeiro fechar e abrir o programa (o PATH novo so chega a processos novos);
|
|
398
|
+
// a rota A so se isso nao resolver (rodar a rota A de novo logo de cara e o laco que o teste real viveu).
|
|
399
|
+
const MSG_OP_NAO_ACHADO = 'O programa do 1Password (op) nao foi achado nesta janela. Se ele foi instalado agora ha pouco, feche e abra o programa (Claude Code, Codex ou o app) e tente de novo. Se continuar, rode UMA vez: npx auroq-os conectar-cofre --cofre 1password --agente (ele confere o programa e instala se faltar); se ele disser ja-conectado e isto voltar, peca ajuda ao Arcano com este codigo: op-nao-achado.';
|
|
400
|
+
|
|
390
401
|
function messageFor(codigo, tipo, detalhe = '') {
|
|
391
402
|
const nome = NOME_TIPO[tipo] || 'cofre';
|
|
392
403
|
switch (codigo) {
|
|
@@ -402,6 +413,8 @@ function messageFor(codigo, tipo, detalhe = '') {
|
|
|
402
413
|
}
|
|
403
414
|
case 'credencial-indisponivel':
|
|
404
415
|
return `Nao consegui usar a chave deste computador no ${nome} agora. Tente de novo em alguns minutos.`;
|
|
416
|
+
case 'op-nao-achado':
|
|
417
|
+
return MSG_OP_NAO_ACHADO;
|
|
405
418
|
case 'sem-permissao':
|
|
406
419
|
return `A chave do agente nao tem permissao para isso no ${nome}.`;
|
|
407
420
|
case 'rede':
|
|
@@ -430,7 +443,9 @@ const OP_PERMISSION_RE = /(forbidden|permission|not allowed|insufficient|denied|
|
|
|
430
443
|
function classifyOp(d, r, secrets = []) {
|
|
431
444
|
const o = mod(d, 'onepassword');
|
|
432
445
|
const stderr = String((r && r.stderr) || '');
|
|
433
|
-
|
|
446
|
+
// sem o `op`: no Windows (ja tentou recompor o PATH) e "nao achei o programa", que rodar a rota A de novo NAO resolve;
|
|
447
|
+
// fora dele segue como sempre foi
|
|
448
|
+
if (r && r.missing) return { codigo: ehWindows(d) ? 'op-nao-achado' : 'cofre-nao-configurado', detalhe: '' };
|
|
434
449
|
if (OP_NOT_VAULT_RE.test(stderr)) return { codigo: 'cofre-nao-configurado', detalhe: '' };
|
|
435
450
|
if (OP_AMBIGUOUS_RE.test(stderr)) return { codigo: 'falha', detalhe: `Tem mais de um item com esse nome no cofre ${VAULT_NAME}; apague ou renomeie o repetido.` };
|
|
436
451
|
if (OP_NOT_FOUND_RE.test(stderr)) return { codigo: 'nao-encontrado', detalhe: '' };
|
|
@@ -472,24 +487,52 @@ function findField(fields, campo) {
|
|
|
472
487
|
function makeOpBackend(d) {
|
|
473
488
|
const tipo = '1password';
|
|
474
489
|
|
|
475
|
-
|
|
490
|
+
const validToken = (t) => {
|
|
491
|
+
const o = mod(d, 'onepassword');
|
|
492
|
+
return t && o.isValidToken(String(t).trim()) ? String(t).trim() : null;
|
|
493
|
+
};
|
|
494
|
+
const fromEnv = () => validToken(envOf(d)[mod(d, 'onepassword').TOKEN_VAR]);
|
|
495
|
+
async function fromDisk() {
|
|
476
496
|
const o = mod(d, 'onepassword');
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
497
|
+
try { return validToken(await (d.readOpToken ? d.readOpToken() : o.readPersistedToken())); } catch { return null; }
|
|
498
|
+
}
|
|
499
|
+
async function token() {
|
|
500
|
+
return fromEnv() || fromDisk();
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// 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
|
|
505
|
+
// comando de novo; chamadas em paralelo esperam a mesma recomposicao. Devolve false se nao ha como recompor.
|
|
506
|
+
let recomposicao = null;
|
|
507
|
+
function recomporPathUmaVez() {
|
|
508
|
+
if (!recomposicao) {
|
|
509
|
+
recomposicao = (async () => {
|
|
510
|
+
const o = mod(d, 'onepassword');
|
|
511
|
+
const recompor = typeof d.refreshPath === 'function' ? d.refreshPath : o.refreshWindowsPath;
|
|
512
|
+
if (typeof recompor !== 'function') return false;
|
|
513
|
+
try { await recompor(); } catch { /* melhor esforco: o comando decide se achou */ }
|
|
514
|
+
return true;
|
|
515
|
+
})();
|
|
516
|
+
}
|
|
517
|
+
return recomposicao;
|
|
481
518
|
}
|
|
482
519
|
|
|
483
520
|
async function call(tk, args, input = '') {
|
|
484
521
|
const o = mod(d, 'onepassword');
|
|
485
522
|
const run = d.op || o.runOp;
|
|
486
|
-
//
|
|
487
|
-
const
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
523
|
+
// o ambiente e montado a cada tentativa: o PATH recomposto vale so para quem copia process.env DEPOIS
|
|
524
|
+
const tentar = () => {
|
|
525
|
+
// `isolate`: o agente nunca pode cair na janela da digital do app; vale so a chave
|
|
526
|
+
const env = o.opEnv(String(tk).trim(), { isolate: true });
|
|
527
|
+
// a chave do Infisical (ID e segredo) nao tem nada a ver com o `op`: nao vai para o ambiente dele
|
|
528
|
+
for (const k of Object.keys(env)) {
|
|
529
|
+
if (/^AUROQ_INFISICAL_/i.test(k)) delete env[k];
|
|
530
|
+
}
|
|
531
|
+
return run(args, { env, input, timeoutMs: OP_TIMEOUT_MS });
|
|
532
|
+
};
|
|
533
|
+
let r = await tentar();
|
|
534
|
+
if (r && r.missing && ehWindows(d) && (await recomporPathUmaVez())) r = await tentar();
|
|
535
|
+
return r;
|
|
493
536
|
}
|
|
494
537
|
|
|
495
538
|
const semChave = () => failure('cofre-nao-configurado', tipo);
|
|
@@ -501,12 +544,34 @@ function makeOpBackend(d) {
|
|
|
501
544
|
});
|
|
502
545
|
|
|
503
546
|
async function run(args, input, secrets) {
|
|
504
|
-
|
|
547
|
+
let tk = await token();
|
|
505
548
|
if (!tk) return { erro: semChave() };
|
|
506
|
-
|
|
549
|
+
let r = await call(tk, args, input);
|
|
550
|
+
let c = r.ok ? null : classifyOp(d, r, secrets);
|
|
551
|
+
// A variavel do programa pode estar velha: janela aberta com uma chave que depois foi revogada e trocada (a
|
|
552
|
+
// nova esta gravada no computador). Chave RECUSADA vinda da variavel => tenta a gravada, se for outra. So esta
|
|
553
|
+
// recusa clara conta; rede, limite ou o que for nao muda de chave.
|
|
554
|
+
if (c && c.codigo === 'credencial-invalida' && tk === fromEnv()) {
|
|
555
|
+
const gravada = await fromDisk();
|
|
556
|
+
if (gravada && gravada !== tk) {
|
|
557
|
+
const r2 = await call(gravada, args, input);
|
|
558
|
+
tk = gravada;
|
|
559
|
+
r = r2;
|
|
560
|
+
c = r.ok ? null : classifyOp(d, r, secrets);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
507
563
|
if (r.ok) return { r, tk };
|
|
508
|
-
const
|
|
509
|
-
|
|
564
|
+
const erro = failure(c.codigo, tipo, c.detalhe);
|
|
565
|
+
// Campos extras so para quem precisa saber a causa (a migracao); nenhum entra na mensagem do `segredo`.
|
|
566
|
+
// `semOp`: o programa nem rodou. `diag`: o que o `op` disse (stderr, uma linha, sem a chave nem o valor gravado:
|
|
567
|
+
// oneLine tira ops_..., o token e os segredos que esta execucao conhece). Nunca o stdout.
|
|
568
|
+
if (r.missing) {
|
|
569
|
+
erro.semOp = true;
|
|
570
|
+
} else {
|
|
571
|
+
const diag = oneLine(r.stderr, 160, [...(secrets || []), tk, tk.replace(/^ops_/, '')]);
|
|
572
|
+
if (diag) erro.diag = diag;
|
|
573
|
+
}
|
|
574
|
+
return { erro };
|
|
510
575
|
}
|
|
511
576
|
|
|
512
577
|
const backend = {
|
|
@@ -1281,6 +1346,24 @@ function migracao(status, codigo, mensagem, n = {}) {
|
|
|
1281
1346
|
return { status, codigo, movidos: n.movidos || 0, iguais: n.iguais || 0, falhas: n.falhas || 0, mensagem };
|
|
1282
1347
|
}
|
|
1283
1348
|
|
|
1349
|
+
// Mensagens do 'conectar-1password-primeiro': cada uma diz o que de fato aconteceu. Com o complemento do `op` (diagDoOp)
|
|
1350
|
+
// ainda cabem nos 400 caracteres da linha AUROQ_RESULTADO (o texto corta ali).
|
|
1351
|
+
const ROTA_A = 'npx auroq-os conectar-cofre --cofre 1password --agente';
|
|
1352
|
+
// A chave NAO foi achada (nem na variavel do programa nem gravada no computador).
|
|
1353
|
+
const MSG_MIGRAR_SEM_CHAVE = `Nao achei a chave do 1Password nesta janela (nem na variavel do programa nem no que o computador tem gravado). Rode: ${ROTA_A} (se a chave ja estiver gravada ele responde ja-conectado na hora). Se responder ja-conectado e esta mensagem voltar, feche e abra o programa e rode a migracao de novo.`;
|
|
1354
|
+
// A chave foi achada e o 1Password disse que ela nao vale.
|
|
1355
|
+
const MSG_MIGRAR_CHAVE_RECUSADA = `O 1Password recusou a chave gravada neste computador (revogada ou invalida). Rode: ${ROTA_A} (ele cria outra) e depois a migracao de novo.`;
|
|
1356
|
+
// A chave foi achada e o `op` rodou, mas o cofre Claude nao aparece para ela (ou ela nao tem permissao nele).
|
|
1357
|
+
const MSG_MIGRAR_COFRE_INVISIVEL = `A chave do 1Password foi achada, mas nao enxerga o cofre ${VAULT_NAME}. Rode: ${ROTA_A}. Depois rode a migracao de novo.`;
|
|
1358
|
+
// A chave foi achada, mas o programa `op` nao existe aqui (fora do Windows; no Windows e o codigo op-nao-achado).
|
|
1359
|
+
const MSG_MIGRAR_SEM_OP = `A chave do 1Password foi achada, mas o programa do 1Password (op) nao foi achado neste computador. Rode: ${ROTA_A}. Depois rode a migracao de novo.`;
|
|
1360
|
+
|
|
1361
|
+
/** " (O 1Password disse: ...)" com a linha que o `op` devolveu (guardada na falha); '' se nao ha. Nunca traz a chave nem o valor. */
|
|
1362
|
+
function diagDoOp(falha) {
|
|
1363
|
+
const linha = falha && typeof falha.diag === 'string' ? falha.diag.trim() : '';
|
|
1364
|
+
return linha ? ` (O 1Password disse: ${linha})` : '';
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1284
1367
|
/**
|
|
1285
1368
|
* Leva as chaves do Infisical para o cofre Claude do 1Password e, so se TUDO foi, troca o cofre ativo (e a UNICA
|
|
1286
1369
|
* coisa que troca para o 1Password quem ja usa o Infisical: conectar o 1Password sozinho nao troca). Nao apaga
|
|
@@ -1304,13 +1387,19 @@ async function migrar(options = {}, d = {}) {
|
|
|
1304
1387
|
return migracao('erro', 'origem-indisponivel', 'O Infisical nao esta conectado neste computador, entao nao tenho de onde levar as chaves.');
|
|
1305
1388
|
}
|
|
1306
1389
|
if (!(await destino.pronto())) {
|
|
1307
|
-
return migracao('aguardando', 'conectar-1password-primeiro',
|
|
1390
|
+
return migracao('aguardando', 'conectar-1password-primeiro', MSG_MIGRAR_SEM_CHAVE);
|
|
1308
1391
|
}
|
|
1309
1392
|
// o cofre Claude precisa estar visivel para a chave do 1Password
|
|
1310
1393
|
const vis = await destino.listar();
|
|
1311
1394
|
if (!vis.ok) {
|
|
1312
|
-
|
|
1313
|
-
|
|
1395
|
+
// programa `op` nao achado (Windows): rodar a rota A de novo nao resolve (e foi o laco de "conectar primeiro")
|
|
1396
|
+
if (vis.codigo === 'op-nao-achado') return migracao('aguardando', 'op-nao-achado', vis.mensagem);
|
|
1397
|
+
if (vis.codigo === 'credencial-invalida') {
|
|
1398
|
+
return migracao('aguardando', 'conectar-1password-primeiro', `${MSG_MIGRAR_CHAVE_RECUSADA}${diagDoOp(vis)}`);
|
|
1399
|
+
}
|
|
1400
|
+
if (vis.semOp) return migracao('aguardando', 'conectar-1password-primeiro', MSG_MIGRAR_SEM_OP); // fora do Windows: a rota A confere o programa
|
|
1401
|
+
if (['cofre-nao-configurado', 'sem-permissao'].includes(vis.codigo)) {
|
|
1402
|
+
return migracao('aguardando', 'conectar-1password-primeiro', `${MSG_MIGRAR_COFRE_INVISIVEL}${diagDoOp(vis)}`);
|
|
1314
1403
|
}
|
|
1315
1404
|
return migracao('erro', 'falha', `Nao consegui olhar o cofre ${VAULT_NAME} do 1Password agora (${vis.codigo}). Tente de novo em alguns minutos.`);
|
|
1316
1405
|
}
|
package/lib/onepassword.js
CHANGED
|
@@ -478,6 +478,7 @@ const BACKUP_TAG = 'auroq-os';
|
|
|
478
478
|
// Contas pessoais (Individual e Families) entram em my.1password.<regiao>; Teams e Business em <time>.1password.*.
|
|
479
479
|
const PERSONAL_ACCOUNT_URL = /^my\.1password\.(com|eu|ca)$/;
|
|
480
480
|
const LOCK_STALE_MS = 2 * 60 * 60 * 1000;
|
|
481
|
+
const APPROVAL_TIMEOUT_MS = 180000; // o aluno tem ate 3 min para aprovar a janela da digital
|
|
481
482
|
const MAX_WAIT_SECONDS = 30 * 60; // espera da chave do app nunca passa disso (a trava de instancia vale 2 h)
|
|
482
483
|
// "Aguardando" (falta uma acao do aluno) e uma ETAPA do fluxo, nao falha: sai com 0 e a linha
|
|
483
484
|
// AUROQ_RESULTADO diz o que falta. Sair com erro faria a telemetria do instalador registrar
|
|
@@ -642,13 +643,14 @@ function pickAccount(accounts, wanted) {
|
|
|
642
643
|
/** Argumentos de `auroq-os conectar-1password [...]`. */
|
|
643
644
|
function parseArgs(argv) {
|
|
644
645
|
const args = Array.isArray(argv) ? argv : [];
|
|
645
|
-
const opts = { manual: false, rotacionar: false, agente: false, estado: false, conta: null, esperar: null };
|
|
646
|
+
const opts = { manual: false, rotacionar: false, agente: false, estado: false, instalarApp: false, conta: null, esperar: null };
|
|
646
647
|
for (let i = 0; i < args.length; i++) {
|
|
647
648
|
const a = args[i];
|
|
648
649
|
if (a === '--manual') opts.manual = true;
|
|
649
650
|
else if (a === '--rotacionar') opts.rotacionar = true;
|
|
650
651
|
else if (a === '--agente') opts.agente = true;
|
|
651
652
|
else if (a === '--estado') opts.estado = true;
|
|
653
|
+
else if (a === '--instalar-app') opts.instalarApp = true;
|
|
652
654
|
else if (a === '--conta' && args[i + 1] && !args[i + 1].startsWith('--')) opts.conta = args[++i];
|
|
653
655
|
else if (a.startsWith('--conta=')) opts.conta = a.slice('--conta='.length) || null;
|
|
654
656
|
else if (a === '--esperar' && args[i + 1] && !args[i + 1].startsWith('--')) opts.esperar = Number(args[++i]);
|
|
@@ -664,8 +666,8 @@ function enableIntegrationSteps(platform) {
|
|
|
664
666
|
? 'Abra as Configuracoes: no menu de cima do Mac, 1Password > Configuracoes (ou Cmd + virgula). Se eu ja abri a tela de Desenvolvedor pra voce, pule pro passo 3.'
|
|
665
667
|
: 'Abra as Configuracoes: clique no nome da sua conta (canto de cima, a esquerda) > Configuracoes. Se eu ja abri a tela de Desenvolvedor pra voce, pule pro passo 3.';
|
|
666
668
|
const digital = platform === 'darwin'
|
|
667
|
-
? 'Para aprovar com a digital: Configuracoes > Seguranca > "Desbloquear com Touch ID". Sem isso o
|
|
668
|
-
: 'Para aprovar com o Windows Hello: Configuracoes > Seguranca > "Desbloquear com Windows Hello". Sem
|
|
669
|
+
? 'Para aprovar com a digital: Configuracoes > Seguranca > "Desbloquear com Touch ID". Sem isso o Mac pede a senha do seu usuario do Mac.'
|
|
670
|
+
: 'Para aprovar com o Windows Hello: Configuracoes > Seguranca > "Desbloquear com Windows Hello". Sem o Windows Hello ligado a janela de aprovacao pode nem aparecer.';
|
|
669
671
|
return [
|
|
670
672
|
'Abra o app do 1Password, entre na sua conta e deixe o app aberto.',
|
|
671
673
|
abrir,
|
|
@@ -674,20 +676,42 @@ function enableIntegrationSteps(platform) {
|
|
|
674
676
|
];
|
|
675
677
|
}
|
|
676
678
|
|
|
679
|
+
/**
|
|
680
|
+
* Receita curta para ligar a digital (Windows Hello / Touch ID) quando nao esta ligada. A documentacao da integracao
|
|
681
|
+
* manda ligar o Windows Hello no Windows; no Mac o Touch ID e opcional (sem ele vale a senha do usuario do Mac). A
|
|
682
|
+
* senha da conta 1Password NAO faz esse papel. O codigo nao liga nada disso por ele.
|
|
683
|
+
*/
|
|
684
|
+
function biometricSteps(platform) {
|
|
685
|
+
if (platform === 'darwin') {
|
|
686
|
+
return [
|
|
687
|
+
'No Mac, cadastre a digital: Ajustes do Sistema > Touch ID e Senha > Adicionar digital.',
|
|
688
|
+
'No 1Password: Configuracoes > Seguranca > marque "Desbloquear com Touch ID".',
|
|
689
|
+
'O Touch ID e opcional: se o seu Mac nao tem, o Mac pede a senha do seu usuario do Mac na janela de aprovacao.',
|
|
690
|
+
];
|
|
691
|
+
}
|
|
692
|
+
return [
|
|
693
|
+
'No Windows, ligue o Windows Hello: Configuracoes do Windows > Contas > Opcoes de entrada, e configure o PIN, a digital ou o reconhecimento facial (o que o seu computador tiver).',
|
|
694
|
+
'No 1Password: Configuracoes > Seguranca > marque "Desbloquear com Windows Hello".',
|
|
695
|
+
'No Windows o 1Password pede o Windows Hello ligado (PIN, digital ou rosto: serve o que o computador tiver); sem ele a janela de aprovacao pode nem aparecer.',
|
|
696
|
+
];
|
|
697
|
+
}
|
|
698
|
+
|
|
677
699
|
// ─── Dependencias reais (trocaveis nos testes) ───────────
|
|
678
700
|
|
|
679
701
|
/**
|
|
680
702
|
* Roda o `op`. Nunca lanca. A saida pode ter segredo (token): quem chama decide o que mostrar.
|
|
681
703
|
* O token NUNCA vai em `args`: so por `env` ou por `input` (stdin).
|
|
682
704
|
*/
|
|
683
|
-
function runOp(args, { env, input, timeoutMs = 120000 } = {}) {
|
|
705
|
+
function runOp(args, { env, input, timeoutMs = 120000, noStdin = false } = {}) {
|
|
684
706
|
// Sem texto para mandar e com terminal de verdade: o `op` herda o terminal, como se o aluno
|
|
685
707
|
// tivesse digitado (a aprovacao do app e amarrada ao terminal; com pipe nao ha terminal).
|
|
686
|
-
|
|
708
|
+
// `noStdin`: o `op` recusa `--template` se o stdin for um pipe, ate vazio ("You can't use both piping and the
|
|
709
|
+
// --template flag"; o `input: ''` do Node vira pipe), entao esse comando roda com o stdin desligado (NUL/dev/null).
|
|
710
|
+
const useTerminal = input === undefined && !noStdin && Boolean(process.stdin.isTTY);
|
|
687
711
|
const r = spawnSync('op', args, {
|
|
688
712
|
encoding: 'utf8',
|
|
689
713
|
env: env || opEnv(),
|
|
690
|
-
...(useTerminal ? { stdio: ['inherit', 'pipe', 'pipe'] } : { input: input === undefined ? '' : input }),
|
|
714
|
+
...(noStdin ? { stdio: ['ignore', 'pipe', 'pipe'] } : useTerminal ? { stdio: ['inherit', 'pipe', 'pipe'] } : { input: input === undefined ? '' : input }),
|
|
691
715
|
timeout: timeoutMs,
|
|
692
716
|
maxBuffer: 16 * 1024 * 1024,
|
|
693
717
|
windowsHide: true,
|
|
@@ -767,6 +791,181 @@ function refreshWindowsPath() {
|
|
|
767
791
|
if (parts.length) process.env.PATH = [...parts, process.env.PATH || ''].join(';');
|
|
768
792
|
}
|
|
769
793
|
|
|
794
|
+
// ─── O app do 1Password (instalacao) ─────────────────────
|
|
795
|
+
|
|
796
|
+
const WINGET_APP_ID = 'AgileBits.1Password';
|
|
797
|
+
// Instalador oficial (reserva do winget, que falhou no teste real de 27/10 com 0x80070002 depois de conferir o hash).
|
|
798
|
+
const APP_INSTALLER_URL = 'https://downloads.1password.com/win/1PasswordSetup-latest.exe';
|
|
799
|
+
// Assinante do instalador oficial: "Valid | CN=Agilebits, O=Agilebits, L=Toronto, S=Ontario, C=CA".
|
|
800
|
+
const APP_SIGNER_RE = /(?:^|,\s*)CN=Agilebits(?:,|$)/i;
|
|
801
|
+
|
|
802
|
+
/** O app do 1Password esta neste computador? true | false | null (sistema sem instalacao automatica: nao sabe). */
|
|
803
|
+
function appInstalled(platform = process.platform, env = process.env) {
|
|
804
|
+
// AUROQ_APP_1PASSWORD=instalado|ausente: so para os testes de processo real, que rodam em Mac/Windows de verdade
|
|
805
|
+
// (onde o app nao esta) e nao podem depender de o app existir na maquina.
|
|
806
|
+
const forcado = String(env.AUROQ_APP_1PASSWORD || '').toLowerCase();
|
|
807
|
+
if (forcado === 'instalado') return true;
|
|
808
|
+
if (forcado === 'ausente') return false;
|
|
809
|
+
if (platform === 'darwin') {
|
|
810
|
+
return ['/Applications/1Password.app', path.join(os.homedir(), 'Applications', '1Password.app')].some((p) => fs.existsSync(p));
|
|
811
|
+
}
|
|
812
|
+
if (platform === 'win32') {
|
|
813
|
+
// Instalador do aluno: %LOCALAPPDATA%\1Password\app\8\1Password.exe; instalacao para a maquina: Arquivos de Programas.
|
|
814
|
+
const roots = [env.LOCALAPPDATA, env.ProgramFiles, env['ProgramFiles(x86)']].filter(Boolean);
|
|
815
|
+
return roots.some((root) => {
|
|
816
|
+
const appDir = path.join(root, '1Password', 'app');
|
|
817
|
+
try {
|
|
818
|
+
return fs.readdirSync(appDir).some((v) => fs.existsSync(path.join(appDir, v, '1Password.exe')));
|
|
819
|
+
} catch {
|
|
820
|
+
return false;
|
|
821
|
+
}
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
return null;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
/**
|
|
828
|
+
* Confere a assinatura Authenticode do instalador ANTES de rodar: precisa ser valida e do publicador "Agilebits".
|
|
829
|
+
* O caminho do arquivo vai por variavel de ambiente (nunca dentro do texto do comando). Nao lanca.
|
|
830
|
+
*/
|
|
831
|
+
function checkInstallerSignature(file, run = spawnSync) {
|
|
832
|
+
const script = "$s = Get-AuthenticodeSignature -LiteralPath $env:AUROQ_INSTALADOR; $a = ''; if ($s.SignerCertificate) { $a = $s.SignerCertificate.Subject }; Write-Output ($s.Status.ToString() + '|' + $a)";
|
|
833
|
+
let r;
|
|
834
|
+
try {
|
|
835
|
+
r = run('powershell', ['-NoProfile', '-NonInteractive', '-Command', script], {
|
|
836
|
+
encoding: 'utf8', windowsHide: true, timeout: 120000, env: { ...opEnv(), AUROQ_INSTALADOR: file },
|
|
837
|
+
});
|
|
838
|
+
} catch (e) {
|
|
839
|
+
return { ok: false, motivo: `nao consegui conferir a assinatura (${oneLine(e && e.message, 80)})` };
|
|
840
|
+
}
|
|
841
|
+
if (!r || r.error || r.status !== 0) return { ok: false, motivo: 'nao consegui conferir a assinatura' };
|
|
842
|
+
const line = String(r.stdout || '').trim().split(/\r?\n/).pop() || '';
|
|
843
|
+
const cut = line.indexOf('|');
|
|
844
|
+
const status = cut === -1 ? line : line.slice(0, cut);
|
|
845
|
+
const subject = cut === -1 ? '' : line.slice(cut + 1);
|
|
846
|
+
if (status !== 'Valid') return { ok: false, motivo: `a assinatura do arquivo nao e valida (${oneLine(status, 40) || 'sem resposta'})` };
|
|
847
|
+
if (!APP_SIGNER_RE.test(subject)) return { ok: false, motivo: 'o arquivo e assinado por outro publicador, nao pelo 1Password' };
|
|
848
|
+
return { ok: true, assunto: oneLine(subject, 120) };
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
/** Baixa um arquivo grande direto para o disco (https so). `curl` primeiro (respeita o proxy do sistema); reserva: fetch do Node. */
|
|
852
|
+
async function downloadToFile(url, dest) {
|
|
853
|
+
const curl = process.platform === 'win32' ? 'curl.exe' : 'curl';
|
|
854
|
+
const r = spawnSync(curl, ['-fL', '--proto', '=https', '--proto-redir', '=https', '--silent', '--show-error', '--connect-timeout', '30', '--max-time', '900', '-o', dest, url], {
|
|
855
|
+
encoding: 'utf8', windowsHide: true, timeout: 16 * 60 * 1000, stdio: ['ignore', 'ignore', 'pipe'], env: opEnv(),
|
|
856
|
+
});
|
|
857
|
+
if (!r.error && r.status === 0 && fs.existsSync(dest)) return;
|
|
858
|
+
if (typeof fetch !== 'function') throw new Error(`download falhou (${r.error ? r.error.code || r.error.message : `curl saiu com ${r.status}`})`);
|
|
859
|
+
try { fs.rmSync(dest, { force: true }); } catch { /* recomeca */ }
|
|
860
|
+
const res = await fetch(url, { redirect: 'follow', headers: { 'User-Agent': 'auroq-os' } });
|
|
861
|
+
if (!res.ok || !res.body) throw new Error(`download falhou (HTTP ${res.status})`);
|
|
862
|
+
const { Readable } = require('stream');
|
|
863
|
+
const { pipeline } = require('stream/promises');
|
|
864
|
+
await pipeline(Readable.fromWeb(res.body), fs.createWriteStream(dest, { flags: 'w' }));
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
/** Roda um programa sem terminal. Nunca lanca. O ambiente sai sem a chave do agente (opEnv). */
|
|
868
|
+
function runProgram(cmd, args, { timeoutMs = 15 * 60 * 1000 } = {}) {
|
|
869
|
+
const r = spawnSync(cmd, args, { encoding: 'utf8', env: opEnv(), timeout: timeoutMs, maxBuffer: 16 * 1024 * 1024, windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
870
|
+
return {
|
|
871
|
+
ok: !r.error && r.status === 0,
|
|
872
|
+
status: r.status,
|
|
873
|
+
stdout: r.stdout || '',
|
|
874
|
+
stderr: r.stderr || '',
|
|
875
|
+
timedOut: !!(r.error && r.error.code === 'ETIMEDOUT'),
|
|
876
|
+
missing: !!(r.error && r.error.code === 'ENOENT'),
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
function makeAppDeps() {
|
|
881
|
+
return {
|
|
882
|
+
platform: process.platform,
|
|
883
|
+
lock: makeLock(),
|
|
884
|
+
hasCommand,
|
|
885
|
+
appInstalled: () => appInstalled(),
|
|
886
|
+
refreshPath: refreshWindowsPath,
|
|
887
|
+
run: runProgram,
|
|
888
|
+
download: downloadToFile,
|
|
889
|
+
checkSignature: checkInstallerSignature,
|
|
890
|
+
mkdtemp: () => fs.mkdtempSync(path.join(os.tmpdir(), 'auroq-1p-app-')),
|
|
891
|
+
out: { log, step, ok, warn, err },
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
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.';
|
|
896
|
+
|
|
897
|
+
/**
|
|
898
|
+
* Instala o app do 1Password (`conectar-1password --instalar-app`). Windows: winget e, se ele falhar, o instalador
|
|
899
|
+
* oficial, so rodado depois de conferir a assinatura (valida e do publicador Agilebits). Mac: Homebrew (cask).
|
|
900
|
+
* O app e um programa novo no computador do aluno: quem chama pede o OK dele antes.
|
|
901
|
+
*/
|
|
902
|
+
async function installApp(deps = {}) {
|
|
903
|
+
const d = { ...makeAppDeps(), ...deps };
|
|
904
|
+
const out = d.out;
|
|
905
|
+
if (d.platform !== 'darwin' && d.platform !== 'win32') {
|
|
906
|
+
return fim('erro', 'app-nao-instalou', 'A instalacao automatica do app so existe no macOS e no Windows. Baixe em https://1password.com/downloads');
|
|
907
|
+
}
|
|
908
|
+
if (!d.lock.acquire()) {
|
|
909
|
+
return fim('aguardando', 'outra-execucao', 'Ja tem outra execucao do conectar-1password em andamento neste computador. Espere ela terminar e rode de novo; nao instalei nada em duplicidade.');
|
|
910
|
+
}
|
|
911
|
+
try {
|
|
912
|
+
out.step('Verificando o app do 1Password...');
|
|
913
|
+
if (d.appInstalled()) {
|
|
914
|
+
out.ok('O app do 1Password ja esta instalado.');
|
|
915
|
+
return fim('aguardando', 'app-instalado', `O app do 1Password ja esta instalado neste computador. ${APP_NEXT_STEPS}`);
|
|
916
|
+
}
|
|
917
|
+
const falhou = (msg) => fim('erro', 'app-nao-instalou', `${msg} Instale a mao em https://1password.com/downloads e rode o conectar-1password de novo.`);
|
|
918
|
+
const pronto = (via) => {
|
|
919
|
+
d.refreshPath();
|
|
920
|
+
out.ok(`App do 1Password instalado (${via}).`);
|
|
921
|
+
return fim('aguardando', 'app-instalado', `App do 1Password instalado. ${APP_NEXT_STEPS}`);
|
|
922
|
+
};
|
|
923
|
+
|
|
924
|
+
if (d.platform === 'darwin') {
|
|
925
|
+
if (!d.hasCommand('brew')) return falhou('Homebrew nao encontrado, entao nao consigo instalar o app daqui.');
|
|
926
|
+
out.log(`${DIM}Instalando via Homebrew (pode levar um minuto)...${RESET}`);
|
|
927
|
+
const r = d.run('brew', ['install', '--cask', '1password']);
|
|
928
|
+
if (r.ok || d.appInstalled()) return pronto('Homebrew');
|
|
929
|
+
return falhou(`A instalacao via Homebrew falhou (${r.timedOut ? 'demorou demais' : `codigo de saida ${r.status}`}).`);
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// Windows 1: winget (rapido, mas falhou no teste real com 0x80070002 mesmo depois de conferir o hash)
|
|
933
|
+
if (d.hasCommand('winget')) {
|
|
934
|
+
out.log(`${DIM}Instalando via winget (pode levar um minuto)...${RESET}`);
|
|
935
|
+
const w = d.run('winget', ['install', '--id', WINGET_APP_ID, '-e', '--source', 'winget', '--accept-source-agreements', '--accept-package-agreements', '--silent']);
|
|
936
|
+
d.refreshPath();
|
|
937
|
+
if (w.ok && d.appInstalled()) return pronto('winget');
|
|
938
|
+
out.warn(`O winget nao conseguiu instalar o app (${w.timedOut ? 'demorou demais' : `codigo de saida ${w.status === null || w.status === undefined ? 'desconhecido' : w.status}`}). Vou usar o instalador oficial do 1Password.`);
|
|
939
|
+
if (d.appInstalled()) return pronto('winget');
|
|
940
|
+
} else {
|
|
941
|
+
out.warn('O winget nao esta disponivel. Vou usar o instalador oficial do 1Password.');
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
// Windows 2: instalador oficial, com a assinatura conferida ANTES de rodar
|
|
945
|
+
let dir = null;
|
|
946
|
+
try {
|
|
947
|
+
dir = d.mkdtemp();
|
|
948
|
+
const file = path.join(dir, '1PasswordSetup.exe');
|
|
949
|
+
out.log(`${DIM}Baixando o instalador oficial (${APP_INSTALLER_URL})...${RESET}`);
|
|
950
|
+
try {
|
|
951
|
+
await d.download(APP_INSTALLER_URL, file);
|
|
952
|
+
} catch (e) {
|
|
953
|
+
return falhou(`Nao consegui baixar o instalador oficial (${oneLine(e && e.message, 120)}).`);
|
|
954
|
+
}
|
|
955
|
+
const sig = d.checkSignature(file);
|
|
956
|
+
if (!sig.ok) return falhou(`Nao executei o instalador baixado: ${sig.motivo}.`);
|
|
957
|
+
out.ok(`Assinatura conferida: ${sig.assunto || 'Agilebits'}`);
|
|
958
|
+
const r = d.run(file, ['--silent']);
|
|
959
|
+
if (!r.ok) return falhou(`O instalador oficial nao terminou bem (${r.timedOut ? 'demorou demais' : `codigo de saida ${r.status}`}).`);
|
|
960
|
+
return pronto('instalador oficial');
|
|
961
|
+
} finally {
|
|
962
|
+
if (dir) { try { fs.rmSync(dir, { recursive: true, force: true }); } catch { /* fica na pasta temporaria do usuario */ } }
|
|
963
|
+
}
|
|
964
|
+
} finally {
|
|
965
|
+
d.lock.release();
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
|
|
770
969
|
// Pasta do aluno fora do git, a mesma onde ficam as credenciais do Auroq (~/.arcane). Aqui vivem a trava de
|
|
771
970
|
// instancia unica e o estado das etapas (nenhum segredo).
|
|
772
971
|
function auroqDir() {
|
|
@@ -905,6 +1104,7 @@ function makeDefaultDeps() {
|
|
|
905
1104
|
platform: process.platform,
|
|
906
1105
|
openUrl,
|
|
907
1106
|
refreshPath: refreshWindowsPath,
|
|
1107
|
+
appInstalled: () => appInstalled(),
|
|
908
1108
|
lock: makeLock(),
|
|
909
1109
|
state: { read: readState, write: writeState },
|
|
910
1110
|
envFileProblem,
|
|
@@ -1017,11 +1217,45 @@ function backupItemTemplate(token) {
|
|
|
1017
1217
|
function saveBackup(d, account, token) {
|
|
1018
1218
|
// Sem --vault: o `op` guarda no cofre pessoal da conta, que se chama Personal, Private ou Employee
|
|
1019
1219
|
// conforme o plano (e pode vir traduzido), por isso nunca se usa o nome.
|
|
1020
|
-
const
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1220
|
+
const body = JSON.stringify(backupItemTemplate(token));
|
|
1221
|
+
const common = ['--tags', BACKUP_TAG, '--account', accountKey(account), '--format', 'json'];
|
|
1222
|
+
if (d.platform !== 'win32') {
|
|
1223
|
+
const r = d.op(['item', 'create', '-', ...common], { env: opEnv(), input: body });
|
|
1224
|
+
return r.ok ? { ok: true } : { ok: false, detalhe: oneLine(r.stderr) };
|
|
1225
|
+
}
|
|
1226
|
+
// Windows: o `op item create -` por pipe falha no PowerShell 5.1 ("invalid JSON in piped input", teste real de 27/10),
|
|
1227
|
+
// entao o molde vai num arquivo temporario (UTF-8 sem BOM) so deste usuario, que o `op` le com --template. O token
|
|
1228
|
+
// continua fora de argumento e de log; o arquivo e apagado em seguida, aconteca o que acontecer. O `op` roda com o
|
|
1229
|
+
// stdin desligado (`noStdin`): com `--template` ele nao aceita nenhum pipe, nem vazio.
|
|
1230
|
+
let tmp = null;
|
|
1231
|
+
try {
|
|
1232
|
+
tmp = writeTemplateFile(body);
|
|
1233
|
+
} catch (e) {
|
|
1234
|
+
return { ok: false, detalhe: `nao consegui preparar o arquivo temporario do molde (${oneLine(e && e.message, 120)})` };
|
|
1235
|
+
}
|
|
1236
|
+
try {
|
|
1237
|
+
const r = d.op(['item', 'create', '--template', tmp.file, ...common], { env: opEnv(), noStdin: true });
|
|
1238
|
+
return r.ok ? { ok: true } : { ok: false, detalhe: oneLine(r.stderr) };
|
|
1239
|
+
} finally {
|
|
1240
|
+
try { fs.rmSync(tmp.dir, { recursive: true, force: true }); } catch { /* fica na pasta temporaria do usuario */ }
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
/**
|
|
1245
|
+
* Grava `content` num arquivo novo, dentro de uma pasta nova e de nome aleatorio na pasta temporaria do usuario
|
|
1246
|
+
* (%TEMP% do perfil, que so ele, o SYSTEM e os administradores leem; o modo 0600 vale no Mac e no Linux e e ignorado no
|
|
1247
|
+
* Windows). UTF-8 SEM BOM: o BOM faria o `op` recusar o JSON. Devolve { dir, file }; quem chama apaga a pasta.
|
|
1248
|
+
*/
|
|
1249
|
+
function writeTemplateFile(content) {
|
|
1250
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'auroq-op-'));
|
|
1251
|
+
const file = path.join(dir, 'molde.json');
|
|
1252
|
+
try {
|
|
1253
|
+
fs.writeFileSync(file, Buffer.from(content, 'utf8'), { flag: 'wx', mode: 0o600 });
|
|
1254
|
+
} catch (e) {
|
|
1255
|
+
try { fs.rmSync(dir, { recursive: true, force: true }); } catch { /* nada a fazer */ }
|
|
1256
|
+
throw e;
|
|
1257
|
+
}
|
|
1258
|
+
return { dir, file };
|
|
1025
1259
|
}
|
|
1026
1260
|
|
|
1027
1261
|
/**
|
|
@@ -1084,12 +1318,57 @@ function showIntegrationHelp(d, out) {
|
|
|
1084
1318
|
out.log('');
|
|
1085
1319
|
}
|
|
1086
1320
|
|
|
1087
|
-
|
|
1321
|
+
/**
|
|
1322
|
+
* Ajuda quando a conta aparece mas a aprovacao nao passou. Nao abre a tela do app (a chave provavelmente ja esta
|
|
1323
|
+
* ligada): so lista o que conferir. `semSessao`: o `op` disse que nao ha sessao, entao tambem lembra da chave do app.
|
|
1324
|
+
*/
|
|
1325
|
+
function showApprovalHelp(d, out, semSessao) {
|
|
1326
|
+
out.warn('O app do 1Password nao liberou o acesso ainda. Confira:');
|
|
1327
|
+
const passos = ['Abra o app do 1Password e deixe-o desbloqueado (com a sua senha).'];
|
|
1328
|
+
if (semSessao) passos.push('Em Configuracoes > Desenvolvedor, deixe marcado "Integrar com a CLI do 1Password" (Integrate with 1Password CLI).');
|
|
1329
|
+
passos.push('Quando a janela da digital aparecer, aprove.');
|
|
1330
|
+
passos.push(...biometricSteps(d.platform).map((s, i) => (i === 0 ? `Se a digital nunca aparece: ${s}` : s)));
|
|
1331
|
+
passos.forEach((line, i) => out.log(` ${i + 1}. ${line}`));
|
|
1332
|
+
out.log('');
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
const APPROVAL_REFUSED_RE = /(dismiss|cancel|declin|denied|reject|refus)/i;
|
|
1336
|
+
|
|
1337
|
+
/**
|
|
1338
|
+
* Vale tentar o `op signin`? So quando o `op` disse que nao ha sessao. Janela da digital dispensada ou negada pelo
|
|
1339
|
+
* aluno nao se repete sozinha (seriam mais janelas na cara dele), nem tempo esgotado.
|
|
1340
|
+
*/
|
|
1341
|
+
function needsSignin(r) {
|
|
1342
|
+
if (!r || r.timedOut) return false;
|
|
1343
|
+
if (APPROVAL_REFUSED_RE.test(String(r.stderr || ''))) return false;
|
|
1344
|
+
return ['sem-sessao', 'sessao'].includes(classifyFailure(r));
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
/**
|
|
1348
|
+
* Pede a aprovacao do app. O `op whoami` NAO serve: ele nao abre a janela da digital e, sem sessao aberta, responde na
|
|
1349
|
+
* hora "account is not signed in" (teste real no Windows, 27/10). Quem pede a aprovacao e qualquer comando que precise
|
|
1350
|
+
* de login; o exemplo da documentacao oficial da integracao com o app e o `op vault list` (so leitura). Segunda
|
|
1351
|
+
* tentativa, so se a primeira voltar "sem sessao" (needsSignin): `op signin --account`. A sessao que o app abre pertence a este
|
|
1352
|
+
* processo: os `op` seguintes sao filhos do mesmo node e a reaproveitam (e por isso a aprovacao NUNCA e feita num
|
|
1353
|
+
* processo a parte).
|
|
1354
|
+
*/
|
|
1355
|
+
function requestApproval(d, account) {
|
|
1356
|
+
const key = accountKey(account);
|
|
1357
|
+
const approve = () => d.op(['vault', 'list', '--account', key, '--format', 'json'], { env: opEnv(), timeoutMs: APPROVAL_TIMEOUT_MS });
|
|
1358
|
+
let r = approve();
|
|
1359
|
+
if (!r.ok && needsSignin(r)) {
|
|
1360
|
+
d.op(['signin', '--account', key], { env: opEnv(), timeoutMs: APPROVAL_TIMEOUT_MS }); // a saida nao interessa (e nunca e mostrada)
|
|
1361
|
+
r = approve();
|
|
1362
|
+
}
|
|
1363
|
+
return r;
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
const APP_PRIORITY_MSG ='O 1Password continua respondendo pelo app (pedindo digital) em vez de usar a chave. Sem isso o agente nao fica sem pedir digital. Nao criei nada novo; peca ajuda ao Arcano com este codigo: app-tem-prioridade.';
|
|
1088
1367
|
const NET_STRONG_RE = /(dial tcp|no such host|tls handshake|network is unreachable|no route to host|i\/o timeout)/i;
|
|
1089
1368
|
|
|
1090
1369
|
// Erros que o aluno (ou o plano dele) resolve: a linha AUROQ_RESULTADO ja diz o que fazer. Saem com 1 mas a
|
|
1091
1370
|
// telemetria do instalador nao os conta como instalacao quebrada.
|
|
1092
|
-
const STUDENT_ACTIONABLE = new Set(['sem-op', 'op-antigo', 'rede', 'conta-nao-encontrada', 'conta-ambigua', 'cofre-duplicado', 'sem-permissao', 'token-indisponivel', 'arquivo-ambiente']);
|
|
1371
|
+
const STUDENT_ACTIONABLE = new Set(['sem-op', 'op-antigo', 'rede', 'conta-nao-encontrada', 'conta-ambigua', 'cofre-duplicado', 'sem-permissao', 'token-indisponivel', 'arquivo-ambiente', 'app-nao-instalou']);
|
|
1093
1372
|
|
|
1094
1373
|
function isStudentActionable(codigo) {
|
|
1095
1374
|
return STUDENT_ACTIONABLE.has(codigo);
|
|
@@ -1203,6 +1482,11 @@ async function runConnect(opts, d, prog) {
|
|
|
1203
1482
|
// 2. Sessao do aluno: o app do 1Password com a chave "Integrate with 1Password CLI" ligada
|
|
1204
1483
|
out.step('Procurando a sua conta no app do 1Password...');
|
|
1205
1484
|
let accounts = listAccounts(d);
|
|
1485
|
+
if (!accounts.length && d.appInstalled() === false) {
|
|
1486
|
+
// Sem app nao ha o que ligar: dizer "ligue a chave" mandaria o aluno procurar uma tela que nao existe (teste real de 27/10).
|
|
1487
|
+
prog.reset('app');
|
|
1488
|
+
return fim('aguardando', 'instalar-app', 'Nao achei o app do 1Password neste computador. Instale com: npx -y auroq-os@latest conectar-1password --instalar-app --agente (ou baixe em https://1password.com/downloads). Se ele ja esta instalado num lugar fora do comum, abra o app, entre na sua conta e ligue "Integrar com a CLI do 1Password" em Configuracoes > Desenvolvedor; depois rode de novo.');
|
|
1489
|
+
}
|
|
1206
1490
|
if (!accounts.length) {
|
|
1207
1491
|
showIntegrationHelp(d, out);
|
|
1208
1492
|
if (waitSeconds > 0) {
|
|
@@ -1242,18 +1526,22 @@ async function runConnect(opts, d, prog) {
|
|
|
1242
1526
|
out.ok(`Conta: ${accountLabel(account)}`);
|
|
1243
1527
|
|
|
1244
1528
|
out.step('Pedindo sua aprovacao ao app do 1Password...');
|
|
1245
|
-
|
|
1246
|
-
|
|
1529
|
+
const digital = d.platform === 'win32' ? 'Windows Hello' : (d.platform === 'darwin' ? 'Touch ID' : 'Touch ID / Windows Hello');
|
|
1530
|
+
out.log(`${BOLD}Vai abrir uma janela do 1Password pedindo a sua digital (${digital}) ou a senha. Aprove. Se ela aparecer mais de uma vez, e so aprovar de novo.${RESET}`);
|
|
1531
|
+
const auth = requestApproval(d, account);
|
|
1247
1532
|
if (!auth.ok) {
|
|
1248
1533
|
const text = String(auth.stderr || '');
|
|
1249
1534
|
if (NET_STRONG_RE.test(text)) return fim('erro', 'rede', `Sem conexao com o 1Password. ${oneLine(text)}`.trim());
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1535
|
+
// A conta JA apareceu na lista do app, entao a chave "Integrar com a CLI" nao e o que falta: nunca devolve
|
|
1536
|
+
// 'ligar-integracao' aqui (o laco do teste real no Windows: o aluno ligava a chave, dizia "pronto" e ouvia de novo
|
|
1537
|
+
// que faltava ligar). Falta a aprovacao no app, ou, so no caso da conta adicionada na mao (`op account add`), a chave.
|
|
1538
|
+
const semSessao = !auth.timedOut && ['sem-sessao', 'sessao'].includes(classifyFailure(auth));
|
|
1539
|
+
showApprovalHelp(d, out, semSessao);
|
|
1255
1540
|
const detail = oneLine(text, 160);
|
|
1256
|
-
|
|
1541
|
+
const falta = semSessao
|
|
1542
|
+
? 'A conta aparece no app, mas ele nao liberou o acesso. Desbloqueie o app, confira "Integrar com a CLI" e aprove a janela (Windows: o Windows Hello precisa estar ligado). Rode de novo.'
|
|
1543
|
+
: 'Abra e desbloqueie o app do 1Password e aprove a janela da digital (no Windows, o Windows Hello precisa estar ligado). Rode de novo.';
|
|
1544
|
+
return fim('aguardando', 'aprovar-no-app', `Nao consegui entrar na conta${auth.timedOut ? ': a janela da digital nao foi aprovada a tempo' : ''}. ${falta}${detail ? ` (O 1Password disse: ${detail})` : ''}`);
|
|
1257
1545
|
}
|
|
1258
1546
|
prog.mark('aprovacao');
|
|
1259
1547
|
out.ok('Aprovado');
|
|
@@ -1486,7 +1774,7 @@ function showState(st) {
|
|
|
1486
1774
|
// ─── Entrada do comando ──────────────────────────────────
|
|
1487
1775
|
|
|
1488
1776
|
/**
|
|
1489
|
-
* `auroq-os conectar-1password [--agente] [--conta X] [--rotacionar] [--esperar N] [--manual]`
|
|
1777
|
+
* `auroq-os conectar-1password [--agente] [--conta X] [--rotacionar] [--esperar N] [--manual] [--instalar-app]`
|
|
1490
1778
|
* Codigo de saida: 0 conectado OU falta uma acao do aluno (ligar a chave no app, escolher conta: a
|
|
1491
1779
|
* linha AUROQ_RESULTADO diz qual), 1 erro.
|
|
1492
1780
|
*/
|
|
@@ -1503,7 +1791,7 @@ async function connect(options = {}) {
|
|
|
1503
1791
|
if (!opts.agente) console.log(`\n${PURPLE}${BOLD} Conectar 1Password — Auroq OS${RESET}`);
|
|
1504
1792
|
let result;
|
|
1505
1793
|
try {
|
|
1506
|
-
result = await connectAutomatic(opts);
|
|
1794
|
+
result = opts.instalarApp ? await installApp() : await connectAutomatic(opts);
|
|
1507
1795
|
} catch (e) {
|
|
1508
1796
|
// Excecao inesperada: o Ops sempre recebe a linha de resultado (e a mensagem nunca leva token).
|
|
1509
1797
|
result = fim('erro', 'falha', `Falha inesperada: ${oneLine(e && e.message)}`);
|
|
@@ -1556,6 +1844,13 @@ module.exports = {
|
|
|
1556
1844
|
shellKind,
|
|
1557
1845
|
freshShellFor,
|
|
1558
1846
|
clearClipboard,
|
|
1847
|
+
requestApproval,
|
|
1848
|
+
biometricSteps,
|
|
1849
|
+
appInstalled,
|
|
1850
|
+
installApp,
|
|
1851
|
+
checkInstallerSignature,
|
|
1852
|
+
writeTemplateFile,
|
|
1853
|
+
APP_INSTALLER_URL,
|
|
1559
1854
|
ETAPAS,
|
|
1560
1855
|
makeLock,
|
|
1561
1856
|
isPersonalAccount,
|
|
@@ -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.5",
|
|
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": "dd74b039850ad9a109e644f60489b65745c85a86295c694c478e341026e00ad8"
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
16
|
"path": ".auroq-core/core/synapse/context/context-builder.js",
|
|
@@ -214,7 +214,7 @@
|
|
|
214
214
|
},
|
|
215
215
|
{
|
|
216
216
|
"path": "bin/auroq-os.js",
|
|
217
|
-
"sha256": "
|
|
217
|
+
"sha256": "937e6e3b748e05f324b196891ea46663b950aa12e1ca20c9aa9dacf6d2bd8962"
|
|
218
218
|
},
|
|
219
219
|
{
|
|
220
220
|
"path": "lib/ahi/client.mjs",
|
|
@@ -254,7 +254,7 @@
|
|
|
254
254
|
},
|
|
255
255
|
{
|
|
256
256
|
"path": "lib/cofre.js",
|
|
257
|
-
"sha256": "
|
|
257
|
+
"sha256": "8dff20e576fda737f5039ceaca64e8e18fe79e19460c3ce0bac1262f7fa490df"
|
|
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": "
|
|
285
|
+
"sha256": "0f5ec74682668925ea078334dcceeb2bbe32e2aad7211b57ad47a41230d64146"
|
|
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": "43885cac0063a63f1d1bb1ad5281daaec8a0c2de8c240c23708073f356dd0c2a"
|
|
302
302
|
},
|
|
303
303
|
{
|
|
304
304
|
"path": "scripts/build-pack.mjs",
|