auroq-os 2.3.6 → 2.4.0

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.
@@ -0,0 +1,500 @@
1
+ #!/bin/bash
2
+ # Auroq OS — instalador do Mac.
3
+ #
4
+ # curl -fsSL https://arka.education/install.sh | bash
5
+ #
6
+ # Este script roda na maquina de alguem que ainda nao tem NADA: sem Git, sem o
7
+ # motor do sistema, as vezes sem as ferramentas da Apple. Por isso ele e escrito
8
+ # pro bash 3.2 (o que vem no macOS), nao pede senha nenhuma e nao usa sudo.
9
+ #
10
+ # Ordem obrigatoria (PRD "Embarque em 2 passos" §4.2):
11
+ # diagnostico -> Git -> motor -> login da mentoria -> pasta do negocio -> init
12
+ #
13
+ # O `init` e quem escreve o registro do conteudo ao vivo. O instalador nao mexe
14
+ # nisso (ADR-7) — a maquina do aluno tem um dono so pra esse arquivo.
15
+ #
16
+ # Modo biblioteca (usado pelos testes): AUROQ_INSTALL_LIB=1 . install.sh
17
+ # Modo ensaio (nao toca em nada): AUROQ_INSTALL_DRY_RUN=1 bash install.sh
18
+
19
+ set -u
20
+
21
+ SUPORTE="11 97312-2273"
22
+ NODE_MIN=20
23
+ MACOS_MIN=13
24
+ DISCO_MIN_MB=2048
25
+ AUROQ_DIR="${HOME}/.auroq"
26
+ NODE_DIR="${AUROQ_DIR}/node"
27
+
28
+ # Cores — desligadas quando a saida nao e um terminal (log de suporte fica limpo).
29
+ if [ -t 1 ]; then
30
+ ROXO=$'\033[38;2;120;80;200m'; CIANO=$'\033[38;2;34;211;238m'; VERDE=$'\033[38;2;52;211;153m'
31
+ AMBAR=$'\033[38;2;251;191;36m'; VERMELHO=$'\033[38;2;248;113;113m'; FRACO=$'\033[2m'; FIM=$'\033[0m'
32
+ else
33
+ ROXO=''; CIANO=''; VERDE=''; AMBAR=''; VERMELHO=''; FRACO=''; FIM=''
34
+ fi
35
+
36
+ # ─── Fala com o aluno ─────────────────────────────────────────────────────────
37
+
38
+ diz() { printf ' %s\n' "$*"; }
39
+ titulo() { printf '\n%s %s%s\n\n' "$ROXO" "$*" "$FIM"; }
40
+ passo() { printf '\n %s>%s %s\n' "$CIANO" "$FIM" "$*"; }
41
+ ok_feito() { printf ' %s+%s %s\n' "$VERDE" "$FIM" "$*"; }
42
+ aviso() { printf ' %s!%s %s\n' "$AMBAR" "$FIM" "$*"; }
43
+
44
+ # Toda falha diz tres coisas: o que aconteceu, o que fazer agora e o suporte.
45
+ # Um instalador que trava calado e pior que o passo a passo manual.
46
+ auroq_erro() {
47
+ printf '\n %sParou aqui:%s %s\n' "$VERMELHO" "$FIM" "${1:-Algo deu errado.}"
48
+ printf '\n %sO que fazer agora:%s %s\n' "$CIANO" "$FIM" "${2:-Rode o mesmo comando de novo.}"
49
+ printf '\n %sSe continuar travado, chame o suporte: %s%s\n\n' "$FRACO" "$SUPORTE" "$FIM"
50
+ auroq_registrar_log "erro" "${1:-}"
51
+ exit 1
52
+ }
53
+
54
+ # ─── Registro pro suporte (§5.7) ──────────────────────────────────────────────
55
+ # Uma linha por evento. Nunca entra senha, token ou e-mail — o suporte precisa
56
+ # saber o que aconteceu na maquina, nao quem e a pessoa.
57
+
58
+ auroq_registrar_log() {
59
+ [ -n "${AUROQ_INSTALL_DRY_RUN:-}" ] && return 0
60
+ mkdir -p "${HOME}/.auroq" 2>/dev/null || return 0
61
+ printf '%s\t%s\t%s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "${1:-}" "${2:-}" \
62
+ >> "${HOME}/.auroq/install.log" 2>/dev/null || true
63
+ }
64
+
65
+ # ─── Decisoes puras (testadas uma a uma) ──────────────────────────────────────
66
+
67
+ # So letras minusculas, numeros e hifen. Recusar e melhor que "consertar" sozinho:
68
+ # o nome da pasta e o nome do negocio dele, e ele precisa reconhecer.
69
+ auroq_validar_pasta() {
70
+ local nome="${1:-}"
71
+ local ok=1
72
+ # Enter vazio nao e erro do aluno: ele ainda nao escreveu nada. So repete a
73
+ # pergunta — acusar "nome errado" aqui assusta a toa.
74
+ if [ -z "$nome" ]; then
75
+ return 1
76
+ fi
77
+ case "$nome" in
78
+ '' ) ok=0 ;;
79
+ * ) LC_ALL=C; case "$nome" in
80
+ *[!a-z0-9-]* ) ok=0 ;;
81
+ -* | *-- ) ok=0 ;;
82
+ * ) ok=1 ;;
83
+ esac ;;
84
+ esac
85
+ if [ "$ok" -eq 1 ]; then
86
+ return 0
87
+ fi
88
+ diz "Esse nome nao serve: use so letras minusculas, numeros e hifen."
89
+ diz "${FRACO}Exemplo bom: minha-empresa${FIM}"
90
+ return 1
91
+ }
92
+
93
+ auroq_macos_serve() {
94
+ local versao="${1:-0}"
95
+ local maior="${versao%%.*}"
96
+ case "$maior" in
97
+ ''|*[!0-9]* ) return 1 ;;
98
+ esac
99
+ [ "$maior" -ge "$MACOS_MIN" ]
100
+ }
101
+
102
+ auroq_node_serve() {
103
+ local versao="${1:-}"
104
+ versao="${versao#v}"
105
+ local maior="${versao%%.*}"
106
+ case "$maior" in
107
+ ''|*[!0-9]* ) return 1 ;;
108
+ esac
109
+ [ "$maior" -ge "$NODE_MIN" ]
110
+ }
111
+
112
+ auroq_node_tarball() {
113
+ local versao="${1:-}"
114
+ local arq="${2:-}"
115
+ local alvo="x64"
116
+ [ "$arq" = "arm64" ] && alvo="arm64"
117
+ printf 'node-v%s-darwin-%s.tar.gz\n' "$versao" "$alvo"
118
+ }
119
+
120
+ # ─── Falar com o aluno de dentro de um `curl | bash` ──────────────────────────
121
+ # Quando o script chega por `curl ... | bash`, a entrada padrao dele — e de TUDO
122
+ # que ele chamar — e o proprio texto do script. Um programa filho que pergunta
123
+ # "qual o seu e-mail?" leria a proxima linha do install.sh como resposta.
124
+ # Por isso todo comando que conversa com o aluno e ligado no terminal de verdade.
125
+
126
+ auroq_tem_terminal() {
127
+ [ -t 0 ] && return 0
128
+ [ -e /dev/tty ] && (exec 3< /dev/tty) 2>/dev/null && return 0
129
+ return 1
130
+ }
131
+
132
+ # npx que o aluno consegue responder. Sem isso, login e as perguntas do `init`
133
+ # recebem pedaco do script no lugar da resposta dele.
134
+ auroq_npx_aluno() {
135
+ if [ -t 0 ]; then
136
+ npx -y auroq-os@latest "$@"
137
+ else
138
+ npx -y auroq-os@latest "$@" < /dev/tty
139
+ fi
140
+ }
141
+
142
+ # nova | vazia | auroq | ocupada — decidido ANTES de escrever qualquer coisa.
143
+ auroq_estado_pasta() {
144
+ local dir="${1:-}"
145
+ if [ ! -d "$dir" ]; then printf 'nova\n'; return 0; fi
146
+ if [ -d "${dir}/.auroq-core" ]; then printf 'auroq\n'; return 0; fi
147
+ local resto
148
+ resto="$(ls -A "$dir" 2>/dev/null | grep -v -x -e '.DS_Store' -e '.localized' | head -1)"
149
+ if [ -z "$resto" ]; then printf 'vazia\n'; else printf 'ocupada\n'; fi
150
+ }
151
+
152
+ # ─── O motor visivel pra quem abre pelo icone (§5.4) ──────────────────────────
153
+ # A falha mais perigosa do projeto: o app aberto pelo Dock monta o ambiente lendo
154
+ # os arquivos de perfil. Se o motor nao estiver la, o app sobe SEM ERRO NENHUM e
155
+ # o Companion parece burro. Por isso escrevemos nos dois arquivos, sempre.
156
+
157
+ auroq_aplicar_path() {
158
+ local arquivo="${1:-}"
159
+ local bin="${2:-}"
160
+ local tmp="${arquivo}.auroq-tmp.$$"
161
+ [ -f "$arquivo" ] || : > "$arquivo"
162
+ # tira o bloco antigo (se houver) e escreve o novo — nunca empilha
163
+ awk '
164
+ /^# >>> auroq >>>$/ { pulando=1; next }
165
+ /^# <<< auroq <<<$/ { pulando=0; next }
166
+ pulando != 1 { print }
167
+ ' "$arquivo" > "$tmp" 2>/dev/null || cp "$arquivo" "$tmp"
168
+ {
169
+ printf '# >>> auroq >>>\n'
170
+ printf '# Motor do Auroq OS. Bloco gerado pelo instalador — pode apagar se desinstalar.\n'
171
+ printf 'export PATH="%s:$PATH"\n' "$bin"
172
+ printf '# <<< auroq <<<\n'
173
+ } >> "$tmp"
174
+ mv "$tmp" "$arquivo"
175
+ }
176
+
177
+ auroq_publicar_node_no_path() {
178
+ local bin="${1:-}"
179
+ local arquivo
180
+ for arquivo in "${HOME}/.zshrc" "${HOME}/.zprofile"; do
181
+ auroq_aplicar_path "$arquivo" "$bin"
182
+ done
183
+ }
184
+
185
+ # ─── Passo 1: diagnostico (§5.1) ──────────────────────────────────────────────
186
+
187
+ auroq_diagnostico() {
188
+ passo "Vendo o seu computador..."
189
+
190
+ local sistema
191
+ sistema="$(uname -s 2>/dev/null || echo desconhecido)"
192
+ if [ "$sistema" != "Darwin" ]; then
193
+ auroq_erro "Este instalador e do Mac, e aqui nao parece ser um Mac." "No Windows, use o outro endereco: arka.education/install.ps1"
194
+ fi
195
+
196
+ local versao
197
+ versao="${AUROQ_INSTALL_FAKE_MACOS:-$(sw_vers -productVersion 2>/dev/null || echo 0)}"
198
+ if ! auroq_macos_serve "$versao"; then
199
+ auroq_erro "Seu Mac esta no macOS ${versao} e o sistema precisa do 13 ou mais novo." "Atualize o Mac em Ajustes > Geral > Atualizacao de Software, ou fale com o suporte antes de continuar."
200
+ fi
201
+
202
+ local chip
203
+ chip="$(uname -m 2>/dev/null || echo desconhecido)"
204
+ local nome_chip="Intel"
205
+ [ "$chip" = "arm64" ] && nome_chip="chip Apple"
206
+ ok_feito "Mac com ${nome_chip}, macOS ${versao} — serve"
207
+
208
+ local livre_mb
209
+ livre_mb="${AUROQ_INSTALL_FAKE_DISCO_MB:-$(df -m "$HOME" 2>/dev/null | awk 'NR==2 {print $4}')}"
210
+ case "${livre_mb:-0}" in
211
+ ''|*[!0-9]* ) livre_mb=999999 ;;
212
+ esac
213
+ if [ "$livre_mb" -lt "$DISCO_MIN_MB" ]; then
214
+ auroq_erro "Falta espaco no disco: sobram ${livre_mb} MB e sao necessarios pelo menos 2 GB." "Libere espaco (Ajustes > Geral > Armazenamento) e rode o mesmo comando de novo."
215
+ fi
216
+
217
+ if [ -n "${AUROQ_INSTALL_FAKE_SEM_INTERNET:-}" ]; then
218
+ auroq_erro "Nao consegui falar com a internet." "Conecte na internet e rode o mesmo comando de novo. Nada foi instalado pela metade."
219
+ fi
220
+ if [ -z "${AUROQ_INSTALL_DRY_RUN:-}" ]; then
221
+ if ! curl -sfI --max-time 15 https://registry.npmjs.org/ >/dev/null 2>&1; then
222
+ auroq_erro "Nao consegui falar com a internet." "Conecte na internet e rode o mesmo comando de novo. Nada foi instalado pela metade."
223
+ fi
224
+ fi
225
+ auroq_registrar_log "diagnostico" "macos=${versao} chip=${chip} disco_mb=${livre_mb}"
226
+ }
227
+
228
+ # ─── Passo 2: Git (§5.2) ──────────────────────────────────────────────────────
229
+ # No Mac o Git vem junto das ferramentas da Apple. O script abre a janela; quem
230
+ # clica em "Instalar" e o aluno. Isso nao da pra automatizar — e janela do sistema.
231
+
232
+ auroq_git_responde() {
233
+ git --version >/dev/null 2>&1 && return 0
234
+ [ -x /usr/bin/git ] && /usr/bin/git --version >/dev/null 2>&1 && return 0
235
+ return 1
236
+ }
237
+
238
+ auroq_garantir_git() {
239
+ passo "Verificando o Git (o programa que guarda as versoes do seu trabalho)..."
240
+
241
+ if [ -n "${AUROQ_INSTALL_DRY_RUN:-}" ]; then
242
+ ok_feito "Git — seria verificado aqui"
243
+ return 0
244
+ fi
245
+
246
+ if auroq_git_responde; then
247
+ ok_feito "Git ja estava instalado"
248
+ auroq_registrar_log "git" "ja-existia"
249
+ return 0
250
+ fi
251
+
252
+ aviso "Falta instalar. Vai abrir uma janela da Apple agora."
253
+ diz " Clique em \"Instalar\" e volte pra ca — pode levar de 5 a 10 minutos."
254
+ diz " Pode deixar baixando e ir tomar um cafe; eu espero."
255
+ xcode-select --install >/dev/null 2>&1 || true
256
+
257
+ local esperou=0
258
+ while [ "$esperou" -lt 1200 ]; do
259
+ if auroq_git_responde; then
260
+ ok_feito "Git instalado"
261
+ auroq_registrar_log "git" "instalado-pelas-ferramentas-da-apple"
262
+ return 0
263
+ fi
264
+ sleep 10
265
+ esperou=$((esperou + 10))
266
+ if [ $((esperou % 120)) -eq 0 ]; then
267
+ diz "${FRACO} ainda baixando... (${esperou}s)${FIM}"
268
+ fi
269
+ done
270
+
271
+ auroq_erro "A instalacao das ferramentas da Apple nao terminou." "Termine a janela da Apple e rode o mesmo comando de novo — nada do que ja foi feito se perde."
272
+ }
273
+
274
+ # ─── Passo 3: o motor (§5.3) ──────────────────────────────────────────────────
275
+ # Fica numa pasta do proprio aluno. Sem senha, sem sudo, sem tocar no que ele ja
276
+ # tinha instalado: se ja existe um motor bom, usamos o dele.
277
+
278
+ auroq_garantir_node() {
279
+ passo "Verificando o motor do sistema..."
280
+
281
+ if [ -n "${AUROQ_INSTALL_DRY_RUN:-}" ]; then
282
+ ok_feito "Motor — seria instalado em uma pasta sua, sem senha"
283
+ return 0
284
+ fi
285
+
286
+ local atual
287
+ atual="$(node --version 2>/dev/null || true)"
288
+ if auroq_node_serve "$atual"; then
289
+ ok_feito "O motor ja estava instalado (${atual})"
290
+ auroq_registrar_log "motor" "ja-existia versao=${atual} origem=$(command -v node 2>/dev/null || echo desconhecida)"
291
+ return 0
292
+ fi
293
+
294
+ if [ -n "$atual" ]; then
295
+ aviso "Voce ja tinha um motor antigo (${atual}); vou instalar um novo do lado, sem mexer no seu."
296
+ fi
297
+ diz " (e um programa que fica numa pasta sua, nao pede senha e nao mexe em nada do seu Mac)"
298
+
299
+ local chip alvo indice versao tarball url tmpdir
300
+ chip="$(uname -m)"
301
+ alvo="$(auroq_node_tarball "0" "$chip")"
302
+
303
+ indice="$(curl -fsSL --max-time 30 https://nodejs.org/dist/index.json 2>/dev/null || true)"
304
+ [ -z "$indice" ] && auroq_erro "Nao consegui baixar o motor do sistema." "Confira a internet (ou o antivirus, se houver) e rode o mesmo comando de novo."
305
+
306
+ versao="$(printf '%s' "$indice" | tr ',' '\n' | grep -o '"version":"v24\.[0-9.]*"' | head -1 | sed 's/.*"v//; s/"//')"
307
+ [ -z "$versao" ] && versao="$(printf '%s' "$indice" | tr ',' '\n' | grep -o '"version":"v2[0-9]\.[0-9.]*"' | head -1 | sed 's/.*"v//; s/"//')"
308
+ [ -z "$versao" ] && auroq_erro "Nao consegui descobrir qual motor baixar." "Rode o mesmo comando de novo em alguns minutos. Se insistir, chame o suporte."
309
+
310
+ tarball="$(auroq_node_tarball "$versao" "$chip")"
311
+ url="https://nodejs.org/dist/v${versao}/${tarball}"
312
+ tmpdir="$(mktemp -d)"
313
+
314
+ curl -fsSL --max-time 600 -o "${tmpdir}/${tarball}" "$url" 2>/dev/null \
315
+ || auroq_erro "O download do motor nao completou." "Pode ser antivirus ou rede instavel. Rode o mesmo comando de novo; se repetir, chame o suporte."
316
+
317
+ # Sem conferir a soma oficial, nao instala. Binario nao verificado nao entra.
318
+ local somas esperado obtido
319
+ somas="$(curl -fsSL --max-time 60 "https://nodejs.org/dist/v${versao}/SHASUMS256.txt" 2>/dev/null || true)"
320
+ esperado="$(printf '%s' "$somas" | grep " ${tarball}\$" | awk '{print $1}')"
321
+ obtido="$(shasum -a 256 "${tmpdir}/${tarball}" 2>/dev/null | awk '{print $1}')"
322
+ if [ -z "$esperado" ] || [ "$esperado" != "$obtido" ]; then
323
+ rm -rf "$tmpdir"
324
+ auroq_erro "O arquivo do motor baixou com defeito." "Rode o mesmo comando de novo — o arquivo estragado ja foi apagado."
325
+ fi
326
+
327
+ mkdir -p "$NODE_DIR" 2>/dev/null || auroq_erro "Nao consegui criar a pasta ${NODE_DIR}." "Confira se voce tem permissao de escrita na sua pasta pessoal e rode de novo."
328
+ tar -xzf "${tmpdir}/${tarball}" -C "$tmpdir" 2>/dev/null \
329
+ || { rm -rf "$tmpdir"; auroq_erro "Nao consegui abrir o arquivo do motor." "Rode o mesmo comando de novo."; }
330
+ rm -rf "${NODE_DIR}.antigo" 2>/dev/null || true
331
+ [ -d "$NODE_DIR" ] && mv "$NODE_DIR" "${NODE_DIR}.antigo" 2>/dev/null || true
332
+ mv "${tmpdir}/node-v${versao}-darwin-"* "$NODE_DIR" 2>/dev/null \
333
+ || { rm -rf "$tmpdir"; auroq_erro "Nao consegui guardar o motor na sua pasta." "Rode o mesmo comando de novo."; }
334
+ rm -rf "$tmpdir" "${NODE_DIR}.antigo"
335
+
336
+ auroq_publicar_node_no_path "${NODE_DIR}/bin"
337
+ export PATH="${NODE_DIR}/bin:${PATH}"
338
+ # npm global dentro da pasta do aluno: instalar ferramenta depois nao pede senha
339
+ export npm_config_prefix="${NODE_DIR}"
340
+
341
+ ok_feito "Motor instalado (${versao})"
342
+ auroq_registrar_log "motor" "instalado versao=${versao} chip=${chip}"
343
+ }
344
+
345
+ # ─── Passo 4: login da mentoria (§5.5) ────────────────────────────────────────
346
+ # Mesma checagem que o resto do sistema usa. So contrato ativo instala.
347
+
348
+ auroq_login_mentoria() {
349
+ passo "Agora entre com o seu acesso da Mentoria Arcane."
350
+
351
+ if [ -n "${AUROQ_INSTALL_DRY_RUN:-}" ]; then
352
+ ok_feito "Login — seria pedido aqui (e-mail e senha da plataforma)"
353
+ return 0
354
+ fi
355
+
356
+ if ! auroq_tem_terminal; then
357
+ auroq_erro "Preciso do seu e-mail e senha da Mentoria Arcane, e esta janela nao aceita resposta." "Abra o Terminal (ou o terminal de dentro do Claude Code) e cole o mesmo comando la."
358
+ fi
359
+
360
+ # `check-access` e a mesma regua do resto do sistema: exit 0 so com contrato
361
+ # ativo conferido agora. Sem sessao salva, ele mesmo pede e-mail e senha; com
362
+ # sessao boa, passa direto e nao pergunta de novo.
363
+ #
364
+ # NAO usar `whoami` aqui: ele imprime "Nao autenticado" e mesmo assim sai com
365
+ # sucesso. Usado como pergunta de sim/nao, ele dizia "voce ja estava logado"
366
+ # pra quem nunca logou, pulava o portao e deixava a conta sem acesso chegar ate
367
+ # a criacao da pasta.
368
+ if ! auroq_npx_aluno check-access; then
369
+ auroq_registrar_log "login" "recusado"
370
+ printf '\n %sSem acesso ativo, a instalacao para aqui — e nada foi criado no seu computador.%s\n' "$FRACO" "$FIM"
371
+ printf ' %sO que fazer agora: fale com o suporte %s%s\n\n' "$FRACO" "$SUPORTE" "$FIM"
372
+ exit 1
373
+ fi
374
+ auroq_registrar_log "login" "ok"
375
+ }
376
+
377
+ # ─── Passo 5: pasta do negocio (§5.6) ─────────────────────────────────────────
378
+
379
+ auroq_escolher_pasta() {
380
+ passo "Qual o nome da pasta do seu negocio?"
381
+ diz "${FRACO}(so letras minusculas e hifen — ex: minha-empresa)${FIM}"
382
+
383
+ local nome="${AUROQ_INSTALL_PASTA:-}"
384
+ local tentativas=0
385
+
386
+ while : ; do
387
+ if [ -z "$nome" ]; then
388
+ # Rodando por `curl ... | bash`, a entrada padrao e o proprio script:
389
+ # a pergunta tem que ir pro terminal de verdade.
390
+ if [ ! -t 0 ]; then
391
+ if [ -e /dev/tty ] && (exec 3< /dev/tty) 2>/dev/null; then
392
+ printf ' Nome: '
393
+ read -r nome < /dev/tty 2>/dev/null || nome=''
394
+ else
395
+ auroq_erro "Nao consegui te perguntar o nome da pasta: esta janela nao aceita resposta." "Abra o Terminal (ou o terminal de dentro do Claude Code) e cole o mesmo comando la."
396
+ fi
397
+ else
398
+ printf ' Nome: '
399
+ read -r nome || nome=''
400
+ fi
401
+ fi
402
+
403
+ if auroq_validar_pasta "$nome"; then
404
+ PASTA_ALVO="${HOME}/${nome}"
405
+ case "$(auroq_estado_pasta "$PASTA_ALVO")" in
406
+ auroq )
407
+ printf '\n'
408
+ aviso "Ja existe um Auroq em ${PASTA_ALVO} — nao vou mexer nele."
409
+ diz "Pra conferir se esta tudo certo la, abra essa pasta e rode:"
410
+ diz "${CIANO} auroq-os doctor${FIM}"
411
+ auroq_registrar_log "pasta" "ja-tinha-auroq"
412
+ exit 0
413
+ ;;
414
+ ocupada )
415
+ aviso "A pasta ${nome} ja existe e tem coisa sua dentro. Escolha outro nome."
416
+ nome=''
417
+ ;;
418
+ * )
419
+ break
420
+ ;;
421
+ esac
422
+ else
423
+ nome=''
424
+ fi
425
+
426
+ tentativas=$((tentativas + 1))
427
+ if [ "$tentativas" -ge 5 ]; then
428
+ auroq_erro "Nao consegui um nome valido pra pasta." "Rode o mesmo comando de novo e escreva algo como: minha-empresa"
429
+ fi
430
+ done
431
+
432
+ if [ -n "${AUROQ_INSTALL_DRY_RUN:-}" ]; then
433
+ ok_feito "Pasta ${PASTA_ALVO} — seria criada aqui"
434
+ return 0
435
+ fi
436
+
437
+ mkdir -p "$PASTA_ALVO" 2>/dev/null \
438
+ || auroq_erro "Nao consegui criar a pasta ${PASTA_ALVO}." "Confira se voce tem permissao de escrita na sua pasta pessoal e rode de novo."
439
+ auroq_registrar_log "pasta" "criada"
440
+ }
441
+
442
+ # ─── Passo 6: montar o Auroq ──────────────────────────────────────────────────
443
+
444
+ auroq_rodar_init() {
445
+ passo "Montando o Auroq dentro de ${PASTA_ALVO}..."
446
+
447
+ if [ -n "${AUROQ_INSTALL_DRY_RUN:-}" ]; then
448
+ ok_feito "Sistema — seria montado aqui"
449
+ return 0
450
+ fi
451
+
452
+ cd "$PASTA_ALVO" || auroq_erro "Nao consegui entrar na pasta ${PASTA_ALVO}." "Rode o mesmo comando de novo."
453
+ # ligado no terminal de verdade: o `init` ainda pergunta se ele ja usa o Auroq
454
+ # em outra maquina (3a pergunta do §4.4), e essa resposta e do aluno.
455
+ if ! auroq_npx_aluno init; then
456
+ auroq_registrar_log "init" "falhou"
457
+ auroq_erro "A montagem do sistema parou no meio." "Rode o mesmo comando de novo. Se repetir, mande pro suporte o arquivo ~/.auroq/install.log"
458
+ fi
459
+ auroq_registrar_log "init" "ok"
460
+ }
461
+
462
+ # ─── Passo 7: fecho ───────────────────────────────────────────────────────────
463
+
464
+ auroq_fecho() {
465
+ printf '\n %s%s%s\n\n' "$ROXO" "─────────────────────────────────────────────" "$FIM"
466
+ diz "Pronto. O Auroq esta em ${PASTA_ALVO}"
467
+ printf '\n'
468
+ diz "Falta um detalhe importante:"
469
+ printf '\n'
470
+ diz " 1. FECHE o Claude Code (ou o Codex) por completo e abra de novo"
471
+ diz " ${FRACO}— sem isso ele nao enxerga o que acabou de ser instalado${FIM}"
472
+ diz " 2. Abra a pasta ${PASTA_ALVO##*/}"
473
+ diz " 3. Chame o Ops: ${CIANO}/AuroqOS:agents:ops${FIM} ${FRACO}(no Codex: \$ops)${FIM}"
474
+ diz " 4. Digite: ${CIANO}*bootstrap${FIM}"
475
+ printf '\n'
476
+ diz "O Ops instala o resto e te guia daqui pra frente."
477
+ printf '\n'
478
+ auroq_registrar_log "fim" "ok pasta=${PASTA_ALVO##*/}"
479
+ }
480
+
481
+ # ─── Fluxo ────────────────────────────────────────────────────────────────────
482
+
483
+ auroq_main() {
484
+ titulo "Auroq OS — instalacao"
485
+ auroq_diagnostico
486
+ auroq_garantir_git
487
+ auroq_garantir_node
488
+ auroq_login_mentoria
489
+ auroq_escolher_pasta
490
+ auroq_rodar_init
491
+ auroq_fecho
492
+ }
493
+
494
+ # Carregado pelos testes: so define as funcoes e sai.
495
+ if [ -n "${AUROQ_INSTALL_LIB:-}" ]; then
496
+ return 0 2>/dev/null || true
497
+ else
498
+ PASTA_ALVO=""
499
+ auroq_main
500
+ fi
package/lib/auth.js CHANGED
@@ -296,13 +296,24 @@ async function logout() {
296
296
  ok('Sessao encerrada. Credenciais locais removidas.');
297
297
  }
