@videan/cli 0.4.0 → 0.4.2

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/index.js CHANGED
@@ -5,8 +5,8 @@ import { Command as Command18 } from "commander";
5
5
 
6
6
  // src/commands/login.ts
7
7
  import { Command } from "commander";
8
- import inquirer from "inquirer";
9
- import chalk from "chalk";
8
+ import inquirer2 from "inquirer";
9
+ import chalk2 from "chalk";
10
10
  import ora from "ora";
11
11
  import os from "node:os";
12
12
 
@@ -733,7 +733,11 @@ var PLANS2 = {
733
733
  includedUsageCents: 300,
734
734
  maxApps: 3,
735
735
  maxInstancesPerApp: 1,
736
- // One app at this size, running all month, is inside the included usage.
736
+ // A *ceiling*, not a promise of free compute: a container task at even
737
+ // 256/512 running all month is ~$16 of usage against the $3 allowance.
738
+ // That is why apps created on this plan default to serverless mode, which
739
+ // meters nothing while idle -- the free tier is "free while it sleeps",
740
+ // and a container app on this plan is a deliberate, metered choice.
737
741
  maxCpu: 512,
738
742
  maxMemory: 1024,
739
743
  maxStorageMb: 1024,
@@ -830,12 +834,94 @@ function clearConfig() {
830
834
  config.clear();
831
835
  }
832
836
 
837
+ // src/prompt.ts
838
+ import inquirer from "inquirer";
839
+ import chalk from "chalk";
840
+ async function prompt(questions, nonInteractiveHint) {
841
+ if (!process.stdin.isTTY) {
842
+ console.error(
843
+ chalk.red(`This command needs a terminal to prompt. ${nonInteractiveHint}`)
844
+ );
845
+ process.exit(1);
846
+ }
847
+ try {
848
+ return await inquirer.prompt(questions);
849
+ } catch (err) {
850
+ if (isStreamClosed(err)) {
851
+ console.error(chalk.red(`Input ended before the prompt was answered. ${nonInteractiveHint}`));
852
+ process.exit(1);
853
+ }
854
+ throw err;
855
+ }
856
+ }
857
+ function isStreamClosed(err) {
858
+ const e = err;
859
+ return e?.code === "ERR_USE_AFTER_CLOSE" || /closed/i.test(e?.message ?? "");
860
+ }
861
+ function installPromptCrashGuard() {
862
+ process.on("uncaughtException", (err) => {
863
+ if (isStreamClosed(err)) {
864
+ console.error(
865
+ chalk.red(
866
+ "Input stream closed mid-prompt. Re-run with the answers as flags \u2014 every prompt has one (see --help)."
867
+ )
868
+ );
869
+ process.exit(1);
870
+ }
871
+ throw err;
872
+ });
873
+ }
874
+
833
875
  // src/commands/login.ts
876
+ async function completeTwoFactor(spinner, baseUrl, login) {
877
+ if (!process.stdin.isTTY) {
878
+ spinner.fail(
879
+ "This account has two-factor enabled: login needs a terminal to type the emailed code. Non-interactively, set VIDEAN_API_KEY instead (create a key from a terminal or the dashboard)."
880
+ );
881
+ process.exit(1);
882
+ }
883
+ spinner.stop();
884
+ console.log(
885
+ login.code_email_sent === false ? chalk2.yellow("Two-factor is on, but the code email could not be sent. Trying anyway \u2014 check your inbox.") : "A 6-digit sign-in code was emailed to you (it expires in 10 minutes)."
886
+ );
887
+ for (let attempt = 0; attempt < 4; attempt++) {
888
+ const { code } = await prompt(
889
+ [
890
+ {
891
+ type: "input",
892
+ name: "code",
893
+ message: "Sign-in code:",
894
+ validate: (v) => /^\d{6}$/.test(v.trim()) ? true : "The code is 6 digits"
895
+ }
896
+ ],
897
+ "Two-factor login cannot be completed non-interactively; set VIDEAN_API_KEY instead."
898
+ );
899
+ let response;
900
+ try {
901
+ response = await fetch(`${baseUrl}/auth/login/2fa`, {
902
+ method: "POST",
903
+ headers: { "Content-Type": "application/json" },
904
+ body: JSON.stringify({ challenge: login.challenge, code: code.trim() })
905
+ });
906
+ } catch (e) {
907
+ spinner.fail(`Login failed: could not reach ${baseUrl} (${e?.message || e})`);
908
+ process.exit(1);
909
+ }
910
+ if (response.ok) {
911
+ const data = await response.json();
912
+ return data.data.token;
913
+ }
914
+ const body = await response.json().catch(() => null);
915
+ console.error(chalk2.red(body?.error?.message || "Invalid or expired sign-in code"));
916
+ }
917
+ spinner.fail("Too many wrong codes. Run `videan login` again to get a new one.");
918
+ process.exit(1);
919
+ }
834
920
  var loginCommand = new Command("login").description("Log in to Cloud").option("-e, --email <email>", "Email (skips the prompt)").option("-p, --password <password>", "Password (skips the prompt; prefer VIDEAN_PASSWORD)").option("-o, --org <id>", "Organization to scope the key to (skips the picker)").option("--api-url <url>", "API base URL to use and remember (default: the public API)").action(async (opts) => {
835
921
  const password = opts.password || process.env.VIDEAN_PASSWORD || process.env.CLOUD_PASSWORD;
836
922
  if ((!opts.email || !password) && !process.stdin.isTTY) {
837
923
  console.error(
838
- chalk.red(
924
+ chalk2.red(
839
925
  "login needs a terminal to prompt. Non-interactively, pass --email and --password (or VIDEAN_PASSWORD), or skip login entirely by setting VIDEAN_API_KEY."
840
926
  )
841
927
  );
@@ -848,12 +934,13 @@ var loginCommand = new Command("login").description("Log in to Cloud").option("-
848
934
  const answers = {
849
935
  email: opts.email,
850
936
  password,
851
- ...prompts.length ? await inquirer.prompt(prompts) : {}
937
+ ...prompts.length ? await inquirer2.prompt(prompts) : {}
852
938
  };
853
939
  if (opts.apiUrl) setBaseUrl(opts.apiUrl.replace(/\/$/, ""));
854
940
  const baseUrl = (getBaseUrl() || DEFAULT_API_URL2).replace(/\/$/, "");
855
941
  const spinner = ora("Logging in...").start();
856
942
  let token;
943
+ let login;
857
944
  try {
858
945
  const response = await fetch(`${baseUrl}/auth/login`, {
859
946
  method: "POST",
@@ -866,17 +953,22 @@ var loginCommand = new Command("login").description("Log in to Cloud").option("-
866
953
  } else {
867
954
  spinner.fail(`Login failed: ${response.status} from ${baseUrl}/auth/login`);
868
955
  console.error(
869
- chalk.gray(" Wrong API URL? Re-run with --api-url <url>, or `videan logout` to reset.")
956
+ chalk2.gray(" Wrong API URL? Re-run with --api-url <url>, or `videan logout` to reset.")
870
957
  );
871
958
  }
872
959
  process.exit(1);
873
960
  }
874
961
  const data = await response.json();
875
- token = data.data.token;
962
+ login = data.data;
876
963
  } catch (e) {
877
964
  spinner.fail(`Login failed: could not reach ${baseUrl} (${e?.message || e})`);
878
965
  process.exit(1);
879
966
  }
967
+ if (login.requires_2fa) {
968
+ token = await completeTwoFactor(spinner, baseUrl, login);
969
+ } else {
970
+ token = login.token;
971
+ }
880
972
  const session = new CloudClient({ apiKey: token, baseUrl });
881
973
  let orgId;
882
974
  try {
@@ -895,7 +987,7 @@ var loginCommand = new Command("login").description("Log in to Cloud").option("-
895
987
  process.exit(1);
896
988
  } else if (orgs.length > 1) {
897
989
  spinner.stop();
898
- const pick = await inquirer.prompt([
990
+ const pick = await inquirer2.prompt([
899
991
  {
900
992
  type: "list",
901
993
  name: "orgId",
@@ -915,7 +1007,7 @@ var loginCommand = new Command("login").description("Log in to Cloud").option("-
915
1007
  if (!orgId) {
916
1008
  setApiKey(token);
917
1009
  spinner.warn(
918
- chalk.yellow("Logged in, but you have no organization yet. Run `videan orgs create <name>`.")
1010
+ chalk2.yellow("Logged in, but you have no organization yet. Run `videan orgs create <name>`.")
919
1011
  );
920
1012
  return;
921
1013
  }
@@ -926,32 +1018,32 @@ var loginCommand = new Command("login").description("Log in to Cloud").option("-
926
1018
  });
927
1019
  setApiKey(key.key);
928
1020
  setDefaultOrg(orgId);
929
- spinner.succeed(chalk.green("Logged in."));
930
- console.log(chalk.gray(` Org: ${orgId}`));
931
- console.log(chalk.gray(` Key: ${key.keyPrefix}\u2026 (revoke with \`videan keys revoke ${key.id}\`)`));
1021
+ spinner.succeed(chalk2.green("Logged in."));
1022
+ console.log(chalk2.gray(` Org: ${orgId}`));
1023
+ console.log(chalk2.gray(` Key: ${key.keyPrefix}\u2026 (revoke with \`videan keys revoke ${key.id}\`)`));
932
1024
  } catch (e) {
933
1025
  setApiKey(token);
934
1026
  setDefaultOrg(orgId);
935
1027
  spinner.warn(
936
- chalk.yellow(`Logged in with a temporary session (key creation failed: ${e?.message}).`)
1028
+ chalk2.yellow(`Logged in with a temporary session (key creation failed: ${e?.message}).`)
937
1029
  );
938
1030
  }
939
1031
  });
940
1032
 
941
1033
  // src/commands/logout.ts
942
1034
  import { Command as Command2 } from "commander";
943
- import chalk2 from "chalk";
1035
+ import chalk3 from "chalk";
944
1036
  var logoutCommand = new Command2("logout").description("Forget the saved key, org, app and API URL").action(() => {
945
1037
  const where = configPath();
946
1038
  clearConfig();
947
- console.log(chalk2.green("Logged out."));
948
- console.log(chalk2.gray(` Cleared ${where}`));
1039
+ console.log(chalk3.green("Logged out."));
1040
+ console.log(chalk3.gray(` Cleared ${where}`));
949
1041
  });
950
1042
 
951
1043
  // src/commands/init.ts
952
1044
  import { Command as Command3 } from "commander";
953
- import inquirer2 from "inquirer";
954
- import chalk3 from "chalk";
1045
+ import inquirer3 from "inquirer";
1046
+ import chalk4 from "chalk";
955
1047
  import fs2 from "fs-extra";
956
1048
  import path2 from "node:path";
957
1049
 
@@ -1018,20 +1110,20 @@ var initCommand = new Command3("init").description("Initialize a new Cloud app i
1018
1110
  const interactive = process.stdin.isTTY && !opts.yes;
1019
1111
  if (await fs2.pathExists(configPath2)) {
1020
1112
  if (!interactive) {
1021
- console.log(chalk3.yellow(`${CONFIG_FILE_NAME} already exists; leaving it alone.`));
1113
+ console.log(chalk4.yellow(`${CONFIG_FILE_NAME} already exists; leaving it alone.`));
1022
1114
  } else {
1023
- console.log(chalk3.yellow(`${CONFIG_FILE_NAME} already exists in this directory.`));
1024
- const { overwrite } = await inquirer2.prompt([
1115
+ console.log(chalk4.yellow(`${CONFIG_FILE_NAME} already exists in this directory.`));
1116
+ const { overwrite } = await inquirer3.prompt([
1025
1117
  { type: "confirm", name: "overwrite", message: "Overwrite?", default: false }
1026
1118
  ]);
1027
1119
  if (!overwrite) {
1028
- for (const f of await writeVideanMd(process.cwd())) console.log(chalk3.green(`Wrote ${f}`));
1120
+ for (const f of await writeVideanMd(process.cwd())) console.log(chalk4.green(`Wrote ${f}`));
1029
1121
  return;
1030
1122
  }
1031
1123
  }
1032
1124
  }
1033
1125
  if (!await fs2.pathExists(configPath2) || interactive) {
1034
- const answers = interactive ? await inquirer2.prompt([
1126
+ const answers = interactive ? await inquirer3.prompt([
1035
1127
  { type: "input", name: "name", message: "App name:", default: opts.name || path2.basename(process.cwd()) },
1036
1128
  {
1037
1129
  type: "list",
@@ -1052,21 +1144,21 @@ var initCommand = new Command3("init").description("Initialize a new Cloud app i
1052
1144
  ""
1053
1145
  ].join("\n");
1054
1146
  await fs2.writeFile(configPath2, config2, "utf-8");
1055
- console.log(chalk3.green(`Created ${CONFIG_FILE_NAME}`));
1147
+ console.log(chalk4.green(`Created ${CONFIG_FILE_NAME}`));
1056
1148
  }
1057
- for (const f of await writeVideanMd(process.cwd())) console.log(chalk3.green(`Wrote ${f}`));
1149
+ for (const f of await writeVideanMd(process.cwd())) console.log(chalk4.green(`Wrote ${f}`));
1058
1150
  });
1059
1151
 
1060
1152
  // src/commands/deploy.ts
1061
1153
  import { Command as Command4 } from "commander";
1062
- import chalk5 from "chalk";
1154
+ import chalk6 from "chalk";
1063
1155
  import ora2 from "ora";
1064
1156
 
1065
1157
  // src/util.ts
1066
1158
  import fs3 from "fs-extra";
1067
1159
  import path3 from "node:path";
1068
1160
  import ignore from "ignore";
1069
- import chalk4 from "chalk";
1161
+ import chalk5 from "chalk";
1070
1162
  var MAX_FILE_BYTES = 10 * 1024 * 1024;
1071
1163
  async function collectFiles(dir) {
1072
1164
  const ig = ignore();
@@ -1127,15 +1219,15 @@ async function collectFiles(dir) {
1127
1219
  function formatStatus(status) {
1128
1220
  switch (status) {
1129
1221
  case "running":
1130
- return chalk4.green(status);
1222
+ return chalk5.green(status);
1131
1223
  case "building":
1132
1224
  case "deploying":
1133
- return chalk4.yellow(status);
1225
+ return chalk5.yellow(status);
1134
1226
  case "failed":
1135
1227
  case "crashed":
1136
- return chalk4.red(status);
1228
+ return chalk5.red(status);
1137
1229
  case "stopped":
1138
- return chalk4.gray(status);
1230
+ return chalk5.gray(status);
1139
1231
  default:
1140
1232
  return status;
1141
1233
  }
@@ -1145,13 +1237,13 @@ function formatRow(columns, widths) {
1145
1237
  }
1146
1238
  function requireApiKey(apiKey) {
1147
1239
  if (!apiKey) {
1148
- console.error(chalk4.red("Not logged in. Run `videan login` first."));
1240
+ console.error(chalk5.red("Not logged in. Run `videan login` first."));
1149
1241
  process.exit(1);
1150
1242
  }
1151
1243
  }
1152
1244
  function requireOrg(orgId) {
1153
1245
  if (!orgId) {
1154
- console.error(chalk4.red("No organization. Pass --org <id> or run `videan login` again."));
1246
+ console.error(chalk5.red("No organization. Pass --org <id> or run `videan login` again."));
1155
1247
  process.exit(1);
1156
1248
  }
1157
1249
  return orgId;
@@ -1163,7 +1255,7 @@ var deployCommand = new Command4("deploy").description("Deploy the current direc
1163
1255
  requireApiKey(apiKey);
1164
1256
  const appId = opts.app || getCurrentApp();
1165
1257
  if (!appId) {
1166
- console.error(chalk5.red("No app specified. Use --app or run `videan init` first."));
1258
+ console.error(chalk6.red("No app specified. Use --app or run `videan init` first."));
1167
1259
  process.exit(1);
1168
1260
  }
1169
1261
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
@@ -1172,19 +1264,19 @@ var deployCommand = new Command4("deploy").description("Deploy the current direc
1172
1264
  const binaryCount = Object.keys(binaryFiles).length;
1173
1265
  const fileCount = Object.keys(files).length + binaryCount;
1174
1266
  if (!fileCount) {
1175
- spinner.fail(chalk5.red(`Nothing to deploy: no files under ${process.cwd()}.`));
1267
+ spinner.fail(chalk6.red(`Nothing to deploy: no files under ${process.cwd()}.`));
1176
1268
  process.exit(1);
1177
1269
  }
1178
1270
  if (skipped.length) {
1179
- spinner.warn(chalk5.yellow(`Skipped ${skipped.length} file(s):`));
1180
- for (const file of skipped) console.log(chalk5.gray(` ${file.path} \u2014 ${file.reason}`));
1271
+ spinner.warn(chalk6.yellow(`Skipped ${skipped.length} file(s):`));
1272
+ for (const file of skipped) console.log(chalk6.gray(` ${file.path} \u2014 ${file.reason}`));
1181
1273
  spinner.start();
1182
1274
  }
1183
1275
  spinner.text = `Pushing ${fileCount} files${binaryCount ? ` (${binaryCount} binary)` : ""}...`;
1184
1276
  const snapshot = await client4.apps.push(appId, files, opts.message, binaryFiles);
1185
1277
  spinner.text = "Triggering deployment...";
1186
1278
  if (opts.env && opts.env !== "preview" && opts.env !== "production") {
1187
- spinner.fail(chalk5.red(`--env must be preview or production, not ${opts.env}`));
1279
+ spinner.fail(chalk6.red(`--env must be preview or production, not ${opts.env}`));
1188
1280
  process.exit(1);
1189
1281
  }
1190
1282
  const deployment = await client4.apps.deploy(appId, snapshot.id, opts.env);
@@ -1202,34 +1294,34 @@ var deployCommand = new Command4("deploy").description("Deploy the current direc
1202
1294
  } catch (e) {
1203
1295
  lastError = e?.message || String(e);
1204
1296
  if (++consecutiveErrors >= 5) {
1205
- spinner.fail(chalk5.red(`Lost track of the deployment: ${lastError}`));
1206
- console.log(chalk5.gray(` It may still finish. Check with \`videan status\`.`));
1297
+ spinner.fail(chalk6.red(`Lost track of the deployment: ${lastError}`));
1298
+ console.log(chalk6.gray(` It may still finish. Check with \`videan status\`.`));
1207
1299
  process.exit(1);
1208
1300
  }
1209
1301
  }
1210
1302
  }
1211
1303
  if (current.status === "running") {
1212
- spinner.succeed(chalk5.green(`Deployed successfully!`));
1304
+ spinner.succeed(chalk6.green(`Deployed successfully!`));
1213
1305
  try {
1214
1306
  const app = await client4.apps.get(appId);
1215
1307
  const gated = app.visibility ? app.visibility !== "public" : app.authRequired !== false;
1216
1308
  console.log(
1217
- gated ? chalk5.gray(
1309
+ gated ? chalk6.gray(
1218
1310
  ` Access: ${app.visibility || "org"} \u2014 visitors sign in once at the edge; your app receives X-User-Id / X-User-Email on every request. Don't build a login page.`
1219
- ) : chalk5.gray(` Access: public \u2014 reachable by anyone; bring your own auth if it needs any.`)
1311
+ ) : chalk6.gray(` Access: public \u2014 reachable by anyone; bring your own auth if it needs any.`)
1220
1312
  );
1221
1313
  } catch {
1222
1314
  }
1223
1315
  if (current.url) {
1224
1316
  const url = /^https?:\/\//.test(current.url) ? current.url : `https://${current.url}`;
1225
- console.log(chalk5.cyan(` URL: ${url}`));
1317
+ console.log(chalk6.cyan(` URL: ${url}`));
1226
1318
  }
1227
1319
  if (current.previewUrl) {
1228
- console.log(chalk5.gray(` This version: ${current.previewUrl}`));
1320
+ console.log(chalk6.gray(` This version: ${current.previewUrl}`));
1229
1321
  }
1230
1322
  } else if (current.status === "failed" || current.status === "crashed") {
1231
- spinner.fail(chalk5.red(`Deployment ${current.status}: ${current.error || "unknown error"}`));
1232
- console.log(chalk5.gray(` Logs: videan logs --app ${appId}`));
1323
+ spinner.fail(chalk6.red(`Deployment ${current.status}: ${current.error || "unknown error"}`));
1324
+ console.log(chalk6.gray(` Logs: videan logs --app ${appId}`));
1233
1325
  process.exit(1);
1234
1326
  } else {
1235
1327
  spinner.info(`Deployment status: ${formatStatus(current.status)}`);
@@ -1239,7 +1331,7 @@ var deployCommand = new Command4("deploy").description("Deploy the current direc
1239
1331
 
1240
1332
  // src/commands/pull.ts
1241
1333
  import { Command as Command5 } from "commander";
1242
- import chalk6 from "chalk";
1334
+ import chalk7 from "chalk";
1243
1335
  import ora3 from "ora";
1244
1336
  import fs4 from "fs-extra";
1245
1337
  import path4 from "node:path";
@@ -1263,14 +1355,14 @@ var pullCommand = new Command5("pull").description("Download a snapshot's files
1263
1355
  if (!snapshotId) {
1264
1356
  const appId = opts.app || getCurrentApp();
1265
1357
  if (!appId) {
1266
- spinner.fail(chalk6.red("No app specified. Use --app, VIDEAN_APP_ID, or name a snapshot id."));
1358
+ spinner.fail(chalk7.red("No app specified. Use --app, VIDEAN_APP_ID, or name a snapshot id."));
1267
1359
  process.exit(1);
1268
1360
  }
1269
1361
  const [latest] = await get(
1270
1362
  `/v1/apps/${appId}/snapshots?limit=1`
1271
1363
  );
1272
1364
  if (!latest) {
1273
- spinner.fail(chalk6.red("This app has no snapshots yet."));
1365
+ spinner.fail(chalk7.red("This app has no snapshots yet."));
1274
1366
  process.exit(1);
1275
1367
  }
1276
1368
  snapshotId = latest.id;
@@ -1282,7 +1374,7 @@ var pullCommand = new Command5("pull").description("Download a snapshot's files
1282
1374
  await fs4.ensureDir(outDir);
1283
1375
  const existing = await fs4.readdir(outDir);
1284
1376
  if (existing.length > 0 && !opts.force) {
1285
- spinner.fail(chalk6.red(`${outDir} is not empty. Pass --force to write into it anyway.`));
1377
+ spinner.fail(chalk7.red(`${outDir} is not empty. Pass --force to write into it anyway.`));
1286
1378
  process.exit(1);
1287
1379
  }
1288
1380
  spinner.text = `Pulling ${detail.manifest.length} files...`;
@@ -1302,29 +1394,29 @@ var pullCommand = new Command5("pull").description("Download a snapshot's files
1302
1394
  spinner.text = `Pulling ${written}/${detail.manifest.length}...`;
1303
1395
  }
1304
1396
  spinner.succeed(
1305
- chalk6.green(
1397
+ chalk7.green(
1306
1398
  `Pulled snapshot ${detail.seq ? `#${detail.seq}` : snapshotId} \u2014 ${written} files into ${outDir}`
1307
1399
  )
1308
1400
  );
1309
1401
  } catch (err) {
1310
- spinner.fail(chalk6.red(err instanceof Error ? err.message : String(err)));
1402
+ spinner.fail(chalk7.red(err instanceof Error ? err.message : String(err)));
1311
1403
  process.exit(1);
1312
1404
  }
1313
1405
  });
1314
1406
 
1315
1407
  // src/commands/logs.ts
1316
1408
  import { Command as Command6 } from "commander";
1317
- import chalk7 from "chalk";
1409
+ import chalk8 from "chalk";
1318
1410
  var logsCommand = new Command6("logs").description("View app logs").option("-a, --app <id>", "App ID").option("-f, --follow", "Follow log output").option("-n, --lines <n>", "Number of lines to show", "50").option("-s, --since <when>", "Start of the window: ISO timestamp or an offset like 15m, 2h, 7d", "1h").option("--until <when>", "End of the window (same formats as --since)").option("-q, --search <text>", "Only lines containing this text").option("--pattern <pattern>", "CloudWatch filter pattern, for the real syntax").option("--instance <prefix>", "Only one instance, by log stream prefix").action(async (opts) => {
1319
1411
  const apiKey = getApiKey();
1320
1412
  requireApiKey(apiKey);
1321
1413
  const appId = opts.app || getCurrentApp();
1322
1414
  if (!appId) {
1323
- console.error(chalk7.red("No app specified. Use --app or link an app."));
1415
+ console.error(chalk8.red("No app specified. Use --app or link an app."));
1324
1416
  process.exit(1);
1325
1417
  }
1326
1418
  if (opts.search && opts.pattern) {
1327
- console.error(chalk7.red("Use either --search or --pattern, not both."));
1419
+ console.error(chalk8.red("Use either --search or --pattern, not both."));
1328
1420
  process.exit(1);
1329
1421
  }
1330
1422
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
@@ -1347,43 +1439,43 @@ var logsCommand = new Command6("logs").description("View app logs").option("-a,
1347
1439
  });
1348
1440
  for (const entry of page.events) print(entry);
1349
1441
  if (page.events.length === 0) {
1350
- console.log(chalk7.gray(`No log lines since ${page.start}.`));
1442
+ console.log(chalk8.gray(`No log lines since ${page.start}.`));
1351
1443
  }
1352
1444
  if (page.truncated) {
1353
1445
  console.log(
1354
- chalk7.yellow(
1446
+ chalk8.yellow(
1355
1447
  "\nThis is part of the window -- there are more lines than one request reads. Narrow --since or add --search."
1356
1448
  )
1357
1449
  );
1358
1450
  }
1359
1451
  });
1360
1452
  function print(entry) {
1361
- console.log(`${chalk7.gray(entry.timestamp)} ${formatLevel(entry.level)} ${entry.message}`);
1453
+ console.log(`${chalk8.gray(entry.timestamp)} ${formatLevel(entry.level)} ${entry.message}`);
1362
1454
  }
1363
1455
  function formatLevel(level) {
1364
1456
  switch (level) {
1365
1457
  case "error":
1366
- return chalk7.red("[ERR]");
1458
+ return chalk8.red("[ERR]");
1367
1459
  case "warn":
1368
- return chalk7.yellow("[WRN]");
1460
+ return chalk8.yellow("[WRN]");
1369
1461
  case "info":
1370
- return chalk7.blue("[INF]");
1462
+ return chalk8.blue("[INF]");
1371
1463
  case "debug":
1372
- return chalk7.gray("[DBG]");
1464
+ return chalk8.gray("[DBG]");
1373
1465
  default:
1374
- return chalk7.gray("[LOG]");
1466
+ return chalk8.gray("[LOG]");
1375
1467
  }
1376
1468
  }
1377
1469
 
1378
1470
  // src/commands/metrics.ts
1379
1471
  import { Command as Command7 } from "commander";
1380
- import chalk8 from "chalk";
1472
+ import chalk9 from "chalk";
1381
1473
  var metricsCommand = new Command7("metrics").description("Traffic, latency and resource use for an app").option("-a, --app <id>", "App ID").option("-s, --since <when>", "Start of the window: ISO timestamp or an offset like 15m, 2h, 7d", "1h").option("--until <when>", "End of the window (same formats as --since)").option("--series", "Print each bucket instead of just the summary").option("--json", "Print the raw response").action(async (opts) => {
1382
1474
  const apiKey = getApiKey();
1383
1475
  requireApiKey(apiKey);
1384
1476
  const appId = opts.app || getCurrentApp();
1385
1477
  if (!appId) {
1386
- console.error(chalk8.red("No app specified. Use --app or link an app."));
1478
+ console.error(chalk9.red("No app specified. Use --app or link an app."));
1387
1479
  process.exit(1);
1388
1480
  }
1389
1481
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
@@ -1394,7 +1486,7 @@ var metricsCommand = new Command7("metrics").description("Traffic, latency and r
1394
1486
  }
1395
1487
  const t = metrics.totals;
1396
1488
  console.log(
1397
- chalk8.dim(`${metrics.start} \u2192 ${metrics.end} (${metrics.period}s buckets)`)
1489
+ chalk9.dim(`${metrics.start} \u2192 ${metrics.end} (${metrics.period}s buckets)`)
1398
1490
  );
1399
1491
  console.log();
1400
1492
  console.log(` ${label("Requests")} ${t.requests}`);
@@ -1402,100 +1494,100 @@ var metricsCommand = new Command7("metrics").description("Traffic, latency and r
1402
1494
  console.log(` ${label("5xx")} ${count(t.serverErrors, true)}`);
1403
1495
  console.log(` ${label("Gate denied")} ${count(t.gateDenied)}`);
1404
1496
  console.log(` ${label("Latency avg")} ${ms(t.latencyAvgMs)}`);
1405
- console.log(` ${label("Latency p95")} ${ms(t.latencyP95Ms)} ${chalk8.dim("(sampled)")}`);
1497
+ console.log(` ${label("Latency p95")} ${ms(t.latencyP95Ms)} ${chalk9.dim("(sampled)")}`);
1406
1498
  console.log(` ${label("CPU avg")} ${units(t.cpuAvg)}`);
1407
1499
  console.log(` ${label("Memory avg")} ${mb(t.memoryAvgMb)}`);
1408
1500
  if (t.requests === 0) {
1409
1501
  console.log();
1410
- console.log(chalk8.dim("No requests recorded in this window."));
1502
+ console.log(chalk9.dim("No requests recorded in this window."));
1411
1503
  }
1412
1504
  if (opts.series) {
1413
1505
  console.log();
1414
1506
  for (const [name, points] of Object.entries(metrics.series)) {
1415
1507
  if (points.length === 0) continue;
1416
- console.log(chalk8.bold(name));
1417
- for (const p of points) console.log(` ${chalk8.dim(p.t)} ${p.v}`);
1508
+ console.log(chalk9.bold(name));
1509
+ for (const p of points) console.log(` ${chalk9.dim(p.t)} ${p.v}`);
1418
1510
  }
1419
1511
  }
1420
1512
  });
1421
1513
  function label(text) {
1422
- return chalk8.dim(text.padEnd(13));
1514
+ return chalk9.dim(text.padEnd(13));
1423
1515
  }
1424
1516
  function count(n, severe = false) {
1425
- if (n === 0) return chalk8.dim("0");
1426
- return severe ? chalk8.red(String(n)) : chalk8.yellow(String(n));
1517
+ if (n === 0) return chalk9.dim("0");
1518
+ return severe ? chalk9.red(String(n)) : chalk9.yellow(String(n));
1427
1519
  }
1428
1520
  function ms(v) {
1429
- return v === null ? chalk8.dim("\u2014") : `${v} ms`;
1521
+ return v === null ? chalk9.dim("\u2014") : `${v} ms`;
1430
1522
  }
1431
1523
  function mb(v) {
1432
- return v === null ? chalk8.dim("\u2014") : `${v} MB`;
1524
+ return v === null ? chalk9.dim("\u2014") : `${v} MB`;
1433
1525
  }
1434
1526
  function units(v) {
1435
- return v === null ? chalk8.dim("\u2014") : `${v} CPU units`;
1527
+ return v === null ? chalk9.dim("\u2014") : `${v} CPU units`;
1436
1528
  }
1437
1529
 
1438
1530
  // src/commands/rollback.ts
1439
1531
  import { Command as Command8 } from "commander";
1440
- import chalk9 from "chalk";
1532
+ import chalk10 from "chalk";
1441
1533
  import ora4 from "ora";
1442
1534
  var rollbackCommand = new Command8("rollback").description("Rollback to the previous production deployment").option("-a, --app <id>", "App ID").action(async (opts) => {
1443
1535
  const apiKey = getApiKey();
1444
1536
  requireApiKey(apiKey);
1445
1537
  const appId = opts.app || getCurrentApp();
1446
1538
  if (!appId) {
1447
- console.error(chalk9.red("No app specified. Use --app or link an app."));
1539
+ console.error(chalk10.red("No app specified. Use --app or link an app."));
1448
1540
  process.exit(1);
1449
1541
  }
1450
1542
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1451
1543
  const spinner = ora4("Rolling back...").start();
1452
1544
  try {
1453
1545
  const deployment = await client4.apps.rollback(appId);
1454
- spinner.succeed(chalk9.green("Rollback successful!"));
1546
+ spinner.succeed(chalk10.green("Rollback successful!"));
1455
1547
  if (deployment.url) {
1456
- console.log(chalk9.cyan(` URL: ${deployment.url}`));
1548
+ console.log(chalk10.cyan(` URL: ${deployment.url}`));
1457
1549
  }
1458
1550
  } catch (error) {
1459
- spinner.fail(chalk9.red(`Rollback failed: ${error.message}`));
1551
+ spinner.fail(chalk10.red(`Rollback failed: ${error.message}`));
1460
1552
  process.exit(1);
1461
1553
  }
1462
1554
  });
1463
1555
 
1464
1556
  // src/commands/secrets.ts
1465
1557
  import { Command as Command9 } from "commander";
1466
- import chalk10 from "chalk";
1467
- import inquirer3 from "inquirer";
1558
+ import chalk11 from "chalk";
1468
1559
  var secretsCommand = new Command9("secrets").description("Manage app secrets");
1469
1560
  secretsCommand.command("set <key> [value]").description("Set a secret").option("-a, --app <id>", "App ID").action(async (key, value, opts) => {
1470
1561
  const apiKey = getApiKey();
1471
1562
  requireApiKey(apiKey);
1472
1563
  const appId = opts.app || getCurrentApp();
1473
1564
  if (!appId) {
1474
- console.error(chalk10.red("No app specified."));
1565
+ console.error(chalk11.red("No app specified."));
1475
1566
  process.exit(1);
1476
1567
  }
1477
1568
  if (!value) {
1478
- const answers = await inquirer3.prompt([
1479
- { type: "password", name: "value", message: `Value for ${key}:` }
1480
- ]);
1569
+ const answers = await prompt(
1570
+ [{ type: "password", name: "value", message: `Value for ${key}:` }],
1571
+ `Pass the value as an argument: videan secrets set ${key} <value>`
1572
+ );
1481
1573
  value = answers.value;
1482
1574
  }
1483
1575
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1484
1576
  await client4.secrets.set(appId, key, value);
1485
- console.log(chalk10.green(`Secret "${key}" set successfully.`));
1577
+ console.log(chalk11.green(`Secret "${key}" set successfully.`));
1486
1578
  });
1487
1579
  secretsCommand.command("list").description("List all secrets").option("-a, --app <id>", "App ID").action(async (opts) => {
1488
1580
  const apiKey = getApiKey();
1489
1581
  requireApiKey(apiKey);
1490
1582
  const appId = opts.app || getCurrentApp();
1491
1583
  if (!appId) {
1492
- console.error(chalk10.red("No app specified."));
1584
+ console.error(chalk11.red("No app specified."));
1493
1585
  process.exit(1);
1494
1586
  }
1495
1587
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1496
1588
  const secrets = await client4.secrets.list(appId);
1497
1589
  if (secrets.length === 0) {
1498
- console.log(chalk10.gray("No secrets configured."));
1590
+ console.log(chalk11.gray("No secrets configured."));
1499
1591
  return;
1500
1592
  }
1501
1593
  console.log(formatRow(["KEY", "UPDATED"], [30, 24]));
@@ -1508,24 +1600,23 @@ secretsCommand.command("remove <key>").description("Remove a secret").option("-a
1508
1600
  requireApiKey(apiKey);
1509
1601
  const appId = opts.app || getCurrentApp();
1510
1602
  if (!appId) {
1511
- console.error(chalk10.red("No app specified."));
1603
+ console.error(chalk11.red("No app specified."));
1512
1604
  process.exit(1);
1513
1605
  }
1514
1606
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1515
1607
  await client4.secrets.delete(appId, key);
1516
- console.log(chalk10.green(`Secret "${key}" removed.`));
1608
+ console.log(chalk11.green(`Secret "${key}" removed.`));
1517
1609
  });
1518
1610
 
1519
1611
  // src/commands/apps.ts
1520
1612
  import { Command as Command10 } from "commander";
1521
- import chalk11 from "chalk";
1522
- import inquirer4 from "inquirer";
1613
+ import chalk12 from "chalk";
1523
1614
  var appsCommand = new Command10("apps").description("Manage apps");
1524
1615
  function accessOption(value) {
1525
1616
  if (value === void 0) return void 0;
1526
1617
  const tier = value.trim().toLowerCase();
1527
1618
  if (!isVisibility(tier)) {
1528
- console.error(chalk11.red(`Unknown --access value "${value}". Use one of: ${VISIBILITIES.join(", ")}`));
1619
+ console.error(chalk12.red(`Unknown --access value "${value}". Use one of: ${VISIBILITIES.join(", ")}`));
1529
1620
  process.exit(1);
1530
1621
  }
1531
1622
  return tier;
@@ -1534,7 +1625,7 @@ function modeOption(value) {
1534
1625
  if (value === void 0) return void 0;
1535
1626
  const mode = value.trim().toLowerCase();
1536
1627
  if (!isAppMode(mode)) {
1537
- console.error(chalk11.red(`Unknown --mode value "${value}". Use one of: ${APP_MODES.join(", ")}`));
1628
+ console.error(chalk12.red(`Unknown --mode value "${value}". Use one of: ${APP_MODES.join(", ")}`));
1538
1629
  process.exit(1);
1539
1630
  }
1540
1631
  return mode;
@@ -1550,7 +1641,7 @@ appsCommand.command("list").description("List all apps").option("-o, --org <id>"
1550
1641
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1551
1642
  const apps = await client4.apps.list(orgId);
1552
1643
  if (apps.length === 0) {
1553
- console.log(chalk11.gray("No apps yet. Create one with `videan apps create`."));
1644
+ console.log(chalk12.gray("No apps yet. Create one with `videan apps create`."));
1554
1645
  return;
1555
1646
  }
1556
1647
  const widths = [20, 24, 10, 44];
@@ -1572,13 +1663,14 @@ appsCommand.command("create").description("Create a new app").option("-n, --name
1572
1663
  const orgId = requireOrg(opts.org || getDefaultOrg());
1573
1664
  let name = opts.name;
1574
1665
  if (!name) {
1575
- const answers = await inquirer4.prompt([
1576
- { type: "input", name: "name", message: "App name:" }
1577
- ]);
1666
+ const answers = await prompt(
1667
+ [{ type: "input", name: "name", message: "App name:" }],
1668
+ "Pass the name: videan apps create --name <name>"
1669
+ );
1578
1670
  name = answers.name;
1579
1671
  }
1580
1672
  if (!name) {
1581
- console.error(chalk11.red("An app name is required."));
1673
+ console.error(chalk12.red("An app name is required."));
1582
1674
  process.exit(1);
1583
1675
  }
1584
1676
  const visibility = accessOption(opts.access) ?? (opts.public ? "public" : void 0);
@@ -1595,11 +1687,11 @@ appsCommand.command("create").description("Create a new app").option("-n, --name
1595
1687
  mode
1596
1688
  });
1597
1689
  setCurrentApp(app.id);
1598
- console.log(chalk11.green(`App "${app.name}" created (${app.id})`));
1599
- console.log(chalk11.gray(` Mode: ${app.mode ?? "container"}`));
1600
- console.log(chalk11.gray(` Access: ${describeAccess(app)}`));
1601
- console.log(chalk11.gray(` URL after first deploy: ${appUrl(app.slug)}`));
1602
- console.log(chalk11.gray(" Push code with `videan deploy` \u2014 the runtime is auto-detected."));
1690
+ console.log(chalk12.green(`App "${app.name}" created (${app.id})`));
1691
+ console.log(chalk12.gray(` Mode: ${app.mode ?? "container"}`));
1692
+ console.log(chalk12.gray(` Access: ${describeAccess(app)}`));
1693
+ console.log(chalk12.gray(` URL after first deploy: ${appUrl(app.slug)}`));
1694
+ console.log(chalk12.gray(" Push code with `videan deploy` \u2014 the runtime is auto-detected."));
1603
1695
  }
1604
1696
  );
1605
1697
  appsCommand.command("set").description("Change an app's settings (scale, rate limit, gate, capabilities)").option("-a, --app <id>", "App ID").option("-n, --name <name>", "Rename the app (the URL keeps its original slug)").option("--cpu <units>", "CPU units per instance (256 = 0.25 vCPU)").option("--memory <mib>", "Memory per instance, in MiB").option("--instances <n>", "Number of instances to run").option("--rate-limit <rpm>", "Requests per minute per client").option("--no-rate-limit", "Remove the rate limit").option("--access <tier>", `Who may open the app: ${VISIBILITIES.join(" | ")}`).option("--public", "Serve without the platform login gate (same as --access public)").option("--private", "Require platform login (leaves the tier alone if it is already gated)").option("--postgres", "Provision a managed Postgres database").option("--storage", "Give the app an object-storage prefix").option("--email", "Let the app send mail through the platform").option("--stack <label>", "Group this app with its siblings under a shared label").option("--no-stack", "Remove the app from its stack").option("--mode <mode>", `How the app runs: ${APP_MODES.join(" | ")} (applies on the next deploy or restart)`).option("--json", "Print the raw response").action(
@@ -1608,16 +1700,16 @@ appsCommand.command("set").description("Change an app's settings (scale, rate li
1608
1700
  requireApiKey(apiKey);
1609
1701
  const appId = opts.app || getCurrentApp();
1610
1702
  if (!appId) {
1611
- console.error(chalk11.red("No app specified. Use --app or link an app."));
1703
+ console.error(chalk12.red("No app specified. Use --app or link an app."));
1612
1704
  process.exit(1);
1613
1705
  }
1614
1706
  if (opts.public && opts.private) {
1615
- console.error(chalk11.red("--public and --private contradict each other."));
1707
+ console.error(chalk12.red("--public and --private contradict each other."));
1616
1708
  process.exit(1);
1617
1709
  }
1618
1710
  const access = accessOption(opts.access);
1619
1711
  if (access && (opts.public || opts.private)) {
1620
- console.error(chalk11.red("--access already says who may open the app; drop --public/--private."));
1712
+ console.error(chalk12.red("--access already says who may open the app; drop --public/--private."));
1621
1713
  process.exit(1);
1622
1714
  }
1623
1715
  const mode = modeOption(opts.mode);
@@ -1642,7 +1734,7 @@ appsCommand.command("set").description("Change an app's settings (scale, rate li
1642
1734
  if (opts.stack === false) changes.stack = null;
1643
1735
  else if (typeof opts.stack === "string") changes.stack = opts.stack;
1644
1736
  if (Object.keys(changes).length === 0) {
1645
- console.error(chalk11.red("Nothing to change. Pass at least one option."));
1737
+ console.error(chalk12.red("Nothing to change. Pass at least one option."));
1646
1738
  process.exit(1);
1647
1739
  }
1648
1740
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
@@ -1651,7 +1743,7 @@ appsCommand.command("set").description("Change an app's settings (scale, rate li
1651
1743
  console.log(JSON.stringify(app, null, 2));
1652
1744
  return;
1653
1745
  }
1654
- console.log(chalk11.green(`Updated ${app.name} (${app.id})`));
1746
+ console.log(chalk12.green(`Updated ${app.name} (${app.id})`));
1655
1747
  console.log(` Mode: ${app.mode ?? "container"}`);
1656
1748
  console.log(` Access: ${describeAccess(app)}`);
1657
1749
  console.log(` Scale: ${app.cpu ?? "default"} CPU / ${app.memory ?? "default"} MiB`);
@@ -1660,7 +1752,7 @@ appsCommand.command("set").description("Change an app's settings (scale, rate li
1660
1752
  );
1661
1753
  if (app.restartRequired) {
1662
1754
  console.log(
1663
- chalk11.yellow("\n These settings apply to the next deploy. Run `videan deploy` to pick them up.")
1755
+ chalk12.yellow("\n These settings apply to the next deploy. Run `videan deploy` to pick them up.")
1664
1756
  );
1665
1757
  }
1666
1758
  }
@@ -1668,37 +1760,42 @@ appsCommand.command("set").description("Change an app's settings (scale, rate li
1668
1760
  function intOption(flag, raw) {
1669
1761
  const n = Number(raw);
1670
1762
  if (!Number.isInteger(n) || n <= 0) {
1671
- console.error(chalk11.red(`${flag} expects a positive whole number, got "${raw}".`));
1763
+ console.error(chalk12.red(`${flag} expects a positive whole number, got "${raw}".`));
1672
1764
  process.exit(1);
1673
1765
  }
1674
1766
  return n;
1675
1767
  }
1676
- appsCommand.command("delete <id>").description("Delete an app and all its resources").action(async (id) => {
1768
+ appsCommand.command("delete <id>").description("Delete an app and all its resources").option("--yes", "Skip the confirmation prompt").action(async (id, opts) => {
1677
1769
  const apiKey = getApiKey();
1678
1770
  requireApiKey(apiKey);
1679
- const { confirm } = await inquirer4.prompt([
1680
- {
1681
- type: "confirm",
1682
- name: "confirm",
1683
- message: `Delete app ${id}? This stops the app and destroys its database, secrets and snapshots.`,
1684
- default: false
1685
- }
1686
- ]);
1687
- if (!confirm) return;
1771
+ if (!opts.yes) {
1772
+ const { confirm } = await prompt(
1773
+ [
1774
+ {
1775
+ type: "confirm",
1776
+ name: "confirm",
1777
+ message: `Delete app ${id}? This stops the app and destroys its database, secrets and snapshots.`,
1778
+ default: false
1779
+ }
1780
+ ],
1781
+ "Confirm with a flag: videan apps delete <id> --yes"
1782
+ );
1783
+ if (!confirm) return;
1784
+ }
1688
1785
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1689
1786
  const { warnings } = await client4.apps.delete(id);
1690
- console.log(chalk11.green("App deleted."));
1787
+ console.log(chalk12.green("App deleted."));
1691
1788
  if (warnings.length > 0) {
1692
- console.log(chalk11.yellow("\nSome resources could not be released:"));
1789
+ console.log(chalk12.yellow("\nSome resources could not be released:"));
1693
1790
  for (const w of warnings) {
1694
- console.log(chalk11.yellow(` ${w.step}: ${w.error || "unknown error"}`));
1791
+ console.log(chalk12.yellow(` ${w.step}: ${w.error || "unknown error"}`));
1695
1792
  }
1696
1793
  }
1697
1794
  });
1698
1795
 
1699
1796
  // src/commands/status.ts
1700
1797
  import { Command as Command11 } from "commander";
1701
- import chalk12 from "chalk";
1798
+ import chalk13 from "chalk";
1702
1799
  function accessLine(app) {
1703
1800
  const tier = app.visibility ? visibilityOf(app.visibility) : app.authRequired ? "org" : "public";
1704
1801
  return `${tier} \u2014 ${VISIBILITY_LABELS[tier].toLowerCase()}`;
@@ -1708,42 +1805,41 @@ var statusCommand = new Command11("status").description("Show current app status
1708
1805
  requireApiKey(apiKey);
1709
1806
  const appId = opts.app || getCurrentApp();
1710
1807
  if (!appId) {
1711
- console.error(chalk12.red("No app specified. Use --app or link an app."));
1808
+ console.error(chalk13.red("No app specified. Use --app or link an app."));
1712
1809
  process.exit(1);
1713
1810
  }
1714
1811
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1715
1812
  const app = await client4.apps.get(appId);
1716
1813
  const health = await client4.apps.health(appId);
1717
- console.log(chalk12.bold(app.name));
1814
+ console.log(chalk13.bold(app.name));
1718
1815
  console.log(` ID: ${app.id}`);
1719
1816
  console.log(` Created: ${app.createdAt}`);
1720
1817
  console.log(` Postgres: ${app.postgresEnabled ? "enabled" : "disabled"}`);
1721
1818
  console.log(` Access: ${accessLine(app)}`);
1722
1819
  console.log(` State: ${formatStatus(health.state)}`);
1723
- console.log(` Healthy: ${health.healthy ? chalk12.green("yes") : chalk12.red("no")}`);
1724
- console.log(` URL: ${chalk12.cyan(health.url || appUrl(app.slug))}`);
1820
+ console.log(` Healthy: ${health.healthy ? chalk13.green("yes") : chalk13.red("no")}`);
1821
+ console.log(` URL: ${chalk13.cyan(health.url || appUrl(app.slug))}`);
1725
1822
  const task = health.task;
1726
1823
  if (task) {
1727
1824
  if (task.exit_code !== void 0 && task.exit_code !== null) {
1728
1825
  const code = String(task.exit_code);
1729
- console.log(` Exit code: ${task.exit_code === 0 ? code : chalk12.red(code)}`);
1826
+ console.log(` Exit code: ${task.exit_code === 0 ? code : chalk13.red(code)}`);
1730
1827
  }
1731
1828
  if (task.stopped_reason) {
1732
- console.log(` Stopped: ${chalk12.yellow(task.stopped_reason)}`);
1829
+ console.log(` Stopped: ${chalk13.yellow(task.stopped_reason)}`);
1733
1830
  }
1734
1831
  if (task.container_reason) {
1735
- console.log(` Detail: ${chalk12.yellow(task.container_reason)}`);
1832
+ console.log(` Detail: ${chalk13.yellow(task.container_reason)}`);
1736
1833
  }
1737
1834
  }
1738
1835
  if (!health.healthy) {
1739
- console.log(chalk12.gray("\nRun `videan logs` to see why."));
1836
+ console.log(chalk13.gray("\nRun `videan logs` to see why."));
1740
1837
  }
1741
1838
  });
1742
1839
 
1743
1840
  // src/commands/keys.ts
1744
1841
  import { Command as Command12 } from "commander";
1745
- import chalk13 from "chalk";
1746
- import inquirer5 from "inquirer";
1842
+ import chalk14 from "chalk";
1747
1843
  var keysCommand = new Command12("keys").description("Manage API keys for programmatic access");
1748
1844
  keysCommand.command("create <name>").description("Create an API key").option("-o, --org <id>", "Organization ID").option("-e, --expires <days>", "Expire the key after N days").option(
1749
1845
  "--app <id>",
@@ -1756,7 +1852,7 @@ keysCommand.command("create <name>").description("Create an API key").option("-o
1756
1852
  if (opts.expires !== void 0) {
1757
1853
  expiresInDays = Number(opts.expires);
1758
1854
  if (!Number.isInteger(expiresInDays) || expiresInDays < 1) {
1759
- console.error(chalk13.red("--expires must be a whole number of days, at least 1."));
1855
+ console.error(chalk14.red("--expires must be a whole number of days, at least 1."));
1760
1856
  process.exit(1);
1761
1857
  }
1762
1858
  }
@@ -1767,22 +1863,22 @@ keysCommand.command("create <name>").description("Create an API key").option("-o
1767
1863
  expiresInDays,
1768
1864
  appId: opts.app
1769
1865
  });
1770
- console.log(chalk13.green(`Created key "${key.name}" (${key.id})`));
1866
+ console.log(chalk14.green(`Created key "${key.name}" (${key.id})`));
1771
1867
  console.log(`
1772
- ${chalk13.bold(key.key)}
1868
+ ${chalk14.bold(key.key)}
1773
1869
  `);
1774
- console.log(chalk13.yellow("Copy it now. It cannot be shown again."));
1870
+ console.log(chalk14.yellow("Copy it now. It cannot be shown again."));
1775
1871
  if (key.appId) {
1776
1872
  console.log(
1777
- chalk13.gray(`Service token, scoped to app ${key.appId}. It cannot reach anything else.`)
1873
+ chalk14.gray(`Service token, scoped to app ${key.appId}. It cannot reach anything else.`)
1778
1874
  );
1779
1875
  } else {
1780
1876
  console.log(
1781
- chalk13.gray("Acts as you across every app in the org -- do not deploy it inside an app.")
1877
+ chalk14.gray("Acts as you across every app in the org -- do not deploy it inside an app.")
1782
1878
  );
1783
1879
  }
1784
1880
  if (key.expiresAt) {
1785
- console.log(chalk13.gray(`Expires ${key.expiresAt}`));
1881
+ console.log(chalk14.gray(`Expires ${key.expiresAt}`));
1786
1882
  }
1787
1883
  });
1788
1884
  keysCommand.command("list").description("List API keys").option("-o, --org <id>", "Organization ID").action(async (opts) => {
@@ -1792,13 +1888,13 @@ keysCommand.command("list").description("List API keys").option("-o, --org <id>"
1792
1888
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1793
1889
  const keys = await client4.apiKeys.list(orgId);
1794
1890
  if (keys.length === 0) {
1795
- console.log(chalk13.gray("No API keys. Create one with `videan keys create <name>`."));
1891
+ console.log(chalk14.gray("No API keys. Create one with `videan keys create <name>`."));
1796
1892
  return;
1797
1893
  }
1798
1894
  const widths = [24, 24, 16, 22, 12, 24];
1799
1895
  console.log(formatRow(["NAME", "ID", "PREFIX", "LAST USED", "STATE", "SCOPE"], widths));
1800
1896
  for (const key of keys) {
1801
- const state = key.revokedAt ? chalk13.red("revoked") : key.expiresAt && new Date(key.expiresAt) < /* @__PURE__ */ new Date() ? chalk13.yellow("expired") : chalk13.green("active");
1897
+ const state = key.revokedAt ? chalk14.red("revoked") : key.expiresAt && new Date(key.expiresAt) < /* @__PURE__ */ new Date() ? chalk14.yellow("expired") : chalk14.green("active");
1802
1898
  console.log(formatRow(
1803
1899
  [
1804
1900
  key.name,
@@ -1812,21 +1908,24 @@ keysCommand.command("list").description("List API keys").option("-o, --org <id>"
1812
1908
  ));
1813
1909
  }
1814
1910
  });
1815
- keysCommand.command("revoke <id>").description("Revoke an API key").action(async (id) => {
1911
+ keysCommand.command("revoke <id>").description("Revoke an API key").option("--yes", "Skip the confirmation prompt").action(async (id, opts) => {
1816
1912
  const apiKey = getApiKey();
1817
1913
  requireApiKey(apiKey);
1818
- const { confirm } = await inquirer5.prompt([
1819
- { type: "confirm", name: "confirm", message: `Revoke key ${id}?`, default: false }
1820
- ]);
1821
- if (!confirm) return;
1914
+ if (!opts.yes) {
1915
+ const { confirm } = await prompt(
1916
+ [{ type: "confirm", name: "confirm", message: `Revoke key ${id}?`, default: false }],
1917
+ "Confirm with a flag: videan keys revoke <id> --yes"
1918
+ );
1919
+ if (!confirm) return;
1920
+ }
1822
1921
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1823
1922
  await client4.apiKeys.revoke(id);
1824
- console.log(chalk13.green("Key revoked."));
1923
+ console.log(chalk14.green("Key revoked."));
1825
1924
  });
1826
1925
 
1827
1926
  // src/commands/orgs.ts
1828
1927
  import { Command as Command13 } from "commander";
1829
- import chalk14 from "chalk";
1928
+ import chalk15 from "chalk";
1830
1929
  var orgsCommand = new Command13("orgs").description("Manage organizations");
1831
1930
  orgsCommand.command("list").description("List your organizations").action(async () => {
1832
1931
  const apiKey = getApiKey();
@@ -1835,14 +1934,14 @@ orgsCommand.command("list").description("List your organizations").action(async
1835
1934
  const orgs = await client4.orgs.list();
1836
1935
  const current = getDefaultOrg();
1837
1936
  if (orgs.length === 0) {
1838
- console.log(chalk14.gray("No organizations. Create one with `videan orgs create <name>`."));
1937
+ console.log(chalk15.gray("No organizations. Create one with `videan orgs create <name>`."));
1839
1938
  return;
1840
1939
  }
1841
1940
  const widths = [24, 24, 10, 8];
1842
1941
  console.log(formatRow(["NAME", "ID", "PLAN", ""], widths));
1843
1942
  for (const org of orgs) {
1844
1943
  console.log(formatRow(
1845
- [org.name, org.id, org.plan, org.id === current ? chalk14.green("default") : ""],
1944
+ [org.name, org.id, org.plan, org.id === current ? chalk15.green("default") : ""],
1846
1945
  widths
1847
1946
  ));
1848
1947
  }
@@ -1853,7 +1952,7 @@ orgsCommand.command("create <name>").description("Create an organization").actio
1853
1952
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1854
1953
  const org = await client4.orgs.create(name);
1855
1954
  if (!getDefaultOrg()) setDefaultOrg(org.id);
1856
- console.log(chalk14.green(`Organization "${org.name}" created (${org.id})`));
1955
+ console.log(chalk15.green(`Organization "${org.name}" created (${org.id})`));
1857
1956
  });
1858
1957
  orgsCommand.command("use <id>").description("Set the default organization for this machine").action(async (id) => {
1859
1958
  const apiKey = getApiKey();
@@ -1861,7 +1960,7 @@ orgsCommand.command("use <id>").description("Set the default organization for th
1861
1960
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1862
1961
  const org = await client4.orgs.get(id);
1863
1962
  setDefaultOrg(org.id);
1864
- console.log(chalk14.green(`Default organization set to "${org.name}".`));
1963
+ console.log(chalk15.green(`Default organization set to "${org.name}".`));
1865
1964
  });
1866
1965
  orgsCommand.command("members").description("List organization members").option("-o, --org <id>", "Organization ID").action(async (opts) => {
1867
1966
  const apiKey = getApiKey();
@@ -1884,12 +1983,12 @@ orgsCommand.command("invite <email>").description("Invite a member by email").op
1884
1983
  const orgId = requireOrg(opts.org || getDefaultOrg());
1885
1984
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1886
1985
  await client4.orgs.invite(orgId, email);
1887
- console.log(chalk14.green(`Invited ${email}.`));
1986
+ console.log(chalk15.green(`Invited ${email}.`));
1888
1987
  });
1889
1988
 
1890
1989
  // src/commands/storage.ts
1891
1990
  import { Command as Command14 } from "commander";
1892
- import chalk15 from "chalk";
1991
+ import chalk16 from "chalk";
1893
1992
  import fs5 from "fs-extra";
1894
1993
  import path5 from "node:path";
1895
1994
  var storageCommand = new Command14("storage").description("Store files for an app");
@@ -1901,7 +2000,7 @@ function client() {
1901
2000
  function requireApp(opt) {
1902
2001
  const appId = opt || getCurrentApp();
1903
2002
  if (!appId) {
1904
- console.error(chalk15.red("No app specified. Use --app or link an app."));
2003
+ console.error(chalk16.red("No app specified. Use --app or link an app."));
1905
2004
  process.exit(1);
1906
2005
  }
1907
2006
  return appId;
@@ -1924,7 +2023,7 @@ storageCommand.command("ls [prefix]").description("List stored objects").option(
1924
2023
  return;
1925
2024
  }
1926
2025
  if (objects.length === 0) {
1927
- console.log(chalk15.gray("No objects stored. Upload one with `videan storage put <file>`."));
2026
+ console.log(chalk16.gray("No objects stored. Upload one with `videan storage put <file>`."));
1928
2027
  return;
1929
2028
  }
1930
2029
  const widths = [52, 12, 24];
@@ -1933,13 +2032,13 @@ storageCommand.command("ls [prefix]").description("List stored objects").option(
1933
2032
  console.log(formatRow([o.key, human(o.size), o.lastModified || "-"], widths));
1934
2033
  }
1935
2034
  const total = objects.reduce((sum, o) => sum + o.size, 0);
1936
- console.log(chalk15.dim(`
2035
+ console.log(chalk16.dim(`
1937
2036
  ${objects.length} object(s), ${human(total)}`));
1938
2037
  });
1939
2038
  storageCommand.command("put <file> [key]").description("Upload a file (key defaults to the file's name)").option("-a, --app <id>", "App ID").option("-t, --type <mime>", "Content type").action(async (file, key, opts) => {
1940
2039
  const appId = requireApp(opts.app);
1941
2040
  if (!await fs5.pathExists(file)) {
1942
- console.error(chalk15.red(`No such file: ${file}`));
2041
+ console.error(chalk16.red(`No such file: ${file}`));
1943
2042
  process.exit(1);
1944
2043
  }
1945
2044
  const body = await fs5.readFile(file);
@@ -1948,27 +2047,27 @@ storageCommand.command("put <file> [key]").description("Upload a file (key defau
1948
2047
  contentType: opts.type
1949
2048
  });
1950
2049
  console.log(
1951
- chalk15.green(`Uploaded ${objectKey}`) + chalk15.dim(` (${human(body.length)})`)
2050
+ chalk16.green(`Uploaded ${objectKey}`) + chalk16.dim(` (${human(body.length)})`)
1952
2051
  );
1953
- if (stored.etag) console.log(chalk15.dim(`etag ${stored.etag}`));
2052
+ if (stored.etag) console.log(chalk16.dim(`etag ${stored.etag}`));
1954
2053
  });
1955
2054
  storageCommand.command("get <key> [file]").description("Download an object (defaults to the key's basename)").option("-a, --app <id>", "App ID").action(async (key, file, opts) => {
1956
2055
  const appId = requireApp(opts.app);
1957
2056
  const bytes = await client().storage.download(appId, key);
1958
2057
  const dest = file || path5.basename(key);
1959
2058
  await fs5.writeFile(dest, bytes);
1960
- console.log(chalk15.green(`Wrote ${dest}`) + chalk15.dim(` (${human(bytes.length)})`));
2059
+ console.log(chalk16.green(`Wrote ${dest}`) + chalk16.dim(` (${human(bytes.length)})`));
1961
2060
  });
1962
2061
  storageCommand.command("rm <key>").description("Delete an object").option("-a, --app <id>", "App ID").action(async (key, opts) => {
1963
2062
  const appId = requireApp(opts.app);
1964
2063
  await client().storage.delete(appId, key);
1965
- console.log(chalk15.green(`Deleted ${key}`));
2064
+ console.log(chalk16.green(`Deleted ${key}`));
1966
2065
  });
1967
2066
  storageCommand.command("url <key>").description("Print a temporary link to an object").option("-a, --app <id>", "App ID").option("-e, --expires <seconds>", "Lifetime in seconds (30-3600)", "900").option("--upload", "Sign an upload (PUT) URL instead of a download").option("-d, --download <filename>", "Serve as a download with this filename").action(async (key, opts) => {
1968
2067
  const appId = requireApp(opts.app);
1969
2068
  const expiresIn = Number(opts.expires);
1970
2069
  if (!Number.isInteger(expiresIn) || expiresIn < 30 || expiresIn > 3600) {
1971
- console.error(chalk15.red("--expires must be a whole number of seconds between 30 and 3600."));
2070
+ console.error(chalk16.red("--expires must be a whole number of seconds between 30 and 3600."));
1972
2071
  process.exit(1);
1973
2072
  }
1974
2073
  const signed = await client().storage.presign(appId, key, {
@@ -1978,7 +2077,7 @@ storageCommand.command("url <key>").description("Print a temporary link to an ob
1978
2077
  });
1979
2078
  console.log(signed.url);
1980
2079
  console.error(
1981
- chalk15.yellow(
2080
+ chalk16.yellow(
1982
2081
  `This ${signed.method} link works for ${expiresIn}s for anyone who has it.`
1983
2082
  )
1984
2083
  );
@@ -1991,7 +2090,7 @@ storageCommand.command("usage").description("Total objects and bytes stored").op
1991
2090
 
1992
2091
  // src/commands/email.ts
1993
2092
  import { Command as Command15 } from "commander";
1994
- import chalk16 from "chalk";
2093
+ import chalk17 from "chalk";
1995
2094
  import fs6 from "fs-extra";
1996
2095
  var emailCommand = new Command15("email").description("Send email from an app");
1997
2096
  function client2() {
@@ -2002,7 +2101,7 @@ function client2() {
2002
2101
  function requireApp2(opt) {
2003
2102
  const appId = opt || getCurrentApp();
2004
2103
  if (!appId) {
2005
- console.error(chalk16.red("No app specified. Use --app or link an app."));
2104
+ console.error(chalk17.red("No app specified. Use --app or link an app."));
2006
2105
  process.exit(1);
2007
2106
  }
2008
2107
  return appId;
@@ -2018,7 +2117,7 @@ emailCommand.command("send").description("Send an email").requiredOption("-t, --
2018
2117
  else text = contents;
2019
2118
  }
2020
2119
  if (!text && !html) {
2021
- console.error(chalk16.red("Nothing to send. Pass --text, --html or --body-file."));
2120
+ console.error(chalk17.red("Nothing to send. Pass --text, --html or --body-file."));
2022
2121
  process.exit(1);
2023
2122
  }
2024
2123
  const sent = await client2().email.send(appId, {
@@ -2032,12 +2131,12 @@ emailCommand.command("send").description("Send an email").requiredOption("-t, --
2032
2131
  console.log(JSON.stringify(sent, null, 2));
2033
2132
  return;
2034
2133
  }
2035
- console.log(chalk16.green("Sent"));
2134
+ console.log(chalk17.green("Sent"));
2036
2135
  console.log(` From: ${sent.from}`);
2037
2136
  console.log(` To: ${sent.to.join(", ")}`);
2038
2137
  console.log(` Message: ${sent.messageId ?? "(no id returned)"}`);
2039
2138
  console.log(
2040
- chalk16.dim(
2139
+ chalk17.dim(
2041
2140
  ` Quota: ${sent.quota.sent}/${sent.quota.quota} today, resets ${sent.quota.resetsAt}`
2042
2141
  )
2043
2142
  );
@@ -2052,12 +2151,12 @@ emailCommand.command("quota").description("Show today's sending allowance").opti
2052
2151
  console.log(`Sent today: ${quota.sent}`);
2053
2152
  console.log(`Daily quota: ${quota.quota}`);
2054
2153
  console.log(`Remaining: ${quota.remaining}`);
2055
- console.log(chalk16.dim(`Resets at: ${quota.resetsAt}`));
2154
+ console.log(chalk17.dim(`Resets at: ${quota.resetsAt}`));
2056
2155
  });
2057
2156
 
2058
2157
  // src/commands/jobs.ts
2059
2158
  import { Command as Command16 } from "commander";
2060
- import chalk17 from "chalk";
2159
+ import chalk18 from "chalk";
2061
2160
  import fs7 from "fs-extra";
2062
2161
  var jobsCommand = new Command16("jobs").description("Enqueue and inspect background jobs");
2063
2162
  function client3() {
@@ -2068,7 +2167,7 @@ function client3() {
2068
2167
  function requireApp3(opt) {
2069
2168
  const appId = opt || getCurrentApp();
2070
2169
  if (!appId) {
2071
- console.error(chalk17.red("No app specified. Use --app or run `videan init` first."));
2170
+ console.error(chalk18.red("No app specified. Use --app or run `videan init` first."));
2072
2171
  process.exit(1);
2073
2172
  }
2074
2173
  return appId;
@@ -2076,20 +2175,20 @@ function requireApp3(opt) {
2076
2175
  function intOption2(flag, raw) {
2077
2176
  const n = Number(raw);
2078
2177
  if (!Number.isInteger(n) || n < 0) {
2079
- console.error(chalk17.red(`${flag} expects a whole number, got "${raw}".`));
2178
+ console.error(chalk18.red(`${flag} expects a whole number, got "${raw}".`));
2080
2179
  process.exit(1);
2081
2180
  }
2082
2181
  return n;
2083
2182
  }
2084
2183
  var STATUS_COLOR = {
2085
- queued: chalk17.yellow,
2086
- running: chalk17.cyan,
2087
- succeeded: chalk17.green,
2088
- dead: chalk17.red,
2089
- cancelled: chalk17.gray
2184
+ queued: chalk18.yellow,
2185
+ running: chalk18.cyan,
2186
+ succeeded: chalk18.green,
2187
+ dead: chalk18.red,
2188
+ cancelled: chalk18.gray
2090
2189
  };
2091
2190
  function statusLabel(status) {
2092
- return (STATUS_COLOR[status] ?? chalk17.white)(status);
2191
+ return (STATUS_COLOR[status] ?? chalk18.white)(status);
2093
2192
  }
2094
2193
  jobsCommand.command("enqueue").description("Queue a job for the app to run").option("-a, --app <id>", "App ID").option("-q, --queue <name>", "Queue name (default: default)").option("-p, --path <path>", "Path on the app to POST to (default: /jobs)").option("-d, --data <json>", "JSON payload").option("-f, --file <path>", "Read the JSON payload from a file").option("--delay <seconds>", "Run no earlier than this many seconds from now").option("--attempts <n>", "Attempts before the job is given up on (default: 3)").option("--timeout <seconds>", "How long the handler gets per attempt (default: 60)").option("--key <key>", "Idempotency key -- a repeat returns the same job").option("--json", "Print the raw response").action(async (opts) => {
2095
2194
  const appId = requireApp3(opts.app);
@@ -2099,7 +2198,7 @@ jobsCommand.command("enqueue").description("Queue a job for the app to run").opt
2099
2198
  try {
2100
2199
  payload = JSON.parse(raw);
2101
2200
  } catch (e) {
2102
- console.error(chalk17.red(`--${opts.file ? "file" : "data"} is not valid JSON: ${e.message}`));
2201
+ console.error(chalk18.red(`--${opts.file ? "file" : "data"} is not valid JSON: ${e.message}`));
2103
2202
  process.exit(1);
2104
2203
  }
2105
2204
  }
@@ -2117,13 +2216,13 @@ jobsCommand.command("enqueue").description("Queue a job for the app to run").opt
2117
2216
  return;
2118
2217
  }
2119
2218
  if (job.deduplicated) {
2120
- console.log(chalk17.yellow(`Already queued as ${job.id} (idempotency key "${opts.key}")`));
2219
+ console.log(chalk18.yellow(`Already queued as ${job.id} (idempotency key "${opts.key}")`));
2121
2220
  } else {
2122
- console.log(chalk17.green(`Queued ${job.id}`));
2221
+ console.log(chalk18.green(`Queued ${job.id}`));
2123
2222
  }
2124
- console.log(chalk17.gray(` ${job.method} ${job.path} on queue "${job.queue}"`));
2223
+ console.log(chalk18.gray(` ${job.method} ${job.path} on queue "${job.queue}"`));
2125
2224
  if (job.runAt && Date.parse(job.runAt) > Date.now() + 1e3) {
2126
- console.log(chalk17.gray(` Runs at ${new Date(job.runAt).toLocaleString()}`));
2225
+ console.log(chalk18.gray(` Runs at ${new Date(job.runAt).toLocaleString()}`));
2127
2226
  }
2128
2227
  });
2129
2228
  jobsCommand.command("list").description("List jobs, newest first").option("-a, --app <id>", "App ID").option("-s, --status <status>", "queued, running, succeeded, dead or cancelled").option("-q, --queue <name>", "Only this queue").option("-n, --limit <n>", "How many to show (default: 20)").option("--json", "Print the raw response").action(async (opts) => {
@@ -2138,17 +2237,17 @@ jobsCommand.command("list").description("List jobs, newest first").option("-a, -
2138
2237
  return;
2139
2238
  }
2140
2239
  const depth = Object.entries(counts).map(([status, n]) => `${statusLabel(status)} ${n}`).join(" ");
2141
- console.log(depth || chalk17.gray("No jobs yet."));
2240
+ console.log(depth || chalk18.gray("No jobs yet."));
2142
2241
  if (jobs.length === 0) return;
2143
2242
  console.log();
2144
2243
  for (const job of jobs) {
2145
- const attempts = job.attempts > 0 ? chalk17.gray(` ${job.attempts}/${job.maxAttempts}`) : "";
2244
+ const attempts = job.attempts > 0 ? chalk18.gray(` ${job.attempts}/${job.maxAttempts}`) : "";
2146
2245
  console.log(
2147
- `${statusLabel(job.status).padEnd(20)} ${chalk17.white(job.id)} ${chalk17.gray(
2246
+ `${statusLabel(job.status).padEnd(20)} ${chalk18.white(job.id)} ${chalk18.gray(
2148
2247
  `${job.queue} ${job.path}`
2149
2248
  )}${attempts}`
2150
2249
  );
2151
- if (job.error) console.log(chalk17.red(` ${job.error}`));
2250
+ if (job.error) console.log(chalk18.red(` ${job.error}`));
2152
2251
  }
2153
2252
  });
2154
2253
  jobsCommand.command("get <job-id>").description("Show one job in full").option("-a, --app <id>", "App ID").option("--json", "Print the raw response").action(async (jobId, opts) => {
@@ -2157,17 +2256,17 @@ jobsCommand.command("get <job-id>").description("Show one job in full").option("
2157
2256
  console.log(JSON.stringify(job, null, 2));
2158
2257
  return;
2159
2258
  }
2160
- console.log(`${chalk17.white(job.id)} ${statusLabel(job.status)}`);
2161
- console.log(chalk17.gray(` ${job.method} ${job.path} on queue "${job.queue}"`));
2162
- console.log(chalk17.gray(` Attempts: ${job.attempts}/${job.maxAttempts}`));
2163
- if (job.statusCode) console.log(chalk17.gray(` Last response: HTTP ${job.statusCode}`));
2164
- if (job.durationMs) console.log(chalk17.gray(` Took: ${job.durationMs}ms`));
2165
- if (job.error) console.log(chalk17.red(` Error: ${job.error}`));
2259
+ console.log(`${chalk18.white(job.id)} ${statusLabel(job.status)}`);
2260
+ console.log(chalk18.gray(` ${job.method} ${job.path} on queue "${job.queue}"`));
2261
+ console.log(chalk18.gray(` Attempts: ${job.attempts}/${job.maxAttempts}`));
2262
+ if (job.statusCode) console.log(chalk18.gray(` Last response: HTTP ${job.statusCode}`));
2263
+ if (job.durationMs) console.log(chalk18.gray(` Took: ${job.durationMs}ms`));
2264
+ if (job.error) console.log(chalk18.red(` Error: ${job.error}`));
2166
2265
  if (job.status === "queued" && job.attempts > 0) {
2167
- console.log(chalk17.yellow(` Retrying at ${new Date(job.runAt).toLocaleString()}`));
2266
+ console.log(chalk18.yellow(` Retrying at ${new Date(job.runAt).toLocaleString()}`));
2168
2267
  }
2169
2268
  if (job.payload !== null && job.payload !== void 0) {
2170
- console.log(chalk17.gray(" Payload:"));
2269
+ console.log(chalk18.gray(" Payload:"));
2171
2270
  console.log(
2172
2271
  JSON.stringify(job.payload, null, 2).split("\n").map((l) => ` ${l}`).join("\n")
2173
2272
  );
@@ -2175,16 +2274,16 @@ jobsCommand.command("get <job-id>").description("Show one job in full").option("
2175
2274
  });
2176
2275
  jobsCommand.command("retry <job-id>").description("Run a dead job again, from a clean attempt count").option("-a, --app <id>", "App ID").action(async (jobId, opts) => {
2177
2276
  const job = await client3().jobs.retry(requireApp3(opts.app), jobId);
2178
- console.log(chalk17.green(`Requeued ${job.id}`));
2277
+ console.log(chalk18.green(`Requeued ${job.id}`));
2179
2278
  });
2180
2279
  jobsCommand.command("cancel <job-id>").description("Cancel a job that has not started yet").option("-a, --app <id>", "App ID").action(async (jobId, opts) => {
2181
2280
  const job = await client3().jobs.cancel(requireApp3(opts.app), jobId);
2182
- console.log(chalk17.green(`Cancelled ${job.id}`));
2281
+ console.log(chalk18.green(`Cancelled ${job.id}`));
2183
2282
  });
2184
2283
 
2185
2284
  // src/commands/usage.ts
2186
2285
  import { Command as Command17 } from "commander";
2187
- import chalk18 from "chalk";
2286
+ import chalk19 from "chalk";
2188
2287
  var usageCommand = new Command17("usage").description("Metered usage and cost for the current month").option("-o, --org <id>", "Organization ID").option("-a, --app <id>", "Show one app's share instead of the whole org").option("-p, --period <YYYY-MM>", "Month to report on (default: the month in progress)").option("--json", "Print the raw response").action(async (opts) => {
2189
2288
  const apiKey = getApiKey();
2190
2289
  requireApiKey(apiKey);
@@ -2193,61 +2292,61 @@ var usageCommand = new Command17("usage").description("Metered usage and cost fo
2193
2292
  const appId = opts.app || getCurrentApp();
2194
2293
  const report2 = await client4.apps.usage(appId, opts.period);
2195
2294
  if (opts.json) return void console.log(JSON.stringify(report2, null, 2));
2196
- console.log(chalk18.dim(`${period(report2.periodStart, report2.periodEnd)} app ${appId}`));
2295
+ console.log(chalk19.dim(`${period(report2.periodStart, report2.periodEnd)} app ${appId}`));
2197
2296
  console.log();
2198
2297
  printMetrics(report2.metrics);
2199
2298
  console.log();
2200
2299
  for (const line of report2.lines) {
2201
- console.log(` ${chalk18.dim(line.label.padEnd(22))} ${money(line.cents)}`);
2300
+ console.log(` ${chalk19.dim(line.label.padEnd(22))} ${money(line.cents)}`);
2202
2301
  }
2203
- console.log(` ${chalk18.bold("Share of the bill".padEnd(22))} ${chalk18.bold(report2.cost)}`);
2302
+ console.log(` ${chalk19.bold("Share of the bill".padEnd(22))} ${chalk19.bold(report2.cost)}`);
2204
2303
  console.log();
2205
2304
  console.log(
2206
- chalk18.dim("At list price, with no plan allowance applied \u2014 see `videan usage` for the org total.")
2305
+ chalk19.dim("At list price, with no plan allowance applied \u2014 see `videan usage` for the org total.")
2207
2306
  );
2208
2307
  return;
2209
2308
  }
2210
2309
  const orgId = opts.org || getDefaultOrg();
2211
2310
  if (!orgId) {
2212
- console.error(chalk18.red("No org specified. Use --org, or --app for a single app."));
2311
+ console.error(chalk19.red("No org specified. Use --org, or --app for a single app."));
2213
2312
  process.exit(1);
2214
2313
  }
2215
2314
  const report = await client4.orgs.usage(orgId, opts.period);
2216
2315
  if (opts.json) return void console.log(JSON.stringify(report, null, 2));
2217
2316
  const inv = report.invoice;
2218
2317
  console.log(
2219
- chalk18.dim(`${period(report.periodStart, report.periodEnd)} org ${orgId} plan ${report.plan}`)
2318
+ chalk19.dim(`${period(report.periodStart, report.periodEnd)} org ${orgId} plan ${report.plan}`)
2220
2319
  );
2221
2320
  console.log();
2222
2321
  printMetrics(report.metrics);
2223
2322
  if (report.apps.length > 0) {
2224
2323
  console.log();
2225
- console.log(chalk18.bold("By app"));
2324
+ console.log(chalk19.bold("By app"));
2226
2325
  for (const app of [...report.apps].sort((a, b) => b.cents - a.cents)) {
2227
2326
  const name = app.slug || app.name || app.appId || "(deleted apps)";
2228
- console.log(` ${chalk18.dim(String(name).padEnd(24))} ${money(app.cents)}`);
2327
+ console.log(` ${chalk19.dim(String(name).padEnd(24))} ${money(app.cents)}`);
2229
2328
  }
2230
2329
  }
2231
2330
  console.log();
2232
2331
  for (const line of inv.lines) {
2233
2332
  console.log(
2234
- ` ${chalk18.dim(line.label.padEnd(22))} ${money(line.cents)} ${chalk18.dim(
2333
+ ` ${chalk19.dim(line.label.padEnd(22))} ${money(line.cents)} ${chalk19.dim(
2235
2334
  `(${line.quantity} \xD7 ${line.unitPriceCents}\xA2)`
2236
2335
  )}`
2237
2336
  );
2238
2337
  }
2239
- console.log(` ${chalk18.dim("Usage".padEnd(22))} ${money(inv.usageCents)}`);
2338
+ console.log(` ${chalk19.dim("Usage".padEnd(22))} ${money(inv.usageCents)}`);
2240
2339
  if (inv.includedUsageCents > 0) {
2241
2340
  console.log(
2242
- ` ${chalk18.dim("Included".padEnd(22))} ${chalk18.green(`-${money(Math.min(inv.usageCents, inv.includedUsageCents))}`)}`
2341
+ ` ${chalk19.dim("Included".padEnd(22))} ${chalk19.green(`-${money(Math.min(inv.usageCents, inv.includedUsageCents))}`)}`
2243
2342
  );
2244
2343
  }
2245
2344
  if (inv.baseCents > 0) {
2246
- console.log(` ${chalk18.dim("Plan".padEnd(22))} ${money(inv.baseCents)}`);
2345
+ console.log(` ${chalk19.dim("Plan".padEnd(22))} ${money(inv.baseCents)}`);
2247
2346
  }
2248
- console.log(` ${chalk18.bold("Total".padEnd(22))} ${chalk18.bold(report.total)}`);
2347
+ console.log(` ${chalk19.bold("Total".padEnd(22))} ${chalk19.bold(report.total)}`);
2249
2348
  console.log();
2250
- console.log(chalk18.dim("Usage to date. The period is priced as it stands right now."));
2349
+ console.log(chalk19.dim("Usage to date. The period is priced as it stands right now."));
2251
2350
  });
2252
2351
  usageCommand.command("plans").description("The plan catalogue and what each one allows").option("--json", "Print the raw response").action(async (opts) => {
2253
2352
  const client4 = new CloudClient({ apiKey: getApiKey() || "", baseUrl: getBaseUrl() });
@@ -2255,11 +2354,11 @@ usageCommand.command("plans").description("The plan catalogue and what each one
2255
2354
  if (opts.json) return void console.log(JSON.stringify(plans, null, 2));
2256
2355
  for (const plan of plans) {
2257
2356
  console.log(
2258
- `${chalk18.bold(plan.name)} ${chalk18.dim(`(${plan.id})`)} ${plan.base}/mo` + (plan.includedUsageCents > 0 ? chalk18.dim(`, ${plan.includedUsage} usage included`) : "")
2357
+ `${chalk19.bold(plan.name)} ${chalk19.dim(`(${plan.id})`)} ${plan.base}/mo` + (plan.includedUsageCents > 0 ? chalk19.dim(`, ${plan.includedUsage} usage included`) : "")
2259
2358
  );
2260
2359
  const l = plan.limits;
2261
2360
  console.log(
2262
- chalk18.dim(
2361
+ chalk19.dim(
2263
2362
  ` ${l.apps} apps \xB7 ${l.instancesPerApp} instances/app \xB7 up to ${l.maxCpu} CPU / ${l.maxMemory} MB \xB7 ${l.maxStorageMb} MB storage${l.postgres ? " \xB7 postgres" : ""}${l.customDomains ? " \xB7 custom domains" : ""}`
2264
2363
  )
2265
2364
  );
@@ -2268,11 +2367,11 @@ usageCommand.command("plans").description("The plan catalogue and what each one
2268
2367
  });
2269
2368
  function printMetrics(metrics) {
2270
2369
  if (metrics.length === 0) {
2271
- console.log(chalk18.dim(" Nothing metered in this period yet."));
2370
+ console.log(chalk19.dim(" Nothing metered in this period yet."));
2272
2371
  return;
2273
2372
  }
2274
2373
  for (const m of metrics) {
2275
- console.log(` ${chalk18.dim(m.metric.padEnd(22))} ${round(m.quantity)}`);
2374
+ console.log(` ${chalk19.dim(m.metric.padEnd(22))} ${round(m.quantity)}`);
2276
2375
  }
2277
2376
  }
2278
2377
  function round(n) {
@@ -2287,8 +2386,9 @@ function period(start, end) {
2287
2386
  }
2288
2387
 
2289
2388
  // src/index.ts
2389
+ installPromptCrashGuard();
2290
2390
  var program = new Command18();
2291
- program.name("videan").description("Videan CLI \u2014 deploy apps in seconds").version("0.4.0");
2391
+ program.name("videan").description("Videan CLI \u2014 deploy apps in seconds").version("0.4.2");
2292
2392
  program.addCommand(loginCommand);
2293
2393
  program.addCommand(logoutCommand);
2294
2394
  program.addCommand(initCommand);