deepspace 0.14.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -3,8 +3,8 @@
3
3
  // src/cli/cli.ts
4
4
  import { defineCommand as defineCommand14, runMain, runCommand, renderUsage } from "citty";
5
5
  import { sync as spawnSync8 } from "cross-spawn";
6
- import { readFileSync as readFileSync28, existsSync as existsSync26 } from "fs";
7
- import { dirname as dirname10, join as join24 } from "path";
6
+ import { readFileSync as readFileSync29, existsSync as existsSync27 } from "fs";
7
+ import { dirname as dirname10, join as join25 } from "path";
8
8
  import { fileURLToPath as fileURLToPath3 } from "url";
9
9
 
10
10
  // src/cli/lib/spinner.ts
@@ -89,11 +89,12 @@ function printAction(action) {
89
89
 
90
90
  // src/cli/lib/api.ts
91
91
  var ApiError = class extends Error {
92
- constructor(message, status3, code, apiPath) {
92
+ constructor(message, status3, code, apiPath, details) {
93
93
  super(message);
94
94
  this.status = status3;
95
95
  this.code = code;
96
96
  this.apiPath = apiPath;
97
+ this.details = details;
97
98
  }
98
99
  };
99
100
  var refreshAuthToken = null;
@@ -387,6 +388,9 @@ var Refusal = class extends Error {
387
388
  this.extra = opts.extra ?? {};
388
389
  }
389
390
  };
391
+ function apiErrorDetails(err) {
392
+ return err instanceof ApiError ? err.details : void 0;
393
+ }
390
394
  function withoutReservedKeys(payload) {
391
395
  return Object.fromEntries(
392
396
  Object.entries(payload).filter(
@@ -450,7 +454,7 @@ function defineDeepspaceCommand(def) {
450
454
  ...refusal?.actionRequired ? { actionRequired: true } : {},
451
455
  error: message,
452
456
  ...refusal?.action ? { action: refusal.action } : {},
453
- ...withoutReservedKeys(refusal?.extra ?? {})
457
+ ...withoutReservedKeys(refusal?.extra ?? apiErrorDetails(err) ?? {})
454
458
  })
455
459
  );
456
460
  } else {
@@ -734,7 +738,7 @@ async function provisionProfile(jwt) {
734
738
  }
735
739
  }
736
740
  function sleep(ms) {
737
- return new Promise((resolve19) => setTimeout(resolve19, ms));
741
+ return new Promise((resolve20) => setTimeout(resolve20, ms));
738
742
  }
739
743
  function base64url(bytes) {
740
744
  return bytes.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
@@ -1305,7 +1309,7 @@ function upsertWorktreeLaunchConfig(mainRepoRoot, worktreeName, appDir, opts) {
1305
1309
  let port = opts.port;
1306
1310
  if (opts.probePort) {
1307
1311
  const taken = new Set(
1308
- config.configurations.map((c) => Number(c?.port)).filter((p34) => Number.isInteger(p34) && p34 > 0)
1312
+ config.configurations.map((c) => Number(c?.port)).filter((p35) => Number.isInteger(p35) && p35 > 0)
1309
1313
  );
1310
1314
  for (let i = 0; i < WORKTREE_PORT_RANGE && taken.has(port); i++) {
1311
1315
  port = WORKTREE_PORT_MIN + (port - WORKTREE_PORT_MIN + 1) % WORKTREE_PORT_RANGE;
@@ -2328,18 +2332,18 @@ function ensureInstallReady(appDir) {
2328
2332
  const logPath = join9(sentinelDir, "install.log");
2329
2333
  if (existsSync9(errPath)) {
2330
2334
  const detail = readFileSync11(errPath, "utf-8").trimEnd();
2331
- const log28 = existsSync9(logPath) ? ` Full log: ${logPath}.` : "";
2335
+ const log29 = existsSync9(logPath) ? ` Full log: ${logPath}.` : "";
2332
2336
  throw new Refusal(
2333
- `Dependency install failed: ${detail}.${log28} Run \`npm install\` (or \`bun install\`) manually, then retry.`,
2337
+ `Dependency install failed: ${detail}.${log29} Run \`npm install\` (or \`bun install\`) manually, then retry.`,
2334
2338
  "install_failed",
2335
2339
  { action: { cwd: appDir, argv: [detectPackageManager(appDir), "install"] } }
2336
2340
  );
2337
2341
  }
2338
2342
  if (existsSync9(startedPath) && !existsSync9(donePath)) {
2339
2343
  if (!installWorkerAlive(sentinelDir)) {
2340
- const log28 = existsSync9(logPath) ? ` See what happened: ${logPath}.` : "";
2344
+ const log29 = existsSync9(logPath) ? ` See what happened: ${logPath}.` : "";
2341
2345
  throw new Refusal(
2342
- `The dependency install is no longer running and never finished.${log28} Run \`npm install\` (or \`bun install\`) manually, then retry.`,
2346
+ `The dependency install is no longer running and never finished.${log29} Run \`npm install\` (or \`bun install\`) manually, then retry.`,
2343
2347
  "install_failed",
2344
2348
  { action: { cwd: appDir, argv: [detectPackageManager(appDir), "install"] } }
2345
2349
  );
@@ -2488,7 +2492,7 @@ function lintSchema(schema) {
2488
2492
  const offendingRoles = Object.entries(schema.permissions).filter(([, perms]) => perms.read === true).map(([role]) => role);
2489
2493
  if (offendingRoles.length > 0) {
2490
2494
  const usesAware = Object.values(schema.permissions).some(
2491
- (p34) => typeof p34.read === "string" && VISIBILITY_AWARE.includes(p34.read)
2495
+ (p35) => typeof p35.read === "string" && VISIBILITY_AWARE.includes(p35.read)
2492
2496
  );
2493
2497
  if (!usesAware) {
2494
2498
  warnings.push(
@@ -2573,11 +2577,11 @@ import { createServer, Socket } from "net";
2573
2577
  var DEFAULT_PORT = 5173;
2574
2578
  function isPortListening(port, host, timeoutMs = 500) {
2575
2579
  const target2 = host === "0.0.0.0" || host === "::" ? "127.0.0.1" : host;
2576
- return new Promise((resolve19) => {
2580
+ return new Promise((resolve20) => {
2577
2581
  const socket = new Socket();
2578
2582
  const done = (listening) => {
2579
2583
  socket.destroy();
2580
- resolve19(listening);
2584
+ resolve20(listening);
2581
2585
  };
2582
2586
  socket.setTimeout(timeoutMs);
2583
2587
  socket.once("connect", () => done(true));
@@ -2590,7 +2594,7 @@ async function waitForPortListening(port, host, timeoutMs) {
2590
2594
  const deadline = Date.now() + timeoutMs;
2591
2595
  while (Date.now() < deadline) {
2592
2596
  if (await isPortListening(port, host)) return true;
2593
- await new Promise((resolve19) => setTimeout(resolve19, 250));
2597
+ await new Promise((resolve20) => setTimeout(resolve20, 250));
2594
2598
  }
2595
2599
  return false;
2596
2600
  }
@@ -3797,60 +3801,97 @@ import { defineCommand as defineCommand3 } from "citty";
3797
3801
  import { resolve as resolve12 } from "path";
3798
3802
 
3799
3803
  // src/cli/lib/edge-propagation.ts
3800
- var NOT_SERVING = ["Assets have not yet deployed", "No app configured"];
3804
+ import { request as httpRequest } from "http";
3805
+ import { request as httpsRequest } from "https";
3806
+
3807
+ // src/shared/app-routing.ts
3801
3808
  var RELEASE_STAMP_PATH = "/.well-known/deepspace/release.json";
3809
+ var SDK_RUN_WORKER_FIRST = [
3810
+ "/api/*",
3811
+ "/ws/*",
3812
+ "/internal/*",
3813
+ "/v1/*",
3814
+ "/_deepspace/*",
3815
+ "/llms.txt",
3816
+ "/llms-full.txt",
3817
+ "/.well-known/mcp",
3818
+ "/.well-known/mcp.json",
3819
+ "/.well-known/mcp/*"
3820
+ ];
3821
+ var DOCUMENTATION_PATHS = [
3822
+ "/_documentation",
3823
+ "/_documentation/*",
3824
+ "/_documentation-root",
3825
+ "/_documentation-root/*"
3826
+ ];
3827
+ var RESERVED_RUN_WORKER_FIRST = [
3828
+ ...SDK_RUN_WORKER_FIRST,
3829
+ ...DOCUMENTATION_PATHS
3830
+ ];
3831
+
3832
+ // src/cli/lib/edge-propagation.ts
3833
+ var CONSECUTIVE_MATCHES = 10;
3834
+ var MATCH_INTERVAL_MS = 250;
3802
3835
  async function waitForLiveRelease(url, releaseStamp, timeoutMs) {
3803
- return releaseStamp ? waitForReleaseStamp(url, releaseStamp, timeoutMs) : waitForEdgePropagation(url, timeoutMs);
3804
- }
3805
- async function waitForReleaseStamp(url, releaseStamp, timeoutMs) {
3836
+ if (!releaseStamp) return "unverifiable";
3806
3837
  const deadline = Date.now() + timeoutMs;
3807
3838
  let attempt = 0;
3808
3839
  let streak = 0;
3809
3840
  while (Date.now() < deadline) {
3810
3841
  attempt++;
3811
- try {
3812
- const probe = new URL(RELEASE_STAMP_PATH, url);
3813
- probe.searchParams.set("probe", `${Date.now()}-${attempt}`);
3814
- const response = await fetch(probe, { redirect: "manual" });
3815
- const body = await response.json().catch(() => null);
3816
- if (response.ok && body?.release === releaseStamp) {
3817
- streak++;
3818
- if (streak >= 3) return true;
3819
- await new Promise((resolve19) => setTimeout(resolve19, 1500));
3820
- continue;
3821
- }
3822
- streak = 0;
3823
- } catch {
3824
- streak = 0;
3842
+ const probe = new URL(RELEASE_STAMP_PATH, url);
3843
+ probe.searchParams.set("probe", `${Date.now()}-${attempt}`);
3844
+ if (await servesRelease(probe, releaseStamp)) {
3845
+ streak++;
3846
+ if (streak >= CONSECUTIVE_MATCHES) return "confirmed";
3847
+ await sleep2(MATCH_INTERVAL_MS);
3848
+ continue;
3825
3849
  }
3826
- const waitMs = Math.min(8e3, 1e3 * 2 ** (attempt - 1));
3827
- await new Promise((resolve19) => setTimeout(resolve19, waitMs));
3850
+ streak = 0;
3851
+ await sleep2(Math.min(4e3, 500 * 2 ** Math.min(attempt - 1, 3)));
3828
3852
  }
3829
- return false;
3853
+ return "unconfirmed";
3830
3854
  }
3831
- async function waitForEdgePropagation(url, timeoutMs) {
3832
- const deadline = Date.now() + timeoutMs;
3833
- let attempt = 0;
3834
- let streak = 0;
3835
- while (Date.now() < deadline) {
3836
- attempt++;
3837
- try {
3838
- const response = await fetch(url, { redirect: "manual" });
3839
- const body = await response.text();
3840
- if (!NOT_SERVING.some((marker) => body.includes(marker))) {
3841
- streak++;
3842
- if (streak >= 3) return true;
3843
- await new Promise((resolve19) => setTimeout(resolve19, 1500));
3844
- continue;
3845
- }
3846
- streak = 0;
3847
- } catch {
3848
- streak = 0;
3849
- }
3850
- const waitMs = Math.min(8e3, 1e3 * 2 ** (attempt - 1));
3851
- await new Promise((resolve19) => setTimeout(resolve19, waitMs));
3855
+ async function servesRelease(probe, releaseStamp) {
3856
+ const body = await getOnFreshConnection(probe);
3857
+ if (body === null) return false;
3858
+ try {
3859
+ return JSON.parse(body).release === releaseStamp;
3860
+ } catch {
3861
+ return false;
3852
3862
  }
3853
- return false;
3863
+ }
3864
+ function getOnFreshConnection(url, timeoutMs = 5e3) {
3865
+ const send = url.protocol === "http:" ? httpRequest : httpsRequest;
3866
+ return new Promise((resolve20) => {
3867
+ const request2 = send(
3868
+ url,
3869
+ { method: "GET", agent: false, timeout: timeoutMs, headers: { "cache-control": "no-cache" } },
3870
+ (response) => {
3871
+ if (response.statusCode !== 200) {
3872
+ response.resume();
3873
+ resolve20(null);
3874
+ return;
3875
+ }
3876
+ let body = "";
3877
+ response.setEncoding("utf8");
3878
+ response.on("data", (chunk) => {
3879
+ if (body.length < 4096) body += chunk;
3880
+ });
3881
+ response.on("end", () => resolve20(body));
3882
+ response.on("error", () => resolve20(null));
3883
+ }
3884
+ );
3885
+ request2.on("timeout", () => {
3886
+ request2.destroy();
3887
+ resolve20(null);
3888
+ });
3889
+ request2.on("error", () => resolve20(null));
3890
+ request2.end();
3891
+ });
3892
+ }
3893
+ function sleep2(ms) {
3894
+ return new Promise((resolve20) => setTimeout(resolve20, ms));
3854
3895
  }
3855
3896
 
3856
3897
  // src/shared/app-files.ts
@@ -3944,7 +3985,7 @@ function readDocumentationDeployManifest(appDir, clientDir) {
3944
3985
  return JSON.parse(readFileSync15(manifestPath, "utf8"));
3945
3986
  }
3946
3987
 
3947
- // src/cli/commands/migrate/app-migrations.ts
3988
+ // src/cli/commands/update/app-migrations.ts
3948
3989
  import {
3949
3990
  closeSync,
3950
3991
  constants,
@@ -3961,7 +4002,7 @@ import { join as join15 } from "path";
3961
4002
  // src/shared/protocol/app-migrations.ts
3962
4003
  var APP_MIGRATION_ID_RE = /^[a-z0-9][a-z0-9._-]{0,127}$/;
3963
4004
  var MAX_APP_MIGRATION_IDS = 256;
3964
- var CANONICAL_APP_IDENTITY_MIGRATION_ID = "2026-08-canonical-app-identity-wire";
4005
+ var WORKER_OWNED_NOT_FOUND_MIGRATION_ID = "2026-08-worker-owned-not-found";
3965
4006
  function validateAppMigrationIds(value) {
3966
4007
  if (!Array.isArray(value)) {
3967
4008
  return { valid: false, reason: "app migrations must be a JSON string array" };
@@ -3987,14 +4028,14 @@ function validateAppMigrationIds(value) {
3987
4028
  return { valid: true, ids };
3988
4029
  }
3989
4030
 
3990
- // src/cli/commands/migrate/app-migrations.ts
4031
+ // src/cli/commands/update/app-migrations.ts
3991
4032
  var APP_MIGRATIONS_MANIFEST = "deepspace.migrations.json";
3992
4033
  var SOURCE_MIGRATIONS = [
3993
4034
  {
3994
- id: CANONICAL_APP_IDENTITY_MIGRATION_ID,
3995
- description: "Use the immutable app id for authenticated platform requests",
3996
- transform: migrateCanonicalIdentityWire,
3997
- findBlockers: findLegacyIdentityWireBlockers
4035
+ id: WORKER_OWNED_NOT_FOUND_MIGRATION_ID,
4036
+ description: "Let the worker answer misses, so deleted files 404 instead of serving HTML",
4037
+ transform: migrateWorkerOwnedNotFound,
4038
+ findBlockers: findWorkerOwnedNotFoundBlockers
3998
4039
  }
3999
4040
  ];
4000
4041
  var APP_MIGRATION_DEFINITIONS = SOURCE_MIGRATIONS.map(
@@ -4039,7 +4080,7 @@ function planAppMigrations(appDir) {
4039
4080
  const before = readRegularFile(absolutePath);
4040
4081
  let after = before;
4041
4082
  for (const migration of pendingMigrations) {
4042
- const result = migration.transform(after);
4083
+ const result = migration.transform(after, repoPath);
4043
4084
  after = result.source;
4044
4085
  if (result.replacements > 0) {
4045
4086
  const planned = perMigration.get(migration.id) ?? {
@@ -4076,9 +4117,6 @@ function planAppMigrations(appDir) {
4076
4117
  };
4077
4118
  }
4078
4119
  function applyAppMigrationPlan(appDir, plan) {
4079
- if (plan.blockers.length > 0) {
4080
- throw new Error("Cannot apply an app migration plan with unresolved blockers.");
4081
- }
4082
4120
  const repo = repoToplevel(appDir);
4083
4121
  for (const edit of plan.edits) {
4084
4122
  const absolutePath = join15(repo, edit.path);
@@ -4138,119 +4176,78 @@ function writeRegularFile(path, before, after) {
4138
4176
  }
4139
4177
  function isSourcePath(repoPath, appPrefix) {
4140
4178
  if (appPrefix && !repoPath.startsWith(appPrefix)) return false;
4179
+ if (repoPath.endsWith("wrangler.toml")) return true;
4141
4180
  return /\.(?:[cm]?[jt]sx?)$/.test(repoPath);
4142
4181
  }
4143
- function migrateCanonicalIdentityWire(source) {
4182
+ function migrateWorkerOwnedNotFound(source, file) {
4144
4183
  let replacements = 0;
4145
- const lines = source.split("\n");
4146
- for (let index = 0; index < lines.length; index++) {
4147
- let line = lines[index];
4148
- line = line.replace(
4149
- /(\.set\(\s*)(['"])x-app-name\2(\s*,\s*)([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)\.APP_NAME(\s*\))/g,
4150
- (_match, open, quote, separator, receiver, close) => {
4151
- replacements++;
4152
- return `${open}${quote}x-app-id${quote}${separator}${receiver}.DEEPSPACE_APP_ID${close}`;
4153
- }
4154
- );
4155
- line = line.replace(
4156
- /(['"])x-app-name\1(\s*:\s*)([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)\.APP_NAME/g,
4157
- (_match, quote, separator, receiver) => {
4158
- replacements++;
4159
- return `${quote}x-app-id${quote}${separator}${receiver}.DEEPSPACE_APP_ID`;
4160
- }
4161
- );
4162
- line = line.replace(
4163
- /(\[\s*)(['"])x-app-name\2(\s*\]\s*=\s*)([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)\.APP_NAME/g,
4164
- (_match, open, quote, separator, receiver) => {
4165
- replacements++;
4166
- return `${open}${quote}x-app-id${quote}${separator}${receiver}.DEEPSPACE_APP_ID`;
4167
- }
4168
- );
4169
- line = line.replace(
4170
- /(forwardedParams\.set\(\s*)(['"])appName\2(\s*,\s*)([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)\.APP_NAME(\s*\))/g,
4171
- (_match, open, quote, separator, receiver, close) => {
4172
- replacements++;
4173
- return `${open}${quote}appId${quote}${separator}${receiver}.DEEPSPACE_APP_ID${close}`;
4174
- }
4175
- );
4176
- lines[index] = line;
4177
- }
4178
- if (replacements === 0) return { source, replacements };
4179
- let migrated = lines.join("\n");
4180
- if (/forwardedParams\.set\(\s*['"]appId['"]/.test(migrated)) {
4181
- migrated = migrated.replaceAll("injectAppName", "injectAppId").replaceAll("`?appName=...`", "`?appId=...`").replaceAll("Inject appName into the query string", "Inject appId into the query string").replaceAll("caller-supplied appName", "caller-supplied appId").replaceAll(
4182
- "/_deepspace/subscriptions/plans?appName=",
4183
- "/_deepspace/subscriptions/plans?appId="
4184
- );
4184
+ if (file.endsWith("wrangler.toml")) {
4185
+ const next = source.replace(/not_found_handling\s*=\s*"single-page-application"/g, () => {
4186
+ replacements++;
4187
+ return 'not_found_handling = "none"';
4188
+ });
4189
+ return { source: next, replacements };
4185
4190
  }
4186
- migrated = migrated.replaceAll(
4187
- "APP_IDENTITY_TOKEN + APP_NAME",
4188
- "APP_IDENTITY_TOKEN + DEEPSPACE_APP_ID"
4191
+ const retargeted = source.replace(
4192
+ /(url\.pathname\s*=\s*)(['"])\/index\.html\2/g,
4193
+ (_match, assignment, quote) => {
4194
+ replacements++;
4195
+ return `${assignment}${quote}/${quote}`;
4196
+ }
4189
4197
  );
4190
- return {
4191
- source: ensureCanonicalIdentityProperties(migrated),
4192
- replacements
4193
- };
4198
+ const guarded = insertFileMissGuard(retargeted);
4199
+ return { source: guarded.source, replacements: replacements + guarded.replacements };
4194
4200
  }
4195
- function ensureCanonicalIdentityProperties(source) {
4201
+ var SHELL_FALLBACK_LINE = /^(\s*)url\.pathname\s*=\s*(['"])\/\2\s*$/;
4202
+ var GUARD_LOOKBACK_LINES = 8;
4203
+ function insertFileMissGuard(source) {
4196
4204
  const lines = source.split("\n");
4205
+ let replacements = 0;
4197
4206
  for (let index = lines.length - 1; index >= 0; index--) {
4198
- const match = /^(\s*)(?:readonly\s+)?APP_NAME\??\s*:\s*string\s*([;,]?)\s*$/.exec(lines[index]);
4199
- if (!match || blockAlreadyDeclaresCanonicalId(lines, index, match[1].length)) continue;
4200
- const suffix = match[2] ?? "";
4201
- lines.splice(index + 1, 0, `${match[1]}DEEPSPACE_APP_ID: string${suffix}`);
4207
+ const match = SHELL_FALLBACK_LINE.exec(lines[index]);
4208
+ if (!match || hasFileMissGuard(lines, index)) continue;
4209
+ const indent = match[1];
4210
+ lines.splice(
4211
+ index,
4212
+ 0,
4213
+ `${indent}// A FILE, not a client route: a miss must 404. Returning the shell here`,
4214
+ `${indent}// is HTML parsed as JavaScript, which is a blank page.`,
4215
+ `${indent}if (${FILE_MISS_TEST}) {`,
4216
+ `${indent} return c.json({ error: 'not_found' }, 404)`,
4217
+ `${indent}}`
4218
+ );
4219
+ replacements++;
4202
4220
  }
4203
- return lines.join("\n");
4221
+ return { source: lines.join("\n"), replacements };
4204
4222
  }
4205
- function blockAlreadyDeclaresCanonicalId(lines, propertyIndex, propertyIndent) {
4206
- for (const direction of [-1, 1]) {
4207
- for (let index = propertyIndex + direction; index >= 0 && index < lines.length; index += direction) {
4208
- const line = lines[index];
4209
- if (/^\s*(?:readonly\s+)?DEEPSPACE_APP_ID\??\s*:/.test(line)) return true;
4210
- const trimmed = line.trimStart();
4211
- if (!trimmed) continue;
4212
- const indent = line.length - trimmed.length;
4213
- if (indent < propertyIndent && (trimmed.includes("{") || trimmed.startsWith("}"))) break;
4214
- }
4215
- }
4216
- return false;
4223
+ var FILE_MISS_TEST = "url.pathname.slice(url.pathname.lastIndexOf('/') + 1).includes('.')";
4224
+ function hasFileMissGuard(lines, fallbackIndex) {
4225
+ const start = Math.max(0, fallbackIndex - GUARD_LOOKBACK_LINES);
4226
+ return lines.slice(start, fallbackIndex).some((line) => line.includes(FILE_MISS_TEST) || /\bnamesAFile\s*\(/.test(line));
4217
4227
  }
4218
- function findLegacyIdentityWireBlockers(source, file) {
4228
+ function findWorkerOwnedNotFoundBlockers(source, file) {
4229
+ if (file.endsWith("wrangler.toml")) return [];
4230
+ if (!source.includes("ASSETS")) return [];
4219
4231
  const blockers = [];
4220
- for (const [index, line] of source.split("\n").entries()) {
4221
- const trimmed = line.trimStart();
4222
- if (!/['"]x-app-name['"]/.test(line) || /\.delete\(\s*['"]x-app-name['"]/.test(line) || trimmed.startsWith("//") || trimmed.startsWith("/*") || trimmed.startsWith("*")) {
4223
- continue;
4224
- }
4232
+ const lines = source.split("\n");
4233
+ for (const [index, line] of lines.entries()) {
4234
+ if (!/['"`]\/index\.html/.test(line)) continue;
4225
4235
  blockers.push({
4226
- migrationId: CANONICAL_APP_IDENTITY_MIGRATION_ID,
4236
+ migrationId: WORKER_OWNED_NOT_FOUND_MIGRATION_ID,
4227
4237
  file,
4228
4238
  line: index + 1,
4229
- message: "This app still sends the removed x-app-name identity header in a form the safe migration cannot rewrite."
4239
+ message: `This worker still asks the asset layer for "/index.html". Deploys now set not_found_handling = "none", and "/index.html" redirects to "/" \u2014 the browser follows it and loses the path it asked for. Fetch "/" instead, and 404 first when the last path segment contains a "." (a file, not a client route).`
4230
4240
  });
4231
4241
  }
4232
4242
  return blockers;
4233
4243
  }
4234
4244
 
4235
4245
  // src/cli/commands/deploy/build.ts
4236
- var RESERVED_RUN_WORKER_FIRST = /* @__PURE__ */ new Set([
4237
- "/api/*",
4238
- "/ws/*",
4239
- "/internal/*",
4240
- "/v1/*",
4241
- "/_deepspace/*",
4242
- "/_documentation",
4243
- "/_documentation/*",
4244
- "/_documentation-root",
4245
- "/_documentation-root/*",
4246
- // Agent-protocol paths: the worker must see them so they cannot fall through
4247
- // to the SPA shell (templates/base/src/server/http-routes.ts reserves them).
4248
- "/llms.txt",
4249
- "/llms-full.txt",
4250
- "/.well-known/mcp",
4251
- "/.well-known/mcp.json",
4252
- "/.well-known/mcp/*"
4253
- ]);
4246
+ var RESERVED_RUN_WORKER_FIRST2 = new Set(RESERVED_RUN_WORKER_FIRST);
4247
+ function declaredCompatibilityDate(config) {
4248
+ const declared = config.compatibility_date;
4249
+ return typeof declared === "string" && /^\d{4}-\d{2}-\d{2}$/.test(declared) ? declared : null;
4250
+ }
4254
4251
  async function buildDeployBundle(options) {
4255
4252
  const { appDir, appName, envName, sharedDevVarsCache, output, spinner: spinner2 } = options;
4256
4253
  const junk = removeMacosJunk(appDir);
@@ -4362,7 +4359,10 @@ async function buildDeployBundle(options) {
4362
4359
  appMigrations,
4363
4360
  doManifest,
4364
4361
  customBindings,
4365
- extraRoutes
4362
+ extraRoutes,
4363
+ compatibilityDate: declaredCompatibilityDate(outputConfig),
4364
+ compatibilityFlags: Array.isArray(outputConfig.compatibility_flags) ? outputConfig.compatibility_flags.filter((flag) => typeof flag === "string") : [],
4365
+ notFoundHandling: typeof outputConfig.assets?.not_found_handling === "string" ? outputConfig.assets.not_found_handling : null
4366
4366
  };
4367
4367
  }
4368
4368
  function extractRunWorkerFirst(config) {
@@ -4372,7 +4372,7 @@ function extractRunWorkerFirst(config) {
4372
4372
  for (const entry of raw) {
4373
4373
  if (typeof entry !== "string") continue;
4374
4374
  const route = entry.trim();
4375
- if (!route.startsWith("/") || RESERVED_RUN_WORKER_FIRST.has(route) || seen.has(route)) continue;
4375
+ if (!route.startsWith("/") || RESERVED_RUN_WORKER_FIRST2.has(route) || seen.has(route)) continue;
4376
4376
  seen.add(route);
4377
4377
  routes.push(route);
4378
4378
  }
@@ -5418,7 +5418,7 @@ async function pushWithTransientRetry(push) {
5418
5418
  const message = errorMessage3(error);
5419
5419
  const transient = /(?:HTTP |error: )(?:429|503)\b/i.test(message);
5420
5420
  if (transient && attempt < backoffMs.length && Date.now() + backoffMs[attempt] < deadline) {
5421
- await new Promise((resolve19) => setTimeout(resolve19, backoffMs[attempt]));
5421
+ await new Promise((resolve20) => setTimeout(resolve20, backoffMs[attempt]));
5422
5422
  continue;
5423
5423
  }
5424
5424
  throw error;
@@ -5510,6 +5510,15 @@ async function deployBuiltBundle(options) {
5510
5510
  if (Object.keys(bundle.assetConfig).length) {
5511
5511
  form.append("assetConfig", JSON.stringify(bundle.assetConfig));
5512
5512
  }
5513
+ if (bundle.compatibilityDate) {
5514
+ form.append("compatibilityDate", bundle.compatibilityDate);
5515
+ }
5516
+ if (bundle.compatibilityFlags.length) {
5517
+ form.append("compatibilityFlags", JSON.stringify(bundle.compatibilityFlags));
5518
+ }
5519
+ if (bundle.notFoundHandling) {
5520
+ form.append("notFoundHandling", bundle.notFoundHandling);
5521
+ }
5513
5522
  form.append("name", appName);
5514
5523
  if (confirmRename) form.append("confirmRename", "true");
5515
5524
  if (shouldSendLineage(repository.commitOid, repository.recoverable)) {
@@ -5763,7 +5772,7 @@ async function postWithRetry(url, makeInit, {
5763
5772
  lastError = error;
5764
5773
  }
5765
5774
  if (attempt < attempts) {
5766
- await new Promise((resolve19) => setTimeout(resolve19, Math.min(8e3, 500 * 2 ** (attempt - 1))));
5775
+ await new Promise((resolve20) => setTimeout(resolve20, Math.min(8e3, 500 * 2 ** (attempt - 1))));
5767
5776
  }
5768
5777
  }
5769
5778
  if (lastResponse) return lastResponse;
@@ -5883,46 +5892,6 @@ function errorMessage5(error) {
5883
5892
  return error instanceof Error ? error.message : String(error);
5884
5893
  }
5885
5894
 
5886
- // src/cli/commands/migrate/legacy-app-id.ts
5887
- var LEGACY_APP_ID_RE = /^[a-z0-9][a-z0-9_-]{0,63}$/;
5888
- function readLegacyAppId(cwd = process.cwd(), wranglerEnv) {
5889
- const cfg = readWranglerIdentityConfig(cwd);
5890
- if (!cfg) return null;
5891
- const slot = wranglerEnv ? cfg.env?.[wranglerEnv] : cfg;
5892
- if (!slot) return null;
5893
- const declaredId = slot.vars?.DEEPSPACE_APP_ID;
5894
- if (declaredId !== void 0) {
5895
- if (typeof declaredId === "string" && LEGACY_APP_ID_RE.test(declaredId)) return declaredId;
5896
- throw new InputError(
5897
- "DEEPSPACE_APP_ID is present but invalid; correct it before migrating.",
5898
- "invalid_app_id"
5899
- );
5900
- }
5901
- const appName = typeof slot.vars?.APP_NAME === "string" ? slot.vars.APP_NAME : null;
5902
- const workerName = typeof slot.name === "string" ? slot.name : null;
5903
- if (!appName) return null;
5904
- if (!workerName) {
5905
- throw new InputError(
5906
- "Legacy migration requires both Worker name and APP_NAME to be present and identical.",
5907
- "ambiguous_legacy_app_id"
5908
- );
5909
- }
5910
- if (appName !== workerName) {
5911
- throw new InputError(
5912
- `Legacy APP_NAME "${appName}" does not match Worker name "${workerName}"; migration cannot choose safely.`,
5913
- "ambiguous_legacy_app_id"
5914
- );
5915
- }
5916
- return !APP_ID_RE.test(appName) && LEGACY_APP_ID_RE.test(appName) ? appName : null;
5917
- }
5918
- function legacyDeployRefusal(appDir, wranglerEnv) {
5919
- const legacyAppId = readLegacyAppId(appDir, wranglerEnv);
5920
- return legacyAppId ? {
5921
- error: `This checkout still uses legacy app identity ${legacyAppId}. Run \`deepspace app migrate --dry-run\`, then complete \`deepspace app migrate\` before deploying.`,
5922
- code: "legacy_app_migration_required"
5923
- } : null;
5924
- }
5925
-
5926
5895
  // src/cli/commands/deploy.ts
5927
5896
  var DEPLOY_URL3 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
5928
5897
  var deploy_default = defineCommand3({
@@ -6006,10 +5975,6 @@ var deploy_default = defineCommand3({
6006
5975
  }
6007
5976
  const appName = nameResult.name;
6008
5977
  let appId = readAppId(appDir, envName);
6009
- if (!appId) {
6010
- const refusal = legacyDeployRefusal(appDir, envName);
6011
- if (refusal) output.die(refusal.error, refusal.code);
6012
- }
6013
5978
  p12.log.info(envName ? `App: ${appName} (env: ${envName})` : `App: ${appName}`);
6014
5979
  const { token, ownerId } = await authenticate(appDir, output);
6015
5980
  if (!appId) {
@@ -6075,15 +6040,18 @@ var deploy_default = defineCommand3({
6075
6040
  output,
6076
6041
  spinner: spinner2
6077
6042
  });
6043
+ let serving = "unverifiable";
6078
6044
  if (body.url) {
6079
- spinner2.message("Waiting for edge propagation...");
6080
- const ready = await waitForLiveRelease(body.url, body.releaseStamp, 9e4);
6081
- if (!ready) {
6082
- spinner2.stop("Deployed (edge propagation still in progress after 90s)");
6045
+ spinner2.message("Waiting for the edge to serve this release...");
6046
+ serving = await waitForLiveRelease(body.url, body.releaseStamp, 9e4);
6047
+ if (serving !== "confirmed") {
6048
+ spinner2.stop(
6049
+ serving === "unconfirmed" ? "Deployed \u2014 the edge is still rolling this release out" : "Deployed \u2014 serving could not be verified from here"
6050
+ );
6083
6051
  p12.log.warn(
6084
- "First requests may briefly hit the assets transitional page; retry in a minute."
6052
+ serving === "unconfirmed" ? "Some requests still get the previous release. Cloudflare rolls a version out per edge machine; it usually settles within a couple of minutes. Wait before asserting against it." : "This release carries no serving stamp (older platform, or a resumed deploy), so the CLI cannot tell which release the edge answers with."
6085
6053
  );
6086
- p12.log.info(`URL: ${body.url} (deploy accepted; serving not yet verified from here)`);
6054
+ p12.log.info(`URL: ${body.url}`);
6087
6055
  await syncCommerce(appDir, appId, token, output.nonInteractive);
6088
6056
  if (output.json) {
6089
6057
  return output.emitJson({
@@ -6093,7 +6061,10 @@ var deploy_default = defineCommand3({
6093
6061
  url: body.url,
6094
6062
  releaseId: body.releaseId ?? null,
6095
6063
  bundleRetained: body.bundleRetained ?? null,
6096
- edgePropagating: true,
6064
+ // `serving` says what was actually established; `edgePropagating`
6065
+ // stays for callers written against the old shape.
6066
+ serving,
6067
+ edgePropagating: serving === "unconfirmed",
6097
6068
  recoverable: repository.recoverable,
6098
6069
  ...staleBaseGuardFields(body)
6099
6070
  });
@@ -6111,6 +6082,9 @@ var deploy_default = defineCommand3({
6111
6082
  appId,
6112
6083
  appName,
6113
6084
  url: body.url ?? null,
6085
+ // Always present, so a caller branches on one field instead of
6086
+ // inferring success from the absence of `edgePropagating`.
6087
+ serving,
6114
6088
  releaseId: body.releaseId ?? null,
6115
6089
  bundleRetained: body.bundleRetained ?? null,
6116
6090
  recoverable: repository.recoverable,
@@ -6565,7 +6539,7 @@ function formatEvent(e, color, now = Date.now()) {
6565
6539
  const client = e.source === "client" ? paint("35", "CLIENT", color) + " " : "";
6566
6540
  return `${time} ${client}${tag} ${body}`;
6567
6541
  }
6568
- var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
6542
+ var sleep3 = (ms) => new Promise((r) => setTimeout(r, ms));
6569
6543
  var logs_default = defineCommand4({
6570
6544
  meta: {
6571
6545
  name: "logs",
@@ -6685,7 +6659,7 @@ var logs_default = defineCommand4({
6685
6659
  }
6686
6660
  }
6687
6661
  for (; ; ) {
6688
- await sleep2(backoff || interval);
6662
+ await sleep3(backoff || interval);
6689
6663
  const freshToken = await ensureToken();
6690
6664
  let page;
6691
6665
  try {
@@ -7855,11 +7829,11 @@ function fmtDate(iso) {
7855
7829
  }
7856
7830
  async function confirm4(message) {
7857
7831
  process.stdout.write(`${message} [y/N] `);
7858
- return new Promise((resolve19) => {
7832
+ return new Promise((resolve20) => {
7859
7833
  process.stdin.setEncoding("utf-8");
7860
7834
  process.stdin.once("data", (data) => {
7861
7835
  const answer = data.toString().trim().toLowerCase();
7862
- resolve19(answer === "y" || answer === "yes");
7836
+ resolve20(answer === "y" || answer === "yes");
7863
7837
  });
7864
7838
  });
7865
7839
  }
@@ -8211,8 +8185,14 @@ async function request(baseUrl, token, path, init = {}) {
8211
8185
  if (res.ok) return res;
8212
8186
  const text2 = await res.text();
8213
8187
  let code;
8188
+ let details;
8214
8189
  try {
8215
- code = JSON.parse(text2.trim()).code;
8190
+ const body = JSON.parse(text2.trim());
8191
+ code = typeof body.code === "string" ? body.code : void 0;
8192
+ const rest = Object.fromEntries(
8193
+ Object.entries(body).filter(([key]) => key !== "error" && key !== "code")
8194
+ );
8195
+ if (Object.keys(rest).length > 0) details = rest;
8216
8196
  } catch {
8217
8197
  }
8218
8198
  throw new ApiError(
@@ -8222,7 +8202,8 @@ async function request(baseUrl, token, path, init = {}) {
8222
8202
  // oversized request short is not a different failure from the worker
8223
8203
  // refusing it, and two codes made it look like one.
8224
8204
  code ?? (res.status === 413 ? "too_large" : res.status >= 500 ? "server_error" : "http_error"),
8225
- path
8205
+ path,
8206
+ details
8226
8207
  );
8227
8208
  }
8228
8209
  async function readJson(res, path) {
@@ -8585,8 +8566,8 @@ var list4 = defineDeepspaceCommand({
8585
8566
  }
8586
8567
  if (pending.length) {
8587
8568
  console.log(`PENDING INVITES ON ${app2}`);
8588
- for (const p34 of pending) {
8589
- console.log(` ${p34.email} (expires ${new Date(p34.expiresAt).toLocaleDateString()})`);
8569
+ for (const p35 of pending) {
8570
+ console.log(` ${p35.email} (expires ${new Date(p35.expiresAt).toLocaleDateString()})`);
8590
8571
  }
8591
8572
  }
8592
8573
  }
@@ -8684,7 +8665,7 @@ var cancel3 = defineDeepspaceCommand({
8684
8665
  `/api/app-collaborators/${encodeURIComponent(app2)}`
8685
8666
  );
8686
8667
  const target2 = email.trim().toLowerCase();
8687
- const found = pending.find((p34) => p34.email.toLowerCase() === target2);
8668
+ const found = pending.find((p35) => p35.email.toLowerCase() === target2);
8688
8669
  if (!found) {
8689
8670
  throw new Refusal(`${email} has no pending invite on ${app2}`, "no_pending_invite", {
8690
8671
  action: cliAction("deepspace", "app", "collaborators", "list", "--app", app2)
@@ -8742,7 +8723,10 @@ var accept = defineDeepspaceCommand({
8742
8723
  }
8743
8724
  return {
8744
8725
  data: { ...res },
8745
- action: cliAction("deepspace", "pull", "--app", res.appId)
8726
+ // `pull` fast-forwards a checkout this caller does not have yet — the
8727
+ // invite just granted access, so there is nothing local to update.
8728
+ // `clone` is the step that actually works from here.
8729
+ action: cliAction("deepspace", "clone", res.appId)
8746
8730
  };
8747
8731
  }
8748
8732
  });
@@ -11032,13 +11016,14 @@ Re-run with --allow-do-deletion to accept the data loss.`,
11032
11016
  "invalid_response"
11033
11017
  );
11034
11018
  }
11035
- let edgePropagating = false;
11019
+ let serving = "unverifiable";
11036
11020
  if (result.url) {
11037
- spinner2?.message("Waiting for edge propagation...");
11038
- edgePropagating = !await waitForLiveRelease(result.url, result.releaseStamp, 9e4);
11021
+ spinner2?.message("Waiting for the edge to serve this release...");
11022
+ serving = await waitForLiveRelease(result.url, result.releaseStamp, 9e4);
11039
11023
  }
11024
+ const edgePropagating = serving === "unconfirmed";
11040
11025
  spinner2?.stop(
11041
- edgePropagating ? `Rolled back to ${result.rolledBackTo ?? releaseId} (edge propagation still in progress after 90s).` : `Rolled back to ${result.rolledBackTo ?? releaseId}.`
11026
+ serving === "confirmed" ? `Rolled back to ${result.rolledBackTo ?? releaseId}.` : serving === "unconfirmed" ? `Rolled back to ${result.rolledBackTo ?? releaseId} \u2014 the edge is still rolling it out.` : `Rolled back to ${result.rolledBackTo ?? releaseId} \u2014 serving could not be verified from here.`
11042
11027
  );
11043
11028
  const doClassGuard = result.doClassGuard === "unverified" ? "unverified" : void 0;
11044
11029
  if (!args.json) {
@@ -11069,6 +11054,8 @@ Re-run with --allow-do-deletion to accept the data loss.`,
11069
11054
  url: result.url ?? null,
11070
11055
  versionId: result.versionId ?? null,
11071
11056
  wranglerEnv: wranglerEnv ?? null,
11057
+ // Always present, matching `deploy`: one field to branch on.
11058
+ serving,
11072
11059
  ...edgePropagating ? { edgePropagating: true } : {},
11073
11060
  ...doClassGuard ? { doClassGuard } : {}
11074
11061
  }
@@ -13072,541 +13059,183 @@ function sameRefs(left, right) {
13072
13059
  return left.every((ref) => expected.get(ref.name) === ref.oid);
13073
13060
  }
13074
13061
 
13075
- // src/cli/commands/migrate/identity-api.ts
13076
- async function getIdentityMigration(deployUrl, token, appId) {
13077
- try {
13078
- const result = await apiFetch(
13079
- deployUrl,
13080
- token,
13081
- `/api/apps/${encodeURIComponent(appId)}/identity-migration`
13082
- );
13083
- return result.migration;
13084
- } catch (error) {
13085
- if (error instanceof ApiError && error.code === "migration_not_found") return null;
13086
- throw error;
13087
- }
13088
- }
13089
- async function prepareIdentityMigration(deployUrl, token, legacyAppId, input) {
13090
- const result = await apiFetch(
13091
- deployUrl,
13092
- token,
13093
- `/api/apps/${encodeURIComponent(legacyAppId)}/identity-migration/prepare`,
13094
- { method: "POST", body: JSON.stringify(input) }
13095
- );
13096
- return result.migration;
13097
- }
13098
- async function inspectIdentityMigration(deployUrl, token, legacyAppId, input) {
13099
- const result = await apiFetch(
13100
- deployUrl,
13101
- token,
13102
- `/api/apps/${encodeURIComponent(legacyAppId)}/identity-migration/inspect`,
13103
- { method: "POST", body: JSON.stringify(input) }
13062
+ // src/cli/commands/update.ts
13063
+ import * as p34 from "@clack/prompts";
13064
+ import { existsSync as existsSync26, readFileSync as readFileSync28, writeFileSync as writeFileSync10 } from "fs";
13065
+ import { join as join24, resolve as resolve19 } from "path";
13066
+ var REGISTRY = "https://registry.npmjs.org/deepspace/latest";
13067
+ var DOCS_URL = "https://documentation.deep.space/cli-reference/commands";
13068
+ var OLDEST_SUPPORTED_MINOR = 12;
13069
+ function parseVersion(raw) {
13070
+ const match = /^(\d+)\.(\d+)\.(\d+)/.exec(raw.trim().replace(/^[\^~]/, ""));
13071
+ if (!match) return null;
13072
+ return { major: Number(match[1]), minor: Number(match[2]), patch: Number(match[3]) };
13073
+ }
13074
+ function gapIsTooWide(from, to) {
13075
+ if (from.major !== to.major) return true;
13076
+ return from.major === 0 && from.minor < OLDEST_SUPPORTED_MINOR;
13077
+ }
13078
+ function readAppSdkVersion(appDir) {
13079
+ const installed = join24(appDir, "node_modules", "deepspace", "package.json");
13080
+ if (existsSync26(installed)) {
13081
+ const parsed2 = JSON.parse(readFileSync28(installed, "utf8"));
13082
+ if (typeof parsed2.version === "string") return parsed2.version;
13083
+ }
13084
+ const manifest = join24(appDir, "package.json");
13085
+ if (!existsSync26(manifest)) return null;
13086
+ const parsed = JSON.parse(readFileSync28(manifest, "utf8"));
13087
+ return parsed.dependencies?.deepspace ?? null;
13088
+ }
13089
+ function pinSdkVersion(appDir, version) {
13090
+ const path = join24(appDir, "package.json");
13091
+ const source = readFileSync28(path, "utf8");
13092
+ const updated = source.replace(
13093
+ /("deepspace"\s*:\s*")[^"]+(")/,
13094
+ (_match, head, tail) => `${head}^${version}${tail}`
13104
13095
  );
13105
- return result.inventory;
13106
- }
13107
- async function commitIdentityMigration(deployUrl, token, appId, input) {
13108
- const result = await apiFetch(
13109
- deployUrl,
13110
- token,
13111
- `/api/apps/${encodeURIComponent(appId)}/identity-migration/commit`,
13112
- { method: "POST", body: JSON.stringify(input) }
13113
- );
13114
- return result.migration;
13096
+ if (updated === source) return false;
13097
+ writeFileSync10(path, updated);
13098
+ return true;
13115
13099
  }
13116
- async function cancelIdentityMigration(deployUrl, token, appId) {
13117
- await apiFetch(
13118
- deployUrl,
13119
- token,
13120
- `/api/apps/${encodeURIComponent(appId)}/identity-migration`,
13121
- { method: "DELETE" }
13100
+ async function latestPublishedVersion() {
13101
+ const response = await fetch(REGISTRY, { signal: AbortSignal.timeout(15e3) }).catch(
13102
+ (error) => {
13103
+ throw new Refusal(
13104
+ `Could not reach the npm registry to find the latest version: ${error instanceof Error ? error.message : String(error)}. Pass --to <version> to update without a lookup.`,
13105
+ "registry_unreachable"
13106
+ );
13107
+ }
13122
13108
  );
13109
+ if (!response.ok) {
13110
+ throw new Refusal(
13111
+ `The npm registry answered ${response.status} for the latest deepspace version. Pass --to <version> to update without a lookup.`,
13112
+ "registry_unreachable"
13113
+ );
13114
+ }
13115
+ const body = await response.json();
13116
+ if (typeof body.version !== "string") {
13117
+ throw new Refusal("The npm registry returned no version for deepspace.", "registry_unreachable");
13118
+ }
13119
+ return body.version;
13123
13120
  }
13124
- async function verifyIdentityMigration(deployUrl, token, appId) {
13125
- const result = await apiFetch(
13126
- deployUrl,
13127
- token,
13128
- `/api/apps/${encodeURIComponent(appId)}/identity-migration/verify`,
13129
- { method: "POST" }
13130
- );
13131
- return result.migration;
13121
+ function reportPlan(plan, applied) {
13122
+ for (const migration of plan.pending) {
13123
+ const verb = applied ? "Applied" : "Would apply";
13124
+ p34.log.success(
13125
+ `${verb}: ${migration.description} \u2014 ${migration.replacements} change(s) in ${migration.files.join(", ")}`
13126
+ );
13127
+ }
13128
+ if (plan.blockers.length) {
13129
+ p34.log.warn(
13130
+ `${plan.blockers.length} change(s) could not be made for you \u2014 this file differs from the version the change was written against:`
13131
+ );
13132
+ for (const blocker of plan.blockers) {
13133
+ p34.log.message(` ${blocker.file}:${blocker.line}
13134
+ ${blocker.message}`);
13135
+ }
13136
+ }
13132
13137
  }
13133
-
13134
- // src/cli/commands/migrate.ts
13135
- var STRICT_APP_ID_RE2 = /^app_[0-9A-HJKMNP-TV-Z]{26}$/;
13136
- var migrate_default = defineDeepspaceCommand({
13138
+ var update_default = defineDeepspaceCommand({
13137
13139
  meta: {
13138
- name: "migrate",
13139
- description: "Apply pending DeepSpace app migrations without moving retained data"
13140
+ name: "update",
13141
+ description: "Move this app onto the current SDK and apply the source changes it requires"
13140
13142
  },
13141
13143
  args: {
13142
- remote: {
13144
+ to: {
13143
13145
  type: "string",
13144
- description: "GitHub remote name when it cannot be inferred (default: origin)",
13145
- required: false
13146
- },
13147
- env: {
13148
- type: "string",
13149
- alias: "e",
13150
- description: "wrangler.toml environment whose app id should be migrated",
13146
+ description: "Target version (default: the latest published deepspace)",
13151
13147
  required: false
13152
13148
  },
13153
13149
  "dry-run": {
13154
13150
  type: "boolean",
13155
- description: "Inspect prerequisites and show the migration without changing anything",
13156
- default: false
13157
- },
13158
- cancel: {
13159
- type: "boolean",
13160
- description: "Cancel a prepared migration after restoring and publishing the legacy id",
13151
+ description: "Report what would change without touching the checkout",
13161
13152
  default: false
13162
13153
  }
13163
13154
  },
13164
13155
  async run({ args }) {
13165
- const recoveryMode = args.cancel === true ? "cancel" : null;
13166
- const exclusiveModes = [args["dry-run"], args.cancel].filter(Boolean).length;
13167
- if (exclusiveModes > 1) {
13168
- throw new Refusal("Use only one of --dry-run or --cancel.", "conflicting_migration_mode");
13169
- }
13170
- const appDir = findAppDir();
13171
- if (!appDir) {
13172
- throw new Refusal("Run this command from a DeepSpace app Git checkout.", "not_in_app_repo");
13173
- }
13174
- assertSyncableRepo(appDir);
13175
- const envName = typeof args.env === "string" && args.env ? args.env : void 0;
13176
- const currentAppId = readAppId(appDir, envName) ?? readLegacyAppId(appDir, envName);
13177
- if (!currentAppId) {
13156
+ const appDir = resolve19(".");
13157
+ if (!hasWranglerConfig(appDir)) {
13178
13158
  throw new Refusal(
13179
- "wrangler.toml has no unambiguous DeepSpace app identity for this environment.",
13180
- "missing_app_id"
13159
+ "No wrangler.toml found \u2014 run this from inside a DeepSpace app directory.",
13160
+ "not_in_app_repo"
13181
13161
  );
13182
13162
  }
13183
- if (!STRICT_APP_ID_RE2.test(currentAppId)) requireTrackedWrangler(appDir);
13184
- const token = await ensureToken();
13185
- const deployUrl = deployBaseUrl();
13186
- const existingMigration = await getIdentityMigration(deployUrl, token, currentAppId);
13187
- if (recoveryMode) {
13188
- if (!existingMigration) {
13189
- if (!args.json) console.log("No reversible identity migration is recorded.");
13190
- return { data: { status: "no_migration", appId: currentAppId } };
13191
- }
13192
- return recoverMigration({
13193
- mode: recoveryMode,
13194
- appDir,
13195
- envName,
13196
- remoteName: typeof args.remote === "string" ? args.remote : void 0,
13197
- currentAppId,
13198
- token,
13199
- deployUrl,
13200
- migration: existingMigration,
13201
- json: args.json
13202
- });
13203
- }
13204
- const sourcePlan = planAppMigrations(appDir);
13205
- if (sourcePlan.blockers.length > 0) {
13206
- const first = sourcePlan.blockers[0];
13163
+ assertSyncableRepo(appDir);
13164
+ const currentRaw = readAppSdkVersion(appDir);
13165
+ if (!currentRaw) {
13207
13166
  throw new Refusal(
13208
- `Migration ${first.migrationId} needs a manual source update at ${first.file}:${first.line}. ${first.message}`,
13209
- "source_migration_blocked",
13210
- { extra: { blockers: sourcePlan.blockers } }
13167
+ "This app does not depend on `deepspace` \u2014 nothing to update.",
13168
+ "not_in_app_repo"
13211
13169
  );
13212
13170
  }
13213
- if (STRICT_APP_ID_RE2.test(currentAppId)) {
13214
- if (!existingMigration) {
13215
- if (args["dry-run"] && sourcePlan.pending.length > 0) {
13216
- reportSourcePlan(sourcePlan, args.json);
13217
- return {
13218
- data: {
13219
- status: "ready",
13220
- appId: currentAppId,
13221
- sourceMigrations: sourcePlan.pending
13222
- }
13223
- };
13224
- }
13225
- if (sourcePlan.pending.length > 0) {
13226
- requireSourceMigrationWorktree(appDir);
13227
- applyAppMigrationPlan(appDir, sourcePlan);
13228
- throw sourceMigrationCommitRequired(appDir, sourcePlan);
13229
- }
13230
- await requireSourceMigrationDeploy(appDir, currentAppId, deployUrl, token, {
13231
- envName,
13232
- remoteName: typeof args.remote === "string" ? args.remote : void 0
13233
- });
13234
- reportUpToDate(currentAppId, args.json);
13235
- return { data: { status: "up_to_date", appId: currentAppId } };
13236
- }
13237
- if (sourcePlan.pending.length === 0 && existingMigration.status === "verified") {
13238
- await requireSourceMigrationDeploy(appDir, currentAppId, deployUrl, token, {
13239
- envName,
13240
- remoteName: typeof args.remote === "string" ? args.remote : void 0
13241
- });
13242
- }
13243
- if (args["dry-run"]) {
13244
- reportMigration(existingMigration, args.json);
13245
- reportSourcePlan(sourcePlan, args.json);
13246
- return {
13247
- data: {
13248
- status: existingMigration.status,
13249
- migration: existingMigration,
13250
- sourceMigrations: sourcePlan.pending
13251
- }
13252
- };
13253
- }
13254
- if (sourcePlan.pending.length > 0) {
13255
- requireSourceMigrationWorktree(appDir, existingMigration.status === "prepared");
13256
- applyAppMigrationPlan(appDir, sourcePlan);
13257
- throw existingMigration.status === "prepared" ? commitRequired(appDir, existingMigration, envName, sourcePlan.changedFiles) : sourceMigrationCommitRequired(appDir, sourcePlan);
13258
- }
13259
- return continuePreparedMigration({
13260
- appDir,
13261
- envName,
13262
- remoteName: typeof args.remote === "string" ? args.remote : void 0,
13263
- token,
13264
- deployUrl,
13265
- migration: existingMigration,
13266
- json: args.json
13267
- });
13268
- }
13269
- if (existingMigration && existingMigration.status !== "prepared" && existingMigration.status !== "rolled_back") {
13171
+ const targetRaw = typeof args.to === "string" && args.to.trim() ? args.to.trim() : await latestPublishedVersion();
13172
+ const current = parseVersion(currentRaw);
13173
+ const target2 = parseVersion(targetRaw);
13174
+ if (!current || !target2) {
13270
13175
  throw new Refusal(
13271
- `The registry already uses ${existingMigration.appId}, but wrangler.toml still uses ${currentAppId}. Restore the migration commit before deploying.`,
13272
- "migration_config_stale",
13273
- { extra: { migration: existingMigration } }
13176
+ `Could not read a version to update from ("${currentRaw}") or to ("${targetRaw}").`,
13177
+ "invalid_version"
13274
13178
  );
13275
13179
  }
13276
- const remote = requireGitHubRemote2(
13277
- appDir,
13278
- typeof args.remote === "string" ? args.remote : void 0
13279
- );
13280
- const head = requirePublishedHead2(appDir, remote);
13281
- const proposedAppId = existingMigration?.appId ?? mintAppId();
13282
- const inventory = await inspectIdentityMigration(deployUrl, token, currentAppId, {
13283
- destinationAppId: proposedAppId,
13284
- repository: remote.repository
13285
- });
13286
- if (!inventory.ready) {
13180
+ if (gapIsTooWide(current, target2)) {
13287
13181
  throw new Refusal(
13288
- `Migration preflight is blocked: ${inventory.blockers.map((blocker) => blocker.message).join("; ")}`,
13289
- "migration_preflight_blocked",
13290
- { extra: { inventory } }
13182
+ `This app is on deepspace ${currentRaw} and the current release is ${targetRaw}. That gap is too wide to migrate automatically \u2014 the changes between them are structural, not mechanical. Follow the upgrade guide instead: ${DOCS_URL}`,
13183
+ "version_gap_too_wide",
13184
+ { actionRequired: true, extra: { currentVersion: currentRaw, targetVersion: targetRaw } }
13291
13185
  );
13292
13186
  }
13293
- if (args["dry-run"]) {
13294
- if (!args.json) {
13295
- console.log(`Legacy app: ${currentAppId}`);
13296
- console.log(`GitHub: ${remote.repository} (${head.branch} @ ${head.oid.slice(0, 10)})`);
13297
- console.log(
13298
- `Immutable app id: ${proposedAppId}${existingMigration ? "" : " (not reserved)"}`
13299
- );
13300
- console.log(
13301
- `Registry rows to re-key: app=1 routes=${inventory.rekey.routes.length} collaborators=${inventory.rekey.collaborators.length} pendingInvites=${inventory.rekey.pendingCollaborators.length} transfers=${inventory.rekey.transfer ? 1 : 0}`
13302
- );
13303
- console.log(
13304
- `Physical stores retained at ${inventory.app.resourceId}: ${inventory.retainedPhysicalStores.map((store) => store.kind).join(", ")}`
13305
- );
13306
- console.log("No changes made.");
13307
- }
13308
- reportSourcePlan(sourcePlan, args.json);
13309
- return {
13310
- data: {
13311
- status: "ready",
13312
- legacyAppId: currentAppId,
13313
- proposedAppId,
13314
- resourceId: inventory.app.resourceId,
13315
- repository: remote.repository,
13316
- branch: head.branch,
13317
- commitOid: head.oid,
13318
- sourceClaimRequired: inventory.app.sourceClaimRequired,
13319
- sourceMigrations: sourcePlan.pending,
13320
- inventory
13321
- }
13322
- };
13323
- }
13324
- const migration = existingMigration?.status === "prepared" ? existingMigration : await prepareIdentityMigration(deployUrl, token, currentAppId, {
13325
- destinationAppId: proposedAppId,
13326
- repository: remote.repository,
13327
- expectedSourceRevision: inventory.app.sourceRevision
13328
- });
13329
- applyAppMigrationPlan(appDir, sourcePlan);
13330
- writeAppId(appDir, migration.appId, { wranglerEnv: envName, force: true });
13331
- throw commitRequired(appDir, migration, envName, sourcePlan.changedFiles);
13332
- }
13333
- });
13334
- async function recoverMigration(input) {
13335
- const { appDir, envName, remoteName, currentAppId, token, deployUrl, migration, json } = input;
13336
- if (migration.status === "rolled_back") {
13337
- if (currentAppId !== migration.legacyAppId) {
13338
- writeAppId(appDir, migration.legacyAppId, { wranglerEnv: envName, force: true });
13339
- throw restoreLegacyCommitRequired(appDir, migration, envName);
13340
- }
13341
- reportMigration(migration, json);
13342
- return { data: { status: "rolled_back", migration } };
13343
- }
13344
- if (migration.status !== "prepared") {
13345
- throw new Refusal(
13346
- "This migration is already committed. Complete it by deploying the canonical app.",
13347
- "forward_recovery_required",
13348
- { extra: { migration } }
13349
- );
13350
- }
13351
- if (currentAppId !== migration.legacyAppId) {
13352
- writeAppId(appDir, migration.legacyAppId, { wranglerEnv: envName, force: true });
13353
- throw restoreLegacyCommitRequired(appDir, migration, envName);
13354
- }
13355
- const remote = requireGitHubRemote2(appDir, remoteName, migration.repository);
13356
- requirePublishedHead2(appDir, remote);
13357
- await cancelIdentityMigration(deployUrl, token, migration.legacyAppId);
13358
- if (!json) console.log(`Canceled ${migration.legacyAppId} \u2192 ${migration.appId}.`);
13359
- return { data: { status: "cancelled", legacyAppId: migration.legacyAppId } };
13360
- }
13361
- async function continuePreparedMigration(input) {
13362
- const { appDir, envName, remoteName, token, deployUrl, migration, json } = input;
13363
- if (migration.status === "rolled_back") {
13364
- throw new Refusal(
13365
- `This migration was rolled back. Restore DEEPSPACE_APP_ID to ${migration.legacyAppId} before continuing.`,
13366
- "migration_rolled_back"
13367
- );
13368
- }
13369
- if (migration.status === "verified") {
13370
- reportMigration(migration, json);
13371
- return { data: { status: "verified", migration } };
13372
- }
13373
- if (migration.status === "committed") {
13374
- await requireSourceMigrationDeploy(appDir, migration.appId, deployUrl, token, {
13375
- envName,
13376
- remoteName,
13377
- after: migration.committedAt ?? void 0
13378
- });
13379
- const verified = await verifyIdentityMigration(deployUrl, token, migration.appId);
13380
- reportMigration(verified, json);
13381
- return { data: { status: "verified", migration: verified } };
13382
- }
13383
- if (!isWorkTreeClean(appDir)) {
13384
- const wranglerPath = wranglerRepoPath(appDir);
13385
- if (statusFiles(repoToplevel(appDir)).includes(wranglerPath)) {
13386
- throw commitRequired(appDir, migration, envName);
13387
- }
13388
- throw new Refusal(
13389
- "Commit or discard unrelated working-tree changes before completing the migration.",
13390
- "dirty_worktree"
13391
- );
13392
- }
13393
- const remote = requireGitHubRemote2(appDir, remoteName, migration.repository);
13394
- const head = requireLocalHead2(appDir);
13395
- const remoteOid = remoteBranchOid(appDir, remote.name, head.branch);
13396
- if (remoteOid !== head.oid) {
13397
- throw new Refusal(
13398
- `GitHub ${remote.repository} does not have ${head.branch} at ${head.oid.slice(0, 10)}. Push the migration commit, then rerun.`,
13399
- "github_push_required",
13400
- {
13401
- actionRequired: true,
13402
- action: { cwd: appDir, argv: ["git", "push", remote.name, head.branch] },
13403
- extra: { appId: migration.appId, repository: remote.repository, ...head }
13404
- }
13405
- );
13406
- }
13407
- const committed = await commitIdentityMigration(deployUrl, token, migration.appId, {
13408
- commitOid: head.oid,
13409
- branch: head.branch
13410
- });
13411
- throw deployRequired(appDir, committed, envName);
13412
- }
13413
- function requireGitHubRemote2(appDir, remoteName, repository) {
13414
- const selected = selectGitHubRemote(appDir, { name: remoteName, repository });
13415
- if (selected) return selected;
13416
- throw new Refusal(
13417
- remoteName ? `Remote "${remoteName}" is not the expected GitHub repository.` : repository ? `No local GitHub remote points at ${repository}. Add one or pass --remote <name>.` : "No unambiguous GitHub remote was found. Add `origin` or pass --remote <name>.",
13418
- "github_remote_required"
13419
- );
13420
- }
13421
- function requireLocalHead2(appDir) {
13422
- const branch = currentBranch(appDir);
13423
- if (!branch) throw new Refusal("HEAD is detached; switch to a branch first.", "detached_head");
13424
- const oid = resolveCommit(appDir, "HEAD");
13425
- if (!oid) throw new Refusal("The repository has no commit to publish.", "no_commits");
13426
- return { branch, oid };
13427
- }
13428
- function requirePublishedHead2(appDir, remote) {
13429
- if (!isWorkTreeClean(appDir)) {
13430
- throw new Refusal(
13431
- "Commit or discard local changes before preparing migration.",
13432
- "dirty_worktree"
13433
- );
13434
- }
13435
- const head = requireLocalHead2(appDir);
13436
- if (remoteBranchOid(appDir, remote.name, head.branch) !== head.oid) {
13437
- throw new Refusal(
13438
- `GitHub ${remote.repository} does not have ${head.branch} at ${head.oid.slice(0, 10)}. Push it, then rerun.`,
13439
- "github_push_required",
13440
- {
13441
- actionRequired: true,
13442
- action: { cwd: appDir, argv: ["git", "push", remote.name, head.branch] },
13443
- extra: { repository: remote.repository, ...head }
13187
+ const dryRun = args["dry-run"] === true;
13188
+ const plan = planAppMigrations(appDir);
13189
+ const pinned = dryRun ? false : pinSdkVersion(appDir, targetRaw);
13190
+ if (!dryRun && plan.edits.length) applyAppMigrationPlan(appDir, plan);
13191
+ if (!args.json) {
13192
+ p34.log.info(`deepspace ${currentRaw} \u2192 ${targetRaw}`);
13193
+ if (pinned) p34.log.success("package.json now asks for the current SDK");
13194
+ if (!plan.pending.length && !plan.blockers.length) {
13195
+ p34.log.success("No source changes required.");
13444
13196
  }
13445
- );
13446
- }
13447
- return head;
13448
- }
13449
- function commitRequired(appDir, migration, envName, additionalPaths = []) {
13450
- const wranglerPath = wranglerRepoPath(appDir);
13451
- const migrationPaths = [.../* @__PURE__ */ new Set([wranglerPath, ...additionalPaths])].sort();
13452
- return new Refusal(
13453
- `Prepared ${migration.legacyAppId} \u2192 ${migration.appId}. Commit the deterministic app migration changes, then rerun.`,
13454
- "migration_commit_required",
13455
- {
13456
- actionRequired: true,
13457
- action: {
13458
- cwd: repoToplevel(appDir),
13459
- argv: [
13460
- "git",
13461
- "commit",
13462
- "--only",
13463
- "--message",
13464
- `Migrate DeepSpace app identity to ${migration.appId}`,
13465
- "--",
13466
- ...migrationPaths
13467
- ]
13468
- },
13469
- extra: { migration, env: envName ?? null }
13470
- }
13471
- );
13472
- }
13473
- function sourceMigrationCommitRequired(appDir, plan) {
13474
- const migrationIds = plan.pending.map((migration) => migration.id);
13475
- return new Refusal(
13476
- `Applied ${migrationIds.join(", ")}. Commit the deterministic app migration changes, then rerun.`,
13477
- "migration_commit_required",
13478
- {
13479
- actionRequired: true,
13480
- action: {
13481
- cwd: repoToplevel(appDir),
13482
- argv: [
13483
- "git",
13484
- "commit",
13485
- "--only",
13486
- "--message",
13487
- "Apply DeepSpace app migrations",
13488
- "--",
13489
- ...plan.changedFiles
13490
- ]
13491
- },
13492
- extra: { sourceMigrations: plan.pending }
13197
+ reportPlan(plan, !dryRun);
13493
13198
  }
13494
- );
13495
- }
13496
- async function requireSourceMigrationDeploy(appDir, appId, deployUrl, token, options = {}) {
13497
- const { release: release2 } = await repoApi(deployUrl, token, appId).latestRelease();
13498
- const deployedIds = releaseMigrationIds(release2?.config);
13499
- const pendingIds = APP_MIGRATION_DEFINITIONS.map(({ id }) => id).filter(
13500
- (id) => !deployedIds.has(id)
13501
- );
13502
- const releaseAfterCutover = options.after === void 0 || typeof release2?.createdAt === "string" && Number.isFinite(Date.parse(release2.createdAt)) && Date.parse(release2.createdAt) > Date.parse(options.after);
13503
- if (pendingIds.length === 0 && releaseAfterCutover) return;
13504
- const sourceState = await getAppSource(deployUrl, token, appId);
13505
- if (sourceState.source?.provider === "github") {
13506
- const remote = requireGitHubRemote2(appDir, options.remoteName, sourceState.source.repository);
13507
- requirePublishedHead2(appDir, remote);
13508
- }
13509
- throw new Refusal(
13510
- pendingIds.length > 0 ? `App migrations ${pendingIds.join(", ")} are not in the live release. Deploy them, then rerun.` : "The canonical app has not been deployed since its identity cutover. Deploy it, then rerun.",
13511
- "migration_deploy_required",
13512
- {
13513
- actionRequired: true,
13514
- action: {
13515
- cwd: appDir,
13516
- argv: ["deepspace", "deploy", ...options.envName ? ["--env", options.envName] : []]
13517
- },
13518
- extra: { appId, sourceMigrations: pendingIds, after: options.after ?? null }
13519
- }
13520
- );
13521
- }
13522
- function releaseMigrationIds(config) {
13523
- if (config === null || typeof config !== "object" || Array.isArray(config)) return /* @__PURE__ */ new Set();
13524
- const validation = validateAppMigrationIds(
13525
- config.appMigrations ?? []
13526
- );
13527
- return new Set(validation.valid ? validation.ids : []);
13528
- }
13529
- function requireSourceMigrationWorktree(appDir, allowDirtyWrangler = false) {
13530
- if (isWorkTreeClean(appDir)) return;
13531
- const changed = statusFiles(repoToplevel(appDir));
13532
- if (allowDirtyWrangler && changed.every((path) => path === wranglerRepoPath(appDir))) return;
13533
- throw new Refusal(
13534
- "Commit or discard unrelated working-tree changes before applying app migrations.",
13535
- "dirty_worktree"
13536
- );
13537
- }
13538
- function restoreLegacyCommitRequired(appDir, migration, envName) {
13539
- const wranglerPath = wranglerRepoPath(appDir);
13540
- return new Refusal(
13541
- `Restored ${migration.legacyAppId} in wrangler.toml. Commit and push this recovery change, then rerun.`,
13542
- "migration_restore_commit_required",
13543
- {
13544
- actionRequired: true,
13545
- action: {
13546
- cwd: repoToplevel(appDir),
13547
- argv: [
13548
- "git",
13549
- "commit",
13550
- "--only",
13551
- "--message",
13552
- `Restore DeepSpace app identity to ${migration.legacyAppId}`,
13553
- "--",
13554
- wranglerPath
13555
- ]
13556
- },
13557
- extra: { migration, env: envName ?? null }
13199
+ if (plan.blockers.length) {
13200
+ throw new Refusal(
13201
+ `${plan.blockers.length} change(s) need a human or agent edit; each is listed above with its file, line, and the change required.`,
13202
+ "manual_changes_required",
13203
+ {
13204
+ actionRequired: true,
13205
+ extra: {
13206
+ currentVersion: currentRaw,
13207
+ targetVersion: targetRaw,
13208
+ applied: plan.pending.map((migration) => migration.id),
13209
+ blockers: plan.blockers
13210
+ }
13211
+ }
13212
+ );
13558
13213
  }
13559
- );
13560
- }
13561
- function wranglerRepoPath(appDir) {
13562
- return `${gitLine(appDir, ["rev-parse", "--show-prefix"])}wrangler.toml`;
13563
- }
13564
- function requireTrackedWrangler(appDir) {
13565
- const path = wranglerRepoPath(appDir);
13566
- if (runGit(repoToplevel(appDir), ["ls-files", "--error-unmatch", "--", path], { allowFail: true }).status !== 0) {
13567
- throw new Refusal(
13568
- "Track, commit, and push wrangler.toml before migrating; GitHub must already contain the deployment configuration.",
13569
- "wrangler_not_tracked"
13570
- );
13571
- }
13572
- }
13573
- function deployRequired(appDir, migration, envName) {
13574
- return new Refusal(
13575
- `Identity cutover committed; deploy once to verify ${migration.appId} on the existing physical resources.`,
13576
- "migration_deploy_required",
13577
- {
13578
- actionRequired: true,
13579
- action: {
13580
- cwd: appDir,
13581
- argv: ["deepspace", "deploy", ...envName ? ["--env", envName] : []]
13214
+ return {
13215
+ data: {
13216
+ currentVersion: currentRaw,
13217
+ targetVersion: targetRaw,
13218
+ pinned,
13219
+ dryRun,
13220
+ applied: plan.pending.map((migration) => ({
13221
+ id: migration.id,
13222
+ description: migration.description,
13223
+ files: migration.files,
13224
+ replacements: migration.replacements
13225
+ }))
13582
13226
  },
13583
- extra: { migration, env: envName ?? null }
13584
- }
13585
- );
13586
- }
13587
- function reportUpToDate(appId, json) {
13588
- if (!json) console.log(`No pending DeepSpace app migrations for ${appId}.`);
13589
- }
13590
- function reportSourcePlan(plan, json) {
13591
- if (json || plan.pending.length === 0) return;
13592
- for (const migration of plan.pending) {
13593
- console.log(
13594
- `Pending source migration: ${migration.id} (${migration.files.length} file${migration.files.length === 1 ? "" : "s"})`
13595
- );
13227
+ // The dependency is pinned but not installed: installing is the package
13228
+ // manager's job, and guessing which one an app uses is how a CLI breaks
13229
+ // a pnpm or bun project.
13230
+ ...pinned ? { action: cliAction("npm", "install") } : {}
13231
+ };
13596
13232
  }
13597
- }
13598
- function reportMigration(migration, json) {
13599
- if (json) return;
13600
- console.log(`Migration: ${migration.legacyAppId} \u2192 ${migration.appId}`);
13601
- console.log(`Status: ${migration.status}`);
13602
- console.log(`Physical resources: ${migration.resourceId}`);
13603
- console.log(`GitHub: ${migration.repository}`);
13604
- }
13233
+ });
13605
13234
 
13606
13235
  // src/cli/cli.ts
13607
13236
  var __dirname = dirname10(fileURLToPath3(import.meta.url));
13608
- var pkg = JSON.parse(readFileSync28(join24(__dirname, "..", "package.json"), "utf-8"));
13609
- if (process.platform === "linux" && !process.env.SSL_CERT_FILE && existsSync26("/etc/ssl/certs/ca-certificates.crt")) {
13237
+ var pkg = JSON.parse(readFileSync29(join25(__dirname, "..", "package.json"), "utf-8"));
13238
+ if (process.platform === "linux" && !process.env.SSL_CERT_FILE && existsSync27("/etc/ssl/certs/ca-certificates.crt")) {
13610
13239
  process.env.SSL_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt";
13611
13240
  }
13612
13241
  var create2 = defineCommand14({
@@ -13655,7 +13284,7 @@ var auth = defineCommand14({
13655
13284
  var app = defineCommand14({
13656
13285
  meta: {
13657
13286
  name: "app",
13658
- description: "The app itself: create, init, list, files, source, migrate, undeploy, transfer, collaborators, domain, usage"
13287
+ description: "The app itself: create, init, list, files, source, update, undeploy, transfer, collaborators, domain, usage"
13659
13288
  },
13660
13289
  subCommands: {
13661
13290
  create: create2,
@@ -13668,7 +13297,7 @@ var app = defineCommand14({
13668
13297
  domain: domain_default,
13669
13298
  usage: usage_default,
13670
13299
  source: source_default,
13671
- migrate: migrate_default
13300
+ update: update_default
13672
13301
  }
13673
13302
  });
13674
13303
  var devGroup = defineCommand14({