deepspace 0.3.6 → 0.3.8

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/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli/cli.ts
4
- import { defineCommand as defineCommand14, runMain } from "citty";
4
+ import { defineCommand as defineCommand16, runMain } from "citty";
5
5
  import { execSync as execSync5 } from "child_process";
6
6
  import { readFileSync as readFileSync9, existsSync as existsSync11 } from "fs";
7
7
  import { dirname as dirname2, join as join12 } from "path";
@@ -14,7 +14,24 @@ import { join as join2 } from "path";
14
14
  import { homedir } from "os";
15
15
  import { exec } from "child_process";
16
16
  import { platform } from "os";
17
+ import * as p2 from "@clack/prompts";
18
+
19
+ // src/cli/lib/spinner.ts
17
20
  import * as p from "@clack/prompts";
21
+ function createSpinner() {
22
+ if (process.stdout.isTTY) return p.spinner();
23
+ return {
24
+ start: (msg) => {
25
+ if (msg) console.log(msg);
26
+ },
27
+ stop: (msg) => {
28
+ if (msg) console.log(msg);
29
+ },
30
+ message: (msg) => {
31
+ if (msg) console.log(msg);
32
+ }
33
+ };
34
+ }
18
35
 
19
36
  // src/cli/env.ts
20
37
  import { existsSync, readFileSync, writeFileSync } from "fs";
