@videan/cli 0.1.1 → 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, path4, body) {
124
- const url = `${this.baseUrl}${path4}`;
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 path4 = `/apps/${appId}/logs${query ? `?${query}` : ""}`;
294
- const res = await this.client.request("GET", path4);
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
  /**
@@ -934,35 +934,100 @@ var logoutCommand = new Command2("logout").description("Forget the saved key, or
934
934
  import { Command as Command3 } from "commander";
935
935
  import inquirer2 from "inquirer";
936
936
  import chalk3 from "chalk";
937
+ import fs2 from "fs-extra";
938
+ import path2 from "node:path";
939
+
940
+ // src/videan-md.ts
937
941
  import fs from "fs-extra";
938
942
  import path from "node:path";
939
- var initCommand = new Command3("init").description("Initialize a new Cloud app in the current directory").action(async () => {
940
- const configPath2 = path.join(process.cwd(), CONFIG_FILE_NAME);
941
- if (await fs.pathExists(configPath2)) {
942
- console.log(chalk3.yellow(`${CONFIG_FILE_NAME} already exists in this directory.`));
943
- const { overwrite } = await inquirer2.prompt([
944
- { type: "confirm", name: "overwrite", message: "Overwrite?", default: false }
945
- ]);
946
- if (!overwrite) return;
943
+ var VIDEAN_MD = `# This project deploys to Videan
944
+
945
+ Deploy: \`videan deploy\` (from this directory). Logs: \`videan logs -f\`.
946
+ Undo a bad deploy: \`videan rollback\`. Full agent guide: https://videan.xyz/llms.txt
947
+
948
+ The contract: serve HTTP on **port 3000**. Runtime (Node/Python/Next.js/Go/
949
+ Dockerfile) is detected from the files \u2014 no config needed. If the app has a
950
+ managed database, read \`DATABASE_URL\` from the environment. Log to stdout.
951
+
952
+ ## Auth \u2014 read this before writing any login code
953
+
954
+ This platform puts a sign-in gate in front of apps unless they are public.
955
+
956
+ - **Gated app (visibility private/org/authenticated \u2014 the default): DO NOT
957
+ build a login page.** Every request arrives with the visitor already
958
+ authenticated: read \`X-User-Id\`, \`X-User-Email\`, \`X-User-Roles\`. If the
959
+ app needs its own user records, find-or-create a local user from
960
+ \`X-User-Email\` and start your session from that \u2014 never render a login
961
+ form when the header is present.
962
+ - **Public app** (\`videan apps set --public\`): the gate is out of the path,
963
+ no headers arrive, bring your own auth if the app needs any. Required for
964
+ anything that must receive webhooks from external services.
965
+
966
+ Secrets: \`videan secrets set KEY=value\` (UPPERCASE keys) \u2014 injected as env
967
+ vars on the next deploy. Never commit credentials to this repo instead.
968
+ `;
969
+ var POINTER = "\n## Videan\n\nThis project deploys to Videan \u2014 see VIDEAN.md for the platform contract (port 3000, auth headers, deploy commands) before writing server or login code.\n";
970
+ async function writeVideanMd(dir) {
971
+ const wrote = [];
972
+ const target = path.join(dir, "VIDEAN.md");
973
+ if (!await fs.pathExists(target)) {
974
+ await fs.writeFile(target, VIDEAN_MD, "utf-8");
975
+ wrote.push("VIDEAN.md");
976
+ }
977
+ for (const name of ["CLAUDE.md", "AGENTS.md"]) {
978
+ const p = path.join(dir, name);
979
+ if (!await fs.pathExists(p)) continue;
980
+ const body = await fs.readFile(p, "utf-8");
981
+ if (body.includes("VIDEAN.md")) continue;
982
+ await fs.appendFile(p, POINTER, "utf-8");
983
+ wrote.push(`${name} (pointer)`);
984
+ }
985
+ return wrote;
986
+ }
987
+
988
+ // src/commands/init.ts
989
+ var initCommand = new Command3("init").description("Initialize a new Cloud app in the current directory").option("--name <name>", "App name (defaults to the directory name)").option("--runtime <runtime>", "One of: " + SUPPORTED_RUNTIMES.join(", ")).option("--entrypoint <file>", "Entry file", "index.ts").option("--yes", "Take the defaults without prompting").action(async (opts) => {
990
+ const configPath2 = path2.join(process.cwd(), CONFIG_FILE_NAME);
991
+ const interactive = process.stdin.isTTY && !opts.yes;
992
+ if (await fs2.pathExists(configPath2)) {
993
+ if (!interactive) {
994
+ console.log(chalk3.yellow(`${CONFIG_FILE_NAME} already exists; leaving it alone.`));
995
+ } else {
996
+ console.log(chalk3.yellow(`${CONFIG_FILE_NAME} already exists in this directory.`));
997
+ const { overwrite } = await inquirer2.prompt([
998
+ { type: "confirm", name: "overwrite", message: "Overwrite?", default: false }
999
+ ]);
1000
+ if (!overwrite) {
1001
+ for (const f of await writeVideanMd(process.cwd())) console.log(chalk3.green(`Wrote ${f}`));
1002
+ return;
1003
+ }
1004
+ }
947
1005
  }
948
- const answers = await inquirer2.prompt([
949
- { type: "input", name: "name", message: "App name:", default: path.basename(process.cwd()) },
950
- {
951
- type: "list",
952
- name: "runtime",
953
- message: "Runtime:",
954
- choices: SUPPORTED_RUNTIMES
955
- },
956
- { type: "input", name: "entrypoint", message: "Entrypoint:", default: "index.ts" }
957
- ]);
958
- const config2 = [
959
- `name: ${answers.name}`,
960
- `runtime: ${answers.runtime}`,
961
- `entrypoint: ${answers.entrypoint}`,
962
- ""
963
- ].join("\n");
964
- await fs.writeFile(configPath2, config2, "utf-8");
965
- console.log(chalk3.green(`Created ${CONFIG_FILE_NAME}`));
1006
+ if (!await fs2.pathExists(configPath2) || interactive) {
1007
+ const answers = interactive ? await inquirer2.prompt([
1008
+ { type: "input", name: "name", message: "App name:", default: opts.name || path2.basename(process.cwd()) },
1009
+ {
1010
+ type: "list",
1011
+ name: "runtime",
1012
+ message: "Runtime:",
1013
+ choices: SUPPORTED_RUNTIMES
1014
+ },
1015
+ { type: "input", name: "entrypoint", message: "Entrypoint:", default: opts.entrypoint }
1016
+ ]) : {
1017
+ name: opts.name || path2.basename(process.cwd()),
1018
+ runtime: opts.runtime || SUPPORTED_RUNTIMES[0],
1019
+ entrypoint: opts.entrypoint
1020
+ };
1021
+ const config2 = [
1022
+ `name: ${answers.name}`,
1023
+ `runtime: ${answers.runtime}`,
1024
+ `entrypoint: ${answers.entrypoint}`,
1025
+ ""
1026
+ ].join("\n");
1027
+ await fs2.writeFile(configPath2, config2, "utf-8");
1028
+ console.log(chalk3.green(`Created ${CONFIG_FILE_NAME}`));
1029
+ }
1030
+ for (const f of await writeVideanMd(process.cwd())) console.log(chalk3.green(`Wrote ${f}`));
966
1031
  });
967
1032
 
968
1033
  // src/commands/deploy.ts
@@ -971,8 +1036,8 @@ import chalk5 from "chalk";
971
1036
  import ora2 from "ora";
972
1037
 
973
1038
  // src/util.ts
974
- import fs2 from "fs-extra";
975
- import path2 from "node:path";
1039
+ import fs3 from "fs-extra";
1040
+ import path3 from "node:path";
976
1041
  import ignore from "ignore";
977
1042
  import chalk4 from "chalk";
978
1043
  var MAX_FILE_BYTES = 10 * 1024 * 1024;
@@ -989,30 +1054,30 @@ async function collectFiles(dir) {
989
1054
  ".DS_Store",
990
1055
  "*.log"
991
1056
  ]);
992
- const gitignorePath = path2.join(dir, ".gitignore");
993
- if (await fs2.pathExists(gitignorePath)) {
994
- const content = await fs2.readFile(gitignorePath, "utf-8");
1057
+ const gitignorePath = path3.join(dir, ".gitignore");
1058
+ if (await fs3.pathExists(gitignorePath)) {
1059
+ const content = await fs3.readFile(gitignorePath, "utf-8");
995
1060
  ig.add(content);
996
1061
  }
997
- const cloudignorePath = path2.join(dir, ".cloudignore");
998
- if (await fs2.pathExists(cloudignorePath)) {
999
- const content = await fs2.readFile(cloudignorePath, "utf-8");
1062
+ const cloudignorePath = path3.join(dir, ".cloudignore");
1063
+ if (await fs3.pathExists(cloudignorePath)) {
1064
+ const content = await fs3.readFile(cloudignorePath, "utf-8");
1000
1065
  ig.add(content);
1001
1066
  }
1002
1067
  const files = {};
1003
1068
  const binaryFiles = {};
1004
1069
  const skipped = [];
1005
1070
  async function walk(currentDir) {
1006
- const entries = await fs2.readdir(currentDir, { withFileTypes: true });
1071
+ const entries = await fs3.readdir(currentDir, { withFileTypes: true });
1007
1072
  for (const entry of entries) {
1008
- const fullPath = path2.join(currentDir, entry.name);
1009
- const relativePath = path2.relative(dir, fullPath);
1073
+ const fullPath = path3.join(currentDir, entry.name);
1074
+ const relativePath = path3.relative(dir, fullPath);
1010
1075
  const ignorePath = entry.isDirectory() ? `${relativePath}/` : relativePath;
1011
1076
  if (ig.ignores(ignorePath)) continue;
1012
1077
  if (entry.isDirectory()) {
1013
1078
  await walk(fullPath);
1014
1079
  } else if (entry.isFile()) {
1015
- const buffer = await fs2.readFile(fullPath);
1080
+ const buffer = await fs3.readFile(fullPath);
1016
1081
  if (buffer.byteLength > MAX_FILE_BYTES) {
1017
1082
  skipped.push({
1018
1083
  path: relativePath,
@@ -1118,6 +1183,16 @@ var deployCommand = new Command4("deploy").description("Deploy the current direc
1118
1183
  }
1119
1184
  if (current.status === "running") {
1120
1185
  spinner.succeed(chalk5.green(`Deployed successfully!`));
1186
+ try {
1187
+ const app = await client4.apps.get(appId);
1188
+ const gated = app.visibility ? app.visibility !== "public" : app.authRequired !== false;
1189
+ console.log(
1190
+ gated ? chalk5.gray(
1191
+ ` 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.`
1192
+ ) : chalk5.gray(` Access: public \u2014 reachable by anyone; bring your own auth if it needs any.`)
1193
+ );
1194
+ } catch {
1195
+ }
1121
1196
  if (current.url) {
1122
1197
  const url = /^https?:\/\//.test(current.url) ? current.url : `https://${current.url}`;
1123
1198
  console.log(chalk5.cyan(` URL: ${url}`));
@@ -1135,19 +1210,94 @@ var deployCommand = new Command4("deploy").description("Deploy the current direc
1135
1210
  }
1136
1211
  });
1137
1212
 
1138
- // src/commands/logs.ts
1213
+ // src/commands/pull.ts
1139
1214
  import { Command as Command5 } from "commander";
1140
1215
  import chalk6 from "chalk";
1141
- 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) => {
1142
1292
  const apiKey = getApiKey();
1143
1293
  requireApiKey(apiKey);
1144
1294
  const appId = opts.app || getCurrentApp();
1145
1295
  if (!appId) {
1146
- 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."));
1147
1297
  process.exit(1);
1148
1298
  }
1149
1299
  if (opts.search && opts.pattern) {
1150
- console.error(chalk6.red("Use either --search or --pattern, not both."));
1300
+ console.error(chalk7.red("Use either --search or --pattern, not both."));
1151
1301
  process.exit(1);
1152
1302
  }
1153
1303
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
@@ -1170,43 +1320,43 @@ var logsCommand = new Command5("logs").description("View app logs").option("-a,
1170
1320
  });
1171
1321
  for (const entry of page.events) print(entry);
1172
1322
  if (page.events.length === 0) {
1173
- console.log(chalk6.gray(`No log lines since ${page.start}.`));
1323
+ console.log(chalk7.gray(`No log lines since ${page.start}.`));
1174
1324
  }
1175
1325
  if (page.truncated) {
1176
1326
  console.log(
1177
- chalk6.yellow(
1327
+ chalk7.yellow(
1178
1328
  "\nThis is part of the window -- there are more lines than one request reads. Narrow --since or add --search."
1179
1329
  )
1180
1330
  );
1181
1331
  }
1182
1332
  });
1183
1333
  function print(entry) {
1184
- console.log(`${chalk6.gray(entry.timestamp)} ${formatLevel(entry.level)} ${entry.message}`);
1334
+ console.log(`${chalk7.gray(entry.timestamp)} ${formatLevel(entry.level)} ${entry.message}`);
1185
1335
  }
1186
1336
  function formatLevel(level) {
1187
1337
  switch (level) {
1188
1338
  case "error":
1189
- return chalk6.red("[ERR]");
1339
+ return chalk7.red("[ERR]");
1190
1340
  case "warn":
1191
- return chalk6.yellow("[WRN]");
1341
+ return chalk7.yellow("[WRN]");
1192
1342
  case "info":
1193
- return chalk6.blue("[INF]");
1343
+ return chalk7.blue("[INF]");
1194
1344
  case "debug":
1195
- return chalk6.gray("[DBG]");
1345
+ return chalk7.gray("[DBG]");
1196
1346
  default:
1197
- return chalk6.gray("[LOG]");
1347
+ return chalk7.gray("[LOG]");
1198
1348
  }
1199
1349
  }
1200
1350
 
1201
1351
  // src/commands/metrics.ts
1202
- import { Command as Command6 } from "commander";
1203
- import chalk7 from "chalk";
1204
- 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) => {
1205
1355
  const apiKey = getApiKey();
1206
1356
  requireApiKey(apiKey);
1207
1357
  const appId = opts.app || getCurrentApp();
1208
1358
  if (!appId) {
1209
- 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."));
1210
1360
  process.exit(1);
1211
1361
  }
1212
1362
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
@@ -1217,7 +1367,7 @@ var metricsCommand = new Command6("metrics").description("Traffic, latency and r
1217
1367
  }
1218
1368
  const t = metrics.totals;
1219
1369
  console.log(
1220
- chalk7.dim(`${metrics.start} \u2192 ${metrics.end} (${metrics.period}s buckets)`)
1370
+ chalk8.dim(`${metrics.start} \u2192 ${metrics.end} (${metrics.period}s buckets)`)
1221
1371
  );
1222
1372
  console.log();
1223
1373
  console.log(` ${label("Requests")} ${t.requests}`);
@@ -1225,76 +1375,76 @@ var metricsCommand = new Command6("metrics").description("Traffic, latency and r
1225
1375
  console.log(` ${label("5xx")} ${count(t.serverErrors, true)}`);
1226
1376
  console.log(` ${label("Gate denied")} ${count(t.gateDenied)}`);
1227
1377
  console.log(` ${label("Latency avg")} ${ms(t.latencyAvgMs)}`);
1228
- console.log(` ${label("Latency p95")} ${ms(t.latencyP95Ms)} ${chalk7.dim("(sampled)")}`);
1378
+ console.log(` ${label("Latency p95")} ${ms(t.latencyP95Ms)} ${chalk8.dim("(sampled)")}`);
1229
1379
  console.log(` ${label("CPU avg")} ${units(t.cpuAvg)}`);
1230
1380
  console.log(` ${label("Memory avg")} ${mb(t.memoryAvgMb)}`);
1231
1381
  if (t.requests === 0) {
1232
1382
  console.log();
1233
- console.log(chalk7.dim("No requests recorded in this window."));
1383
+ console.log(chalk8.dim("No requests recorded in this window."));
1234
1384
  }
1235
1385
  if (opts.series) {
1236
1386
  console.log();
1237
1387
  for (const [name, points] of Object.entries(metrics.series)) {
1238
1388
  if (points.length === 0) continue;
1239
- console.log(chalk7.bold(name));
1240
- 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}`);
1241
1391
  }
1242
1392
  }
1243
1393
  });
1244
1394
  function label(text) {
1245
- return chalk7.dim(text.padEnd(13));
1395
+ return chalk8.dim(text.padEnd(13));
1246
1396
  }
1247
1397
  function count(n, severe = false) {
1248
- if (n === 0) return chalk7.dim("0");
1249
- 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));
1250
1400
  }
1251
1401
  function ms(v) {
1252
- return v === null ? chalk7.dim("\u2014") : `${v} ms`;
1402
+ return v === null ? chalk8.dim("\u2014") : `${v} ms`;
1253
1403
  }
1254
1404
  function mb(v) {
1255
- return v === null ? chalk7.dim("\u2014") : `${v} MB`;
1405
+ return v === null ? chalk8.dim("\u2014") : `${v} MB`;
1256
1406
  }
1257
1407
  function units(v) {
1258
- return v === null ? chalk7.dim("\u2014") : `${v} CPU units`;
1408
+ return v === null ? chalk8.dim("\u2014") : `${v} CPU units`;
1259
1409
  }
1260
1410
 
1261
1411
  // src/commands/rollback.ts
1262
- import { Command as Command7 } from "commander";
1263
- import chalk8 from "chalk";
1264
- import ora3 from "ora";
1265
- 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) => {
1266
1416
  const apiKey = getApiKey();
1267
1417
  requireApiKey(apiKey);
1268
1418
  const appId = opts.app || getCurrentApp();
1269
1419
  if (!appId) {
1270
- 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."));
1271
1421
  process.exit(1);
1272
1422
  }
1273
1423
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1274
- const spinner = ora3("Rolling back...").start();
1424
+ const spinner = ora4("Rolling back...").start();
1275
1425
  try {
1276
1426
  const deployment = await client4.apps.rollback(appId);
1277
- spinner.succeed(chalk8.green("Rollback successful!"));
1427
+ spinner.succeed(chalk9.green("Rollback successful!"));
1278
1428
  if (deployment.url) {
1279
- console.log(chalk8.cyan(` URL: ${deployment.url}`));
1429
+ console.log(chalk9.cyan(` URL: ${deployment.url}`));
1280
1430
  }
1281
1431
  } catch (error) {
1282
- spinner.fail(chalk8.red(`Rollback failed: ${error.message}`));
1432
+ spinner.fail(chalk9.red(`Rollback failed: ${error.message}`));
1283
1433
  process.exit(1);
1284
1434
  }
1285
1435
  });
1286
1436
 
1287
1437
  // src/commands/secrets.ts
1288
- import { Command as Command8 } from "commander";
1289
- import chalk9 from "chalk";
1438
+ import { Command as Command9 } from "commander";
1439
+ import chalk10 from "chalk";
1290
1440
  import inquirer3 from "inquirer";
1291
- var secretsCommand = new Command8("secrets").description("Manage app secrets");
1441
+ var secretsCommand = new Command9("secrets").description("Manage app secrets");
1292
1442
  secretsCommand.command("set <key> [value]").description("Set a secret").option("-a, --app <id>", "App ID").action(async (key, value, opts) => {
1293
1443
  const apiKey = getApiKey();
1294
1444
  requireApiKey(apiKey);
1295
1445
  const appId = opts.app || getCurrentApp();
1296
1446
  if (!appId) {
1297
- console.error(chalk9.red("No app specified."));
1447
+ console.error(chalk10.red("No app specified."));
1298
1448
  process.exit(1);
1299
1449
  }
1300
1450
  if (!value) {
@@ -1305,20 +1455,20 @@ secretsCommand.command("set <key> [value]").description("Set a secret").option("
1305
1455
  }
1306
1456
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1307
1457
  await client4.secrets.set(appId, key, value);
1308
- console.log(chalk9.green(`Secret "${key}" set successfully.`));
1458
+ console.log(chalk10.green(`Secret "${key}" set successfully.`));
1309
1459
  });
1310
1460
  secretsCommand.command("list").description("List all secrets").option("-a, --app <id>", "App ID").action(async (opts) => {
1311
1461
  const apiKey = getApiKey();
1312
1462
  requireApiKey(apiKey);
1313
1463
  const appId = opts.app || getCurrentApp();
1314
1464
  if (!appId) {
1315
- console.error(chalk9.red("No app specified."));
1465
+ console.error(chalk10.red("No app specified."));
1316
1466
  process.exit(1);
1317
1467
  }
1318
1468
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1319
1469
  const secrets = await client4.secrets.list(appId);
1320
1470
  if (secrets.length === 0) {
1321
- console.log(chalk9.gray("No secrets configured."));
1471
+ console.log(chalk10.gray("No secrets configured."));
1322
1472
  return;
1323
1473
  }
1324
1474
  console.log(formatRow(["KEY", "UPDATED"], [30, 24]));
@@ -1331,24 +1481,24 @@ secretsCommand.command("remove <key>").description("Remove a secret").option("-a
1331
1481
  requireApiKey(apiKey);
1332
1482
  const appId = opts.app || getCurrentApp();
1333
1483
  if (!appId) {
1334
- console.error(chalk9.red("No app specified."));
1484
+ console.error(chalk10.red("No app specified."));
1335
1485
  process.exit(1);
1336
1486
  }
1337
1487
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1338
1488
  await client4.secrets.delete(appId, key);
1339
- console.log(chalk9.green(`Secret "${key}" removed.`));
1489
+ console.log(chalk10.green(`Secret "${key}" removed.`));
1340
1490
  });
1341
1491
 
1342
1492
  // src/commands/apps.ts
1343
- import { Command as Command9 } from "commander";
1344
- import chalk10 from "chalk";
1493
+ import { Command as Command10 } from "commander";
1494
+ import chalk11 from "chalk";
1345
1495
  import inquirer4 from "inquirer";
1346
- var appsCommand = new Command9("apps").description("Manage apps");
1496
+ var appsCommand = new Command10("apps").description("Manage apps");
1347
1497
  function accessOption(value) {
1348
1498
  if (value === void 0) return void 0;
1349
1499
  const tier = value.trim().toLowerCase();
1350
1500
  if (!isVisibility(tier)) {
1351
- 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(", ")}`));
1352
1502
  process.exit(1);
1353
1503
  }
1354
1504
  return tier;
@@ -1364,7 +1514,7 @@ appsCommand.command("list").description("List all apps").option("-o, --org <id>"
1364
1514
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1365
1515
  const apps = await client4.apps.list(orgId);
1366
1516
  if (apps.length === 0) {
1367
- 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`."));
1368
1518
  return;
1369
1519
  }
1370
1520
  const widths = [20, 24, 10, 44];
@@ -1386,7 +1536,7 @@ appsCommand.command("create").description("Create a new app").option("-n, --name
1386
1536
  name = answers.name;
1387
1537
  }
1388
1538
  if (!name) {
1389
- console.error(chalk10.red("An app name is required."));
1539
+ console.error(chalk11.red("An app name is required."));
1390
1540
  process.exit(1);
1391
1541
  }
1392
1542
  const visibility = accessOption(opts.access) ?? (opts.public ? "public" : void 0);
@@ -1400,10 +1550,10 @@ appsCommand.command("create").description("Create a new app").option("-n, --name
1400
1550
  visibility
1401
1551
  });
1402
1552
  setCurrentApp(app.id);
1403
- console.log(chalk10.green(`App "${app.name}" created (${app.id})`));
1404
- console.log(chalk10.gray(` Access: ${describeAccess(app)}`));
1405
- console.log(chalk10.gray(` URL after first deploy: ${appUrl(app.slug)}`));
1406
- 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."));
1407
1557
  }
1408
1558
  );
1409
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(
@@ -1412,16 +1562,16 @@ appsCommand.command("set").description("Change an app's settings (scale, rate li
1412
1562
  requireApiKey(apiKey);
1413
1563
  const appId = opts.app || getCurrentApp();
1414
1564
  if (!appId) {
1415
- 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."));
1416
1566
  process.exit(1);
1417
1567
  }
1418
1568
  if (opts.public && opts.private) {
1419
- console.error(chalk10.red("--public and --private contradict each other."));
1569
+ console.error(chalk11.red("--public and --private contradict each other."));
1420
1570
  process.exit(1);
1421
1571
  }
1422
1572
  const access = accessOption(opts.access);
1423
1573
  if (access && (opts.public || opts.private)) {
1424
- 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."));
1425
1575
  process.exit(1);
1426
1576
  }
1427
1577
  const changes = {};
@@ -1442,7 +1592,7 @@ appsCommand.command("set").description("Change an app's settings (scale, rate li
1442
1592
  if (opts.storage) changes.storage = true;
1443
1593
  if (opts.email) changes.email = true;
1444
1594
  if (Object.keys(changes).length === 0) {
1445
- 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."));
1446
1596
  process.exit(1);
1447
1597
  }
1448
1598
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
@@ -1451,7 +1601,7 @@ appsCommand.command("set").description("Change an app's settings (scale, rate li
1451
1601
  console.log(JSON.stringify(app, null, 2));
1452
1602
  return;
1453
1603
  }
1454
- console.log(chalk10.green(`Updated ${app.name} (${app.id})`));
1604
+ console.log(chalk11.green(`Updated ${app.name} (${app.id})`));
1455
1605
  console.log(` Access: ${describeAccess(app)}`);
1456
1606
  console.log(` Scale: ${app.cpu ?? "default"} CPU / ${app.memory ?? "default"} MiB`);
1457
1607
  console.log(
@@ -1459,7 +1609,7 @@ appsCommand.command("set").description("Change an app's settings (scale, rate li
1459
1609
  );
1460
1610
  if (app.restartRequired) {
1461
1611
  console.log(
1462
- 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.")
1463
1613
  );
1464
1614
  }
1465
1615
  }
@@ -1467,7 +1617,7 @@ appsCommand.command("set").description("Change an app's settings (scale, rate li
1467
1617
  function intOption(flag, raw) {
1468
1618
  const n = Number(raw);
1469
1619
  if (!Number.isInteger(n) || n <= 0) {
1470
- 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}".`));
1471
1621
  process.exit(1);
1472
1622
  }
1473
1623
  return n;
@@ -1486,64 +1636,64 @@ appsCommand.command("delete <id>").description("Delete an app and all its resour
1486
1636
  if (!confirm) return;
1487
1637
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1488
1638
  const { warnings } = await client4.apps.delete(id);
1489
- console.log(chalk10.green("App deleted."));
1639
+ console.log(chalk11.green("App deleted."));
1490
1640
  if (warnings.length > 0) {
1491
- console.log(chalk10.yellow("\nSome resources could not be released:"));
1641
+ console.log(chalk11.yellow("\nSome resources could not be released:"));
1492
1642
  for (const w of warnings) {
1493
- console.log(chalk10.yellow(` ${w.step}: ${w.error || "unknown error"}`));
1643
+ console.log(chalk11.yellow(` ${w.step}: ${w.error || "unknown error"}`));
1494
1644
  }
1495
1645
  }
1496
1646
  });
1497
1647
 
1498
1648
  // src/commands/status.ts
1499
- import { Command as Command10 } from "commander";
1500
- import chalk11 from "chalk";
1649
+ import { Command as Command11 } from "commander";
1650
+ import chalk12 from "chalk";
1501
1651
  function accessLine(app) {
1502
1652
  const tier = app.visibility ? visibilityOf(app.visibility) : app.authRequired ? "org" : "public";
1503
1653
  return `${tier} \u2014 ${VISIBILITY_LABELS[tier].toLowerCase()}`;
1504
1654
  }
1505
- 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) => {
1506
1656
  const apiKey = getApiKey();
1507
1657
  requireApiKey(apiKey);
1508
1658
  const appId = opts.app || getCurrentApp();
1509
1659
  if (!appId) {
1510
- 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."));
1511
1661
  process.exit(1);
1512
1662
  }
1513
1663
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1514
1664
  const app = await client4.apps.get(appId);
1515
1665
  const health = await client4.apps.health(appId);
1516
- console.log(chalk11.bold(app.name));
1666
+ console.log(chalk12.bold(app.name));
1517
1667
  console.log(` ID: ${app.id}`);
1518
1668
  console.log(` Created: ${app.createdAt}`);
1519
1669
  console.log(` Postgres: ${app.postgresEnabled ? "enabled" : "disabled"}`);
1520
1670
  console.log(` Access: ${accessLine(app)}`);
1521
1671
  console.log(` State: ${formatStatus(health.state)}`);
1522
- console.log(` Healthy: ${health.healthy ? chalk11.green("yes") : chalk11.red("no")}`);
1523
- 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))}`);
1524
1674
  const task = health.task;
1525
1675
  if (task) {
1526
1676
  if (task.exit_code !== void 0 && task.exit_code !== null) {
1527
1677
  const code = String(task.exit_code);
1528
- 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)}`);
1529
1679
  }
1530
1680
  if (task.stopped_reason) {
1531
- console.log(` Stopped: ${chalk11.yellow(task.stopped_reason)}`);
1681
+ console.log(` Stopped: ${chalk12.yellow(task.stopped_reason)}`);
1532
1682
  }
1533
1683
  if (task.container_reason) {
1534
- console.log(` Detail: ${chalk11.yellow(task.container_reason)}`);
1684
+ console.log(` Detail: ${chalk12.yellow(task.container_reason)}`);
1535
1685
  }
1536
1686
  }
1537
1687
  if (!health.healthy) {
1538
- console.log(chalk11.gray("\nRun `videan logs` to see why."));
1688
+ console.log(chalk12.gray("\nRun `videan logs` to see why."));
1539
1689
  }
1540
1690
  });
1541
1691
 
1542
1692
  // src/commands/keys.ts
1543
- import { Command as Command11 } from "commander";
1544
- import chalk12 from "chalk";
1693
+ import { Command as Command12 } from "commander";
1694
+ import chalk13 from "chalk";
1545
1695
  import inquirer5 from "inquirer";
1546
- 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");
1547
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(
1548
1698
  "--app <id>",
1549
1699
  "Pin the key to one app (a service token, for code running inside it)"
@@ -1555,7 +1705,7 @@ keysCommand.command("create <name>").description("Create an API key").option("-o
1555
1705
  if (opts.expires !== void 0) {
1556
1706
  expiresInDays = Number(opts.expires);
1557
1707
  if (!Number.isInteger(expiresInDays) || expiresInDays < 1) {
1558
- 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."));
1559
1709
  process.exit(1);
1560
1710
  }
1561
1711
  }
@@ -1566,22 +1716,22 @@ keysCommand.command("create <name>").description("Create an API key").option("-o
1566
1716
  expiresInDays,
1567
1717
  appId: opts.app
1568
1718
  });
1569
- console.log(chalk12.green(`Created key "${key.name}" (${key.id})`));
1719
+ console.log(chalk13.green(`Created key "${key.name}" (${key.id})`));
1570
1720
  console.log(`
1571
- ${chalk12.bold(key.key)}
1721
+ ${chalk13.bold(key.key)}
1572
1722
  `);
1573
- 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."));
1574
1724
  if (key.appId) {
1575
1725
  console.log(
1576
- 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.`)
1577
1727
  );
1578
1728
  } else {
1579
1729
  console.log(
1580
- 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.")
1581
1731
  );
1582
1732
  }
1583
1733
  if (key.expiresAt) {
1584
- console.log(chalk12.gray(`Expires ${key.expiresAt}`));
1734
+ console.log(chalk13.gray(`Expires ${key.expiresAt}`));
1585
1735
  }
1586
1736
  });
1587
1737
  keysCommand.command("list").description("List API keys").option("-o, --org <id>", "Organization ID").action(async (opts) => {
@@ -1591,13 +1741,13 @@ keysCommand.command("list").description("List API keys").option("-o, --org <id>"
1591
1741
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1592
1742
  const keys = await client4.apiKeys.list(orgId);
1593
1743
  if (keys.length === 0) {
1594
- 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>`."));
1595
1745
  return;
1596
1746
  }
1597
1747
  const widths = [24, 24, 16, 22, 12, 24];
1598
1748
  console.log(formatRow(["NAME", "ID", "PREFIX", "LAST USED", "STATE", "SCOPE"], widths));
1599
1749
  for (const key of keys) {
1600
- 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");
1601
1751
  console.log(formatRow(
1602
1752
  [
1603
1753
  key.name,
@@ -1620,13 +1770,13 @@ keysCommand.command("revoke <id>").description("Revoke an API key").action(async
1620
1770
  if (!confirm) return;
1621
1771
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1622
1772
  await client4.apiKeys.revoke(id);
1623
- console.log(chalk12.green("Key revoked."));
1773
+ console.log(chalk13.green("Key revoked."));
1624
1774
  });
1625
1775
 
1626
1776
  // src/commands/orgs.ts
1627
- import { Command as Command12 } from "commander";
1628
- import chalk13 from "chalk";
1629
- 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");
1630
1780
  orgsCommand.command("list").description("List your organizations").action(async () => {
1631
1781
  const apiKey = getApiKey();
1632
1782
  requireApiKey(apiKey);
@@ -1634,14 +1784,14 @@ orgsCommand.command("list").description("List your organizations").action(async
1634
1784
  const orgs = await client4.orgs.list();
1635
1785
  const current = getDefaultOrg();
1636
1786
  if (orgs.length === 0) {
1637
- 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>`."));
1638
1788
  return;
1639
1789
  }
1640
1790
  const widths = [24, 24, 10, 8];
1641
1791
  console.log(formatRow(["NAME", "ID", "PLAN", ""], widths));
1642
1792
  for (const org of orgs) {
1643
1793
  console.log(formatRow(
1644
- [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") : ""],
1645
1795
  widths
1646
1796
  ));
1647
1797
  }
@@ -1652,7 +1802,7 @@ orgsCommand.command("create <name>").description("Create an organization").actio
1652
1802
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1653
1803
  const org = await client4.orgs.create(name);
1654
1804
  if (!getDefaultOrg()) setDefaultOrg(org.id);
1655
- console.log(chalk13.green(`Organization "${org.name}" created (${org.id})`));
1805
+ console.log(chalk14.green(`Organization "${org.name}" created (${org.id})`));
1656
1806
  });
1657
1807
  orgsCommand.command("use <id>").description("Set the default organization for this machine").action(async (id) => {
1658
1808
  const apiKey = getApiKey();
@@ -1660,7 +1810,7 @@ orgsCommand.command("use <id>").description("Set the default organization for th
1660
1810
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1661
1811
  const org = await client4.orgs.get(id);
1662
1812
  setDefaultOrg(org.id);
1663
- console.log(chalk13.green(`Default organization set to "${org.name}".`));
1813
+ console.log(chalk14.green(`Default organization set to "${org.name}".`));
1664
1814
  });
1665
1815
  orgsCommand.command("members").description("List organization members").option("-o, --org <id>", "Organization ID").action(async (opts) => {
1666
1816
  const apiKey = getApiKey();
@@ -1683,15 +1833,15 @@ orgsCommand.command("invite <email>").description("Invite a member by email").op
1683
1833
  const orgId = requireOrg(opts.org || getDefaultOrg());
1684
1834
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1685
1835
  await client4.orgs.invite(orgId, email);
1686
- console.log(chalk13.green(`Invited ${email}.`));
1836
+ console.log(chalk14.green(`Invited ${email}.`));
1687
1837
  });
1688
1838
 
1689
1839
  // src/commands/storage.ts
1690
- import { Command as Command13 } from "commander";
1691
- import chalk14 from "chalk";
1692
- import fs3 from "fs-extra";
1693
- import path3 from "node:path";
1694
- 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");
1695
1845
  function client() {
1696
1846
  const apiKey = getApiKey();
1697
1847
  requireApiKey(apiKey);
@@ -1700,7 +1850,7 @@ function client() {
1700
1850
  function requireApp(opt) {
1701
1851
  const appId = opt || getCurrentApp();
1702
1852
  if (!appId) {
1703
- 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."));
1704
1854
  process.exit(1);
1705
1855
  }
1706
1856
  return appId;
@@ -1723,7 +1873,7 @@ storageCommand.command("ls [prefix]").description("List stored objects").option(
1723
1873
  return;
1724
1874
  }
1725
1875
  if (objects.length === 0) {
1726
- 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>`."));
1727
1877
  return;
1728
1878
  }
1729
1879
  const widths = [52, 12, 24];
@@ -1732,42 +1882,42 @@ storageCommand.command("ls [prefix]").description("List stored objects").option(
1732
1882
  console.log(formatRow([o.key, human(o.size), o.lastModified || "-"], widths));
1733
1883
  }
1734
1884
  const total = objects.reduce((sum, o) => sum + o.size, 0);
1735
- console.log(chalk14.dim(`
1885
+ console.log(chalk15.dim(`
1736
1886
  ${objects.length} object(s), ${human(total)}`));
1737
1887
  });
1738
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) => {
1739
1889
  const appId = requireApp(opts.app);
1740
- if (!await fs3.pathExists(file)) {
1741
- console.error(chalk14.red(`No such file: ${file}`));
1890
+ if (!await fs5.pathExists(file)) {
1891
+ console.error(chalk15.red(`No such file: ${file}`));
1742
1892
  process.exit(1);
1743
1893
  }
1744
- const body = await fs3.readFile(file);
1745
- const objectKey = key || path3.basename(file);
1894
+ const body = await fs5.readFile(file);
1895
+ const objectKey = key || path5.basename(file);
1746
1896
  const stored = await client().storage.upload(appId, objectKey, body, {
1747
1897
  contentType: opts.type
1748
1898
  });
1749
1899
  console.log(
1750
- chalk14.green(`Uploaded ${objectKey}`) + chalk14.dim(` (${human(body.length)})`)
1900
+ chalk15.green(`Uploaded ${objectKey}`) + chalk15.dim(` (${human(body.length)})`)
1751
1901
  );
1752
- if (stored.etag) console.log(chalk14.dim(`etag ${stored.etag}`));
1902
+ if (stored.etag) console.log(chalk15.dim(`etag ${stored.etag}`));
1753
1903
  });
1754
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) => {
1755
1905
  const appId = requireApp(opts.app);
1756
1906
  const bytes = await client().storage.download(appId, key);
1757
- const dest = file || path3.basename(key);
1758
- await fs3.writeFile(dest, bytes);
1759
- 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)})`));
1760
1910
  });
1761
1911
  storageCommand.command("rm <key>").description("Delete an object").option("-a, --app <id>", "App ID").action(async (key, opts) => {
1762
1912
  const appId = requireApp(opts.app);
1763
1913
  await client().storage.delete(appId, key);
1764
- console.log(chalk14.green(`Deleted ${key}`));
1914
+ console.log(chalk15.green(`Deleted ${key}`));
1765
1915
  });
1766
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) => {
1767
1917
  const appId = requireApp(opts.app);
1768
1918
  const expiresIn = Number(opts.expires);
1769
1919
  if (!Number.isInteger(expiresIn) || expiresIn < 30 || expiresIn > 3600) {
1770
- 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."));
1771
1921
  process.exit(1);
1772
1922
  }
1773
1923
  const signed = await client().storage.presign(appId, key, {
@@ -1777,7 +1927,7 @@ storageCommand.command("url <key>").description("Print a temporary link to an ob
1777
1927
  });
1778
1928
  console.log(signed.url);
1779
1929
  console.error(
1780
- chalk14.yellow(
1930
+ chalk15.yellow(
1781
1931
  `This ${signed.method} link works for ${expiresIn}s for anyone who has it.`
1782
1932
  )
1783
1933
  );
@@ -1789,10 +1939,10 @@ storageCommand.command("usage").description("Total objects and bytes stored").op
1789
1939
  });
1790
1940
 
1791
1941
  // src/commands/email.ts
1792
- import { Command as Command14 } from "commander";
1793
- import chalk15 from "chalk";
1794
- import fs4 from "fs-extra";
1795
- 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");
1796
1946
  function client2() {
1797
1947
  const apiKey = getApiKey();
1798
1948
  requireApiKey(apiKey);
@@ -1801,7 +1951,7 @@ function client2() {
1801
1951
  function requireApp2(opt) {
1802
1952
  const appId = opt || getCurrentApp();
1803
1953
  if (!appId) {
1804
- 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."));
1805
1955
  process.exit(1);
1806
1956
  }
1807
1957
  return appId;
@@ -1812,12 +1962,12 @@ emailCommand.command("send").description("Send an email").requiredOption("-t, --
1812
1962
  let text = opts.text;
1813
1963
  let html = opts.html;
1814
1964
  if (opts.bodyFile) {
1815
- const contents = await fs4.readFile(opts.bodyFile, "utf-8");
1965
+ const contents = await fs6.readFile(opts.bodyFile, "utf-8");
1816
1966
  if (/\.html?$/i.test(opts.bodyFile)) html = contents;
1817
1967
  else text = contents;
1818
1968
  }
1819
1969
  if (!text && !html) {
1820
- 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."));
1821
1971
  process.exit(1);
1822
1972
  }
1823
1973
  const sent = await client2().email.send(appId, {
@@ -1831,12 +1981,12 @@ emailCommand.command("send").description("Send an email").requiredOption("-t, --
1831
1981
  console.log(JSON.stringify(sent, null, 2));
1832
1982
  return;
1833
1983
  }
1834
- console.log(chalk15.green("Sent"));
1984
+ console.log(chalk16.green("Sent"));
1835
1985
  console.log(` From: ${sent.from}`);
1836
1986
  console.log(` To: ${sent.to.join(", ")}`);
1837
1987
  console.log(` Message: ${sent.messageId ?? "(no id returned)"}`);
1838
1988
  console.log(
1839
- chalk15.dim(
1989
+ chalk16.dim(
1840
1990
  ` Quota: ${sent.quota.sent}/${sent.quota.quota} today, resets ${sent.quota.resetsAt}`
1841
1991
  )
1842
1992
  );
@@ -1851,14 +2001,14 @@ emailCommand.command("quota").description("Show today's sending allowance").opti
1851
2001
  console.log(`Sent today: ${quota.sent}`);
1852
2002
  console.log(`Daily quota: ${quota.quota}`);
1853
2003
  console.log(`Remaining: ${quota.remaining}`);
1854
- console.log(chalk15.dim(`Resets at: ${quota.resetsAt}`));
2004
+ console.log(chalk16.dim(`Resets at: ${quota.resetsAt}`));
1855
2005
  });
1856
2006
 
1857
2007
  // src/commands/jobs.ts
1858
- import { Command as Command15 } from "commander";
1859
- import chalk16 from "chalk";
1860
- import fs5 from "fs-extra";
1861
- 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");
1862
2012
  function client3() {
1863
2013
  const apiKey = getApiKey();
1864
2014
  requireApiKey(apiKey);
@@ -1867,7 +2017,7 @@ function client3() {
1867
2017
  function requireApp3(opt) {
1868
2018
  const appId = opt || getCurrentApp();
1869
2019
  if (!appId) {
1870
- 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."));
1871
2021
  process.exit(1);
1872
2022
  }
1873
2023
  return appId;
@@ -1875,30 +2025,30 @@ function requireApp3(opt) {
1875
2025
  function intOption2(flag, raw) {
1876
2026
  const n = Number(raw);
1877
2027
  if (!Number.isInteger(n) || n < 0) {
1878
- console.error(chalk16.red(`${flag} expects a whole number, got "${raw}".`));
2028
+ console.error(chalk17.red(`${flag} expects a whole number, got "${raw}".`));
1879
2029
  process.exit(1);
1880
2030
  }
1881
2031
  return n;
1882
2032
  }
1883
2033
  var STATUS_COLOR = {
1884
- queued: chalk16.yellow,
1885
- running: chalk16.cyan,
1886
- succeeded: chalk16.green,
1887
- dead: chalk16.red,
1888
- cancelled: chalk16.gray
2034
+ queued: chalk17.yellow,
2035
+ running: chalk17.cyan,
2036
+ succeeded: chalk17.green,
2037
+ dead: chalk17.red,
2038
+ cancelled: chalk17.gray
1889
2039
  };
1890
2040
  function statusLabel(status) {
1891
- return (STATUS_COLOR[status] ?? chalk16.white)(status);
2041
+ return (STATUS_COLOR[status] ?? chalk17.white)(status);
1892
2042
  }
1893
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) => {
1894
2044
  const appId = requireApp3(opts.app);
1895
2045
  let payload;
1896
- const raw = opts.file ? await fs5.readFile(opts.file, "utf-8") : opts.data;
2046
+ const raw = opts.file ? await fs7.readFile(opts.file, "utf-8") : opts.data;
1897
2047
  if (raw !== void 0) {
1898
2048
  try {
1899
2049
  payload = JSON.parse(raw);
1900
2050
  } catch (e) {
1901
- 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}`));
1902
2052
  process.exit(1);
1903
2053
  }
1904
2054
  }
@@ -1916,13 +2066,13 @@ jobsCommand.command("enqueue").description("Queue a job for the app to run").opt
1916
2066
  return;
1917
2067
  }
1918
2068
  if (job.deduplicated) {
1919
- 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}")`));
1920
2070
  } else {
1921
- console.log(chalk16.green(`Queued ${job.id}`));
2071
+ console.log(chalk17.green(`Queued ${job.id}`));
1922
2072
  }
1923
- 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}"`));
1924
2074
  if (job.runAt && Date.parse(job.runAt) > Date.now() + 1e3) {
1925
- console.log(chalk16.gray(` Runs at ${new Date(job.runAt).toLocaleString()}`));
2075
+ console.log(chalk17.gray(` Runs at ${new Date(job.runAt).toLocaleString()}`));
1926
2076
  }
1927
2077
  });
1928
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) => {
@@ -1937,17 +2087,17 @@ jobsCommand.command("list").description("List jobs, newest first").option("-a, -
1937
2087
  return;
1938
2088
  }
1939
2089
  const depth = Object.entries(counts).map(([status, n]) => `${statusLabel(status)} ${n}`).join(" ");
1940
- console.log(depth || chalk16.gray("No jobs yet."));
2090
+ console.log(depth || chalk17.gray("No jobs yet."));
1941
2091
  if (jobs.length === 0) return;
1942
2092
  console.log();
1943
2093
  for (const job of jobs) {
1944
- const attempts = job.attempts > 0 ? chalk16.gray(` ${job.attempts}/${job.maxAttempts}`) : "";
2094
+ const attempts = job.attempts > 0 ? chalk17.gray(` ${job.attempts}/${job.maxAttempts}`) : "";
1945
2095
  console.log(
1946
- `${statusLabel(job.status).padEnd(20)} ${chalk16.white(job.id)} ${chalk16.gray(
2096
+ `${statusLabel(job.status).padEnd(20)} ${chalk17.white(job.id)} ${chalk17.gray(
1947
2097
  `${job.queue} ${job.path}`
1948
2098
  )}${attempts}`
1949
2099
  );
1950
- if (job.error) console.log(chalk16.red(` ${job.error}`));
2100
+ if (job.error) console.log(chalk17.red(` ${job.error}`));
1951
2101
  }
1952
2102
  });
1953
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) => {
@@ -1956,17 +2106,17 @@ jobsCommand.command("get <job-id>").description("Show one job in full").option("
1956
2106
  console.log(JSON.stringify(job, null, 2));
1957
2107
  return;
1958
2108
  }
1959
- console.log(`${chalk16.white(job.id)} ${statusLabel(job.status)}`);
1960
- console.log(chalk16.gray(` ${job.method} ${job.path} on queue "${job.queue}"`));
1961
- console.log(chalk16.gray(` Attempts: ${job.attempts}/${job.maxAttempts}`));
1962
- if (job.statusCode) console.log(chalk16.gray(` Last response: HTTP ${job.statusCode}`));
1963
- if (job.durationMs) console.log(chalk16.gray(` Took: ${job.durationMs}ms`));
1964
- 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}`));
1965
2115
  if (job.status === "queued" && job.attempts > 0) {
1966
- console.log(chalk16.yellow(` Retrying at ${new Date(job.runAt).toLocaleString()}`));
2116
+ console.log(chalk17.yellow(` Retrying at ${new Date(job.runAt).toLocaleString()}`));
1967
2117
  }
1968
2118
  if (job.payload !== null && job.payload !== void 0) {
1969
- console.log(chalk16.gray(" Payload:"));
2119
+ console.log(chalk17.gray(" Payload:"));
1970
2120
  console.log(
1971
2121
  JSON.stringify(job.payload, null, 2).split("\n").map((l) => ` ${l}`).join("\n")
1972
2122
  );
@@ -1974,17 +2124,17 @@ jobsCommand.command("get <job-id>").description("Show one job in full").option("
1974
2124
  });
1975
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) => {
1976
2126
  const job = await client3().jobs.retry(requireApp3(opts.app), jobId);
1977
- console.log(chalk16.green(`Requeued ${job.id}`));
2127
+ console.log(chalk17.green(`Requeued ${job.id}`));
1978
2128
  });
1979
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) => {
1980
2130
  const job = await client3().jobs.cancel(requireApp3(opts.app), jobId);
1981
- console.log(chalk16.green(`Cancelled ${job.id}`));
2131
+ console.log(chalk17.green(`Cancelled ${job.id}`));
1982
2132
  });
1983
2133
 
1984
2134
  // src/commands/usage.ts
1985
- import { Command as Command16 } from "commander";
1986
- import chalk17 from "chalk";
1987
- 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) => {
1988
2138
  const apiKey = getApiKey();
1989
2139
  requireApiKey(apiKey);
1990
2140
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
@@ -1992,61 +2142,61 @@ var usageCommand = new Command16("usage").description("Metered usage and cost fo
1992
2142
  const appId = opts.app || getCurrentApp();
1993
2143
  const report2 = await client4.apps.usage(appId, opts.period);
1994
2144
  if (opts.json) return void console.log(JSON.stringify(report2, null, 2));
1995
- console.log(chalk17.dim(`${period(report2.periodStart, report2.periodEnd)} app ${appId}`));
2145
+ console.log(chalk18.dim(`${period(report2.periodStart, report2.periodEnd)} app ${appId}`));
1996
2146
  console.log();
1997
2147
  printMetrics(report2.metrics);
1998
2148
  console.log();
1999
2149
  for (const line of report2.lines) {
2000
- console.log(` ${chalk17.dim(line.label.padEnd(22))} ${money(line.cents)}`);
2150
+ console.log(` ${chalk18.dim(line.label.padEnd(22))} ${money(line.cents)}`);
2001
2151
  }
2002
- 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)}`);
2003
2153
  console.log();
2004
2154
  console.log(
2005
- 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.")
2006
2156
  );
2007
2157
  return;
2008
2158
  }
2009
2159
  const orgId = opts.org || getDefaultOrg();
2010
2160
  if (!orgId) {
2011
- 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."));
2012
2162
  process.exit(1);
2013
2163
  }
2014
2164
  const report = await client4.orgs.usage(orgId, opts.period);
2015
2165
  if (opts.json) return void console.log(JSON.stringify(report, null, 2));
2016
2166
  const inv = report.invoice;
2017
2167
  console.log(
2018
- 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}`)
2019
2169
  );
2020
2170
  console.log();
2021
2171
  printMetrics(report.metrics);
2022
2172
  if (report.apps.length > 0) {
2023
2173
  console.log();
2024
- console.log(chalk17.bold("By app"));
2174
+ console.log(chalk18.bold("By app"));
2025
2175
  for (const app of [...report.apps].sort((a, b) => b.cents - a.cents)) {
2026
2176
  const name = app.slug || app.name || app.appId || "(deleted apps)";
2027
- console.log(` ${chalk17.dim(String(name).padEnd(24))} ${money(app.cents)}`);
2177
+ console.log(` ${chalk18.dim(String(name).padEnd(24))} ${money(app.cents)}`);
2028
2178
  }
2029
2179
  }
2030
2180
  console.log();
2031
2181
  for (const line of inv.lines) {
2032
2182
  console.log(
2033
- ` ${chalk17.dim(line.label.padEnd(22))} ${money(line.cents)} ${chalk17.dim(
2183
+ ` ${chalk18.dim(line.label.padEnd(22))} ${money(line.cents)} ${chalk18.dim(
2034
2184
  `(${line.quantity} \xD7 ${line.unitPriceCents}\xA2)`
2035
2185
  )}`
2036
2186
  );
2037
2187
  }
2038
- console.log(` ${chalk17.dim("Usage".padEnd(22))} ${money(inv.usageCents)}`);
2188
+ console.log(` ${chalk18.dim("Usage".padEnd(22))} ${money(inv.usageCents)}`);
2039
2189
  if (inv.includedUsageCents > 0) {
2040
2190
  console.log(
2041
- ` ${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))}`)}`
2042
2192
  );
2043
2193
  }
2044
2194
  if (inv.baseCents > 0) {
2045
- console.log(` ${chalk17.dim("Plan".padEnd(22))} ${money(inv.baseCents)}`);
2195
+ console.log(` ${chalk18.dim("Plan".padEnd(22))} ${money(inv.baseCents)}`);
2046
2196
  }
2047
- console.log(` ${chalk17.bold("Total".padEnd(22))} ${chalk17.bold(report.total)}`);
2197
+ console.log(` ${chalk18.bold("Total".padEnd(22))} ${chalk18.bold(report.total)}`);
2048
2198
  console.log();
2049
- 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."));
2050
2200
  });
2051
2201
  usageCommand.command("plans").description("The plan catalogue and what each one allows").option("--json", "Print the raw response").action(async (opts) => {
2052
2202
  const client4 = new CloudClient({ apiKey: getApiKey() || "", baseUrl: getBaseUrl() });
@@ -2054,11 +2204,11 @@ usageCommand.command("plans").description("The plan catalogue and what each one
2054
2204
  if (opts.json) return void console.log(JSON.stringify(plans, null, 2));
2055
2205
  for (const plan of plans) {
2056
2206
  console.log(
2057
- `${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`) : "")
2058
2208
  );
2059
2209
  const l = plan.limits;
2060
2210
  console.log(
2061
- chalk17.dim(
2211
+ chalk18.dim(
2062
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" : ""}`
2063
2213
  )
2064
2214
  );
@@ -2067,11 +2217,11 @@ usageCommand.command("plans").description("The plan catalogue and what each one
2067
2217
  });
2068
2218
  function printMetrics(metrics) {
2069
2219
  if (metrics.length === 0) {
2070
- console.log(chalk17.dim(" Nothing metered in this period yet."));
2220
+ console.log(chalk18.dim(" Nothing metered in this period yet."));
2071
2221
  return;
2072
2222
  }
2073
2223
  for (const m of metrics) {
2074
- console.log(` ${chalk17.dim(m.metric.padEnd(22))} ${round(m.quantity)}`);
2224
+ console.log(` ${chalk18.dim(m.metric.padEnd(22))} ${round(m.quantity)}`);
2075
2225
  }
2076
2226
  }
2077
2227
  function round(n) {
@@ -2086,12 +2236,13 @@ function period(start, end) {
2086
2236
  }
2087
2237
 
2088
2238
  // src/index.ts
2089
- var program = new Command17();
2239
+ var program = new Command18();
2090
2240
  program.name("videan").description("Videan CLI \u2014 deploy apps in seconds").version("0.1.0");
2091
2241
  program.addCommand(loginCommand);
2092
2242
  program.addCommand(logoutCommand);
2093
2243
  program.addCommand(initCommand);
2094
2244
  program.addCommand(deployCommand);
2245
+ program.addCommand(pullCommand);
2095
2246
  program.addCommand(logsCommand);
2096
2247
  program.addCommand(metricsCommand);
2097
2248
  program.addCommand(rollbackCommand);