deepspace 0.19.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 [
@@ -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
@@ -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) {
@@ -3115,8 +3139,8 @@ function validTestAccounts(value) {
3115
3139
  );
3116
3140
  }
3117
3141
  function saveTestAccountStore(store) {
3118
- mkdirSync5(join13(homedir3(), ".deepspace"), { recursive: true });
3119
- 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 });
3120
3144
  }
3121
3145
 
3122
3146
  // src/cli/lib/test-account-service.ts
@@ -3616,7 +3640,7 @@ var clear = defineDeepspaceCommand({
3616
3640
  return { data: { deleted: 0, requested: 0, failures: [] } };
3617
3641
  }
3618
3642
  if (!args.yes) {
3619
- if (args.json) {
3643
+ if (!process.stdin.isTTY || args.json) {
3620
3644
  throw new Refusal(
3621
3645
  "Deleting test accounts needs confirmation. Pass --yes to confirm non-interactively.",
3622
3646
  "confirmation_required"
@@ -3845,15 +3869,15 @@ function describeFilesFailure(status3, body) {
3845
3869
  import * as p6 from "@clack/prompts";
3846
3870
  import { execSync as execSync2 } from "child_process";
3847
3871
  import { createHash as createHash2 } from "crypto";
3848
- import { chmodSync as chmodSync2, existsSync as existsSync14, lstatSync as lstatSync2, readFileSync as readFileSync17, readdirSync as readdirSync4 } from "fs";
3849
- import { dirname as dirname6, join as join16, resolve as resolve11 } from "path";
3872
+ import { existsSync as existsSync14, readFileSync as readFileSync17, readdirSync as readdirSync4 } from "fs";
3873
+ import { dirname as dirname7, join as join15, resolve as resolve11 } from "path";
3850
3874
 
3851
3875
  // src/documentation/deploy.ts
3852
3876
  import { existsSync as existsSync12, readFileSync as readFileSync15 } from "fs";
3853
- import { join as join14 } from "path";
3877
+ import { join as join13 } from "path";
3854
3878
  function readDocumentationDeployManifest(appDir, clientDir) {
3855
- if (!existsSync12(join14(appDir, "documentation.json"))) return void 0;
3856
- 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");
3857
3881
  if (!existsSync12(manifestPath)) {
3858
3882
  throw new Error(
3859
3883
  "documentation.json exists but the build produced no documentation manifest. Ensure vite.config.ts includes deepSpaceDocumentation() from deepspace/documentation."
@@ -3874,7 +3898,7 @@ import {
3874
3898
  readFileSync as readFileSync16,
3875
3899
  writeSync
3876
3900
  } from "fs";
3877
- import { join as join15 } from "path";
3901
+ import { join as join14 } from "path";
3878
3902
 
3879
3903
  // src/shared/protocol/app-migrations.ts
3880
3904
  var APP_MIGRATION_ID_RE = /^[a-z0-9][a-z0-9._-]{0,127}$/;
@@ -3926,7 +3950,7 @@ var APP_MIGRATION_DEFINITIONS = SOURCE_MIGRATIONS.map(
3926
3950
  ({ id, description }) => ({ id, description })
3927
3951
  );
3928
3952
  function readAppliedAppMigrations(appDir) {
3929
- const path = join15(appDir, APP_MIGRATIONS_MANIFEST);
3953
+ const path = join14(appDir, APP_MIGRATIONS_MANIFEST);
3930
3954
  if (!existsSync13(path)) return [];
3931
3955
  const source = readRegularFile(path);
3932
3956
  let parsed;
@@ -3953,7 +3977,7 @@ function planAppMigrations(appDir) {
3953
3977
  const edits = [];
3954
3978
  for (const repoPath of tracked) {
3955
3979
  if (!isSourcePath(repoPath, appPrefix)) continue;
3956
- const absolutePath = join15(repo, repoPath);
3980
+ const absolutePath = join14(repo, repoPath);
3957
3981
  let stat;
3958
3982
  try {
3959
3983
  stat = lstatSync(absolutePath);
@@ -3961,7 +3985,8 @@ function planAppMigrations(appDir) {
3961
3985
  continue;
3962
3986
  }
3963
3987
  if (!stat.isFile() || stat.isSymbolicLink()) continue;
3964
- const before = readRegularFile(absolutePath);
3988
+ const checkout = readRegularFile(absolutePath);
3989
+ const before = normalizeEol(checkout);
3965
3990
  let after = before;
3966
3991
  for (const migration of pendingMigrations) {
3967
3992
  const result = migration.transform(after, repoPath);
@@ -3979,7 +4004,9 @@ function planAppMigrations(appDir) {
3979
4004
  }
3980
4005
  blockers.push(...migration.findBlockers(after, repoPath));
3981
4006
  }
3982
- 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
+ }
3983
4010
  }
3984
4011
  const pending = pendingMigrations.map((migration) => {
3985
4012
  const planned = perMigration.get(migration.id);
@@ -3987,7 +4014,7 @@ function planAppMigrations(appDir) {
3987
4014
  });
3988
4015
  if (pending.length > 0 && blockers.length === 0) {
3989
4016
  const manifestPath = `${appPrefix}${APP_MIGRATIONS_MANIFEST}`;
3990
- const absoluteManifestPath = join15(repo, manifestPath);
4017
+ const absoluteManifestPath = join14(repo, manifestPath);
3991
4018
  const before = existsSync13(absoluteManifestPath) ? readRegularFile(absoluteManifestPath) : null;
3992
4019
  const after = `${JSON.stringify([...appliedIds, ...pending.map(({ id }) => id)], null, 2)}
3993
4020
  `;
@@ -4003,13 +4030,19 @@ function planAppMigrations(appDir) {
4003
4030
  function applyAppMigrationPlan(appDir, plan) {
4004
4031
  const repo = repoToplevel(appDir);
4005
4032
  for (const edit of plan.edits) {
4006
- const absolutePath = join15(repo, edit.path);
4033
+ const absolutePath = join14(repo, edit.path);
4007
4034
  const current = existsSync13(absolutePath) ? readRegularFile(absolutePath) : null;
4008
4035
  if (current !== edit.before) {
4009
4036
  throw new Error(`Migration input changed after planning: ${edit.path}`);
4010
4037
  }
4011
4038
  }
4012
- 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;
4013
4046
  }
4014
4047
  function readRegularFile(path) {
4015
4048
  const before = lstatSync(path);
@@ -4161,6 +4194,7 @@ ${SECURE_JOB_ROOM_CONSTRUCTOR}`
4161
4194
  ]
4162
4195
  ];
4163
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/;
4164
4198
  var HTTP_ROLE_IMPORT_SEAM = `import type { AppContext, Env } from '../../worker.js'`;
4165
4199
  var DEBUG_ROUTE_SEAM = ` app.all('/api/debug/*', async (c) => {
4166
4200
  if (c.env.ALLOW_DEBUG_ROUTES !== 'true') {
@@ -4199,8 +4233,11 @@ function secureDebugRouteOccurrences(source) {
4199
4233
  0
4200
4234
  );
4201
4235
  }
4236
+ function httpRoleImportOccurrences(source) {
4237
+ return source.match(new RegExp(HTTP_ROLE_IMPORT_PATTERN.source, "g"))?.length ?? 0;
4238
+ }
4202
4239
  function hasSecureDebugRoute(source) {
4203
- return debugRouteOccurrences(source) === 1 && secureDebugRouteOccurrences(source) === 1 && occurrences(source, HTTP_ROLE_IMPORT) === 1;
4240
+ return debugRouteOccurrences(source) === 1 && secureDebugRouteOccurrences(source) === 1 && httpRoleImportOccurrences(source) === 1;
4204
4241
  }
4205
4242
  function migrateSecureRoomBoundaries(source, file) {
4206
4243
  if (file.endsWith("src/server/realtime-routes.ts")) {
@@ -4243,7 +4280,7 @@ function migrateDebugRouteAuthorization(source) {
4243
4280
  const matchingRoutes = DEBUG_ROUTE_REPLACEMENTS.filter(
4244
4281
  ([before2]) => occurrences(source, before2) === 1
4245
4282
  );
4246
- if (debugRouteOccurrences(source) !== 1 || matchingRoutes.length !== 1 || occurrences(source, HTTP_ROLE_IMPORT) !== 0 || occurrences(source, HTTP_ROLE_IMPORT_SEAM) !== 1) {
4283
+ if (debugRouteOccurrences(source) !== 1 || matchingRoutes.length !== 1 || httpRoleImportOccurrences(source) !== 0 || occurrences(source, HTTP_ROLE_IMPORT_SEAM) !== 1) {
4247
4284
  return { source, replacements: 0 };
4248
4285
  }
4249
4286
  const [before, after] = matchingRoutes[0];
@@ -4357,7 +4394,9 @@ function findWorkerOwnedNotFoundBlockers(source, file) {
4357
4394
  const blockers = [];
4358
4395
  const lines = source.split("\n");
4359
4396
  for (const [index, line] of lines.entries()) {
4360
- if (!/['"`]\/index\.html/.test(line)) continue;
4397
+ if (!/(?:\burl\.pathname\s*=\s*|\bnew\s+URL\s*\(\s*)['"`]\/index\.html/.test(line)) {
4398
+ continue;
4399
+ }
4361
4400
  blockers.push({
4362
4401
  migrationId: WORKER_OWNED_NOT_FOUND_MIGRATION_ID,
4363
4402
  file,
@@ -4370,16 +4409,6 @@ function findWorkerOwnedNotFoundBlockers(source, file) {
4370
4409
 
4371
4410
  // src/cli/commands/deploy/build.ts
4372
4411
  var RESERVED_RUN_WORKER_FIRST2 = new Set(RESERVED_RUN_WORKER_FIRST);
4373
- function secureBuildDevVars(workerDir) {
4374
- const path = join16(workerDir, ".dev.vars");
4375
- if (!existsSync14(path)) return false;
4376
- const stat = lstatSync2(path);
4377
- if (!stat.isFile() || stat.isSymbolicLink()) {
4378
- throw new Error(`Refusing unsafe build secret path: ${path}`);
4379
- }
4380
- chmodSync2(path, 384);
4381
- return true;
4382
- }
4383
4412
  function declaredCompatibilityDate(config) {
4384
4413
  const declared = config.compatibility_date;
4385
4414
  return typeof declared === "string" && /^\d{4}-\d{2}-\d{2}$/.test(declared) ? declared : null;
@@ -4415,7 +4444,7 @@ async function buildDeployBundle(options) {
4415
4444
  preparedWranglerConfig?.cleanup();
4416
4445
  }
4417
4446
  spinner2.stop("Built");
4418
- const deployConfigPath = join16(appDir, ".wrangler", "deploy", "config.json");
4447
+ const deployConfigPath = join15(appDir, ".wrangler", "deploy", "config.json");
4419
4448
  if (!existsSync14(deployConfigPath)) {
4420
4449
  output.die(
4421
4450
  "Build output config not found at .wrangler/deploy/config.json",
@@ -4423,7 +4452,7 @@ async function buildDeployBundle(options) {
4423
4452
  );
4424
4453
  }
4425
4454
  const deployConfig = JSON.parse(readFileSync17(deployConfigPath, "utf-8"));
4426
- const outputWranglerPath = resolve11(dirname6(deployConfigPath), deployConfig.configPath);
4455
+ const outputWranglerPath = resolve11(dirname7(deployConfigPath), deployConfig.configPath);
4427
4456
  if (!existsSync14(outputWranglerPath)) {
4428
4457
  output.die(`Output wrangler.json not found at ${outputWranglerPath}`, "build_output_missing");
4429
4458
  }
@@ -4435,13 +4464,8 @@ async function buildDeployBundle(options) {
4435
4464
  "build_output_mismatch"
4436
4465
  );
4437
4466
  }
4438
- const workerDir = dirname6(outputWranglerPath);
4439
- try {
4440
- secureBuildDevVars(workerDir);
4441
- } catch (error) {
4442
- output.die(errorMessage(error), "build_output_unsafe");
4443
- }
4444
- const workerBundlePath = join16(workerDir, outputConfig.main);
4467
+ const workerDir = dirname7(outputWranglerPath);
4468
+ const workerBundlePath = join15(workerDir, outputConfig.main);
4445
4469
  const clientDir = outputConfig.assets?.directory ? resolve11(workerDir, outputConfig.assets.directory) : null;
4446
4470
  if (!existsSync14(workerBundlePath)) {
4447
4471
  output.die(`Worker bundle not found at ${workerBundlePath}`, "build_output_missing");
@@ -4533,7 +4557,7 @@ function collectAssets(dir) {
4533
4557
  const walk = (currentDir, prefix) => {
4534
4558
  for (const entry of readdirSync4(currentDir, { withFileTypes: true })) {
4535
4559
  if (isDeployAssetControlFile(entry.name)) continue;
4536
- const fullPath = join16(currentDir, entry.name);
4560
+ const fullPath = join15(currentDir, entry.name);
4537
4561
  const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
4538
4562
  if (entry.isDirectory()) {
4539
4563
  walk(fullPath, relativePath);
@@ -4557,7 +4581,7 @@ function readDeployAssetConfig(clientDir) {
4557
4581
  ["_headers", "_headers"],
4558
4582
  ["_redirects", "_redirects"]
4559
4583
  ]) {
4560
- const filePath = join16(clientDir, file);
4584
+ const filePath = join15(clientDir, file);
4561
4585
  if (!existsSync14(filePath)) continue;
4562
4586
  const content = readFileSync17(filePath, "utf8");
4563
4587
  if (content.trim()) config[key] = content;
@@ -4574,17 +4598,17 @@ function errorMessage(error) {
4574
4598
  // src/cli/commands/deploy/commerce.ts
4575
4599
  import * as p7 from "@clack/prompts";
4576
4600
  import { existsSync as existsSync15 } from "fs";
4577
- import { join as join17 } from "path";
4601
+ import { join as join16 } from "path";
4578
4602
  import { pathToFileURL as pathToFileURL2 } from "url";
4579
4603
  var API_URL2 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
4580
4604
  function errorMessage2(error) {
4581
4605
  return error instanceof Error ? error.message : String(error);
4582
4606
  }
4583
4607
  async function loadDeclaration(appDir, sourceFile, outputFile, exportName) {
4584
- const sourcePath = join17(appDir, "src", sourceFile);
4608
+ const sourcePath = join16(appDir, "src", sourceFile);
4585
4609
  if (!existsSync15(sourcePath)) return void 0;
4586
4610
  const esbuild = await import("esbuild");
4587
- const outputPath = join17(appDir, ".wrangler", "deploy", outputFile);
4611
+ const outputPath = join16(appDir, ".wrangler", "deploy", outputFile);
4588
4612
  await esbuild.build({
4589
4613
  entryPoints: [sourcePath],
4590
4614
  bundle: true,
@@ -4677,7 +4701,7 @@ Stripe will keep charging them until you cancel. Cancel at the end of their curr
4677
4701
  }
4678
4702
  }
4679
4703
  async function syncSubscriptionPlans(appDir, appName, token, nonInteractive) {
4680
- if (!existsSync15(join17(appDir, "src", "subscriptions.ts"))) return;
4704
+ if (!existsSync15(join16(appDir, "src", "subscriptions.ts"))) return;
4681
4705
  let plans;
4682
4706
  try {
4683
4707
  plans = await loadDeclaration(
@@ -4732,7 +4756,7 @@ async function syncSubscriptionPlans(appDir, appName, token, nonInteractive) {
4732
4756
  }
4733
4757
  }
4734
4758
  async function syncOneTimeProducts(appDir, appName, token) {
4735
- if (!existsSync15(join17(appDir, "src", "products.ts"))) return;
4759
+ if (!existsSync15(join16(appDir, "src", "products.ts"))) return;
4736
4760
  let products;
4737
4761
  try {
4738
4762
  products = await loadDeclaration(
@@ -4827,7 +4851,7 @@ function createDeployOutput(json) {
4827
4851
  else console.error(withSlug(message, code));
4828
4852
  if (action) printAction(action);
4829
4853
  }
4830
- process.exit(opts.actionRequired ? 2 : 1);
4854
+ throw new CliExit(opts.actionRequired ? 2 : 1);
4831
4855
  }
4832
4856
  };
4833
4857
  }
@@ -5556,6 +5580,7 @@ async function syncDeployRepository(options) {
5556
5580
  sourceRevision = claimed.revision;
5557
5581
  }
5558
5582
  } catch (error) {
5583
+ if (error instanceof CliExit) throw error;
5559
5584
  const failure = deployRepositoryFailure(error, appDir);
5560
5585
  output.die(failure.error, failure.code);
5561
5586
  }
@@ -5672,7 +5697,7 @@ async function deployBuiltBundle(options) {
5672
5697
  ...action ? { action } : {}
5673
5698
  });
5674
5699
  }
5675
- process.exit(actionRequired ? 2 : 1);
5700
+ throw new CliExit(actionRequired ? 2 : 1);
5676
5701
  };
5677
5702
  const makeForm = () => {
5678
5703
  const form = new FormData();
@@ -6583,6 +6608,7 @@ var apps_default = defineDeepspaceCommand({
6583
6608
 
6584
6609
  // src/cli/commands/logs.ts
6585
6610
  import { defineCommand as defineCommand4 } from "citty";
6611
+ import { setTimeout as delay } from "timers/promises";
6586
6612
 
6587
6613
  // src/shared/log-events.ts
6588
6614
  var LOG_LEVELS = ["debug", "log", "info", "warn", "error"];
@@ -6693,7 +6719,7 @@ function formatEvent(e, color, now = Date.now()) {
6693
6719
  const client = e.source === "client" ? paint("35", "CLIENT", color) + " " : "";
6694
6720
  return `${time} ${client}${tag} ${body}`;
6695
6721
  }
6696
- var sleep3 = (ms) => new Promise((r) => setTimeout(r, ms));
6722
+ var sleep3 = (ms, signal) => delay(ms, void 0, { signal });
6697
6723
  var logs_default = defineCommand4({
6698
6724
  meta: {
6699
6725
  name: "logs",
@@ -6769,12 +6795,17 @@ var logs_default = defineCommand4({
6769
6795
  const windowLabel = args.since?.trim() || "15m";
6770
6796
  const MAX_LIMIT = 500;
6771
6797
  const followLimit = followInitialLimit(limit, MAX_LIMIT);
6772
- const fetchPage = async (since, pageLimit, token2) => {
6798
+ const fetchPage = async (since, pageLimit, token2, signal) => {
6773
6799
  const params = new URLSearchParams({ since: String(since) });
6774
6800
  if (pageLimit !== void 0) params.set("limit", String(pageLimit));
6775
6801
  if (args.level) params.set("level", args.level);
6776
6802
  if (args.search) params.set("search", args.search);
6777
- 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
+ );
6778
6809
  };
6779
6810
  const print = (events) => {
6780
6811
  for (const e of events) {
@@ -6810,7 +6841,8 @@ var logs_default = defineCommand4({
6810
6841
  }
6811
6842
  return;
6812
6843
  }
6813
- process.on("SIGINT", () => process.exit(0));
6844
+ const tail = new AbortController();
6845
+ process.once("SIGINT", () => tail.abort());
6814
6846
  if (!args.json) {
6815
6847
  console.error(`Tailing logs for ${appId} \u2014 Ctrl+C to stop.`);
6816
6848
  }
@@ -6835,12 +6867,23 @@ var logs_default = defineCommand4({
6835
6867
  }
6836
6868
  }
6837
6869
  for (; ; ) {
6838
- await sleep3(backoff || interval);
6870
+ try {
6871
+ await sleep3(backoff || interval, tail.signal);
6872
+ } catch {
6873
+ return;
6874
+ }
6839
6875
  const freshToken = await ensureToken();
6876
+ if (tail.signal.aborted) return;
6840
6877
  let page;
6841
6878
  try {
6842
- page = await fetchPage(nextPollSince(cursor, initialSince), followLimit, freshToken);
6879
+ page = await fetchPage(
6880
+ nextPollSince(cursor, initialSince),
6881
+ followLimit,
6882
+ freshToken,
6883
+ tail.signal
6884
+ );
6843
6885
  } catch (err) {
6886
+ if (tail.signal.aborted) return;
6844
6887
  if (err instanceof ApiError && [401, 403, 404].includes(err.status)) throw err;
6845
6888
  backoff = Math.min(backoff ? backoff * 2 : MIN_POLL_MS, 3e4);
6846
6889
  const msg = err instanceof Error ? err.message : String(err);
@@ -6946,7 +6989,7 @@ var usage_default = defineDeepspaceCommand({
6946
6989
 
6947
6990
  // src/cli/commands/add.ts
6948
6991
  import { existsSync as existsSync18, readFileSync as readFileSync20, realpathSync as realpathSync4 } from "fs";
6949
- 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";
6950
6993
  import { pathToFileURL as pathToFileURL3 } from "url";
6951
6994
 
6952
6995
  // src/cli/commands/feature-installer.ts
@@ -6962,17 +7005,17 @@ import {
6962
7005
  readSync,
6963
7006
  readdirSync as readdirSync5,
6964
7007
  rmSync as rmSync4,
6965
- writeFileSync as writeFileSync8
7008
+ writeFileSync as writeFileSync6
6966
7009
  } from "fs";
6967
7010
  import { spawnSync as spawnSync6 } from "child_process";
6968
7011
  import { tmpdir as tmpdir2 } from "os";
6969
- 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";
6970
7013
  import { fileURLToPath } from "url";
6971
7014
  var NAV_MARKER = "// \u2500\u2500 Features add nav items below this line \u2500\u2500";
6972
7015
  function featurePackageRoot(moduleUrl = import.meta.url) {
6973
- let dir = dirname7(fileURLToPath(moduleUrl));
7016
+ let dir = dirname8(fileURLToPath(moduleUrl));
6974
7017
  for (; ; ) {
6975
- const packagePath = join18(dir, "package.json");
7018
+ const packagePath = join17(dir, "package.json");
6976
7019
  if (existsSync17(packagePath)) {
6977
7020
  try {
6978
7021
  const pkg2 = JSON.parse(readFileSync19(packagePath, "utf-8"));
@@ -6980,16 +7023,16 @@ function featurePackageRoot(moduleUrl = import.meta.url) {
6980
7023
  } catch {
6981
7024
  }
6982
7025
  }
6983
- const parent = dirname7(dir);
7026
+ const parent = dirname8(dir);
6984
7027
  if (parent === dir) throw new Error("Could not locate the deepspace package root");
6985
7028
  dir = parent;
6986
7029
  }
6987
7030
  }
6988
7031
  function hasFeatureCatalog(packageRoot = featurePackageRoot()) {
6989
- return existsSync17(join18(packageRoot, "features"));
7032
+ return existsSync17(join17(packageRoot, "features"));
6990
7033
  }
6991
7034
  function readFeatureConfig(id, packageRoot = featurePackageRoot()) {
6992
- const configPath = join18(packageRoot, "features", id, "feature.json");
7035
+ const configPath = join17(packageRoot, "features", id, "feature.json");
6993
7036
  if (!existsSync17(configPath)) return null;
6994
7037
  try {
6995
7038
  const config = JSON.parse(readFileSync19(configPath, "utf-8"));
@@ -7002,7 +7045,7 @@ function readFeatureConfig(id, packageRoot = featurePackageRoot()) {
7002
7045
  }
7003
7046
  }
7004
7047
  function listFeatureConfigs(packageRoot = featurePackageRoot()) {
7005
- const featuresDir = join18(packageRoot, "features");
7048
+ const featuresDir = join17(packageRoot, "features");
7006
7049
  if (!existsSync17(featuresDir)) return [];
7007
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));
7008
7051
  }
@@ -7051,17 +7094,17 @@ function resolveFeatureDestPath(fileDest, route) {
7051
7094
  return route?.protected === false ? `${appPages}${rest}` : `${appPages}(protected)/${rest}`;
7052
7095
  }
7053
7096
  function copyFile(src, dest) {
7054
- mkdirSync6(dirname7(dest), { recursive: true });
7097
+ mkdirSync6(dirname8(dest), { recursive: true });
7055
7098
  copyFileSync(src, dest);
7056
7099
  }
7057
7100
  function copyFileWithImportShift(src, dest, levels) {
7058
- mkdirSync6(dirname7(dest), { recursive: true });
7101
+ mkdirSync6(dirname8(dest), { recursive: true });
7059
7102
  const prefix = "../".repeat(levels);
7060
7103
  const shifted = readFileSync19(src, "utf-8").replace(
7061
7104
  /(from\s+|import\s+|import\()(['"])(\.\.\/)/g,
7062
7105
  `$1$2${prefix}$3`
7063
7106
  );
7064
- writeFileSync8(dest, shifted);
7107
+ writeFileSync6(dest, shifted);
7065
7108
  }
7066
7109
  function installFiles(packageRoot, config, targetDir, emit) {
7067
7110
  let copied = 0;
@@ -7070,9 +7113,9 @@ function installFiles(packageRoot, config, targetDir, emit) {
7070
7113
  const preexisting = [];
7071
7114
  const missing = [];
7072
7115
  for (const file of config.files) {
7073
- const srcPath = join18(packageRoot, "features", config.id, file.src);
7116
+ const srcPath = join17(packageRoot, "features", config.id, file.src);
7074
7117
  const resolvedDest = resolveFeatureDestPath(file.dest, config.route);
7075
- const destPath = join18(targetDir, resolvedDest);
7118
+ const destPath = join17(targetDir, resolvedDest);
7076
7119
  const existed = existsSync17(destPath);
7077
7120
  if (existed) preexisting.push(resolvedDest);
7078
7121
  if (!existsSync17(srcPath)) {
@@ -7112,7 +7155,7 @@ function unresolvedSchema(schema, reason, emit) {
7112
7155
  }
7113
7156
  function integrateSchema(config, targetDir, emit) {
7114
7157
  if (!config.schema) return [];
7115
- const schemasPath = join18(targetDir, "src", "schemas.ts");
7158
+ const schemasPath = join17(targetDir, "src", "schemas.ts");
7116
7159
  if (!existsSync17(schemasPath)) {
7117
7160
  return [unresolvedSchema(config.schema, "src/schemas.ts was not found.", emit)];
7118
7161
  }
@@ -7147,7 +7190,7 @@ export const schemas`
7147
7190
  return [unresolvedSchema(config.schema, reason, emit)];
7148
7191
  }
7149
7192
  content = withEntry;
7150
- writeFileSync8(schemasPath, content);
7193
+ writeFileSync6(schemasPath, content);
7151
7194
  emit(` Schema integrated: ${exportName} -> schemas.ts`);
7152
7195
  return [];
7153
7196
  }
@@ -7169,7 +7212,7 @@ function unresolvedActions(actions, reason, emit) {
7169
7212
  }
7170
7213
  function integrateActions(config, targetDir, emit) {
7171
7214
  if (!config.actions?.length) return [];
7172
- const actionsPath = join18(targetDir, "src", "actions", "index.ts");
7215
+ const actionsPath = join17(targetDir, "src", "actions", "index.ts");
7173
7216
  if (!existsSync17(actionsPath)) {
7174
7217
  return [unresolvedActions(config.actions, "src/actions/index.ts was not found.", emit)];
7175
7218
  }
@@ -7213,13 +7256,13 @@ ${registryEntry}`);
7213
7256
  integrated++;
7214
7257
  emit(` Action integrated: ${action.registerAs} (${action.exportName})`);
7215
7258
  }
7216
- if (integrated > 0) writeFileSync8(actionsPath, content);
7259
+ if (integrated > 0) writeFileSync6(actionsPath, content);
7217
7260
  return unresolved;
7218
7261
  }
7219
7262
  function integrateCode(config, targetDir, emit) {
7220
7263
  const unresolved = [];
7221
7264
  for (const insertion of config.code ?? []) {
7222
- const targetPath = join18(targetDir, insertion.file);
7265
+ const targetPath = join17(targetDir, insertion.file);
7223
7266
  if (!existsSync17(targetPath)) {
7224
7267
  unresolved.push({
7225
7268
  area: "code",
@@ -7256,27 +7299,27 @@ ${content.slice(importIndex < 0 ? 0 : importIndex)}`;
7256
7299
  const prefix = insertion.placement === "before" ? "" : "\n";
7257
7300
  const suffix = insertion.placement === "before" ? "\n" : "";
7258
7301
  content = `${content.slice(0, insertAt)}${prefix}${insertion.insert}${suffix}${content.slice(insertAt)}`;
7259
- writeFileSync8(targetPath, content);
7302
+ writeFileSync6(targetPath, content);
7260
7303
  emit(` Integrated: ${insertion.file}`);
7261
7304
  }
7262
7305
  return unresolved;
7263
7306
  }
7264
7307
  function integrateIgnore(config, targetDir) {
7265
7308
  if (!config.ignore?.length) return;
7266
- const targetPath = join18(targetDir, ".gitignore");
7309
+ const targetPath = join17(targetDir, ".gitignore");
7267
7310
  const current = existsSync17(targetPath) ? readFileSync19(targetPath, "utf-8") : "";
7268
7311
  const present = new Set(current.split(/\r?\n/).map((line) => line.trim()));
7269
7312
  const missing = config.ignore.filter((pattern) => !present.has(pattern));
7270
7313
  if (missing.length === 0) return;
7271
7314
  const separator = current === "" || current.endsWith("\n") ? "" : "\n";
7272
- writeFileSync8(targetPath, `${current}${separator}${missing.join("\n")}
7315
+ writeFileSync6(targetPath, `${current}${separator}${missing.join("\n")}
7273
7316
  `);
7274
7317
  }
7275
7318
  function integrateWrangler(config, targetDir, emit) {
7276
7319
  const runWorkerFirst = config.wrangler?.runWorkerFirst === true;
7277
7320
  const rateLimits = config.wrangler?.rateLimits ?? [];
7278
7321
  if (!runWorkerFirst && rateLimits.length === 0) return [];
7279
- const targetPath = join18(targetDir, "wrangler.toml");
7322
+ const targetPath = join17(targetDir, "wrangler.toml");
7280
7323
  const steps = [
7281
7324
  ...runWorkerFirst ? ["Set assets.run_worker_first = true in wrangler.toml."] : [],
7282
7325
  ...rateLimits.map((binding) => `Add the ${binding.name} rate-limit binding to wrangler.toml.`)
@@ -7306,7 +7349,7 @@ ${rateLimitBindingBlock(scope, binding)}
7306
7349
  }
7307
7350
  }
7308
7351
  if (changed) {
7309
- writeFileSync8(targetPath, source);
7352
+ writeFileSync6(targetPath, source);
7310
7353
  emit(" Integrated: wrangler.toml");
7311
7354
  } else emit(" Integration already present: wrangler.toml");
7312
7355
  return [];
@@ -7360,7 +7403,7 @@ function rateLimitBindingBlock(scope, binding) {
7360
7403
  }
7361
7404
  function integrateCss(packageRoot, config, targetDir, emit) {
7362
7405
  if (!config.css?.length) return [];
7363
- const stylesPath = join18(targetDir, "src", "styles.css");
7406
+ const stylesPath = join17(targetDir, "src", "styles.css");
7364
7407
  if (!existsSync17(stylesPath)) {
7365
7408
  emit(" Warning: Cannot integrate CSS \u2014 src/styles.css not found");
7366
7409
  return [
@@ -7377,7 +7420,7 @@ function integrateCss(packageRoot, config, targetDir, emit) {
7377
7420
  let integrated = 0;
7378
7421
  const unresolved = [];
7379
7422
  for (const cssFile of config.css) {
7380
- const srcPath = join18(packageRoot, "features", config.id, cssFile);
7423
+ const srcPath = join17(packageRoot, "features", config.id, cssFile);
7381
7424
  if (!existsSync17(srcPath)) {
7382
7425
  emit(` Warning: CSS source not found: ${cssFile}`, "stderr");
7383
7426
  unresolved.push({
@@ -7399,7 +7442,7 @@ ${css}`;
7399
7442
  integrated++;
7400
7443
  emit(` CSS integrated: ${cssFile} -> styles.css`);
7401
7444
  }
7402
- if (integrated > 0) writeFileSync8(stylesPath, styles);
7445
+ if (integrated > 0) writeFileSync6(stylesPath, styles);
7403
7446
  return unresolved;
7404
7447
  }
7405
7448
  function unresolvedRoute(route, config, reason, emit) {
@@ -7414,7 +7457,7 @@ function unresolvedRoute(route, config, reason, emit) {
7414
7457
  }
7415
7458
  function integrateRoute(config, targetDir, emit) {
7416
7459
  if (!config.route) return [];
7417
- const navPath = join18(targetDir, "src", "nav.ts");
7460
+ const navPath = join17(targetDir, "src", "nav.ts");
7418
7461
  if (!existsSync17(navPath)) {
7419
7462
  return [unresolvedRoute(config.route, config, "src/nav.ts was not found.", emit)];
7420
7463
  }
@@ -7439,7 +7482,7 @@ function integrateRoute(config, targetDir, emit) {
7439
7482
  const entry = ` { path: '${routePath}', label: '${config.name}'${roles} },`;
7440
7483
  content = content.replace(NAV_MARKER, `${NAV_MARKER}
7441
7484
  ${entry}`);
7442
- writeFileSync8(navPath, content);
7485
+ writeFileSync6(navPath, content);
7443
7486
  emit(` Nav wired: ${routePath} (${config.name})`);
7444
7487
  emit(" Route: automatic (file-based routing via src/pages/)");
7445
7488
  return [];
@@ -7465,10 +7508,10 @@ function runPackageInstall(packageManager, targetDir, outputVisible) {
7465
7508
  const child = spawnSync6(packageManager, ["install"], { ...options, stdio: "inherit" });
7466
7509
  return { error: child.error, status: child.status };
7467
7510
  }
7468
- const captureDir = mkdtempSync(join18(tmpdir2(), "deepspace-add-install-"));
7511
+ const captureDir = mkdtempSync(join17(tmpdir2(), "deepspace-add-install-"));
7469
7512
  let outputFd;
7470
7513
  try {
7471
- outputFd = openSync2(join18(captureDir, "output.log"), "w+");
7514
+ outputFd = openSync2(join17(captureDir, "output.log"), "w+");
7472
7515
  const child = spawnSync6(packageManager, ["install"], {
7473
7516
  ...options,
7474
7517
  stdio: ["ignore", outputFd, outputFd]
@@ -7492,7 +7535,7 @@ function integrateDependencies(config, targetDir, runInstall, outputVisible, emi
7492
7535
  if (declarations.every(({ values }) => !values || Object.keys(values).length === 0)) {
7493
7536
  return { added: [], installRan: false, packageManager };
7494
7537
  }
7495
- const packagePath = join18(targetDir, "package.json");
7538
+ const packagePath = join17(targetDir, "package.json");
7496
7539
  if (!existsSync17(packagePath)) {
7497
7540
  emit(" Could not patch package.json (file not found)");
7498
7541
  return { added: [], installRan: false, packageManager };
@@ -7523,7 +7566,7 @@ function integrateDependencies(config, targetDir, runInstall, outputVisible, emi
7523
7566
  if (added.length === 0) {
7524
7567
  emit(" Dependencies already present");
7525
7568
  } else {
7526
- writeFileSync8(packagePath, `${JSON.stringify(pkg2, null, 2)}
7569
+ writeFileSync6(packagePath, `${JSON.stringify(pkg2, null, 2)}
7527
7570
  `, "utf-8");
7528
7571
  emit(` Added to package.json: ${added.map(({ name }) => name).join(", ")}`);
7529
7572
  if (skipped.length) emit(` Already present (kept your version): ${skipped.join(", ")}`);
@@ -7559,7 +7602,7 @@ ${child.diagnosticTail}` : "";
7559
7602
  }
7560
7603
  function dependencySnapshot(targetDir) {
7561
7604
  try {
7562
- const pkg2 = JSON.parse(readFileSync19(join18(targetDir, "package.json"), "utf-8"));
7605
+ const pkg2 = JSON.parse(readFileSync19(join17(targetDir, "package.json"), "utf-8"));
7563
7606
  return {
7564
7607
  dependencies: pkg2.dependencies ?? {},
7565
7608
  devDependencies: pkg2.devDependencies ?? {}
@@ -7571,7 +7614,7 @@ function dependencySnapshot(targetDir) {
7571
7614
  function installedNavFeatures(packageRoot, targetDir) {
7572
7615
  return listFeatureConfigs(packageRoot).filter((feature) => feature.category === "nav" && feature.files.length > 0).filter((feature) => {
7573
7616
  const signature = resolveFeatureDestPath(feature.files[0].dest, feature.route);
7574
- return existsSync17(join18(targetDir, signature));
7617
+ return existsSync17(join17(targetDir, signature));
7575
7618
  }).map((feature) => feature.id);
7576
7619
  }
7577
7620
  function warnNavConflicts(packageRoot, config, targetDir, emit) {
@@ -7804,8 +7847,8 @@ function renderFeatureInfo(config) {
7804
7847
  return lines.join("\n");
7805
7848
  }
7806
7849
  function isDeepSpaceApp(dir) {
7807
- if (existsSync18(join19(dir, "wrangler.toml"))) return true;
7808
- const packagePath = join19(dir, "package.json");
7850
+ if (existsSync18(join18(dir, "wrangler.toml"))) return true;
7851
+ const packagePath = join18(dir, "package.json");
7809
7852
  if (!existsSync18(packagePath)) return false;
7810
7853
  try {
7811
7854
  const pkg2 = JSON.parse(readFileSync20(packagePath, "utf-8"));
@@ -7817,9 +7860,9 @@ function isDeepSpaceApp(dir) {
7817
7860
  function installedPackageRoot(fromDir) {
7818
7861
  let dir = fromDir;
7819
7862
  for (; ; ) {
7820
- const candidate = join19(dir, "node_modules", "deepspace");
7821
- if (existsSync18(join19(candidate, "package.json"))) return candidate;
7822
- 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);
7823
7866
  if (parent === dir) return null;
7824
7867
  dir = parent;
7825
7868
  }
@@ -7835,8 +7878,8 @@ async function targetInstaller(appDir) {
7835
7878
  if (realpathSync4(installedRoot) === realpathSync4(featurePackageRoot())) {
7836
7879
  return { installFeature };
7837
7880
  }
7838
- const entry = join19(installedRoot, "dist", "feature-installer.js");
7839
- const catalog = join19(installedRoot, "features");
7881
+ const entry = join18(installedRoot, "dist", "feature-installer.js");
7882
+ const catalog = join18(installedRoot, "features");
7840
7883
  if (!existsSync18(entry) || !existsSync18(catalog)) {
7841
7884
  throw new Refusal(
7842
7885
  `The app's installed deepspace package does not contain the typed feature installer.
@@ -8011,13 +8054,12 @@ function fmtDate(iso) {
8011
8054
  }
8012
8055
  async function confirm4(message) {
8013
8056
  process.stdout.write(`${message} [y/N] `);
8014
- return new Promise((resolve21) => {
8057
+ const answer = await new Promise((resolve21) => {
8015
8058
  process.stdin.setEncoding("utf-8");
8016
- process.stdin.once("data", (data) => {
8017
- const answer = data.toString().trim().toLowerCase();
8018
- resolve21(answer === "y" || answer === "yes");
8019
- });
8059
+ process.stdin.once("data", (data) => resolve21(data.toString().trim().toLowerCase()));
8020
8060
  });
8061
+ process.stdin.pause();
8062
+ return answer === "y" || answer === "yes";
8021
8063
  }
8022
8064
  var cannotPrompt = (json) => json || !process.stdin.isTTY;
8023
8065
  var search = defineDeepspaceCommand({
@@ -9368,7 +9410,7 @@ var integrations_default = defineCommand9({
9368
9410
  // src/cli/commands/feedback.ts
9369
9411
  import * as p16 from "@clack/prompts";
9370
9412
  import { existsSync as existsSync20, readFileSync as readFileSync22 } from "fs";
9371
- import { dirname as dirname9, join as join20 } from "path";
9413
+ import { dirname as dirname10, join as join19 } from "path";
9372
9414
  import { fileURLToPath as fileURLToPath2 } from "url";
9373
9415
  import { release } from "os";
9374
9416
  var API_URL8 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
@@ -9389,10 +9431,10 @@ function normalizeType(raw) {
9389
9431
  }
9390
9432
  function readCliVersion() {
9391
9433
  try {
9392
- const here = dirname9(fileURLToPath2(import.meta.url));
9434
+ const here = dirname10(fileURLToPath2(import.meta.url));
9393
9435
  const candidates = [
9394
- join20(here, "..", "package.json"),
9395
- join20(here, "..", "..", "..", "package.json")
9436
+ join19(here, "..", "package.json"),
9437
+ join19(here, "..", "..", "..", "package.json")
9396
9438
  ];
9397
9439
  for (const path of candidates) {
9398
9440
  if (!existsSync20(path)) continue;
@@ -9793,7 +9835,7 @@ var get2 = defineCommand10({
9793
9835
  const { secrets } = await listSecrets(DEPLOY_URL11, t.token, t.appId, t.configName);
9794
9836
  const item = secrets.find((s) => s.key === key);
9795
9837
  if (!item)
9796
- 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");
9797
9839
  ok(
9798
9840
  args.json === true,
9799
9841
  {
@@ -9865,7 +9907,7 @@ var upload = defineCommand10({
9865
9907
  const content = args.file === "-" ? readFileSync23(0, "utf-8") : readFileSync23(args.file, "utf-8");
9866
9908
  const secrets = parseSecretsUpload(content);
9867
9909
  if (Object.keys(secrets).length === 0)
9868
- fail(new InputError("No secrets found in the input.", "empty_input"));
9910
+ throw new InputError("No secrets found in the input.", "empty_input");
9869
9911
  await uploadSecrets(DEPLOY_URL11, t.token, t.appId, t.configName, secrets, args.replace);
9870
9912
  const uploaded = Object.keys(secrets);
9871
9913
  ok(
@@ -9896,11 +9938,9 @@ var download = defineCommand10({
9896
9938
  const t = await resolveTarget(args);
9897
9939
  const format = args.format;
9898
9940
  if (!["dotenv", "json", "shell"].includes(format)) {
9899
- fail(
9900
- new InputError(
9901
- `Unknown format "${args.format}" \u2014 use dotenv, json, or shell.`,
9902
- "invalid_format"
9903
- )
9941
+ throw new InputError(
9942
+ `Unknown format "${args.format}" \u2014 use dotenv, json, or shell.`,
9943
+ "invalid_format"
9904
9944
  );
9905
9945
  }
9906
9946
  const { secrets } = await fetchSecretsValues(DEPLOY_URL11, t.token, t.appId, t.configName);
@@ -9918,11 +9958,9 @@ var pull = defineCommand10({
9918
9958
  const wranglerEnv = args.env?.trim() || void 0;
9919
9959
  const appDir = findAppDir();
9920
9960
  if (!appDir)
9921
- fail(
9922
- new InputError(
9923
- "Run from a DeepSpace app directory (one containing wrangler.toml).",
9924
- "not_in_app_repo"
9925
- )
9961
+ throw new InputError(
9962
+ "Run from a DeepSpace app directory (one containing wrangler.toml).",
9963
+ "not_in_app_repo"
9926
9964
  );
9927
9965
  const t = await resolveTarget(args);
9928
9966
  const ownerId = decodeJwtPayload(t.token).sub;
@@ -9986,11 +10024,9 @@ var configsCreate = defineCommand10({
9986
10024
  const { configs: configs2 } = await listConfigs(DEPLOY_URL11, t.token, t.appId);
9987
10025
  if (configs2.some((c) => c.name === name)) {
9988
10026
  if (copyFrom) {
9989
- fail(
9990
- new InputError(
9991
- `Config "${name}" already exists \u2014 refusing to copy "${copyFrom}" over it. Delete it first, or pick a new name.`,
9992
- "config_exists"
9993
- )
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"
9994
10030
  );
9995
10031
  }
9996
10032
  ok(
@@ -10890,7 +10926,7 @@ var pull_default = defineDeepspaceCommand({
10890
10926
  // src/cli/commands/clone.ts
10891
10927
  import * as p20 from "@clack/prompts";
10892
10928
  import { existsSync as existsSync22, readdirSync as readdirSync6, rmSync as rmSync5 } from "fs";
10893
- 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";
10894
10930
  function cleanFailedCloneDir(dir) {
10895
10931
  let entries;
10896
10932
  try {
@@ -10900,7 +10936,7 @@ function cleanFailedCloneDir(dir) {
10900
10936
  }
10901
10937
  if (entries.length === 1 && entries[0] === ".git") {
10902
10938
  try {
10903
- rmSync5(join21(dir, ".git"), { recursive: true, force: true });
10939
+ rmSync5(join20(dir, ".git"), { recursive: true, force: true });
10904
10940
  return [];
10905
10941
  } catch {
10906
10942
  return entries;
@@ -11385,8 +11421,16 @@ function printOverlaps(overlaps, json) {
11385
11421
 
11386
11422
  // src/cli/commands/workspace/local.ts
11387
11423
  import * as p24 from "@clack/prompts";
11388
- import { mkdirSync as mkdirSync7, readFileSync as readFileSync25, realpathSync as realpathSync6, renameSync as renameSync2, statSync as statSync3, writeFileSync as writeFileSync9 } from "fs";
11389
- 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";
11390
11434
  var WORKSPACE_MARKER = "deepspace-workspace.json";
11391
11435
  function excludeWorktreeDir(appDir, dir) {
11392
11436
  try {
@@ -11407,7 +11451,7 @@ function worktreeMarkerPath(worktreeRoot) {
11407
11451
  const result = runGit(worktreeRoot, ["rev-parse", "--absolute-git-dir"], { allowFail: true });
11408
11452
  if (result.status !== 0) return null;
11409
11453
  const gitDir = result.stdout.toString("utf-8").trim();
11410
- return isAbsolute5(gitDir) ? join22(gitDir, WORKSPACE_MARKER) : null;
11454
+ return isAbsolute5(gitDir) ? join21(gitDir, WORKSPACE_MARKER) : null;
11411
11455
  } catch {
11412
11456
  return null;
11413
11457
  }
@@ -11418,8 +11462,8 @@ function markManagedWorkspace(worktreeRoot, workspaceId) {
11418
11462
  const marker = { version: 1, workspaceId, mode: "managed-linked" };
11419
11463
  const tempPath = `${markerPath}.${process.pid}.tmp`;
11420
11464
  try {
11421
- mkdirSync7(dirname10(markerPath), { recursive: true });
11422
- writeFileSync9(tempPath, JSON.stringify(marker) + "\n");
11465
+ mkdirSync7(dirname11(markerPath), { recursive: true });
11466
+ writeFileSync7(tempPath, JSON.stringify(marker) + "\n");
11423
11467
  renameSync2(tempPath, markerPath);
11424
11468
  return true;
11425
11469
  } catch {
@@ -11452,11 +11496,11 @@ function isManagedWorkspaceWorktree(worktreeRoot, workspaceId) {
11452
11496
  function defaultWorkspaceRoot(appDir, workspaceId) {
11453
11497
  const primary = listWorktrees(appDir)[0];
11454
11498
  if (!primary) throw new Error("Git did not report a primary worktree");
11455
- return join22(primary.path, ".deepspace", "ws", workspaceId.slice(3).toLowerCase());
11499
+ return join21(primary.path, ".deepspace", "ws", workspaceId.slice(3).toLowerCase());
11456
11500
  }
11457
11501
  function appDirInWorktree(appDir, worktreeRoot) {
11458
11502
  const appRelative = relative2(realpathSync6(repoToplevel(appDir)), realpathSync6(appDir));
11459
- return appRelative ? join22(worktreeRoot, appRelative) : worktreeRoot;
11503
+ return appRelative ? join21(worktreeRoot, appRelative) : worktreeRoot;
11460
11504
  }
11461
11505
  function primaryAppDir(appDir) {
11462
11506
  const primary = listWorktrees(appDir)[0];
@@ -11491,7 +11535,7 @@ function rematerializeWorkspaceWorktree(appDir, worktreeRoot, branch, workspaceI
11491
11535
  function inOwnLinkedWorktree(appDir, id) {
11492
11536
  if (workspaceIdFromBranch(currentBranch(appDir)) !== id) return false;
11493
11537
  try {
11494
- return statSync3(join22(repoToplevel(appDir), ".git")).isFile();
11538
+ return statSync3(join21(repoToplevel(appDir), ".git")).isFile();
11495
11539
  } catch {
11496
11540
  return false;
11497
11541
  }
@@ -11532,6 +11576,7 @@ function cleanupJson(outcome) {
11532
11576
  worktreeRetained: outcome.worktreeRetained,
11533
11577
  branchDeleted: outcome.branchDeleted,
11534
11578
  ...outcome.mainDir ? { mainDir: outcome.mainDir } : {},
11579
+ ...outcome.leftoverDir ? { leftoverDir: outcome.leftoverDir } : {},
11535
11580
  ...outcome.error ? { error: outcome.error } : {}
11536
11581
  };
11537
11582
  }
@@ -11606,7 +11651,23 @@ function cleanupWorkspaceLocal(appDir, id, trunkBranch, options = {}) {
11606
11651
  }
11607
11652
  const remove2 = runGit(mainDir, ["worktree", "remove", linked.path], { allowFail: true });
11608
11653
  if (remove2.status !== 0) {
11609
- 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
+ }
11610
11671
  return out;
11611
11672
  }
11612
11673
  out.worktreeRemoved = linked.path;
@@ -11628,12 +11689,20 @@ function cleanupWorkspaceLocal(appDir, id, trunkBranch, options = {}) {
11628
11689
  function cleanupRefusalMessage(outcome) {
11629
11690
  if (outcome.worktreeDir) {
11630
11691
  const dir = shQuote(outcome.worktreeDir);
11631
- 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)}`;
11632
11699
  }
11633
11700
  return `Cleanup incomplete: ${outcome.error}. Finish it from the main checkout once ${shQuote(outcome.branch)} is safe to delete.`;
11634
11701
  }
11635
11702
  function cleanupAction(outcome) {
11636
- 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] };
11637
11706
  }
11638
11707
  function reportCleanupHuman(outcome) {
11639
11708
  if (outcome.error) {
@@ -11641,6 +11710,7 @@ function reportCleanupHuman(outcome) {
11641
11710
  cleanupRefusalMessage({
11642
11711
  error: outcome.error,
11643
11712
  worktreeDir: outcome.worktreeDir,
11713
+ leftoverDir: outcome.leftoverDir,
11644
11714
  branch: outcome.branch
11645
11715
  })
11646
11716
  );
@@ -11888,7 +11958,7 @@ var newWorkspaceCommand = defineDeepspaceCommand({
11888
11958
 
11889
11959
  // src/cli/commands/workspace/attach.ts
11890
11960
  import * as p26 from "@clack/prompts";
11891
- 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";
11892
11962
  import { isAbsolute as isAbsolute7, resolve as resolve19 } from "path";
11893
11963
  function samePath(a, b) {
11894
11964
  const real = (p35) => {
@@ -11908,7 +11978,7 @@ function finishedWorkspaceMessage(id, view) {
11908
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.`;
11909
11979
  }
11910
11980
  function cleanFailedFreshAttachDir(dir) {
11911
- if (!existsSync23(dir)) return [];
11981
+ if (!existsSync24(dir)) return [];
11912
11982
  let entries;
11913
11983
  try {
11914
11984
  entries = readdirSync7(dir);
@@ -11990,7 +12060,7 @@ var attachWorkspaceCommand = defineDeepspaceCommand({
11990
12060
  const localTip = resolveCommit(appDir, `refs/heads/${branch}`);
11991
12061
  if (localTip) {
11992
12062
  let existing = resolveWorkspaceWorktree(listWorktrees(appDir), id);
11993
- if (existing && !existsSync23(existing)) {
12063
+ if (existing && !existsSync24(existing)) {
11994
12064
  runGit(appDir, ["worktree", "prune"], { allowFail: true });
11995
12065
  existing = null;
11996
12066
  }
@@ -12108,7 +12178,7 @@ var attachWorkspaceCommand = defineDeepspaceCommand({
12108
12178
  process.cwd(),
12109
12179
  (typeof args.dir === "string" ? args.dir.trim() : "") || `ws-${id.slice(3).toLowerCase()}`
12110
12180
  );
12111
- if (existsSync23(dir)) {
12181
+ if (existsSync24(dir)) {
12112
12182
  spinner2?.stop("Target exists.");
12113
12183
  throw new Refusal(`${dir} already exists \u2014 pass a different directory.`, "dir_exists");
12114
12184
  }
@@ -12446,12 +12516,12 @@ import * as p30 from "@clack/prompts";
12446
12516
  // src/cli/lib/validation.ts
12447
12517
  import { sync as spawnSync7 } from "cross-spawn";
12448
12518
  import { readFileSync as readFileSync26 } from "fs";
12449
- import { join as join23 } from "path";
12519
+ import { join as join22 } from "path";
12450
12520
  var MAX_SUMMARY_CHARS = 4e3;
12451
12521
  var MAX_OUTPUT_BUFFER = 64 * 1024 * 1024;
12452
12522
  function configuredCommand(appDir) {
12453
12523
  try {
12454
- const pkg2 = JSON.parse(readFileSync26(join23(appDir, "package.json"), "utf-8"));
12524
+ const pkg2 = JSON.parse(readFileSync26(join22(appDir, "package.json"), "utf-8"));
12455
12525
  if (pkg2.scripts?.validate) return `${detectPackageManager(appDir)} run --silent validate`;
12456
12526
  } catch {
12457
12527
  }
@@ -12792,12 +12862,14 @@ var landWorkspaceCommand = defineDeepspaceCommand({
12792
12862
  if (cleanup?.error) {
12793
12863
  const action2 = cleanupAction({
12794
12864
  worktreeDir: cleanup.worktreeDir,
12865
+ leftoverDir: cleanup.leftoverDir,
12795
12866
  branch: cleanup.branch
12796
12867
  });
12797
12868
  throw new Refusal(
12798
12869
  cleanupRefusalMessage({
12799
12870
  error: cleanup.error,
12800
12871
  worktreeDir: cleanup.worktreeDir,
12872
+ leftoverDir: cleanup.leftoverDir,
12801
12873
  branch: cleanup.branch
12802
12874
  }),
12803
12875
  "cleanup_incomplete",
@@ -12963,12 +13035,14 @@ var dropWorkspaceCommand = defineDeepspaceCommand({
12963
13035
  if (cleanup?.error) {
12964
13036
  const action = cleanupAction({
12965
13037
  worktreeDir: cleanup.worktreeDir,
13038
+ leftoverDir: cleanup.leftoverDir,
12966
13039
  branch: cleanup.branch
12967
13040
  });
12968
13041
  throw new Refusal(
12969
13042
  cleanupRefusalMessage({
12970
13043
  error: cleanup.error,
12971
13044
  worktreeDir: cleanup.worktreeDir,
13045
+ leftoverDir: cleanup.leftoverDir,
12972
13046
  branch: cleanup.branch
12973
13047
  }),
12974
13048
  "cleanup_incomplete",
@@ -13523,10 +13597,165 @@ function sameRefs(left, right) {
13523
13597
 
13524
13598
  // src/cli/commands/update.ts
13525
13599
  import * as p34 from "@clack/prompts";
13526
- import { existsSync as existsSync24, readFileSync as readFileSync27, writeFileSync as writeFileSync10 } from "fs";
13527
- 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
13528
13756
  var REGISTRY = "https://registry.npmjs.org/deepspace/latest";
13529
13757
  var DOCS_URL = "https://documentation.deep.space/cli-reference/commands";
13758
+ var COMPATIBLE_AI_VERSION = package_default.dependencies.ai;
13530
13759
  var OLDEST_SUPPORTED_MINOR = 12;
13531
13760
  function parseVersion(raw) {
13532
13761
  const match = /^(\d+)\.(\d+)\.(\d+)/.exec(raw.trim().replace(/^[\^~]/, ""));
@@ -13538,25 +13767,29 @@ function gapIsTooWide(from, to) {
13538
13767
  return from.major === 0 && from.minor < OLDEST_SUPPORTED_MINOR;
13539
13768
  }
13540
13769
  function readAppSdkVersion(appDir) {
13541
- const installed = join24(appDir, "node_modules", "deepspace", "package.json");
13542
- if (existsSync24(installed)) {
13770
+ const installed = join23(appDir, "node_modules", "deepspace", "package.json");
13771
+ if (existsSync25(installed)) {
13543
13772
  const parsed2 = JSON.parse(readFileSync27(installed, "utf8"));
13544
13773
  if (typeof parsed2.version === "string") return parsed2.version;
13545
13774
  }
13546
- const manifest = join24(appDir, "package.json");
13547
- if (!existsSync24(manifest)) return null;
13775
+ const manifest = join23(appDir, "package.json");
13776
+ if (!existsSync25(manifest)) return null;
13548
13777
  const parsed = JSON.parse(readFileSync27(manifest, "utf8"));
13549
13778
  return parsed.dependencies?.deepspace ?? null;
13550
13779
  }
13551
13780
  function pinSdkVersion(appDir, version) {
13552
- const path = join24(appDir, "package.json");
13781
+ const path = join23(appDir, "package.json");
13553
13782
  const source = readFileSync27(path, "utf8");
13554
- const updated = source.replace(
13783
+ let updated = source.replace(
13555
13784
  /("deepspace"\s*:\s*")[^"]+(")/,
13556
13785
  (_match, head, tail) => `${head}^${version}${tail}`
13557
13786
  );
13787
+ updated = updated.replace(
13788
+ /("ai"\s*:\s*")[^"]+(")/,
13789
+ (_match, head, tail) => `${head}${COMPATIBLE_AI_VERSION}${tail}`
13790
+ );
13558
13791
  if (updated === source) return false;
13559
- writeFileSync10(path, updated);
13792
+ writeFileSync8(path, updated);
13560
13793
  return true;
13561
13794
  }
13562
13795
  async function latestPublishedVersion() {
@@ -13695,9 +13928,9 @@ var update_default = defineDeepspaceCommand({
13695
13928
  });
13696
13929
 
13697
13930
  // src/cli/cli.ts
13698
- var __dirname = dirname11(fileURLToPath3(import.meta.url));
13699
- var pkg = JSON.parse(readFileSync28(join25(__dirname, "..", "package.json"), "utf-8"));
13700
- 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")) {
13701
13934
  process.env.SSL_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt";
13702
13935
  }
13703
13936
  var create2 = defineCommand14({