@@ -261,36 +278,36 @@ var login_default = defineCommand({
261
278
  console.log("Logged in");
262
279
  return;
263
280
  }
264
- p.intro("DeepSpace Login");
265
- const s = p.spinner();
281
+ p2.intro("DeepSpace Login");
282
+ const s = createSpinner();
266
283
  s.start("Creating login session...");
267
284
  const sessionRes = await fetch(`${AUTH_URL}/api/auth/cli/session`, { method: "POST" });
268
285
  if (!sessionRes.ok) {
269
286
  s.stop("Failed");
270
- p.cancel("Could not create login session");
287
+ p2.cancel("Could not create login session");
271
288
  process.exit(1);
272
289
  }
273
290
  const sessionData = await sessionRes.json();
274
291
  if (!sessionData.sessionId || !sessionData.loginUrl) {
275
292
  s.stop("Failed");
276
- p.cancel("Could not create login session");
293
+ p2.cancel("Could not create login session");
277
294
  process.exit(1);
278
295
  }
279
296
  const { sessionId, loginUrl } = sessionData;
280
297
  s.stop("Opening browser...");
281
- p.log.info(`If the browser doesn't open, visit:
298
+ p2.log.info(`If the browser doesn't open, visit:
282
299
  ${loginUrl}`);
283
300
  openBrowser(loginUrl);
284
301
  s.start("Waiting for authentication...");
285
302
  const result = await pollForCompletion(sessionId);
286
303
  if (!result) {
287
304
  s.stop("Timed out");
288
- p.cancel("Login timed out. Run `deepspace login` to try again.");
305
+ p2.cancel("Login timed out. Run `deepspace login` to try again.");
289
306
  process.exit(1);
290
307
  }
291
308
  storeCredentials(result.sessionToken, result.jwt);
292
309
  s.stop("Authenticated");
293
- p.outro(`Logged in as ${result.name ?? result.email}`);
310
+ p2.outro(`Logged in as ${result.name ?? result.email}`);
294
311
  }
295
312
  });
296
313
  async function pollForCompletion(sessionId) {
@@ -328,7 +345,7 @@ function openBrowser(url) {
328
345
  });
329
346
  }
330
347
  function sleep(ms) {
331
- return new Promise((resolve6) => setTimeout(resolve6, ms));
348
+ return new Promise((resolve7) => setTimeout(resolve7, ms));
332
349
  }
333
350
  async function doEmailLogin(email, password) {
334
351
  const res = await fetch(`${AUTH_URL}/api/auth/sign-in/email`, {
@@ -422,13 +439,13 @@ async function ensureToken() {
422
439
  return existing;
423
440
  }
424
441
  }
425
- const token = await exchangeSession(AUTH_URL3, sessionToken);
426
- if (!token) {
442
+ const token2 = await exchangeSession(AUTH_URL3, sessionToken);
443
+ if (!token2) {
427
444
  throw new Error("Session expired. Run `deepspace login` to re-authenticate.");
428
445
  }
429
446
  mkdirSync2(DIR2, { recursive: true });
430
- writeFileSync3(TOKEN_PATH2, token, { mode: 384 });
431
- return token;
447
+ writeFileSync3(TOKEN_PATH2, token2, { mode: 384 });
448
+ return token2;
432
449
  }
433
450
  function isTokenValid(jwt) {
434
451
  try {
@@ -471,16 +488,16 @@ var dev_default = defineCommand3({
471
488
  console.error("No wrangler.toml found. Are you in a DeepSpace app directory?");
472
489
  process.exit(1);
473
490
  }
474
- let token;
491
+ let token2;
475
492
  try {
476
- token = await ensureToken();
493
+ token2 = await ensureToken();
477
494
  } catch (err) {
478
495
  console.error(err.message);
479
496
  process.exit(1);
480
497
  }
481
498
  let payload;
482
499
  try {
483
- payload = JSON.parse(atob(token.split(".")[1]));
500
+ payload = JSON.parse(atob(token2.split(".")[1]));
484
501
  } catch {
485
502
  console.error("Malformed session token. Run `npx deepspace login`.");
486
503
  process.exit(1);
@@ -488,7 +505,7 @@ var dev_default = defineCommand3({
488
505
  console.log(`Logged in as ${payload.name ?? payload.email}`);
489
506
  console.log(`Environment: ${env}`);
490
507
  console.log(`Port: ${port}`);
491
- await writeDevVars(appDir, env, payload.sub, token);
508
+ await writeDevVars(appDir, env, payload.sub, token2);
492
509
  console.log("Starting dev server...\n");
493
510
  const vite = spawn(
494
511
  "npx",
@@ -681,7 +698,33 @@ function isAlive(pid) {
681
698
  import { defineCommand as defineCommand5 } from "citty";
682
699
  import { existsSync as existsSync5 } from "fs";
683
700
  import { join as join6, resolve as resolve2 } from "path";
701
+ import { spawnSync as spawnSync3 } from "child_process";
702
+
703
+ // src/cli/lib/playwright.ts
684
704
  import { execSync, spawnSync as spawnSync2 } from "child_process";
705
+ function ensurePlaywright(appDir) {
706
+ try {
707
+ execSync("npx playwright --version", { cwd: appDir, stdio: "pipe" });
708
+ } catch {
709
+ console.log("Installing Playwright...");
710
+ execSync("npm install -D @playwright/test", { cwd: appDir, stdio: "pipe" });
711
+ }
712
+ const isLinux = process.platform === "linux";
713
+ const isRoot = isLinux && typeof process.getuid === "function" && process.getuid() === 0;
714
+ const args = isRoot ? ["install", "--with-deps", "chromium"] : ["install", "chromium"];
715
+ const result = spawnSync2("npx", ["playwright", ...args], {
716
+ cwd: appDir,
717
+ stdio: "inherit",
718
+ shell: process.platform === "win32"
719
+ });
720
+ if (result.status !== 0) {
721
+ console.error("\nPlaywright install failed. On Linux, system libs may be missing. Try:");
722
+ console.error(" sudo npx playwright install --with-deps chromium");
723
+ process.exit(1);
724
+ }
725
+ }
726
+
727
+ // src/cli/commands/test.ts
685
728
  var DEFAULT_PORT3 = 5173;
686
729
  var test_default = defineCommand5({
687
730
  meta: {
@@ -708,17 +751,17 @@ var test_default = defineCommand5({
708
751
  console.error("No wrangler.toml found. Are you in a DeepSpace app directory?");
709
752
  process.exit(1);
710
753
  }
711
- let token;
754
+ let token2;
712
755
  let ownerId;
713
756
  try {
714
- token = await ensureToken();
715
- const payload = JSON.parse(atob(token.split(".")[1]));
757
+ token2 = await ensureToken();
758
+ const payload = JSON.parse(atob(token2.split(".")[1]));
716
759
  ownerId = payload.sub;
717
760
  } catch (err) {
718
761
  console.error("`deepspace test` requires you to be logged in. Run `deepspace login` first.");
719
762
  process.exit(1);
720
763
  }
721
- await writeDevVars(appDir, "dev", ownerId, token);
764
+ await writeDevVars(appDir, "dev", ownerId, token2);
722
765
  if (suite !== "unit") {
723
766
  ensurePlaywright(appDir);
724
767
  }
@@ -756,7 +799,7 @@ var test_default = defineCommand5({
756
799
  }
757
800
  });
758
801
  function runPlaywright(appDir, testFiles, port) {
759
- const result = spawnSync2("npx", [
802
+ const result = spawnSync3("npx", [
760
803
  "playwright",
761
804
  "test",
762
805
  "--config",
@@ -771,30 +814,13 @@ function runPlaywright(appDir, testFiles, port) {
771
814
  return result.status ?? 1;
772
815
  }
773
816
  function runVitest(appDir) {
774
- const result = spawnSync2("npx", ["vitest", "run", "--passWithNoTests"], {
817
+ const result = spawnSync3("npx", ["vitest", "run", "--passWithNoTests"], {
775
818
  cwd: appDir,
776
819
  stdio: "inherit",
777
820
  shell: process.platform === "win32"
778
821
  });
779
822
  return result.status ?? 1;
780
823
  }
781
- function ensurePlaywright(appDir) {
782
- try {
783
- execSync("npx playwright --version", { cwd: appDir, stdio: "pipe" });
784
- } catch {
785
- console.log("Installing Playwright...");
786
- execSync("npm install -D @playwright/test", { cwd: appDir, stdio: "pipe" });
787
- }
788
- const isLinux = process.platform === "linux";
789
- const isRoot = isLinux && typeof process.getuid === "function" && process.getuid() === 0;
790
- const args = isRoot ? ["install", "--with-deps", "chromium"] : ["install", "chromium"];
791
- const result = spawnSync2("npx", ["playwright", ...args], { cwd: appDir, stdio: "inherit" });
792
- if (result.status !== 0) {
793
- console.error("\nPlaywright install failed. On Linux, system libs may be missing \u2014 try:");
794
- console.error(" sudo npx playwright install --with-deps chromium");
795
- process.exit(1);
796
- }
797
- }
798
824
  function resolvePort3(arg) {
799
825
  const raw = arg ?? process.env.DEEPSPACE_PORT;
800
826
  if (!raw) return DEFAULT_PORT3;
@@ -806,9 +832,57 @@ function resolvePort3(arg) {
806
832
  return n;
807
833
  }
808
834
 
809
- // src/cli/commands/test-accounts.ts
835
+ // src/cli/commands/screenshot.ts
810
836
  import { defineCommand as defineCommand6 } from "citty";
811
- import * as p2 from "@clack/prompts";
837
+ import { spawnSync as spawnSync4 } from "child_process";
838
+ import { resolve as resolve3 } from "path";
839
+ var screenshot_default = defineCommand6({
840
+ meta: {
841
+ name: "screenshot",
842
+ description: "Take a Playwright screenshot, installing Chromium on demand"
843
+ },
844
+ args: {
845
+ url: {
846
+ type: "positional",
847
+ description: "URL to capture, for example http://localhost:5173/",
848
+ required: true
849
+ },
850
+ output: {
851
+ type: "positional",
852
+ description: "Output image path, for example screenshot.png",
853
+ required: true
854
+ },
855
+ "full-page": {
856
+ type: "boolean",
857
+ description: "Capture the full scrollable page",
858
+ required: false
859
+ },
860
+ "wait-for-timeout": {
861
+ type: "string",
862
+ description: "Milliseconds to wait before capture",
863
+ required: false
864
+ }
865
+ },
866
+ run({ args }) {
867
+ const appDir = resolve3(".");
868
+ ensurePlaywright(appDir);
869
+ const playwrightArgs = ["playwright", "screenshot", args.url, args.output];
870
+ if (args["full-page"]) playwrightArgs.push("--full-page");
871
+ if (args["wait-for-timeout"]) {
872
+ playwrightArgs.push("--wait-for-timeout", String(args["wait-for-timeout"]));
873
+ }
874
+ const result = spawnSync4("npx", playwrightArgs, {
875
+ cwd: appDir,
876
+ stdio: "inherit",
877
+ shell: process.platform === "win32"
878
+ });
879
+ process.exit(result.status ?? 1);
880
+ }
881
+ });
882
+
883
+ // src/cli/commands/test-accounts.ts
884
+ import { defineCommand as defineCommand7 } from "citty";
885
+ import * as p3 from "@clack/prompts";
812
886
  import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
813
887
  import { join as join7 } from "path";
814
888
  import { homedir as homedir4 } from "os";
@@ -829,8 +903,8 @@ function saveAccounts(accounts) {
829
903
  writeFileSync4(ACCOUNTS_PATH, JSON.stringify(accounts, null, 2), { mode: 384 });
830
904
  }
831
905
  function sessionCookie() {
832
- const token = readFileSync4(SESSION_PATH2, "utf-8").trim();
833
- return `${SESSION_COOKIE2}=${encodeURIComponent(token)}`;
906
+ const token2 = readFileSync4(SESSION_PATH2, "utf-8").trim();
907
+ return `${SESSION_COOKIE2}=${encodeURIComponent(token2)}`;
834
908
  }
835
909
  async function fetchRemoteAccounts() {
836
910
  const res = await fetch(`${AUTH_URL4}/api/auth/test-accounts`, {
@@ -852,7 +926,7 @@ async function deleteRemote(id) {
852
926
  throw new Error(data.error ?? `DELETE returned ${res.status}`);
853
927
  }
854
928
  }
855
- var create = defineCommand6({
929
+ var create = defineCommand7({
856
930
  meta: {
857
931
  name: "create",
858
932
  description: "Create a test account"
@@ -922,7 +996,7 @@ var create = defineCommand6({
922
996
  Saved to ${ACCOUNTS_PATH}`);
923
997
  }
924
998
  });
