onveloz 0.0.0-beta.32 → 0.0.0-beta.34

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.
Files changed (2) hide show
  1. package/dist/index.mjs +162 -58
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -222,32 +222,32 @@ const SERVICE_SIZES = {
222
222
  };
223
223
  const SERVICE_SIZE_KEYS = Object.keys(SERVICE_SIZES);
224
224
  const HOURS_PER_MONTH = 720;
225
- /** Service monthly prices in cents. */
225
+ /** Service monthly prices in cents. Magalu Cloud base + 10% margin. */
226
226
  const SERVICE_MONTHLY_PRICES = {
227
- basico: 2500,
228
- essencial: 6e3,
229
- turbo: 13e3,
230
- "turbo-plus": 22e3,
231
- nitro: 4e4,
232
- "nitro-plus": 72e3
227
+ basico: 500,
228
+ essencial: 2e3,
229
+ turbo: 4e3,
230
+ "turbo-plus": 7e3,
231
+ nitro: 9500,
232
+ "nitro-plus": 17e3
233
233
  };
234
- /** Disk-based DB monthly prices in cents (~30% premium over services). */
234
+ /** Disk-based DB monthly prices in cents. Service price + 15% managed premium. */
235
235
  const DATABASE_MONTHLY_PRICES = {
236
- basico: 3e3,
237
- essencial: 7500,
238
- turbo: 17e3,
239
- "turbo-plus": 29e3,
240
- nitro: 52e3,
241
- "nitro-plus": 95e3
236
+ basico: 600,
237
+ essencial: 2300,
238
+ turbo: 4600,
239
+ "turbo-plus": 8e3,
240
+ nitro: 11e3,
241
+ "nitro-plus": 19500
242
242
  };
243
- /** Redis monthly prices in cents. */
243
+ /** Redis monthly prices in cents. Matches disk-DB pricing. */
244
244
  const REDIS_MONTHLY_PRICES = {
245
- basico: 2500,
246
- essencial: 3e3,
247
- turbo: 5e3,
248
- "turbo-plus": 9500,
249
- nitro: 17e3,
250
- "nitro-plus": 29e3
245
+ basico: 600,
246
+ essencial: 2300,
247
+ turbo: 4600,
248
+ "turbo-plus": 8e3,
249
+ nitro: 11e3,
250
+ "nitro-plus": 19500
251
251
  };
252
252
  /** Hourly rates derived from monthly prices (cents per instance per hour). */
253
253
  const SERVICE_HOURLY_RATES = Object.fromEntries(Object.entries(SERVICE_MONTHLY_PRICES).map(([k, v]) => [k, v / HOURS_PER_MONTH]));