298
298
 
299
+ /**
300
+ * Mostra quem esta autenticado NESTA maquina, pelo que esta salvo no disco.
301
+ *
302
+ * Retorna `true` so quando ha sessao salva com o ultimo status conhecido
303
+ * 'ativo' — e o CLI transforma isso em codigo de saida. Antes ele saia com
304
+ * sucesso ate sem credencial nenhuma, e quem usasse `whoami` como pergunta de
305
+ * sim/nao (era o caso do instalador) concluia que o aluno ja estava logado.
306
+ *
307
+ * Continua sendo uma leitura LOCAL: pra decidir acesso de verdade, o portao e
308
+ * `check-access` / `requireActiveAccess()`, que confere online.
309
+ */
299
310
  function whoami() {
300
311
  const cred = credentials.load();
301
312
  if (!cred) {
302
313
  log(`${DIM}Nao autenticado.${RESET}`);
303
314
  log('');
304
315
  log(`${DIM}Para entrar: ${CYAN}auroq-os login${RESET}`);
305
- return;
316
+ return false;
306
317
  }
307
318
  const now = Math.floor(Date.now() / 1000);
308
319
  const validatedAgo = cred.last_validated_at ? Math.floor((now - cred.last_validated_at) / 60) : null;
@@ -320,6 +331,7 @@ function whoami() {
320
331
  }
321
332
  log(`${DIM}Access token:${RESET} ${expired ? `${GOLD}expirado${RESET}` : `${GREEN}valido${RESET}`}`);
322
333
  log('');
334
+ return cred.last_status === 'ativo';
323
335
  }