925
- var list = defineCommand6({
999
+ var list = defineCommand7({
926
1000
  meta: {
927
1001
  name: "list",
928
1002
  description: "List your test accounts"
@@ -957,7 +1031,7 @@ var list = defineCommand6({
957
1031
  }
958
1032
  }
959
1033
  });
960
- var del = defineCommand6({
1034
+ var del = defineCommand7({
961
1035
  meta: {
962
1036
  name: "delete",
963
1037
  description: "Delete a test account by --email or --id"
@@ -1014,7 +1088,7 @@ var del = defineCommand6({
1014
1088
  console.log(`Test account deleted${targetEmail ? `: ${targetEmail}` : `: ${targetId}`}`);
1015
1089
  }
1016
1090
  });
1017
- var clear = defineCommand6({
1091
+ var clear = defineCommand7({
1018
1092
  meta: {
1019
1093
  name: "clear",
1020
1094
  description: "Delete all your test accounts (or those matching --label)"
@@ -1048,11 +1122,11 @@ var clear = defineCommand6({
1048
1122
  }
1049
1123
  if (!args.yes) {
1050
1124
  const subject = args.label ? `${targets.length} test account(s) labeled '${args.label}'` : `all ${targets.length} test account(s)`;
1051
- const confirmed = await p2.confirm({
1125
+ const confirmed = await p3.confirm({
1052
1126
  message: `Delete ${subject}? This is not reversible.`,
1053
1127
  initialValue: false
1054
1128
  });
1055
- if (p2.isCancel(confirmed) || !confirmed) {
1129
+ if (p3.isCancel(confirmed) || !confirmed) {
1056
1130
  console.log("Cancelled.");
1057
1131
  return;
1058
1132
  }
@@ -1080,7 +1154,7 @@ Failed:`);
1080
1154
  }
1081
1155
  }
1082
1156
  });
1083
- var test_accounts_default = defineCommand6({
1157
+ var test_accounts_default = defineCommand7({
1084
1158
  meta: {
1085
1159
  name: "test-accounts",
1086
1160
  description: "Manage test accounts for development"
@@ -1094,12 +1168,12 @@ var test_accounts_default = defineCommand6({
1094
1168
  });
1095
1169
 
1096
1170
  // src/cli/commands/deploy.ts
1097
- import { defineCommand as defineCommand7 } from "citty";
1171
+ import { defineCommand as defineCommand8 } from "citty";
1098
1172
  import { execSync as execSync2 } from "child_process";
1099
1173
  import { readFileSync as readFileSync5, readdirSync, existsSync as existsSync7 } from "fs";
1100
1174
  import { parse as parseToml2 } from "smol-toml";
1101
- import { join as join8, resolve as resolve3, dirname } from "path";
1102
- import * as p3 from "@clack/prompts";
1175
+ import { join as join8, resolve as resolve4, dirname } from "path";
1176
+ import * as p4 from "@clack/prompts";
1103
1177
 
1104
1178
  // src/server/rooms/binding-manifest.ts
1105
1179
  var AUTO_PROVISION_SENTINEL = "auto";
@@ -1359,7 +1433,7 @@ function sanitizeAppName(raw) {
1359
1433
 
1360
1434
  // src/cli/commands/deploy.ts
1361
1435
  var DEPLOY_URL = process.env.DEEPSPACE_DEPLOY_URL ?? ENVS.prod.deploy;
1362
- var deploy_default = defineCommand7({
1436
+ var deploy_default = defineCommand8({
1363
1437
  meta: {
1364
1438
  name: "deploy",
1365
1439
  description: "Build and deploy your DeepSpace app"
@@ -1372,30 +1446,30 @@ var deploy_default = defineCommand7({
1372
1446
  }
1373
1447
  },
1374
1448
  async run({ args }) {
1375
- const appDir = resolve3(args.dir ?? ".");
1376
- p3.intro("Deploying DeepSpace app");
1449
+ const appDir = resolve4(args.dir ?? ".");
1450
+ p4.intro("Deploying DeepSpace app");
1377
1451
  const wranglerPath = join8(appDir, "wrangler.toml");
1378
1452
  if (!existsSync7(wranglerPath)) {
1379
- p3.cancel("No wrangler.toml found. Are you in a DeepSpace app directory?");
1453
+ p4.cancel("No wrangler.toml found. Are you in a DeepSpace app directory?");
1380
1454
  process.exit(1);
1381
1455
  }
1382
1456
  const wranglerConfig = parseToml2(readFileSync5(wranglerPath, "utf-8"));
1383
1457
  const nameRes = resolveAppName(wranglerConfig.name);
1384
1458
  if (!nameRes.ok) {
1385
- p3.cancel(`wrangler.toml: ${nameRes.reason}`);
1459
+ p4.cancel(`wrangler.toml: ${nameRes.reason}`);
1386
1460
  process.exit(1);
1387
1461
  }
1388
- if (nameRes.warning) p3.log.warn(nameRes.warning);
1462
+ if (nameRes.warning) p4.log.warn(nameRes.warning);
1389
1463
  const appName = nameRes.name;
1390
- p3.log.info(`App: ${appName}`);
1391
- let token;
1464
+ p4.log.info(`App: ${appName}`);
1465
+ let token2;
1392
1466
  try {
1393
- token = await ensureToken();
1467
+ token2 = await ensureToken();
1394
1468
  } catch (err) {
1395
- p3.cancel(err.message);
1469
+ p4.cancel(err.message);
1396
1470
  process.exit(1);
1397
1471
  }
1398
- const s = p3.spinner();
1472
+ const s = createSpinner();
1399
1473
  s.start("Building...");
1400
1474
  try {
1401
1475
  execSync2("npx vite build", { cwd: appDir, stdio: "pipe" });
@@ -1407,25 +1481,25 @@ var deploy_default = defineCommand7({
1407
1481
  s.stop("Built");
1408
1482
  const deployConfigPath = join8(appDir, ".wrangler", "deploy", "config.json");
1409
1483
  if (!existsSync7(deployConfigPath)) {
1410
- p3.cancel("Build output config not found at .wrangler/deploy/config.json");
1484
+ p4.cancel("Build output config not found at .wrangler/deploy/config.json");
1411
1485
  process.exit(1);
1412
1486
  }
1413
1487
  const deployConfig = JSON.parse(readFileSync5(deployConfigPath, "utf-8"));
1414
- const outputWranglerPath = resolve3(dirname(deployConfigPath), deployConfig.configPath);
1488
+ const outputWranglerPath = resolve4(dirname(deployConfigPath), deployConfig.configPath);
1415
1489
  if (!existsSync7(outputWranglerPath)) {
1416
- p3.cancel(`Output wrangler.json not found at ${outputWranglerPath}`);
1490
+ p4.cancel(`Output wrangler.json not found at ${outputWranglerPath}`);
1417
1491
  process.exit(1);
1418
1492
  }
1419
1493
  const outputConfig = JSON.parse(readFileSync5(outputWranglerPath, "utf-8"));
1420
1494
  const workerDir = dirname(outputWranglerPath);
1421
1495
  const workerBundlePath = join8(workerDir, outputConfig.main);
1422
- const clientDir = outputConfig.assets?.directory ? resolve3(workerDir, outputConfig.assets.directory) : null;
1496
+ const clientDir = outputConfig.assets?.directory ? resolve4(workerDir, outputConfig.assets.directory) : null;
1423
1497
  if (!existsSync7(workerBundlePath)) {
1424
- p3.cancel(`Worker bundle not found at ${workerBundlePath}`);
1498
+ p4.cancel(`Worker bundle not found at ${workerBundlePath}`);
1425
1499
  process.exit(1);
1426
1500
  }
1427
1501
  if (!clientDir || !existsSync7(clientDir)) {
1428
- p3.cancel(`Client assets not found at ${clientDir}`);
1502
+ p4.cancel(`Client assets not found at ${clientDir}`);
1429
1503
  process.exit(1);
1430
1504
  }
1431
1505
  s.start("Collecting assets...");
@@ -1442,19 +1516,19 @@ var deploy_default = defineCommand7({
1442
1516
  sqlite: sqliteClasses.has(b.class_name)
1443
1517
  }));
1444
1518
  if (doManifest?.length) {
1445
- p3.log.info(`DO manifest: ${doManifest.length} binding(s)`);
1519
+ p4.log.info(`DO manifest: ${doManifest.length} binding(s)`);
1446
1520
  }
1447
1521
  const customBindings = bindingManifestFromOutputConfig(outputConfig);
1448
1522
  const validation = validateBindingManifest(customBindings);
1449
1523
  if (!validation.valid) {
1450
- p3.cancel(
1524
+ p4.cancel(
1451
1525
  `Invalid binding(s) in wrangler.toml:
1452
1526
  ` + validation.errors.map((e) => ` \u2022 ${e.reason}`).join("\n")
1453
1527
  );
1454
1528
  process.exit(1);
1455
1529
  }
1456
1530
  if (customBindings.length) {
1457
- p3.log.info(
1531
+ p4.log.info(
1458
1532
  `Custom bindings: ${customBindings.map((b) => `${b.name} (${b.type})`).join(", ")}`
1459
1533
  );
1460
1534
  }
@@ -1466,7 +1540,7 @@ var deploy_default = defineCommand7({
1466
1540
  try {
1467
1541
  userSecrets = parseDevVars(customSection);
1468
1542
  } catch (err) {
1469
- p3.cancel(`.dev.vars: ${err.message}`);
1543
+ p4.cancel(`.dev.vars: ${err.message}`);
1470
1544
  process.exit(1);
1471
1545
  }
1472
1546
  }
@@ -1478,20 +1552,20 @@ var deploy_default = defineCommand7({
1478
1552
  ]);
1479
1553
  for (const name of userSecretNames) {
1480
1554
  if (RESERVED_BINDING_NAMES.has(name)) {
1481
- p3.cancel(
1555
+ p4.cancel(
1482
1556
  `.dev.vars: "${name}" is a reserved binding name and cannot be used as an app secret.`
1483
1557
  );
1484
1558
  process.exit(1);
1485
1559
  }
1486
1560
  if (declaredBindingNames.has(name)) {
1487
- p3.cancel(
1561
+ p4.cancel(
1488
1562
  `.dev.vars: "${name}" collides with a binding declared in wrangler.toml \u2014 rename one or the other.`
1489
1563
  );
1490
1564
  process.exit(1);
1491
1565
  }
1492
1566
  }
1493
1567
  if (userSecretNames.length) {
1494
- p3.log.info(`App secrets (from .dev.vars): ${userSecretNames.join(", ")}`);
1568
+ p4.log.info(`App secrets (from .dev.vars): ${userSecretNames.join(", ")}`);
1495
1569
  }
1496
1570
  s.start(`Deploying to ${appName}.app.space...`);
1497
1571
  const form = new FormData();
@@ -1508,13 +1582,13 @@ var deploy_default = defineCommand7({
1508
1582
  }
1509
1583
  const res = await fetch(`${DEPLOY_URL}/api/deploy/${appName}`, {
1510
1584
  method: "POST",
1511
- headers: { Authorization: `Bearer ${token}` },
1585
+ headers: { Authorization: `Bearer ${token2}` },
1512
1586
  body: form
1513
1587
  });
1514
1588
  const body = await res.json().catch(() => ({}));
1515
1589
  if (!res.ok || !body.success) {
1516
1590
  s.stop("Deploy failed");
1517
- p3.cancel(body.error ?? `Deployment error (${res.status})`);
1591
+ p4.cancel(body.error ?? `Deployment error (${res.status})`);
1518
1592
  process.exit(1);
1519
1593
  }
1520
1594
  if (body.url) {
@@ -1522,15 +1596,15 @@ var deploy_default = defineCommand7({
1522
1596
  const ok = await waitForAssetsReady(body.url, 9e4);
1523
1597
  if (!ok) {
1524
1598
  s.stop("Deployed (edge propagation still in progress after 90s)");
1525
- p3.log.warn("First requests may briefly hit the assets transitional page; retry in a minute.");
1526
- p3.log.success(`Live at: ${body.url}`);
1527
- p3.outro("Done");
1599
+ p4.log.warn("First requests may briefly hit the assets transitional page; retry in a minute.");
1600
+ p4.log.success(`Live at: ${body.url}`);
1601
+ p4.outro("Done");
1528
1602
  return;
1529
1603
  }
1530
1604
  }
1531
1605
  s.stop("Deployed!");
1532
- p3.log.success(`Live at: ${body.url}`);
1533
- p3.outro("Done");
1606
+ p4.log.success(`Live at: ${body.url}`);
1607
+ p4.outro("Done");
1534
1608
  }
1535
1609
  });
1536
1610
  async function waitForAssetsReady(url, timeoutMs) {
@@ -1573,13 +1647,13 @@ function collectAssets(dir) {
1573
1647
  }
1574
1648
 
1575
1649
  // src/cli/commands/undeploy.ts
1576
- import { defineCommand as defineCommand8 } from "citty";
1650
+ import { defineCommand as defineCommand9 } from "citty";
1577
1651
  import { readFileSync as readFileSync6, existsSync as existsSync8 } from "fs";
1578
1652
  import { join as join9 } from "path";
1579
1653
  import { parse as parseToml3 } from "smol-toml";
1580
- import * as p4 from "@clack/prompts";
1654
+ import * as p5 from "@clack/prompts";
1581
1655
  var DEPLOY_URL2 = process.env.DEEPSPACE_DEPLOY_URL ?? ENVS.prod.deploy;
1582
- var undeploy_default = defineCommand8({
1656
+ var undeploy_default = defineCommand9({
1583
1657
  meta: {
1584
1658
  name: "undeploy",
1585
1659
  description: "Remove a deployed DeepSpace app"
@@ -1601,37 +1675,37 @@ var undeploy_default = defineCommand8({
1601
1675
  }
1602
1676
  }
1603
1677
  if (!appName) {
1604
- p4.cancel("Provide an app name or run from a DeepSpace app directory.");
1678
+ p5.cancel("Provide an app name or run from a DeepSpace app directory.");
1605
1679
  process.exit(1);
1606
1680
  }
1607
- let token;
1681
+ let token2;
1608
1682
  try {
1609
- token = await ensureToken();
1683
+ token2 = await ensureToken();
1610
1684
  } catch (err) {
1611
- p4.cancel(err.message);
1685
+ p5.cancel(err.message);
1612
1686
  process.exit(1);
1613
1687
  }
1614
- p4.intro(`Undeploying ${appName}`);
1615
- const s = p4.spinner();
1688
+ p5.intro(`Undeploying ${appName}`);
1689
+ const s = createSpinner();
1616
1690
  s.start("Removing...");
1617
1691
  const res = await fetch(`${DEPLOY_URL2}/api/deploy/${appName}`, {
1618
1692
  method: "DELETE",
1619
- headers: { Authorization: `Bearer ${token}` }
1693
+ headers: { Authorization: `Bearer ${token2}` }
1620
1694
  });
1621
1695
  const body = await res.json().catch(() => ({}));
1622
1696
  if (!res.ok || !body.success) {
1623
1697
  s.stop("Failed");
1624
- p4.cancel(body.error ?? `Undeploy error (${res.status})`);
1698
+ p5.cancel(body.error ?? `Undeploy error (${res.status})`);
1625
1699
  process.exit(1);
1626
1700
  }
1627
1701
  s.stop("Removed");
1628
- p4.outro(`${appName}.app.space has been taken down`);
1702
+ p5.outro(`${appName}.app.space has been taken down`);
1629
1703
  }
1630
1704
  });
1631
1705
 
1632
1706
  // src/cli/commands/whoami.ts
1633
- import { defineCommand as defineCommand9 } from "citty";
1634
- var whoami_default = defineCommand9({
1707
+ import { defineCommand as defineCommand10 } from "citty";
1708
+ var whoami_default = defineCommand10({
1635
1709
  meta: {
1636
1710
  name: "whoami",
1637
1711
  description: "Show the currently logged-in user"
@@ -1685,16 +1759,16 @@ var whoami_default = defineCommand9({
1685
1759
  });
1686
1760
 
1687
1761
  // src/cli/commands/add.ts
1688
- import { defineCommand as defineCommand10 } from "citty";
1762
+ import { defineCommand as defineCommand11 } from "citty";
1689
1763
  import { existsSync as existsSync9 } from "fs";
1690
- import { resolve as resolve4, join as join10 } from "path";
1764
+ import { resolve as resolve5, join as join10 } from "path";
1691
1765
  import { execSync as execSync3 } from "child_process";
1692
1766
  function findScript(from) {
1693
1767
  const candidate = join10(from, ".deepspace", "scripts", "add-feature.cjs");
1694
1768
  if (existsSync9(candidate)) return candidate;
1695
1769
  return null;
1696
1770
  }
1697
- var add_default = defineCommand10({
1771
+ var add_default = defineCommand11({
1698
1772
  meta: {
1699
1773
  name: "add",
1700
1774
  description: "Add a feature to your DeepSpace app"
@@ -1723,7 +1797,7 @@ var add_default = defineCommand10({
1723
1797
  }
1724
1798
  },
1725
1799
  run({ args }) {
1726
- const appDir = resolve4(args.dir ?? ".");
1800
+ const appDir = resolve5(args.dir ?? ".");
1727
1801
  const script = findScript(appDir);
1728
1802
  if (!script) {
1729
1803
  console.error("No .deepspace/scripts/add-feature.cjs found.");
@@ -1748,15 +1822,15 @@ var add_default = defineCommand10({
1748
1822
  });
1749
1823
 
1750
1824
  // src/cli/commands/domain.ts
1751
- import { defineCommand as defineCommand11 } from "citty";
1825
+ import { defineCommand as defineCommand12 } from "citty";
1752
1826
  import { execSync as execSync4 } from "child_process";
1753
1827
 
1754
1828
  // src/cli/lib/app-context.ts
1755
1829
  import { existsSync as existsSync10, readFileSync as readFileSync7 } from "fs";
1756
- import { join as join11, resolve as resolve5 } from "path";
1830
+ import { join as join11, resolve as resolve6 } from "path";
1757
1831
  import { parse as parseToml4 } from "smol-toml";
1758
1832
  function detectAppName(cwd = process.cwd()) {
1759
- const wranglerPath = join11(resolve5(cwd), "wrangler.toml");
1833
+ const wranglerPath = join11(resolve6(cwd), "wrangler.toml");
1760
1834
  if (!existsSync10(wranglerPath)) return null;
1761
1835
  try {
1762
1836
  const cfg = parseToml4(readFileSync7(wranglerPath, "utf-8"));
@@ -1776,11 +1850,11 @@ function requireAppName(explicit, cwd) {
1776
1850
 
1777
1851
  // src/cli/commands/domain.ts
1778
1852
  var API_URL = process.env.DEEPSPACE_API_URL ?? ENVS.prod.api;
1779
- async function api(token, path, init) {
1853
+ async function api(token2, path, init) {
1780
1854
  const res = await fetch(`${API_URL}${path}`, {
1781
1855
  ...init,
1782
1856
  headers: {
1783
- Authorization: `Bearer ${token}`,
1857
+ Authorization: `Bearer ${token2}`,
1784
1858
  "Content-Type": "application/json",
1785
1859
  ...init?.headers ?? {}
1786
1860
  }
@@ -1796,6 +1870,18 @@ async function api(token, path, init) {
1796
1870
  }
1797
1871
  return JSON.parse(text);
1798
1872
  }
1873
+ function scrubInternal(value) {
1874
+ if (Array.isArray(value)) return value.map(scrubInternal);
1875
+ if (value && typeof value === "object") {
1876
+ const out = {};
1877
+ for (const [k, v] of Object.entries(value)) {
1878
+ if (k === "costCents") continue;
1879
+ out[k] = scrubInternal(v);
1880
+ }
1881
+ return out;
1882
+ }
1883
+ return value;
1884
+ }
1799
1885
  function fmtCents(cents) {
1800
1886
  if (cents === null || cents === void 0) return "\u2014";
1801
1887
  return `$${(cents / 100).toFixed(2)}`;
@@ -1809,15 +1895,15 @@ async function confirm2(message) {
1809
1895
  throw new Error(`${message} \u2014 pass --yes to confirm non-interactively`);
1810
1896
  }
1811
1897
  process.stdout.write(`${message} [y/N] `);
1812
- return new Promise((resolve6) => {
1898
+ return new Promise((resolve7) => {
1813
1899
  process.stdin.setEncoding("utf-8");
1814
1900
  process.stdin.once("data", (data) => {
1815
1901
  const answer = data.toString().trim().toLowerCase();
1816
- resolve6(answer === "y" || answer === "yes");
1902
+ resolve7(answer === "y" || answer === "yes");
1817
1903
  });
1818
1904
  });
1819
1905
  }
1820
- var search = defineCommand11({
1906
+ var search = defineCommand12({
1821
1907
  meta: { name: "search", description: "Search for available domains" },
1822
1908
  args: {
1823
1909
  query: { type: "positional", description: "Domain or keyword", required: true },
@@ -1825,14 +1911,14 @@ var search = defineCommand11({
1825
1911
  json: { type: "boolean", description: "Emit JSON instead of a table" }
1826
1912
  },
1827
1913
  async run({ args }) {
1828
- const token = await ensureToken();
1914
+ const token2 = await ensureToken();
1829
1915
  const limit = parseInt(args.limit ?? "10", 10) || 10;
1830
1916
  const result = await api(
1831
- token,
1917
+ token2,
1832
1918
  `/api/domains/search?q=${encodeURIComponent(args.query)}&limit=${limit}`
1833
1919
  );
1834
1920
  if (args.json) {
1835
- process.stdout.write(JSON.stringify(result, null, 2) + "\n");
1921
+ process.stdout.write(JSON.stringify(scrubInternal(result), null, 2) + "\n");
1836
1922
  return;
1837
1923
  }
1838
1924
  if (result.domains.length === 0) {
@@ -1847,20 +1933,20 @@ var search = defineCommand11({
1847
1933
  }
1848
1934
  }
1849
1935
  });
1850
- var buy = defineCommand11({
1936
+ var buy = defineCommand12({
1851
1937
  meta: { name: "buy", description: "Buy a domain and attach it to an app" },
1852
1938
  args: {
1853
1939
  domain: { type: "positional", description: "Domain to buy", required: true },
1854
1940
  app: { type: "string", description: "App to attach (defaults to ./wrangler.toml)" },
1855
1941
  yes: { type: "boolean", description: "Skip the confirmation prompt", default: false },
1856
- "no-open": { type: "boolean", description: "Do not open browser; print Stripe URL", default: false },
1857
- "no-wait": { type: "boolean", description: "Exit after creating session; do not poll until active", default: false },
1942
+ open: { type: "boolean", description: "Open browser at Stripe URL (use --no-open to print URL only)", default: true },
1943
+ wait: { type: "boolean", description: "Poll until provisioning completes (use --no-wait to exit after session creation)", default: true },
1858
1944
  json: { type: "boolean", description: "Emit JSON to stdout", default: false }
1859
1945
  },
1860
1946
  async run({ args }) {
1861
- const token = await ensureToken();
1947
+ const token2 = await ensureToken();
1862
1948
  const appName = requireAppName(args.app);
1863
- const priceCheck = await api(token, "/api/domains/check", {
1949
+ const priceCheck = await api(token2, "/api/domains/check", {
1864
1950
  method: "POST",
1865
1951
  body: JSON.stringify({ domains: [args.domain] })
1866
1952
  });
@@ -1881,7 +1967,7 @@ var buy = defineCommand11({
1881
1967
  }
1882
1968
  }
1883
1969
  const checkout = await api(
1884
- token,
1970
+ token2,
1885
1971
  "/api/domains/checkout",
1886
1972
  {
1887
1973
  method: "POST",
@@ -1889,14 +1975,14 @@ var buy = defineCommand11({
1889
1975
  }
1890
1976
  );
1891
1977
  if (args.json) {
1892
- process.stdout.write(JSON.stringify({ ...checkout, expectedTime, registrar: result.registrar }, null, 2) + "\n");
1978
+ process.stdout.write(JSON.stringify(scrubInternal({ ...checkout, expectedTime, registrar: result.registrar }), null, 2) + "\n");
1893
1979
  } else {
1894
1980
  console.log(`Stripe Checkout: ${checkout.url}`);
1895
1981
  }
1896
- if (!args["no-open"] && process.stdin.isTTY) {
1982
+ if (args.open && process.stdin.isTTY) {
1897
1983
  openBrowser2(checkout.url);
1898
1984
  }
1899
- if (args["no-wait"]) return;
1985
+ if (!args.wait) return;
1900
1986
  const timeoutMs = result.registrar === "cloudflare" ? 5 * 60 * 1e3 : 60 * 60 * 1e3;
1901
1987
  if (!args.json) {
1902
1988
  console.log(`Waiting for payment & provisioning (up to ${expectedTime}). Press Ctrl-C anytime \u2014`);
@@ -1908,7 +1994,7 @@ var buy = defineCommand11({
1908
1994
  await new Promise((r) => setTimeout(r, 5e3));
1909
1995
  try {
1910
1996
  const detail = await api(
1911
- token,
1997
+ token2,
1912
1998
  `/api/domains/${checkout.purchaseId}`
1913
1999
  );
1914
2000
  if (detail.domain.status !== lastStatus) {
@@ -1917,7 +2003,7 @@ var buy = defineCommand11({
1917
2003
  }
1918
2004
  if (detail.domain.status === "active") {
1919
2005
  if (args.json) {
1920
- process.stdout.write(JSON.stringify({ ...checkout, status: "active", domain: args.domain }, null, 2) + "\n");
2006
+ process.stdout.write(JSON.stringify(scrubInternal({ ...checkout, status: "active", domain: args.domain }), null, 2) + "\n");
1921
2007
  } else {
1922
2008
  console.log(`\u2713 ${args.domain} is live and pointing at ${appName}`);
1923
2009
  }
@@ -1936,16 +2022,16 @@ var buy = defineCommand11({
1936
2022
  process.exit(1);
1937
2023
  }
1938
2024
  });
1939
- var list2 = defineCommand11({
2025
+ var list2 = defineCommand12({
1940
2026
  meta: { name: "list", description: "List your domains" },
1941
2027
  args: {
1942
2028
  json: { type: "boolean", description: "Emit JSON" }
1943
2029
  },
1944
2030
  async run({ args }) {
1945
- const token = await ensureToken();
1946
- const result = await api(token, "/api/domains");
2031
+ const token2 = await ensureToken();
2032
+ const result = await api(token2, "/api/domains");
1947
2033
  if (args.json) {
1948
- process.stdout.write(JSON.stringify(result, null, 2) + "\n");
2034
+ process.stdout.write(JSON.stringify(scrubInternal(result), null, 2) + "\n");
1949
2035
  return;
1950
2036
  }
1951
2037
  if (result.domains.length === 0) {
@@ -1960,22 +2046,22 @@ var list2 = defineCommand11({
1960
2046
  }
1961
2047
  }
1962
2048
  });
1963
- var status = defineCommand11({
2049
+ var status = defineCommand12({
1964
2050
  meta: { name: "status", description: "Show details for one domain" },
1965
2051
  args: {
1966
2052
  domain: { type: "positional", description: "Domain", required: true },
1967
2053
  json: { type: "boolean" }
1968
2054
  },
1969
2055
  async run({ args }) {
1970
- const token = await ensureToken();
1971
- const all = await api(token, "/api/domains");
2056
+ const token2 = await ensureToken();
2057
+ const all = await api(token2, "/api/domains");
1972
2058
  const found = all.domains.find((d) => d.domain === args.domain);
1973
2059
  if (!found) {
1974
2060
  console.error(`No domain ${args.domain} in your account`);
1975
2061
  process.exit(1);
1976
2062
  }
1977
2063
  if (args.json) {
1978
- process.stdout.write(JSON.stringify(found, null, 2) + "\n");
2064
+ process.stdout.write(JSON.stringify(scrubInternal(found), null, 2) + "\n");
1979
2065
  return;
1980
2066
  }
1981
2067
  console.log(`Domain: ${found.domain}`);
@@ -1988,7 +2074,7 @@ var status = defineCommand11({
1988
2074
  console.log(`Charged: ${fmtCents(found.chargedCents)}/yr`);
1989
2075
  }
1990
2076
  });
1991
- var attach = defineCommand11({
2077
+ var attach = defineCommand12({
1992
2078
  meta: { name: "attach", description: "Re-point a domain at a different app" },
1993
2079
  args: {
1994
2080
  domain: { type: "positional", description: "Domain to re-point", required: true },
@@ -1996,16 +2082,16 @@ var attach = defineCommand11({
1996
2082
  json: { type: "boolean" }
1997
2083
  },
1998
2084
  async run({ args }) {
1999
- const token = await ensureToken();
2085
+ const token2 = await ensureToken();
2000
2086
  const targetApp = requireAppName(args.app);
2001
- const all = await api(token, "/api/domains");
2087
+ const all = await api(token2, "/api/domains");
2002
2088
  const found = all.domains.find((d) => d.domain === args.domain);
2003
2089
  if (!found) {
2004
2090
  console.error(`No domain ${args.domain} in your account`);
2005
2091
  process.exit(1);
2006
2092
  }
2007
2093
  const result = await api(
2008
- token,
2094
+ token2,
2009
2095
  `/api/domains/${found.id}/reattach`,
2010
2096
  {
2011
2097
  method: "POST",
@@ -2019,7 +2105,7 @@ var attach = defineCommand11({
2019
2105
  console.log(`\u2713 ${args.domain} now points at ${targetApp}`);
2020
2106
  }
2021
2107
  });
2022
- var detach = defineCommand11({
2108
+ var detach = defineCommand12({
2023
2109
  meta: {
2024
2110
  name: "detach",
2025
2111
  description: "Stop routing the domain (keeps the registration; auto-renew unchanged)"
@@ -2029,8 +2115,8 @@ var detach = defineCommand11({
2029
2115
  yes: { type: "boolean", description: "Skip the confirmation prompt" }
2030
2116
  },
2031
2117
  async run({ args }) {
2032
- const token = await ensureToken();
2033
- const all = await api(token, "/api/domains");
2118
+ const token2 = await ensureToken();
2119
+ const all = await api(token2, "/api/domains");
2034
2120
  const found = all.domains.find((d) => d.domain === args.domain);
2035
2121
  if (!found) {
2036
2122
  console.error(`No domain ${args.domain} in your account`);
@@ -2045,11 +2131,11 @@ var detach = defineCommand11({
2045
2131
  return;
2046
2132
  }
2047
2133
  }
2048
- await api(token, `/api/domains/${found.id}`, { method: "DELETE" });
2134
+ await api(token2, `/api/domains/${found.id}`, { method: "DELETE" });
2049
2135
  console.log(`\u2713 ${args.domain} detached. Use \`deepspace domain attach\` to re-route, or \`renew --auto off\` to stop auto-renewal.`);
2050
2136
  }
2051
2137
  });
2052
- var renew = defineCommand11({
2138
+ var renew = defineCommand12({
2053
2139
  meta: { name: "renew", description: "Toggle auto-renewal at the registrar" },
2054
2140
  args: {
2055
2141
  domain: { type: "positional", description: "Domain", required: true },
@@ -2060,15 +2146,15 @@ var renew = defineCommand11({
2060
2146
  console.error('--auto must be "on" or "off"');
2061
2147
  process.exit(1);
2062
2148
  }
2063
- const token = await ensureToken();
2064
- const all = await api(token, "/api/domains");
2149
+ const token2 = await ensureToken();
2150
+ const all = await api(token2, "/api/domains");
2065
2151
  const found = all.domains.find((d) => d.domain === args.domain);
2066
2152
  if (!found) {
2067
2153
  console.error(`No domain ${args.domain} in your account`);
2068
2154
  process.exit(1);
2069
2155
  }
2070
2156
  const enabled = args.auto === "on";
2071
- await api(token, `/api/domains/${found.id}/auto-renew`, {
2157
+ await api(token2, `/api/domains/${found.id}/auto-renew`, {
2072
2158
  method: "POST",
2073
2159
  body: JSON.stringify({ autoRenew: enabled })
2074
2160
  });
@@ -2083,13 +2169,13 @@ function openBrowser2(url) {
2083
2169
  } catch {
2084
2170
  }
2085
2171
  }
2086
- var domain_default = defineCommand11({
2172
+ var domain_default = defineCommand12({
2087
2173
  meta: { name: "domain", description: "Buy and manage custom domains" },
2088
2174
  subCommands: { search, buy, list: list2, status, attach, detach, renew }
2089
2175
  });
2090
2176
 
2091
2177
  // src/cli/commands/integrations.ts
2092
- import { defineCommand as defineCommand12 } from "citty";
2178
+ import { defineCommand as defineCommand13 } from "citty";
2093
2179
 
2094
2180
  // src/cli/commands/_invoke-impl.ts
2095
2181
  import { readFileSync as readFileSync8 } from "fs";
@@ -2263,7 +2349,7 @@ async function runInvoke(args) {
2263
2349
  }
2264
2350
 
2265
2351
  // src/cli/commands/integrations.ts
2266
- var list3 = defineCommand12({
2352
+ var list3 = defineCommand13({
2267
2353
  meta: {
2268
2354
  name: "list",
2269
2355
  description: "List all available integrations"
@@ -2284,7 +2370,7 @@ var list3 = defineCommand12({
2284
2370
  }
2285
2371
  }
2286
2372
  });
2287
- var info = defineCommand12({
2373
+ var info = defineCommand13({
2288
2374
  meta: {
2289
2375
  name: "info",
2290
2376
  description: "Print schema + example body for an integration endpoint"
@@ -2310,7 +2396,7 @@ var info = defineCommand12({
2310
2396
  }
2311
2397
  }
2312
2398
  });
2313
- var invoke = defineCommand12({
2399
+ var invoke = defineCommand13({
2314
2400
  meta: {
2315
2401
  name: "invoke",
2316
2402
  description: "Invoke a platform integration as the logged-in user"
@@ -2363,7 +2449,7 @@ var invoke = defineCommand12({
2363
2449
  }
2364
2450
  }
2365
2451
  });
2366
- var integrations_default = defineCommand12({
2452
+ var integrations_default = defineCommand13({
2367
2453
  meta: {
2368
2454
  name: "integrations",
2369
2455
  description: "List, inspect, and invoke platform integrations"
@@ -2376,8 +2462,8 @@ var integrations_default = defineCommand12({
2376
2462
  });
2377
2463
 
2378
2464
  // src/cli/commands/invoke.ts
2379
- import { defineCommand as defineCommand13 } from "citty";
2380
- var invoke_default = defineCommand13({
2465
+ import { defineCommand as defineCommand14 } from "citty";
2466
+ var invoke_default = defineCommand14({
2381
2467
  meta: {
2382
2468
  name: "invoke",
2383
2469
  description: "Invoke a platform integration as the logged-in user"
@@ -2449,13 +2535,256 @@ var invoke_default = defineCommand13({
2449
2535
  }
2450
2536
  });
2451
2537
 
2538
+ // src/cli/commands/managed-repos.ts
2539
+ import { defineCommand as defineCommand15 } from "citty";
2540
+ var API_URL3 = process.env.DEEPSPACE_API_URL ?? ENVS.prod.api;
2541
+ var SIMPLE_APP_ID = /^[a-z0-9][a-z0-9_.:-]{0,119}$/i;
2542
+ var SIMPLE_APP_NAME = /^[a-z0-9][a-z0-9_-]{0,119}$/i;
2543
+ async function api2(token2, path, init) {
2544
+ const res = await fetch(`${API_URL3}${path}`, {
2545
+ ...init,
2546
+ headers: {
2547
+ Authorization: `Bearer ${token2}`,
2548
+ "Content-Type": "application/json",
2549
+ ...init?.headers ?? {}
2550
+ }
2551
+ });
2552
+ const text = await res.text();
2553
+ if (!res.ok) {
2554
+ let msg = text;
2555
+ try {
2556
+ msg = JSON.parse(text).error ?? text;
2557
+ } catch {
2558
+ }
2559
+ throw new Error(`API ${path} (${res.status}): ${msg}`);
2560
+ }
2561
+ return JSON.parse(text);
2562
+ }
2563
+ function defaultAppId(appName) {
2564
+ return `cli-${slugId(appName)}`;
2565
+ }
2566
+ function slugId(raw) {
2567
+ return raw.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "") || "app";
2568
+ }
2569
+ function assertSimple(value, pattern, label) {
2570
+ if (!pattern.test(value)) {
2571
+ throw new Error(`${label} must start with a letter or number and contain only simple identifier characters.`);
2572
+ }
2573
+ }
2574
+ function printQuota(quota) {
2575
+ console.log(`Repo quota: ${quota.activeCount} / ${quota.limit} active (${quota.remaining} remaining)`);
2576
+ }
2577
+ function printRepo(repo) {
2578
+ console.log(`${repo.fullName}`);
2579
+ console.log(` ID: ${repo.id}`);
2580
+ console.log(` App: ${repo.appName}`);
2581
+ console.log(` App ID: ${repo.appId}`);
2582
+ console.log(` URL: ${repo.htmlUrl}`);
2583
+ console.log(` Clone: ${repo.cloneUrl}`);
2584
+ }
2585
+ async function confirm3(message) {
2586
+ if (!process.stdin.isTTY) {
2587
+ throw new Error(`${message} \u2014 pass --yes to confirm non-interactively`);
2588
+ }
2589
+ process.stdout.write(`${message} [y/N] `);
2590
+ return new Promise((resolve7) => {
2591
+ process.stdin.setEncoding("utf-8");
2592
+ process.stdin.once("data", (data) => {
2593
+ const answer = data.toString().trim().toLowerCase();
2594
+ resolve7(answer === "y" || answer === "yes");
2595
+ });
2596
+ });
2597
+ }
2598
+ var list4 = defineCommand15({
2599
+ meta: {
2600
+ name: "list",
2601
+ description: "List your DeepSpace-managed repos and quota"
2602
+ },
2603
+ args: {
2604
+ json: {
2605
+ type: "boolean",
2606
+ description: "Emit JSON instead of human output",
2607
+ default: false
2608
+ }
2609
+ },
2610
+ async run({ args }) {
2611
+ const token2 = await ensureToken();
2612
+ const result = await api2(token2, "/api/github/repos");
2613
+ if (args.json) {
2614
+ process.stdout.write(JSON.stringify(result, null, 2) + "\n");
2615
+ return;
2616
+ }
2617
+ printQuota(result.quota);
2618
+ if (result.repos.length === 0) {
2619
+ console.log("No DeepSpace-managed repos. Create one with `deepspace managed-repos create <app-name>`.");
2620
+ return;
2621
+ }
2622
+ console.log("");
2623
+ for (const repo of result.repos) {
2624
+ printRepo(repo);
2625
+ }
2626
+ }
2627
+ });
2628
+ var create2 = defineCommand15({
2629
+ meta: {
2630
+ name: "create",
2631
+ description: "Create a private DeepSpace-managed GitHub repo"
2632
+ },
2633
+ args: {
2634
+ appName: {
2635
+ type: "positional",
2636
+ description: "Working app name for this repo",
2637
+ required: true
2638
+ },
2639
+ appId: {
2640
+ type: "string",
2641
+ description: "Stable DeepSpace repo app id (defaults to cli-<app-name>)",
2642
+ required: false
2643
+ },
2644
+ nameHint: {
2645
+ type: "string",
2646
+ description: "Repo-name hint (defaults to app name)",
2647
+ required: false
2648
+ },
2649
+ json: {
2650
+ type: "boolean",
2651
+ description: "Emit JSON instead of human output",
2652
+ default: false
2653
+ }
2654
+ },
2655
+ async run({ args }) {
2656
+ const appName = String(args.appName);
2657
+ const appId = args.appId ? String(args.appId) : defaultAppId(appName);
2658
+ const nameHint = args.nameHint ? String(args.nameHint) : void 0;
2659
+ try {
2660
+ assertSimple(appName, SIMPLE_APP_NAME, "appName");
2661
+ assertSimple(appId, SIMPLE_APP_ID, "appId");
2662
+ if (nameHint) assertSimple(nameHint, SIMPLE_APP_NAME, "nameHint");
2663
+ } catch (err) {
2664
+ console.error(err.message);
2665
+ process.exit(1);
2666
+ }
2667
+ const token2 = await ensureToken();
2668
+ const result = await api2(token2, "/api/github/repos", {
2669
+ method: "POST",
2670
+ body: JSON.stringify({ appName, appId, ...nameHint ? { nameHint } : {} })
2671
+ });
2672
+ if (args.json) {
2673
+ process.stdout.write(JSON.stringify(result, null, 2) + "\n");
2674
+ return;
2675
+ }
2676
+ console.log(result.alreadyExists ? "Existing DeepSpace-managed repo:" : "Created DeepSpace-managed repo:");
2677
+ printRepo(result.repo);
2678
+ }
2679
+ });
2680
+ var token = defineCommand15({
2681
+ meta: {
2682
+ name: "token",
2683
+ description: "Mint a short-lived GitHub token for a DeepSpace-managed repo"
2684
+ },
2685
+ args: {
2686
+ repoId: {
2687
+ type: "positional",
2688
+ description: "Repo id from `deepspace managed-repos list`",
2689
+ required: true
2690
+ },
2691
+ json: {
2692
+ type: "boolean",
2693
+ description: "Emit JSON instead of human output",
2694
+ default: false
2695
+ }
2696
+ },
2697
+ async run({ args }) {
2698
+ const jwt = await ensureToken();
2699
+ const result = await api2(
2700
+ jwt,
2701
+ `/api/github/repos/${encodeURIComponent(String(args.repoId))}/agent-token`,
2702
+ { method: "POST" }
2703
+ );
2704
+ if (args.json) {
2705
+ process.stdout.write(JSON.stringify(result, null, 2) + "\n");
2706
+ return;
2707
+ }
2708
+ console.log(`Repo: ${result.repo.fullName}`);
2709
+ console.log(`Expires: ${result.token.expiresAt}`);
2710
+ console.log(`Token: ${result.token.token}`);
2711
+ console.log("");
2712
+ console.log(`Clone: git clone https://x-access-token:${encodeURIComponent(result.token.token)}@github.com/${result.repo.fullName}.git`);
2713
+ }
2714
+ });
2715
+ var remove = defineCommand15({
2716
+ meta: {
2717
+ name: "delete",
2718
+ description: "Delete a DeepSpace-managed GitHub repo"
2719
+ },
2720
+ args: {
2721
+ repoId: {
2722
+ type: "positional",
2723
+ description: "Repo id from `deepspace managed-repos list`",
2724
+ required: true
2725
+ },
2726
+ yes: {
2727
+ type: "boolean",
2728
+ description: "Skip the confirmation prompt",
2729
+ default: false
2730
+ },
2731
+ json: {
2732
+ type: "boolean",
2733
+ description: "Emit JSON instead of human output",
2734
+ default: false
2735
+ }
2736
+ },
2737
+ async run({ args }) {
2738
+ if (!args.yes) {
2739
+ try {
2740
+ const ok = await confirm3(
2741
+ "Delete this DeepSpace-managed GitHub repo? This removes the GitHub repository and counts against your daily delete quota."
2742
+ );
2743
+ if (!ok) {
2744
+ console.log("Cancelled.");
2745
+ return;
2746
+ }
2747
+ } catch (err) {
2748
+ console.error(err.message);
2749
+ process.exit(1);
2750
+ }
2751
+ }
2752
+ const jwt = await ensureToken();
2753
+ const result = await api2(
2754
+ jwt,
2755
+ `/api/github/repos/${encodeURIComponent(String(args.repoId))}`,
2756
+ { method: "DELETE" }
2757
+ );
2758
+ if (args.json) {
2759
+ process.stdout.write(JSON.stringify(result, null, 2) + "\n");
2760
+ return;
2761
+ }
2762
+ console.log(`Deleted ${result.repo.fullName}`);
2763
+ console.log(
2764
+ `Delete quota: ${result.deletionQuota.deletedCount} / ${result.deletionQuota.limit} used today (${result.deletionQuota.remaining} remaining)`
2765
+ );
2766
+ }
2767
+ });
2768
+ var managed_repos_default = defineCommand15({
2769
+ meta: {
2770
+ name: "managed-repos",
2771
+ description: "Create and manage DeepSpace-owned GitHub repos"
2772
+ },
2773
+ subCommands: {
2774
+ list: list4,
2775
+ create: create2,
2776
+ token,
2777
+ delete: remove
2778
+ }
2779
+ });
2780
+
2452
2781
  // src/cli/cli.ts
2453
2782
  var __dirname = dirname2(fileURLToPath(import.meta.url));
2454
2783
  var pkg = JSON.parse(readFileSync9(join12(__dirname, "..", "package.json"), "utf-8"));
2455
2784
  if (process.platform === "linux" && !process.env.SSL_CERT_FILE && existsSync11("/etc/ssl/certs/ca-certificates.crt")) {
2456
2785
  process.env.SSL_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt";
2457
2786
  }
2458
- var create2 = defineCommand14({
2787
+ var create3 = defineCommand16({
2459
2788
  meta: {
2460
2789
  name: "create",
2461
2790
  description: "Create a new DeepSpace app"
@@ -2474,23 +2803,25 @@ var create2 = defineCommand14({
2474
2803
  execSync5(`npm create deepspace@latest${name}`, { stdio: "inherit" });
2475
2804
  }
2476
2805
  });
2477
- var main = defineCommand14({
2806
+ var main = defineCommand16({
2478
2807
  meta: {
2479
2808
  name: "deepspace",
2480
2809
  version: pkg.version,
2481
2810
  description: "DeepSpace SDK CLI"
2482
2811
  },
2483
2812
  subCommands: {
2484
- create: create2,
2813
+ create: create3,
2485
2814
  login: login_default,
2486
2815
  logout: logout_default,
2487
2816
  whoami: whoami_default,
2488
2817
  dev: dev_default,
2489
2818
  kill: kill_default,
2490
2819
  test: test_default,
2820
+ screenshot: screenshot_default,
2491
2821
  "test-accounts": test_accounts_default,
2492
2822
  add: add_default,
2493
2823
  integrations: integrations_default,
2824
+ "managed-repos": managed_repos_default,
2494
2825
  invoke: invoke_default,
2495
2826
  deploy: deploy_default,
2496
2827
  undeploy: undeploy_default,