onveloz 0.0.0-beta.34 → 0.0.0-beta.36

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.
@@ -1,4 +1,4 @@
1
- import { a as parseBuildLine, c as getClient, g as success, h as info, i as isHiddenMessage, o as TERMINAL_STATUSES, r as cleanDisplayLine, s as statusLabels } from "./index.mjs";
1
+ import { a as statusLabels, i as TERMINAL_STATUSES, m as success, n as isHiddenMessage, o as getClient, p as info, r as parseBuildLine, t as cleanDisplayLine } from "./index.mjs";
2
2
  import chalk from "chalk";
3
3
  import { useEffect, useRef, useState } from "react";
4
4
  import { Box, Static, Text, render, useApp } from "ink";
@@ -9,12 +9,12 @@ const TIMESTAMP_RE = /^\[(\d{4}-\d{2}-\d{2}T[\d:.]+Z)\]\s*/;
9
9
  const STEP_PREFIX_RE = /^#(\d+)\s+(.*)/;
10
10
  const TIMING_PREFIX_RE = /^(\d+\.\d+)\s*(.*)/;
11
11
  const DONE_RE = /^DONE\s+([\d.]+s?)$/;
12
- const STAGE_RE = /^\[([\w-]+)\s+(\d+)\/\d+\]/;
12
+ const STAGE_RE = /^\[\s*(?:([\w-]+)\s+)?(\d+)\/\d+\]/;
13
13
  function parseStageInfo(title) {
14
14
  const match = title.match(STAGE_RE);
15
15
  if (!match) return null;
16
16
  return {
17
- stage: match[1].toLowerCase(),
17
+ stage: (match[1] ?? "").toLowerCase(),
18
18
  substep: parseInt(match[2], 10)
19
19
  };
20
20
  }
@@ -32,9 +32,9 @@ const PRE_START_HEADER_RE = /^──\s*Logs do comando pre-start/;
32
32
  /** Matches the pre-start unavailable message. */
33
33
  const PRE_START_UNAVAILABLE_RE = /^──\s*Logs do comando pre-start indisponíveis/;
34
34
  /** Matches the crash log section header written by the reconciler. */
35
- const CRASH_LOG_HEADER_RE = /^──\s*Logs do container \(crash\)/;
35
+ const CRASH_LOG_HEADER_RE = /^──\s*Logs do (?:serviço \(falha\)|container \(crash\))/;
36
36
  /** Matches the crash logs unavailable message. */
37
- const CRASH_LOG_UNAVAILABLE_RE = /^──\s*Logs do container indisponíveis/;
37
+ const CRASH_LOG_UNAVAILABLE_RE = /^──\s*Logs do (?:serviço|container) indisponíveis/;
38
38
  /** Lines that mark the beginning of the deploy/rollout phase. */