324
336
 
325
337
  /**
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Diagnostico do motor — as perguntas que o `doctor` faz em voz alta.
3
+ *
4
+ * Existe por causa de uma falha que nao aparece na tela: o programa aberto pelo
5
+ * icone (Dock no Mac, Menu Iniciar no Windows) monta o ambiente lendo o perfil do
6
+ * usuario, nao a janela do terminal onde a instalacao rodou. Se o motor ficou so
7
+ * na sessao, tudo funciona no terminal e nada funciona no app — e o aluno conclui
8
+ * que o Companion "nao sabe de nada".
9
+ *
10
+ * PRD "Embarque em 2 passos" §5.4.
11
+ */
12
+ const fs = require('fs');
13
+ const path = require('path');
14
+
15
+ const INICIO = '# >>> auroq >>>';
16
+ const FIM = '# <<< auroq <<<';
17
+ const PERFIS = ['.zshrc', '.zprofile'];
18
+ const NODE_MIN = 20;
19
+
20
+ /** Devolve o caminho que o bloco do instalador publica, ou null. */
21
+ function lerBlocoAuroq(conteudo) {
22
+ if (!conteudo || typeof conteudo !== 'string') return null;
23
+ const i = conteudo.indexOf(INICIO);
24
+ if (i === -1) return null;
25
+ const f = conteudo.indexOf(FIM, i);
26
+ const trecho = f === -1 ? conteudo.slice(i) : conteudo.slice(i, f);
27
+ const m = trecho.match(/export PATH="([^":]+):\$PATH"/);
28
+ return m ? m[1] : null;
29
+ }
30
+
31
+ /**
32
+ * O programa aberto pelo icone vai enxergar o motor?
33
+ *
34
+ * No Mac a resposta mora nos perfis do usuario. No Windows o ambiente do usuario
35
+ * ja e herdado pelo processo grafico, entao a pergunta se resolve no proprio PATH.
36
+ */
37
+ function appEnxergaMotor({ homeDir, pathAtual = '', caminhoMotor = '', plataforma = process.platform } = {}) {
38
+ const partes = String(pathAtual).split(plataforma === 'win32' ? ';' : ':').filter(Boolean);
39
+
40
+ if (plataforma === 'win32') {
41
+ const temAuroq = partes.some((p) => /auroq/i.test(p));
42
+ return {
43
+ visivel: partes.length > 0,
44
+ perfisSemBloco: [],
45
+ motivo: temAuroq ? '' : 'O motor esta valendo nesta janela.',
46
+ };
47
+ }
48
+
49
+ const perfisSemBloco = [];
50
+ const conteudos = [];
51
+ let declarado = null;
52
+ for (const nome of PERFIS) {
53
+ let conteudo = null;
54
+ try {
55
+ conteudo = fs.readFileSync(path.join(homeDir, nome), 'utf8');
56
+ } catch {
57
+ conteudo = null;
58
+ }
59
+ conteudos.push(conteudo || '');
60
+ const bin = lerBlocoAuroq(conteudo);
61
+ if (bin) declarado = declarado || bin;
62
+ else perfisSemBloco.push(nome);
63
+ }
64
+
65
+ // A pergunta e "o app aberto pelo icone vai achar UM motor?", nao "o instalador
66
+ // passou por aqui?". Quem ja tinha Homebrew ou nvm declarado no perfil esta
67
+ // servido — acusar essa gente encheria o suporte de chamado sem defeito.
68
+ if (!declarado && caminhoMotor) {
69
+ const dir = path.dirname(String(caminhoMotor));
70
+ // /usr/bin e /bin ja estao no caminho padrao de qualquer programa do sistema
71
+ if (dir === '/usr/bin' || dir === '/bin') {
72
+ return { visivel: true, perfisSemBloco: [], motivo: '' };
73
+ }
74
+ const juntos = conteudos.join('\n');
75
+ const raiz = dir.replace(/\/(bin|sbin)$/, '');
76
+ if (juntos.includes(dir) || (raiz && raiz !== dir && juntos.includes(raiz))) {
77
+ return { visivel: true, perfisSemBloco: [], motivo: '' };
78
+ }
79
+ }
80
+
81
+ if (!declarado) {
82
+ return {
83
+ visivel: false,
84
+ perfisSemBloco,
85
+ motivo: 'O motor nao esta no seu perfil: quem abre o programa pelo icone (Dock) pode nao enxergar ele.',
86
+ };
87
+ }
88
+ if (perfisSemBloco.length > 0) {
89
+ return {
90
+ visivel: false,
91
+ perfisSemBloco,
92
+ motivo: `Falta o motor em ${perfisSemBloco.join(' e ')} — dependendo de como voce abre o programa, ele nao aparece.`,
93
+ };
94
+ }
95
+ const noPathAgora = partes.some((p) => p === declarado || p.startsWith(declarado));
96
+ return {
97
+ visivel: true,
98
+ perfisSemBloco: [],
99
+ motivo: noPathAgora ? '' : 'Esta no seu perfil. Feche e abra o programa pra ele passar a enxergar.',
100
+ };
101
+ }
102
+
103
+ /** De onde veio o motor que esta rodando — importa quando ha mais de um. */
104
+ function origemDoMotor(caminho) {
105
+ if (!caminho || typeof caminho !== 'string') return 'desconhecida';
106
+ if (caminho.includes('/.auroq/') || /\\Auroq\\/i.test(caminho)) return 'Auroq';
107
+ if (caminho.includes('/.nvm/') || caminho.includes('/.fnm/')) return 'nvm';
108
+ if (caminho.startsWith('/opt/homebrew/') || caminho.startsWith('/usr/local/')) return 'Homebrew';
109
+ if (caminho.startsWith('/usr/')) return 'sistema';
110
+ return 'desconhecida';
111
+ }
112
+
113
+ /** Uma frase que o aluno entende, sem jargao. */
114
+ function resumoDoMotor({ versao = '', caminho = '' } = {}) {
115
+ const limpa = String(versao).replace(/^v/, '');
116
+ const maior = parseInt(limpa.split('.')[0], 10);
117
+ const origem = origemDoMotor(caminho);
118
+ const base = `Motor do sistema: ${limpa || 'nao encontrado'} (instalado por: ${origem})`;
119
+ if (Number.isFinite(maior) && maior < NODE_MIN) {
120
+ return `${base} — esse motor e antigo demais pro sistema; rode o instalador de novo.`;
121
+ }
122
+ return base;
123
+ }
124
+
125
+ module.exports = { lerBlocoAuroq, appEnxergaMotor, origemDoMotor, resumoDoMotor, PERFIS };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "auroq-os",
3
- "version": "2.3.6",
3
+ "version": "2.4.0",
4
4
  "description": "Auroq OS — Sistema Operacional de IA para Experts",
