@aiuby-ia/cli 0.5.1 → 0.6.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/dist/aiuby.mjs +45 -12
- package/docs/cli.md +4 -2
- package/package.json +1 -1
- package/templates/hf-stable-diffusion/Dockerfile +1 -1
package/dist/aiuby.mjs
CHANGED
|
@@ -3888,9 +3888,9 @@ var $ZodIBAN = /* @__PURE__ */ $constructor("$ZodIBAN", (inst, def) => {
|
|
|
3888
3888
|
});
|
|
3889
3889
|
};
|
|
3890
3890
|
});
|
|
3891
|
-
function isValidJWT(
|
|
3891
|
+
function isValidJWT(token2, algorithm = null) {
|
|
3892
3892
|
try {
|
|
3893
|
-
const tokensParts =
|
|
3893
|
+
const tokensParts = token2.split(".");
|
|
3894
3894
|
if (tokensParts.length !== 3)
|
|
3895
3895
|
return false;
|
|
3896
3896
|
const [header] = tokensParts;
|
|
@@ -20495,6 +20495,7 @@ var HardwareRequest = external_exports.object({
|
|
|
20495
20495
|
memoryMb: external_exports.number().int().min(256).max(786432),
|
|
20496
20496
|
gpu: external_exports.object({ sku: external_exports.string().min(1).max(60), count: external_exports.number().int().min(1).max(8) }).nullable().default(null)
|
|
20497
20497
|
}).strict();
|
|
20498
|
+
var ACCESS_MODES = ["public", "token"];
|
|
20498
20499
|
var AppConfig = external_exports.object({
|
|
20499
20500
|
/** `container`: imagem pronta. `python`: build a partir do código (planejado). */
|
|
20500
20501
|
runtime: external_exports.enum(["container", "python"]),
|
|
@@ -20509,6 +20510,11 @@ var AppConfig = external_exports.object({
|
|
|
20509
20510
|
*/
|
|
20510
20511
|
env: external_exports.record(external_exports.string().regex(ENV_NAME, "nome de vari\xE1vel: mai\xFAsculas, d\xEDgitos e sublinhado").refine((name) => !RESERVED_ENV.has(name), "nome reservado pela plataforma"), external_exports.string().max(2e3).refine(noControl, "valor n\xE3o aceita caracteres de controle")).refine((env) => Object.keys(env).length <= MAX_ENV_VARS, `no m\xE1ximo ${MAX_ENV_VARS} vari\xE1veis`).default({}),
|
|
20511
20512
|
hardware: HardwareRequest,
|
|
20513
|
+
/**
|
|
20514
|
+
* Quem chama o endereço: `public` (qualquer um) ou `token` (só com token de acesso, veja `aiuby token`).
|
|
20515
|
+
* Ausente: aplicação nova nasce pública, e revisão nova mantém o acesso da atual.
|
|
20516
|
+
*/
|
|
20517
|
+
access: external_exports.enum(ACCESS_MODES).optional(),
|
|
20512
20518
|
scaling: external_exports.object({
|
|
20513
20519
|
minReplicas: external_exports.number().int().min(0).max(MAX_REPLICAS).default(0),
|
|
20514
20520
|
maxReplicas: external_exports.number().int().min(1).max(MAX_REPLICAS).default(1)
|
|
@@ -20580,10 +20586,10 @@ var emit = (io, data, human) => io.json ? io.out(JSON.stringify(data, null, 2))
|
|
|
20580
20586
|
async function login(io, args) {
|
|
20581
20587
|
const urlFlag = args.indexOf("--url");
|
|
20582
20588
|
const apiUrl = normalizeApiUrl(urlFlag >= 0 ? args[urlFlag + 1] ?? "" : process.env.AIUBY_API_URL ?? DEFAULT_API_URL);
|
|
20583
|
-
const
|
|
20584
|
-
if (!looksLikeToken(
|
|
20585
|
-
const who = await apiGet({ apiUrl, token }, "/api/platform/compute/whoami", io.fetcher);
|
|
20586
|
-
saveCredentials({ apiUrl, token });
|
|
20589
|
+
const token2 = (await io.readSecret()).trim();
|
|
20590
|
+
if (!looksLikeToken(token2)) throw new CliError('Isso n\xE3o parece uma chave de API da AIUBY. Crie uma em Chaves de API, com o escopo "Ler o Compute".', EXIT.usage);
|
|
20591
|
+
const who = await apiGet({ apiUrl, token: token2 }, "/api/platform/compute/whoami", io.fetcher);
|
|
20592
|
+
saveCredentials({ apiUrl, token: token2 });
|
|
20587
20593
|
emit(io, { ok: true, ...who }, () => io.out(`Autenticado em ${who.organization}, workspace ${who.workspace}. Credencial guardada em ${credentialsPath()}.`));
|
|
20588
20594
|
}
|
|
20589
20595
|
async function logout(io) {
|
|
@@ -20637,7 +20643,7 @@ async function appsStatus(io, args) {
|
|
|
20637
20643
|
});
|
|
20638
20644
|
}
|
|
20639
20645
|
var brl = (microBrl) => (microBrl / 1e6).toLocaleString("pt-BR", { style: "currency", currency: "BRL" });
|
|
20640
|
-
var paidLine = (paid) => `Tamanho ${paid.sizeName}${paid.priceMicroBrlPerHour !== null ? `: ${brl(paid.priceMicroBrlPerHour)} por hora em uso` : ""}.`;
|
|
20646
|
+
var paidLine = (paid) => `Tamanho ${paid.sizeName}${paid.priceMicroBrlPerHour !== null ? `: ${brl(paid.priceMicroBrlPerHour)} ${paid.unit === "hour_started" ? "por hora iniciada de inst\xE2ncia ligada" : "por hora em uso"}` : ""}.`;
|
|
20641
20647
|
var fetchDeployment = (io, stored, slug) => apiGet(stored, `/api/platform/compute/apps/${encodeURIComponent(slug)}/deployments`, io.fetcher);
|
|
20642
20648
|
var when = (iso) => new Date(iso).toLocaleString("pt-BR", { dateStyle: "short", timeStyle: "short" });
|
|
20643
20649
|
function printDeployment(io, d) {
|
|
@@ -20687,6 +20693,28 @@ async function stop(io, args) {
|
|
|
20687
20693
|
await apiPost(requireLogin(), `/api/platform/compute/apps/${encodeURIComponent(slug)}/deployments/stop`, void 0, io.fetcher).catch(scopeHint(DEPLOY_HINT));
|
|
20688
20694
|
emit(io, { ok: true, slug }, () => io.out(`${slug} parada: o servi\xE7o foi apagado e o endere\xE7o n\xE3o responde mais.`));
|
|
20689
20695
|
}
|
|
20696
|
+
async function rollback(io, args) {
|
|
20697
|
+
const [slug, raw] = args.filter((a) => !a.startsWith("--"));
|
|
20698
|
+
const target = Number((raw ?? "").replace(/^r/, ""));
|
|
20699
|
+
if (!slug || !Number.isInteger(target) || target < 1) throw new CliError("Uso: aiuby rollback <identificador> <revis\xE3o> (ex.: aiuby rollback minha-api 3)", EXIT.usage);
|
|
20700
|
+
const stored = requireLogin();
|
|
20701
|
+
const app = await apiGet(stored, `/api/platform/compute/apps/${encodeURIComponent(slug)}`, io.fetcher);
|
|
20702
|
+
const done = await apiPost(stored, `/api/platform/compute/apps/${encodeURIComponent(app.id)}/revisions/revert`, { revision: target }, io.fetcher).catch(scopeHint(WRITE_HINT));
|
|
20703
|
+
emit(io, { id: app.id, slug: app.slug, ...done }, () => {
|
|
20704
|
+
io.out(`${app.slug}: revis\xE3o r${done.revision} gravada com a configura\xE7\xE3o da r${done.from}. Nenhuma revis\xE3o foi apagada.`);
|
|
20705
|
+
io.out(`O que est\xE1 implantado n\xE3o mudou. Para publicar: aiuby deploy ${app.slug}`);
|
|
20706
|
+
});
|
|
20707
|
+
}
|
|
20708
|
+
var INVOKE_HINT = 'A chave precisa do escopo "Chamar aplica\xE7\xF5es do Compute".';
|
|
20709
|
+
async function token(io, args) {
|
|
20710
|
+
const slug = args.find((a) => !a.startsWith("--"));
|
|
20711
|
+
if (!slug) throw new CliError("Uso: aiuby token <identificador>", EXIT.usage);
|
|
20712
|
+
const issued = await apiPost(requireLogin(), `/api/platform/compute/apps/${encodeURIComponent(slug)}/access-token`, void 0, io.fetcher).catch(scopeHint(INVOKE_HINT));
|
|
20713
|
+
emit(io, issued, () => {
|
|
20714
|
+
io.err(`Token de ${slug}, v\xE1lido at\xE9 ${when(issued.expiresAt)}, s\xF3 para ${issued.url}. Vai no cabe\xE7alho "Authorization: Bearer".`);
|
|
20715
|
+
io.out(issued.token);
|
|
20716
|
+
});
|
|
20717
|
+
}
|
|
20690
20718
|
async function logs(io, args) {
|
|
20691
20719
|
const slug = args.find((a, i) => !a.startsWith("--") && args[i - 1] !== "--limit");
|
|
20692
20720
|
if (!slug) throw new CliError("Uso: aiuby logs [--limit N] <identificador>", EXIT.usage);
|
|
@@ -20707,7 +20735,7 @@ async function billing(io) {
|
|
|
20707
20735
|
io.out(`Compute no m\xEAs: ${brl(data.monthSpendMicroBrl)}`);
|
|
20708
20736
|
io.out(`Teto do workspace: ${data.budgetMicroBrl === null ? "n\xE3o definido (dono ou admin define na tela do Compute)" : brl(data.budgetMicroBrl)}`);
|
|
20709
20737
|
io.out("");
|
|
20710
|
-
table(io, ["TAMANHO", "VCPU", "MEM\xD3RIA", "PRE\xC7O
|
|
20738
|
+
table(io, ["TAMANHO", "VCPU", "MEM\xD3RIA", "REGI\xC3O", "PRE\xC7O"], data.sizes.map((s) => [s.name, String(s.cpu), `${s.memoryMb} MB`, s.region ?? "S\xE3o Paulo", s.priceMicroBrlPerHour === null ? "-" : `${brl(s.priceMicroBrlPerHour)} ${s.unit === "hour_started" ? "por hora iniciada" : "por hora em uso"}`]));
|
|
20711
20739
|
});
|
|
20712
20740
|
}
|
|
20713
20741
|
async function usage(io, args) {
|
|
@@ -20777,7 +20805,8 @@ async function appsUpdate(io, args) {
|
|
|
20777
20805
|
const stored_ = current.revisions.find((r) => r.revision === current.currentRevision)?.config;
|
|
20778
20806
|
const normalized = stored_ ? AppConfig.safeParse(stored_) : null;
|
|
20779
20807
|
const latest = normalized?.success ? normalized.data : stored_;
|
|
20780
|
-
|
|
20808
|
+
const wanted = app.config.access === void 0 && latest?.access !== void 0 ? { ...app.config, access: latest.access } : app.config;
|
|
20809
|
+
if (latest && JSON.stringify(sortKeys(latest)) === JSON.stringify(sortKeys(wanted))) {
|
|
20781
20810
|
emit(io, { id: current.id, slug: current.slug, revision: current.currentRevision, changed: false }, () => io.out(`Sem mudan\xE7a: ${current.slug} j\xE1 est\xE1 com essa configura\xE7\xE3o (r${current.currentRevision}).`));
|
|
20782
20811
|
return;
|
|
20783
20812
|
}
|
|
@@ -20841,7 +20870,7 @@ var notYet = (command) => async () => {
|
|
|
20841
20870
|
};
|
|
20842
20871
|
|
|
20843
20872
|
// src/main.ts
|
|
20844
|
-
var VERSION = "0.
|
|
20873
|
+
var VERSION = "0.6.0";
|
|
20845
20874
|
var HELP = `aiuby ${VERSION} \u2014 linha de comando do AIUBY Compute
|
|
20846
20875
|
|
|
20847
20876
|
Uso: aiuby <comando> [op\xE7\xF5es]
|
|
@@ -20861,13 +20890,15 @@ Dispon\xEDveis hoje:
|
|
|
20861
20890
|
deploy --dry-run <identificador> Mostra o plano, sem criar nada
|
|
20862
20891
|
deploy [--no-wait] <identificador> Implanta a revis\xE3o atual (o plano diz se a organiza\xE7\xE3o pode)
|
|
20863
20892
|
stop <identificador> Para a implanta\xE7\xE3o e apaga o servi\xE7o
|
|
20893
|
+
rollback <identificador> <N> Grava uma revis\xE3o nova com a configura\xE7\xE3o da revis\xE3o N (n\xE3o implanta)
|
|
20894
|
+
token <identificador> Token de acesso (1 hora) para aplica\xE7\xE3o com endere\xE7o protegido
|
|
20864
20895
|
logs [--limit N] <identificador> \xDAltimas linhas de log da implanta\xE7\xE3o mais recente
|
|
20865
20896
|
billing Saldo, teto e gasto do m\xEAs de Compute, e os tamanhos com pre\xE7o
|
|
20866
20897
|
usage [--limit N] Extrato do Compute no workspace
|
|
20867
20898
|
doctor Confere ambiente, credencial e API
|
|
20868
20899
|
|
|
20869
20900
|
Ainda n\xE3o dispon\xEDveis:
|
|
20870
|
-
run, scale,
|
|
20901
|
+
run, scale, volumes, secrets, domains
|
|
20871
20902
|
|
|
20872
20903
|
Op\xE7\xF5es gerais:
|
|
20873
20904
|
--json Sa\xEDda em JSON
|
|
@@ -20876,7 +20907,7 @@ Op\xE7\xF5es gerais:
|
|
|
20876
20907
|
|
|
20877
20908
|
Ambiente: AIUBY_TOKEN, AIUBY_API_URL, AIUBY_CONFIG_DIR
|
|
20878
20909
|
C\xF3digos de sa\xEDda: 0 ok \xB7 1 erro \xB7 2 uso incorreto \xB7 3 n\xE3o autenticado \xB7 4 recusado \xB7 5 configura\xE7\xE3o inv\xE1lida`;
|
|
20879
|
-
var PLANNED = ["run", "scale", "
|
|
20910
|
+
var PLANNED = ["run", "scale", "volumes", "secrets", "domains"];
|
|
20880
20911
|
var ROUTES = {
|
|
20881
20912
|
login,
|
|
20882
20913
|
logout,
|
|
@@ -20891,6 +20922,8 @@ var ROUTES = {
|
|
|
20891
20922
|
"config validate": configValidate,
|
|
20892
20923
|
deploy,
|
|
20893
20924
|
stop,
|
|
20925
|
+
rollback,
|
|
20926
|
+
token,
|
|
20894
20927
|
logs,
|
|
20895
20928
|
billing,
|
|
20896
20929
|
usage,
|
package/docs/cli.md
CHANGED
|
@@ -53,12 +53,14 @@ runtime habilitada pela AIUBY. Fora dela, a API recusa com os motivos (código d
|
|
|
53
53
|
| --- | --- |
|
|
54
54
|
| `aiuby deploy [--no-wait] <identificador>` | Implanta a revisão atual no Google Cloud e acompanha até ficar em execução (até 3 minutos). Mostra o endereço público e o prazo. Reimplantar troca a revisão no mesmo endereço |
|
|
55
55
|
| `aiuby stop <identificador>` | Para a implantação: o serviço é apagado e o endereço deixa de responder |
|
|
56
|
+
| `aiuby rollback <identificador> <N>` | Grava uma revisão nova com a configuração da revisão N. Nada é apagado e nada é implantado: publique com `aiuby deploy`. Pede o escopo "Gerenciar aplicações do Compute" |
|
|
57
|
+
| `aiuby token <identificador>` | Token de acesso, válido por 1 hora, para chamar uma aplicação com endereço protegido (`access = "token"`). Escreve só o token na saída, para `TOKEN=$(aiuby token minha-api)`. Pede o escopo "Chamar aplicações do Compute" |
|
|
56
58
|
| `aiuby billing` | Saldo pré-pago, teto mensal e gasto do mês de Compute no workspace, e os tamanhos com o preço por hora em uso |
|
|
57
59
|
| `aiuby usage [--limit N]` | Extrato do Compute: períodos medidos e cobrados (padrão 50, até 200) |
|
|
58
60
|
| `aiuby logs [--limit N] <identificador>` | Últimas linhas de log da implantação mais recente (padrão 100, até 200). Vale também para uma que falhou ou já parou |
|
|
59
61
|
|
|
60
62
|
Limites da prévia: só CPU, até 1 vCPU e 1 GB, 1 réplica (escala a zero quando ociosa),
|
|
61
|
-
endereço público
|
|
63
|
+
endereço público por padrão (`access = "token"` no `[config]` do `aiuby.toml` exige token de acesso), prazo de algumas horas. Sem cobrança nesta fase.
|
|
62
64
|
|
|
63
65
|
## Local
|
|
64
66
|
|
|
@@ -70,7 +72,7 @@ endereço público sem autenticação, prazo de algumas horas. Sem cobrança nes
|
|
|
70
72
|
|
|
71
73
|
## Ainda não disponíveis
|
|
72
74
|
|
|
73
|
-
`run`, `scale`, `
|
|
75
|
+
`run`, `scale`, `volumes`, `secrets`
|
|
74
76
|
e `domains` respondem que ainda não estão disponíveis e saem com código 4. Nenhum deles chama a API.
|
|
75
77
|
|
|
76
78
|
## Opções gerais
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Geração de imagem com um modelo de difusão do Hugging Face. O modelo é baixado na primeira
|
|
2
2
|
# subida: escolha um cuja licença permita o seu uso e que caiba na memória da GPU pedida.
|
|
3
|
-
FROM pytorch/pytorch:2.
|
|
3
|
+
FROM pytorch/pytorch:2.7.1-cuda12.6-cudnn9-runtime
|
|
4
4
|
WORKDIR /app
|
|
5
5
|
COPY requirements.txt .
|
|
6
6
|
RUN pip install --no-cache-dir -r requirements.txt
|