@videan/cli 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { Command as Command17 } from "commander";
4
+ import { Command as Command18 } from "commander";
5
5
 
6
6
  // src/commands/login.ts
7
7
  import { Command } from "commander";
@@ -120,8 +120,8 @@ var CloudClient = class {
120
120
  const res = await this.request("GET", "/plans");
121
121
  return res.data;
122
122
  }
123
- async request(method, path5, body) {
124
- const url = `${this.baseUrl}${path5}`;
123
+ async request(method, path6, body) {
124
+ const url = `${this.baseUrl}${path6}`;
125
125
  const headers = {
126
126
  "Authorization": `Bearer ${this.apiKey}`,
127
127
  "Content-Type": "application/json"
@@ -290,8 +290,8 @@ var AppsNamespace = class {
290
290
  if (opts?.instance) params.set("instance", opts.instance);
291
291
  if (opts?.nextToken) params.set("next_token", opts.nextToken);
292
292
  const query = params.toString();
293
- const path5 = `/apps/${appId}/logs${query ? `?${query}` : ""}`;
294
- const res = await this.client.request("GET", path5);
293
+ const path6 = `/apps/${appId}/logs${query ? `?${query}` : ""}`;
294
+ const res = await this.client.request("GET", path6);
295
295
  return res.data;
296
296
  }
297
297
  /**
@@ -1210,19 +1210,94 @@ var deployCommand = new Command4("deploy").description("Deploy the current direc
1210
1210
  }
1211
1211
  });
1212
1212
 
1213
- // src/commands/logs.ts
1213
+ // src/commands/pull.ts
1214
1214
  import { Command as Command5 } from "commander";
1215
1215
  import chalk6 from "chalk";
1216
- var logsCommand = new Command5("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) => {
1216
+ import ora3 from "ora";
1217
+ import fs4 from "fs-extra";
1218
+ import path4 from "node:path";
1219
+ var pullCommand = new Command5("pull").description("Download a snapshot's files into a directory").argument("[snapshot]", "Snapshot id (defaults to the newest)").option("-a, --app <id>", "App ID to pull from").option("-o, --out <dir>", "Directory to write into", ".").option("--force", "Write into a non-empty directory").action(async (snapshotArg, opts) => {
1220
+ const apiKey = getApiKey();
1221
+ requireApiKey(apiKey);
1222
+ const baseUrl = getBaseUrl();
1223
+ const get = async (p) => {
1224
+ const res = await fetch(`${baseUrl}${p}`, {
1225
+ headers: { authorization: `Bearer ${apiKey}` }
1226
+ });
1227
+ if (!res.ok) {
1228
+ const body = await res.json().catch(() => null);
1229
+ throw new Error(body?.error?.message || `${res.status} from ${p}`);
1230
+ }
1231
+ return (await res.json()).data;
1232
+ };
1233
+ const spinner = ora3("Resolving snapshot...").start();
1234
+ try {
1235
+ let snapshotId = snapshotArg ?? "";
1236
+ if (!snapshotId) {
1237
+ const appId = opts.app || getCurrentApp();
1238
+ if (!appId) {
1239
+ spinner.fail(chalk6.red("No app specified. Use --app, VIDEAN_APP_ID, or name a snapshot id."));
1240
+ process.exit(1);
1241
+ }
1242
+ const [latest] = await get(
1243
+ `/v1/apps/${appId}/snapshots?limit=1`
1244
+ );
1245
+ if (!latest) {
1246
+ spinner.fail(chalk6.red("This app has no snapshots yet."));
1247
+ process.exit(1);
1248
+ }
1249
+ snapshotId = latest.id;
1250
+ }
1251
+ const detail = await get(
1252
+ `/v1/snapshots/${snapshotId}`
1253
+ );
1254
+ const outDir = path4.resolve(opts.out);
1255
+ await fs4.ensureDir(outDir);
1256
+ const existing = await fs4.readdir(outDir);
1257
+ if (existing.length > 0 && !opts.force) {
1258
+ spinner.fail(chalk6.red(`${outDir} is not empty. Pass --force to write into it anyway.`));
1259
+ process.exit(1);
1260
+ }
1261
+ spinner.text = `Pulling ${detail.manifest.length} files...`;
1262
+ let written = 0;
1263
+ for (const file of detail.manifest) {
1264
+ const body = await get(
1265
+ `/v1/snapshots/${snapshotId}/files/${file}`
1266
+ );
1267
+ const target = path4.join(outDir, file);
1268
+ if (!target.startsWith(outDir + path4.sep) && target !== outDir) continue;
1269
+ await fs4.ensureDir(path4.dirname(target));
1270
+ await fs4.writeFile(
1271
+ target,
1272
+ body.encoding === "base64" ? Buffer.from(body.content, "base64") : body.content
1273
+ );
1274
+ written++;
1275
+ spinner.text = `Pulling ${written}/${detail.manifest.length}...`;
1276
+ }
1277
+ spinner.succeed(
1278
+ chalk6.green(
1279
+ `Pulled snapshot ${detail.seq ? `#${detail.seq}` : snapshotId} \u2014 ${written} files into ${outDir}`
1280
+ )
1281
+ );
1282
+ } catch (err) {
1283
+ spinner.fail(chalk6.red(err instanceof Error ? err.message : String(err)));
1284
+ process.exit(1);
1285
+ }
1286
+ });
1287
+
1288
+ // src/commands/logs.ts
1289
+ import { Command as Command6 } from "commander";
1290
+ import chalk7 from "chalk";
1291
+ 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) => {
1217
1292
  const apiKey = getApiKey();
1218
1293
  requireApiKey(apiKey);
1219
1294
  const appId = opts.app || getCurrentApp();
1220
1295
  if (!appId) {
1221
- console.error(chalk6.red("No app specified. Use --app or link an app."));
1296
+ console.error(chalk7.red("No app specified. Use --app or link an app."));
1222
1297
  process.exit(1);
1223
1298
  }
1224
1299
  if (opts.search && opts.pattern) {
1225
- console.error(chalk6.red("Use either --search or --pattern, not both."));
1300
+ console.error(chalk7.red("Use either --search or --pattern, not both."));
1226
1301
  process.exit(1);
1227
1302
  }
1228
1303
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
@@ -1245,43 +1320,43 @@ var logsCommand = new Command5("logs").description("View app logs").option("-a,
1245
1320
  });
1246
1321
  for (const entry of page.events) print(entry);
1247
1322
  if (page.events.length === 0) {
1248
- console.log(chalk6.gray(`No log lines since ${page.start}.`));
1323
+ console.log(chalk7.gray(`No log lines since ${page.start}.`));
1249
1324
  }
1250
1325
  if (page.truncated) {
1251
1326
  console.log(
1252
- chalk6.yellow(
1327
+ chalk7.yellow(
1253
1328
  "\nThis is part of the window -- there are more lines than one request reads. Narrow --since or add --search."
1254
1329
  )
1255
1330
  );
1256
1331
  }
1257
1332
  });
1258
1333
  function print(entry) {
1259
- console.log(`${chalk6.gray(entry.timestamp)} ${formatLevel(entry.level)} ${entry.message}`);
1334
+ console.log(`${chalk7.gray(entry.timestamp)} ${formatLevel(entry.level)} ${entry.message}`);
1260
1335
  }
1261
1336
  function formatLevel(level) {
1262
1337
  switch (level) {
1263
1338
  case "error":
1264
- return chalk6.red("[ERR]");
1339
+ return chalk7.red("[ERR]");
1265
1340
  case "warn":
1266
- return chalk6.yellow("[WRN]");
1341
+ return chalk7.yellow("[WRN]");
1267
1342
  case "info":
1268
- return chalk6.blue("[INF]");
1343
+ return chalk7.blue("[INF]");
1269
1344
  case "debug":
1270
- return chalk6.gray("[DBG]");
1345
+ return chalk7.gray("[DBG]");
1271
1346
  default:
1272
- return chalk6.gray("[LOG]");
1347
+ return chalk7.gray("[LOG]");
1273
1348
  }
1274
1349
  }
1275
1350
 
1276
1351
  // src/commands/metrics.ts
1277
- import { Command as Command6 } from "commander";
1278
- import chalk7 from "chalk";
1279
- var metricsCommand = new Command6("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) => {
1352
+ import { Command as Command7 } from "commander";
1353
+ import chalk8 from "chalk";
1354
+ 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) => {
1280
1355
  const apiKey = getApiKey();
1281
1356
  requireApiKey(apiKey);
1282
1357
  const appId = opts.app || getCurrentApp();
1283
1358
  if (!appId) {
1284
- console.error(chalk7.red("No app specified. Use --app or link an app."));
1359
+ console.error(chalk8.red("No app specified. Use --app or link an app."));
1285
1360
  process.exit(1);
1286
1361
  }
1287
1362
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
@@ -1292,7 +1367,7 @@ var metricsCommand = new Command6("metrics").description("Traffic, latency and r
1292
1367
  }
1293
1368
  const t = metrics.totals;
1294
1369
  console.log(
1295
- chalk7.dim(`${metrics.start} \u2192 ${metrics.end} (${metrics.period}s buckets)`)
1370
+ chalk8.dim(`${metrics.start} \u2192 ${metrics.end} (${metrics.period}s buckets)`)
1296
1371
  );
1297
1372
  console.log();
1298
1373
  console.log(` ${label("Requests")} ${t.requests}`);
@@ -1300,76 +1375,76 @@ var metricsCommand = new Command6("metrics").description("Traffic, latency and r
1300
1375
  console.log(` ${label("5xx")} ${count(t.serverErrors, true)}`);
1301
1376
  console.log(` ${label("Gate denied")} ${count(t.gateDenied)}`);
1302
1377
  console.log(` ${label("Latency avg")} ${ms(t.latencyAvgMs)}`);
1303
- console.log(` ${label("Latency p95")} ${ms(t.latencyP95Ms)} ${chalk7.dim("(sampled)")}`);
1378
+ console.log(` ${label("Latency p95")} ${ms(t.latencyP95Ms)} ${chalk8.dim("(sampled)")}`);
1304
1379
  console.log(` ${label("CPU avg")} ${units(t.cpuAvg)}`);
1305
1380
  console.log(` ${label("Memory avg")} ${mb(t.memoryAvgMb)}`);
1306
1381
  if (t.requests === 0) {
1307
1382
  console.log();
1308
- console.log(chalk7.dim("No requests recorded in this window."));
1383
+ console.log(chalk8.dim("No requests recorded in this window."));
1309
1384
  }
1310
1385
  if (opts.series) {
1311
1386
  console.log();
1312
1387
  for (const [name, points] of Object.entries(metrics.series)) {
1313
1388
  if (points.length === 0) continue;
1314
- console.log(chalk7.bold(name));
1315
- for (const p of points) console.log(` ${chalk7.dim(p.t)} ${p.v}`);
1389
+ console.log(chalk8.bold(name));
1390
+ for (const p of points) console.log(` ${chalk8.dim(p.t)} ${p.v}`);
1316
1391
  }
1317
1392
  }
1318
1393
  });
1319
1394
  function label(text) {
1320
- return chalk7.dim(text.padEnd(13));
1395
+ return chalk8.dim(text.padEnd(13));
1321
1396
  }
1322
1397
  function count(n, severe = false) {
1323
- if (n === 0) return chalk7.dim("0");
1324
- return severe ? chalk7.red(String(n)) : chalk7.yellow(String(n));
1398
+ if (n === 0) return chalk8.dim("0");
1399
+ return severe ? chalk8.red(String(n)) : chalk8.yellow(String(n));
1325
1400
  }
1326
1401
  function ms(v) {
1327
- return v === null ? chalk7.dim("\u2014") : `${v} ms`;
1402
+ return v === null ? chalk8.dim("\u2014") : `${v} ms`;
1328
1403
  }
1329
1404
  function mb(v) {
1330
- return v === null ? chalk7.dim("\u2014") : `${v} MB`;
1405
+ return v === null ? chalk8.dim("\u2014") : `${v} MB`;
1331
1406
  }
1332
1407
  function units(v) {
1333
- return v === null ? chalk7.dim("\u2014") : `${v} CPU units`;
1408
+ return v === null ? chalk8.dim("\u2014") : `${v} CPU units`;
1334
1409
  }
1335
1410
 
1336
1411
  // src/commands/rollback.ts
1337
- import { Command as Command7 } from "commander";
1338
- import chalk8 from "chalk";
1339
- import ora3 from "ora";
1340
- var rollbackCommand = new Command7("rollback").description("Rollback to the previous production deployment").option("-a, --app <id>", "App ID").action(async (opts) => {
1412
+ import { Command as Command8 } from "commander";
1413
+ import chalk9 from "chalk";
1414
+ import ora4 from "ora";
1415
+ var rollbackCommand = new Command8("rollback").description("Rollback to the previous production deployment").option("-a, --app <id>", "App ID").action(async (opts) => {
1341
1416
  const apiKey = getApiKey();
1342
1417
  requireApiKey(apiKey);
1343
1418
  const appId = opts.app || getCurrentApp();
1344
1419
  if (!appId) {
1345
- console.error(chalk8.red("No app specified. Use --app or link an app."));
1420
+ console.error(chalk9.red("No app specified. Use --app or link an app."));
1346
1421
  process.exit(1);
1347
1422
  }
1348
1423
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1349
- const spinner = ora3("Rolling back...").start();
1424
+ const spinner = ora4("Rolling back...").start();
1350
1425
  try {
1351
1426
  const deployment = await client4.apps.rollback(appId);
1352
- spinner.succeed(chalk8.green("Rollback successful!"));
1427
+ spinner.succeed(chalk9.green("Rollback successful!"));
1353
1428
  if (deployment.url) {
1354
- console.log(chalk8.cyan(` URL: ${deployment.url}`));
1429
+ console.log(chalk9.cyan(` URL: ${deployment.url}`));
1355
1430
  }
1356
1431
  } catch (error) {
1357
- spinner.fail(chalk8.red(`Rollback failed: ${error.message}`));
1432
+ spinner.fail(chalk9.red(`Rollback failed: ${error.message}`));
1358
1433
  process.exit(1);
1359
1434
  }
1360
1435
  });
1361
1436
 
1362
1437
  // src/commands/secrets.ts
1363
- import { Command as Command8 } from "commander";
1364
- import chalk9 from "chalk";
1438
+ import { Command as Command9 } from "commander";
1439
+ import chalk10 from "chalk";
1365
1440
  import inquirer3 from "inquirer";
1366
- var secretsCommand = new Command8("secrets").description("Manage app secrets");
1441
+ var secretsCommand = new Command9("secrets").description("Manage app secrets");
1367
1442
  secretsCommand.command("set <key> [value]").description("Set a secret").option("-a, --app <id>", "App ID").action(async (key, value, opts) => {
1368
1443
  const apiKey = getApiKey();
1369
1444
  requireApiKey(apiKey);
1370
1445
  const appId = opts.app || getCurrentApp();
1371
1446
  if (!appId) {
1372
- console.error(chalk9.red("No app specified."));
1447
+ console.error(chalk10.red("No app specified."));
1373
1448
  process.exit(1);
1374
1449
  }
1375
1450
  if (!value) {
@@ -1380,20 +1455,20 @@ secretsCommand.command("set <key> [value]").description("Set a secret").option("
1380
1455
  }
1381
1456
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1382
1457
  await client4.secrets.set(appId, key, value);
1383
- console.log(chalk9.green(`Secret "${key}" set successfully.`));
1458
+ console.log(chalk10.green(`Secret "${key}" set successfully.`));
1384
1459
  });
1385
1460
  secretsCommand.command("list").description("List all secrets").option("-a, --app <id>", "App ID").action(async (opts) => {
1386
1461
  const apiKey = getApiKey();
1387
1462
  requireApiKey(apiKey);
1388
1463
  const appId = opts.app || getCurrentApp();
1389
1464
  if (!appId) {
1390
- console.error(chalk9.red("No app specified."));
1465
+ console.error(chalk10.red("No app specified."));
1391
1466
  process.exit(1);
1392
1467
  }
1393
1468
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1394
1469
  const secrets = await client4.secrets.list(appId);
1395
1470
  if (secrets.length === 0) {
1396
- console.log(chalk9.gray("No secrets configured."));
1471
+ console.log(chalk10.gray("No secrets configured."));
1397
1472
  return;
1398
1473
  }
1399
1474
  console.log(formatRow(["KEY", "UPDATED"], [30, 24]));
@@ -1406,24 +1481,24 @@ secretsCommand.command("remove <key>").description("Remove a secret").option("-a
1406
1481
  requireApiKey(apiKey);
1407
1482
  const appId = opts.app || getCurrentApp();
1408
1483
  if (!appId) {
1409
- console.error(chalk9.red("No app specified."));
1484
+ console.error(chalk10.red("No app specified."));
1410
1485
  process.exit(1);
1411
1486
  }
1412
1487
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1413
1488
  await client4.secrets.delete(appId, key);
1414
- console.log(chalk9.green(`Secret "${key}" removed.`));
1489
+ console.log(chalk10.green(`Secret "${key}" removed.`));
1415
1490
  });
1416
1491
 
1417
1492
  // src/commands/apps.ts
1418
- import { Command as Command9 } from "commander";
1419
- import chalk10 from "chalk";
1493
+ import { Command as Command10 } from "commander";
1494
+ import chalk11 from "chalk";
1420
1495
  import inquirer4 from "inquirer";
1421
- var appsCommand = new Command9("apps").description("Manage apps");
1496
+ var appsCommand = new Command10("apps").description("Manage apps");
1422
1497
  function accessOption(value) {
1423
1498
  if (value === void 0) return void 0;
1424
1499
  const tier = value.trim().toLowerCase();
1425
1500
  if (!isVisibility(tier)) {
1426
- console.error(chalk10.red(`Unknown --access value "${value}". Use one of: ${VISIBILITIES.join(", ")}`));
1501
+ console.error(chalk11.red(`Unknown --access value "${value}". Use one of: ${VISIBILITIES.join(", ")}`));
1427
1502
  process.exit(1);
1428
1503
  }
1429
1504
  return tier;
@@ -1439,7 +1514,7 @@ appsCommand.command("list").description("List all apps").option("-o, --org <id>"
1439
1514
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1440
1515
  const apps = await client4.apps.list(orgId);
1441
1516
  if (apps.length === 0) {
1442
- console.log(chalk10.gray("No apps yet. Create one with `videan apps create`."));
1517
+ console.log(chalk11.gray("No apps yet. Create one with `videan apps create`."));
1443
1518
  return;
1444
1519
  }
1445
1520
  const widths = [20, 24, 10, 44];
@@ -1461,7 +1536,7 @@ appsCommand.command("create").description("Create a new app").option("-n, --name
1461
1536
  name = answers.name;
1462
1537
  }
1463
1538
  if (!name) {
1464
- console.error(chalk10.red("An app name is required."));
1539
+ console.error(chalk11.red("An app name is required."));
1465
1540
  process.exit(1);
1466
1541
  }
1467
1542
  const visibility = accessOption(opts.access) ?? (opts.public ? "public" : void 0);
@@ -1475,10 +1550,10 @@ appsCommand.command("create").description("Create a new app").option("-n, --name
1475
1550
  visibility
1476
1551
  });
1477
1552
  setCurrentApp(app.id);
1478
- console.log(chalk10.green(`App "${app.name}" created (${app.id})`));
1479
- console.log(chalk10.gray(` Access: ${describeAccess(app)}`));
1480
- console.log(chalk10.gray(` URL after first deploy: ${appUrl(app.slug)}`));
1481
- console.log(chalk10.gray(" Push code with `videan deploy` \u2014 the runtime is auto-detected."));
1553
+ console.log(chalk11.green(`App "${app.name}" created (${app.id})`));
1554
+ console.log(chalk11.gray(` Access: ${describeAccess(app)}`));
1555
+ console.log(chalk11.gray(` URL after first deploy: ${appUrl(app.slug)}`));
1556
+ console.log(chalk11.gray(" Push code with `videan deploy` \u2014 the runtime is auto-detected."));
1482
1557
  }
1483
1558
  );
1484
1559
  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("--json", "Print the raw response").action(
@@ -1487,16 +1562,16 @@ appsCommand.command("set").description("Change an app's settings (scale, rate li
1487
1562
  requireApiKey(apiKey);
1488
1563
  const appId = opts.app || getCurrentApp();
1489
1564
  if (!appId) {
1490
- console.error(chalk10.red("No app specified. Use --app or link an app."));
1565
+ console.error(chalk11.red("No app specified. Use --app or link an app."));
1491
1566
  process.exit(1);
1492
1567
  }
1493
1568
  if (opts.public && opts.private) {
1494
- console.error(chalk10.red("--public and --private contradict each other."));
1569
+ console.error(chalk11.red("--public and --private contradict each other."));
1495
1570
  process.exit(1);
1496
1571
  }
1497
1572
  const access = accessOption(opts.access);
1498
1573
  if (access && (opts.public || opts.private)) {
1499
- console.error(chalk10.red("--access already says who may open the app; drop --public/--private."));
1574
+ console.error(chalk11.red("--access already says who may open the app; drop --public/--private."));
1500
1575
  process.exit(1);
1501
1576
  }
1502
1577
  const changes = {};
@@ -1517,7 +1592,7 @@ appsCommand.command("set").description("Change an app's settings (scale, rate li
1517
1592
  if (opts.storage) changes.storage = true;
1518
1593
  if (opts.email) changes.email = true;
1519
1594
  if (Object.keys(changes).length === 0) {
1520
- console.error(chalk10.red("Nothing to change. Pass at least one option."));
1595
+ console.error(chalk11.red("Nothing to change. Pass at least one option."));
1521
1596
  process.exit(1);
1522
1597
  }
1523
1598
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
@@ -1526,7 +1601,7 @@ appsCommand.command("set").description("Change an app's settings (scale, rate li
1526
1601
  console.log(JSON.stringify(app, null, 2));
1527
1602
  return;
1528
1603
  }
1529
- console.log(chalk10.green(`Updated ${app.name} (${app.id})`));
1604
+ console.log(chalk11.green(`Updated ${app.name} (${app.id})`));
1530
1605
  console.log(` Access: ${describeAccess(app)}`);
1531
1606
  console.log(` Scale: ${app.cpu ?? "default"} CPU / ${app.memory ?? "default"} MiB`);
1532
1607
  console.log(
@@ -1534,7 +1609,7 @@ appsCommand.command("set").description("Change an app's settings (scale, rate li
1534
1609
  );
1535
1610
  if (app.restartRequired) {
1536
1611
  console.log(
1537
- chalk10.yellow("\n These settings apply to the next deploy. Run `videan deploy` to pick them up.")
1612
+ chalk11.yellow("\n These settings apply to the next deploy. Run `videan deploy` to pick them up.")
1538
1613
  );
1539
1614
  }
1540
1615
  }
@@ -1542,7 +1617,7 @@ appsCommand.command("set").description("Change an app's settings (scale, rate li
1542
1617
  function intOption(flag, raw) {
1543
1618
  const n = Number(raw);
1544
1619
  if (!Number.isInteger(n) || n <= 0) {
1545
- console.error(chalk10.red(`${flag} expects a positive whole number, got "${raw}".`));
1620
+ console.error(chalk11.red(`${flag} expects a positive whole number, got "${raw}".`));
1546
1621
  process.exit(1);
1547
1622
  }
1548
1623
  return n;
@@ -1561,64 +1636,64 @@ appsCommand.command("delete <id>").description("Delete an app and all its resour
1561
1636
  if (!confirm) return;
1562
1637
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1563
1638
  const { warnings } = await client4.apps.delete(id);
1564
- console.log(chalk10.green("App deleted."));
1639
+ console.log(chalk11.green("App deleted."));
1565
1640
  if (warnings.length > 0) {
1566
- console.log(chalk10.yellow("\nSome resources could not be released:"));
1641
+ console.log(chalk11.yellow("\nSome resources could not be released:"));
1567
1642
  for (const w of warnings) {
1568
- console.log(chalk10.yellow(` ${w.step}: ${w.error || "unknown error"}`));
1643
+ console.log(chalk11.yellow(` ${w.step}: ${w.error || "unknown error"}`));
1569
1644
  }
1570
1645
  }
1571
1646
  });
1572
1647
 
1573
1648
  // src/commands/status.ts
1574
- import { Command as Command10 } from "commander";
1575
- import chalk11 from "chalk";
1649
+ import { Command as Command11 } from "commander";
1650
+ import chalk12 from "chalk";
1576
1651
  function accessLine(app) {
1577
1652
  const tier = app.visibility ? visibilityOf(app.visibility) : app.authRequired ? "org" : "public";
1578
1653
  return `${tier} \u2014 ${VISIBILITY_LABELS[tier].toLowerCase()}`;
1579
1654
  }
1580
- var statusCommand = new Command10("status").description("Show current app status").option("-a, --app <id>", "App ID").action(async (opts) => {
1655
+ var statusCommand = new Command11("status").description("Show current app status").option("-a, --app <id>", "App ID").action(async (opts) => {
1581
1656
  const apiKey = getApiKey();
1582
1657
  requireApiKey(apiKey);
1583
1658
  const appId = opts.app || getCurrentApp();
1584
1659
  if (!appId) {
1585
- console.error(chalk11.red("No app specified. Use --app or link an app."));
1660
+ console.error(chalk12.red("No app specified. Use --app or link an app."));
1586
1661
  process.exit(1);
1587
1662
  }
1588
1663
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1589
1664
  const app = await client4.apps.get(appId);
1590
1665
  const health = await client4.apps.health(appId);
1591
- console.log(chalk11.bold(app.name));
1666
+ console.log(chalk12.bold(app.name));
1592
1667
  console.log(` ID: ${app.id}`);
1593
1668
  console.log(` Created: ${app.createdAt}`);
1594
1669
  console.log(` Postgres: ${app.postgresEnabled ? "enabled" : "disabled"}`);
1595
1670
  console.log(` Access: ${accessLine(app)}`);
1596
1671
  console.log(` State: ${formatStatus(health.state)}`);
1597
- console.log(` Healthy: ${health.healthy ? chalk11.green("yes") : chalk11.red("no")}`);
1598
- console.log(` URL: ${chalk11.cyan(health.url || appUrl(app.slug))}`);
1672
+ console.log(` Healthy: ${health.healthy ? chalk12.green("yes") : chalk12.red("no")}`);
1673
+ console.log(` URL: ${chalk12.cyan(health.url || appUrl(app.slug))}`);
1599
1674
  const task = health.task;
1600
1675
  if (task) {
1601
1676
  if (task.exit_code !== void 0 && task.exit_code !== null) {
1602
1677
  const code = String(task.exit_code);
1603
- console.log(` Exit code: ${task.exit_code === 0 ? code : chalk11.red(code)}`);
1678
+ console.log(` Exit code: ${task.exit_code === 0 ? code : chalk12.red(code)}`);
1604
1679
  }
1605
1680
  if (task.stopped_reason) {
1606
- console.log(` Stopped: ${chalk11.yellow(task.stopped_reason)}`);
1681
+ console.log(` Stopped: ${chalk12.yellow(task.stopped_reason)}`);
1607
1682
  }
1608
1683
  if (task.container_reason) {
1609
- console.log(` Detail: ${chalk11.yellow(task.container_reason)}`);
1684
+ console.log(` Detail: ${chalk12.yellow(task.container_reason)}`);
1610
1685
  }
1611
1686
  }
1612
1687
  if (!health.healthy) {
1613
- console.log(chalk11.gray("\nRun `videan logs` to see why."));
1688
+ console.log(chalk12.gray("\nRun `videan logs` to see why."));
1614
1689
  }
1615
1690
  });
1616
1691
 
1617
1692
  // src/commands/keys.ts
1618
- import { Command as Command11 } from "commander";
1619
- import chalk12 from "chalk";
1693
+ import { Command as Command12 } from "commander";
1694
+ import chalk13 from "chalk";
1620
1695
  import inquirer5 from "inquirer";
1621
- var keysCommand = new Command11("keys").description("Manage API keys for programmatic access");
1696
+ var keysCommand = new Command12("keys").description("Manage API keys for programmatic access");
1622
1697
  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(
1623
1698
  "--app <id>",
1624
1699
  "Pin the key to one app (a service token, for code running inside it)"
@@ -1630,7 +1705,7 @@ keysCommand.command("create <name>").description("Create an API key").option("-o
1630
1705
  if (opts.expires !== void 0) {
1631
1706
  expiresInDays = Number(opts.expires);
1632
1707
  if (!Number.isInteger(expiresInDays) || expiresInDays < 1) {
1633
- console.error(chalk12.red("--expires must be a whole number of days, at least 1."));
1708
+ console.error(chalk13.red("--expires must be a whole number of days, at least 1."));
1634
1709
  process.exit(1);
1635
1710
  }
1636
1711
  }
@@ -1641,22 +1716,22 @@ keysCommand.command("create <name>").description("Create an API key").option("-o
1641
1716
  expiresInDays,
1642
1717
  appId: opts.app
1643
1718
  });
1644
- console.log(chalk12.green(`Created key "${key.name}" (${key.id})`));
1719
+ console.log(chalk13.green(`Created key "${key.name}" (${key.id})`));
1645
1720
  console.log(`
1646
- ${chalk12.bold(key.key)}
1721
+ ${chalk13.bold(key.key)}
1647
1722
  `);
1648
- console.log(chalk12.yellow("Copy it now. It cannot be shown again."));
1723
+ console.log(chalk13.yellow("Copy it now. It cannot be shown again."));
1649
1724
  if (key.appId) {
1650
1725
  console.log(
1651
- chalk12.gray(`Service token, scoped to app ${key.appId}. It cannot reach anything else.`)
1726
+ chalk13.gray(`Service token, scoped to app ${key.appId}. It cannot reach anything else.`)
1652
1727
  );
1653
1728
  } else {
1654
1729
  console.log(
1655
- chalk12.gray("Acts as you across every app in the org -- do not deploy it inside an app.")
1730
+ chalk13.gray("Acts as you across every app in the org -- do not deploy it inside an app.")
1656
1731
  );
1657
1732
  }
1658
1733
  if (key.expiresAt) {
1659
- console.log(chalk12.gray(`Expires ${key.expiresAt}`));
1734
+ console.log(chalk13.gray(`Expires ${key.expiresAt}`));
1660
1735
  }
1661
1736
  });
1662
1737
  keysCommand.command("list").description("List API keys").option("-o, --org <id>", "Organization ID").action(async (opts) => {
@@ -1666,13 +1741,13 @@ keysCommand.command("list").description("List API keys").option("-o, --org <id>"
1666
1741
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1667
1742
  const keys = await client4.apiKeys.list(orgId);
1668
1743
  if (keys.length === 0) {
1669
- console.log(chalk12.gray("No API keys. Create one with `videan keys create <name>`."));
1744
+ console.log(chalk13.gray("No API keys. Create one with `videan keys create <name>`."));
1670
1745
  return;
1671
1746
  }
1672
1747
  const widths = [24, 24, 16, 22, 12, 24];
1673
1748
  console.log(formatRow(["NAME", "ID", "PREFIX", "LAST USED", "STATE", "SCOPE"], widths));
1674
1749
  for (const key of keys) {
1675
- const state = key.revokedAt ? chalk12.red("revoked") : key.expiresAt && new Date(key.expiresAt) < /* @__PURE__ */ new Date() ? chalk12.yellow("expired") : chalk12.green("active");
1750
+ const state = key.revokedAt ? chalk13.red("revoked") : key.expiresAt && new Date(key.expiresAt) < /* @__PURE__ */ new Date() ? chalk13.yellow("expired") : chalk13.green("active");
1676
1751
  console.log(formatRow(
1677
1752
  [
1678
1753
  key.name,
@@ -1695,13 +1770,13 @@ keysCommand.command("revoke <id>").description("Revoke an API key").action(async
1695
1770
  if (!confirm) return;
1696
1771
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1697
1772
  await client4.apiKeys.revoke(id);
1698
- console.log(chalk12.green("Key revoked."));
1773
+ console.log(chalk13.green("Key revoked."));
1699
1774
  });
1700
1775
 
1701
1776
  // src/commands/orgs.ts
1702
- import { Command as Command12 } from "commander";
1703
- import chalk13 from "chalk";
1704
- var orgsCommand = new Command12("orgs").description("Manage organizations");
1777
+ import { Command as Command13 } from "commander";
1778
+ import chalk14 from "chalk";
1779
+ var orgsCommand = new Command13("orgs").description("Manage organizations");
1705
1780
  orgsCommand.command("list").description("List your organizations").action(async () => {
1706
1781
  const apiKey = getApiKey();
1707
1782
  requireApiKey(apiKey);
@@ -1709,14 +1784,14 @@ orgsCommand.command("list").description("List your organizations").action(async
1709
1784
  const orgs = await client4.orgs.list();
1710
1785
  const current = getDefaultOrg();
1711
1786
  if (orgs.length === 0) {
1712
- console.log(chalk13.gray("No organizations. Create one with `videan orgs create <name>`."));
1787
+ console.log(chalk14.gray("No organizations. Create one with `videan orgs create <name>`."));
1713
1788
  return;
1714
1789
  }
1715
1790
  const widths = [24, 24, 10, 8];
1716
1791
  console.log(formatRow(["NAME", "ID", "PLAN", ""], widths));
1717
1792
  for (const org of orgs) {
1718
1793
  console.log(formatRow(
1719
- [org.name, org.id, org.plan, org.id === current ? chalk13.green("default") : ""],
1794
+ [org.name, org.id, org.plan, org.id === current ? chalk14.green("default") : ""],
1720
1795
  widths
1721
1796
  ));
1722
1797
  }
@@ -1727,7 +1802,7 @@ orgsCommand.command("create <name>").description("Create an organization").actio
1727
1802
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1728
1803
  const org = await client4.orgs.create(name);
1729
1804
  if (!getDefaultOrg()) setDefaultOrg(org.id);
1730
- console.log(chalk13.green(`Organization "${org.name}" created (${org.id})`));
1805
+ console.log(chalk14.green(`Organization "${org.name}" created (${org.id})`));
1731
1806
  });
1732
1807
  orgsCommand.command("use <id>").description("Set the default organization for this machine").action(async (id) => {
1733
1808
  const apiKey = getApiKey();
@@ -1735,7 +1810,7 @@ orgsCommand.command("use <id>").description("Set the default organization for th
1735
1810
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1736
1811
  const org = await client4.orgs.get(id);
1737
1812
  setDefaultOrg(org.id);
1738
- console.log(chalk13.green(`Default organization set to "${org.name}".`));
1813
+ console.log(chalk14.green(`Default organization set to "${org.name}".`));
1739
1814
  });
1740
1815
  orgsCommand.command("members").description("List organization members").option("-o, --org <id>", "Organization ID").action(async (opts) => {
1741
1816
  const apiKey = getApiKey();
@@ -1758,15 +1833,15 @@ orgsCommand.command("invite <email>").description("Invite a member by email").op
1758
1833
  const orgId = requireOrg(opts.org || getDefaultOrg());
1759
1834
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1760
1835
  await client4.orgs.invite(orgId, email);
1761
- console.log(chalk13.green(`Invited ${email}.`));
1836
+ console.log(chalk14.green(`Invited ${email}.`));
1762
1837
  });
1763
1838
 
1764
1839
  // src/commands/storage.ts
1765
- import { Command as Command13 } from "commander";
1766
- import chalk14 from "chalk";
1767
- import fs4 from "fs-extra";
1768
- import path4 from "node:path";
1769
- var storageCommand = new Command13("storage").description("Store files for an app");
1840
+ import { Command as Command14 } from "commander";
1841
+ import chalk15 from "chalk";
1842
+ import fs5 from "fs-extra";
1843
+ import path5 from "node:path";
1844
+ var storageCommand = new Command14("storage").description("Store files for an app");
1770
1845
  function client() {
1771
1846
  const apiKey = getApiKey();
1772
1847
  requireApiKey(apiKey);
@@ -1775,7 +1850,7 @@ function client() {
1775
1850
  function requireApp(opt) {
1776
1851
  const appId = opt || getCurrentApp();
1777
1852
  if (!appId) {
1778
- console.error(chalk14.red("No app specified. Use --app or link an app."));
1853
+ console.error(chalk15.red("No app specified. Use --app or link an app."));
1779
1854
  process.exit(1);
1780
1855
  }
1781
1856
  return appId;
@@ -1798,7 +1873,7 @@ storageCommand.command("ls [prefix]").description("List stored objects").option(
1798
1873
  return;
1799
1874
  }
1800
1875
  if (objects.length === 0) {
1801
- console.log(chalk14.gray("No objects stored. Upload one with `videan storage put <file>`."));
1876
+ console.log(chalk15.gray("No objects stored. Upload one with `videan storage put <file>`."));
1802
1877
  return;
1803
1878
  }
1804
1879
  const widths = [52, 12, 24];
@@ -1807,42 +1882,42 @@ storageCommand.command("ls [prefix]").description("List stored objects").option(
1807
1882
  console.log(formatRow([o.key, human(o.size), o.lastModified || "-"], widths));
1808
1883
  }
1809
1884
  const total = objects.reduce((sum, o) => sum + o.size, 0);
1810
- console.log(chalk14.dim(`
1885
+ console.log(chalk15.dim(`
1811
1886
  ${objects.length} object(s), ${human(total)}`));
1812
1887
  });
1813
1888
  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) => {
1814
1889
  const appId = requireApp(opts.app);
1815
- if (!await fs4.pathExists(file)) {
1816
- console.error(chalk14.red(`No such file: ${file}`));
1890
+ if (!await fs5.pathExists(file)) {
1891
+ console.error(chalk15.red(`No such file: ${file}`));
1817
1892
  process.exit(1);
1818
1893
  }
1819
- const body = await fs4.readFile(file);
1820
- const objectKey = key || path4.basename(file);
1894
+ const body = await fs5.readFile(file);
1895
+ const objectKey = key || path5.basename(file);
1821
1896
  const stored = await client().storage.upload(appId, objectKey, body, {
1822
1897
  contentType: opts.type
1823
1898
  });
1824
1899
  console.log(
1825
- chalk14.green(`Uploaded ${objectKey}`) + chalk14.dim(` (${human(body.length)})`)
1900
+ chalk15.green(`Uploaded ${objectKey}`) + chalk15.dim(` (${human(body.length)})`)
1826
1901
  );
1827
- if (stored.etag) console.log(chalk14.dim(`etag ${stored.etag}`));
1902
+ if (stored.etag) console.log(chalk15.dim(`etag ${stored.etag}`));
1828
1903
  });
1829
1904
  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) => {
1830
1905
  const appId = requireApp(opts.app);
1831
1906
  const bytes = await client().storage.download(appId, key);
1832
- const dest = file || path4.basename(key);
1833
- await fs4.writeFile(dest, bytes);
1834
- console.log(chalk14.green(`Wrote ${dest}`) + chalk14.dim(` (${human(bytes.length)})`));
1907
+ const dest = file || path5.basename(key);
1908
+ await fs5.writeFile(dest, bytes);
1909
+ console.log(chalk15.green(`Wrote ${dest}`) + chalk15.dim(` (${human(bytes.length)})`));
1835
1910
  });
1836
1911
  storageCommand.command("rm <key>").description("Delete an object").option("-a, --app <id>", "App ID").action(async (key, opts) => {
1837
1912
  const appId = requireApp(opts.app);
1838
1913
  await client().storage.delete(appId, key);
1839
- console.log(chalk14.green(`Deleted ${key}`));
1914
+ console.log(chalk15.green(`Deleted ${key}`));
1840
1915
  });
1841
1916
  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) => {
1842
1917
  const appId = requireApp(opts.app);
1843
1918
  const expiresIn = Number(opts.expires);
1844
1919
  if (!Number.isInteger(expiresIn) || expiresIn < 30 || expiresIn > 3600) {
1845
- console.error(chalk14.red("--expires must be a whole number of seconds between 30 and 3600."));
1920
+ console.error(chalk15.red("--expires must be a whole number of seconds between 30 and 3600."));
1846
1921
  process.exit(1);
1847
1922
  }
1848
1923
  const signed = await client().storage.presign(appId, key, {
@@ -1852,7 +1927,7 @@ storageCommand.command("url <key>").description("Print a temporary link to an ob
1852
1927
  });
1853
1928
  console.log(signed.url);
1854
1929
  console.error(
1855
- chalk14.yellow(
1930
+ chalk15.yellow(
1856
1931
  `This ${signed.method} link works for ${expiresIn}s for anyone who has it.`
1857
1932
  )
1858
1933
  );
@@ -1864,10 +1939,10 @@ storageCommand.command("usage").description("Total objects and bytes stored").op
1864
1939
  });
1865
1940
 
1866
1941
  // src/commands/email.ts
1867
- import { Command as Command14 } from "commander";
1868
- import chalk15 from "chalk";
1869
- import fs5 from "fs-extra";
1870
- var emailCommand = new Command14("email").description("Send email from an app");
1942
+ import { Command as Command15 } from "commander";
1943
+ import chalk16 from "chalk";
1944
+ import fs6 from "fs-extra";
1945
+ var emailCommand = new Command15("email").description("Send email from an app");
1871
1946
  function client2() {
1872
1947
  const apiKey = getApiKey();
1873
1948
  requireApiKey(apiKey);
@@ -1876,7 +1951,7 @@ function client2() {
1876
1951
  function requireApp2(opt) {
1877
1952
  const appId = opt || getCurrentApp();
1878
1953
  if (!appId) {
1879
- console.error(chalk15.red("No app specified. Use --app or link an app."));
1954
+ console.error(chalk16.red("No app specified. Use --app or link an app."));
1880
1955
  process.exit(1);
1881
1956
  }
1882
1957
  return appId;
@@ -1887,12 +1962,12 @@ emailCommand.command("send").description("Send an email").requiredOption("-t, --
1887
1962
  let text = opts.text;
1888
1963
  let html = opts.html;
1889
1964
  if (opts.bodyFile) {
1890
- const contents = await fs5.readFile(opts.bodyFile, "utf-8");
1965
+ const contents = await fs6.readFile(opts.bodyFile, "utf-8");
1891
1966
  if (/\.html?$/i.test(opts.bodyFile)) html = contents;
1892
1967
  else text = contents;
1893
1968
  }
1894
1969
  if (!text && !html) {
1895
- console.error(chalk15.red("Nothing to send. Pass --text, --html or --body-file."));
1970
+ console.error(chalk16.red("Nothing to send. Pass --text, --html or --body-file."));
1896
1971
  process.exit(1);
1897
1972
  }
1898
1973
  const sent = await client2().email.send(appId, {
@@ -1906,12 +1981,12 @@ emailCommand.command("send").description("Send an email").requiredOption("-t, --
1906
1981
  console.log(JSON.stringify(sent, null, 2));
1907
1982
  return;
1908
1983
  }
1909
- console.log(chalk15.green("Sent"));
1984
+ console.log(chalk16.green("Sent"));
1910
1985
  console.log(` From: ${sent.from}`);
1911
1986
  console.log(` To: ${sent.to.join(", ")}`);
1912
1987
  console.log(` Message: ${sent.messageId ?? "(no id returned)"}`);
1913
1988
  console.log(
1914
- chalk15.dim(
1989
+ chalk16.dim(
1915
1990
  ` Quota: ${sent.quota.sent}/${sent.quota.quota} today, resets ${sent.quota.resetsAt}`
1916
1991
  )
1917
1992
  );
@@ -1926,14 +2001,14 @@ emailCommand.command("quota").description("Show today's sending allowance").opti
1926
2001
  console.log(`Sent today: ${quota.sent}`);
1927
2002
  console.log(`Daily quota: ${quota.quota}`);
1928
2003
  console.log(`Remaining: ${quota.remaining}`);
1929
- console.log(chalk15.dim(`Resets at: ${quota.resetsAt}`));
2004
+ console.log(chalk16.dim(`Resets at: ${quota.resetsAt}`));
1930
2005
  });
1931
2006
 
1932
2007
  // src/commands/jobs.ts
1933
- import { Command as Command15 } from "commander";
1934
- import chalk16 from "chalk";
1935
- import fs6 from "fs-extra";
1936
- var jobsCommand = new Command15("jobs").description("Enqueue and inspect background jobs");
2008
+ import { Command as Command16 } from "commander";
2009
+ import chalk17 from "chalk";
2010
+ import fs7 from "fs-extra";
2011
+ var jobsCommand = new Command16("jobs").description("Enqueue and inspect background jobs");
1937
2012
  function client3() {
1938
2013
  const apiKey = getApiKey();
1939
2014
  requireApiKey(apiKey);
@@ -1942,7 +2017,7 @@ function client3() {
1942
2017
  function requireApp3(opt) {
1943
2018
  const appId = opt || getCurrentApp();
1944
2019
  if (!appId) {
1945
- console.error(chalk16.red("No app specified. Use --app or run `videan init` first."));
2020
+ console.error(chalk17.red("No app specified. Use --app or run `videan init` first."));
1946
2021
  process.exit(1);
1947
2022
  }
1948
2023
  return appId;
@@ -1950,30 +2025,30 @@ function requireApp3(opt) {
1950
2025
  function intOption2(flag, raw) {
1951
2026
  const n = Number(raw);
1952
2027
  if (!Number.isInteger(n) || n < 0) {
1953
- console.error(chalk16.red(`${flag} expects a whole number, got "${raw}".`));
2028
+ console.error(chalk17.red(`${flag} expects a whole number, got "${raw}".`));
1954
2029
  process.exit(1);
1955
2030
  }
1956
2031
  return n;
1957
2032
  }
1958
2033
  var STATUS_COLOR = {
1959
- queued: chalk16.yellow,
1960
- running: chalk16.cyan,
1961
- succeeded: chalk16.green,
1962
- dead: chalk16.red,
1963
- cancelled: chalk16.gray
2034
+ queued: chalk17.yellow,
2035
+ running: chalk17.cyan,
2036
+ succeeded: chalk17.green,
2037
+ dead: chalk17.red,
2038
+ cancelled: chalk17.gray
1964
2039
  };
1965
2040
  function statusLabel(status) {
1966
- return (STATUS_COLOR[status] ?? chalk16.white)(status);
2041
+ return (STATUS_COLOR[status] ?? chalk17.white)(status);
1967
2042
  }
1968
2043
  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) => {
1969
2044
  const appId = requireApp3(opts.app);
1970
2045
  let payload;
1971
- const raw = opts.file ? await fs6.readFile(opts.file, "utf-8") : opts.data;
2046
+ const raw = opts.file ? await fs7.readFile(opts.file, "utf-8") : opts.data;
1972
2047
  if (raw !== void 0) {
1973
2048
  try {
1974
2049
  payload = JSON.parse(raw);
1975
2050
  } catch (e) {
1976
- console.error(chalk16.red(`--${opts.file ? "file" : "data"} is not valid JSON: ${e.message}`));
2051
+ console.error(chalk17.red(`--${opts.file ? "file" : "data"} is not valid JSON: ${e.message}`));
1977
2052
  process.exit(1);
1978
2053
  }
1979
2054
  }
@@ -1991,13 +2066,13 @@ jobsCommand.command("enqueue").description("Queue a job for the app to run").opt
1991
2066
  return;
1992
2067
  }
1993
2068
  if (job.deduplicated) {
1994
- console.log(chalk16.yellow(`Already queued as ${job.id} (idempotency key "${opts.key}")`));
2069
+ console.log(chalk17.yellow(`Already queued as ${job.id} (idempotency key "${opts.key}")`));
1995
2070
  } else {
1996
- console.log(chalk16.green(`Queued ${job.id}`));
2071
+ console.log(chalk17.green(`Queued ${job.id}`));
1997
2072
  }
1998
- console.log(chalk16.gray(` ${job.method} ${job.path} on queue "${job.queue}"`));
2073
+ console.log(chalk17.gray(` ${job.method} ${job.path} on queue "${job.queue}"`));
1999
2074
  if (job.runAt && Date.parse(job.runAt) > Date.now() + 1e3) {
2000
- console.log(chalk16.gray(` Runs at ${new Date(job.runAt).toLocaleString()}`));
2075
+ console.log(chalk17.gray(` Runs at ${new Date(job.runAt).toLocaleString()}`));
2001
2076
  }
2002
2077
  });
2003
2078
  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) => {
@@ -2012,17 +2087,17 @@ jobsCommand.command("list").description("List jobs, newest first").option("-a, -
2012
2087
  return;
2013
2088
  }
2014
2089
  const depth = Object.entries(counts).map(([status, n]) => `${statusLabel(status)} ${n}`).join(" ");
2015
- console.log(depth || chalk16.gray("No jobs yet."));
2090
+ console.log(depth || chalk17.gray("No jobs yet."));
2016
2091
  if (jobs.length === 0) return;
2017
2092
  console.log();
2018
2093
  for (const job of jobs) {
2019
- const attempts = job.attempts > 0 ? chalk16.gray(` ${job.attempts}/${job.maxAttempts}`) : "";
2094
+ const attempts = job.attempts > 0 ? chalk17.gray(` ${job.attempts}/${job.maxAttempts}`) : "";
2020
2095
  console.log(
2021
- `${statusLabel(job.status).padEnd(20)} ${chalk16.white(job.id)} ${chalk16.gray(
2096
+ `${statusLabel(job.status).padEnd(20)} ${chalk17.white(job.id)} ${chalk17.gray(
2022
2097
  `${job.queue} ${job.path}`
2023
2098
  )}${attempts}`
2024
2099
  );
2025
- if (job.error) console.log(chalk16.red(` ${job.error}`));
2100
+ if (job.error) console.log(chalk17.red(` ${job.error}`));
2026
2101
  }
2027
2102
  });
2028
2103
  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) => {
@@ -2031,17 +2106,17 @@ jobsCommand.command("get <job-id>").description("Show one job in full").option("
2031
2106
  console.log(JSON.stringify(job, null, 2));
2032
2107
  return;
2033
2108
  }
2034
- console.log(`${chalk16.white(job.id)} ${statusLabel(job.status)}`);
2035
- console.log(chalk16.gray(` ${job.method} ${job.path} on queue "${job.queue}"`));
2036
- console.log(chalk16.gray(` Attempts: ${job.attempts}/${job.maxAttempts}`));
2037
- if (job.statusCode) console.log(chalk16.gray(` Last response: HTTP ${job.statusCode}`));
2038
- if (job.durationMs) console.log(chalk16.gray(` Took: ${job.durationMs}ms`));
2039
- if (job.error) console.log(chalk16.red(` Error: ${job.error}`));
2109
+ console.log(`${chalk17.white(job.id)} ${statusLabel(job.status)}`);
2110
+ console.log(chalk17.gray(` ${job.method} ${job.path} on queue "${job.queue}"`));
2111
+ console.log(chalk17.gray(` Attempts: ${job.attempts}/${job.maxAttempts}`));
2112
+ if (job.statusCode) console.log(chalk17.gray(` Last response: HTTP ${job.statusCode}`));
2113
+ if (job.durationMs) console.log(chalk17.gray(` Took: ${job.durationMs}ms`));
2114
+ if (job.error) console.log(chalk17.red(` Error: ${job.error}`));
2040
2115
  if (job.status === "queued" && job.attempts > 0) {
2041
- console.log(chalk16.yellow(` Retrying at ${new Date(job.runAt).toLocaleString()}`));
2116
+ console.log(chalk17.yellow(` Retrying at ${new Date(job.runAt).toLocaleString()}`));
2042
2117
  }
2043
2118
  if (job.payload !== null && job.payload !== void 0) {
2044
- console.log(chalk16.gray(" Payload:"));
2119
+ console.log(chalk17.gray(" Payload:"));
2045
2120
  console.log(
2046
2121
  JSON.stringify(job.payload, null, 2).split("\n").map((l) => ` ${l}`).join("\n")
2047
2122
  );
@@ -2049,17 +2124,17 @@ jobsCommand.command("get <job-id>").description("Show one job in full").option("
2049
2124
  });
2050
2125
  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) => {
2051
2126
  const job = await client3().jobs.retry(requireApp3(opts.app), jobId);
2052
- console.log(chalk16.green(`Requeued ${job.id}`));
2127
+ console.log(chalk17.green(`Requeued ${job.id}`));
2053
2128
  });
2054
2129
  jobsCommand.command("cancel <job-id>").description("Cancel a job that has not started yet").option("-a, --app <id>", "App ID").action(async (jobId, opts) => {
2055
2130
  const job = await client3().jobs.cancel(requireApp3(opts.app), jobId);
2056
- console.log(chalk16.green(`Cancelled ${job.id}`));
2131
+ console.log(chalk17.green(`Cancelled ${job.id}`));
2057
2132
  });
2058
2133
 
2059
2134
  // src/commands/usage.ts
2060
- import { Command as Command16 } from "commander";
2061
- import chalk17 from "chalk";
2062
- var usageCommand = new Command16("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) => {
2135
+ import { Command as Command17 } from "commander";
2136
+ import chalk18 from "chalk";
2137
+ 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) => {
2063
2138
  const apiKey = getApiKey();
2064
2139
  requireApiKey(apiKey);
2065
2140
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
@@ -2067,61 +2142,61 @@ var usageCommand = new Command16("usage").description("Metered usage and cost fo
2067
2142
  const appId = opts.app || getCurrentApp();
2068
2143
  const report2 = await client4.apps.usage(appId, opts.period);
2069
2144
  if (opts.json) return void console.log(JSON.stringify(report2, null, 2));
2070
- console.log(chalk17.dim(`${period(report2.periodStart, report2.periodEnd)} app ${appId}`));
2145
+ console.log(chalk18.dim(`${period(report2.periodStart, report2.periodEnd)} app ${appId}`));
2071
2146
  console.log();
2072
2147
  printMetrics(report2.metrics);
2073
2148
  console.log();
2074
2149
  for (const line of report2.lines) {
2075
- console.log(` ${chalk17.dim(line.label.padEnd(22))} ${money(line.cents)}`);
2150
+ console.log(` ${chalk18.dim(line.label.padEnd(22))} ${money(line.cents)}`);
2076
2151
  }
2077
- console.log(` ${chalk17.bold("Share of the bill".padEnd(22))} ${chalk17.bold(report2.cost)}`);
2152
+ console.log(` ${chalk18.bold("Share of the bill".padEnd(22))} ${chalk18.bold(report2.cost)}`);
2078
2153
  console.log();
2079
2154
  console.log(
2080
- chalk17.dim("At list price, with no plan allowance applied \u2014 see `videan usage` for the org total.")
2155
+ chalk18.dim("At list price, with no plan allowance applied \u2014 see `videan usage` for the org total.")
2081
2156
  );
2082
2157
  return;
2083
2158
  }
2084
2159
  const orgId = opts.org || getDefaultOrg();
2085
2160
  if (!orgId) {
2086
- console.error(chalk17.red("No org specified. Use --org, or --app for a single app."));
2161
+ console.error(chalk18.red("No org specified. Use --org, or --app for a single app."));
2087
2162
  process.exit(1);
2088
2163
  }
2089
2164
  const report = await client4.orgs.usage(orgId, opts.period);
2090
2165
  if (opts.json) return void console.log(JSON.stringify(report, null, 2));
2091
2166
  const inv = report.invoice;
2092
2167
  console.log(
2093
- chalk17.dim(`${period(report.periodStart, report.periodEnd)} org ${orgId} plan ${report.plan}`)
2168
+ chalk18.dim(`${period(report.periodStart, report.periodEnd)} org ${orgId} plan ${report.plan}`)
2094
2169
  );
2095
2170
  console.log();
2096
2171
  printMetrics(report.metrics);
2097
2172
  if (report.apps.length > 0) {
2098
2173
  console.log();
2099
- console.log(chalk17.bold("By app"));
2174
+ console.log(chalk18.bold("By app"));
2100
2175
  for (const app of [...report.apps].sort((a, b) => b.cents - a.cents)) {
2101
2176
  const name = app.slug || app.name || app.appId || "(deleted apps)";
2102
- console.log(` ${chalk17.dim(String(name).padEnd(24))} ${money(app.cents)}`);
2177
+ console.log(` ${chalk18.dim(String(name).padEnd(24))} ${money(app.cents)}`);
2103
2178
  }
2104
2179
  }
2105
2180
  console.log();
2106
2181
  for (const line of inv.lines) {
2107
2182
  console.log(
2108
- ` ${chalk17.dim(line.label.padEnd(22))} ${money(line.cents)} ${chalk17.dim(
2183
+ ` ${chalk18.dim(line.label.padEnd(22))} ${money(line.cents)} ${chalk18.dim(
2109
2184
  `(${line.quantity} \xD7 ${line.unitPriceCents}\xA2)`
2110
2185
  )}`
2111
2186
  );
2112
2187
  }
2113
- console.log(` ${chalk17.dim("Usage".padEnd(22))} ${money(inv.usageCents)}`);
2188
+ console.log(` ${chalk18.dim("Usage".padEnd(22))} ${money(inv.usageCents)}`);
2114
2189
  if (inv.includedUsageCents > 0) {
2115
2190
  console.log(
2116
- ` ${chalk17.dim("Included".padEnd(22))} ${chalk17.green(`-${money(Math.min(inv.usageCents, inv.includedUsageCents))}`)}`
2191
+ ` ${chalk18.dim("Included".padEnd(22))} ${chalk18.green(`-${money(Math.min(inv.usageCents, inv.includedUsageCents))}`)}`
2117
2192
  );
2118
2193
  }
2119
2194
  if (inv.baseCents > 0) {
2120
- console.log(` ${chalk17.dim("Plan".padEnd(22))} ${money(inv.baseCents)}`);
2195
+ console.log(` ${chalk18.dim("Plan".padEnd(22))} ${money(inv.baseCents)}`);
2121
2196
  }
2122
- console.log(` ${chalk17.bold("Total".padEnd(22))} ${chalk17.bold(report.total)}`);
2197
+ console.log(` ${chalk18.bold("Total".padEnd(22))} ${chalk18.bold(report.total)}`);
2123
2198
  console.log();
2124
- console.log(chalk17.dim("Usage to date. The period is priced as it stands right now."));
2199
+ console.log(chalk18.dim("Usage to date. The period is priced as it stands right now."));
2125
2200
  });
2126
2201
  usageCommand.command("plans").description("The plan catalogue and what each one allows").option("--json", "Print the raw response").action(async (opts) => {
2127
2202
  const client4 = new CloudClient({ apiKey: getApiKey() || "", baseUrl: getBaseUrl() });
@@ -2129,11 +2204,11 @@ usageCommand.command("plans").description("The plan catalogue and what each one
2129
2204
  if (opts.json) return void console.log(JSON.stringify(plans, null, 2));
2130
2205
  for (const plan of plans) {
2131
2206
  console.log(
2132
- `${chalk17.bold(plan.name)} ${chalk17.dim(`(${plan.id})`)} ${plan.base}/mo` + (plan.includedUsageCents > 0 ? chalk17.dim(`, ${plan.includedUsage} usage included`) : "")
2207
+ `${chalk18.bold(plan.name)} ${chalk18.dim(`(${plan.id})`)} ${plan.base}/mo` + (plan.includedUsageCents > 0 ? chalk18.dim(`, ${plan.includedUsage} usage included`) : "")
2133
2208
  );
2134
2209
  const l = plan.limits;
2135
2210
  console.log(
2136
- chalk17.dim(
2211
+ chalk18.dim(
2137
2212
  ` ${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" : ""}`
2138
2213
  )
2139
2214
  );
@@ -2142,11 +2217,11 @@ usageCommand.command("plans").description("The plan catalogue and what each one
2142
2217
  });
2143
2218
  function printMetrics(metrics) {
2144
2219
  if (metrics.length === 0) {
2145
- console.log(chalk17.dim(" Nothing metered in this period yet."));
2220
+ console.log(chalk18.dim(" Nothing metered in this period yet."));
2146
2221
  return;
2147
2222
  }
2148
2223
  for (const m of metrics) {
2149
- console.log(` ${chalk17.dim(m.metric.padEnd(22))} ${round(m.quantity)}`);
2224
+ console.log(` ${chalk18.dim(m.metric.padEnd(22))} ${round(m.quantity)}`);
2150
2225
  }
2151
2226
  }
2152
2227
  function round(n) {
@@ -2161,12 +2236,13 @@ function period(start, end) {
2161
2236
  }
2162
2237
 
2163
2238
  // src/index.ts
2164
- var program = new Command17();
2239
+ var program = new Command18();
2165
2240
  program.name("videan").description("Videan CLI \u2014 deploy apps in seconds").version("0.1.0");
2166
2241
  program.addCommand(loginCommand);
2167
2242
  program.addCommand(logoutCommand);
2168
2243
  program.addCommand(initCommand);
2169
2244
  program.addCommand(deployCommand);
2245
+ program.addCommand(pullCommand);
2170
2246
  program.addCommand(logsCommand);
2171
2247
  program.addCommand(metricsCommand);
2172
2248
  program.addCommand(rollbackCommand);