@@ -1260,7 +1260,7 @@ async function requireAuth(options) {
1260
1260
 
1261
1261
  //#endregion
1262
1262
  //#region src/lib/client.ts
1263
- const CLI_VERSION = "0.0.0-beta.32";
1263
+ const CLI_VERSION = "0.0.0-beta.34";
1264
1264
  const USER_AGENT = `veloz-cli/${CLI_VERSION}`;
1265
1265
  /**
1266
1266
  * Client source — "cli" by default; the init wizard sets this to "cli-wizard"
@@ -24221,7 +24221,7 @@ const LOGO_LINES$1 = [
24221
24221
  ];
24222
24222
  const BRAND_COLOR$1 = "#FF4D00";
24223
24223
  function getVersion() {
24224
- return "0.0.0-beta.32";
24224
+ return "0.0.0-beta.34";
24225
24225
  }
24226
24226
  function printBanner(subtitle) {
24227
24227
  const version$2 = getVersion();
@@ -25327,7 +25327,7 @@ async function fetchLatestVersion() {
25327
25327
  }
25328
25328
  }
25329
25329
  function getCurrentVersion() {
25330
- return "0.0.0-beta.32";
25330
+ return "0.0.0-beta.34";
25331
25331
  }
25332
25332
  /**
25333
25333
  * Install a specific CLI version. Returns true on success.
@@ -30407,7 +30407,7 @@ function PickerMenu({ items, onSelect, onCancel }) {
30407
30407
 
30408
30408
  //#endregion
30409
30409
  //#region src/wizard/ui/primitives/ScreenLayout.tsx
30410
- const version = "0.0.0-beta.32";
30410
+ const version = "0.0.0-beta.34";
30411
30411
  const LOGO_LINES = [
30412
30412
  "██╗ ██╗███████╗██╗ ██████╗ ███████╗",
30413
30413
  "██║ ██║██╔════╝██║ ██╔═══██╗╚══███╔╝",
@@ -30596,6 +30596,7 @@ function createInitialSession() {
30596
30596
  selectedOrgId: null,
30597
30597
  newOrgName: null,
30598
30598
  orgCreateError: null,
30599
+ reauthNotice: null,
30599
30600
  detectedFramework: null,
30600
30601
  detectedFrameworkLabel: null,
30601
30602
  selectedFramework: null,
@@ -30729,29 +30730,45 @@ function AuthScreen({ store }) {
30729
30730
  })]
30730
30731
  })] });
30731
30732
  }
30732
- if (session.authPhase === "browser" || session.authPhase === "polling") return /* @__PURE__ */ jsxs(ScreenLayout, { children: [/* @__PURE__ */ jsx(Spinner, { label: COPY.authPolling }), session.authDeviceCode ? /* @__PURE__ */ jsxs(Box, {
30733
- marginTop: 1,
30734
- flexDirection: "column",
30735
- gap: 1,
30736
- children: [/* @__PURE__ */ jsxs(Box, {
30737
- gap: 1,
30738
- children: [/* @__PURE__ */ jsx(Text, { children: COPY.authDeviceCode }), /* @__PURE__ */ jsx(Text, {
30739
- bold: true,
30740
- color: BRAND_COLOR,
30741
- children: session.authDeviceCode
30742
- })]
30743
- }), session.authVerificationUrl ? /* @__PURE__ */ jsxs(Box, {
30733
+ if (session.authPhase === "browser" || session.authPhase === "polling") return /* @__PURE__ */ jsxs(ScreenLayout, { children: [
30734
+ session.reauthNotice ? /* @__PURE__ */ jsx(Box, {
30735
+ marginBottom: 1,
30736
+ children: /* @__PURE__ */ jsxs(Text, {
30737
+ color: "yellow",
30738
+ children: ["⚠ ", session.reauthNotice]
30739
+ })
30740
+ }) : null,
30741
+ /* @__PURE__ */ jsx(Spinner, { label: COPY.authPolling }),
30742
+ session.authDeviceCode ? /* @__PURE__ */ jsxs(Box, {
30743
+ marginTop: 1,
30744
30744
  flexDirection: "column",
30745
- children: [/* @__PURE__ */ jsx(Text, {
30746
- dimColor: true,
30747
- children: COPY.authBrowserFallback
30748
- }), /* @__PURE__ */ jsx(Text, {
30749
- dimColor: true,
30750
- children: session.authVerificationUrl
30751
- })]
30752
- }) : null]
30753
- }) : null] });
30754
- return /* @__PURE__ */ jsx(ScreenLayout, { children: /* @__PURE__ */ jsx(Spinner, { label: COPY.authChecking }) });
30745
+ gap: 1,
30746
+ children: [/* @__PURE__ */ jsxs(Box, {
30747
+ gap: 1,
30748
+ children: [/* @__PURE__ */ jsx(Text, { children: COPY.authDeviceCode }), /* @__PURE__ */ jsx(Text, {
30749
+ bold: true,
30750
+ color: BRAND_COLOR,
30751
+ children: session.authDeviceCode
30752
+ })]
30753
+ }), session.authVerificationUrl ? /* @__PURE__ */ jsxs(Box, {
30754
+ flexDirection: "column",
30755
+ children: [/* @__PURE__ */ jsx(Text, {
30756
+ dimColor: true,
30757
+ children: COPY.authBrowserFallback
30758
+ }), /* @__PURE__ */ jsx(Text, {
30759
+ dimColor: true,
30760
+ children: session.authVerificationUrl
30761
+ })]
30762
+ }) : null]
30763
+ }) : null
30764
+ ] });
30765
+ return /* @__PURE__ */ jsxs(ScreenLayout, { children: [session.reauthNotice ? /* @__PURE__ */ jsx(Box, {
30766
+ marginBottom: 1,
30767
+ children: /* @__PURE__ */ jsxs(Text, {
30768
+ color: "yellow",
30769
+ children: ["⚠ ", session.reauthNotice]
30770
+ })
30771
+ }) : null, /* @__PURE__ */ jsx(Spinner, { label: COPY.authChecking })] });
30755
30772
  }