5
5
  "bin": {
6
6
  "auroq-os": "bin/auroq-os.js"
@@ -18,7 +18,7 @@
18
18
  "auroq:sync:codex:check": "npm run sync:codex:check",
19
19
  "auroq:validate": "npm run validate:hybrid",
20
20
  "validate:hybrid": "node scripts/validate-hybrid.mjs",
21
- "lint": "node --check bin/auroq-os.js && node --check lib/auth.js && node --check lib/credentials.js && node --check lib/onepassword.js && node --check lib/supabase.js && node --check scripts/sync-codex-skills.mjs && node --check scripts/validate-hybrid.mjs && node --check scripts/prepack-casca.mjs && node --check scripts/postpack-casca.mjs",
21
+ "lint": "node --check bin/auroq-os.js && node --check lib/auth.js && node --check lib/diagnostico.js && node --check lib/credentials.js && node --check lib/onepassword.js && node --check lib/supabase.js && node --check scripts/sync-codex-skills.mjs && node --check scripts/validate-hybrid.mjs && node --check scripts/prepack-casca.mjs && node --check scripts/postpack-casca.mjs",
22
22
  "typecheck": "npm run validate:hybrid",
23
23
  "test": "node --test tests/*.test.mjs",
24
24
  "prepack": "node scripts/prepack-casca.mjs",
@@ -29,6 +29,7 @@
29
29
  "bin/",
30
30
  "lib/",
31
31
  "scripts/",
32
+ "installer/",
32
33
  "seeds/",
33
34
  ".auroq-core/core/",
34
35
  ".auroq-core/core-config.yaml",