deepspace 0.18.0 → 0.19.1

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
@@ -8,8 +8,8 @@ import {
8
8
  renderUsage
9
9
  } from "citty";
10
10
  import { sync as spawnSync8 } from "cross-spawn";
11
- import { readFileSync as readFileSync28, existsSync as existsSync25 } from "fs";
12
- import { dirname as dirname11, join as join25 } from "path";
11
+ import { readFileSync as readFileSync28, existsSync as existsSync26 } from "fs";
12
+ import { dirname as dirname12, join as join24 } from "path";
13
13
  import { fileURLToPath as fileURLToPath3 } from "url";
14
14
 
15
15
  // src/cli/lib/spinner.ts
@@ -205,6 +205,13 @@ var InputError = class extends Error {
205
205
  this.code = code;
206
206
  }
207
207
  };
208
+ var CliExit = class extends Error {
209
+ constructor(exitCode) {
210
+ super(`exit ${exitCode}`);
211
+ this.exitCode = exitCode;
212
+ this.name = "CliExit";
213
+ }
214
+ };
208
215
  function errorCode(err) {
209
216
  if (err instanceof ApiError || err instanceof InputError) return err.code;
210
217
  if (err instanceof Error && err.name === "GitError") return err.code ?? "git_error";
@@ -230,12 +237,17 @@ function formatCliError(err) {
230
237
  ${hint}` : message;
231
238
  }
232
239
  function renderCliError(err) {
240
+ if (err instanceof CliExit) {
241
+ process.exitCode = err.exitCode;
242
+ return;
243
+ }
233
244
  stopActiveSpinner();
234
245
  const message = formatCliError(err);
235
246
  const slug = errorCode(err);
236
247
  if (process.argv.includes("--json")) {
237
248
  console.log(JSON.stringify({ ok: false, ...slug ? { code: slug } : {}, error: message }));
238
- process.exit(1);
249
+ process.exitCode = 1;
250
+ return;
239
251
  }
240
252
  console.error(slug ? withSlug(message, slug) : message);
241
253
  if (process.env.DEBUG) {
@@ -244,7 +256,7 @@ function renderCliError(err) {
244
256
  API ${apiPath}${status3 ? ` (${status3})` : ""}`);
245
257
  if (err instanceof Error && err.stack) console.error("\n" + err.stack);
246
258
  }
247
- process.exit(1);
259
+ process.exitCode = 1;
248
260
  }
249
261
  function wrapCommandErrors(cmd) {
250
262
  const run2 = cmd.run;
@@ -301,10 +313,9 @@ function withoutReservedKeys(payload) {
301
313
  )
302
314
  );
303
315
  }
304
- function exitWhenFlushed(code) {
305
- if (process.stdout.writableLength === 0) process.exit(code);
306
- process.stdout.write("", () => process.exit(code));
307
- return void 0;
316
+ function finishCommand(code) {
317
+ stopActiveSpinner();
318
+ process.exitCode = code;
308
319
  }
309
320
  function nameVariants(name) {
310
321
  return [
@@ -342,7 +353,7 @@ function defineDeepspaceCommand(def) {
342
353
  ...def.args ?? {},
343
354
  json: {
344
355
  type: "boolean",
345
- description: "Emit a single-line JSON result for scripts/agents",
356
+ description: def.jsonDescription ?? "Emit a single-line JSON result for scripts/agents",
346
357
  default: false
347
358
  }
348
359
  };
@@ -398,9 +409,9 @@ function defineDeepspaceCommand(def) {
398
409
  else p3.log.error(line);
399
410
  if (refusal?.action) printAction(refusal.action);
400
411
  }
401
- exitWhenFlushed(refusal?.actionRequired ? 2 : 1);
412
+ finishCommand(refusal?.actionRequired ? 2 : 1);
402
413
  }
403
- if (succeeded) exitWhenFlushed(0);
414
+ if (succeeded) finishCommand(0);
404
415
  }
405
416
  });
406
417
  }
@@ -515,7 +526,7 @@ function effectivePlatformUrls(env = process.env) {
515
526
  var DASHBOARD_URL = DEEPSPACE_ENV === "production" ? "https://dashboard.deep.space" : DEEPSPACE_ENV === "staging" ? "https://dashboard.deepspacesites.com" : "http://127.0.0.1:9";
516
527
 
517
528
  // src/cli/auth.ts
518
- import { readFileSync as readFileSync2, writeFileSync, existsSync, mkdirSync } from "fs";
529
+ import { readFileSync as readFileSync2, existsSync, mkdirSync } from "fs";
519
530
  import { join } from "path";
520
531
  import { homedir } from "os";
521
532
 
@@ -543,6 +554,16 @@ async function exchangeSession(authUrl, sessionToken) {
543
554
  return data.token ?? null;
544
555
  }
545
556
 
557
+ // src/cli/lib/secure-file.ts
558
+ import { chmodSync, writeFileSync } from "fs";
559
+ function writeSecretFileSync(path, content) {
560
+ writeFileSync(path, content, { mode: 384 });
561
+ try {
562
+ chmodSync(path, 384);
563
+ } catch {
564
+ }
565
+ }
566
+
546
567
  // src/cli/auth.ts
547
568
  var AUTH_URL = process.env.DEEPSPACE_AUTH_URL ?? PLATFORM_URLS.auth;
548
569
  var PROD_AUTH_URL = "https://auth.deep.space";
@@ -585,7 +606,7 @@ async function ensureToken() {
585
606
  );
586
607
  }
587
608
  mkdirSync(DIR, { recursive: true, mode: 448 });
588
- writeFileSync(TOKEN_PATH, token, { mode: 384 });
609
+ writeSecretFileSync(TOKEN_PATH, token);
589
610
  return token;
590
611
  }