39
39
  const DEPLOY_PHASE_MARKERS = [
40
40
  /^Realizando deploy/,
@@ -175,16 +175,16 @@ function parseBuildSteps(rawText) {
175
175
  const stageOrder = buildStageOrder(numberedWithIndex.map((n) => n.step));
176
176
  numberedWithIndex.sort((a, b) => {
177
177
  if (a.step.phase !== b.step.phase) return a.step.phase - b.step.phase;
178
- if (a.step.startedAt && b.step.startedAt) {
179
- const tsDiff = new Date(a.step.startedAt).getTime() - new Date(b.step.startedAt).getTime();
180
- if (tsDiff !== 0) return tsDiff;
181
- }
182
178
  const aInfo = parseStageInfo(a.step.title);
183
179
  const bInfo = parseStageInfo(b.step.title);
184
180
  const aOrder = aInfo ? stageOrder.get(aInfo.stage) ?? 99 : 99;
185
181
  const bOrder = bInfo ? stageOrder.get(bInfo.stage) ?? 99 : 99;
186
182
  if (aOrder !== bOrder) return aOrder - bOrder;
187
- return (aInfo?.substep ?? a.step.stepNumber ?? 0) - (bInfo?.substep ?? b.step.stepNumber ?? 0);
183
+ const aSubstep = aInfo?.substep ?? a.step.stepNumber ?? 0;
184
+ const bSubstep = bInfo?.substep ?? b.step.stepNumber ?? 0;
185
+ if (aSubstep !== bSubstep) return aSubstep - bSubstep;
186
+ if (a.step.startedAt && b.step.startedAt) return new Date(a.step.startedAt).getTime() - new Date(b.step.startedAt).getTime();
187
+ return 0;
188
188
  });
189
189
  const result = [];
190
190
  let nIdx = 0;
package/dist/index.mjs CHANGED
@@ -292,6 +292,18 @@ const PROJECT_FLAG_DEFINITIONS = {
292
292
  shield: {
293
293
  label: "Shield",
294
294
  description: "Auditoria de segurança automática (Veloz Shield) a cada deploy. Detecta Supabase e executa verificações de RLS, secrets e HTTP."
295
+ },
296
+ multiProvider: {
297
+ label: "Multi-Provider",
298
+ description: "Roteia este projeto pela nova arquitetura multi-provider (agent + DesiredState + build como Job). Desligado = pipeline atual (reconciler + workers)."
299
+ },
300
+ blobStorage: {
301
+ label: "Blob Storage + CDN",
302
+ description: "Armazenamento de arquivos estáticos com CDN via Cloudflare R2 + Worker"
303
+ },
304
+ imageOptimization: {
305
+ label: "Otimização de imagens",
306
+ description: "Otimização de imagens em /_next/image (AVIF/WebP, redimensionamento). Aplica-se aos domínios verificados deste projeto."
295
307
  }
296
308
  };
297
309
  const PROJECT_FLAG_KEYS = Object.keys(PROJECT_FLAG_DEFINITIONS);
@@ -598,7 +610,7 @@ const LOWERCASE_SIZE_KEYS = DATABASE_SIZE_KEYS.map((k) => k);
598
610
  const DatabaseSizeSchema = z$1.enum(LOWERCASE_SIZE_KEYS);
599
611
  const DatabaseConfigSchema = z$1.object({
600
612
  id: z$1.string().optional(),
601
- name: z$1.string().optional(),
613
+ name: z$1.string().max(63).regex(/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/).optional(),
602
614
  engine: z$1.enum([
603
615
  "postgresql",
604
616
  "mysql",
@@ -607,6 +619,7 @@ const DatabaseConfigSchema = z$1.object({
607
619
  version: z$1.string().optional(),
608
620
  storage: z$1.string().regex(/^[0-9]+(Gi)$/).refine((val) => Number.parseInt(val, 10) >= 10, "Tamanho mínimo de storage é 10Gi").optional(),
609
621
  size: DatabaseSizeSchema.optional(),
622
+ insightsEnabled: z$1.boolean().optional(),
610
623
  pooler: PoolerConfigSchema.optional(),
611
624
  fromTemplate: z$1.string().optional()
612
625
  });
@@ -1260,7 +1273,7 @@ async function requireAuth(options) {
1260
1273
 
1261
1274
  //#endregion
1262
1275
  //#region src/lib/client.ts
1263
- const CLI_VERSION = "0.0.0-beta.34";
1276
+ const CLI_VERSION = "0.0.0-beta.36";
1264
1277
  const USER_AGENT = `veloz-cli/${CLI_VERSION}`;
1265
1278
  /**
1266
1279
  * Client source — "cli" by default; the init wizard sets this to "cli-wizard"
@@ -1299,7 +1312,14 @@ async function getAuthHeaders() {
1299
1312
  }
1300
1313
  async function getClient() {
1301
1314
  const authConfig = await requireAuth();
1302
- return createClient(authConfig.apiUrl, () => buildVelozHeaders(authConfig));
1315
+ return createClient(authConfig.apiUrl, () => {
1316
+ const fresh = loadConfig();
1317
+ return buildVelozHeaders({
1318
+ apiKey: authConfig.apiKey,
1319
+ apiUrl: authConfig.apiUrl,
1320
+ organizationId: fresh.organizationId ?? authConfig.organizationId
1321
+ });
1322
+ });
1303
1323
  }
1304
1324
 
1305
1325
  //#endregion
@@ -1322,7 +1342,7 @@ const requireAuth$1 = middleware(async (c, next) => {
1322
1342
  description: "Autenticar na plataforma"
1323
1343
  }] }
1324
1344
  });
1325
- const { performLogin: performLogin$1 } = await import("./login-B0sENXl7.mjs");
1345
+ const { performLogin: performLogin$1 } = await import("./login-BXY4HXAt.mjs");
1326
1346
  await performLogin$1();
1327
1347
  }
1328
1348
  await next();
@@ -1440,6 +1460,32 @@ orgsGroup.command("use", {
1440
1460
  //#endregion
1441
1461
  //#region src/lib/org-resolver.ts
1442
1462
  /**
1463
+ * Server emits `ORG_MISMATCH` (FORBIDDEN with `data.code === "ORG_MISMATCH"`)
1464
+ * when the X-Organization-Id header points at a different org than the
1465
+ * resource's owning org AND the user is a verified member of that target org
1466
+ * (see `packages/api/src/lib/ownership.ts:throwOrgMismatch`). The error data
1467
+ * carries the correct `organizationId` so the CLI can switch transparently.
1468
+ */
1469
+ function isOrgMismatchError(error) {
1470
+ if (!(error instanceof Error)) return false;
1471
+ const e = error;
1472
+ return e.data?.code === "ORG_MISMATCH" && typeof e.data?.organizationId === "string";
1473
+ }
1474
+ /**
1475
+ * Persist the org id surfaced by an `ORG_MISMATCH` error and notify the user.
1476
+ * Returns the new org id so callers can decide whether to retry. Trusts the
1477
+ * server's membership check — no extra round-trip required.
1478
+ */
1479
+ function applyOrgMismatchSwitch(error) {
1480
+ if (!isOrgMismatchError(error)) return null;
1481
+ const targetOrgId = error.data?.organizationId;
1482
+ if (!targetOrgId) return null;
1483
+ if (loadConfig().organizationId === targetOrgId) return null;
1484
+ saveConfig({ organizationId: targetOrgId });
1485
+ info(`Workspace alterado automaticamente para o dono deste recurso (${chalk.dim(targetOrgId)}).`);
1486
+ return targetOrgId;
1487
+ }
1488
+ /**
1443
1489
  * Ensure an active organization is selected for the current CLI session.
1444
1490
  *
1445
1491
  * Short-circuits when:
@@ -2167,6 +2213,25 @@ const tlsStatusLabels = {
2167
2213
  ACTIVE: "Ativo",
2168
2214
  FAILED: "Falhou"
2169
2215
  };
2216
+ const purposeLabels = {
2217
+ traffic: "tráfego",
2218
+ ownership: "validação de propriedade",
2219
+ ssl: "validação de SSL"
2220
+ };
2221
+ function printDnsRecords(records) {
2222
+ if (records.length === 0) return;
2223
+ console.log();
2224
+ console.log(chalk.yellow.bold(" Registros DNS a publicar:"));
2225
+ for (const r of records) {
2226
+ const label = purposeLabels[r.purpose] ?? r.purpose;
2227
+ console.log(` ${chalk.cyan(r.type.padEnd(5))} ${chalk.white(r.name)} → ${chalk.white(r.value)} ${chalk.dim(`(${label})`)}`);
2228
+ }
2229
+ if (records.some((r) => r.type === "TXT")) {
2230
+ console.log();
2231
+ console.log(chalk.dim(" TXT são obrigatórios para curingas (propriedade da zona + SSL DV)."));
2232
+ }
2233
+ console.log();
2234
+ }
2170
2235
  const domainsGroup = Cli.create("domains", { description: "Gerenciar domínios personalizados" });
2171
2236
  domainsGroup.command("list", {
2172
2237
  description: "Listar domínios dos serviços",
@@ -2181,7 +2246,17 @@ domainsGroup.command("list", {
2181
2246
  id: z.string(),
2182
2247
  domain: z.string(),
2183
2248
  tlsStatus: z.string(),
2184
- isAutoGenerated: z.boolean()
2249
+ isAutoGenerated: z.boolean(),
2250
+ dnsRecords: z.array(z.object({
2251
+ type: z.enum(["CNAME", "TXT"]),
2252
+ name: z.string(),
2253
+ value: z.string(),
2254
+ purpose: z.enum([
2255
+ "traffic",
2256
+ "ownership",
2257
+ "ssl"
2258
+ ])
2259
+ })).default([])
2185
2260
  }))
2186
2261
  })) }),
2187
2262
  async run(c) {
@@ -2203,14 +2278,19 @@ domainsGroup.command("list", {
2203
2278
  });
2204
2279
  continue;
2205
2280
  }
2206
- for (const d of domains) console.log(` ${d.id} ${chalk.bold(d.domain)} ${tlsStatusLabels[d.tlsStatus] ?? d.tlsStatus} ${d.isAutoGenerated ? "Auto" : "Personalizado"}`);
2281
+ for (const d of domains) {
2282
+ console.log(` ${d.id} ${chalk.bold(d.domain)} ${tlsStatusLabels[d.tlsStatus] ?? d.tlsStatus} ${d.isAutoGenerated ? "Auto" : "Personalizado"}`);
2283
+ const records = d.dnsRecords ?? [];
2284
+ if (!d.isAutoGenerated && d.tlsStatus !== "ACTIVE" && records.length > 0) printDnsRecords(records);
2285
+ }
2207
2286
  result$2.push({
2208
2287
  serviceName: service$2.name,
2209
2288
  domains: domains.map((d) => ({
2210
2289
  id: d.id,
2211
2290
  domain: d.domain,
2212
2291
  tlsStatus: d.tlsStatus,
2213
- isAutoGenerated: d.isAutoGenerated
2292
+ isAutoGenerated: d.isAutoGenerated,
2293
+ dnsRecords: d.dnsRecords ?? []
2214
2294
  }))
2215
2295
  });
2216
2296
  }
@@ -2238,14 +2318,12 @@ domainsGroup.command("add", {
2238
2318
  })
2239
2319
  });
2240
2320
  success(`Domínio ${chalk.bold(c.args.dominio)} adicionado com sucesso!`);
2241
- console.log();
2242
- console.log(chalk.yellow.bold(" Instruções de DNS:"));
2243
- console.log(chalk.white(` ${result$2.dnsInstruction}`));
2244
- console.log();
2321
+ printDnsRecords(result$2.dnsRecords ?? []);
2245
2322
  return c.ok({
2246
2323
  id: result$2.id,
2247
2324
  domain: c.args.dominio,
2248
- dnsInstruction: result$2.dnsInstruction
2325
+ dnsInstruction: result$2.dnsInstruction,
2326
+ dnsRecords: result$2.dnsRecords ?? []
2249
2327
  }, { cta: { commands: [{
2250
2328
  command: `domains verify ${result$2.id}`,
2251
2329
  description: "Verificar configuração DNS"
@@ -2268,15 +2346,114 @@ domainsGroup.command("verify", {
2268
2346
  if (result$2.ready) success(`Domínio ${chalk.bold(result$2.domain.domain)} com TLS ativo!`);
2269
2347
  else {
2270
2348
  console.log(chalk.yellow(`\n⚠ Certificado TLS de ${chalk.bold(result$2.domain.domain)} ainda sendo provisionado.`));
2271
- info("Verifique se o CNAME foi configurado para cname.onveloz.com.");
2349
+ const records = result$2.domain.dnsRecords ?? [];
2350
+ if (records.length > 0) {
2351
+ info("Confirme que os registros DNS abaixo estão publicados:");
2352
+ printDnsRecords(records);
2353
+ } else info("Rode `veloz domains list` para ver os registros DNS esperados.");
2272
2354
  }
2273
2355
  return {
2274
2356
  ready: result$2.ready,
2275
2357
  domain: result$2.domain.domain,
2276
- tlsStatus: result$2.domain.tlsStatus
2358
+ tlsStatus: result$2.domain.tlsStatus,
2359
+ dnsRecords: result$2.domain.dnsRecords ?? []
2277
2360
  };
2278
2361
  }
2279
2362
  });
2363
+ domainsGroup.command("search", {
2364
+ description: "Buscar domínios disponíveis para registro",
2365
+ middleware: [requireAuth$1],
2366
+ args: z.object({ query: z.string().describe("Nome do domínio a buscar (ex: meusite)") }),
2367
+ output: z.array(z.object({
2368
+ domain: z.string(),
2369
+ available: z.boolean()
2370
+ })),
2371
+ async run(c) {
2372
+ const client = await getClient();
2373
+ const results = await withSpinner({
2374
+ text: "Buscando domínios...",
2375
+ fn: () => client.domains.search({ query: c.args.query })
2376
+ });
2377
+ if (results.length === 0) {
2378
+ info("Nenhum resultado encontrado.");
2379
+ return [];
2380
+ }
2381
+ for (const r of results) {
2382
+ const status = r.available ? chalk.green("Disponível") : chalk.red("Indisponível");
2383
+ console.log(` ${chalk.bold(r.domain.padEnd(30))} ${status}`);
2384
+ }
2385
+ return results;
2386
+ }
2387
+ });
2388
+ domainsGroup.command("buy", {
2389
+ description: "Registrar um domínio",
2390
+ middleware: [requireAuth$1],
2391
+ args: z.object({ dominio: z.string().describe("Domínio a registrar (ex: meusite.com)") }),
2392
+ options: z.object({ service: z.string().optional().describe("Conectar ao serviço automaticamente") }),
2393
+ output: z.object({
2394
+ id: z.string(),
2395
+ domain: z.string(),
2396
+ status: z.string()
2397
+ }),
2398
+ async run(c) {
2399
+ const client = await getClient();
2400
+ const serviceId = c.options.service ? await resolveServiceId(c.options.service) : void 0;
2401
+ const result$2 = await withSpinner({
2402
+ text: `Registrando ${chalk.bold(c.args.dominio)}...`,
2403
+ fn: () => client.domains.register({
2404
+ domain: c.args.dominio,
2405
+ serviceId
2406
+ })
2407
+ });
2408
+ success(`Domínio ${chalk.bold(c.args.dominio)} registrado com sucesso!`);
2409
+ if (serviceId) success("DNS configurado e domínio conectado ao serviço automaticamente.");
2410
+ else info(`Para conectar a um serviço: ${chalk.bold(`veloz domains add ${c.args.dominio} --service <nome>`)}`);
2411
+ return {
2412
+ id: result$2.id,
2413
+ domain: result$2.domain,
2414
+ status: result$2.status
2415
+ };
2416
+ }
2417
+ });
2418
+ domainsGroup.command("purchased", {
2419
+ description: "Listar domínios registrados",
2420
+ middleware: [requireAuth$1],
2421
+ output: z.array(z.object({
2422
+ id: z.string(),
2423
+ domain: z.string(),
2424
+ status: z.string(),
2425
+ expiresAt: z.string().nullable()
2426
+ })),
2427
+ async run() {
2428
+ const client = await getClient();
2429
+ const purchased = await withSpinner({
2430
+ text: "Carregando domínios registrados...",
2431
+ fn: () => client.domains.listPurchased({})
2432
+ });
2433
+ if (purchased.length === 0) {
2434
+ info("Nenhum domínio registrado.");
2435
+ return [];
2436
+ }
2437
+ const statusLabels$1 = {
2438
+ REGISTERING: chalk.yellow("Registrando"),
2439
+ PROPAGATING: chalk.yellow("Propagando DNS"),
2440
+ ACTIVE: chalk.green("Ativo"),
2441
+ FAILED: chalk.red("Falhou"),
2442
+ EXPIRED: chalk.red("Expirado")
2443
+ };
2444
+ for (const d of purchased) {
2445
+ const status = statusLabels$1[d.status] ?? d.status;
2446
+ const expires = d.expiresAt ? new Date(d.expiresAt).toLocaleDateString("pt-BR") : "—";
2447
+ console.log(` ${d.id} ${chalk.bold(d.domain.padEnd(30))} ${status} Expira: ${expires}`);
2448
+ }
2449
+ return purchased.map((d) => ({
2450
+ id: d.id,
2451
+ domain: d.domain,
2452
+ status: d.status,
2453
+ expiresAt: d.expiresAt ? new Date(d.expiresAt).toISOString() : null
2454
+ }));
2455
+ }
2456
+ });
2280
2457
  domainsGroup.command("delete", {
2281
2458
  description: "Remover domínio",
2282
2459
  middleware: [requireAuth$1],
@@ -5056,22 +5233,6 @@ githubGroup.command("setup", {
5056
5233
  const s = spinner("Verificando instalação do GitHub App...");
5057
5234
  const installation = await client.github.getInstallation({ owner: remote.owner });
5058
5235
  s.stop();
5059
- if (installation.error === "TOKEN_EXPIRED") {
5060
- warn("Token do GitHub expirado. Faça login novamente no dashboard para reconectar sua conta GitHub.");
5061
- return {
5062
- connected: false,
5063
- installationId: null,
5064
- projectId
5065
- };
5066
- }
5067
- if (installation.error === "RATE_LIMITED") {
5068
- warn("Limite de requisições do GitHub atingido. Aguarde alguns minutos e tente novamente.");
5069
- return {
5070
- connected: false,
5071
- installationId: null,
5072
- projectId
5073
- };
5074
- }
5075
5236
  if (installation.installed && installation.installationId) {
5076
5237
  info(`GitHub App já instalado para ${chalk.bold(remote.owner)}.`);
5077
5238
  await client.projects.connectRepo({
@@ -5134,14 +5295,6 @@ githubGroup.command("setup", {
5134
5295
  installationId = check.installationId;
5135
5296
  break;
5136
5297
  }
5137
- if (check.error === "TOKEN_EXPIRED") {
5138
- pollSpinner.fail("Token do GitHub expirou durante a espera.");
5139
- return {
5140
- connected: false,
5141
- installationId: null,
5142
- projectId
5143
- };
5144
- }
5145
5298
  }
5146
5299
  pollSpinner.stop();
5147
5300
  if (!installationId) {
@@ -5233,28 +5386,37 @@ const statusLabels = {
5233
5386
  BUILDING: "Compilando",
5234
5387
  BUILD_FAILED: "Falha na compilação",
5235
5388
  DEPLOYING: "Realizando deploy",
5389
+ WAITING_ON_PROVIDER: "Provisionando",
5236
5390
  LIVE: "Ativo",
5237
5391
  FAILED: "Falhou",
5238
- CANCELLED: "Cancelado"
5392
+ CANCELLED: "Cancelado",
5393
+ SKIPPED: "Ignorado",
5394
+ SUPERSEDED: "Substituído"
5239
5395
  };
5240
5396
  function makeStatusIcons() {
5241
5397
  if (!process.stdout.isTTY) return {
5242
5398
  QUEUED: "○",
5243
5399
  BUILDING: "●",
5244
5400
  DEPLOYING: "●",
5401
+ WAITING_ON_PROVIDER: "●",
5245
5402
  LIVE: "●",
5246
5403
  BUILD_FAILED: "●",
5247
5404
  FAILED: "●",
5248
- CANCELLED: "●"
5405
+ CANCELLED: "●",
5406
+ SKIPPED: "○",
5407
+ SUPERSEDED: "●"
5249
5408
  };
5250
5409
  return {
5251
5410
  QUEUED: chalk.gray("○"),
5252
5411
  BUILDING: chalk.yellow("●"),
5253
5412
  DEPLOYING: chalk.blue("●"),
5413
+ WAITING_ON_PROVIDER: chalk.blue("●"),
5254
5414
  LIVE: chalk.green("●"),
5255
5415
  BUILD_FAILED: chalk.red("●"),
5256
5416
  FAILED: chalk.red("●"),
5257
- CANCELLED: chalk.gray("●")
5417
+ CANCELLED: chalk.gray("●"),
5418
+ SKIPPED: chalk.gray("○"),
5419
+ SUPERSEDED: chalk.gray("●")
5258
5420
  };
5259
5421
  }
5260
5422
  function getStatusIcon(status) {
@@ -5268,6 +5430,7 @@ const TERMINAL_STATUSES = new Set([
5268
5430
  "BUILD_FAILED",
5269
5431
  "FAILED",
5270
5432
  "CANCELLED",
5433
+ "SKIPPED",
5271
5434
  "SUPERSEDED"
5272
5435
  ]);
5273
5436
 
@@ -5435,7 +5598,7 @@ async function streamDeploymentLogs(deploymentId, serviceId, serviceName) {
5435
5598
  const isTTY = !mcp && process.stdout.isTTY;
5436
5599
  const isGHA = !mcp && process.env.GITHUB_ACTIONS === "true";
5437
5600
  if (isTTY && !isVerbose) {
5438
- const { renderDeployTUI } = await import("./deploy-tui-DnoPRU81.mjs");
5601
+ const { renderDeployTUI } = await import("./deploy-tui-6DaoY351.mjs");
5439
5602
  return renderDeployTUI(deploymentId, serviceId, serviceName);
5440
5603
  }
5441
5604
  const allLogLines = [];
@@ -5476,6 +5639,9 @@ async function streamDeploymentLogs(deploymentId, serviceId, serviceName) {
5476
5639
  } catch {}
5477
5640
  } catch {}
5478
5641
  }
5642
+ if (!TERMINAL_STATUSES.has(finalStatus)) try {
5643
+ finalStatus = (await client.deployments.get({ deploymentId })).status;
5644
+ } catch {}
5479
5645
  if (isGHA) endGroup();
5480
5646
  const urls = finalStatus === "LIVE" ? await fetchDeployUrls$1(client, serviceId) : [];
5481
5647
  if (finalStatus === "LIVE") {
@@ -24221,7 +24387,7 @@ const LOGO_LINES$1 = [
24221
24387
  ];
24222
24388
  const BRAND_COLOR$1 = "#FF4D00";
24223
24389
  function getVersion() {
24224
- return "0.0.0-beta.34";
24390
+ return "0.0.0-beta.36";
24225
24391
  }
24226
24392
  function printBanner(subtitle) {
24227
24393
  const version$2 = getVersion();
@@ -24254,9 +24420,16 @@ function stripAnsi(str) {
24254
24420
  //#endregion
24255
24421
  //#region src/lib/retry.ts
24256
24422
  async function withRetry(fn$2, maxRetries = 3) {
24423
+ let orgSwitched = false;
24257
24424
  for (let attempt = 0; attempt <= maxRetries; attempt++) try {
24258
24425
  return await fn$2();
24259
24426
  } catch (error) {
24427
+ if (!orgSwitched && isOrgMismatchError(error)) {
24428
+ if (applyOrgMismatchSwitch(error)) {
24429
+ orgSwitched = true;
24430
+ continue;
24431
+ }
24432
+ }
24260
24433
  if (attempt >= maxRetries) throw error;
24261
24434
  const rateLimit = isRateLimitError(error);
24262
24435
  if (rateLimit) {
@@ -24310,7 +24483,8 @@ async function deploySingleService(service$2, options) {
24310
24483
  const baseTarball = await createBaseTarball(projectRoot);
24311
24484
  const deployment = await withRetry(() => client.deployments.create({
24312
24485
  serviceId: service$2.serviceId,
24313
- serviceConfig: service$2.serviceConfig
24486
+ serviceConfig: service$2.serviceConfig,
24487
+ config: service$2.config
24314
24488
  }));
24315
24489
  try {
24316
24490
  await withRetry(() => uploadTarball(deployment.id, baseTarball.tarPath));
@@ -24354,7 +24528,8 @@ async function deployMultipleServices(services, options) {
24354
24528
  try {
24355
24529
  const deployment = await withRetry(() => client.deployments.create({
24356
24530
  serviceId: service$2.serviceId,
24357
- serviceConfig: service$2.serviceConfig
24531
+ serviceConfig: service$2.serviceConfig,
24532
+ config: service$2.config
24358
24533
  }));
24359
24534
  await withRetry(() => client.deployments.startBuild({
24360
24535
  deploymentId: deployment.id,
@@ -24415,6 +24590,15 @@ async function deployMultipleServices(services, options) {
24415
24590
  }
24416
24591
  if (isTTY) output.renderProgress(progressMap);
24417
24592
  }
24593
+ const finalProgress = progressMap.get(service$2.serviceId);
24594
+ if (finalProgress && !finalProgress.completed) try {
24595
+ const d = await client.deployments.get({ deploymentId });
24596
+ finalProgress.status = d.status;
24597
+ finalProgress.completed = TERMINAL_STATUSES.has(d.status);
24598
+ finalProgress.success = d.status === "LIVE";
24599
+ if (!isTTY) output.statusUpdate(service$2.serviceName, d.status);
24600
+ else output.renderProgress(progressMap);
24601
+ } catch {}
24418
24602
  } catch (error) {
24419
24603
  const errorMessage = error instanceof Error ? error.message : String(error);
24420
24604
  const progress = progressMap.get(service$2.serviceId);
@@ -25327,7 +25511,7 @@ async function fetchLatestVersion() {
25327
25511
  }
25328
25512
  }
25329
25513
  function getCurrentVersion() {
25330
- return "0.0.0-beta.34";
25514
+ return "0.0.0-beta.36";
25331
25515
  }
25332
25516
  /**
25333
25517
  * Install a specific CLI version. Returns true on success.
@@ -25389,7 +25573,7 @@ async function provisionDatabases(config, opts) {
25389
25573
  const client = await getClient();
25390
25574
  const serverDatabases = await withSpinner({
25391
25575
  text: "Verificando bancos de dados...",
25392
- fn: () => client.databases.list({ projectId })
25576
+ fn: () => withRetry(() => client.databases.list({ projectId }))
25393
25577
  });
25394
25578
  const idUpdates = {};
25395
25579
  for (const [key, dbConfig] of entries) {
@@ -25665,6 +25849,7 @@ async function triggerDeploy(serviceId, serviceName) {
25665
25849
  async function findServicesFromConfig() {
25666
25850
  const config = loadConfig$1();
25667
25851
  if (!config) return [];
25852
+ if (!config.project.id) return [];
25668
25853
  const missingIds = Object.entries(config.services).filter(([_, svc]) => !svc.id);
25669
25854
  if (missingIds.length > 0 && config.project.id) {
25670
25855
  const client = await getClient();
@@ -26470,7 +26655,7 @@ function registerDeploy(cli$1) {
26470
26655
  run(c) {
26471
26656
  const opts = c.options;
26472
26657
  if (opts.dryRun) return dryRunFlow();
26473
- if (isMcpMode()) return mcpDeployFlow(opts);
26658
+ if (isMcpMode()) return headlessDeployFlow(opts);
26474
26659
  return cliDeployFlow(opts);
26475
26660
  }
26476
26661
  });
@@ -26597,7 +26782,14 @@ function buildDraftVelozConfig(input) {
26597
26782
  services: configServices
26598
26783
  };
26599
26784
  }
26600
- async function* mcpDeployFlow(opts) {
26785
+ /**
26786
+ * Non-interactive deploy flow used by both MCP mode and `veloz init --ci`.
26787
+ *
26788
+ * Bootstraps a project + service automatically when no veloz.json exists,
26789
+ * then deploys. Output is routed through `createDeployOutput()` which picks
26790
+ * the right formatter for the current environment (MCP, GHA, plain, TTY).
26791
+ */
26792
+ async function* headlessDeployFlow(opts) {
26601
26793
  migrateResourcesConfig();
26602
26794
  if (isInheritedConfig()) {
26603
26795
  const parentRoot = findProjectRoot();
@@ -30407,7 +30599,7 @@ function PickerMenu({ items, onSelect, onCancel }) {
30407
30599
 
30408
30600
  //#endregion
30409
30601
  //#region src/wizard/ui/primitives/ScreenLayout.tsx
30410
- const version = "0.0.0-beta.34";
30602
+ const version = "0.0.0-beta.36";
30411
30603
  const LOGO_LINES = [
30412
30604
  "██╗ ██╗███████╗██╗ ██████╗ ███████╗",
30413
30605
  "██║ ██║██╔════╝██║ ██╔═══██╗╚══███╔╝",
@@ -34412,7 +34604,7 @@ function registerInit(cli$1) {
34412
34604
  description: "Wizard de deploy — configura e faz deploy do projeto automaticamente",
34413
34605
  options: z.object({
34414
34606
  framework: z.string().optional().describe("Forçar framework específico (ex: nextjs-app-router, express)"),
34415
- ci: z.boolean().default(false).describe("Modo CI (sem TUI interativo)")
34607
+ ci: z.boolean().default(false).describe("Modo CI pula o wizard de IA e faz deploy direto via veloz deploy")
34416
34608
  }),
34417
34609
  alias: {
34418
34610
  framework: "f",
@@ -34466,12 +34658,8 @@ async function runInitFlow(c, logger, cwd) {
34466
34658
  const detectedLabel = analysis.framework?.label ?? null;
34467
34659
  if (!process.stdout.isTTY || c.options.ci) {
34468
34660
  logger.log("mode", "running headless (no TUI)");
34469
- return await runHeadless({
34470
- cwd,
34471
- frameworkId: forcedFramework ?? detectedFrameworkId ?? "nodejs",
34472
- frameworkLabel: forcedFramework ? FRAMEWORK_LABELS[forcedFramework] ?? forcedFramework : detectedLabel ?? "Node.js",
34473
- packageManager: analysis.packageManager ?? "npm"
34474
- });
34661
+ if (forcedFramework) process.stderr.write(`[veloz init] aviso: --framework é ignorado em modo CI. Configure o framework em veloz.json.\n`);
34662
+ return await runHeadless({ cwd });
34475
34663
  }
34476
34664
  const tui = startTUI();
34477
34665
  const { store } = tui;
@@ -34655,14 +34843,6 @@ async function runGithubSetupFlow(store, cwd) {
34655
34843
  return;
34656
34844
  }
34657
34845
  const installation = await client.github.getInstallation({ owner: remote.owner });
34658
- if (installation.error === "TOKEN_EXPIRED") {
34659
- store.failGithubSetup("Token do GitHub expirado. Reconecte sua conta no dashboard e tente novamente.");
34660
- return;
34661
- }
34662
- if (installation.error === "RATE_LIMITED") {
34663
- store.failGithubSetup("Limite de requisições do GitHub atingido. Tente de novo em alguns minutos.");
34664
- return;
34665
- }
34666
34846
  if (installation.installed && installation.installationId) {
34667
34847
  store.setGithubSetupPhase("connecting");
34668
34848
  await client.projects.connectRepo({
@@ -34810,7 +34990,7 @@ function runVelozSubcommand(args$1) {
34810
34990
  * Poll the server for a completed GitHub App installation on `owner`.
34811
34991
  * Returns the installation id when detected, or null on timeout / auth loss.
34812
34992
  */
34813
- async function pollGithubInstallation(store, owner) {
34993
+ async function pollGithubInstallation(_store, owner) {
34814
34994
  const client = await getClient();
34815
34995
  const maxAttempts = 60;
34816
34996
  const pollInterval = 5e3;
@@ -34820,35 +35000,20 @@ async function pollGithubInstallation(store, owner) {
34820
35000
  });
34821
35001
  const check = await client.github.getInstallation({ owner });
34822
35002
  if (check.installed && check.installationId) return check.installationId;
34823
- if (check.error === "TOKEN_EXPIRED") {
34824
- store.failGithubSetup("Token do GitHub expirou durante a espera.");
34825
- return null;
34826
- }
34827
35003
  }
34828
35004
  return null;
34829
35005
  }
35006
+ /**
35007
+ * In CI / non-TTY contexts (real CI runners, MCP tool calls, Claude Code's
35008
+ * bash tool), `veloz init` delegates to the deterministic deploy flow rather
35009
+ * than spawning a Claude Code agent. The deploy flow auto-bootstraps the
35010
+ * project + service when no veloz.json exists, then deploys.
35011
+ */
34830
35012
  async function runHeadless(opts) {
34831
- const { WizardStore: WizardStore$1 } = await import("./store-CFF2J0lW.mjs");
34832
- const { runAgent: run } = await import("./agent-interface-DL5S8SEn.mjs");
34833
35013
  const logger = getSessionLogger();
34834
35014
  logger.log("headless", "resolving auth (non-interactive)");
34835
35015
  const config = await requireAuth({ nonInteractive: true });
34836
- const store = new WizardStore$1();
34837
- store.setAuth({
34838
- userName: "CI",
34839
- orgId: config.organizationId ?? "",
34840
- orgName: "",
34841
- apiKey: config.apiKey
34842
- });
34843
- store.subscribe(() => {
34844
- const s = store.session;
34845
- const lastLog = s.agentLogLines[s.agentLogLines.length - 1];
34846
- if (lastLog) process.stderr.write(`[veloz init] ${lastLog}\n`);
34847
- });
34848
35016
  logger.updateMeta({
34849
- framework: opts.frameworkId,
34850
- frameworkLabel: opts.frameworkLabel,
34851
- packageManager: opts.packageManager,
34852
35017
  userName: "CI",
34853
35018
  orgId: config.organizationId ?? ""
34854
35019
  });
@@ -34861,37 +35026,35 @@ async function runHeadless(opts) {
34861
35026
  };
34862
35027
  process.on("SIGINT", onSigint);
34863
35028
  try {
34864
- logger.log("headless", "invoking runAgent");
34865
- await run({
34866
- store,
34867
- cwd: opts.cwd,
34868
- framework: opts.frameworkId,
34869
- frameworkLabel: opts.frameworkLabel,
34870
- userName: "CI",
34871
- orgName: "",
34872
- packageManager: opts.packageManager
34873
- });
34874
- const success$1 = !store.session.agentError;
34875
- await finalizeAndUploadOnce(logger, success$1 ? "success" : "error", {
34876
- error: store.session.agentError ?? void 0,
34877
- deployUrl: store.session.deployUrl ?? void 0,
34878
- retries: store.session.retryCount
34879
- });
35029
+ logger.log("headless", "invoking headlessDeployFlow", { cwd: opts.cwd });
35030
+ process.stderr.write("[veloz init] modo CI, executando deploy não-interativo...\n");
35031
+ let success$1 = false;
35032
+ let url;
35033
+ for await (const event of headlessDeployFlow({
35034
+ all: true,
35035
+ yes: true
35036
+ })) if (event.type === "result") {
35037
+ const data = event.data;
35038
+ if (data?.status === "LIVE") {
35039
+ success$1 = true;
35040
+ url ??= data.urls?.[0];
35041
+ } else if (data?.failReason) logger.log("headless", "deploy failed", {
35042
+ status: data.status,
35043
+ failReason: data.failReason
35044
+ });
35045
+ }
35046
+ await finalizeAndUploadOnce(logger, success$1 ? "success" : "error", { deployUrl: url });
34880
35047
  return {
34881
35048
  success: success$1,
34882
- url: store.session.deployUrl ?? void 0
35049
+ url
34883
35050
  };
34884
35051
  } catch (err) {
34885
35052
  const { message, stack } = describeError(err);
34886
- logger.log("headless", "runAgent threw", {
35053
+ logger.log("headless", "deploy threw", {
34887
35054
  message,
34888
35055
  stack
34889
35056
  });
34890
- await finalizeAndUploadOnce(logger, "error", {
34891
- error: formatErrorForMeta("headless", err),
34892
- deployUrl: store.session.deployUrl ?? void 0,
34893
- retries: store.session.retryCount
34894
- });
35057
+ await finalizeAndUploadOnce(logger, "error", { error: formatErrorForMeta("headless", err) });
34895
35058
  throw err;
34896
35059
  } finally {
34897
35060
  process.off("SIGINT", onSigint);
@@ -35066,7 +35229,7 @@ async function runOrgCreationFlow(store, apiUrl) {
35066
35229
  //#region src/index.ts
35067
35230
  if (process.argv.includes("--mcp")) process.env.VELOZ_MCP = "true";
35068
35231
  const cli = Cli.create("veloz", {
35069
- version: "0.0.0-beta.34",
35232
+ version: "0.0.0-beta.36",
35070
35233
  description: "CLI da plataforma Veloz — deploy rápido para o Brasil",
35071
35234
  env: z.object({ VELOZ_ENV: z.string().optional().describe("Ambiente alvo (ex: preview, staging)") }),
35072
35235
  mcp: { command: "npx -y onveloz --mcp" }
@@ -35187,4 +35350,4 @@ registerInit(cli);
35187
35350
  cli.serve();
35188
35351
 
35189
35352
  //#endregion
35190
- export { parseBuildLine as a, getClient as c, buildVerificationUrl as d, performLogin as f, success as g, info as h, isHiddenMessage as i, DEVICE_AUTH_CLIENT_ID as l, registerLogin as m, runAgent as n, TERMINAL_STATUSES as o, pollForToken as p, cleanDisplayLine as r, statusLabels as s, WizardStore as t, buildDeviceAuthClient as u };
35353
+ export { statusLabels as a, buildDeviceAuthClient as c, pollForToken as d, registerLogin as f, TERMINAL_STATUSES as i, buildVerificationUrl as l, success as m, isHiddenMessage as n, getClient as o, info as p, parseBuildLine as r, DEVICE_AUTH_CLIENT_ID as s, cleanDisplayLine as t, performLogin as u };
@@ -0,0 +1,3 @@
1
+ import { c as buildDeviceAuthClient, d as pollForToken, f as registerLogin, l as buildVerificationUrl, s as DEVICE_AUTH_CLIENT_ID, u as performLogin } from "./index.mjs";
2
+
3
+ export { performLogin };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "onveloz",
3
- "version": "0.0.0-beta.34",
3
+ "version": "0.0.0-beta.36",
4
4
  "description": "CLI da plataforma Veloz — deploy rápido para o Brasil",
5
5
  "keywords": [
6
6
  "brasil",
@@ -37,7 +37,7 @@
37
37
  "ink": "^6.8.0",
38
38
  "nanostores": "^0.11.3",
39
39
  "ora": "^8.2.0",
40
- "react": "^19.2.4",
40
+ "react": "catalog:",
41
41
  "tar": "^6.2.0",
42
42
  "ws": "^8.20.0",
43
43
  "zod": "^4.1.13"
@@ -1,3 +0,0 @@
1
- import { n as runAgent } from "./index.mjs";
2
-
3
- export { runAgent };
@@ -1,3 +0,0 @@
1
- import { d as buildVerificationUrl, f as performLogin, l as DEVICE_AUTH_CLIENT_ID, m as registerLogin, p as pollForToken, u as buildDeviceAuthClient } from "./index.mjs";
2
-
3
- export { performLogin };
@@ -1,3 +0,0 @@
1
- import { t as WizardStore } from "./index.mjs";
2
-
3
- export { WizardStore };