30756
30773
  function OrgCreateForm({ store }) {
30757
30774
  const [value, setValue] = useState("");
@@ -33884,6 +33901,15 @@ var WizardStore = class {
33884
33901
  this.$session.setKey("authPhase", "browser");
33885
33902
  this.emitChange();
33886
33903
  }
33904
+ /** Show a banner on AuthScreen explaining why we're re-running device auth. */
33905
+ setReauthNotice(message) {
33906
+ this.$session.setKey("reauthNotice", message);
33907
+ this.emitChange();
33908
+ }
33909
+ clearReauthNotice() {
33910
+ this.$session.setKey("reauthNotice", null);
33911
+ this.emitChange();
33912
+ }
33887
33913
  setAvailableOrgs(orgs) {
33888
33914
  this.$session.setKey("availableOrgs", orgs);
33889
33915
  this.$session.setKey("authPhase", "org-select");
@@ -34157,7 +34183,6 @@ async function uploadInitTelemetry(sessionDir) {
34157
34183
  return;
34158
34184
  }
34159
34185
  const authConfig = loadConfig();
34160
- if (!authConfig.apiKey) return;
34161
34186
  const files = readdirSync(sessionDir).filter((name) => {
34162
34187
  const full = join(sessionDir, name);
34163
34188
  try {
@@ -34170,9 +34195,10 @@ async function uploadInitTelemetry(sessionDir) {
34170
34195
  if (files.length === 0) return;
34171
34196
  const client = createClient(authConfig.apiUrl, () => {
34172
34197
  const headers = {
34173
- Authorization: `Bearer ${authConfig.apiKey}`,
34174
- "User-Agent": "veloz-cli/telemetry"
34198
+ "User-Agent": "veloz-cli/telemetry",
34199
+ "X-Veloz-Client-Source": "cli-wizard"
34175
34200
  };
34201
+ if (authConfig.apiKey) headers.Authorization = `Bearer ${authConfig.apiKey}`;
34176
34202
  if (authConfig.organizationId) headers["X-Organization-Id"] = authConfig.organizationId;
34177
34203
  return headers;
34178
34204
  });
@@ -34263,7 +34289,7 @@ function startTUI(cwd) {
34263
34289
  }));
34264
34290
  teardown = unmount;
34265
34291
  const cleanup = () => {
34266
- if (process.stdout.isTTY) process.stdout.write("\x1B[0m\x1B[2J\x1B[H");
34292
+ if (process.stdout.isTTY) process.stdout.write("\x1B[0m");
34267
34293
  };
34268
34294
  process.on("exit", cleanup);
34269
34295
  let sigintCount = 0;
@@ -34304,16 +34330,35 @@ function describeError(err) {
34304
34330
  };
34305
34331
  return { message: typeof err === "string" ? err : JSON.stringify(err) };
34306
34332
  }
34333
+ /**
34334
+ * Detect an ORPC / HTTP 401 from a thrown value. Covers the three shapes we see
34335
+ * in practice: ORPCError instances (`.code === "UNAUTHORIZED"`), ORPC fetch
34336
+ * failures that preserve `.status === 401`, and raw Error messages containing
34337
+ * "Unauthorized".
34338
+ */
34339
+ function isUnauthorizedError(err) {
34340
+ if (!err || typeof err !== "object") return false;
34341
+ const e = err;
34342
+ if (e.code === "UNAUTHORIZED") return true;
34343
+ if (e.status === 401) return true;
34344
+ if (typeof e.message === "string" && /unauthorized/i.test(e.message)) return true;
34345
+ return false;
34346
+ }
34307
34347
  /** Compose the error string we persist on `meta.error`. */
34308
34348
  function formatErrorForMeta(prefix, err) {
34309
34349
  const { message, stack } = describeError(err);
34310
34350
  return stack ? `${prefix}: ${message}\n${stack}` : `${prefix}: ${message}`;
34311
34351
  }
34312
- /**
34313
- * Install process-level safety nets so a stray throw / rejection still flushes
34314
- * telemetry before the CLI dies. Returns a disposer that removes the handlers
34315
- * (so we don't pollute long-running parent processes in tests).
34316
- */
34352
+ const TERMINAL_SIGNALS = [
34353
+ "SIGHUP",
34354
+ "SIGTERM",
34355
+ "SIGQUIT"
34356
+ ];
34357
+ const SIGNAL_EXIT_CODES = {
34358
+ SIGHUP: 129,
34359
+ SIGTERM: 143,
34360
+ SIGQUIT: 131
34361
+ };
34317
34362
  function installCrashHandlers(logger) {
34318
34363
  const handleCrash = (kind, err) => {
34319
34364
  const { message, stack } = describeError(err);
@@ -34324,13 +34369,23 @@ function installCrashHandlers(logger) {
34324
34369
  process.stderr.write(`[veloz init] ${kind}: ${message}\n`);
34325
34370
  finalizeAndUploadOnce(logger, "error", { error: formatErrorForMeta(kind, err) }).catch(() => {}).finally(() => process.exit(1));
34326
34371
  };
34372
+ const handleSignal = (signal) => {
34373
+ logger.log("signal", `${signal} received`);
34374
+ finalizeAndUploadOnce(logger, "abort", { error: `signal ${signal}` }).catch(() => {}).finally(() => process.exit(SIGNAL_EXIT_CODES[signal]));
34375
+ };
34327
34376
  const onUncaught = (err) => handleCrash("uncaughtException", err);
34328
34377
  const onUnhandled = (reason) => handleCrash("unhandledRejection", reason);
34378
+ const signalHandlers = TERMINAL_SIGNALS.map((signal) => {
34379
+ const handler = () => handleSignal(signal);
34380
+ process.on(signal, handler);
34381
+ return [signal, handler];
34382
+ });
34329
34383
  process.on("uncaughtException", onUncaught);
34330
34384
  process.on("unhandledRejection", onUnhandled);
34331
34385
  return () => {
34332
34386
  process.off("uncaughtException", onUncaught);
34333
34387
  process.off("unhandledRejection", onUnhandled);
34388
+ for (const [signal, handler] of signalHandlers) process.off(signal, handler);
34334
34389
  };
34335
34390
  }
34336
34391
  /** Detect repo using the lazy local filesystem layer. */
@@ -34421,6 +34476,29 @@ async function runInitFlow(c, logger, cwd) {
34421
34476
  const tui = startTUI();
34422
34477
  const { store } = tui;
34423
34478
  logger.log("tui", "TUI started");
34479
+ try {
34480
+ return await runInteractiveFlow({
34481
+ tui,
34482
+ store,
34483
+ logger,
34484
+ cwd,
34485
+ analysis,
34486
+ detectedFrameworkId,
34487
+ detectedLabel,
34488
+ forcedFramework
34489
+ });
34490
+ } catch (err) {
34491
+ try {
34492
+ tui.unmount();
34493
+ } catch {}
34494
+ const { message } = describeError(err);
34495
+ process.stderr.write(`\n✗ Erro: ${message}\n`);
34496
+ process.stderr.write(` Logs de depuração: ${logger.dir}\n\n`);
34497
+ throw err;
34498
+ }
34499
+ }
34500
+ async function runInteractiveFlow(opts) {
34501
+ const { tui, store, logger, cwd, analysis, detectedFrameworkId, detectedLabel, forcedFramework } = opts;
34424
34502
  store.setDetection({
34425
34503
  framework: detectedFrameworkId,
34426
34504
  frameworkLabel: detectedLabel,
@@ -34842,6 +34920,31 @@ async function listOrganizations() {
34842
34920
  }));
34843
34921
  }
34844
34922
  /**
34923
+ * Fetch orgs, handling an expired local API key by transparently re-running
34924
+ * the device auth flow inside the TUI. The user sees: "Verificando autenticação…"
34925
+ * → "Sessão expirou, autenticando novamente…" → device-code screen →
34926
+ * org picker, without ever bailing back to the shell.
34927
+ */
34928
+ async function listOrgsOrReauth(store) {
34929
+ const logger = getSessionLogger();
34930
+ try {
34931
+ return await listOrganizations();
34932
+ } catch (err) {
34933
+ if (!isUnauthorizedError(err)) throw err;
34934
+ logger.log("auth", "stale api key detected (401) — forcing re-login", { message: describeError(err).message });
34935
+ store.setReauthNotice("Sua sessão expirou. Vamos autenticar novamente para continuar.");
34936
+ saveConfig({ apiKey: "" });
34937
+ const config = loadConfig();
34938
+ store.setAuthPhase("browser");
34939
+ const token = await performDeviceAuth(store, config.apiUrl);
34940
+ if (!token) throw new Error("Autenticação cancelada ou expirada.");
34941
+ logger.log("auth", "re-login succeeded — retrying org fetch");
34942
+ saveConfig({ apiKey: token });
34943
+ store.clearReauthNotice();
34944
+ return await listOrganizations();
34945
+ }
34946
+ }
34947
+ /**
34845
34948
  * Create a new organization via Better Auth. The slug is derived from the name
34846
34949
  * and suffixed with a random token when a collision occurs.
34847
34950
  */
@@ -34910,8 +35013,9 @@ async function resolveAuthAndOrg(store) {
34910
35013
  }
34911
35014
  } else logger.log("auth", "already authenticated from config");
34912
35015
  logger.log("auth", "fetching organizations");
34913
- const orgs = await listOrganizations();
35016
+ const orgs = await listOrgsOrReauth(store);
34914
35017
  logger.log("auth", "organizations loaded", { count: orgs.length });
35018
+ config = loadConfig();
34915
35019
  let chosen;
34916
35020
  if (orgs.length === 0) {
34917
35021
  store.setAuthPhase("org-create");
@@ -34962,7 +35066,7 @@ async function runOrgCreationFlow(store, apiUrl) {
34962
35066
  //#region src/index.ts
34963
35067
  if (process.argv.includes("--mcp")) process.env.VELOZ_MCP = "true";
34964
35068
  const cli = Cli.create("veloz", {
34965
- version: "0.0.0-beta.32",
35069
+ version: "0.0.0-beta.34",
34966
35070
  description: "CLI da plataforma Veloz — deploy rápido para o Brasil",
34967
35071
  env: z.object({ VELOZ_ENV: z.string().optional().describe("Ambiente alvo (ex: preview, staging)") }),
34968
35072
  mcp: { command: "npx -y onveloz --mcp" }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "onveloz",
3
- "version": "0.0.0-beta.32",
3
+ "version": "0.0.0-beta.34",
4
4
  "description": "CLI da plataforma Veloz — deploy rápido para o Brasil",
5
5
  "keywords": [
6
6
  "brasil",