591
612
  function isTokenValid(jwt) {
@@ -603,7 +624,7 @@ registerAuthRefresh(async () => {
603
624
  const token = await exchangeSession(AUTH_URL, sessionToken);
604
625
  if (!token) return null;
605
626
  mkdirSync(DIR, { recursive: true, mode: 448 });
606
- writeFileSync(TOKEN_PATH, token, { mode: 384 });
627
+ writeSecretFileSync(TOKEN_PATH, token);
607
628
  return token;
608
629
  } catch {
609
630
  return null;
@@ -611,19 +632,18 @@ registerAuthRefresh(async () => {
611
632
  });
612
633
 
613
634
  // src/cli/commands/login.ts
614
- import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync3 } from "fs";
615
- import { join as join2 } from "path";
616
- import { homedir as homedir2 } from "os";
635
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync3 } from "fs";
636
+ import { dirname as dirname2 } from "path";
617
637
  import { randomBytes, createHash } from "crypto";
618
638
  import * as p4 from "@clack/prompts";
619
639
 
620
640
  // src/cli/lib/open-browser.ts
621
- import { exec } from "child_process";
641
+ import { spawn } from "child_process";
622
642
  function openBrowser(url) {
623
643
  const quoted = JSON.stringify(url);
624
644
  const cmd = process.platform === "darwin" ? `open ${quoted}` : process.platform === "win32" ? `start "" ${quoted}` : `xdg-open ${quoted}`;
625
- exec(cmd, () => {
626
- });
645
+ spawn(cmd, { shell: true, detached: true, stdio: "ignore", windowsHide: true }).on("error", () => {
646
+ }).unref();
627
647
  }
628
648
 
629
649
  // src/cli/commands/login.ts
@@ -716,9 +736,13 @@ var login_default = defineDeepspaceCommand({
716
736
  if (!json) s.start("Waiting for authentication...");
717
737
  const result = await pollForCompletion(sessionId, codeVerifier);
718
738
  if (!result) {
719
- throw new Refusal("Login timed out. Run `deepspace auth login` to try again.", "login_timeout", {
720
- action: cliAction("deepspace", "auth", "login")
721
- });
739
+ throw new Refusal(
740
+ "Login timed out. Run `deepspace auth login` to try again.",
741
+ "login_timeout",
742
+ {
743
+ action: cliAction("deepspace", "auth", "login")
744
+ }
745
+ );
722
746
  }
723
747
  storeCredentials(result.sessionToken, result.jwt);
724
748
  await provisionProfile(result.jwt);
@@ -798,11 +822,10 @@ async function pollForCompletion(sessionId, codeVerifier) {
798
822
  return null;
799
823
  }
800
824
  function storeCredentials(sessionToken, jwt) {
801
- const dir = join2(homedir2(), ".deepspace");
802
- mkdirSync2(dir, { recursive: true, mode: 448 });
803
825
  const { sessionPath, tokenPath } = credentialPaths(AUTH_URL2);
804
- writeFileSync2(sessionPath, sessionToken, { mode: 384 });
805
- writeFileSync2(tokenPath, jwt, { mode: 384 });
826
+ mkdirSync2(dirname2(sessionPath), { recursive: true, mode: 448 });
827
+ writeSecretFileSync(sessionPath, sessionToken);
828
+ writeSecretFileSync(tokenPath, jwt);
806
829
  }
807
830
  async function provisionProfile(jwt) {
808
831
  try {
@@ -883,8 +906,8 @@ var logout_default = defineDeepspaceCommand({
883
906
  });
884
907
 
885
908
  // src/cli/lib/app-identity.ts
886
- import { existsSync as existsSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
887
- import { join as join3, resolve as resolve2 } from "path";
909
+ import { existsSync as existsSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
910
+ import { join as join2, resolve as resolve2 } from "path";
888
911
  import { parse as parseToml } from "smol-toml";
889
912
 
890
913
  // src/server/utils/registry-client.ts
@@ -919,7 +942,7 @@ function mintAppId(now = Date.now()) {
919
942
  return `app_${mintUlid(now)}`;
920
943
  }
921
944
  function readWranglerIdentityConfig(cwd) {
922
- const wranglerPath = join3(resolve2(cwd), "wrangler.toml");
945
+ const wranglerPath = join2(resolve2(cwd), "wrangler.toml");
923
946
  if (!existsSync3(wranglerPath)) return null;
924
947
  try {
925
948
  return parseToml(readFileSync5(wranglerPath, "utf-8"));
@@ -938,7 +961,7 @@ function readAppId(cwd = process.cwd(), wranglerEnv) {
938
961
  return typeof id === "string" && APP_ID_RE.test(id) ? id : null;
939
962
  }
940
963
  function writeAppId(cwd, appId, opts = {}) {
941
- const wranglerPath = join3(resolve2(cwd), "wrangler.toml");
964
+ const wranglerPath = join2(resolve2(cwd), "wrangler.toml");
942
965
  if (!existsSync3(wranglerPath)) {
943
966
  throw new Error(`No wrangler.toml in ${resolve2(cwd)}`);
944
967
  }
@@ -971,19 +994,19 @@ ${header}
971
994
  ${line}
972
995
  `;
973
996
  }
974
- writeFileSync3(wranglerPath, src);
997
+ writeFileSync2(wranglerPath, src);
975
998
  }
976
999
 
977
1000
  // src/cli/commands/dev.ts
978
- import { resolve as resolve8, basename as basename2, join as join12 } from "path";
979
- import spawn from "cross-spawn";
1001
+ import { resolve as resolve8, basename as basename2, join as join11 } from "path";
1002
+ import spawn2 from "cross-spawn";
980
1003
 
981
1004
  // src/cli/lib/app-context.ts
982
1005
  import { existsSync as existsSync4, readFileSync as readFileSync6, readdirSync } from "fs";
983
- import { dirname as dirname2, join as join4, resolve as resolve3 } from "path";
1006
+ import { dirname as dirname3, join as join3, resolve as resolve3 } from "path";
984
1007
  import { parse as parseToml2 } from "smol-toml";
985
1008
  function detectAppName(cwd = process.cwd()) {
986
- const wranglerPath = join4(resolve3(cwd), "wrangler.toml");
1009
+ const wranglerPath = join3(resolve3(cwd), "wrangler.toml");
987
1010
  if (!existsSync4(wranglerPath)) return null;
988
1011
  try {
989
1012
  const config = parseToml2(readFileSync6(wranglerPath, "utf-8"));
@@ -998,15 +1021,15 @@ function detectAppName(cwd = process.cwd()) {
998
1021
  function findAppDir(start = process.cwd()) {
999
1022
  let dir = resolve3(start);
1000
1023
  for (; ; ) {
1001
- if (existsSync4(join4(dir, "wrangler.toml"))) return dir;
1002
- const parent = dirname2(dir);
1024
+ if (existsSync4(join3(dir, "wrangler.toml"))) return dir;
1025
+ const parent = dirname3(dir);
1003
1026
  if (parent === dir) return null;
1004
1027
  dir = parent;
1005
1028
  }
1006
1029
  }
1007
1030
  function findChildApps(dir) {
1008
1031
  try {
1009
- return readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && existsSync4(join4(dir, entry.name, "wrangler.toml"))).map((entry) => entry.name).sort();
1032
+ return readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && existsSync4(join3(dir, entry.name, "wrangler.toml"))).map((entry) => entry.name).sort();
1010
1033
  } catch {
1011
1034
  return [];
1012
1035
  }
@@ -1017,16 +1040,16 @@ import {
1017
1040
  existsSync as existsSync5,
1018
1041
  readFileSync as readFileSync8,
1019
1042
  realpathSync as realpathSync2,
1020
- writeFileSync as writeFileSync4,
1043
+ writeFileSync as writeFileSync3,
1021
1044
  mkdirSync as mkdirSync4,
1022
1045
  renameSync,
1023
1046
  unlinkSync
1024
1047
  } from "fs";
1025
- import { join as join6, resolve as resolve5, dirname as dirname4, basename, isAbsolute as isAbsolute2, sep } from "path";
1048
+ import { join as join5, resolve as resolve5, dirname as dirname5, basename, isAbsolute as isAbsolute2, sep } from "path";
1026
1049
 
1027
1050
  // src/cli/lib/git/repository.ts
1028
1051
  import { appendFileSync, mkdirSync as mkdirSync3, readFileSync as readFileSync7 } from "fs";
1029
- import { dirname as dirname3, join as join5, resolve as resolve4 } from "path";
1052
+ import { dirname as dirname4, join as join4, resolve as resolve4 } from "path";
1030
1053
 
1031
1054
  // src/cli/lib/git/process.ts
1032
1055
  import { sync as spawnSync } from "cross-spawn";
@@ -1230,7 +1253,7 @@ function listWorktrees(cwd) {
1230
1253
  }
1231
1254
  function ensureLocalExclude(cwd, pattern) {
1232
1255
  const commonDir = resolve4(cwd, gitLine(cwd, ["rev-parse", "--git-common-dir"]));
1233
- const excludePath = join5(commonDir, "info", "exclude");
1256
+ const excludePath = join4(commonDir, "info", "exclude");
1234
1257
  let current = "";
1235
1258
  try {
1236
1259
  current = readFileSync7(excludePath, "utf-8");
@@ -1238,7 +1261,7 @@ function ensureLocalExclude(cwd, pattern) {
1238
1261
  }
1239
1262
  if (current.split("\n").some((line) => line.trim() === pattern)) return;
1240
1263
  try {
1241
- mkdirSync3(dirname3(excludePath), { recursive: true });
1264
+ mkdirSync3(dirname4(excludePath), { recursive: true });
1242
1265
  appendFileSync(
1243
1266
  excludePath,
1244
1267
  `${current.endsWith("\n") || current === "" ? "" : "\n"}${pattern}
@@ -1250,9 +1273,9 @@ function ensureLocalExclude(cwd, pattern) {
1250
1273
 
1251
1274
  // src/cli/lib/launch-config.ts
1252
1275
  function writeLaunchFile(launchPath, config) {
1253
- mkdirSync4(dirname4(launchPath), { recursive: true });
1276
+ mkdirSync4(dirname5(launchPath), { recursive: true });
1254
1277
  const tmpPath = `${launchPath}.${process.pid}.tmp`;
1255
- writeFileSync4(tmpPath, JSON.stringify(config, null, 2) + "\n");
1278
+ writeFileSync3(tmpPath, JSON.stringify(config, null, 2) + "\n");
1256
1279
  try {
1257
1280
  renameSync(tmpPath, launchPath);
1258
1281
  } catch (err) {
@@ -1273,7 +1296,7 @@ function readLaunchFile(launchPath) {
1273
1296
  return parsed;
1274
1297
  }
1275
1298
  function writeLaunchConfigIfMissing(appDir, appName, port, opts = {}) {
1276
- const launchPath = join6(appDir, ".claude", "launch.json");
1299
+ const launchPath = join5(appDir, ".claude", "launch.json");
1277
1300
  try {
1278
1301
  const config = readLaunchFile(launchPath);
1279
1302
  if (!config) return;
@@ -1314,7 +1337,7 @@ function writeLaunchConfigIfMissing(appDir, appName, port, opts = {}) {
1314
1337
  }
1315
1338
  function resolveAppLaunchPort(appDir) {
1316
1339
  try {
1317
- const config = readLaunchFile(join6(appDir, ".claude", "launch.json"));
1340
+ const config = readLaunchFile(join5(appDir, ".claude", "launch.json"));
1318
1341
  if (!config) return null;
1319
1342
  const appName = detectAppName(appDir) ?? basename(appDir);
1320
1343
  const entry = config.configurations.find((c) => c?.name === appName) ?? (config.configurations.length === 1 ? config.configurations[0] : void 0);
@@ -1331,10 +1354,10 @@ function detectClaudeWorktree(appDir) {
1331
1354
  listWorktrees(appDir).map((worktree) => canonicalPath(worktree.path))
1332
1355
  );
1333
1356
  if (!registered.has(worktreeRoot)) return null;
1334
- const worktreesDir = dirname4(worktreeRoot);
1335
- const claudeDir = dirname4(worktreesDir);
1357
+ const worktreesDir = dirname5(worktreeRoot);
1358
+ const claudeDir = dirname5(worktreesDir);
1336
1359
  if (basename(worktreesDir) !== "worktrees" || basename(claudeDir) !== ".claude") return null;
1337
- const ownerRoot = canonicalPath(dirname4(claudeDir));
1360
+ const ownerRoot = canonicalPath(dirname5(claudeDir));
1338
1361
  if (!registered.has(ownerRoot)) return null;
1339
1362
  return { mainRepoRoot: ownerRoot, worktreeName: basename(worktreeRoot) };
1340
1363
  } catch {
@@ -1368,12 +1391,12 @@ function deriveWorktreePort(worktreeKey) {
1368
1391
  return WORKTREE_PORT_MIN + hash % WORKTREE_PORT_RANGE;
1369
1392
  }
1370
1393
  function upsertWorktreeLaunchConfig(mainRepoRoot, worktreeName, appDir, opts) {
1371
- const launchPath = join6(mainRepoRoot, ".claude", "launch.json");
1394
+ const launchPath = join5(mainRepoRoot, ".claude", "launch.json");
1372
1395
  const entryName = WORKTREE_ENTRY_PREFIX + worktreeName;
1373
1396
  try {
1374
1397
  const config = readLaunchFile(launchPath);
1375
1398
  if (!config) return null;
1376
- const worktreesRoot = join6(mainRepoRoot, ".claude", "worktrees") + sep;
1399
+ const worktreesRoot = join5(mainRepoRoot, ".claude", "worktrees") + sep;
1377
1400
  config.configurations = config.configurations.filter((c) => {
1378
1401
  if (c?.name === entryName) return false;
1379
1402
  const isStaleWorktreeEntry = typeof c?.name === "string" && c.name.startsWith(WORKTREE_ENTRY_PREFIX) && typeof c.cwd === "string" && isAbsolute2(c.cwd) && c.cwd.startsWith(worktreesRoot) && !existsSync5(c.cwd);
@@ -1411,7 +1434,7 @@ function resolveWorktreePort(appDir) {
1411
1434
  const claude = detectClaudeWorktree(appDir);
1412
1435
  if (claude) {
1413
1436
  try {
1414
- const config = readLaunchFile(join6(claude.mainRepoRoot, ".claude", "launch.json"));
1437
+ const config = readLaunchFile(join5(claude.mainRepoRoot, ".claude", "launch.json"));
1415
1438
  const entry = config?.configurations.find(
1416
1439
  (c) => c?.name === WORKTREE_ENTRY_PREFIX + claude.worktreeName
1417
1440
  );
@@ -1472,19 +1495,9 @@ async function fetchAppIdentityToken(deployUrl, callerJwt, appId) {
1472
1495
  return body.token;
1473
1496
  }
1474
1497
 
1475
- // src/cli/lib/secure-file.ts
1476
- import { chmodSync, writeFileSync as writeFileSync5 } from "fs";
1477
- function writeSecretFileSync(path, content) {
1478
- writeFileSync5(path, content, { mode: 384 });
1479
- try {
1480
- chmodSync(path, 384);
1481
- } catch {
1482
- }
1483
- }
1484
-
1485
1498
  // src/cli/lib/wrangler-env.ts
1486
- import { existsSync as existsSync6, readFileSync as readFileSync9, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "fs";
1487
- import { join as join7 } from "path";
1499
+ import { existsSync as existsSync6, readFileSync as readFileSync9, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
1500
+ import { join as join6 } from "path";
1488
1501
  import { parse as parseToml3, stringify as stringifyToml } from "smol-toml";
1489
1502
 
1490
1503
  // src/server/rooms/app-name.ts
@@ -1585,7 +1598,7 @@ function resolveAppNameForEnv(config, envName) {
1585
1598
  return resolveAppName(envBlock.name);
1586
1599
  }
1587
1600
  function devVarsPathFor(appDir) {
1588
- return join7(appDir, ".dev.vars");
1601
+ return join6(appDir, ".dev.vars");
1589
1602
  }
1590
1603
  function prepareWranglerEnvConfig(appDir, envName) {
1591
1604
  if (!envName) return { cleanup() {
@@ -1594,11 +1607,11 @@ function prepareWranglerEnvConfig(appDir, envName) {
1594
1607
  const resolved = resolveAppNameForEnv(config, envName);
1595
1608
  if (!resolved.ok) throw new Error(resolved.reason);
1596
1609
  const flattened = flattenWranglerEnvConfig(config, envName);
1597
- const configPath = join7(
1610
+ const configPath = join6(
1598
1611
  appDir,
1599
1612
  `.wrangler.deepspace.${safeGeneratedConfigSegment(envName)}.${process.pid}.${++generatedConfigSeq}.toml`
1600
1613
  );
1601
- writeFileSync6(
1614
+ writeFileSync4(
1602
1615
  configPath,
1603
1616
  [
1604
1617
  "# Generated by DeepSpace for this CLI run.",
@@ -1660,7 +1673,7 @@ var WranglerConfigError = class extends Error {
1660
1673
  }
1661
1674
  };
1662
1675
  function readWranglerConfig(appDir) {
1663
- const path = join7(appDir, "wrangler.toml");
1676
+ const path = join6(appDir, "wrangler.toml");
1664
1677
  const raw = readFileSync9(path, "utf-8");
1665
1678
  try {
1666
1679
  return parseToml3(raw);
@@ -1669,7 +1682,7 @@ function readWranglerConfig(appDir) {
1669
1682
  }
1670
1683
  }
1671
1684
  function hasWranglerConfig(appDir) {
1672
- return existsSync6(join7(appDir, "wrangler.toml"));
1685
+ return existsSync6(join6(appDir, "wrangler.toml"));
1673
1686
  }
1674
1687
 
1675
1688
  // src/cli/lib/dev-vars.ts
@@ -1712,21 +1725,21 @@ async function writeDevVars(appDir, ownerId, callerJwt, wranglerEnv, opts = {})
1712
1725
 
1713
1726
  // src/cli/lib/install-status.ts
1714
1727
  import { existsSync as existsSync8, readFileSync as readFileSync11, realpathSync as realpathSync3 } from "fs";
1715
- import { dirname as dirname5, join as join9, resolve as resolve6 } from "path";
1728
+ import { dirname as dirname6, join as join8, resolve as resolve6 } from "path";
1716
1729
 
1717
1730
  // src/cli/lib/package-manager.ts
1718
1731
  import { existsSync as existsSync7, readFileSync as readFileSync10 } from "fs";
1719
- import { join as join8 } from "path";
1732
+ import { join as join7 } from "path";
1720
1733
  function detectPackageManager(appDir) {
1721
1734
  try {
1722
- const pkg2 = JSON.parse(readFileSync10(join8(appDir, "package.json"), "utf-8"));
1735
+ const pkg2 = JSON.parse(readFileSync10(join7(appDir, "package.json"), "utf-8"));
1723
1736
  const name = typeof pkg2.packageManager === "string" ? pkg2.packageManager.split("@")[0] : "";
1724
1737
  if (name === "npm" || name === "pnpm" || name === "yarn" || name === "bun") return name;
1725
1738
  } catch {
1726
1739
  }
1727
- if (existsSync7(join8(appDir, "pnpm-lock.yaml"))) return "pnpm";
1728
- if (existsSync7(join8(appDir, "yarn.lock"))) return "yarn";
1729
- if (existsSync7(join8(appDir, "bun.lock")) || existsSync7(join8(appDir, "bun.lockb"))) return "bun";
1740
+ if (existsSync7(join7(appDir, "pnpm-lock.yaml"))) return "pnpm";
1741
+ if (existsSync7(join7(appDir, "yarn.lock"))) return "yarn";
1742
+ if (existsSync7(join7(appDir, "bun.lock")) || existsSync7(join7(appDir, "bun.lockb"))) return "bun";
1730
1743
  return "npm";
1731
1744
  }
1732
1745
  function installCommand(appDir) {
@@ -1736,10 +1749,10 @@ function installCommand(appDir) {
1736
1749
  // src/cli/lib/install-status.ts
1737
1750
  function installState(appDir) {
1738
1751
  if (resolvesDeepspace(appDir)) return "ready";
1739
- const sentinelDir = join9(appDir, ".deepspace");
1740
- if (existsSync8(join9(sentinelDir, "install.err"))) return "failed";
1741
- if (existsSync8(join9(sentinelDir, "install.started"))) {
1742
- if (!existsSync8(join9(sentinelDir, "install.done")) && installWorkerAlive(sentinelDir)) {
1752
+ const sentinelDir = join8(appDir, ".deepspace");
1753
+ if (existsSync8(join8(sentinelDir, "install.err"))) return "failed";
1754
+ if (existsSync8(join8(sentinelDir, "install.started"))) {
1755
+ if (!existsSync8(join8(sentinelDir, "install.done")) && installWorkerAlive(sentinelDir)) {
1743
1756
  return "installing";
1744
1757
  }
1745
1758
  return "failed";
@@ -1751,11 +1764,11 @@ function tailHint(logPath, platform = process.platform) {
1751
1764
  }
1752
1765
  function ensureInstallReady(appDir) {
1753
1766
  if (resolvesDeepspace(appDir)) return;
1754
- const sentinelDir = join9(appDir, ".deepspace");
1755
- const errPath = join9(sentinelDir, "install.err");
1756
- const startedPath = join9(sentinelDir, "install.started");
1757
- const donePath = join9(sentinelDir, "install.done");
1758
- const logPath = join9(sentinelDir, "install.log");
1767
+ const sentinelDir = join8(appDir, ".deepspace");
1768
+ const errPath = join8(sentinelDir, "install.err");
1769
+ const startedPath = join8(sentinelDir, "install.started");
1770
+ const donePath = join8(sentinelDir, "install.done");
1771
+ const logPath = join8(sentinelDir, "install.log");
1759
1772
  if (existsSync8(errPath)) {
1760
1773
  const detail = readFileSync11(errPath, "utf-8").trimEnd();
1761
1774
  const log29 = existsSync8(logPath) ? ` Full log: ${logPath}.` : "";
@@ -1796,9 +1809,9 @@ function resolvesDeepspace(fromDir) {
1796
1809
  } catch {
1797
1810
  }
1798
1811
  for (; ; ) {
1799
- if (existsSync8(join9(dir, "node_modules", "deepspace", "package.json"))) return true;
1812
+ if (existsSync8(join8(dir, "node_modules", "deepspace", "package.json"))) return true;
1800
1813
  if (dir === checkoutRoot) return false;
1801
- const parent = dirname5(dir);
1814
+ const parent = dirname6(dir);
1802
1815
  if (parent === dir) return false;
1803
1816
  dir = parent;
1804
1817
  }
@@ -1811,7 +1824,7 @@ function canonicalPath2(path) {
1811
1824
  }
1812
1825
  }
1813
1826
  function installWorkerAlive(sentinelDir) {
1814
- const pidPath = join9(sentinelDir, "install.pid");
1827
+ const pidPath = join8(sentinelDir, "install.pid");
1815
1828
  if (!existsSync8(pidPath)) return false;
1816
1829
  const pid = Number(readFileSync11(pidPath, "utf-8").trim());
1817
1830
  if (!Number.isSafeInteger(pid) || pid <= 0) return false;
@@ -1864,7 +1877,7 @@ function preflightWindowsWorkerd(appDir) {
1864
1877
 
1865
1878
  // src/cli/lib/macos-junk.ts
1866
1879
  import { readdirSync as readdirSync2, rmSync as rmSync3 } from "fs";
1867
- import { join as join10 } from "path";
1880
+ import { join as join9 } from "path";
1868
1881
  var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", ".wrangler", ".vite"]);
1869
1882
  function removeMacosJunk(dir) {
1870
1883
  let removed = 0;
@@ -1875,7 +1888,7 @@ function removeMacosJunk(dir) {
1875
1888
  return removed;
1876
1889
  }
1877
1890
  for (const entry of entries) {
1878
- const path = join10(dir, entry.name);
1891
+ const path = join9(dir, entry.name);
1879
1892
  if (entry.isDirectory()) {
1880
1893
  if (!SKIP_DIRS.has(entry.name)) removed += removeMacosJunk(path);
1881
1894
  continue;
@@ -2373,7 +2386,7 @@ async function refreshSecretsCache(deployUrl, token, appId, wranglerEnv, selecte
2373
2386
 
2374
2387
  // src/cli/lib/schema-lint.ts
2375
2388
  import { existsSync as existsSync10 } from "fs";
2376
- import { join as join11 } from "path";
2389
+ import { join as join10 } from "path";
2377
2390
  import { pathToFileURL } from "url";
2378
2391
 
2379
2392
  // src/server/schemas/registry.ts
@@ -2412,7 +2425,7 @@ function lintSchema(schema) {
2412
2425
  if (schema.ownerField) {
2413
2426
  const col = schema.columns.find((c) => c.name === schema.ownerField);
2414
2427
  const spoofableRoles = Object.entries(schema.permissions).filter(
2415
- ([role, permissions]) => role !== "owner" && role !== "admin" && permissions.create === true
2428
+ ([role, permissions]) => role !== "owner" && role !== "admin" && permissions.create === true && permissions.update !== "unclaimed-or-own"
2416
2429
  ).map(([role]) => role);
2417
2430
  if (col && !col.userBound && spoofableRoles.length > 0) {
2418
2431
  warnings.push(
@@ -2432,12 +2445,12 @@ function lintSchema(schema) {
2432
2445
 
2433
2446
  // src/cli/lib/schema-lint.ts
2434
2447
  async function lintProjectSchemas(appDir) {
2435
- const schemasPath = join11(appDir, "src", "schemas.ts");
2448
+ const schemasPath = join10(appDir, "src", "schemas.ts");
2436
2449
  if (!existsSync10(schemasPath)) return null;
2437
2450
  let schemas;
2438
2451
  try {
2439
2452
  const esbuild = await import("esbuild");
2440
- const outFile = join11(appDir, ".wrangler", "deploy", "schemas.lint.bundle.mjs");
2453
+ const outFile = join10(appDir, ".wrangler", "deploy", "schemas.lint.bundle.mjs");
2441
2454
  await esbuild.build({
2442
2455
  entryPoints: [schemasPath],
2443
2456
  bundle: true,
@@ -2498,9 +2511,9 @@ function isPortListening(port, host, timeoutMs = 500) {
2498
2511
  socket.connect(port, target2);
2499
2512
  });
2500
2513
  }
2501
- async function waitForPortListening(port, host, timeoutMs) {
2514
+ async function waitForPortListening(port, host, timeoutMs, signal) {
2502
2515
  const deadline = Date.now() + timeoutMs;
2503
- while (Date.now() < deadline) {
2516
+ while (Date.now() < deadline && !signal?.aborted) {
2504
2517
  if (await isPortListening(port, host)) return true;
2505
2518
  await new Promise((resolve21) => setTimeout(resolve21, 250));
2506
2519
  }
@@ -2598,7 +2611,7 @@ var dev_default = defineDeepspaceCommand({
2598
2611
  );
2599
2612
  } else if (!args.json) {
2600
2613
  console.warn(
2601
- `Warning: could not update ${join12(worktree.mainRepoRoot, ".claude", "launch.json")} (malformed or unwritable) \u2014 the Claude preview tool may serve the main repo's code instead of this worktree.`
2614
+ `Warning: could not update ${join11(worktree.mainRepoRoot, ".claude", "launch.json")} (malformed or unwritable) \u2014 the Claude preview tool may serve the main repo's code instead of this worktree.`
2602
2615
  );
2603
2616
  }
2604
2617
  }
@@ -2664,13 +2677,15 @@ Free it with \`${killCmd}\`, or start on another port: \`deepspace dev start --p
2664
2677
  }
2665
2678
  const url = `http://${host === DEFAULT_DEV_HOST ? "localhost" : host}:${port}`;
2666
2679
  say("Starting dev server...\n");
2667
- const vite = spawn("npx", ["vite", "--port", String(port), "--strictPort", "--host", host], {
2680
+ const vite = spawn2("npx", ["vite", "--port", String(port), "--strictPort", "--host", host], {
2668
2681
  cwd: appDir,
2669
2682
  stdio: "inherit",
2670
2683
  env: wranglerViteEnv(process.env, wranglerConfig, { DEEPSPACE_PORT: String(port) })
2671
2684
  });
2685
+ const readiness = new AbortController();
2672
2686
  if (args.json) {
2673
- void waitForPortListening(port, host, READINESS_TIMEOUT_MS).then((ready) => {
2687
+ void waitForPortListening(port, host, READINESS_TIMEOUT_MS, readiness.signal).then((ready) => {
2688
+ if (readiness.signal.aborted) return;
2674
2689
  console.log(
2675
2690
  JSON.stringify(
2676
2691
  ready ? { ok: true, ready: true, url, port, host, appDir } : {
@@ -2702,6 +2717,7 @@ Free it with \`${killCmd}\`, or start on another port: \`deepspace dev start --p
2702
2717
  });
2703
2718
  process.off("SIGINT", stop);
2704
2719
  process.off("SIGTERM", stop);
2720
+ readiness.abort();
2705
2721
  if (!devExitSucceeded(code, stopping)) {
2706
2722
  throw new Refusal(`Dev server exited with code ${code}.`, "dev_server_failed", {
2707
2723
  extra: { port, appDir, exitCode: code }
@@ -2817,12 +2833,12 @@ var kill_default = defineDeepspaceCommand({
2817
2833
  }
2818
2834
  });
2819
2835
  function noTargetsMessage(opts) {
2820
- const { enumerated, swept, all, port } = opts;
2836
+ const { enumerated, swept, all, port, platform = process.platform } = opts;
2821
2837
  const couldObserve = enumerated || all && swept;
2822
2838
  if (!couldObserve) {
2823
2839
  return {
2824
2840
  ok: false,
2825
- message: `Couldn't determine what's running on :${port}: no lsof/pgrep and /proc is unavailable, so nothing was inspected or killed. Install lsof (or procps for pgrep), or find and kill the process manually.`
2841
+ message: platform === "win32" ? `Couldn't determine what's running on :${port}: the PowerShell port query could not be run, so nothing was inspected or killed. Either powershell.exe (or pwsh) is not on PATH, or Get-NetTCPConnection is unavailable on this Windows edition. Find the process manually with \`netstat -ano | findstr :${port}\`.` : `Couldn't determine what's running on :${port}: no lsof/pgrep and /proc is unavailable, so nothing was inspected or killed. Install lsof (or procps for pgrep), or find and kill the process manually.`
2826
2842
  };
2827
2843
  }
2828
2844
  return {
@@ -2917,7 +2933,7 @@ function sweepByName(names) {
2917
2933
  const all = /* @__PURE__ */ new Set();
2918
2934
  if (IS_WIN) {
2919
2935
  const orClauses = names.map((n) => `($_.Name -like '*${n}*') -or ($_.CommandLine -like '*${n}*')`).join(" -or ");
2920
- const script = `Get-CimInstance Win32_Process | Where-Object { ${orClauses} } | Select-Object -ExpandProperty ProcessId`;
2936
+ const script = `Get-CimInstance Win32_Process | Where-Object { $_.ProcessId -ne $PID -and (${orClauses}) } | Select-Object -ExpandProperty ProcessId`;
2921
2937
  const r = runPowershellChecked(script);
2922
2938
  for (const pid of r.pids) if (pid !== process.pid) all.add(pid);
2923
2939
  return { pids: [...all], swept: r.ran };
@@ -2954,14 +2970,22 @@ function run(cmd) {
2954
2970
  function runPowershell(script) {
2955
2971
  return runPowershellChecked(script).pids;
2956
2972
  }
2973
+ var PS_SENTINEL = "__deepspace_ps_ok__";
2974
+ function withSentinel(script) {
2975
+ return `$ErrorActionPreference='Stop'; try { ${script}; '${PS_SENTINEL}' } catch { }`;
2976
+ }
2977
+ function readPowershellPids(stdout) {
2978
+ if (!stdout.includes(PS_SENTINEL)) return { pids: [], ran: false };
2979
+ return { pids: parsePidLines(stdout.replace(PS_SENTINEL, "")), ran: true };
2980
+ }
2957
2981
  function runPowershellChecked(script) {
2958
2982
  const exe = pwshOrPowershell();
2959
2983
  try {
2960
- const r = spawnSync2(exe, ["-NoProfile", "-NonInteractive", "-Command", script], {
2984
+ const r = spawnSync2(exe, ["-NoProfile", "-NonInteractive", "-Command", withSentinel(script)], {
2961
2985
  encoding: "utf-8"
2962
2986
  });
2963
2987
  if (r.error) return { pids: [], ran: false };
2964
- return { pids: r.status === 0 ? parsePidLines(r.stdout) : [], ran: r.status === 0 };
2988
+ return readPowershellPids(r.stdout ?? "");
2965
2989
  } catch {
2966
2990
  return { pids: [], ran: false };
2967
2991
  }
@@ -3026,10 +3050,10 @@ function ensurePlaywright(appDir) {
3026
3050
  import { readFileSync as readFileSync14 } from "fs";
3027
3051
 
3028
3052
  // src/testing/accounts.ts
3029
- import { existsSync as existsSync11, mkdirSync as mkdirSync5, readFileSync as readFileSync13, writeFileSync as writeFileSync7 } from "fs";
3030
- import { homedir as homedir3 } from "os";
3031
- import { join as join13 } from "path";
3032
- var TEST_ACCOUNTS_PATH = join13(homedir3(), ".deepspace", "test-accounts.json");
3053
+ import { existsSync as existsSync11, mkdirSync as mkdirSync5, readFileSync as readFileSync13, writeFileSync as writeFileSync5 } from "fs";
3054
+ import { homedir as homedir2 } from "os";
3055
+ import { join as join12 } from "path";
3056
+ var TEST_ACCOUNTS_PATH = join12(homedir2(), ".deepspace", "test-accounts.json");
3033
3057
  var PRODUCTION_AUTH_ORIGIN = "https://auth.deep.space";
3034
3058
  var STAGING_AUTH_ORIGIN = "https://auth.deepspacesites.com";
3035
3059
  function currentTestAccountScope(env = process.env) {
@@ -3045,7 +3069,7 @@ function normalizeTestAccountScope(value) {
3045
3069
  }
3046
3070
  function loadAllTestAccounts(scope = currentTestAccountScope()) {
3047
3071
  const store = readTestAccountStore();
3048
- return mergeTestAccounts(store.scopes[scope] ?? [], store.unscoped);
3072
+ return store.scopes[scope] ?? [];
3049
3073
  }
3050
3074
  function reconcileTestAccountStore(remote, scope = currentTestAccountScope()) {
3051
3075
  const result = reconcileTestAccountScopes(readTestAccountStore(), remote, scope);
@@ -3054,24 +3078,20 @@ function reconcileTestAccountStore(remote, scope = currentTestAccountScope()) {
3054
3078
  }
3055
3079
  function reconcileTestAccountScopes(store, remote, scope) {
3056
3080
  const scoped = store.scopes[scope] ?? [];
3057
- const current = reconcileTestAccounts(scoped, remote);
3058
- const migrated = reconcileTestAccounts(store.unscoped, remote);
3059
- const accounts = mergeTestAccounts(current, migrated);
3081
+ const accounts = reconcileTestAccounts(scoped, remote);
3060
3082
  return {
3061
3083
  accounts,
3062
- removed: Math.max(0, scoped.length - current.length),
3084
+ removed: Math.max(0, scoped.length - accounts.length),
3063
3085
  store: {
3064
3086
  version: 2,
3065
- scopes: { ...store.scopes, [scope]: accounts },
3066
- unscoped: store.unscoped.filter((account) => !remote.some((item) => accountMatchesRemote(account, item)))
3087
+ scopes: { ...store.scopes, [scope]: accounts }
3067
3088
  }
3068
3089
  };
3069
3090
  }
3070
3091
  function reconcileTestAccounts(local, remote) {
3071
3092
  const localById = new Map(local.filter((account) => account.id).map((account) => [account.id, account]));
3072
- const legacyByEmail = new Map(local.filter((account) => !account.id).map((account) => [account.email, account]));
3073
3093
  return remote.flatMap((account) => {
3074
- const stored = localById.get(account.id) ?? legacyByEmail.get(account.email);
3094
+ const stored = localById.get(account.id);
3075
3095
  return stored ? [{ ...stored, ...account }] : [];
3076
3096
  });
3077
3097
  }
@@ -3082,10 +3102,7 @@ function upsertTestAccount(account, scope = currentTestAccountScope()) {
3082
3102
  );
3083
3103
  saveTestAccountStore({
3084
3104
  version: 2,
3085
- scopes: { ...store.scopes, [scope]: [...accounts, account] },
3086
- unscoped: store.unscoped.filter(
3087
- (stored) => stored.id !== account.id && stored.email !== account.email
3088
- )
3105
+ scopes: { ...store.scopes, [scope]: [...accounts, account] }
3089
3106
  });
3090
3107
  }
3091
3108
  function removeTestAccounts(ids, emails = [], scope = currentTestAccountScope()) {
@@ -3095,45 +3112,35 @@ function removeTestAccounts(ids, emails = [], scope = currentTestAccountScope())
3095
3112
  const store = readTestAccountStore();
3096
3113
  saveTestAccountStore({
3097
3114
  version: 2,
3098
- scopes: { ...store.scopes, [scope]: (store.scopes[scope] ?? []).filter(keep) },
3099
- unscoped: store.unscoped.filter(keep)
3115
+ scopes: { ...store.scopes, [scope]: (store.scopes[scope] ?? []).filter(keep) }
3100
3116
  });
3101
3117
  }
3102
3118
  function readTestAccountStore() {
3103
- if (!existsSync11(TEST_ACCOUNTS_PATH)) return { version: 2, scopes: {}, unscoped: [] };
3119
+ if (!existsSync11(TEST_ACCOUNTS_PATH)) return { version: 2, scopes: {} };
3104
3120
  try {
3105
3121
  const raw = JSON.parse(readFileSync13(TEST_ACCOUNTS_PATH, "utf-8"));
3106
- if (Array.isArray(raw)) return { version: 2, scopes: {}, unscoped: validTestAccounts(raw) };
3107
- if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
3108
- return { version: 2, scopes: {}, unscoped: [] };
3122
+ if (Array.isArray(raw)) return { version: 2, scopes: {} };
3123
+ if (!raw || typeof raw !== "object") {
3124
+ return { version: 2, scopes: {} };
3109
3125
  }
3110
3126
  const candidate = raw;
3111
3127
  const scopes = candidate.scopes && typeof candidate.scopes === "object" && !Array.isArray(candidate.scopes) ? Object.fromEntries(Object.entries(candidate.scopes).map(([key, value]) => [key, validTestAccounts(value)])) : {};
3112
- return { version: 2, scopes, unscoped: validTestAccounts(candidate.unscoped) };
3128
+ return { version: 2, scopes };
3113
3129
  } catch {
3114
- return { version: 2, scopes: {}, unscoped: [] };
3130
+ return { version: 2, scopes: {} };
3115
3131
  }
3116
3132
  }
3117
3133
  function validTestAccounts(value) {
3118
3134
  if (!Array.isArray(value)) return [];
3119
- return value.filter((item) => Boolean(
3120
- item && typeof item === "object" && typeof item.email === "string" && typeof item.password === "string"
3121
- ));
3122
- }
3123
- function mergeTestAccounts(...groups) {
3124
- const accounts = /* @__PURE__ */ new Map();
3125
- for (const account of groups.flat()) {
3126
- const key = account.id ? `id:${account.id}` : `email:${account.email}`;
3127
- if (!accounts.has(key)) accounts.set(key, account);
3128
- }
3129
- return [...accounts.values()];
3130
- }
3131
- function accountMatchesRemote(local, remote) {
3132
- return local.id ? local.id === remote.id : local.email === remote.email;
3135
+ return value.filter(
3136
+ (item) => Boolean(
3137
+ item && typeof item === "object" && typeof item.id === "string" && typeof item.email === "string" && typeof item.password === "string"
3138
+ )
3139
+ );
3133
3140
  }
3134
3141
  function saveTestAccountStore(store) {
3135
- mkdirSync5(join13(homedir3(), ".deepspace"), { recursive: true });
3136
- writeFileSync7(TEST_ACCOUNTS_PATH, JSON.stringify(store, null, 2), { mode: 384 });
3142
+ mkdirSync5(join12(homedir2(), ".deepspace"), { recursive: true });
3143
+ writeFileSync5(TEST_ACCOUNTS_PATH, JSON.stringify(store, null, 2), { mode: 384 });
3137
3144
  }
3138
3145
 
3139
3146
  // src/cli/lib/test-account-service.ts
@@ -3187,11 +3194,24 @@ async function syncTestAccountStore() {
3187
3194
 
3188
3195
  // src/cli/commands/test.ts
3189
3196
  var DEPLOY_URL2 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
3197
+ var PLAYWRIGHT_OUTPUT_DIR = ".deepspace/test-results";
3198
+ function playwrightTestArgs(testFiles) {
3199
+ return [
3200
+ "playwright",
3201
+ "test",
3202
+ "--config",
3203
+ "tests/playwright.config.ts",
3204
+ "--output",
3205
+ PLAYWRIGHT_OUTPUT_DIR,
3206
+ ...testFiles
3207
+ ];
3208
+ }
3190
3209
  var test_default = defineDeepspaceCommand({
3191
3210
  meta: {
3192
3211
  name: "test",
3193
3212
  description: "Run tests for your DeepSpace app"
3194
3213
  },
3214
+ jsonDescription: "Stream test output; emit the final JSON result on the last line",
3195
3215
  args: {
3196
3216
  suite: {
3197
3217
  type: "positional",
@@ -3324,15 +3344,14 @@ function runPlaywright(appDir, testFiles, port, wranglerEnv) {
3324
3344
  return 1;
3325
3345
  }
3326
3346
  try {
3327
- const result = spawnSync4(
3328
- "npx",
3329
- ["playwright", "test", "--config", "tests/playwright.config.ts", ...testFiles],
3330
- {
3331
- cwd: appDir,
3332
- stdio: "inherit",
3333
- env: wranglerViteEnv(process.env, wranglerConfig, { DEEPSPACE_PORT: String(port) })
3334
- }
3335
- );
3347
+ const result = spawnSync4("npx", playwrightTestArgs(testFiles), {
3348
+ cwd: appDir,
3349
+ stdio: "inherit",
3350
+ env: wranglerViteEnv(process.env, wranglerConfig, {
3351
+ DEEPSPACE_PORT: String(port),
3352
+ PLAYWRIGHT_HTML_OUTPUT_DIR: ".deepspace/playwright-report"
3353
+ })
3354
+ });
3336
3355
  return result.status ?? 1;
3337
3356
  } finally {
3338
3357
  wranglerConfig.cleanup();
@@ -3621,7 +3640,7 @@ var clear = defineDeepspaceCommand({
3621
3640
  return { data: { deleted: 0, requested: 0, failures: [] } };
3622
3641
  }
3623
3642
  if (!args.yes) {
3624
- if (args.json) {
3643
+ if (!process.stdin.isTTY || args.json) {
3625
3644
  throw new Refusal(
3626
3645
  "Deleting test accounts needs confirmation. Pass --yes to confirm non-interactively.",
3627
3646
  "confirmation_required"
@@ -3851,14 +3870,14 @@ import * as p6 from "@clack/prompts";
3851
3870
  import { execSync as execSync2 } from "child_process";
3852
3871
  import { createHash as createHash2 } from "crypto";
3853
3872
  import { existsSync as existsSync14, readFileSync as readFileSync17, readdirSync as readdirSync4 } from "fs";
3854
- import { dirname as dirname6, join as join16, resolve as resolve11 } from "path";
3873
+ import { dirname as dirname7, join as join15, resolve as resolve11 } from "path";
3855
3874
 
3856
3875
  // src/documentation/deploy.ts
3857
3876
  import { existsSync as existsSync12, readFileSync as readFileSync15 } from "fs";
3858
- import { join as join14 } from "path";
3877
+ import { join as join13 } from "path";
3859
3878
  function readDocumentationDeployManifest(appDir, clientDir) {
3860
- if (!existsSync12(join14(appDir, "documentation.json"))) return void 0;
3861
- const manifestPath = join14(clientDir, "_documentation", "manifest.json");
3879
+ if (!existsSync12(join13(appDir, "documentation.json"))) return void 0;
3880
+ const manifestPath = join13(clientDir, "_documentation", "manifest.json");
3862
3881
  if (!existsSync12(manifestPath)) {
3863
3882
  throw new Error(
3864
3883
  "documentation.json exists but the build produced no documentation manifest. Ensure vite.config.ts includes deepSpaceDocumentation() from deepspace/documentation."
@@ -3879,12 +3898,13 @@ import {
3879
3898
  readFileSync as readFileSync16,
3880
3899
  writeSync
3881
3900
  } from "fs";
3882
- import { join as join15 } from "path";
3901
+ import { join as join14 } from "path";
3883
3902
 
3884
3903
  // src/shared/protocol/app-migrations.ts
3885
3904
  var APP_MIGRATION_ID_RE = /^[a-z0-9][a-z0-9._-]{0,127}$/;
3886
3905
  var MAX_APP_MIGRATION_IDS = 256;
3887
3906
  var WORKER_OWNED_NOT_FOUND_MIGRATION_ID = "2026-08-worker-owned-not-found";
3907
+ var SECURE_ROOM_BOUNDARIES_MIGRATION_ID = "2026-08-secure-room-boundaries";
3888
3908
  function validateAppMigrationIds(value) {
3889
3909
  if (!Array.isArray(value)) {
3890
3910
  return { valid: false, reason: "app migrations must be a JSON string array" };
@@ -3918,13 +3938,19 @@ var SOURCE_MIGRATIONS = [
3918
3938
  description: "Let the worker answer misses, so deleted files 404 instead of serving HTML",
3919
3939
  transform: migrateWorkerOwnedNotFound,
3920
3940
  findBlockers: findWorkerOwnedNotFoundBlockers
3941
+ },
3942
+ {
3943
+ id: SECURE_ROOM_BOUNDARIES_MIGRATION_ID,
3944
+ description: "Secure room identity, job mutations, and production debug routes",
3945
+ transform: migrateSecureRoomBoundaries,
3946
+ findBlockers: findSecureRoomBoundaryBlockers
3921
3947
  }
3922
3948
  ];
3923
3949
  var APP_MIGRATION_DEFINITIONS = SOURCE_MIGRATIONS.map(
3924
3950
  ({ id, description }) => ({ id, description })
3925
3951
  );
3926
3952
  function readAppliedAppMigrations(appDir) {
3927
- const path = join15(appDir, APP_MIGRATIONS_MANIFEST);
3953
+ const path = join14(appDir, APP_MIGRATIONS_MANIFEST);
3928
3954
  if (!existsSync13(path)) return [];
3929
3955
  const source = readRegularFile(path);
3930
3956
  let parsed;
@@ -3951,7 +3977,7 @@ function planAppMigrations(appDir) {
3951
3977
  const edits = [];
3952
3978
  for (const repoPath of tracked) {
3953
3979
  if (!isSourcePath(repoPath, appPrefix)) continue;
3954
- const absolutePath = join15(repo, repoPath);
3980
+ const absolutePath = join14(repo, repoPath);
3955
3981
  let stat;
3956
3982
  try {
3957
3983
  stat = lstatSync(absolutePath);
@@ -3959,7 +3985,8 @@ function planAppMigrations(appDir) {
3959
3985
  continue;
3960
3986
  }
3961
3987
  if (!stat.isFile() || stat.isSymbolicLink()) continue;
3962
- const before = readRegularFile(absolutePath);
3988
+ const checkout = readRegularFile(absolutePath);
3989
+ const before = normalizeEol(checkout);
3963
3990
  let after = before;
3964
3991
  for (const migration of pendingMigrations) {
3965
3992
  const result = migration.transform(after, repoPath);
@@ -3977,7 +4004,9 @@ function planAppMigrations(appDir) {
3977
4004
  }
3978
4005
  blockers.push(...migration.findBlockers(after, repoPath));
3979
4006
  }
3980
- if (after !== before) edits.push({ path: repoPath, before, after });
4007
+ if (after !== before) {
4008
+ edits.push({ path: repoPath, before: checkout, after: restoreEol(after, checkout) });
4009
+ }
3981
4010
  }
3982
4011
  const pending = pendingMigrations.map((migration) => {
3983
4012
  const planned = perMigration.get(migration.id);
@@ -3985,7 +4014,7 @@ function planAppMigrations(appDir) {
3985
4014
  });
3986
4015
  if (pending.length > 0 && blockers.length === 0) {
3987
4016
  const manifestPath = `${appPrefix}${APP_MIGRATIONS_MANIFEST}`;
3988
- const absoluteManifestPath = join15(repo, manifestPath);
4017
+ const absoluteManifestPath = join14(repo, manifestPath);
3989
4018
  const before = existsSync13(absoluteManifestPath) ? readRegularFile(absoluteManifestPath) : null;
3990
4019
  const after = `${JSON.stringify([...appliedIds, ...pending.map(({ id }) => id)], null, 2)}
3991
4020
  `;
@@ -4001,13 +4030,19 @@ function planAppMigrations(appDir) {
4001
4030
  function applyAppMigrationPlan(appDir, plan) {
4002
4031
  const repo = repoToplevel(appDir);
4003
4032
  for (const edit of plan.edits) {
4004
- const absolutePath = join15(repo, edit.path);
4033
+ const absolutePath = join14(repo, edit.path);
4005
4034
  const current = existsSync13(absolutePath) ? readRegularFile(absolutePath) : null;
4006
4035
  if (current !== edit.before) {
4007
4036
  throw new Error(`Migration input changed after planning: ${edit.path}`);
4008
4037
  }
4009
4038
  }
4010
- for (const edit of plan.edits) writeRegularFile(join15(repo, edit.path), edit.before, edit.after);
4039
+ for (const edit of plan.edits) writeRegularFile(join14(repo, edit.path), edit.before, edit.after);
4040
+ }
4041
+ function normalizeEol(source) {
4042
+ return source.replaceAll("\r\n", "\n");
4043
+ }
4044
+ function restoreEol(source, checkout) {
4045
+ return checkout.includes("\r\n") ? source.replaceAll("\n", "\r\n") : source;
4011
4046
  }
4012
4047
  function readRegularFile(path) {
4013
4048
  const before = lstatSync(path);
@@ -4061,6 +4096,252 @@ function isSourcePath(repoPath, appPrefix) {
4061
4096
  if (repoPath.endsWith("wrangler.toml")) return true;
4062
4097
  return /\.(?:[cm]?[jt]sx?)$/.test(repoPath);
4063
4098
  }
4099
+ var LEGACY_AUTHENTICATED_ROOM_HELPER = `const IDENTITY_QUERY_PARAMS = ['userId', 'userName', 'userEmail', 'userImageUrl', 'role'] as const
4100
+
4101
+ function authenticatedRoomUrl(
4102
+ requestUrl: string,
4103
+ auth: VerifyResult | null,
4104
+ extraParams?: (auth: VerifyResult) => Record<string, string>,
4105
+ ): URL {
4106
+ const doUrl = new URL(requestUrl)
4107
+ doUrl.searchParams.delete('token')
4108
+ for (const key of IDENTITY_QUERY_PARAMS) {
4109
+ doUrl.searchParams.delete(key)
4110
+ }
4111
+
4112
+ if (!auth) return doUrl
4113
+
4114
+ doUrl.searchParams.set('userId', auth.userId)
4115
+ if (auth.claims.name) doUrl.searchParams.set('userName', auth.claims.name)
4116
+ if (auth.claims.email) doUrl.searchParams.set('userEmail', auth.claims.email)
4117
+ if (auth.claims.image) doUrl.searchParams.set('userImageUrl', auth.claims.image)
4118
+ if (extraParams) {
4119
+ for (const [key, value] of Object.entries(extraParams(auth))) {
4120
+ doUrl.searchParams.set(key, value)
4121
+ }
4122
+ }
4123
+ return doUrl
4124
+ }`;
4125
+ var IDENTITY_ROUTE_REPLACEMENTS = [
4126
+ [
4127
+ `import { verifyJwt } from 'deepspace/worker'`,
4128
+ `import { authenticatedRoomRequest, verifyJwt } from 'deepspace/worker'`,
4129
+ 1
4130
+ ],
4131
+ [LEGACY_AUTHENTICATED_ROOM_HELPER, "", 1],
4132
+ [
4133
+ `function wsRoute(
4134
+ doNamespace: (env: Env) => DurableObjectNamespace,
4135
+ extraParams?: (auth: VerifyResult) => Record<string, string>,
4136
+ ) {`,
4137
+ `function wsRoute(
4138
+ doNamespace: (env: Env) => DurableObjectNamespace,
4139
+ extraIdentity?: (auth: VerifyResult) => { role?: string },
4140
+ ) {`,
4141
+ 1
4142
+ ],
4143
+ [
4144
+ ` const doUrl = authenticatedRoomUrl(c.req.url, auth, extraParams)`,
4145
+ ` const roomRequest = authenticatedRoomRequest(
4146
+ c.req.raw,
4147
+ auth,
4148
+ auth ? extraIdentity?.(auth) : undefined,
4149
+ )`,
4150
+ 1
4151
+ ],
4152
+ [
4153
+ ` const doUrl = authenticatedRoomUrl(c.req.url, auth, () => ({ role }))`,
4154
+ ` const roomRequest = authenticatedRoomRequest(c.req.raw, auth, { role })`,
4155
+ 1
4156
+ ],
4157
+ [
4158
+ ` return stub.fetch(new Request(doUrl.toString(), c.req.raw))`,
4159
+ ` return stub.fetch(roomRequest)`,
4160
+ 2
4161
+ ]
4162
+ ];
4163
+ function occurrences(source, pattern) {
4164
+ return source.split(pattern).length - 1;
4165
+ }
4166
+ var SECURE_JOB_ROOM_CONSTRUCTOR = ` constructor(state: DurableObjectState, env: Env) {
4167
+ super(state, env, {
4168
+ authorizeWrite: async (user) => {
4169
+ if (user.userId.startsWith('anon-')) return false
4170
+ const role = await resolveAppRole(env, user.userId)
4171
+ return role === 'member' || role === 'admin'
4172
+ },
4173
+ })
4174
+ }`;
4175
+ var APP_JOB_ROOM_CLASS = `export class AppJobRoom extends JobRoom<Env> {`;
4176
+ var SECURE_APP_JOB_ROOM = `${APP_JOB_ROOM_CLASS}
4177
+ ${SECURE_JOB_ROOM_CONSTRUCTOR}`;
4178
+ function hasSecureAppJobRoom(source) {
4179
+ return occurrences(source, "class AppJobRoom") === 1 && source.includes(SECURE_APP_JOB_ROOM);
4180
+ }
4181
+ var WORKER_JOB_REPLACEMENTS = [
4182
+ [
4183
+ `import type { DOBindings, DOManifest, Job, JobContext } from 'deepspace/worker'`,
4184
+ `import { resolveAppRole } from 'deepspace/worker'
4185
+ import type { DOBindings, DOManifest, Job, JobContext } from 'deepspace/worker'`
4186
+ ],
4187
+ [
4188
+ `export class AppJobRoom extends JobRoom<Env> {
4189
+ constructor(state: DurableObjectState, env: Env) {
4190
+ super(state, env)
4191
+ }`,
4192
+ `export class AppJobRoom extends JobRoom<Env> {
4193
+ ${SECURE_JOB_ROOM_CONSTRUCTOR}`
4194
+ ]
4195
+ ];
4196
+ var HTTP_ROLE_IMPORT = `import { resolveAppRole } from 'deepspace/worker'`;
4197
+ var HTTP_ROLE_IMPORT_PATTERN = /\bimport\s*\{[^}]*\bresolveAppRole\b[^}]*\}\s*from\s*(['"])deepspace\/worker\1/;
4198
+ var HTTP_ROLE_IMPORT_SEAM = `import type { AppContext, Env } from '../../worker.js'`;
4199
+ var DEBUG_ROUTE_SEAM = ` app.all('/api/debug/*', async (c) => {
4200
+ if (c.env.ALLOW_DEBUG_ROUTES !== 'true') {
4201
+ return c.notFound()
4202
+ }
4203
+ const stub = c.env.RECORD_ROOMS.get(`;
4204
+ var DOUBLE_QUOTED_DEBUG_ROUTE_SEAM = DEBUG_ROUTE_SEAM.replace(
4205
+ "app.all('/api/debug/*'",
4206
+ 'app.all("/api/debug/*"'
4207
+ );
4208
+ var SECURE_DEBUG_ROUTE_SEAM = ` app.all('/api/debug/*', async (c) => {
4209
+ if (c.env.ALLOW_DEBUG_ROUTES !== 'true') {
4210
+ return c.notFound()
4211
+ }
4212
+ const auth = await resolveAuth(c.req.raw, c.env)
4213
+ if (!auth) return c.json({ error: 'unauthorized' }, 401)
4214
+ if ((await resolveAppRole(c.env, auth.userId)) !== 'admin') {
4215
+ return c.json({ error: 'forbidden' }, 403)
4216
+ }
4217
+ const stub = c.env.RECORD_ROOMS.get(`;
4218
+ var DOUBLE_QUOTED_SECURE_DEBUG_ROUTE_SEAM = SECURE_DEBUG_ROUTE_SEAM.replace(
4219
+ "app.all('/api/debug/*'",
4220
+ 'app.all("/api/debug/*"'
4221
+ );
4222
+ var DEBUG_ROUTE_REPLACEMENTS = [
4223
+ [DEBUG_ROUTE_SEAM, SECURE_DEBUG_ROUTE_SEAM],
4224
+ [DOUBLE_QUOTED_DEBUG_ROUTE_SEAM, DOUBLE_QUOTED_SECURE_DEBUG_ROUTE_SEAM]
4225
+ ];
4226
+ var DEBUG_ROUTE_PATTERN = /\bapp\.all\(\s*(['"])\/api\/debug\/\*\1\s*,/;
4227
+ function debugRouteOccurrences(source) {
4228
+ return source.match(new RegExp(DEBUG_ROUTE_PATTERN.source, "g"))?.length ?? 0;
4229
+ }
4230
+ function secureDebugRouteOccurrences(source) {
4231
+ return DEBUG_ROUTE_REPLACEMENTS.reduce(
4232
+ (count, [, secure]) => count + occurrences(source, secure),
4233
+ 0
4234
+ );
4235
+ }
4236
+ function httpRoleImportOccurrences(source) {
4237
+ return source.match(new RegExp(HTTP_ROLE_IMPORT_PATTERN.source, "g"))?.length ?? 0;
4238
+ }
4239
+ function hasSecureDebugRoute(source) {
4240
+ return debugRouteOccurrences(source) === 1 && secureDebugRouteOccurrences(source) === 1 && httpRoleImportOccurrences(source) === 1;
4241
+ }
4242
+ function migrateSecureRoomBoundaries(source, file) {
4243
+ if (file.endsWith("src/server/realtime-routes.ts")) {
4244
+ return migrateRealtimeRoomBoundaries(source);
4245
+ }
4246
+ if (file.endsWith("worker.ts")) return migrateWorkerJobAuthorization(source);
4247
+ if (file.endsWith("src/server/http-routes.ts")) return migrateDebugRouteAuthorization(source);
4248
+ return { source, replacements: 0 };
4249
+ }
4250
+ function migrateRealtimeRoomBoundaries(source) {
4251
+ let migrated = source;
4252
+ let replacements = 0;
4253
+ if (!migrated.includes("authenticatedRoomRequest(")) {
4254
+ if (IDENTITY_ROUTE_REPLACEMENTS.some(
4255
+ ([before, , expected]) => occurrences(migrated, before) !== expected
4256
+ )) {
4257
+ return { source, replacements: 0 };
4258
+ }
4259
+ for (const [before, after, expected] of IDENTITY_ROUTE_REPLACEMENTS) {
4260
+ migrated = migrated.replaceAll(before, after);
4261
+ replacements += expected;
4262
+ }
4263
+ }
4264
+ return { source: migrated, replacements };
4265
+ }
4266
+ function migrateWorkerJobAuthorization(source) {
4267
+ if (!source.includes("class AppJobRoom")) return { source, replacements: 0 };
4268
+ if (hasSecureAppJobRoom(source)) return { source, replacements: 0 };
4269
+ if (WORKER_JOB_REPLACEMENTS.some(([before]) => occurrences(source, before) !== 1)) {
4270
+ return { source, replacements: 0 };
4271
+ }
4272
+ let migrated = source;
4273
+ for (const [before, after] of WORKER_JOB_REPLACEMENTS) migrated = migrated.replace(before, after);
4274
+ return { source: migrated, replacements: WORKER_JOB_REPLACEMENTS.length };
4275
+ }
4276
+ function migrateDebugRouteAuthorization(source) {
4277
+ if (debugRouteOccurrences(source) === 0 || hasSecureDebugRoute(source)) {
4278
+ return { source, replacements: 0 };
4279
+ }
4280
+ const matchingRoutes = DEBUG_ROUTE_REPLACEMENTS.filter(
4281
+ ([before2]) => occurrences(source, before2) === 1
4282
+ );
4283
+ if (debugRouteOccurrences(source) !== 1 || matchingRoutes.length !== 1 || httpRoleImportOccurrences(source) !== 0 || occurrences(source, HTTP_ROLE_IMPORT_SEAM) !== 1) {
4284
+ return { source, replacements: 0 };
4285
+ }
4286
+ const [before, after] = matchingRoutes[0];
4287
+ return {
4288
+ source: source.replace(HTTP_ROLE_IMPORT_SEAM, `${HTTP_ROLE_IMPORT_SEAM}
4289
+ ${HTTP_ROLE_IMPORT}`).replace(before, after),
4290
+ replacements: 2
4291
+ };
4292
+ }
4293
+ function findSecureRoomBoundaryBlockers(source, file) {
4294
+ if (file.endsWith("worker.ts") && source.includes("class AppJobRoom")) {
4295
+ if (!hasSecureAppJobRoom(source)) {
4296
+ return [
4297
+ roomBoundaryBlocker(
4298
+ file,
4299
+ source,
4300
+ "class AppJobRoom",
4301
+ "Configure AppJobRoom with authorizeWrite using the current users.role."
4302
+ )
4303
+ ];
4304
+ }
4305
+ return [];
4306
+ }
4307
+ if (file.endsWith("src/server/http-routes.ts") && debugRouteOccurrences(source) > 0) {
4308
+ if (!hasSecureDebugRoute(source)) {
4309
+ const route = source.match(DEBUG_ROUTE_PATTERN)?.[0] ?? "/api/debug/*";
4310
+ return [
4311
+ roomBoundaryBlocker(
4312
+ file,
4313
+ source,
4314
+ route,
4315
+ "Require verified owner/admin access inside the enabled debug-route handler."
4316
+ )
4317
+ ];
4318
+ }
4319
+ return [];
4320
+ }
4321
+ if (!file.endsWith("src/server/realtime-routes.ts")) return [];
4322
+ const lines = source.split("\n");
4323
+ let index = lines.findIndex(
4324
+ (line) => line.includes("authenticatedRoomUrl") || /searchParams\.(?:set|append)\(\s*['"](?:userId|userName|userEmail|userImageUrl|role)['"]/.test(
4325
+ line
4326
+ )
4327
+ );
4328
+ if (index < 0 && /\bauth\.(?:userId|claims)\b/.test(source)) {
4329
+ index = lines.findIndex((line) => /searchParams\.(?:set|append)\s*\(/.test(line));
4330
+ }
4331
+ if (index < 0) return [];
4332
+ return [
4333
+ {
4334
+ migrationId: SECURE_ROOM_BOUNDARIES_MIGRATION_ID,
4335
+ file,
4336
+ line: index + 1,
4337
+ message: "This customized room proxy still places verified user identity in its internal URL. Replace that forwarding code with authenticatedRoomRequest() from deepspace/worker before the Durable Object fetch."
4338
+ }
4339
+ ];
4340
+ }
4341
+ function roomBoundaryBlocker(file, source, seam, message) {
4342
+ const line = source.slice(0, source.indexOf(seam)).split("\n").length;
4343
+ return { migrationId: SECURE_ROOM_BOUNDARIES_MIGRATION_ID, file, line, message };
4344
+ }
4064
4345
  function migrateWorkerOwnedNotFound(source, file) {
4065
4346
  let replacements = 0;
4066
4347
  if (file.endsWith("wrangler.toml")) {
@@ -4113,7 +4394,9 @@ function findWorkerOwnedNotFoundBlockers(source, file) {
4113
4394
  const blockers = [];
4114
4395
  const lines = source.split("\n");
4115
4396
  for (const [index, line] of lines.entries()) {
4116
- if (!/['"`]\/index\.html/.test(line)) continue;
4397
+ if (!/(?:\burl\.pathname\s*=\s*|\bnew\s+URL\s*\(\s*)['"`]\/index\.html/.test(line)) {
4398
+ continue;
4399
+ }
4117
4400
  blockers.push({
4118
4401
  migrationId: WORKER_OWNED_NOT_FOUND_MIGRATION_ID,
4119
4402
  file,
@@ -4161,7 +4444,7 @@ async function buildDeployBundle(options) {
4161
4444
  preparedWranglerConfig?.cleanup();
4162
4445
  }
4163
4446
  spinner2.stop("Built");
4164
- const deployConfigPath = join16(appDir, ".wrangler", "deploy", "config.json");
4447
+ const deployConfigPath = join15(appDir, ".wrangler", "deploy", "config.json");
4165
4448
  if (!existsSync14(deployConfigPath)) {
4166
4449
  output.die(
4167
4450
  "Build output config not found at .wrangler/deploy/config.json",
@@ -4169,7 +4452,7 @@ async function buildDeployBundle(options) {
4169
4452
  );
4170
4453
  }
4171
4454
  const deployConfig = JSON.parse(readFileSync17(deployConfigPath, "utf-8"));
4172
- const outputWranglerPath = resolve11(dirname6(deployConfigPath), deployConfig.configPath);
4455
+ const outputWranglerPath = resolve11(dirname7(deployConfigPath), deployConfig.configPath);
4173
4456
  if (!existsSync14(outputWranglerPath)) {
4174
4457
  output.die(`Output wrangler.json not found at ${outputWranglerPath}`, "build_output_missing");
4175
4458
  }
@@ -4181,8 +4464,8 @@ async function buildDeployBundle(options) {
4181
4464
  "build_output_mismatch"
4182
4465
  );
4183
4466
  }
4184
- const workerDir = dirname6(outputWranglerPath);
4185
- const workerBundlePath = join16(workerDir, outputConfig.main);
4467
+ const workerDir = dirname7(outputWranglerPath);
4468
+ const workerBundlePath = join15(workerDir, outputConfig.main);
4186
4469
  const clientDir = outputConfig.assets?.directory ? resolve11(workerDir, outputConfig.assets.directory) : null;
4187
4470
  if (!existsSync14(workerBundlePath)) {
4188
4471
  output.die(`Worker bundle not found at ${workerBundlePath}`, "build_output_missing");
@@ -4274,7 +4557,7 @@ function collectAssets(dir) {
4274
4557
  const walk = (currentDir, prefix) => {
4275
4558
  for (const entry of readdirSync4(currentDir, { withFileTypes: true })) {
4276
4559
  if (isDeployAssetControlFile(entry.name)) continue;
4277
- const fullPath = join16(currentDir, entry.name);
4560
+ const fullPath = join15(currentDir, entry.name);
4278
4561
  const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
4279
4562
  if (entry.isDirectory()) {
4280
4563
  walk(fullPath, relativePath);
@@ -4298,7 +4581,7 @@ function readDeployAssetConfig(clientDir) {
4298
4581
  ["_headers", "_headers"],
4299
4582
  ["_redirects", "_redirects"]
4300
4583
  ]) {
4301
- const filePath = join16(clientDir, file);
4584
+ const filePath = join15(clientDir, file);
4302
4585
  if (!existsSync14(filePath)) continue;
4303
4586
  const content = readFileSync17(filePath, "utf8");
4304
4587
  if (content.trim()) config[key] = content;
@@ -4315,17 +4598,17 @@ function errorMessage(error) {
4315
4598
  // src/cli/commands/deploy/commerce.ts
4316
4599
  import * as p7 from "@clack/prompts";
4317
4600
  import { existsSync as existsSync15 } from "fs";
4318
- import { join as join17 } from "path";
4601
+ import { join as join16 } from "path";
4319
4602
  import { pathToFileURL as pathToFileURL2 } from "url";
4320
4603
  var API_URL2 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
4321
4604
  function errorMessage2(error) {
4322
4605
  return error instanceof Error ? error.message : String(error);
4323
4606
  }
4324
4607
  async function loadDeclaration(appDir, sourceFile, outputFile, exportName) {
4325
- const sourcePath = join17(appDir, "src", sourceFile);
4608
+ const sourcePath = join16(appDir, "src", sourceFile);
4326
4609
  if (!existsSync15(sourcePath)) return void 0;
4327
4610
  const esbuild = await import("esbuild");
4328
- const outputPath = join17(appDir, ".wrangler", "deploy", outputFile);
4611
+ const outputPath = join16(appDir, ".wrangler", "deploy", outputFile);
4329
4612
  await esbuild.build({
4330
4613
  entryPoints: [sourcePath],
4331
4614
  bundle: true,
@@ -4418,7 +4701,7 @@ Stripe will keep charging them until you cancel. Cancel at the end of their curr
4418
4701
  }
4419
4702
  }
4420
4703
  async function syncSubscriptionPlans(appDir, appName, token, nonInteractive) {
4421
- if (!existsSync15(join17(appDir, "src", "subscriptions.ts"))) return;
4704
+ if (!existsSync15(join16(appDir, "src", "subscriptions.ts"))) return;
4422
4705
  let plans;
4423
4706
  try {
4424
4707
  plans = await loadDeclaration(
@@ -4455,7 +4738,7 @@ async function syncSubscriptionPlans(appDir, appName, token, nonInteractive) {
4455
4738
  spinner2.stop(`Synced ${plans.length} plan${plans.length === 1 ? "" : "s"} to Stripe`);
4456
4739
  if (body.connectMissing) {
4457
4740
  p7.log.warn(
4458
- "Stripe Connect not yet set up. Customers can subscribe and earnings will queue in your platform balance until you finish onboarding at /earnings on the dashboard. The first payout after you connect will sweep everything that accumulated in the meantime."
4741
+ "Stripe Connect is not ready. Paid checkout will remain unavailable until the app owner finishes onboarding at /earnings on the dashboard."
4459
4742
  );
4460
4743
  }
4461
4744
  const changes = (body.planChanges ?? []).filter((change) => change.affectedSubscribers > 0);
@@ -4473,7 +4756,7 @@ async function syncSubscriptionPlans(appDir, appName, token, nonInteractive) {
4473
4756
  }
4474
4757
  }
4475
4758
  async function syncOneTimeProducts(appDir, appName, token) {
4476
- if (!existsSync15(join17(appDir, "src", "products.ts"))) return;
4759
+ if (!existsSync15(join16(appDir, "src", "products.ts"))) return;
4477
4760
  let products;
4478
4761
  try {
4479
4762
  products = await loadDeclaration(
@@ -4568,7 +4851,7 @@ function createDeployOutput(json) {
4568
4851
  else console.error(withSlug(message, code));
4569
4852
  if (action) printAction(action);
4570
4853
  }
4571
- process.exit(opts.actionRequired ? 2 : 1);
4854
+ throw new CliExit(opts.actionRequired ? 2 : 1);
4572
4855
  }
4573
4856
  };
4574
4857
  }
@@ -5096,7 +5379,16 @@ function selectGitHubRemote(cwd, options = {}) {
5096
5379
  return matching.find((remote) => remote.name === "origin") ?? (matching.length === 1 ? matching[0] : null);
5097
5380
  }
5098
5381
  function remotePublicRefs(cwd, remote) {
5099
- const output = runGit(cwd, ["ls-remote", "--refs", remote]).stdout.toString("utf-8");
5382
+ let output;
5383
+ try {
5384
+ output = runGit(cwd, ["ls-remote", "--refs", remote]).stdout.toString("utf-8");
5385
+ } catch (error) {
5386
+ if (!isGitCredentialFailure(error)) throw error;
5387
+ throw new GitError(
5388
+ "Git could not authenticate the remote. In a container, forward an SSH agent or use an ephemeral Git credential helper, then retry. Do not put tokens in remote URLs.",
5389
+ "github_credentials_required"
5390
+ );
5391
+ }
5100
5392
  return output.split("\n").map((line) => line.trim()).filter(Boolean).map((line) => {
5101
5393
  const [oid, name] = line.split(/\s+/, 2);
5102
5394
  return { oid, name };
@@ -5104,6 +5396,12 @@ function remotePublicRefs(cwd, remote) {
5104
5396
  (ref) => /^[0-9a-f]{40}$/.test(ref.oid) && (ref.name.startsWith("refs/heads/") || ref.name.startsWith("refs/tags/"))
5105
5397
  );
5106
5398
  }
5399
+ function isGitCredentialFailure(error) {
5400
+ if (!(error instanceof Error)) return false;
5401
+ return /authentication failed|could not read username|permission denied \(publickey\)|repository not found|terminal prompts disabled/i.test(
5402
+ error.message
5403
+ );
5404
+ }
5107
5405
  function remoteBranchOid(cwd, remote, branch) {
5108
5406
  return remotePublicRefs(cwd, remote).find((ref) => ref.name === `refs/heads/${branch}`)?.oid ?? null;
5109
5407
  }
@@ -5145,6 +5443,25 @@ function setAppSource(deployUrl, token, appId, input) {
5145
5443
  }
5146
5444
 
5147
5445
  // src/cli/commands/deploy/repository.ts
5446
+ function preflightDeployRepository(options) {
5447
+ const { appDir, push, source } = options;
5448
+ if (!push || source?.provider === "github") return null;
5449
+ try {
5450
+ assertSyncableRepo(appDir);
5451
+ if (!source && listGitHubRemotes(appDir).length > 0) {
5452
+ return {
5453
+ code: "source_unclaimed",
5454
+ error: "This app has a GitHub remote but no claimed source. Choose once with `deepspace app source github` (manual GitHub ownership) or `deepspace app source deepspace` (packaged DeepSpace source), then deploy again."
5455
+ };
5456
+ }
5457
+ const branch = currentBranch(appDir);
5458
+ if (!isWorkTreeClean(appDir)) return dirtyWorktreeRefusal(branch);
5459
+ if (!branch) return detachedHeadRefusal();
5460
+ return null;
5461
+ } catch (error) {
5462
+ return deployRepositoryFailure(error, appDir);
5463
+ }
5464
+ }
5148
5465
  async function syncDeployRepository(options) {
5149
5466
  const { deployUrl, appDir, appId, token, push, ignoreStale, output, sourceState } = options;
5150
5467
  let commitOid = null;
@@ -5165,21 +5482,12 @@ async function syncDeployRepository(options) {
5165
5482
  return { commitOid, recoverable, deployKey, source, sourceRevision, baseReleaseId };
5166
5483
  }
5167
5484
  try {
5168
- assertSyncableRepo(appDir);
5169
- if (!source && listGitHubRemotes(appDir).length > 0) {
5170
- output.die(
5171
- "This app has a GitHub remote but no claimed source. Choose once with `deepspace app source github` (manual GitHub ownership) or `deepspace app source deepspace` (packaged DeepSpace source), then deploy again.",
5172
- "source_unclaimed"
5173
- );
5174
- }
5485
+ const preflight = preflightDeployRepository({ appDir, push, source });
5486
+ if (preflight) output.die(preflight.error, preflight.code);
5175
5487
  const sourceRemote = spaceRemoteName();
5176
5488
  ensureSpaceRemote(appDir, appId, sourceRemote);
5177
5489
  const branch = currentBranch(appDir);
5178
5490
  const workspaceBranchId = workspaceIdFromBranch(branch);
5179
- if (!isWorkTreeClean(appDir)) {
5180
- const refusal = dirtyWorktreeRefusal(branch);
5181
- output.die(refusal.error, refusal.code);
5182
- }
5183
5491
  if (!branch) {
5184
5492
  const refusal = detachedHeadRefusal();
5185
5493
  output.die(refusal.error, refusal.code);
@@ -5272,6 +5580,7 @@ async function syncDeployRepository(options) {
5272
5580
  sourceRevision = claimed.revision;
5273
5581
  }
5274
5582
  } catch (error) {
5583
+ if (error instanceof CliExit) throw error;
5275
5584
  const failure = deployRepositoryFailure(error, appDir);
5276
5585
  output.die(failure.error, failure.code);
5277
5586
  }
@@ -5281,12 +5590,7 @@ function classifyRemoteState(appDir, token, branch, localTip, remote) {
5281
5590
  let remoteTip = null;
5282
5591
  const remoteRef = `refs/remotes/${remote}/${branch}`;
5283
5592
  try {
5284
- runGitRemote(appDir, token, [
5285
- "fetch",
5286
- "--quiet",
5287
- remote,
5288
- `+refs/heads/${branch}:${remoteRef}`
5289
- ]);
5593
+ runGitRemote(appDir, token, ["fetch", "--quiet", remote, `+refs/heads/${branch}:${remoteRef}`]);
5290
5594
  remoteTip = resolveCommit(appDir, remoteRef);
5291
5595
  return {
5292
5596
  remoteTip,
@@ -5393,7 +5697,7 @@ async function deployBuiltBundle(options) {
5393
5697
  ...action ? { action } : {}
5394
5698
  });
5395
5699
  }
5396
- process.exit(actionRequired ? 2 : 1);
5700
+ throw new CliExit(actionRequired ? 2 : 1);
5397
5701
  };
5398
5702
  const makeForm = () => {
5399
5703
  const form = new FormData();
@@ -5860,6 +6164,14 @@ var deploy_default = defineCommand3({
5860
6164
  output
5861
6165
  });
5862
6166
  const sourceState = await getAppSource(DEPLOY_URL3, token, appId);
6167
+ const repositoryPreflight = preflightDeployRepository({
6168
+ appDir,
6169
+ push: args.push !== false,
6170
+ source: sourceState.source
6171
+ });
6172
+ if (repositoryPreflight) {
6173
+ output.die(repositoryPreflight.error, repositoryPreflight.code);
6174
+ }
5863
6175
  const spinner2 = createSpinner();
5864
6176
  const bundle = await buildDeployBundle({
5865
6177
  appDir,
@@ -6296,6 +6608,7 @@ var apps_default = defineDeepspaceCommand({
6296
6608
 
6297
6609
  // src/cli/commands/logs.ts
6298
6610
  import { defineCommand as defineCommand4 } from "citty";
6611
+ import { setTimeout as delay } from "timers/promises";
6299
6612
 
6300
6613
  // src/shared/log-events.ts
6301
6614
  var LOG_LEVELS = ["debug", "log", "info", "warn", "error"];
@@ -6322,11 +6635,17 @@ function parseSince(input, now = Date.now()) {
6322
6635
  } else {
6323
6636
  ts = Date.parse(trimmed);
6324
6637
  if (Number.isNaN(ts)) {
6325
- throw new InputError(`Invalid --since "${input}" \u2014 use 30s, 15m, 2h, 7d, or an ISO timestamp.`, "invalid_since");
6638
+ throw new InputError(
6639
+ `Invalid --since "${input}" \u2014 use 30s, 15m, 2h, 7d, or an ISO timestamp.`,
6640
+ "invalid_since"
6641
+ );
6326
6642
  }
6327
6643
  }
6328
6644
  if (now - ts > RETENTION_MS) {
6329
- throw new InputError("Logs are retained for 7 days \u2014 --since can reach back at most 7d.", "since_out_of_range");
6645
+ throw new InputError(
6646
+ "Logs are retained for 7 days \u2014 --since can reach back at most 7d.",
6647
+ "since_out_of_range"
6648
+ );
6330
6649
  }
6331
6650
  return ts;
6332
6651
  }
@@ -6400,7 +6719,7 @@ function formatEvent(e, color, now = Date.now()) {
6400
6719
  const client = e.source === "client" ? paint("35", "CLIENT", color) + " " : "";
6401
6720
  return `${time} ${client}${tag} ${body}`;
6402
6721
  }
6403
- var sleep3 = (ms) => new Promise((r) => setTimeout(r, ms));
6722
+ var sleep3 = (ms, signal) => delay(ms, void 0, { signal });
6404
6723
  var logs_default = defineCommand4({
6405
6724
  meta: {
6406
6725
  name: "logs",
@@ -6457,29 +6776,42 @@ var logs_default = defineCommand4({
6457
6776
  const token = await ensureToken();
6458
6777
  const appId = await resolveAppTarget(DEPLOY_URL6, token, args.app, { wranglerEnv: envArg });
6459
6778
  if (args.level && !LOG_LEVELS.includes(args.level)) {
6460
- throw new InputError(`Invalid --level "${args.level}". Use: ${LOG_LEVELS.join(", ")}`, "invalid_level");
6779
+ throw new InputError(
6780
+ `Invalid --level "${args.level}". Use: ${LOG_LEVELS.join(", ")}`,
6781
+ "invalid_level"
6782
+ );
6461
6783
  }
6462
6784
  let limit;
6463
6785
  if (args.limit !== void 0) {
6464
6786
  limit = Number(args.limit);
6465
6787
  if (!Number.isInteger(limit) || limit < 1 || limit > 500) {
6466
- throw new InputError("Invalid --limit \u2014 expected an integer between 1 and 500.", "invalid_limit");
6788
+ throw new InputError(
6789
+ "Invalid --limit \u2014 expected an integer between 1 and 500.",
6790
+ "invalid_limit"
6791
+ );
6467
6792
  }
6468
6793
  }
6469
6794
  const initialSince = args.since ? parseSince(args.since) : Date.now() - DEFAULT_WINDOW_MS;
6470
6795
  const windowLabel = args.since?.trim() || "15m";
6471
6796
  const MAX_LIMIT = 500;
6472
6797
  const followLimit = followInitialLimit(limit, MAX_LIMIT);
6473
- const fetchPage = async (since, pageLimit, token2) => {
6798
+ const fetchPage = async (since, pageLimit, token2, signal) => {
6474
6799
  const params = new URLSearchParams({ since: String(since) });
6475
6800
  if (pageLimit !== void 0) params.set("limit", String(pageLimit));
6476
6801
  if (args.level) params.set("level", args.level);
6477
6802
  if (args.search) params.set("search", args.search);
6478
- return apiFetch(DEPLOY_URL6, token2, `/api/apps/${appId}/logs?${params}`);
6803
+ return apiFetch(
6804
+ DEPLOY_URL6,
6805
+ token2,
6806
+ `/api/apps/${appId}/logs?${params}`,
6807
+ signal ? { signal } : void 0
6808
+ );
6479
6809
  };
6480
6810
  const print = (events) => {
6481
6811
  for (const e of events) {
6482
- process.stdout.write(args.json ? JSON.stringify(e) + "\n" : formatEvent(e, stdoutColor()) + "\n");
6812
+ process.stdout.write(
6813
+ args.json ? JSON.stringify(e) + "\n" : formatEvent(e, stdoutColor()) + "\n"
6814
+ );
6483
6815
  }
6484
6816
  };
6485
6817
  const first = await fetchPage(initialSince, args.follow ? followLimit : limit, token);
@@ -6487,19 +6819,30 @@ var logs_default = defineCommand4({
6487
6819
  if (!args.follow) {
6488
6820
  if (first.events.length === 0) {
6489
6821
  if (args.json) {
6490
- process.stdout.write(JSON.stringify({ type: "meta", count: 0, window: windowLabel }) + "\n");
6822
+ process.stdout.write(
6823
+ JSON.stringify({ type: "meta", count: 0, window: windowLabel }) + "\n"
6824
+ );
6491
6825
  } else {
6492
6826
  console.log(
6493
6827
  `No logs in the last ${windowLabel} for ${appId}. New logs can take up to a minute to appear; retention is 7 days.`
6494
6828
  );
6495
6829
  }
6496
6830
  } else if (first.truncated) {
6497
- if (args.json) process.stdout.write(JSON.stringify({ type: "meta", truncated: true }) + "\n");
6498
- else console.error(`(showing the newest ${first.events.length} events \u2014 narrow with --since/--level/--search)`);
6831
+ if (args.json)
6832
+ process.stdout.write(JSON.stringify({ type: "meta", truncated: true }) + "\n");
6833
+ else if (args.search)
6834
+ console.error(
6835
+ `(showing the newest ${first.events.length} matches \u2014 search scans at most the newest 500 level-filtered events per fetch; narrow with --since/--level or raise --limit)`
6836
+ );
6837
+ else
6838
+ console.error(
6839
+ `(showing the newest ${first.events.length} events \u2014 narrow with --since/--level)`
6840
+ );
6499
6841
  }
6500
6842
  return;
6501
6843
  }
6502
- process.on("SIGINT", () => process.exit(0));
6844
+ const tail = new AbortController();
6845
+ process.once("SIGINT", () => tail.abort());
6503
6846
  if (!args.json) {
6504
6847
  console.error(`Tailing logs for ${appId} \u2014 Ctrl+C to stop.`);
6505
6848
  }
@@ -6515,35 +6858,56 @@ var logs_default = defineCommand4({
6515
6858
  process.stdout.write(JSON.stringify({ type: "meta", truncated: true }) + "\n");
6516
6859
  } else {
6517
6860
  console.error(
6518
- paint("33", `(more than ${followLimit} events in the initial ${windowLabel} window \u2014 some older events not shown; narrow with --since/--level/--search)`, stderrColor())
6861
+ paint(
6862
+ "33",
6863
+ args.search ? `(search scanned only the newest ${MAX_LIMIT} level-filtered events in the initial ${windowLabel} window \u2014 older matches may be omitted; narrow with --since/--level)` : `(more than ${followLimit} events in the initial ${windowLabel} window \u2014 some older events not shown; narrow with --since/--level)`,
6864
+ stderrColor()
6865
+ )
6519
6866
  );
6520
6867
  }
6521
6868
  }
6522
6869
  for (; ; ) {
6523
- await sleep3(backoff || interval);
6870
+ try {
6871
+ await sleep3(backoff || interval, tail.signal);
6872
+ } catch {
6873
+ return;
6874
+ }
6524
6875
  const freshToken = await ensureToken();
6876
+ if (tail.signal.aborted) return;
6525
6877
  let page;
6526
6878
  try {
6527
- page = await fetchPage(nextPollSince(cursor, initialSince), followLimit, freshToken);
6879
+ page = await fetchPage(
6880
+ nextPollSince(cursor, initialSince),
6881
+ followLimit,
6882
+ freshToken,
6883
+ tail.signal
6884
+ );
6528
6885
  } catch (err) {
6886
+ if (tail.signal.aborted) return;
6529
6887
  if (err instanceof ApiError && [401, 403, 404].includes(err.status)) throw err;
6530
6888
  backoff = Math.min(backoff ? backoff * 2 : MIN_POLL_MS, 3e4);
6531
6889
  const msg = err instanceof Error ? err.message : String(err);
6532
- console.error(paint("2", `(logs fetch failed: ${msg} \u2014 retrying in ${backoff / 1e3}s)`, stderrColor()));
6890
+ console.error(
6891
+ paint("2", `(logs fetch failed: ${msg} \u2014 retrying in ${backoff / 1e3}s)`, stderrColor())
6892
+ );
6533
6893
  continue;
6534
6894
  }
6535
6895
  backoff = 0;
6536
6896
  interval = Math.max(page.pollIntervalMs || 0, MIN_POLL_MS);
6537
6897
  const fresh = seen.fresh(page.events);
6538
6898
  print(fresh);
6539
- const realDrop = fresh.length >= followLimit;
6899
+ const realDrop = fresh.length >= followLimit || Boolean(args.search && page.truncated);
6540
6900
  if (realDrop && !warnedTruncated) {
6541
6901
  warnedTruncated = true;
6542
6902
  if (args.json) {
6543
6903
  process.stdout.write(JSON.stringify({ type: "meta", truncated: true }) + "\n");
6544
6904
  } else {
6545
6905
  console.error(
6546
- paint("33", `(burst exceeded ${followLimit} events/poll \u2014 some may be dropped; narrow with --level/--search)`, stderrColor())
6906
+ paint(
6907
+ "33",
6908
+ args.search ? `(search scanned only the newest ${MAX_LIMIT} level-filtered events this poll \u2014 older matches may be omitted; narrow with --level)` : `(burst exceeded ${followLimit} events/poll \u2014 some may be dropped; narrow with --level)`,
6909
+ stderrColor()
6910
+ )
6547
6911
  );
6548
6912
  }
6549
6913
  } else if (!realDrop) {
@@ -6625,7 +6989,7 @@ var usage_default = defineDeepspaceCommand({
6625
6989
 
6626
6990
  // src/cli/commands/add.ts
6627
6991
  import { existsSync as existsSync18, readFileSync as readFileSync20, realpathSync as realpathSync4 } from "fs";
6628
- import { dirname as dirname8, join as join19, resolve as resolve15 } from "path";
6992
+ import { dirname as dirname9, join as join18, resolve as resolve15 } from "path";
6629
6993
  import { pathToFileURL as pathToFileURL3 } from "url";
6630
6994
 
6631
6995
  // src/cli/commands/feature-installer.ts
@@ -6641,17 +7005,17 @@ import {
6641
7005
  readSync,
6642
7006
  readdirSync as readdirSync5,
6643
7007
  rmSync as rmSync4,
6644
- writeFileSync as writeFileSync8
7008
+ writeFileSync as writeFileSync6
6645
7009
  } from "fs";
6646
7010
  import { spawnSync as spawnSync6 } from "child_process";
6647
7011
  import { tmpdir as tmpdir2 } from "os";
6648
- import { dirname as dirname7, isAbsolute as isAbsolute4, join as join18, resolve as resolve14 } from "path";
7012
+ import { dirname as dirname8, isAbsolute as isAbsolute4, join as join17, resolve as resolve14 } from "path";
6649
7013
  import { fileURLToPath } from "url";
6650
7014
  var NAV_MARKER = "// \u2500\u2500 Features add nav items below this line \u2500\u2500";
6651
7015
  function featurePackageRoot(moduleUrl = import.meta.url) {
6652
- let dir = dirname7(fileURLToPath(moduleUrl));
7016
+ let dir = dirname8(fileURLToPath(moduleUrl));
6653
7017
  for (; ; ) {
6654
- const packagePath = join18(dir, "package.json");
7018
+ const packagePath = join17(dir, "package.json");
6655
7019
  if (existsSync17(packagePath)) {
6656
7020
  try {
6657
7021
  const pkg2 = JSON.parse(readFileSync19(packagePath, "utf-8"));
@@ -6659,16 +7023,16 @@ function featurePackageRoot(moduleUrl = import.meta.url) {
6659
7023
  } catch {
6660
7024
  }
6661
7025
  }
6662
- const parent = dirname7(dir);
7026
+ const parent = dirname8(dir);
6663
7027
  if (parent === dir) throw new Error("Could not locate the deepspace package root");
6664
7028
  dir = parent;
6665
7029
  }
6666
7030
  }
6667
7031
  function hasFeatureCatalog(packageRoot = featurePackageRoot()) {
6668
- return existsSync17(join18(packageRoot, "features"));
7032
+ return existsSync17(join17(packageRoot, "features"));
6669
7033
  }
6670
7034
  function readFeatureConfig(id, packageRoot = featurePackageRoot()) {
6671
- const configPath = join18(packageRoot, "features", id, "feature.json");
7035
+ const configPath = join17(packageRoot, "features", id, "feature.json");
6672
7036
  if (!existsSync17(configPath)) return null;
6673
7037
  try {
6674
7038
  const config = JSON.parse(readFileSync19(configPath, "utf-8"));
@@ -6681,7 +7045,7 @@ function readFeatureConfig(id, packageRoot = featurePackageRoot()) {
6681
7045
  }
6682
7046
  }
6683
7047
  function listFeatureConfigs(packageRoot = featurePackageRoot()) {
6684
- const featuresDir = join18(packageRoot, "features");
7048
+ const featuresDir = join17(packageRoot, "features");
6685
7049
  if (!existsSync17(featuresDir)) return [];
6686
7050
  return readdirSync5(featuresDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => readFeatureConfig(entry.name, packageRoot)).filter((config) => config !== null && !config.internal).sort((a, b) => a.id.localeCompare(b.id));
6687
7051
  }
@@ -6730,17 +7094,17 @@ function resolveFeatureDestPath(fileDest, route) {
6730
7094
  return route?.protected === false ? `${appPages}${rest}` : `${appPages}(protected)/${rest}`;
6731
7095
  }
6732
7096
  function copyFile(src, dest) {
6733
- mkdirSync6(dirname7(dest), { recursive: true });
7097
+ mkdirSync6(dirname8(dest), { recursive: true });
6734
7098
  copyFileSync(src, dest);
6735
7099
  }
6736
7100
  function copyFileWithImportShift(src, dest, levels) {
6737
- mkdirSync6(dirname7(dest), { recursive: true });
7101
+ mkdirSync6(dirname8(dest), { recursive: true });
6738
7102
  const prefix = "../".repeat(levels);
6739
7103
  const shifted = readFileSync19(src, "utf-8").replace(
6740
7104
  /(from\s+|import\s+|import\()(['"])(\.\.\/)/g,
6741
7105
  `$1$2${prefix}$3`
6742
7106
  );
6743
- writeFileSync8(dest, shifted);
7107
+ writeFileSync6(dest, shifted);
6744
7108
  }
6745
7109
  function installFiles(packageRoot, config, targetDir, emit) {
6746
7110
  let copied = 0;
@@ -6749,9 +7113,9 @@ function installFiles(packageRoot, config, targetDir, emit) {
6749
7113
  const preexisting = [];
6750
7114
  const missing = [];
6751
7115
  for (const file of config.files) {
6752
- const srcPath = join18(packageRoot, "features", config.id, file.src);
7116
+ const srcPath = join17(packageRoot, "features", config.id, file.src);
6753
7117
  const resolvedDest = resolveFeatureDestPath(file.dest, config.route);
6754
- const destPath = join18(targetDir, resolvedDest);
7118
+ const destPath = join17(targetDir, resolvedDest);
6755
7119
  const existed = existsSync17(destPath);
6756
7120
  if (existed) preexisting.push(resolvedDest);
6757
7121
  if (!existsSync17(srcPath)) {
@@ -6791,7 +7155,7 @@ function unresolvedSchema(schema, reason, emit) {
6791
7155
  }
6792
7156
  function integrateSchema(config, targetDir, emit) {
6793
7157
  if (!config.schema) return [];
6794
- const schemasPath = join18(targetDir, "src", "schemas.ts");
7158
+ const schemasPath = join17(targetDir, "src", "schemas.ts");
6795
7159
  if (!existsSync17(schemasPath)) {
6796
7160
  return [unresolvedSchema(config.schema, "src/schemas.ts was not found.", emit)];
6797
7161
  }
@@ -6826,7 +7190,7 @@ export const schemas`
6826
7190
  return [unresolvedSchema(config.schema, reason, emit)];
6827
7191
  }
6828
7192
  content = withEntry;
6829
- writeFileSync8(schemasPath, content);
7193
+ writeFileSync6(schemasPath, content);
6830
7194
  emit(` Schema integrated: ${exportName} -> schemas.ts`);
6831
7195
  return [];
6832
7196
  }
@@ -6848,7 +7212,7 @@ function unresolvedActions(actions, reason, emit) {
6848
7212
  }
6849
7213
  function integrateActions(config, targetDir, emit) {
6850
7214
  if (!config.actions?.length) return [];
6851
- const actionsPath = join18(targetDir, "src", "actions", "index.ts");
7215
+ const actionsPath = join17(targetDir, "src", "actions", "index.ts");
6852
7216
  if (!existsSync17(actionsPath)) {
6853
7217
  return [unresolvedActions(config.actions, "src/actions/index.ts was not found.", emit)];
6854
7218
  }
@@ -6892,13 +7256,13 @@ ${registryEntry}`);
6892
7256
  integrated++;
6893
7257
  emit(` Action integrated: ${action.registerAs} (${action.exportName})`);
6894
7258
  }
6895
- if (integrated > 0) writeFileSync8(actionsPath, content);
7259
+ if (integrated > 0) writeFileSync6(actionsPath, content);
6896
7260
  return unresolved;
6897
7261
  }
6898
7262
  function integrateCode(config, targetDir, emit) {
6899
7263
  const unresolved = [];
6900
7264
  for (const insertion of config.code ?? []) {
6901
- const targetPath = join18(targetDir, insertion.file);
7265
+ const targetPath = join17(targetDir, insertion.file);
6902
7266
  if (!existsSync17(targetPath)) {
6903
7267
  unresolved.push({
6904
7268
  area: "code",
@@ -6935,27 +7299,27 @@ ${content.slice(importIndex < 0 ? 0 : importIndex)}`;
6935
7299
  const prefix = insertion.placement === "before" ? "" : "\n";
6936
7300
  const suffix = insertion.placement === "before" ? "\n" : "";
6937
7301
  content = `${content.slice(0, insertAt)}${prefix}${insertion.insert}${suffix}${content.slice(insertAt)}`;
6938
- writeFileSync8(targetPath, content);
7302
+ writeFileSync6(targetPath, content);
6939
7303
  emit(` Integrated: ${insertion.file}`);
6940
7304
  }
6941
7305
  return unresolved;
6942
7306
  }
6943
7307
  function integrateIgnore(config, targetDir) {
6944
7308
  if (!config.ignore?.length) return;
6945
- const targetPath = join18(targetDir, ".gitignore");
7309
+ const targetPath = join17(targetDir, ".gitignore");
6946
7310
  const current = existsSync17(targetPath) ? readFileSync19(targetPath, "utf-8") : "";
6947
7311
  const present = new Set(current.split(/\r?\n/).map((line) => line.trim()));
6948
7312
  const missing = config.ignore.filter((pattern) => !present.has(pattern));
6949
7313
  if (missing.length === 0) return;
6950
7314
  const separator = current === "" || current.endsWith("\n") ? "" : "\n";
6951
- writeFileSync8(targetPath, `${current}${separator}${missing.join("\n")}
7315
+ writeFileSync6(targetPath, `${current}${separator}${missing.join("\n")}
6952
7316
  `);
6953
7317
  }
6954
7318
  function integrateWrangler(config, targetDir, emit) {
6955
7319
  const runWorkerFirst = config.wrangler?.runWorkerFirst === true;
6956
7320
  const rateLimits = config.wrangler?.rateLimits ?? [];
6957
7321
  if (!runWorkerFirst && rateLimits.length === 0) return [];
6958
- const targetPath = join18(targetDir, "wrangler.toml");
7322
+ const targetPath = join17(targetDir, "wrangler.toml");
6959
7323
  const steps = [
6960
7324
  ...runWorkerFirst ? ["Set assets.run_worker_first = true in wrangler.toml."] : [],
6961
7325
  ...rateLimits.map((binding) => `Add the ${binding.name} rate-limit binding to wrangler.toml.`)
@@ -6985,7 +7349,7 @@ ${rateLimitBindingBlock(scope, binding)}
6985
7349
  }
6986
7350
  }
6987
7351
  if (changed) {
6988
- writeFileSync8(targetPath, source);
7352
+ writeFileSync6(targetPath, source);
6989
7353
  emit(" Integrated: wrangler.toml");
6990
7354
  } else emit(" Integration already present: wrangler.toml");
6991
7355
  return [];
@@ -7039,7 +7403,7 @@ function rateLimitBindingBlock(scope, binding) {
7039
7403
  }
7040
7404
  function integrateCss(packageRoot, config, targetDir, emit) {
7041
7405
  if (!config.css?.length) return [];
7042
- const stylesPath = join18(targetDir, "src", "styles.css");
7406
+ const stylesPath = join17(targetDir, "src", "styles.css");
7043
7407
  if (!existsSync17(stylesPath)) {
7044
7408
  emit(" Warning: Cannot integrate CSS \u2014 src/styles.css not found");
7045
7409
  return [
@@ -7056,7 +7420,7 @@ function integrateCss(packageRoot, config, targetDir, emit) {
7056
7420
  let integrated = 0;
7057
7421
  const unresolved = [];
7058
7422
  for (const cssFile of config.css) {
7059
- const srcPath = join18(packageRoot, "features", config.id, cssFile);
7423
+ const srcPath = join17(packageRoot, "features", config.id, cssFile);
7060
7424
  if (!existsSync17(srcPath)) {
7061
7425
  emit(` Warning: CSS source not found: ${cssFile}`, "stderr");
7062
7426
  unresolved.push({
@@ -7078,7 +7442,7 @@ ${css}`;
7078
7442
  integrated++;
7079
7443
  emit(` CSS integrated: ${cssFile} -> styles.css`);
7080
7444
  }
7081
- if (integrated > 0) writeFileSync8(stylesPath, styles);
7445
+ if (integrated > 0) writeFileSync6(stylesPath, styles);
7082
7446
  return unresolved;
7083
7447
  }
7084
7448
  function unresolvedRoute(route, config, reason, emit) {
@@ -7093,7 +7457,7 @@ function unresolvedRoute(route, config, reason, emit) {
7093
7457
  }
7094
7458
  function integrateRoute(config, targetDir, emit) {
7095
7459
  if (!config.route) return [];
7096
- const navPath = join18(targetDir, "src", "nav.ts");
7460
+ const navPath = join17(targetDir, "src", "nav.ts");
7097
7461
  if (!existsSync17(navPath)) {
7098
7462
  return [unresolvedRoute(config.route, config, "src/nav.ts was not found.", emit)];
7099
7463
  }
@@ -7118,7 +7482,7 @@ function integrateRoute(config, targetDir, emit) {
7118
7482
  const entry = ` { path: '${routePath}', label: '${config.name}'${roles} },`;
7119
7483
  content = content.replace(NAV_MARKER, `${NAV_MARKER}
7120
7484
  ${entry}`);
7121
- writeFileSync8(navPath, content);
7485
+ writeFileSync6(navPath, content);
7122
7486
  emit(` Nav wired: ${routePath} (${config.name})`);
7123
7487
  emit(" Route: automatic (file-based routing via src/pages/)");
7124
7488
  return [];
@@ -7144,10 +7508,10 @@ function runPackageInstall(packageManager, targetDir, outputVisible) {
7144
7508
  const child = spawnSync6(packageManager, ["install"], { ...options, stdio: "inherit" });
7145
7509
  return { error: child.error, status: child.status };
7146
7510
  }
7147
- const captureDir = mkdtempSync(join18(tmpdir2(), "deepspace-add-install-"));
7511
+ const captureDir = mkdtempSync(join17(tmpdir2(), "deepspace-add-install-"));
7148
7512
  let outputFd;
7149
7513
  try {
7150
- outputFd = openSync2(join18(captureDir, "output.log"), "w+");
7514
+ outputFd = openSync2(join17(captureDir, "output.log"), "w+");
7151
7515
  const child = spawnSync6(packageManager, ["install"], {
7152
7516
  ...options,
7153
7517
  stdio: ["ignore", outputFd, outputFd]
@@ -7171,7 +7535,7 @@ function integrateDependencies(config, targetDir, runInstall, outputVisible, emi
7171
7535
  if (declarations.every(({ values }) => !values || Object.keys(values).length === 0)) {
7172
7536
  return { added: [], installRan: false, packageManager };
7173
7537
  }
7174
- const packagePath = join18(targetDir, "package.json");
7538
+ const packagePath = join17(targetDir, "package.json");
7175
7539
  if (!existsSync17(packagePath)) {
7176
7540
  emit(" Could not patch package.json (file not found)");
7177
7541
  return { added: [], installRan: false, packageManager };
@@ -7202,7 +7566,7 @@ function integrateDependencies(config, targetDir, runInstall, outputVisible, emi
7202
7566
  if (added.length === 0) {
7203
7567
  emit(" Dependencies already present");
7204
7568
  } else {
7205
- writeFileSync8(packagePath, `${JSON.stringify(pkg2, null, 2)}
7569
+ writeFileSync6(packagePath, `${JSON.stringify(pkg2, null, 2)}
7206
7570
  `, "utf-8");
7207
7571
  emit(` Added to package.json: ${added.map(({ name }) => name).join(", ")}`);
7208
7572
  if (skipped.length) emit(` Already present (kept your version): ${skipped.join(", ")}`);
@@ -7238,7 +7602,7 @@ ${child.diagnosticTail}` : "";
7238
7602
  }
7239
7603
  function dependencySnapshot(targetDir) {
7240
7604
  try {
7241
- const pkg2 = JSON.parse(readFileSync19(join18(targetDir, "package.json"), "utf-8"));
7605
+ const pkg2 = JSON.parse(readFileSync19(join17(targetDir, "package.json"), "utf-8"));
7242
7606
  return {
7243
7607
  dependencies: pkg2.dependencies ?? {},
7244
7608
  devDependencies: pkg2.devDependencies ?? {}
@@ -7250,7 +7614,7 @@ function dependencySnapshot(targetDir) {
7250
7614
  function installedNavFeatures(packageRoot, targetDir) {
7251
7615
  return listFeatureConfigs(packageRoot).filter((feature) => feature.category === "nav" && feature.files.length > 0).filter((feature) => {
7252
7616
  const signature = resolveFeatureDestPath(feature.files[0].dest, feature.route);
7253
- return existsSync17(join18(targetDir, signature));
7617
+ return existsSync17(join17(targetDir, signature));
7254
7618
  }).map((feature) => feature.id);
7255
7619
  }
7256
7620
  function warnNavConflicts(packageRoot, config, targetDir, emit) {
@@ -7483,8 +7847,8 @@ function renderFeatureInfo(config) {
7483
7847
  return lines.join("\n");
7484
7848
  }
7485
7849
  function isDeepSpaceApp(dir) {
7486
- if (existsSync18(join19(dir, "wrangler.toml"))) return true;
7487
- const packagePath = join19(dir, "package.json");
7850
+ if (existsSync18(join18(dir, "wrangler.toml"))) return true;
7851
+ const packagePath = join18(dir, "package.json");
7488
7852
  if (!existsSync18(packagePath)) return false;
7489
7853
  try {
7490
7854
  const pkg2 = JSON.parse(readFileSync20(packagePath, "utf-8"));
@@ -7496,9 +7860,9 @@ function isDeepSpaceApp(dir) {
7496
7860
  function installedPackageRoot(fromDir) {
7497
7861
  let dir = fromDir;
7498
7862
  for (; ; ) {
7499
- const candidate = join19(dir, "node_modules", "deepspace");
7500
- if (existsSync18(join19(candidate, "package.json"))) return candidate;
7501
- const parent = dirname8(dir);
7863
+ const candidate = join18(dir, "node_modules", "deepspace");
7864
+ if (existsSync18(join18(candidate, "package.json"))) return candidate;
7865
+ const parent = dirname9(dir);
7502
7866
  if (parent === dir) return null;
7503
7867
  dir = parent;
7504
7868
  }
@@ -7514,8 +7878,8 @@ async function targetInstaller(appDir) {
7514
7878
  if (realpathSync4(installedRoot) === realpathSync4(featurePackageRoot())) {
7515
7879
  return { installFeature };
7516
7880
  }
7517
- const entry = join19(installedRoot, "dist", "feature-installer.js");
7518
- const catalog = join19(installedRoot, "features");
7881
+ const entry = join18(installedRoot, "dist", "feature-installer.js");
7882
+ const catalog = join18(installedRoot, "features");
7519
7883
  if (!existsSync18(entry) || !existsSync18(catalog)) {
7520
7884
  throw new Refusal(
7521
7885
  `The app's installed deepspace package does not contain the typed feature installer.
@@ -7690,13 +8054,12 @@ function fmtDate(iso) {
7690
8054
  }
7691
8055
  async function confirm4(message) {
7692
8056
  process.stdout.write(`${message} [y/N] `);
7693
- return new Promise((resolve21) => {
8057
+ const answer = await new Promise((resolve21) => {
7694
8058
  process.stdin.setEncoding("utf-8");
7695
- process.stdin.once("data", (data) => {
7696
- const answer = data.toString().trim().toLowerCase();
7697
- resolve21(answer === "y" || answer === "yes");
7698
- });
8059
+ process.stdin.once("data", (data) => resolve21(data.toString().trim().toLowerCase()));
7699
8060
  });
8061
+ process.stdin.pause();
8062
+ return answer === "y" || answer === "yes";
7700
8063
  }
7701
8064
  var cannotPrompt = (json) => json || !process.stdin.isTTY;
7702
8065
  var search = defineDeepspaceCommand({
@@ -9047,7 +9410,7 @@ var integrations_default = defineCommand9({
9047
9410
  // src/cli/commands/feedback.ts
9048
9411
  import * as p16 from "@clack/prompts";
9049
9412
  import { existsSync as existsSync20, readFileSync as readFileSync22 } from "fs";
9050
- import { dirname as dirname9, join as join20 } from "path";
9413
+ import { dirname as dirname10, join as join19 } from "path";
9051
9414
  import { fileURLToPath as fileURLToPath2 } from "url";
9052
9415
  import { release } from "os";
9053
9416
  var API_URL8 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
@@ -9068,10 +9431,10 @@ function normalizeType(raw) {
9068
9431
  }
9069
9432
  function readCliVersion() {
9070
9433
  try {
9071
- const here = dirname9(fileURLToPath2(import.meta.url));
9434
+ const here = dirname10(fileURLToPath2(import.meta.url));
9072
9435
  const candidates = [
9073
- join20(here, "..", "package.json"),
9074
- join20(here, "..", "..", "..", "package.json")
9436
+ join19(here, "..", "package.json"),
9437
+ join19(here, "..", "..", "..", "package.json")
9075
9438
  ];
9076
9439
  for (const path of candidates) {
9077
9440
  if (!existsSync20(path)) continue;
@@ -9368,7 +9731,7 @@ function ok(json, data, human) {
9368
9731
  function warnIfDebugRoutesEnabled(secrets) {
9369
9732
  if (secrets.ALLOW_DEBUG_ROUTES !== "true") return false;
9370
9733
  console.warn(
9371
- "\n\u26A0 ALLOW_DEBUG_ROUTES=true exposes an UNAUTHENTICATED debug API on the deployed app \u2014\n anyone who can reach /api/debug/* can read and mutate any record. Redeploy to apply;\n `deepspace secrets delete ALLOW_DEBUG_ROUTES` (then redeploy) turns it back off."
9734
+ "\n\u26A0 ALLOW_DEBUG_ROUTES=true enables the owner/admin debug API on the deployed app \u2014\n those users can read and mutate any app record through /api/debug/*. Redeploy to apply;\n `deepspace secrets delete ALLOW_DEBUG_ROUTES` (then redeploy) turns it back off."
9372
9735
  );
9373
9736
  return true;
9374
9737
  }
@@ -9472,7 +9835,7 @@ var get2 = defineCommand10({
9472
9835
  const { secrets } = await listSecrets(DEPLOY_URL11, t.token, t.appId, t.configName);
9473
9836
  const item = secrets.find((s) => s.key === key);
9474
9837
  if (!item)
9475
- fail(new InputError(`Secret "${key}" not found in ${t.configName}`, "secret_not_found"));
9838
+ throw new InputError(`Secret "${key}" not found in ${t.configName}`, "secret_not_found");
9476
9839
  ok(
9477
9840
  args.json === true,
9478
9841
  {
@@ -9544,7 +9907,7 @@ var upload = defineCommand10({
9544
9907
  const content = args.file === "-" ? readFileSync23(0, "utf-8") : readFileSync23(args.file, "utf-8");
9545
9908
  const secrets = parseSecretsUpload(content);
9546
9909
  if (Object.keys(secrets).length === 0)
9547
- fail(new InputError("No secrets found in the input.", "empty_input"));
9910
+ throw new InputError("No secrets found in the input.", "empty_input");
9548
9911
  await uploadSecrets(DEPLOY_URL11, t.token, t.appId, t.configName, secrets, args.replace);
9549
9912
  const uploaded = Object.keys(secrets);
9550
9913
  ok(
@@ -9575,11 +9938,9 @@ var download = defineCommand10({
9575
9938
  const t = await resolveTarget(args);
9576
9939
  const format = args.format;
9577
9940
  if (!["dotenv", "json", "shell"].includes(format)) {
9578
- fail(
9579
- new InputError(
9580
- `Unknown format "${args.format}" \u2014 use dotenv, json, or shell.`,
9581
- "invalid_format"
9582
- )
9941
+ throw new InputError(
9942
+ `Unknown format "${args.format}" \u2014 use dotenv, json, or shell.`,
9943
+ "invalid_format"
9583
9944
  );
9584
9945
  }
9585
9946
  const { secrets } = await fetchSecretsValues(DEPLOY_URL11, t.token, t.appId, t.configName);
@@ -9597,11 +9958,9 @@ var pull = defineCommand10({
9597
9958
  const wranglerEnv = args.env?.trim() || void 0;
9598
9959
  const appDir = findAppDir();
9599
9960
  if (!appDir)
9600
- fail(
9601
- new InputError(
9602
- "Run from a DeepSpace app directory (one containing wrangler.toml).",
9603
- "not_in_app_repo"
9604
- )
9961
+ throw new InputError(
9962
+ "Run from a DeepSpace app directory (one containing wrangler.toml).",
9963
+ "not_in_app_repo"
9605
9964
  );
9606
9965
  const t = await resolveTarget(args);
9607
9966
  const ownerId = decodeJwtPayload(t.token).sub;
@@ -9665,11 +10024,9 @@ var configsCreate = defineCommand10({
9665
10024
  const { configs: configs2 } = await listConfigs(DEPLOY_URL11, t.token, t.appId);
9666
10025
  if (configs2.some((c) => c.name === name)) {
9667
10026
  if (copyFrom) {
9668
- fail(
9669
- new InputError(
9670
- `Config "${name}" already exists \u2014 refusing to copy "${copyFrom}" over it. Delete it first, or pick a new name.`,
9671
- "config_exists"
9672
- )
10027
+ throw new InputError(
10028
+ `Config "${name}" already exists \u2014 refusing to copy "${copyFrom}" over it. Delete it first, or pick a new name.`,
10029
+ "config_exists"
9673
10030
  );
9674
10031
  }
9675
10032
  ok(
@@ -10569,7 +10926,7 @@ var pull_default = defineDeepspaceCommand({
10569
10926
  // src/cli/commands/clone.ts
10570
10927
  import * as p20 from "@clack/prompts";
10571
10928
  import { existsSync as existsSync22, readdirSync as readdirSync6, rmSync as rmSync5 } from "fs";
10572
- import { resolve as resolve17, basename as basename5, join as join21 } from "path";
10929
+ import { resolve as resolve17, basename as basename5, join as join20 } from "path";
10573
10930
  function cleanFailedCloneDir(dir) {
10574
10931
  let entries;
10575
10932
  try {
@@ -10579,7 +10936,7 @@ function cleanFailedCloneDir(dir) {
10579
10936
  }
10580
10937
  if (entries.length === 1 && entries[0] === ".git") {
10581
10938
  try {
10582
- rmSync5(join21(dir, ".git"), { recursive: true, force: true });
10939
+ rmSync5(join20(dir, ".git"), { recursive: true, force: true });
10583
10940
  return [];
10584
10941
  } catch {
10585
10942
  return entries;
@@ -11064,8 +11421,16 @@ function printOverlaps(overlaps, json) {
11064
11421
 
11065
11422
  // src/cli/commands/workspace/local.ts
11066
11423
  import * as p24 from "@clack/prompts";
11067
- import { mkdirSync as mkdirSync7, readFileSync as readFileSync25, realpathSync as realpathSync6, renameSync as renameSync2, statSync as statSync3, writeFileSync as writeFileSync9 } from "fs";
11068
- import { dirname as dirname10, isAbsolute as isAbsolute5, join as join22, relative as relative2, sep as sep2 } from "path";
11424
+ import {
11425
+ existsSync as existsSync23,
11426
+ mkdirSync as mkdirSync7,
11427
+ readFileSync as readFileSync25,
11428
+ realpathSync as realpathSync6,
11429
+ renameSync as renameSync2,
11430
+ statSync as statSync3,
11431
+ writeFileSync as writeFileSync7
11432
+ } from "fs";
11433
+ import { dirname as dirname11, isAbsolute as isAbsolute5, join as join21, relative as relative2, sep as sep2 } from "path";
11069
11434
  var WORKSPACE_MARKER = "deepspace-workspace.json";
11070
11435
  function excludeWorktreeDir(appDir, dir) {
11071
11436
  try {
@@ -11086,7 +11451,7 @@ function worktreeMarkerPath(worktreeRoot) {
11086
11451
  const result = runGit(worktreeRoot, ["rev-parse", "--absolute-git-dir"], { allowFail: true });
11087
11452
  if (result.status !== 0) return null;
11088
11453
  const gitDir = result.stdout.toString("utf-8").trim();
11089
- return isAbsolute5(gitDir) ? join22(gitDir, WORKSPACE_MARKER) : null;
11454
+ return isAbsolute5(gitDir) ? join21(gitDir, WORKSPACE_MARKER) : null;
11090
11455
  } catch {
11091
11456
  return null;
11092
11457
  }
@@ -11097,8 +11462,8 @@ function markManagedWorkspace(worktreeRoot, workspaceId) {
11097
11462
  const marker = { version: 1, workspaceId, mode: "managed-linked" };
11098
11463
  const tempPath = `${markerPath}.${process.pid}.tmp`;
11099
11464
  try {
11100
- mkdirSync7(dirname10(markerPath), { recursive: true });
11101
- writeFileSync9(tempPath, JSON.stringify(marker) + "\n");
11465
+ mkdirSync7(dirname11(markerPath), { recursive: true });
11466
+ writeFileSync7(tempPath, JSON.stringify(marker) + "\n");
11102
11467
  renameSync2(tempPath, markerPath);
11103
11468
  return true;
11104
11469
  } catch {
@@ -11131,11 +11496,16 @@ function isManagedWorkspaceWorktree(worktreeRoot, workspaceId) {
11131
11496
  function defaultWorkspaceRoot(appDir, workspaceId) {
11132
11497
  const primary = listWorktrees(appDir)[0];
11133
11498
  if (!primary) throw new Error("Git did not report a primary worktree");
11134
- return join22(primary.path, ".deepspace", "ws", workspaceId.slice(3).toLowerCase());
11499
+ return join21(primary.path, ".deepspace", "ws", workspaceId.slice(3).toLowerCase());
11135
11500
  }
11136
11501
  function appDirInWorktree(appDir, worktreeRoot) {
11137
11502
  const appRelative = relative2(realpathSync6(repoToplevel(appDir)), realpathSync6(appDir));
11138
- return appRelative ? join22(worktreeRoot, appRelative) : worktreeRoot;
11503
+ return appRelative ? join21(worktreeRoot, appRelative) : worktreeRoot;
11504
+ }
11505
+ function primaryAppDir(appDir) {
11506
+ const primary = listWorktrees(appDir)[0];
11507
+ if (!primary) throw new Error("Git did not report a primary worktree");
11508
+ return appDirInWorktree(appDir, primary.path);
11139
11509
  }
11140
11510
  function materializeWorkspaceWorktree(appDir, worktreeRoot, branch, startPoint, workspaceId, options = {}) {
11141
11511
  const createdOid = resolveCommit(appDir, startPoint);
@@ -11165,7 +11535,7 @@ function rematerializeWorkspaceWorktree(appDir, worktreeRoot, branch, workspaceI
11165
11535
  function inOwnLinkedWorktree(appDir, id) {
11166
11536
  if (workspaceIdFromBranch(currentBranch(appDir)) !== id) return false;
11167
11537
  try {
11168
- return statSync3(join22(repoToplevel(appDir), ".git")).isFile();
11538
+ return statSync3(join21(repoToplevel(appDir), ".git")).isFile();
11169
11539
  } catch {
11170
11540
  return false;
11171
11541
  }
@@ -11206,6 +11576,7 @@ function cleanupJson(outcome) {
11206
11576
  worktreeRetained: outcome.worktreeRetained,
11207
11577
  branchDeleted: outcome.branchDeleted,
11208
11578
  ...outcome.mainDir ? { mainDir: outcome.mainDir } : {},
11579
+ ...outcome.leftoverDir ? { leftoverDir: outcome.leftoverDir } : {},
11209
11580
  ...outcome.error ? { error: outcome.error } : {}
11210
11581
  };
11211
11582
  }
@@ -11280,7 +11651,23 @@ function cleanupWorkspaceLocal(appDir, id, trunkBranch, options = {}) {
11280
11651
  }
11281
11652
  const remove2 = runGit(mainDir, ["worktree", "remove", linked.path], { allowFail: true });
11282
11653
  if (remove2.status !== 0) {
11283
- out.error = `git worktree remove ${linked.path} failed: ${remove2.stderr.toString("utf-8").trim() || "unknown git error"}`;
11654
+ const reason = remove2.stderr.toString("utf-8").trim() || "unknown git error";
11655
+ const registered = listWorktrees(mainDir);
11656
+ const stillRegistered = registered.length === 0 || registered.some((worktree, index) => index > 0 && worktree.path === linked.path);
11657
+ if (stillRegistered) {
11658
+ out.error = `git worktree remove ${linked.path} failed: ${reason}`;
11659
+ return out;
11660
+ }
11661
+ runGit(mainDir, ["worktree", "prune"], { allowFail: true });
11662
+ out.worktreeDir = null;
11663
+ const leftover = existsSync23(linked.path);
11664
+ if (leftover) out.leftoverDir = linked.path;
11665
+ else out.worktreeRemoved = linked.path;
11666
+ deleteWsBranch(mainDir, branch, expectedBranchOid, out);
11667
+ if (leftover) {
11668
+ const branchProblem = out.error;
11669
+ out.error = `git removed the worktree but could not delete its files (${reason})` + (branchProblem ? `. Also: ${branchProblem}` : "");
11670
+ }
11284
11671
  return out;
11285
11672
  }
11286
11673
  out.worktreeRemoved = linked.path;
@@ -11302,12 +11689,20 @@ function cleanupWorkspaceLocal(appDir, id, trunkBranch, options = {}) {
11302
11689
  function cleanupRefusalMessage(outcome) {
11303
11690
  if (outcome.worktreeDir) {
11304
11691
  const dir = shQuote(outcome.worktreeDir);
11305
- return `Cleanup incomplete: ${outcome.error}. The worktree at ${dir} still holds uncommitted or untracked work \u2014 that's why the plain removal refused. Inspect it (\`git -C ${dir} status\`) and commit anything worth keeping to the workspace branch. Once it's clean, remove it from the main checkout: \`git worktree remove ${dir}\`.`;
11692
+ if (outcome.error.includes("contains modified or untracked files")) {
11693
+ return `Cleanup incomplete: ${outcome.error}. The worktree at ${dir} still holds uncommitted or untracked work \u2014 that's why the plain removal refused. Inspect it (\`git -C ${dir} status\`) and commit anything worth keeping to the workspace branch. Once it's clean, remove it from the main checkout: \`git worktree remove ${dir}\`.`;
11694
+ }
11695
+ return `Cleanup incomplete: ${outcome.error}. The worktree at ${dir} is still registered; git refused the plain removal for the reason above. Inspect it (\`git -C ${dir} status\`), resolve that, then remove it from the main checkout: \`git worktree remove ${dir}\`.`;
11696
+ }
11697
+ if (outcome.leftoverDir) {
11698
+ return `Cleanup incomplete: ${outcome.error}. Close anything using that folder \u2014 a dev server, an editor, or a shell inside it \u2014 then delete: ${shQuote(outcome.leftoverDir)}`;
11306
11699
  }
11307
11700
  return `Cleanup incomplete: ${outcome.error}. Finish it from the main checkout once ${shQuote(outcome.branch)} is safe to delete.`;
11308
11701
  }
11309
11702
  function cleanupAction(outcome) {
11310
- return outcome.worktreeDir ? { cwd: outcome.worktreeDir, argv: ["git", "status"] } : { cwd: process.cwd(), argv: ["git", "branch", "--list", outcome.branch] };
11703
+ if (outcome.worktreeDir) return { cwd: outcome.worktreeDir, argv: ["git", "status"] };
11704
+ if (outcome.leftoverDir) return { cwd: process.cwd(), argv: ["git", "worktree", "list"] };
11705
+ return { cwd: process.cwd(), argv: ["git", "branch", "--list", outcome.branch] };
11311
11706
  }
11312
11707
  function reportCleanupHuman(outcome) {
11313
11708
  if (outcome.error) {
@@ -11315,6 +11710,7 @@ function reportCleanupHuman(outcome) {
11315
11710
  cleanupRefusalMessage({
11316
11711
  error: outcome.error,
11317
11712
  worktreeDir: outcome.worktreeDir,
11713
+ leftoverDir: outcome.leftoverDir,
11318
11714
  branch: outcome.branch
11319
11715
  })
11320
11716
  );
@@ -11562,7 +11958,7 @@ var newWorkspaceCommand = defineDeepspaceCommand({
11562
11958
 
11563
11959
  // src/cli/commands/workspace/attach.ts
11564
11960
  import * as p26 from "@clack/prompts";
11565
- import { existsSync as existsSync23, readdirSync as readdirSync7, realpathSync as realpathSync7, rmSync as rmSync6 } from "fs";
11961
+ import { existsSync as existsSync24, readdirSync as readdirSync7, realpathSync as realpathSync7, rmSync as rmSync6 } from "fs";
11566
11962
  import { isAbsolute as isAbsolute7, resolve as resolve19 } from "path";
11567
11963
  function samePath(a, b) {
11568
11964
  const real = (p35) => {
@@ -11582,7 +11978,7 @@ function finishedWorkspaceMessage(id, view) {
11582
11978
  return `Workspace ${id} is ${workspace.status} \u2014 its ref is retained briefly (fetch ${workspace.ref} to inspect), but attach only works on active workspaces.`;
11583
11979
  }
11584
11980
  function cleanFailedFreshAttachDir(dir) {
11585
- if (!existsSync23(dir)) return [];
11981
+ if (!existsSync24(dir)) return [];
11586
11982
  let entries;
11587
11983
  try {
11588
11984
  entries = readdirSync7(dir);
@@ -11664,7 +12060,7 @@ var attachWorkspaceCommand = defineDeepspaceCommand({
11664
12060
  const localTip = resolveCommit(appDir, `refs/heads/${branch}`);
11665
12061
  if (localTip) {
11666
12062
  let existing = resolveWorkspaceWorktree(listWorktrees(appDir), id);
11667
- if (existing && !existsSync23(existing)) {
12063
+ if (existing && !existsSync24(existing)) {
11668
12064
  runGit(appDir, ["worktree", "prune"], { allowFail: true });
11669
12065
  existing = null;
11670
12066
  }
@@ -11782,7 +12178,7 @@ var attachWorkspaceCommand = defineDeepspaceCommand({
11782
12178
  process.cwd(),
11783
12179
  (typeof args.dir === "string" ? args.dir.trim() : "") || `ws-${id.slice(3).toLowerCase()}`
11784
12180
  );
11785
- if (existsSync23(dir)) {
12181
+ if (existsSync24(dir)) {
11786
12182
  spinner2?.stop("Target exists.");
11787
12183
  throw new Refusal(`${dir} already exists \u2014 pass a different directory.`, "dir_exists");
11788
12184
  }
@@ -12120,12 +12516,12 @@ import * as p30 from "@clack/prompts";
12120
12516
  // src/cli/lib/validation.ts
12121
12517
  import { sync as spawnSync7 } from "cross-spawn";
12122
12518
  import { readFileSync as readFileSync26 } from "fs";
12123
- import { join as join23 } from "path";
12519
+ import { join as join22 } from "path";
12124
12520
  var MAX_SUMMARY_CHARS = 4e3;
12125
12521
  var MAX_OUTPUT_BUFFER = 64 * 1024 * 1024;
12126
12522
  function configuredCommand(appDir) {
12127
12523
  try {
12128
- const pkg2 = JSON.parse(readFileSync26(join23(appDir, "package.json"), "utf-8"));
12524
+ const pkg2 = JSON.parse(readFileSync26(join22(appDir, "package.json"), "utf-8"));
12129
12525
  if (pkg2.scripts?.validate) return `${detectPackageManager(appDir)} run --silent validate`;
12130
12526
  } catch {
12131
12527
  }
@@ -12163,6 +12559,11 @@ function conflictMarkerFiles(diffCheckOutput) {
12163
12559
  }
12164
12560
  return [...files];
12165
12561
  }
12562
+ function pullAfterLandAction(primaryDir, intoBranch, landedOid) {
12563
+ if (currentBranch(primaryDir) !== intoBranch) return null;
12564
+ if (resolveCommit(primaryDir, "HEAD") === landedOid) return null;
12565
+ return { cwd: primaryDir, argv: ["deepspace", "pull"] };
12566
+ }
12166
12567
  function landResumeArgv(args) {
12167
12568
  const argv = ["deepspace", "workspace", "land"];
12168
12569
  const workspace = args.workspace?.trim();
@@ -12441,6 +12842,7 @@ var landWorkspaceCommand = defineDeepspaceCommand({
12441
12842
  );
12442
12843
  }
12443
12844
  spinner2?.stop(`Landed ${id} into ${intoBranch} at ${landedOid.slice(0, 10)}.`);
12845
+ const primaryDir = primaryAppDir(appDir);
12444
12846
  const cleanup = keepWorktree ? null : cleanupWorkspaceLocal(appDir, id, intoBranch, { expectedBranchOid: landedOid });
12445
12847
  const inOwnWorktree = !cleanup && inOwnLinkedWorktree(appDir, id);
12446
12848
  const retainedWorktree = cleanup?.worktreeRetained ?? (keepWorktree || inOwnWorktree ? appDir : null);
@@ -12458,21 +12860,24 @@ var landWorkspaceCommand = defineDeepspaceCommand({
12458
12860
  p30.log.info(`Retained checkout: ${retainedWorktree}`);
12459
12861
  }
12460
12862
  if (cleanup?.error) {
12461
- const action = cleanupAction({
12863
+ const action2 = cleanupAction({
12462
12864
  worktreeDir: cleanup.worktreeDir,
12865
+ leftoverDir: cleanup.leftoverDir,
12463
12866
  branch: cleanup.branch
12464
12867
  });
12465
12868
  throw new Refusal(
12466
12869
  cleanupRefusalMessage({
12467
12870
  error: cleanup.error,
12468
12871
  worktreeDir: cleanup.worktreeDir,
12872
+ leftoverDir: cleanup.leftoverDir,
12469
12873
  branch: cleanup.branch
12470
12874
  }),
12471
12875
  "cleanup_incomplete",
12472
- { actionRequired: true, action, extra: data }
12876
+ { actionRequired: true, action: action2, extra: data }
12473
12877
  );
12474
12878
  }
12475
- return { data };
12879
+ const action = pullAfterLandAction(primaryDir, intoBranch, landedOid);
12880
+ return { data, ...action ? { action } : {} };
12476
12881
  }
12477
12882
  });
12478
12883
 
@@ -12630,12 +13035,14 @@ var dropWorkspaceCommand = defineDeepspaceCommand({
12630
13035
  if (cleanup?.error) {
12631
13036
  const action = cleanupAction({
12632
13037
  worktreeDir: cleanup.worktreeDir,
13038
+ leftoverDir: cleanup.leftoverDir,
12633
13039
  branch: cleanup.branch
12634
13040
  });
12635
13041
  throw new Refusal(
12636
13042
  cleanupRefusalMessage({
12637
13043
  error: cleanup.error,
12638
13044
  worktreeDir: cleanup.worktreeDir,
13045
+ leftoverDir: cleanup.leftoverDir,
12639
13046
  branch: cleanup.branch
12640
13047
  }),
12641
13048
  "cleanup_incomplete",
@@ -12912,7 +13319,7 @@ var source_default = defineDeepspaceCommand({
12912
13319
  args: {
12913
13320
  provider: {
12914
13321
  type: "positional",
12915
- description: "Source provider: github or deepspace (omit to show)",
13322
+ description: "Source authority: deepspace is platform-hosted and commit-first; github deploys the local working tree without Git sync (omit to show)",
12916
13323
  required: false
12917
13324
  },
12918
13325
  app: {
@@ -13190,10 +13597,165 @@ function sameRefs(left, right) {
13190
13597
 
13191
13598
  // src/cli/commands/update.ts
13192
13599
  import * as p34 from "@clack/prompts";
13193
- import { existsSync as existsSync24, readFileSync as readFileSync27, writeFileSync as writeFileSync10 } from "fs";
13194
- import { join as join24, resolve as resolve20 } from "path";
13600
+ import { existsSync as existsSync25, readFileSync as readFileSync27, writeFileSync as writeFileSync8 } from "fs";
13601
+ import { join as join23, resolve as resolve20 } from "path";
13602
+
13603
+ // package.json
13604
+ var package_default = {
13605
+ name: "deepspace",
13606
+ version: "0.19.1",
13607
+ engines: {
13608
+ node: ">=22.15.0 <23 || >=24 <25 || >=26 <27"
13609
+ },
13610
+ description: "DeepSpace SDK \u2014 build real-time collaborative apps on Cloudflare Workers",
13611
+ type: "module",
13612
+ sideEffects: false,
13613
+ main: "./dist/index.js",
13614
+ types: "./dist/index.d.ts",
13615
+ bin: {
13616
+ deepspace: "dist/cli.js"
13617
+ },
13618
+ exports: {
13619
+ ".": {
13620
+ types: "./dist/index.d.ts",
13621
+ import: "./dist/index.js"
13622
+ },
13623
+ "./schema": {
13624
+ types: "./dist/schema.d.ts",
13625
+ import: "./dist/schema.js"
13626
+ },
13627
+ "./worker": {
13628
+ types: "./dist/worker.d.ts",
13629
+ import: "./dist/worker.js"
13630
+ },
13631
+ "./server": {
13632
+ types: "./dist/server.d.ts",
13633
+ import: "./dist/server.js"
13634
+ },
13635
+ "./testing": {
13636
+ types: "./dist/testing.d.ts",
13637
+ import: "./dist/testing.js"
13638
+ },
13639
+ "./documentation": {
13640
+ types: "./dist/documentation.d.ts",
13641
+ import: "./dist/documentation.js"
13642
+ },
13643
+ "./documentation/react": {
13644
+ types: "./dist/documentation-react.d.ts",
13645
+ import: "./dist/documentation-react.js"
13646
+ }
13647
+ },
13648
+ files: [
13649
+ "dist",
13650
+ "features",
13651
+ "CHANGELOG.md"
13652
+ ],
13653
+ scripts: {
13654
+ build: "node ../../scripts/clean-artifacts.mjs dist --tsbuildinfo && tsup",
13655
+ "type-check": "tsc --noEmit",
13656
+ test: "vitest run",
13657
+ "test:documentation-browser": "playwright test --config src/documentation/runtime/__browser__/playwright.config.ts",
13658
+ clean: "node ../../scripts/clean-artifacts.mjs dist --tsbuildinfo",
13659
+ prepublishOnly: "pnpm build"
13660
+ },
13661
+ dependencies: {
13662
+ "@ai-sdk/anthropic": "2.0.91",
13663
+ "@ai-sdk/openai": "2.0.116",
13664
+ "@ai-sdk/openai-compatible": "1.0.46",
13665
+ "@clack/prompts": "^1.2.0",
13666
+ "@mdx-js/mdx": "^3.1.1",
13667
+ acorn: "^8.16.0",
13668
+ ai: "5.0.222",
13669
+ "better-auth": "1.6.23",
13670
+ citty: "^0.2.0",
13671
+ "cross-spawn": "^7.0.6",
13672
+ esbuild: "^0.27.0",
13673
+ "highlight.js": "^11.11.1",
13674
+ hono: "^4.6.0",
13675
+ jose: "^6.1.0",
13676
+ marked: "^18.0.7",
13677
+ "mdast-util-gfm": "^3.1.0",
13678
+ "mdast-util-to-markdown": "^2.1.2",
13679
+ "rehype-highlight": "^7.0.2",
13680
+ "remark-frontmatter": "^5.0.0",
13681
+ "remark-gfm": "^4.0.1",
13682
+ "sanitize-html": "^2.17.6",
13683
+ "smol-toml": "^1.6.1",
13684
+ yaml: "^2.9.0",
13685
+ yjs: "^13.6.0",
13686
+ zod: "^4.3.6"
13687
+ },
13688
+ peerDependencies: {
13689
+ "@playwright/test": ">=1.40.0",
13690
+ clsx: ">=2.0.0",
13691
+ "lucide-react": ">=0.400.0",
13692
+ react: "^18.0.0 || ^19.0.0",
13693
+ "react-dom": "^18.0.0 || ^19.0.0",
13694
+ "tailwind-merge": ">=2.0.0"
13695
+ },
13696
+ peerDependenciesMeta: {
13697
+ "@playwright/test": {
13698
+ optional: true
13699
+ },
13700
+ react: {
13701
+ optional: true
13702
+ },
13703
+ "react-dom": {
13704
+ optional: true
13705
+ },
13706
+ clsx: {
13707
+ optional: true
13708
+ },
13709
+ "tailwind-merge": {
13710
+ optional: true
13711
+ },
13712
+ "lucide-react": {
13713
+ optional: true
13714
+ }
13715
+ },
13716
+ devDependencies: {
13717
+ "@cloudflare/workers-types": "^4.20241205.0",
13718
+ "@playwright/test": "^1.59.1",
13719
+ "@types/better-sqlite3": "^7.6.12",
13720
+ "@types/cross-spawn": "^6.0.6",
13721
+ "@types/react": "^19.0.0",
13722
+ "@types/react-dom": "^19.0.0",
13723
+ "@types/sanitize-html": "^2.16.1",
13724
+ "better-sqlite3": "^12.0.0",
13725
+ clsx: "^2.1.1",
13726
+ "framer-motion": "^12.38.0",
13727
+ jsdom: "^29.1.1",
13728
+ "lucide-react": "^1.7.0",
13729
+ "react-router-dom": "^7.1.0",
13730
+ "tailwind-merge": "^3.5.0",
13731
+ tsup: "^8.3.0",
13732
+ typescript: "^5.7.0",
13733
+ vitest: "^4.1.0"
13734
+ },
13735
+ keywords: [
13736
+ "deepspace",
13737
+ "cloudflare-workers",
13738
+ "durable-objects",
13739
+ "real-time",
13740
+ "collaborative",
13741
+ "sdk"
13742
+ ],
13743
+ license: "Apache-2.0",
13744
+ homepage: "https://deep.space",
13745
+ repository: {
13746
+ type: "git",
13747
+ url: "git+https://github.com/deepdotspace/deepspace.git",
13748
+ directory: "packages/deepspace"
13749
+ },
13750
+ bugs: {
13751
+ url: "https://github.com/deepdotspace/deepspace/issues"
13752
+ }
13753
+ };
13754
+
13755
+ // src/cli/commands/update.ts
13195
13756
  var REGISTRY = "https://registry.npmjs.org/deepspace/latest";
13196
13757
  var DOCS_URL = "https://documentation.deep.space/cli-reference/commands";
13758
+ var COMPATIBLE_AI_VERSION = package_default.dependencies.ai;
13197
13759
  var OLDEST_SUPPORTED_MINOR = 12;
13198
13760
  function parseVersion(raw) {
13199
13761
  const match = /^(\d+)\.(\d+)\.(\d+)/.exec(raw.trim().replace(/^[\^~]/, ""));
@@ -13205,25 +13767,29 @@ function gapIsTooWide(from, to) {
13205
13767
  return from.major === 0 && from.minor < OLDEST_SUPPORTED_MINOR;
13206
13768
  }
13207
13769
  function readAppSdkVersion(appDir) {
13208
- const installed = join24(appDir, "node_modules", "deepspace", "package.json");
13209
- if (existsSync24(installed)) {
13770
+ const installed = join23(appDir, "node_modules", "deepspace", "package.json");
13771
+ if (existsSync25(installed)) {
13210
13772
  const parsed2 = JSON.parse(readFileSync27(installed, "utf8"));
13211
13773
  if (typeof parsed2.version === "string") return parsed2.version;
13212
13774
  }
13213
- const manifest = join24(appDir, "package.json");
13214
- if (!existsSync24(manifest)) return null;
13775
+ const manifest = join23(appDir, "package.json");
13776
+ if (!existsSync25(manifest)) return null;
13215
13777
  const parsed = JSON.parse(readFileSync27(manifest, "utf8"));
13216
13778
  return parsed.dependencies?.deepspace ?? null;
13217
13779
  }
13218
13780
  function pinSdkVersion(appDir, version) {
13219
- const path = join24(appDir, "package.json");
13781
+ const path = join23(appDir, "package.json");
13220
13782
  const source = readFileSync27(path, "utf8");
13221
- const updated = source.replace(
13783
+ let updated = source.replace(
13222
13784
  /("deepspace"\s*:\s*")[^"]+(")/,
13223
13785
  (_match, head, tail) => `${head}^${version}${tail}`
13224
13786
  );
13787
+ updated = updated.replace(
13788
+ /("ai"\s*:\s*")[^"]+(")/,
13789
+ (_match, head, tail) => `${head}${COMPATIBLE_AI_VERSION}${tail}`
13790
+ );
13225
13791
  if (updated === source) return false;
13226
- writeFileSync10(path, updated);
13792
+ writeFileSync8(path, updated);
13227
13793
  return true;
13228
13794
  }
13229
13795
  async function latestPublishedVersion() {
@@ -13362,9 +13928,9 @@ var update_default = defineDeepspaceCommand({
13362
13928
  });
13363
13929
 
13364
13930
  // src/cli/cli.ts
13365
- var __dirname = dirname11(fileURLToPath3(import.meta.url));
13366
- var pkg = JSON.parse(readFileSync28(join25(__dirname, "..", "package.json"), "utf-8"));
13367
- if (process.platform === "linux" && !process.env.SSL_CERT_FILE && existsSync25("/etc/ssl/certs/ca-certificates.crt")) {
13931
+ var __dirname = dirname12(fileURLToPath3(import.meta.url));
13932
+ var pkg = JSON.parse(readFileSync28(join24(__dirname, "..", "package.json"), "utf-8"));
13933
+ if (process.platform === "linux" && !process.env.SSL_CERT_FILE && existsSync26("/etc/ssl/certs/ca-certificates.crt")) {
13368
13934
  process.env.SSL_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt";
13369
13935
  }
13370
13936
  var create2 = defineCommand14({