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.
- package/README.md +22 -0
- package/bin/auroq-os.js +105 -10
- package/installer/README.md +102 -0
- package/installer/install.ps1 +478 -0
- package/installer/install.sh +500 -0
- package/lib/auth.js +13 -1
- package/lib/diagnostico.js +125 -0
- package/package.json +3 -2
|
@@ -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
|
+
"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",
|