@aiuby-ia/cli 0.5.2 → 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 CHANGED
@@ -3888,9 +3888,9 @@ var $ZodIBAN = /* @__PURE__ */ $constructor("$ZodIBAN", (inst, def) => {
3888
3888
  });
3889
3889
  };
3890
3890
  });
3891
- function isValidJWT(token, algorithm = null) {
3891
+ function isValidJWT(token2, algorithm = null) {
3892
3892
  try {
3893
- const tokensParts = token.split(".");
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 token = (await io.readSecret()).trim();
20584
- if (!looksLikeToken(token)) 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);
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) {
@@ -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);
@@ -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
- if (latest && JSON.stringify(sortKeys(latest)) === JSON.stringify(sortKeys(app.config))) {
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.5.2";
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, rollback, volumes, secrets, domains
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", "rollback", "volumes", "secrets", "domains"];
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 sem autenticação, prazo de algumas horas. Sem cobrança nesta fase.
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`, `rollback`, `volumes`, `secrets`
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
  {
2
2
  "name": "@aiuby-ia/cli",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "Linha de comando do AIUBY Compute",
5
5
  "type": "module",
6
6
  "bin": {