@devrouter/cli 0.6.1 → 0.6.3

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/devrouter.js CHANGED
@@ -4033,7 +4033,7 @@ function parseManagedRuntime(value, configPath) {
4033
4033
  );
4034
4034
  ensureAllowedKeys2(
4035
4035
  devcontainer,
4036
- ["baseServices", "profileServices", "prepareCommand", "hostPorts"],
4036
+ ["baseServices", "profileServices", "prepareCommand", "hostPorts", "freshBootstrap"],
4037
4037
  `${configPath}.managedRuntime.devcontainer`
4038
4038
  );
4039
4039
  const baseServices = parseRequiredUniqueStringArray(
@@ -4048,6 +4048,23 @@ function parseManagedRuntime(value, configPath) {
4048
4048
  devcontainer.prepareCommand,
4049
4049
  `${configPath}.managedRuntime.devcontainer.prepareCommand`
4050
4050
  );
4051
+ let freshBootstrap;
4052
+ if (devcontainer.freshBootstrap !== void 0) {
4053
+ const label2 = `${configPath}.managedRuntime.devcontainer.freshBootstrap`;
4054
+ const raw = ensureObject2(devcontainer.freshBootstrap, label2);
4055
+ ensureAllowedKeys2(raw, ["command", "dataVolumes", "sharedCaches"], label2);
4056
+ const command = parsePrepareCommand(raw.command, `${label2}.command`);
4057
+ if (!command) throw new Error(`${label2}.command is required.`);
4058
+ const dataVolumes = parseRequiredUniqueStringArray(raw.dataVolumes, `${label2}.dataVolumes`);
4059
+ const sharedCaches = raw.sharedCaches === void 0 ? [] : parseRequiredUniqueStringArray(raw.sharedCaches, `${label2}.sharedCaches`);
4060
+ if (dataVolumes.length === 0 || dataVolumes.length + sharedCaches.length > 64)
4061
+ throw new Error(`${label2} requires data volumes and at most 64 volume bindings.`);
4062
+ if ([...dataVolumes, ...sharedCaches].some(
4063
+ (key) => !/^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,127}$/.test(key)
4064
+ ) || dataVolumes.some((key) => sharedCaches.includes(key)))
4065
+ throw new Error(`${label2} requires distinct exact data-volume and shared-cache keys.`);
4066
+ freshBootstrap = { command, dataVolumes, sharedCaches };
4067
+ }
4051
4068
  const baseSet = new Set(baseServices);
4052
4069
  const overlappingServices = profileServices.filter((service) => baseSet.has(service));
4053
4070
  if (overlappingServices.length > 0) {
@@ -4109,7 +4126,8 @@ function parseManagedRuntime(value, configPath) {
4109
4126
  baseServices,
4110
4127
  profileServices,
4111
4128
  ...prepareCommand ? { prepareCommand } : {},
4112
- ...hostPorts !== void 0 ? { hostPorts } : {}
4129
+ ...hostPorts !== void 0 ? { hostPorts } : {},
4130
+ ...freshBootstrap ? { freshBootstrap } : {}
4113
4131
  },
4114
4132
  processes
4115
4133
  };
@@ -4880,7 +4898,7 @@ function loadRepoConfig(repoPath, read2 = (file) => import_node_fs8.default.read
4880
4898
  const config = parseRepoConfigSource(raw, configPath);
4881
4899
  const requiredVersion = config.devrouter?.version;
4882
4900
  if (requiredVersion && !hasWarnedVersionMismatch) {
4883
- const cliVersion = true ? "0.6.1" : "0.0.0-dev";
4901
+ const cliVersion = true ? "0.6.3" : "0.0.0-dev";
4884
4902
  if (cliVersion !== "0.0.0-dev" && compareSemver(requiredVersion, cliVersion) > 0) {
4885
4903
  hasWarnedVersionMismatch = true;
4886
4904
  process.stderr.write(
@@ -7153,7 +7171,7 @@ var init_reliability_contract = __esm({
7153
7171
 
7154
7172
  // src/core/reliability-operation-store.ts
7155
7173
  function installedCliVersion() {
7156
- return true ? "0.6.1" : "0.0.0-dev";
7174
+ return true ? "0.6.3" : "0.0.0-dev";
7157
7175
  }
7158
7176
  function compareCliVersions(left, right) {
7159
7177
  const parts = (value) => value.split(".").map((part) => Number.parseInt(part, 10) || 0);
@@ -7294,6 +7312,7 @@ function validate2(record6, identity2) {
7294
7312
  "state",
7295
7313
  "worker",
7296
7314
  "stopWorkStarted",
7315
+ "freshBootstrap",
7297
7316
  "effectSequence",
7298
7317
  "outcome",
7299
7318
  "result",
@@ -7311,6 +7330,47 @@ function validate2(record6, identity2) {
7311
7330
  ]);
7312
7331
  validateConsumerProtection(record6);
7313
7332
  validateIdleProtection(record6);
7333
+ if (record6.freshBootstrap !== void 0) {
7334
+ const attempt = record6.freshBootstrap;
7335
+ keys(attempt, [
7336
+ "version",
7337
+ "operationId",
7338
+ "workerId",
7339
+ "runtimeGeneration",
7340
+ "sourceSha256",
7341
+ "providerId",
7342
+ "providerContext",
7343
+ "definitionSha256",
7344
+ "endpoint",
7345
+ "daemonId",
7346
+ "previousVolumeNameDigests",
7347
+ "stage",
7348
+ "resources"
7349
+ ]);
7350
+ const digest5 = (value) => typeof value === "string" && /^[a-f0-9]{64}$/.test(value);
7351
+ const exact = (value) => typeof value === "string" && /^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,127}$/.test(value);
7352
+ if (attempt.version !== 1 || !isReliabilityId(attempt.operationId) || !isReliabilityId(attempt.workerId) || !isReliabilityCounter(attempt.runtimeGeneration) || !digest5(attempt.sourceSha256) || !digest5(attempt.definitionSha256) || !exact(attempt.providerId) || !exact(attempt.providerContext) || typeof attempt.endpoint !== "string" || !attempt.endpoint.startsWith("unix:///") || attempt.endpoint.includes("\0") || typeof attempt.daemonId !== "string" || attempt.daemonId.length === 0 || attempt.daemonId.length > 256 || !["claimed", "provisioned", "initializing", "initialized", "ready", "failed"].includes(
7353
+ attempt.stage
7354
+ ) || !Array.isArray(attempt.previousVolumeNameDigests) || attempt.previousVolumeNameDigests.length > 4096 || !attempt.previousVolumeNameDigests.every(digest5) || new Set(attempt.previousVolumeNameDigests).size !== attempt.previousVolumeNameDigests.length)
7355
+ throw new Error("Invalid durable fresh-bootstrap attempt.");
7356
+ if (attempt.resources !== void 0) {
7357
+ const resources = attempt.resources;
7358
+ keys(resources, ["primaryId", "project", "dataVolumes"]);
7359
+ if (!digest5(resources.primaryId) || !exact(resources.project) || !Array.isArray(resources.dataVolumes) || resources.dataVolumes.length === 0 || resources.dataVolumes.length > 64)
7360
+ throw new Error("Invalid fresh-bootstrap resource binding.");
7361
+ const names = /* @__PURE__ */ new Set();
7362
+ const bindings2 = /* @__PURE__ */ new Set();
7363
+ for (const volume of resources.dataVolumes) {
7364
+ keys(volume, ["key", "name", "createdAt", "mountpoint"]);
7365
+ if (!exact(volume.key) || typeof volume.name !== "string" || !/^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,255}$/.test(volume.name) || names.has(volume.name) || bindings2.has(volume.key) || typeof volume.createdAt !== "string" || !Number.isFinite(Date.parse(volume.createdAt)) || typeof volume.mountpoint !== "string" || !import_node_path16.default.isAbsolute(volume.mountpoint) || volume.mountpoint.includes("\0"))
7366
+ throw new Error("Invalid fresh-bootstrap data-volume binding.");
7367
+ names.add(volume.name);
7368
+ bindings2.add(volume.key);
7369
+ }
7370
+ } else if (["provisioned", "initializing", "initialized", "ready"].includes(attempt.stage)) {
7371
+ throw new Error("Fresh-bootstrap phase requires proven resource bindings.");
7372
+ }
7373
+ }
7314
7374
  if (record6.writtenByVersion !== void 0 && compareCliVersions(record6.writtenByVersion, installedCliVersion()) > 0) {
7315
7375
  throw new Error(
7316
7376
  `Reliability record was last written by devrouter ${record6.writtenByVersion}, which is newer than the installed ${installedCliVersion()}; upgrade devrouter (devrouter upgrade) before running lifecycle commands.`
@@ -7775,10 +7835,30 @@ function updateReliabilityOperation(identity2, operation) {
7775
7835
  const previousEnrollment = record6.enrollment && { ...record6.enrollment };
7776
7836
  const previousProtection = structuredClone(record6.consumerProtection);
7777
7837
  const previousIdleProtection = structuredClone(record6.idleProtection);
7838
+ const previousBootstrap = structuredClone(record6.freshBootstrap);
7778
7839
  const result = operation(record6);
7779
7840
  if (result && typeof result.then === "function") {
7780
7841
  throw new Error("Reliability record updates must be synchronous.");
7781
7842
  }
7843
+ if (previousBootstrap && !(0, import_node_util3.isDeepStrictEqual)(previousBootstrap, record6.freshBootstrap)) {
7844
+ const current = record6.freshBootstrap;
7845
+ const {
7846
+ stage: previousStage,
7847
+ resources: previousResources,
7848
+ ...previousIdentity
7849
+ } = previousBootstrap;
7850
+ const { stage, resources, ...currentIdentity } = current ?? {};
7851
+ const next = {
7852
+ claimed: ["provisioned", "failed"],
7853
+ provisioned: ["initializing", "failed"],
7854
+ initializing: ["initialized", "failed"],
7855
+ initialized: ["ready"],
7856
+ ready: [],
7857
+ failed: []
7858
+ };
7859
+ if (!current || !(0, import_node_util3.isDeepStrictEqual)(previousIdentity, currentIdentity) || !next[previousStage].includes(stage ?? "") || previousResources && !(0, import_node_util3.isDeepStrictEqual)(previousResources, resources))
7860
+ throw new Error("Durable fresh-bootstrap attempt cannot be cleared, replaced or rearmed.");
7861
+ }
7782
7862
  if (record6.version < previousVersion || previousEnrollment && !(0, import_node_util3.isDeepStrictEqual)(record6.enrollment, previousEnrollment))
7783
7863
  throw new Error("Durable capacity enrollment cannot be downgraded or replaced.");
7784
7864
  if (!(0, import_node_util3.isDeepStrictEqual)(record6.consumerProtection, previousProtection))
@@ -8936,13 +9016,15 @@ function parseControllerRequest(input2) {
8936
9016
  input2,
8937
9017
  "operation-submit",
8938
9018
  ["session", "store", "epoch", "generation", "requestId", "kind"],
8939
- ["operation", "command"]
9019
+ ["operation", "command", "initializeNew"]
8940
9020
  );
8941
9021
  const kind = input2.kind;
8942
9022
  if (kind !== "ensure" && kind !== "exec") invalidRequest();
8943
9023
  const operation = optionalString(input2, "operation", parseId);
8944
9024
  if (kind === "ensure") {
8945
9025
  if (Object.hasOwn(input2, "command")) invalidRequest();
9026
+ if (Object.hasOwn(input2, "initializeNew") && input2.initializeNew !== true)
9027
+ invalidRequest();
8946
9028
  return {
8947
9029
  ...header,
8948
9030
  session: parseId(input2.session),
@@ -8951,10 +9033,12 @@ function parseControllerRequest(input2) {
8951
9033
  generation: parseId(input2.generation),
8952
9034
  requestId: parseId(input2.requestId),
8953
9035
  kind,
8954
- ...operation === void 0 ? {} : { operation }
9036
+ ...operation === void 0 ? {} : { operation },
9037
+ ...input2.initializeNew === true ? { initializeNew: true } : {}
8955
9038
  };
8956
9039
  }
8957
- if (!Object.hasOwn(input2, "command")) invalidRequest();
9040
+ if (!Object.hasOwn(input2, "command") || Object.hasOwn(input2, "initializeNew"))
9041
+ invalidRequest();
8958
9042
  return {
8959
9043
  ...header,
8960
9044
  session: parseId(input2.session),
@@ -14923,12 +15007,113 @@ var init_devsy_mutation = __esm({
14923
15007
  }
14924
15008
  });
14925
15009
 
15010
+ // src/core/lifecycle-progress.ts
15011
+ function isLifecycleProgressPhase(value) {
15012
+ return typeof value === "string" && Object.hasOwn(ROLES, value);
15013
+ }
15014
+ function muteLifecycleProgressHeartbeat() {
15015
+ heartbeatMuted = true;
15016
+ }
15017
+ function lifecycleProgressHeartbeatMuted() {
15018
+ return heartbeatMuted;
15019
+ }
15020
+ function lifecycleProgressSnapshot(phase, receivedAt, now) {
15021
+ const elapsedMs = receivedAt === void 0 ? null : Math.max(0, Math.floor(now - receivedAt));
15022
+ return {
15023
+ type: "lifecycle-progress",
15024
+ phase: phase ?? "unknown",
15025
+ role: phase ? ROLES[phase] : "unknown",
15026
+ elapsedMs,
15027
+ evidence: elapsedMs === null ? "unknown" : elapsedMs >= 3e4 ? "stale" : "recent",
15028
+ liveness: "unknown"
15029
+ };
15030
+ }
15031
+ var ROLES, heartbeatMuted;
15032
+ var init_lifecycle_progress = __esm({
15033
+ "src/core/lifecycle-progress.ts"() {
15034
+ "use strict";
15035
+ ROLES = {
15036
+ validation: "lifecycle-worker",
15037
+ preparation: "repository-preparation",
15038
+ "fresh-bootstrap": "repository-fresh-bootstrap",
15039
+ provider: "provider-operation",
15040
+ "service-start": "service-reconciliation",
15041
+ "service-stop": "service-reconciliation",
15042
+ "process-start": "repository-process-adapter",
15043
+ "process-stop": "repository-process-adapter",
15044
+ "route-publication": "lifecycle-worker",
15045
+ "route-removal": "lifecycle-worker",
15046
+ readiness: "lifecycle-worker",
15047
+ rollback: "lifecycle-worker",
15048
+ stop: "lifecycle-stop"
15049
+ };
15050
+ heartbeatMuted = false;
15051
+ }
15052
+ });
15053
+
15054
+ // src/core/managed-fresh-bootstrap.ts
15055
+ function object4(value) {
15056
+ return value !== null && typeof value === "object" && !Array.isArray(value);
15057
+ }
15058
+ function qualifiedContext(source) {
15059
+ if (source.userEnvProbe !== void 0 && source.userEnvProbe !== "loginInteractiveShell" || object4(source.remoteEnv) && Object.hasOwn(source.remoteEnv, "PATH") || object4(source.secrets) && Object.keys(source.secrets).length > 0 || object4(source.features) && Object.keys(source.features).length > 0 || source.onCreateCommand || source.updateContentCommand)
15060
+ throw new Error(
15061
+ "Fresh bootstrap requires a qualified default-probe context without PATH overrides, secrets, features or earlier container hooks."
15062
+ );
15063
+ if (source.postStartCommand && !["postStartCommand", "postAttachCommand"].includes(String(source.waitFor)))
15064
+ throw new Error("Fresh bootstrap requires waitFor to include the declared post-start hook.");
15065
+ }
15066
+ function assertFreshBootstrapRequest(identity2, options) {
15067
+ if (!options.initializeNew) return;
15068
+ if (identity2.provider !== "devsy" || options.repair)
15069
+ throw new Error(
15070
+ "--initialize-new requires a fresh Devsy managed environment without --repair."
15071
+ );
15072
+ if (!loadRepoConfig(identity2.repoPath).managedRuntime?.devcontainer.freshBootstrap)
15073
+ throw new Error("--initialize-new requires managedRuntime.devcontainer.freshBootstrap.");
15074
+ if (readReliabilityOperation(identity2)?.freshBootstrap)
15075
+ throw new Error(
15076
+ "This environment already records an initialization attempt; initialization cannot be replayed. Use preserving recovery or a new disposable checkout."
15077
+ );
15078
+ if (readManagedRuntimeState(identity2.repoPath, identity2.workspace ?? void 0) || selectDevsyWorkspace(listDevsyWorkspaces(), identity2.repoPath))
15079
+ throw new Error("Fresh bootstrap refuses a retained runtime or provider registration.");
15080
+ qualifiedContext(readDevcontainerConfig(identity2.repoPath).source);
15081
+ }
15082
+ var import_node_child_process20, import_node_crypto18, import_node_fs31, import_node_os6, import_node_path39, import_node_util11, import_yaml4;
15083
+ var init_managed_fresh_bootstrap = __esm({
15084
+ "src/core/managed-fresh-bootstrap.ts"() {
15085
+ "use strict";
15086
+ import_node_child_process20 = require("child_process");
15087
+ import_node_crypto18 = require("crypto");
15088
+ import_node_fs31 = __toESM(require("fs"));
15089
+ import_node_os6 = __toESM(require("os"));
15090
+ import_node_path39 = __toESM(require("path"));
15091
+ import_node_util11 = require("util");
15092
+ import_yaml4 = __toESM(require("yaml"));
15093
+ init_devcontainer_config();
15094
+ init_devcontainer_profile();
15095
+ init_devpod_environment();
15096
+ init_devsy_agent();
15097
+ init_devsy_exec_proof();
15098
+ init_devsy_workspaces();
15099
+ init_lifecycle_progress();
15100
+ init_managed_runtime_state();
15101
+ init_managed_stop_recovery();
15102
+ init_network_provider_binding();
15103
+ init_network_provider_inspect();
15104
+ init_reliability_lifecycle();
15105
+ init_reliability_operation_store();
15106
+ init_repo_config();
15107
+ init_workspace();
15108
+ }
15109
+ });
15110
+
14926
15111
  // src/core/managed-host-ports.ts
14927
15112
  function unavailableEntry(declaration, reason2) {
14928
15113
  return { ...declaration, status: "unavailable", reason: reason2 };
14929
15114
  }
14930
15115
  function livePublishedBindings(containerId, target) {
14931
- const result = (0, import_node_child_process20.spawnSync)("docker", ["port", containerId, `${target}/tcp`], {
15116
+ const result = (0, import_node_child_process21.spawnSync)("docker", ["port", containerId, `${target}/tcp`], {
14932
15117
  encoding: "utf-8",
14933
15118
  timeout: PORT_LOOKUP_TIMEOUT_MS,
14934
15119
  maxBuffer: PORT_LOOKUP_MAX_BUFFER,
@@ -15068,7 +15253,7 @@ function managedHostPortDeclarationsIdentity(declarations) {
15068
15253
  const keys2 = Object.keys(declarations).sort();
15069
15254
  if (keys2.length === 0) return void 0;
15070
15255
  const canonical = keys2.map((key) => [key, declarations[key].service, declarations[key].target]);
15071
- return (0, import_node_crypto18.createHash)("sha256").update(JSON.stringify(canonical)).digest("hex");
15256
+ return (0, import_node_crypto19.createHash)("sha256").update(JSON.stringify(canonical)).digest("hex");
15072
15257
  }
15073
15258
  function assertManagedHostPortsAvailable(report) {
15074
15259
  const unavailable = Object.entries(report).filter(([, entry]) => entry.status === "unavailable").map(([key]) => key).sort();
@@ -15076,12 +15261,12 @@ function assertManagedHostPortsAvailable(report) {
15076
15261
  throw new Error(`Selected managed host ports are unavailable: ${unavailable.join(", ")}.`);
15077
15262
  }
15078
15263
  }
15079
- var import_node_child_process20, import_node_crypto18, PORT_LOOKUP_TIMEOUT_MS, PORT_LOOKUP_MAX_BUFFER, LOOPBACK_BINDING;
15264
+ var import_node_child_process21, import_node_crypto19, PORT_LOOKUP_TIMEOUT_MS, PORT_LOOKUP_MAX_BUFFER, LOOPBACK_BINDING;
15080
15265
  var init_managed_host_ports = __esm({
15081
15266
  "src/core/managed-host-ports.ts"() {
15082
15267
  "use strict";
15083
- import_node_child_process20 = require("child_process");
15084
- import_node_crypto18 = require("crypto");
15268
+ import_node_child_process21 = require("child_process");
15269
+ import_node_crypto19 = require("crypto");
15085
15270
  init_devpod_environment();
15086
15271
  init_network_effect_scope();
15087
15272
  PORT_LOOKUP_TIMEOUT_MS = 1e4;
@@ -15092,21 +15277,21 @@ var init_managed_host_ports = __esm({
15092
15277
 
15093
15278
  // src/core/managed-post-start.ts
15094
15279
  function readRegularFileBytes(filePath) {
15095
- if (!import_node_fs31.default.existsSync(filePath) || !import_node_fs31.default.lstatSync(filePath).isFile()) return void 0;
15096
- return import_node_fs31.default.readFileSync(filePath);
15280
+ if (!import_node_fs32.default.existsSync(filePath) || !import_node_fs32.default.lstatSync(filePath).isFile()) return void 0;
15281
+ return import_node_fs32.default.readFileSync(filePath);
15097
15282
  }
15098
15283
  function readRegularFile(filePath) {
15099
15284
  return readRegularFileBytes(filePath)?.toString("utf-8");
15100
15285
  }
15101
15286
  function adapterFingerprint(adapter) {
15102
- return (0, import_node_crypto19.createHash)("sha256").update(adapter).digest("hex");
15287
+ return (0, import_node_crypto20.createHash)("sha256").update(adapter).digest("hex");
15103
15288
  }
15104
15289
  function resolveManagedPostStartPlan(repoPath) {
15105
- const adapterPath = import_node_path39.default.join(repoPath, MANAGED_ADAPTER_PATH);
15290
+ const adapterPath = import_node_path40.default.join(repoPath, MANAGED_ADAPTER_PATH);
15106
15291
  const adapterBytes = readRegularFileBytes(adapterPath);
15107
15292
  const adapterText = adapterBytes?.toString("utf-8");
15108
- const dockerfilePath = import_node_path39.default.join(repoPath, ".devcontainer", "Dockerfile");
15109
- const devcontainerPath = import_node_path39.default.join(repoPath, ".devcontainer", "devcontainer.json");
15293
+ const dockerfilePath = import_node_path40.default.join(repoPath, ".devcontainer", "Dockerfile");
15294
+ const devcontainerPath = import_node_path40.default.join(repoPath, ".devcontainer", "devcontainer.json");
15110
15295
  const dockerfile = readRegularFile(dockerfilePath) ?? "";
15111
15296
  const devcontainer = readRegularFile(devcontainerPath) ?? "";
15112
15297
  const devrouterPattern = [adapterText, dockerfile, devcontainer].filter((value) => value !== void 0).some(
@@ -15152,7 +15337,7 @@ function assertSafeProcessName(name) {
15152
15337
  }
15153
15338
  function runManagedProcessAction(options) {
15154
15339
  assertSafeProcessName(options.name);
15155
- const result = (0, import_node_child_process21.spawnSync)(
15340
+ const result = (0, import_node_child_process22.spawnSync)(
15156
15341
  "docker",
15157
15342
  [
15158
15343
  "exec",
@@ -15189,14 +15374,14 @@ function runManagedProcessAction(options) {
15189
15374
  }
15190
15375
  return "drifted";
15191
15376
  }
15192
- var import_node_child_process21, import_node_crypto19, import_node_fs31, import_node_path39, MANAGED_MARKER, MANAGED_ADAPTER_PATH, RUNTIME_HELPER_PATH;
15377
+ var import_node_child_process22, import_node_crypto20, import_node_fs32, import_node_path40, MANAGED_MARKER, MANAGED_ADAPTER_PATH, RUNTIME_HELPER_PATH;
15193
15378
  var init_managed_post_start = __esm({
15194
15379
  "src/core/managed-post-start.ts"() {
15195
15380
  "use strict";
15196
- import_node_child_process21 = require("child_process");
15197
- import_node_crypto19 = require("crypto");
15198
- import_node_fs31 = __toESM(require("fs"));
15199
- import_node_path39 = __toESM(require("path"));
15381
+ import_node_child_process22 = require("child_process");
15382
+ import_node_crypto20 = require("crypto");
15383
+ import_node_fs32 = __toESM(require("fs"));
15384
+ import_node_path40 = __toESM(require("path"));
15200
15385
  init_devcontainer_config();
15201
15386
  init_managed_host_ports();
15202
15387
  init_network_effect_scope();
@@ -16060,80 +16245,37 @@ var init_reliability_model = __esm({
16060
16245
  }
16061
16246
  });
16062
16247
 
16063
- // src/core/lifecycle-progress.ts
16064
- function isLifecycleProgressPhase(value) {
16065
- return typeof value === "string" && Object.hasOwn(ROLES, value);
16066
- }
16067
- function muteLifecycleProgressHeartbeat() {
16068
- heartbeatMuted = true;
16069
- }
16070
- function lifecycleProgressHeartbeatMuted() {
16071
- return heartbeatMuted;
16072
- }
16073
- function lifecycleProgressSnapshot(phase, receivedAt, now) {
16074
- const elapsedMs = receivedAt === void 0 ? null : Math.max(0, Math.floor(now - receivedAt));
16075
- return {
16076
- type: "lifecycle-progress",
16077
- phase: phase ?? "unknown",
16078
- role: phase ? ROLES[phase] : "unknown",
16079
- elapsedMs,
16080
- evidence: elapsedMs === null ? "unknown" : elapsedMs >= 3e4 ? "stale" : "recent",
16081
- liveness: "unknown"
16082
- };
16083
- }
16084
- var ROLES, heartbeatMuted;
16085
- var init_lifecycle_progress = __esm({
16086
- "src/core/lifecycle-progress.ts"() {
16087
- "use strict";
16088
- ROLES = {
16089
- validation: "lifecycle-worker",
16090
- preparation: "repository-preparation",
16091
- provider: "provider-operation",
16092
- "service-start": "service-reconciliation",
16093
- "service-stop": "service-reconciliation",
16094
- "process-start": "repository-process-adapter",
16095
- "process-stop": "repository-process-adapter",
16096
- "route-publication": "lifecycle-worker",
16097
- "route-removal": "lifecycle-worker",
16098
- readiness: "lifecycle-worker",
16099
- rollback: "lifecycle-worker",
16100
- stop: "lifecycle-stop"
16101
- };
16102
- heartbeatMuted = false;
16103
- }
16104
- });
16105
-
16106
16248
  // src/core/lifecycle-diagnostics.ts
16107
16249
  function privateDirectory(directory) {
16108
- const stat = import_node_fs32.default.lstatSync(directory);
16250
+ const stat = import_node_fs33.default.lstatSync(directory);
16109
16251
  return stat.isDirectory() && stat.uid === process.getuid?.() && (stat.mode & 63) === 0;
16110
16252
  }
16111
16253
  function lifecycleDiagnosticsPath(identity2) {
16112
- return import_node_path40.default.join(
16254
+ return import_node_path41.default.join(
16113
16255
  DEVROUTER_HOME,
16114
16256
  "lifecycle-diagnostics",
16115
- `${(0, import_node_crypto20.createHash)("sha256").update(identity2.repoPath).digest("hex")}.json`
16257
+ `${(0, import_node_crypto21.createHash)("sha256").update(identity2.repoPath).digest("hex")}.json`
16116
16258
  );
16117
16259
  }
16118
16260
  function readReceipt(identity2) {
16119
16261
  let descriptor;
16120
16262
  try {
16121
16263
  const file = lifecycleDiagnosticsPath(identity2);
16122
- if (!privateDirectory(import_node_path40.default.dirname(file))) return void 0;
16123
- descriptor = import_node_fs32.default.openSync(
16264
+ if (!privateDirectory(import_node_path41.default.dirname(file))) return void 0;
16265
+ descriptor = import_node_fs33.default.openSync(
16124
16266
  file,
16125
- import_node_fs32.default.constants.O_RDONLY | import_node_fs32.default.constants.O_NOFOLLOW | import_node_fs32.default.constants.O_NONBLOCK
16267
+ import_node_fs33.default.constants.O_RDONLY | import_node_fs33.default.constants.O_NOFOLLOW | import_node_fs33.default.constants.O_NONBLOCK
16126
16268
  );
16127
- const stat = import_node_fs32.default.fstatSync(descriptor);
16269
+ const stat = import_node_fs33.default.fstatSync(descriptor);
16128
16270
  if (!stat.isFile() || stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0 || stat.size > MAX_RECEIPT_BYTES)
16129
16271
  return void 0;
16130
16272
  const buffer = Buffer.alloc(MAX_RECEIPT_BYTES + 1);
16131
- const size = import_node_fs32.default.readSync(descriptor, buffer, 0, buffer.length, 0);
16273
+ const size = import_node_fs33.default.readSync(descriptor, buffer, 0, buffer.length, 0);
16132
16274
  if (size > MAX_RECEIPT_BYTES) return void 0;
16133
16275
  const receipt = JSON.parse(buffer.subarray(0, size).toString("utf8"));
16134
16276
  const timestamp = (value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
16135
16277
  const optionalTime = (value) => value === null || timestamp(value);
16136
- if (receipt.version !== 1 || !(0, import_node_util11.isDeepStrictEqual)(receipt.identity, identity2) || typeof receipt.operationId !== "string" || typeof receipt.workerId !== "string" || !timestamp(receipt.startedAtMs) || !timestamp(receipt.updatedAtMs) || receipt.updatedAtMs < receipt.startedAtMs || !(receipt.phase === null || isLifecycleProgressPhase(receipt.phase)) || !optionalTime(receipt.phaseAtMs) || !optionalTime(receipt.lastOutputAtMs) || !optionalTime(receipt.lastProgressAtMs) || !(receipt.buildPackage === null || typeof receipt.buildPackage === "string" && receipt.buildPackage.length <= 128 && /^(?:@[a-zA-Z0-9_.-]+\/)?[a-zA-Z0-9_.-]+$/.test(receipt.buildPackage)) || typeof receipt.cancellationRequested !== "boolean" || typeof receipt.closed !== "boolean" || !Array.isArray(receipt.output) || receipt.output.length > 64)
16278
+ if (receipt.version !== 1 || !(0, import_node_util12.isDeepStrictEqual)(receipt.identity, identity2) || typeof receipt.operationId !== "string" || typeof receipt.workerId !== "string" || !timestamp(receipt.startedAtMs) || !timestamp(receipt.updatedAtMs) || receipt.updatedAtMs < receipt.startedAtMs || !(receipt.phase === null || isLifecycleProgressPhase(receipt.phase)) || !optionalTime(receipt.phaseAtMs) || !optionalTime(receipt.lastOutputAtMs) || !optionalTime(receipt.lastProgressAtMs) || !(receipt.buildPackage === null || typeof receipt.buildPackage === "string" && receipt.buildPackage.length <= 128 && /^(?:@[a-zA-Z0-9_.-]+\/)?[a-zA-Z0-9_.-]+$/.test(receipt.buildPackage)) || typeof receipt.cancellationRequested !== "boolean" || typeof receipt.closed !== "boolean" || !Array.isArray(receipt.output) || receipt.output.length > 64)
16137
16279
  return void 0;
16138
16280
  let bytes2 = 0;
16139
16281
  for (const chunk of receipt.output) {
@@ -16145,7 +16287,7 @@ function readReceipt(identity2) {
16145
16287
  } catch {
16146
16288
  return void 0;
16147
16289
  } finally {
16148
- if (descriptor !== void 0) import_node_fs32.default.closeSync(descriptor);
16290
+ if (descriptor !== void 0) import_node_fs33.default.closeSync(descriptor);
16149
16291
  }
16150
16292
  }
16151
16293
  function readLifecycleDiagnostics(identity2, now = Date.now()) {
@@ -16153,7 +16295,7 @@ function readLifecycleDiagnostics(identity2, now = Date.now()) {
16153
16295
  const before = readReliabilityOperation(identity2);
16154
16296
  const receipt = readReceipt(identity2);
16155
16297
  const after = readReliabilityOperation(identity2);
16156
- if (!before || !after || !receipt || before.revision !== after.revision || !(0, import_node_util11.isDeepStrictEqual)(before, after) || !(0, import_node_util11.isDeepStrictEqual)(receipt.fence, reliabilityFence(after.state)) || after.state.operation?.id !== receipt.operationId || after.state.operation.kind !== "ensure" || after.worker !== null && after.worker.id !== receipt.workerId || !receipt.closed && !after.worker || now < receipt.updatedAtMs || [receipt.phaseAtMs, receipt.lastOutputAtMs, receipt.lastProgressAtMs].some(
16298
+ if (!before || !after || !receipt || before.revision !== after.revision || !(0, import_node_util12.isDeepStrictEqual)(before, after) || !(0, import_node_util12.isDeepStrictEqual)(receipt.fence, reliabilityFence(after.state)) || after.state.operation?.id !== receipt.operationId || after.state.operation.kind !== "ensure" || after.worker !== null && after.worker.id !== receipt.workerId || !receipt.closed && !after.worker || now < receipt.updatedAtMs || [receipt.phaseAtMs, receipt.lastOutputAtMs, receipt.lastProgressAtMs].some(
16157
16299
  (time) => time !== null && (time < receipt.startedAtMs || time > receipt.updatedAtMs)
16158
16300
  ))
16159
16301
  return void 0;
@@ -16183,14 +16325,14 @@ function readLifecycleDiagnostics(identity2, now = Date.now()) {
16183
16325
  return void 0;
16184
16326
  }
16185
16327
  }
16186
- var import_node_crypto20, import_node_fs32, import_node_path40, import_node_util11, MAX_TAIL_BYTES, MAX_RECEIPT_BYTES, PACKAGE_TASK, LifecycleDiagnosticRecorder;
16328
+ var import_node_crypto21, import_node_fs33, import_node_path41, import_node_util12, MAX_TAIL_BYTES, MAX_RECEIPT_BYTES, PACKAGE_TASK, LifecycleDiagnosticRecorder;
16187
16329
  var init_lifecycle_diagnostics = __esm({
16188
16330
  "src/core/lifecycle-diagnostics.ts"() {
16189
16331
  "use strict";
16190
- import_node_crypto20 = require("crypto");
16191
- import_node_fs32 = __toESM(require("fs"));
16192
- import_node_path40 = __toESM(require("path"));
16193
- import_node_util11 = require("util");
16332
+ import_node_crypto21 = require("crypto");
16333
+ import_node_fs33 = __toESM(require("fs"));
16334
+ import_node_path41 = __toESM(require("path"));
16335
+ import_node_util12 = require("util");
16194
16336
  init_atomic_file();
16195
16337
  init_lifecycle_progress();
16196
16338
  init_reliability_contract();
@@ -16242,7 +16384,7 @@ var init_lifecycle_diagnostics = __esm({
16242
16384
  const lines = text6.split(/[\r\n]/);
16243
16385
  this.fragments[stream] = (lines.pop() ?? "").slice(-4096);
16244
16386
  for (const raw of lines) {
16245
- const line = (0, import_node_util11.stripVTControlCharacters)(raw).trim();
16387
+ const line = (0, import_node_util12.stripVTControlCharacters)(raw).trim();
16246
16388
  if (!line || line === this.lastLine || [...line].some(
16247
16389
  (character) => character.charCodeAt(0) < 32 || character.charCodeAt(0) === 127
16248
16390
  ) || line.startsWith('{"type":"lifecycle-progress"'))
@@ -16271,7 +16413,7 @@ var init_lifecycle_diagnostics = __esm({
16271
16413
  this.pending = void 0;
16272
16414
  try {
16273
16415
  const record6 = readReliabilityOperation(this.request.identity);
16274
- if (!record6 || record6.state.operation?.id !== this.request.operationId || !(0, import_node_util11.isDeepStrictEqual)(reliabilityFence(record6.state), this.request.fence) || record6.worker && record6.worker.id !== this.request.workerId)
16416
+ if (!record6 || record6.state.operation?.id !== this.request.operationId || !(0, import_node_util12.isDeepStrictEqual)(reliabilityFence(record6.state), this.request.fence) || record6.worker && record6.worker.id !== this.request.workerId)
16275
16417
  return;
16276
16418
  let remaining = MAX_TAIL_BYTES;
16277
16419
  const chunks = [];
@@ -16284,8 +16426,8 @@ var init_lifecycle_diagnostics = __esm({
16284
16426
  this.receipt.output = chunks;
16285
16427
  this.receipt.updatedAtMs = Date.now();
16286
16428
  const file = lifecycleDiagnosticsPath(this.request.identity);
16287
- import_node_fs32.default.mkdirSync(import_node_path40.default.dirname(file), { recursive: true, mode: 448 });
16288
- if (!privateDirectory(import_node_path40.default.dirname(file))) return;
16429
+ import_node_fs33.default.mkdirSync(import_node_path41.default.dirname(file), { recursive: true, mode: 448 });
16430
+ if (!privateDirectory(import_node_path41.default.dirname(file))) return;
16289
16431
  writeFileAtomically(file, `${JSON.stringify(this.receipt)}
16290
16432
  `);
16291
16433
  } catch {
@@ -16318,11 +16460,11 @@ function workerGroupAbsent(pid) {
16318
16460
  if (process.platform !== "linux") return false;
16319
16461
  let observedZombie = false;
16320
16462
  try {
16321
- for (const entry of import_node_fs33.default.readdirSync("/proc")) {
16463
+ for (const entry of import_node_fs34.default.readdirSync("/proc")) {
16322
16464
  if (!/^\d+$/.test(entry)) continue;
16323
16465
  let stat;
16324
16466
  try {
16325
- stat = import_node_fs33.default.readFileSync(`/proc/${entry}/stat`, "utf8");
16467
+ stat = import_node_fs34.default.readFileSync(`/proc/${entry}/stat`, "utf8");
16326
16468
  } catch (error) {
16327
16469
  if (error.code === "ENOENT") continue;
16328
16470
  return false;
@@ -16353,10 +16495,10 @@ async function runLifecycleWorker(request2, supervision, beforeAdmission) {
16353
16495
  throw new Error("Lifecycle invocation was cancelled before dispatch.");
16354
16496
  if (process.platform === "win32")
16355
16497
  throw new Error("Lifecycle workers require POSIX process-group ownership.");
16356
- const workerPath = import_node_path41.default.join(__dirname, "devrouter-lifecycle-worker.js");
16357
- if (!import_node_fs33.default.existsSync(workerPath))
16498
+ const workerPath = import_node_path42.default.join(__dirname, "devrouter-lifecycle-worker.js");
16499
+ if (!import_node_fs34.default.existsSync(workerPath))
16358
16500
  throw new Error("The packaged lifecycle worker is missing; rebuild the CLI.");
16359
- const child = (0, import_node_child_process22.fork)(workerPath, [], {
16501
+ const child = (0, import_node_child_process23.fork)(workerPath, [], {
16360
16502
  detached: true,
16361
16503
  stdio: supervision ? ["ignore", "pipe", "pipe", "ipc"] : request2.kind === "ensure" ? ["inherit", "pipe", "pipe", "ipc"] : ["inherit", "inherit", "inherit", "ipc"],
16362
16504
  execArgv: []
@@ -16645,16 +16787,16 @@ async function runLifecycleWorker(request2, supervision, beforeAdmission) {
16645
16787
  }
16646
16788
  }
16647
16789
  function newLifecycleIds() {
16648
- return { requestId: (0, import_node_crypto21.randomUUID)(), operationId: (0, import_node_crypto21.randomUUID)(), workerId: (0, import_node_crypto21.randomUUID)() };
16790
+ return { requestId: (0, import_node_crypto22.randomUUID)(), operationId: (0, import_node_crypto22.randomUUID)(), workerId: (0, import_node_crypto22.randomUUID)() };
16649
16791
  }
16650
- var import_node_child_process22, import_node_crypto21, import_node_fs33, import_node_path41, OUTPUT_BUFFER_LIMIT, DEFAULT_OUTPUT_PAGE_BYTES, LifecycleOutput, LifecycleWorkerAdmissionBusyError;
16792
+ var import_node_child_process23, import_node_crypto22, import_node_fs34, import_node_path42, OUTPUT_BUFFER_LIMIT, DEFAULT_OUTPUT_PAGE_BYTES, LifecycleOutput, LifecycleWorkerAdmissionBusyError;
16651
16793
  var init_reliability_worker = __esm({
16652
16794
  "src/core/reliability-worker.ts"() {
16653
16795
  "use strict";
16654
- import_node_child_process22 = require("child_process");
16655
- import_node_crypto21 = require("crypto");
16656
- import_node_fs33 = __toESM(require("fs"));
16657
- import_node_path41 = __toESM(require("path"));
16796
+ import_node_child_process23 = require("child_process");
16797
+ import_node_crypto22 = require("crypto");
16798
+ import_node_fs34 = __toESM(require("fs"));
16799
+ import_node_path42 = __toESM(require("path"));
16658
16800
  init_file_lock();
16659
16801
  init_lifecycle_diagnostics();
16660
16802
  init_lifecycle_progress();
@@ -16773,7 +16915,7 @@ var init_reliability_worker = __esm({
16773
16915
 
16774
16916
  // src/core/devpod-mutation.ts
16775
16917
  function withMutationLock2(activity, target, operation) {
16776
- import_node_fs34.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
16918
+ import_node_fs35.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
16777
16919
  return withFileLockSync(
16778
16920
  DEVPOD_MUTATION_LOCK_FILE,
16779
16921
  {
@@ -16793,7 +16935,7 @@ function runDevpodAction(action2, devpodId, force = false, repoPath) {
16793
16935
  const args = action2 === "delete" ? [action2, devpodId, ...force ? ["--force"] : [], "--ignore-not-found"] : [action2, devpodId];
16794
16936
  claimLifecycleEffect();
16795
16937
  const binding = repoPath ? networkProviderEffectOptions("devpod", devpodId, repoPath) : { args: [] };
16796
- const result = (0, import_node_child_process23.spawnSync)("devpod", [...args, ...binding.args], {
16938
+ const result = (0, import_node_child_process24.spawnSync)("devpod", [...args, ...binding.args], {
16797
16939
  encoding: "utf-8",
16798
16940
  ...binding.env ? { env: binding.env } : {}
16799
16941
  });
@@ -16863,13 +17005,13 @@ function deleteOwnedDevpodWorkspace(devpodId, worktreePath) {
16863
17005
  resetWorkspaceRuntimeCaches();
16864
17006
  return result;
16865
17007
  }
16866
- var import_node_child_process23, import_node_fs34, import_node_path42, DEVPOD_MUTATION_LOCK_FILE, DEVPOD_MUTATION_WAIT_MS;
17008
+ var import_node_child_process24, import_node_fs35, import_node_path43, DEVPOD_MUTATION_LOCK_FILE, DEVPOD_MUTATION_WAIT_MS;
16867
17009
  var init_devpod_mutation = __esm({
16868
17010
  "src/core/devpod-mutation.ts"() {
16869
17011
  "use strict";
16870
- import_node_child_process23 = require("child_process");
16871
- import_node_fs34 = __toESM(require("fs"));
16872
- import_node_path42 = __toESM(require("path"));
17012
+ import_node_child_process24 = require("child_process");
17013
+ import_node_fs35 = __toESM(require("fs"));
17014
+ import_node_path43 = __toESM(require("path"));
16873
17015
  init_devpod_workspaces();
16874
17016
  init_devsy_mutation();
16875
17017
  init_file_lock();
@@ -16878,7 +17020,7 @@ var init_devpod_mutation = __esm({
16878
17020
  init_reliability_context();
16879
17021
  init_router();
16880
17022
  init_workspace_runtime();
16881
- DEVPOD_MUTATION_LOCK_FILE = import_node_path42.default.join(DEVROUTER_HOME, "devpod-mutation.lock");
17023
+ DEVPOD_MUTATION_LOCK_FILE = import_node_path43.default.join(DEVROUTER_HOME, "devpod-mutation.lock");
16882
17024
  DEVPOD_MUTATION_WAIT_MS = 18e5;
16883
17025
  }
16884
17026
  });
@@ -16979,7 +17121,7 @@ function hostPortBindingsConflict(desired, holder) {
16979
17121
  (desired.hostIp === null || holder.hostIp === null || desired.hostIp === holder.hostIp);
16980
17122
  }
16981
17123
  function runDocker(args) {
16982
- const result = (0, import_node_child_process24.spawnSync)("docker", args, {
17124
+ const result = (0, import_node_child_process25.spawnSync)("docker", args, {
16983
17125
  ...networkDockerOptions(),
16984
17126
  encoding: "utf-8",
16985
17127
  timeout: DOCKER_TIMEOUT_MS,
@@ -17049,7 +17191,7 @@ function listHolderSnapshots() {
17049
17191
  }
17050
17192
  function renderManagedComposeModel(plan, workspace) {
17051
17193
  const fileArgs = plan.composeFiles.flatMap((file) => ["-f", file]);
17052
- const result = (0, import_node_child_process24.spawnSync)(
17194
+ const result = (0, import_node_child_process25.spawnSync)(
17053
17195
  "docker",
17054
17196
  [
17055
17197
  "compose",
@@ -17084,8 +17226,8 @@ function renderManagedComposeModel(plan, workspace) {
17084
17226
  }
17085
17227
  }
17086
17228
  function attributeHolderWorktree(workingDir) {
17087
- if (import_node_path43.default.basename(workingDir) !== ".devcontainer") return void 0;
17088
- return comparableWorkspacePath(import_node_path43.default.dirname(workingDir));
17229
+ if (import_node_path44.default.basename(workingDir) !== ".devcontainer") return void 0;
17230
+ return comparableWorkspacePath(import_node_path44.default.dirname(workingDir));
17089
17231
  }
17090
17232
  function holderAttribution(holder, repoPath) {
17091
17233
  const attribution = {};
@@ -17120,7 +17262,7 @@ function detectHostPortClaimConflicts(options) {
17120
17262
  );
17121
17263
  if (desired.length === 0) return [];
17122
17264
  const holders = listHolderSnapshots();
17123
- const devcontainerDir = import_node_path43.default.join(options.repoPath, ".devcontainer");
17265
+ const devcontainerDir = import_node_path44.default.join(options.repoPath, ".devcontainer");
17124
17266
  const conflicts = [];
17125
17267
  for (const binding of desired) {
17126
17268
  for (const holder of holders) {
@@ -17142,12 +17284,12 @@ function detectHostPortClaimConflicts(options) {
17142
17284
  }
17143
17285
  return conflicts;
17144
17286
  }
17145
- var import_node_child_process24, import_node_path43, COMPOSE_RENDER_TIMEOUT_MS, COMPOSE_RENDER_MAX_BUFFER, DOCKER_TIMEOUT_MS, DOCKER_MAX_BUFFER, MAX_DESIRED_HOST_PORTS, MAX_RUNNING_HOLDERS, HOST_PORT_INSPECT_TEMPLATE;
17287
+ var import_node_child_process25, import_node_path44, COMPOSE_RENDER_TIMEOUT_MS, COMPOSE_RENDER_MAX_BUFFER, DOCKER_TIMEOUT_MS, DOCKER_MAX_BUFFER, MAX_DESIRED_HOST_PORTS, MAX_RUNNING_HOLDERS, HOST_PORT_INSPECT_TEMPLATE;
17146
17288
  var init_host_port_claims = __esm({
17147
17289
  "src/core/host-port-claims.ts"() {
17148
17290
  "use strict";
17149
- import_node_child_process24 = require("child_process");
17150
- import_node_path43 = __toESM(require("path"));
17291
+ import_node_child_process25 = require("child_process");
17292
+ import_node_path44 = __toESM(require("path"));
17151
17293
  init_devcontainer_profile();
17152
17294
  init_network_effect_scope();
17153
17295
  init_router();
@@ -17189,7 +17331,7 @@ function probeHttpRoute(host, options = {}) {
17189
17331
  args.push("--cacert", CERT_FILE);
17190
17332
  }
17191
17333
  args.push(url);
17192
- const result = (0, import_node_child_process25.spawnSync)("curl", args, { encoding: "utf-8" });
17334
+ const result = (0, import_node_child_process26.spawnSync)("curl", args, { encoding: "utf-8" });
17193
17335
  const stdout = result.stdout?.trim() ?? "";
17194
17336
  const status = /^\d{3}$/.test(stdout) ? Number(stdout) : void 0;
17195
17337
  const ok = result.status === 0 && status !== void 0 && status >= 100 && status < 500;
@@ -17224,7 +17366,7 @@ function probeHttpReadinessSync(host, readiness, maxTimeSeconds, repoPath) {
17224
17366
  }
17225
17367
  let result;
17226
17368
  try {
17227
- result = (0, import_node_child_process25.spawnSync)("curl", args, {
17369
+ result = (0, import_node_child_process26.spawnSync)("curl", args, {
17228
17370
  encoding: "utf-8",
17229
17371
  maxBuffer: CONTRACT_MAX_BUFFER,
17230
17372
  timeout: Math.ceil(parentTimeoutMs)
@@ -17306,11 +17448,11 @@ function transportFailure() {
17306
17448
  classification: "transport"
17307
17449
  };
17308
17450
  }
17309
- var import_node_child_process25, DEFAULT_MAX_TIME_SECONDS, CONTROLLER_CURL_MAX_TIME_SECONDS, PARENT_TIMEOUT_PADDING_MS, CONTRACT_MAX_BUFFER, MIME_TOKEN_RE2;
17451
+ var import_node_child_process26, DEFAULT_MAX_TIME_SECONDS, CONTROLLER_CURL_MAX_TIME_SECONDS, PARENT_TIMEOUT_PADDING_MS, CONTRACT_MAX_BUFFER, MIME_TOKEN_RE2;
17310
17452
  var init_http_route_probe = __esm({
17311
17453
  "src/core/http-route-probe.ts"() {
17312
17454
  "use strict";
17313
- import_node_child_process25 = require("child_process");
17455
+ import_node_child_process26 = require("child_process");
17314
17456
  init_router();
17315
17457
  init_tls();
17316
17458
  DEFAULT_MAX_TIME_SECONDS = 5;
@@ -17322,12 +17464,12 @@ var init_http_route_probe = __esm({
17322
17464
  });
17323
17465
 
17324
17466
  // src/core/managed-host-preparation.ts
17325
- var import_node_child_process26, import_node_fs35;
17467
+ var import_node_child_process27, import_node_fs36;
17326
17468
  var init_managed_host_preparation = __esm({
17327
17469
  "src/core/managed-host-preparation.ts"() {
17328
17470
  "use strict";
17329
- import_node_child_process26 = require("child_process");
17330
- import_node_fs35 = __toESM(require("fs"));
17471
+ import_node_child_process27 = require("child_process");
17472
+ import_node_fs36 = __toESM(require("fs"));
17331
17473
  init_repo_config();
17332
17474
  }
17333
17475
  });
@@ -17452,7 +17594,7 @@ function classifyManagedMountNesting(configured, effectiveDestinations, containe
17452
17594
  };
17453
17595
  }
17454
17596
  function readContainerMountinfo(containerId, options) {
17455
- const result = (0, import_node_child_process27.spawnSync)("docker", ["exec", containerId, "cat", "/proc/self/mountinfo"], {
17597
+ const result = (0, import_node_child_process28.spawnSync)("docker", ["exec", containerId, "cat", "/proc/self/mountinfo"], {
17456
17598
  encoding: "utf-8",
17457
17599
  timeout: options.timeoutMs ?? 5e3,
17458
17600
  stdio: ["ignore", "pipe", "pipe"],
@@ -17467,11 +17609,11 @@ function observeManagedMountNesting(container, options = {}) {
17467
17609
  const effective = destinations.some((_destination, index) => isNestedIndex(destinations, index)) ? readContainerMountinfo(container.id, options) : null;
17468
17610
  return classifyManagedMountNesting(container.mounts, effective, container.id);
17469
17611
  }
17470
- var import_node_child_process27, NESTED_MOUNT_REASON, UNREADABLE_MOUNT_REASON;
17612
+ var import_node_child_process28, NESTED_MOUNT_REASON, UNREADABLE_MOUNT_REASON;
17471
17613
  var init_managed_mount_nesting = __esm({
17472
17614
  "src/core/managed-mount-nesting.ts"() {
17473
17615
  "use strict";
17474
- import_node_child_process27 = require("child_process");
17616
+ import_node_child_process28 = require("child_process");
17475
17617
  init_network_effect_scope();
17476
17618
  NESTED_MOUNT_REASON = "no configured mount is nested inside another configured mount";
17477
17619
  UNREADABLE_MOUNT_REASON = "the container's own mount table could not be read";
@@ -17489,7 +17631,11 @@ function reliabilityRecovery(reason2, repoPath, variant) {
17489
17631
  switch (reason2) {
17490
17632
  case "stop-incomplete":
17491
17633
  case "operation-unknown":
17492
- return [`Run: devrouter stop ${checkout}`, `Run: devrouter ensure ${checkout} --repair`];
17634
+ return [
17635
+ `Run: devrouter stop ${checkout}`,
17636
+ "Verify the repository startup hooks preserve retained data and startup is authorized before continuing.",
17637
+ `Run: devrouter ensure ${checkout}${variantOption}`
17638
+ ];
17493
17639
  case "start-refused":
17494
17640
  return [
17495
17641
  `Run: devrouter doctor --repo ${checkout} to read the refused fixed host-port claim and its holder`,
@@ -18029,7 +18175,7 @@ function readDocker(endpoint, args, timeoutMs) {
18029
18175
  const env = { ...process.env };
18030
18176
  delete env.DOCKER_CONTEXT;
18031
18177
  delete env.DOCKER_HOST;
18032
- const result = (0, import_node_child_process28.spawnSync)("docker", ["--host", endpoint, ...args], {
18178
+ const result = (0, import_node_child_process29.spawnSync)("docker", ["--host", endpoint, ...args], {
18033
18179
  env,
18034
18180
  encoding: "utf-8",
18035
18181
  timeout: timeoutMs,
@@ -18191,11 +18337,11 @@ function collectDockerNetworkInventory(options = {}, dependencies = {}) {
18191
18337
  }
18192
18338
  return result;
18193
18339
  }
18194
- var import_node_child_process28, NetworkInventoryError, MAX_IDS, MAX_OUTPUT_BYTES, FULL_ID, NETWORK_TEMPLATE, CONTAINER_TEMPLATE;
18340
+ var import_node_child_process29, NetworkInventoryError, MAX_IDS, MAX_OUTPUT_BYTES, FULL_ID, NETWORK_TEMPLATE, CONTAINER_TEMPLATE;
18195
18341
  var init_network_inventory = __esm({
18196
18342
  "src/core/network-inventory.ts"() {
18197
18343
  "use strict";
18198
- import_node_child_process28 = require("child_process");
18344
+ import_node_child_process29 = require("child_process");
18199
18345
  init_devpod_environment();
18200
18346
  NetworkInventoryError = class extends Error {
18201
18347
  };
@@ -18523,26 +18669,26 @@ function hasLocalDockerNetworkNamespace(endpoint, procRoot = "/proc") {
18523
18669
  try {
18524
18670
  if (!endpoint.startsWith("unix:///")) return false;
18525
18671
  const socket = endpoint.slice("unix://".length);
18526
- const table = import_node_fs36.default.readFileSync(import_node_path44.default.join(procRoot, "net/unix"), "utf8");
18672
+ const table = import_node_fs37.default.readFileSync(import_node_path45.default.join(procRoot, "net/unix"), "utf8");
18527
18673
  if (Buffer.byteLength(table) > 1024 * 1024) return false;
18528
18674
  const rows2 = table.split("\n").map((line) => line.trim().split(/\s+/)).filter((row) => row[7] === socket && row[3] === "00010000");
18529
18675
  if (rows2.length !== 1 || !/^\d+$/.test(rows2[0][6])) return false;
18530
18676
  const inode = `socket:[${rows2[0][6]}]`;
18531
- const ownNamespace = import_node_fs36.default.readlinkSync(import_node_path44.default.join(procRoot, "self/ns/net"));
18532
- const pids = import_node_fs36.default.readdirSync(procRoot).filter((name) => /^\d+$/.test(name));
18677
+ const ownNamespace = import_node_fs37.default.readlinkSync(import_node_path45.default.join(procRoot, "self/ns/net"));
18678
+ const pids = import_node_fs37.default.readdirSync(procRoot).filter((name) => /^\d+$/.test(name));
18533
18679
  if (pids.length > 4096) return false;
18534
18680
  for (const pid of pids) {
18535
18681
  if (Date.now() > deadline) return false;
18536
18682
  try {
18537
- const root = import_node_path44.default.join(procRoot, pid);
18538
- if (import_node_path44.default.basename(import_node_fs36.default.readlinkSync(import_node_path44.default.join(root, "exe"))) !== "dockerd") continue;
18539
- if (import_node_fs36.default.readlinkSync(import_node_path44.default.join(root, "ns/net")) !== ownNamespace) continue;
18540
- const descriptors = import_node_fs36.default.readdirSync(import_node_path44.default.join(root, "fd"));
18683
+ const root = import_node_path45.default.join(procRoot, pid);
18684
+ if (import_node_path45.default.basename(import_node_fs37.default.readlinkSync(import_node_path45.default.join(root, "exe"))) !== "dockerd") continue;
18685
+ if (import_node_fs37.default.readlinkSync(import_node_path45.default.join(root, "ns/net")) !== ownNamespace) continue;
18686
+ const descriptors = import_node_fs37.default.readdirSync(import_node_path45.default.join(root, "fd"));
18541
18687
  if (descriptors.length > 4096) continue;
18542
18688
  if (descriptors.some((fd) => {
18543
18689
  if (Date.now() > deadline) return false;
18544
18690
  try {
18545
- return import_node_fs36.default.readlinkSync(import_node_path44.default.join(root, "fd", fd)) === inode;
18691
+ return import_node_fs37.default.readlinkSync(import_node_path45.default.join(root, "fd", fd)) === inode;
18546
18692
  } catch {
18547
18693
  return false;
18548
18694
  }
@@ -18556,12 +18702,12 @@ function hasLocalDockerNetworkNamespace(endpoint, procRoot = "/proc") {
18556
18702
  }
18557
18703
  return false;
18558
18704
  }
18559
- var import_node_fs36, import_node_path44;
18705
+ var import_node_fs37, import_node_path45;
18560
18706
  var init_network_local_daemon = __esm({
18561
18707
  "src/core/network-local-daemon.ts"() {
18562
18708
  "use strict";
18563
- import_node_fs36 = __toESM(require("fs"));
18564
- import_node_path44 = __toESM(require("path"));
18709
+ import_node_fs37 = __toESM(require("fs"));
18710
+ import_node_path45 = __toESM(require("path"));
18565
18711
  }
18566
18712
  });
18567
18713
 
@@ -18589,7 +18735,7 @@ function parseLinuxNetworkRoutes(output2) {
18589
18735
  function collectNetworkRoutes(options) {
18590
18736
  if ((options.platform ?? process.platform) !== "linux" || !hasLocalDockerNetworkNamespace(options.endpoint))
18591
18737
  return { status: "unknown", routes: [] };
18592
- const result = (0, import_node_child_process29.spawnSync)("ip", ["-j", "-4", "route", "show", "table", "all"], {
18738
+ const result = (0, import_node_child_process30.spawnSync)("ip", ["-j", "-4", "route", "show", "table", "all"], {
18593
18739
  encoding: "utf8",
18594
18740
  timeout: 5e3,
18595
18741
  maxBuffer: 1024 * 1024
@@ -18597,11 +18743,11 @@ function collectNetworkRoutes(options) {
18597
18743
  if (result.error || result.status !== 0) return { status: "unknown", routes: [] };
18598
18744
  return parseLinuxNetworkRoutes(result.stdout);
18599
18745
  }
18600
- var import_node_child_process29, import_node_net3;
18746
+ var import_node_child_process30, import_node_net3;
18601
18747
  var init_network_routes = __esm({
18602
18748
  "src/core/network-routes.ts"() {
18603
18749
  "use strict";
18604
- import_node_child_process29 = require("child_process");
18750
+ import_node_child_process30 = require("child_process");
18605
18751
  import_node_net3 = require("net");
18606
18752
  init_network_capacity();
18607
18753
  init_network_local_daemon();
@@ -18769,11 +18915,11 @@ function extendDockerComposeFile(nativeConfig, overlayPath) {
18769
18915
  const config = { ...nativeConfig, dockerComposeFile };
18770
18916
  return { status: "ready", config, reasons: [] };
18771
18917
  }
18772
- var import_node_crypto22;
18918
+ var import_node_crypto23;
18773
18919
  var init_network_compose = __esm({
18774
18920
  "src/core/network-compose.ts"() {
18775
18921
  "use strict";
18776
- import_node_crypto22 = require("crypto");
18922
+ import_node_crypto23 = require("crypto");
18777
18923
  }
18778
18924
  });
18779
18925
 
@@ -18781,8 +18927,8 @@ var init_network_compose = __esm({
18781
18927
  function prepareNetworkComposeFiles(plan, subnet) {
18782
18928
  const overlay = deriveComposeNetworkOverlay(subnet);
18783
18929
  if (overlay.status !== "ready" || !overlay.overlay) throw new Error("Network subnet is invalid.");
18784
- const overlayPath = import_node_path45.default.join(plan.composeDirectory, OVERLAY);
18785
- const ignored = (0, import_node_child_process30.spawnSync)("git", [
18930
+ const overlayPath = import_node_path46.default.join(plan.composeDirectory, OVERLAY);
18931
+ const ignored = (0, import_node_child_process31.spawnSync)("git", [
18786
18932
  "-C",
18787
18933
  plan.composeDirectory,
18788
18934
  "check-ignore",
@@ -18794,11 +18940,11 @@ function prepareNetworkComposeFiles(plan, subnet) {
18794
18940
  if (ignored.status !== 0)
18795
18941
  throw new Error("Generated network overlay must be ignored before allocation.");
18796
18942
  const bytes2 = `${MARKER}
18797
- ${import_yaml4.default.stringify(overlay.overlay)}`;
18943
+ ${import_yaml5.default.stringify(overlay.overlay)}`;
18798
18944
  const assertOwnership = () => {
18799
- if (!import_node_fs37.default.existsSync(overlayPath)) return;
18800
- const stat = import_node_fs37.default.lstatSync(overlayPath);
18801
- if (!stat.isFile() || stat.size > MAX_BYTES3 || import_node_fs37.default.readFileSync(overlayPath, "utf8") !== bytes2)
18945
+ if (!import_node_fs38.default.existsSync(overlayPath)) return;
18946
+ const stat = import_node_fs38.default.lstatSync(overlayPath);
18947
+ if (!stat.isFile() || stat.size > MAX_BYTES3 || import_node_fs38.default.readFileSync(overlayPath, "utf8") !== bytes2)
18802
18948
  throw new Error("Retained network overlay changed; repair is required.");
18803
18949
  };
18804
18950
  assertOwnership();
@@ -18812,7 +18958,7 @@ ${JSON.stringify(extended.config, null, 2)}
18812
18958
  ...plan,
18813
18959
  contents,
18814
18960
  composeFiles: [...plan.composeFiles, overlayPath],
18815
- effectiveConfigSha256: (0, import_node_crypto23.createHash)("sha256").update(contents).digest("hex")
18961
+ effectiveConfigSha256: (0, import_node_crypto24.createHash)("sha256").update(contents).digest("hex")
18816
18962
  };
18817
18963
  return {
18818
18964
  plan: result,
@@ -18822,15 +18968,15 @@ ${JSON.stringify(extended.config, null, 2)}
18822
18968
  }
18823
18969
  };
18824
18970
  }
18825
- var import_node_child_process30, import_node_crypto23, import_node_fs37, import_node_path45, import_yaml4, OVERLAY, MARKER, MAX_BYTES3;
18971
+ var import_node_child_process31, import_node_crypto24, import_node_fs38, import_node_path46, import_yaml5, OVERLAY, MARKER, MAX_BYTES3;
18826
18972
  var init_network_compose_files = __esm({
18827
18973
  "src/core/network-compose-files.ts"() {
18828
18974
  "use strict";
18829
- import_node_child_process30 = require("child_process");
18830
- import_node_crypto23 = require("crypto");
18831
- import_node_fs37 = __toESM(require("fs"));
18832
- import_node_path45 = __toESM(require("path"));
18833
- import_yaml4 = __toESM(require("yaml"));
18975
+ import_node_child_process31 = require("child_process");
18976
+ import_node_crypto24 = require("crypto");
18977
+ import_node_fs38 = __toESM(require("fs"));
18978
+ import_node_path46 = __toESM(require("path"));
18979
+ import_yaml5 = __toESM(require("yaml"));
18834
18980
  init_atomic_file();
18835
18981
  init_devcontainer_profile();
18836
18982
  init_network_compose();
@@ -18841,11 +18987,11 @@ var init_network_compose_files = __esm({
18841
18987
  });
18842
18988
 
18843
18989
  // src/core/network-connected-route.ts
18844
- var import_node_child_process31;
18990
+ var import_node_child_process32;
18845
18991
  var init_network_connected_route = __esm({
18846
18992
  "src/core/network-connected-route.ts"() {
18847
18993
  "use strict";
18848
- import_node_child_process31 = require("child_process");
18994
+ import_node_child_process32 = require("child_process");
18849
18995
  init_network_capacity();
18850
18996
  }
18851
18997
  });
@@ -18861,11 +19007,11 @@ var init_network_lifecycle = __esm({
18861
19007
  });
18862
19008
 
18863
19009
  // src/core/network-managed.ts
18864
- var import_node_crypto24;
19010
+ var import_node_crypto25;
18865
19011
  var init_network_managed = __esm({
18866
19012
  "src/core/network-managed.ts"() {
18867
19013
  "use strict";
18868
- import_node_crypto24 = require("crypto");
19014
+ import_node_crypto25 = require("crypto");
18869
19015
  init_devpod_environment();
18870
19016
  init_network_capacity();
18871
19017
  init_network_claim_lookup();
@@ -18935,13 +19081,13 @@ function resolveLinkedTarget(repoPath) {
18935
19081
  gitCommonDir: resolveGitCommonDir(repoPath)
18936
19082
  };
18937
19083
  }
18938
- var import_node_child_process32, import_node_fs38, import_node_path46;
19084
+ var import_node_child_process33, import_node_fs39, import_node_path47;
18939
19085
  var init_workspace_ensure = __esm({
18940
19086
  "src/core/workspace-ensure.ts"() {
18941
19087
  "use strict";
18942
- import_node_child_process32 = require("child_process");
18943
- import_node_fs38 = __toESM(require("fs"));
18944
- import_node_path46 = __toESM(require("path"));
19088
+ import_node_child_process33 = require("child_process");
19089
+ import_node_fs39 = __toESM(require("fs"));
19090
+ import_node_path47 = __toESM(require("path"));
18945
19091
  init_devcontainer_profile();
18946
19092
  init_devpod_environment();
18947
19093
  init_devpod_mutation();
@@ -18952,6 +19098,7 @@ var init_workspace_ensure = __esm({
18952
19098
  init_host_routes();
18953
19099
  init_http_route_probe();
18954
19100
  init_lifecycle_progress();
19101
+ init_managed_fresh_bootstrap();
18955
19102
  init_managed_host_ports();
18956
19103
  init_managed_host_preparation();
18957
19104
  init_managed_post_start();
@@ -19057,6 +19204,9 @@ function prepareManagedLifecycleOperation(input2) {
19057
19204
  throw new Error("Invalid managed operation command.");
19058
19205
  if (Buffer.byteLength(JSON.stringify(input2)) > 32768)
19059
19206
  throw new Error("Managed operation input exceeds its byte limit.");
19207
+ if (input2.initializeNew && input2.kind !== "ensure")
19208
+ throw new Error("Initialization intent is valid only for an ensure operation.");
19209
+ if (input2.kind === "ensure") assertFreshBootstrapRequest(input2.identity, input2);
19060
19210
  const ids2 = newLifecycleIds();
19061
19211
  return updateReliabilityOperation(input2.identity, (record6) => {
19062
19212
  const retained = readManagedRuntimeState(
@@ -19105,13 +19255,17 @@ function prepareManagedLifecycleOperation(input2) {
19105
19255
  repoPath: input2.identity.repoPath,
19106
19256
  identity: { ...input2.identity },
19107
19257
  fence: reliabilityFence(record6.state),
19108
- options: { profile: input2.profile, quiet: true },
19258
+ options: {
19259
+ profile: input2.profile,
19260
+ quiet: true,
19261
+ ...input2.initializeNew ? { initializeNew: true } : {}
19262
+ },
19109
19263
  ...input2.command ? { command: [...input2.command] } : {}
19110
19264
  }
19111
19265
  };
19112
19266
  });
19113
19267
  }
19114
- function assertCurrentCapacityController(expected, directory = import_node_path47.default.join(DEVROUTER_HOME, "controller")) {
19268
+ function assertCurrentCapacityController(expected, directory = import_node_path48.default.join(DEVROUTER_HOME, "controller")) {
19115
19269
  const current = new ControllerStore(directory).read();
19116
19270
  if (!expected || current?.store !== expected.store || current.epoch !== expected.epoch)
19117
19271
  throw new Error("Capacity controller incarnation changed.");
@@ -19282,7 +19436,7 @@ function withdrawUnstartedLifecycleStop(input2) {
19282
19436
  if (record6.worker || record6.stopWorkStarted) return false;
19283
19437
  if (state.desired !== "stopped-by-user" || state.phase !== "stopping") return false;
19284
19438
  if (state.stopProof.workloadsStopped || state.stopProof.routesRemoved) return false;
19285
- if (!(0, import_node_util12.isDeepStrictEqual)(state.operation, intent.recordedOperation)) return false;
19439
+ if (!(0, import_node_util13.isDeepStrictEqual)(state.operation, intent.recordedOperation)) return false;
19286
19440
  const prior = intent.prior;
19287
19441
  state.intentRevision = prior.intentRevision;
19288
19442
  state.observationsAfterMs = prior.observationsAfterMs;
@@ -19360,7 +19514,7 @@ function prepareResumeLifecycleOperation(input2) {
19360
19514
  });
19361
19515
  }
19362
19516
  function settlePreparedLifecycleCapacity(input2) {
19363
- const directory = input2.directory ?? import_node_path47.default.join(DEVROUTER_HOME, "controller");
19517
+ const directory = input2.directory ?? import_node_path48.default.join(DEVROUTER_HOME, "controller");
19364
19518
  const store = createLifecycleCapacityStore(directory);
19365
19519
  for (let attempt = 0; attempt < 3; attempt++) {
19366
19520
  const snapshot = store.read();
@@ -19388,21 +19542,21 @@ function settlePreparedLifecycleCapacity(input2) {
19388
19542
  reservationId: binding.reservationId,
19389
19543
  policyRevision: binding.policyRevision
19390
19544
  };
19391
- if (operation.kind === "exec" && !(0, import_node_util12.isDeepStrictEqual)(target.totals, binding.execSteady?.totals))
19545
+ if (operation.kind === "exec" && !(0, import_node_util13.isDeepStrictEqual)(target.totals, binding.execSteady?.totals))
19392
19546
  return null;
19393
19547
  const retained = snapshot.reservations.find(
19394
19548
  (entry) => entry.environmentId === target.environmentId
19395
19549
  );
19396
19550
  if (!retained || retained.reservationId !== target.reservationId || retained.operationId !== target.operationId || retained.policyRevision !== target.policyRevision)
19397
19551
  return null;
19398
- if (!record6.phaseSettlement && (0, import_node_util12.isDeepStrictEqual)(retained, target)) return null;
19552
+ if (!record6.phaseSettlement && (0, import_node_util13.isDeepStrictEqual)(retained, target)) return null;
19399
19553
  const marker = {
19400
19554
  id: binding.reservationId,
19401
19555
  fence: { ...proof.fence },
19402
19556
  target,
19403
19557
  estimatesDigest: input2.enrollment.estimatesDigest
19404
19558
  };
19405
- if (record6.phaseSettlement && !(0, import_node_util12.isDeepStrictEqual)(record6.phaseSettlement, marker))
19559
+ if (record6.phaseSettlement && !(0, import_node_util13.isDeepStrictEqual)(record6.phaseSettlement, marker))
19406
19560
  throw new Error("Capacity phase settlement proof changed.");
19407
19561
  binding.validUntilMs = 0;
19408
19562
  record6.phaseSettlement = marker;
@@ -19417,7 +19571,7 @@ function settlePreparedLifecycleCapacity(input2) {
19417
19571
  }
19418
19572
  return updateReliabilityOperation(input2.identity, (record6) => {
19419
19573
  assertCurrentCapacityController(input2.controller, directory);
19420
- if (!matchesFence(record6, pending.fence) || !(0, import_node_util12.isDeepStrictEqual)(record6.phaseSettlement, pending))
19574
+ if (!matchesFence(record6, pending.fence) || !(0, import_node_util13.isDeepStrictEqual)(record6.phaseSettlement, pending))
19421
19575
  return false;
19422
19576
  record6.phaseSettlement = null;
19423
19577
  return true;
@@ -19467,6 +19621,7 @@ async function superviseLifecycle(kind, repoPath, options = {}, command, hooks =
19467
19621
  };
19468
19622
  const ids2 = newLifecycleIds();
19469
19623
  const copiedOptions = { ...options };
19624
+ if (kind === "ensure") assertFreshBootstrapRequest(identity2, options);
19470
19625
  const copiedCommand = command ? [...command] : void 0;
19471
19626
  if (kind === "stop") {
19472
19627
  const intent = updateReliabilityOperation(identity2, (record6) => {
@@ -19510,6 +19665,7 @@ async function superviseLifecycle(kind, repoPath, options = {}, command, hooks =
19510
19665
  identity: identity2,
19511
19666
  requestId: ids2.requestId,
19512
19667
  profile: copiedOptions.profile,
19668
+ ...copiedOptions.initializeNew ? { initializeNew: true } : {},
19513
19669
  ...copiedCommand ? { command: copiedCommand } : {}
19514
19670
  });
19515
19671
  if (initial.state.executionPolicy !== "manual")
@@ -19616,7 +19772,7 @@ async function superviseLifecycle(kind, repoPath, options = {}, command, hooks =
19616
19772
  removeSignals();
19617
19773
  }
19618
19774
  }
19619
- function admitLifecycleCapacity(request2, reservation, budgets, samples, nowMs, maxSampleAgeMs, directory = import_node_path47.default.join(DEVROUTER_HOME, "controller"), controller, admission) {
19775
+ function admitLifecycleCapacity(request2, reservation, budgets, samples, nowMs, maxSampleAgeMs, directory = import_node_path48.default.join(DEVROUTER_HOME, "controller"), controller, admission) {
19620
19776
  if (request2.kind === "stop") throw new Error("Stop never requires capacity admission.");
19621
19777
  const capacity = createLifecycleCapacityStore(directory);
19622
19778
  const snapshot = capacity.read();
@@ -19672,10 +19828,10 @@ function admitLifecycleCapacity(request2, reservation, budgets, samples, nowMs,
19672
19828
  totals
19673
19829
  };
19674
19830
  if (binding.operationId === request2.operationId) {
19675
- if (binding.execSteady && !(0, import_node_util12.isDeepStrictEqual)(binding.execSteady, receipt))
19831
+ if (binding.execSteady && !(0, import_node_util13.isDeepStrictEqual)(binding.execSteady, receipt))
19676
19832
  throw new Error("Repeated exec settlement proof changed.");
19677
19833
  execSteady = binding.execSteady;
19678
- } else if (!retained.startup && !retained.heavy && (0, import_node_util12.isDeepStrictEqual)(retained.totals, totals)) {
19834
+ } else if (!retained.startup && !retained.heavy && (0, import_node_util13.isDeepStrictEqual)(retained.totals, totals)) {
19679
19835
  execSteady = receipt;
19680
19836
  }
19681
19837
  } else if (binding.operationId === request2.operationId && binding.execSteady) {
@@ -19716,7 +19872,7 @@ function admitLifecycleCapacity(request2, reservation, budgets, samples, nowMs,
19716
19872
  return decision;
19717
19873
  }
19718
19874
  function capacityPolicyTargets(repoPath) {
19719
- const policy = readCapacityPolicy(import_node_path47.default.join(DEVROUTER_HOME, "controller"));
19875
+ const policy = readCapacityPolicy(import_node_path48.default.join(DEVROUTER_HOME, "controller"));
19720
19876
  return policy?.admissions === "enabled" && policy.enrollments.some((entry) => entry.repoPath === repoPath);
19721
19877
  }
19722
19878
  function controllerWaitSeconds(deadline) {
@@ -19725,7 +19881,7 @@ function controllerWaitSeconds(deadline) {
19725
19881
  async function superviseThroughController(input2) {
19726
19882
  if (input2.kind === "exec" && !input2.command)
19727
19883
  throw new Error("Exec requires a command for controller admission.");
19728
- const directory = import_node_path47.default.join(DEVROUTER_HOME, "controller");
19884
+ const directory = import_node_path48.default.join(DEVROUTER_HOME, "controller");
19729
19885
  const cancellation = { requested: false };
19730
19886
  const removeSignals = installLifecycleWaitSignals(cancellation);
19731
19887
  const cancelled = () => {
@@ -19749,7 +19905,7 @@ async function superviseThroughController(input2) {
19749
19905
  try {
19750
19906
  const bind = () => observeControllerBinding(directory, {
19751
19907
  path: input2.repoPath,
19752
- session: (0, import_node_crypto25.randomUUID)(),
19908
+ session: (0, import_node_crypto26.randomUUID)(),
19753
19909
  profile: input2.profile ?? "full",
19754
19910
  // The protocol requires at least one readiness selector, and every
19755
19911
  // supervised operation depends on runtime readiness; "runtime" is the
@@ -19759,7 +19915,12 @@ async function superviseThroughController(input2) {
19759
19915
  let binding = await bind();
19760
19916
  let pending = await submitControllerOperation(
19761
19917
  directory,
19762
- input2.kind === "exec" ? { ...binding, requestId: input2.requestId, kind: "exec", command: input2.command ?? [] } : { ...binding, requestId: input2.requestId, kind: "ensure" },
19918
+ input2.kind === "exec" ? { ...binding, requestId: input2.requestId, kind: "exec", command: input2.command ?? [] } : {
19919
+ ...binding,
19920
+ requestId: input2.requestId,
19921
+ kind: "ensure",
19922
+ ...input2.initializeNew ? { initializeNew: true } : {}
19923
+ },
19763
19924
  { waitSeconds: controllerWaitSeconds(deadline), onOutput }
19764
19925
  );
19765
19926
  while (pending.status !== "terminal") {
@@ -19797,7 +19958,7 @@ async function superviseThroughController(input2) {
19797
19958
  removeSignals();
19798
19959
  }
19799
19960
  }
19800
- function renewLifecycleCapacity(request2, policy, samples, controller, directory = import_node_path47.default.join(DEVROUTER_HOME, "controller"), nowMs = Date.now()) {
19961
+ function renewLifecycleCapacity(request2, policy, samples, controller, directory = import_node_path48.default.join(DEVROUTER_HOME, "controller"), nowMs = Date.now()) {
19801
19962
  return updateReliabilityOperation(request2.identity, (record6) => {
19802
19963
  const binding = record6.capacity;
19803
19964
  if (!matchesFence(record6, request2.fence) || !binding || binding.operationId !== request2.operationId || binding.workerId !== request2.workerId || binding.controller?.store !== controller.store || binding.controller?.epoch !== controller.epoch)
@@ -19885,7 +20046,7 @@ function retireQueuedLifecycle(request2, supersededOnly = false) {
19885
20046
  return true;
19886
20047
  });
19887
20048
  }
19888
- function bindLifecycleCapacity(request2, binding, directory = import_node_path47.default.join(DEVROUTER_HOME, "controller")) {
20049
+ function bindLifecycleCapacity(request2, binding, directory = import_node_path48.default.join(DEVROUTER_HOME, "controller")) {
19889
20050
  if (request2.kind === "stop") throw new Error("Stop never requires capacity admission.");
19890
20051
  updateReliabilityOperation(request2.identity, (record6) => {
19891
20052
  if (!matchesFence(record6, request2.fence) || record6.worker || record6.state.operation?.id !== request2.operationId || record6.state.operation.status !== "NOT_STARTED" || record6.state.operation.drained)
@@ -19900,13 +20061,13 @@ function bindLifecycleCapacity(request2, binding, directory = import_node_path47
19900
20061
  stepRecord(record6, { ...request2.fence, type: "admission", result: "admitted" });
19901
20062
  });
19902
20063
  }
19903
- var import_node_crypto25, import_node_path47, import_node_util12, activeWorker, lockHeld, BUSY_LIFECYCLE_WAIT_MS, BUSY_LIFECYCLE_POLL_MS, CAPACITY_CLIENT_WAIT_MS, BUSY_LIFECYCLE_PROGRESS_MS;
20064
+ var import_node_crypto26, import_node_path48, import_node_util13, activeWorker, lockHeld, BUSY_LIFECYCLE_WAIT_MS, BUSY_LIFECYCLE_POLL_MS, CAPACITY_CLIENT_WAIT_MS, BUSY_LIFECYCLE_PROGRESS_MS;
19904
20065
  var init_reliability_lifecycle = __esm({
19905
20066
  "src/core/reliability-lifecycle.ts"() {
19906
20067
  "use strict";
19907
- import_node_crypto25 = require("crypto");
19908
- import_node_path47 = __toESM(require("path"));
19909
- import_node_util12 = require("util");
20068
+ import_node_crypto26 = require("crypto");
20069
+ import_node_path48 = __toESM(require("path"));
20070
+ import_node_util13 = require("util");
19910
20071
  init_capacity_accounting();
19911
20072
  init_capacity_policy();
19912
20073
  init_capacity_request();
@@ -19922,6 +20083,7 @@ var init_reliability_lifecycle = __esm({
19922
20083
  init_file_lock();
19923
20084
  init_host_routes();
19924
20085
  init_managed_devsy_stop();
20086
+ init_managed_fresh_bootstrap();
19925
20087
  init_managed_post_start();
19926
20088
  init_managed_runtime_state();
19927
20089
  init_managed_stop_recovery();
@@ -19947,11 +20109,11 @@ var init_reliability_lifecycle = __esm({
19947
20109
  });
19948
20110
 
19949
20111
  // src/core/capacity-queue.ts
19950
- var import_node_util13, CapacityQueue;
20112
+ var import_node_util14, CapacityQueue;
19951
20113
  var init_capacity_queue = __esm({
19952
20114
  "src/core/capacity-queue.ts"() {
19953
20115
  "use strict";
19954
- import_node_util13 = require("util");
20116
+ import_node_util14 = require("util");
19955
20117
  init_capacity_policy();
19956
20118
  init_capacity_store();
19957
20119
  init_reliability_contract();
@@ -19987,7 +20149,7 @@ var init_capacity_queue = __esm({
19987
20149
  if (existing) {
19988
20150
  const existingRequest = existing.phase === "terminal" ? { ...existing.request, command: void 0 } : existing.request;
19989
20151
  const incomingRequest = existing.phase === "terminal" ? { ...request2, command: void 0 } : request2;
19990
- if (!(0, import_node_util13.isDeepStrictEqual)(existingRequest, incomingRequest) || !(0, import_node_util13.isDeepStrictEqual)(existing.reservation, reservation) || !(0, import_node_util13.isDeepStrictEqual)(existing.admission, admission) || existing.autoResume !== autoResume)
20152
+ if (!(0, import_node_util14.isDeepStrictEqual)(existingRequest, incomingRequest) || !(0, import_node_util14.isDeepStrictEqual)(existing.reservation, reservation) || !(0, import_node_util14.isDeepStrictEqual)(existing.admission, admission) || existing.autoResume !== autoResume)
19991
20153
  throw new Error("Operation reference belongs to another accepted request.");
19992
20154
  return request2.operationId;
19993
20155
  }
@@ -20150,7 +20312,7 @@ var init_capacity_queue = __esm({
20150
20312
  const operation = record6?.state.operation?.id === entry.request.operationId ? record6.state.operation : record6?.state.operationHistory.find(
20151
20313
  (operation2) => operation2.id === entry.request.operationId
20152
20314
  );
20153
- if (record6 && (!(0, import_node_util13.isDeepStrictEqual)(reliabilityFence(record6.state), entry.request.fence) || record6.state.operation?.id !== entry.request.operationId) && operation?.drained && ["NOT_STARTED", "NOT_LAUNCHED"].includes(operation.status) && record6.worker?.operationId !== entry.request.operationId) {
20315
+ if (record6 && (!(0, import_node_util14.isDeepStrictEqual)(reliabilityFence(record6.state), entry.request.fence) || record6.state.operation?.id !== entry.request.operationId) && operation?.drained && ["NOT_STARTED", "NOT_LAUNCHED"].includes(operation.status) && record6.worker?.operationId !== entry.request.operationId) {
20154
20316
  entry.reason = "intent-superseded";
20155
20317
  entry.phase = "terminal";
20156
20318
  entry.request.command = void 0;
@@ -20286,7 +20448,7 @@ var init_capacity_startup_witness = __esm({
20286
20448
  function readHostWakeEpoch(platform = process.platform) {
20287
20449
  if (platform !== "darwin") return void 0;
20288
20450
  try {
20289
- const result = (0, import_node_child_process33.spawnSync)("/usr/sbin/sysctl", ["-n", "kern.wake_abs_time"], {
20451
+ const result = (0, import_node_child_process34.spawnSync)("/usr/sbin/sysctl", ["-n", "kern.wake_abs_time"], {
20290
20452
  encoding: "utf8",
20291
20453
  stdio: ["ignore", "pipe", "pipe"],
20292
20454
  timeout: 250,
@@ -20301,11 +20463,11 @@ function readHostWakeEpoch(platform = process.platform) {
20301
20463
  return null;
20302
20464
  }
20303
20465
  }
20304
- var import_node_child_process33;
20466
+ var import_node_child_process34;
20305
20467
  var init_host_wake = __esm({
20306
20468
  "src/core/host-wake.ts"() {
20307
20469
  "use strict";
20308
- import_node_child_process33 = require("child_process");
20470
+ import_node_child_process34 = require("child_process");
20309
20471
  }
20310
20472
  });
20311
20473
 
@@ -20379,7 +20541,7 @@ function createCapacityController(options) {
20379
20541
  let matches = false;
20380
20542
  try {
20381
20543
  current = new ControllerStore(options.directory).read();
20382
- matches = current?.store === controller.store && current.epoch === controller.epoch && (0, import_node_util14.isDeepStrictEqual)(readCapacityPolicy(options.directory), policy);
20544
+ matches = current?.store === controller.store && current.epoch === controller.epoch && (0, import_node_util15.isDeepStrictEqual)(readCapacityPolicy(options.directory), policy);
20383
20545
  } catch {
20384
20546
  }
20385
20547
  if (invalidated || lifetime.signal.aborted || !matches) {
@@ -20531,12 +20693,12 @@ function createCapacityController(options) {
20531
20693
  controller,
20532
20694
  policyRevision: policy.revision
20533
20695
  });
20534
- const payloadKey = (0, import_node_crypto26.randomBytes)(32);
20696
+ const payloadKey = (0, import_node_crypto27.randomBytes)(32);
20535
20697
  const acceptedPayloads = /* @__PURE__ */ new Map();
20536
20698
  const settlePreparations = () => {
20537
20699
  if (lifetime.signal.aborted) return;
20538
20700
  try {
20539
- if (!(0, import_node_util14.isDeepStrictEqual)(readCapacityPolicy(options.directory), policy)) return;
20701
+ if (!(0, import_node_util15.isDeepStrictEqual)(readCapacityPolicy(options.directory), policy)) return;
20540
20702
  } catch {
20541
20703
  return;
20542
20704
  }
@@ -20550,7 +20712,7 @@ function createCapacityController(options) {
20550
20712
  const record6 = readReliabilityOperation(identity2);
20551
20713
  if (!record6?.capacity || !record6.preparation && !record6.capacity.execSteady || record6.worker || !record6.state.operation?.drained || !["ensure", "exec"].includes(record6.state.operation.kind))
20552
20714
  continue;
20553
- const bytes2 = readControllerEvidence(import_node_path48.default.join(enrollment.repoPath, ".devrouter.yml"));
20715
+ const bytes2 = readControllerEvidence(import_node_path49.default.join(enrollment.repoPath, ".devrouter.yml"));
20554
20716
  const estimates = loadRepoConfig(enrollment.repoPath, () => bytes2).capacity;
20555
20717
  if (!estimates) continue;
20556
20718
  settlePreparedLifecycleCapacity({
@@ -20588,7 +20750,7 @@ function createCapacityController(options) {
20588
20750
  };
20589
20751
  const resolveCapacityTarget = async (environment, signal) => {
20590
20752
  const current = readCapacityPolicy(options.directory);
20591
- if (current?.admissions !== "enabled" || !(0, import_node_util14.isDeepStrictEqual)(current, policy)) return void 0;
20753
+ if (current?.admissions !== "enabled" || !(0, import_node_util15.isDeepStrictEqual)(current, policy)) return void 0;
20592
20754
  let resolved2;
20593
20755
  try {
20594
20756
  resolved2 = await resolveCapacityEnrollment(
@@ -20600,10 +20762,10 @@ function createCapacityController(options) {
20600
20762
  } catch {
20601
20763
  return void 0;
20602
20764
  }
20603
- if (signal.aborted || !(0, import_node_util14.isDeepStrictEqual)(resolved2.environment, environment)) return void 0;
20604
- if (!(0, import_node_util14.isDeepStrictEqual)(readCapacityPolicy(options.directory), current)) return void 0;
20765
+ if (signal.aborted || !(0, import_node_util15.isDeepStrictEqual)(resolved2.environment, environment)) return void 0;
20766
+ if (!(0, import_node_util15.isDeepStrictEqual)(readCapacityPolicy(options.directory), current)) return void 0;
20605
20767
  const runtime = current.domains[resolved2.enrollment.runtimeDomain];
20606
- if (runtime?.kind !== "runtime" || runtime.hostDomain !== resolved2.enrollment.hostDomain || current.domains[runtime.hostDomain]?.kind !== "host" || !current.enrollments.some((entry) => (0, import_node_util14.isDeepStrictEqual)(entry, resolved2.enrollment)))
20768
+ if (runtime?.kind !== "runtime" || runtime.hostDomain !== resolved2.enrollment.hostDomain || current.domains[runtime.hostDomain]?.kind !== "host" || !current.enrollments.some((entry) => (0, import_node_util15.isDeepStrictEqual)(entry, resolved2.enrollment)))
20607
20769
  return void 0;
20608
20770
  return { current, resolved: resolved2, runtime };
20609
20771
  };
@@ -20645,7 +20807,7 @@ function createCapacityController(options) {
20645
20807
  const bounded = AbortSignal.any([signal, lifetime.signal]);
20646
20808
  if (bounded.aborted) return false;
20647
20809
  const current = readCapacityPolicy(options.directory);
20648
- if (current?.admissions !== "enabled" || !(0, import_node_util14.isDeepStrictEqual)(current, policy)) return false;
20810
+ if (current?.admissions !== "enabled" || !(0, import_node_util15.isDeepStrictEqual)(current, policy)) return false;
20649
20811
  const request2 = reconcileParkedLifecycleStop({
20650
20812
  identity: { ...identity2 },
20651
20813
  controller,
@@ -20695,7 +20857,7 @@ function createCapacityController(options) {
20695
20857
  {
20696
20858
  ...charge,
20697
20859
  operationId: prepared.operationId,
20698
- reservationId: (0, import_node_crypto26.randomUUID)(),
20860
+ reservationId: (0, import_node_crypto27.randomUUID)(),
20699
20861
  policyRevision: target.current.revision
20700
20862
  },
20701
20863
  {
@@ -20724,7 +20886,7 @@ function createCapacityController(options) {
20724
20886
  signal = AbortSignal.any([signal, lifetime.signal]);
20725
20887
  if (signal.aborted) throw new Error("Capacity submission unavailable.");
20726
20888
  const current = readCapacityPolicy(options.directory);
20727
- if (current?.admissions !== "enabled" || !(0, import_node_util14.isDeepStrictEqual)(current, policy))
20889
+ if (current?.admissions !== "enabled" || !(0, import_node_util15.isDeepStrictEqual)(current, policy))
20728
20890
  throw new Error("Capacity controller policy changed.");
20729
20891
  validate3();
20730
20892
  const resolved2 = await enrollCapacityLifecycle(
@@ -20738,12 +20900,12 @@ function createCapacityController(options) {
20738
20900
  options.directory,
20739
20901
  bindingResolver
20740
20902
  );
20741
- if (!(0, import_node_util14.isDeepStrictEqual)(resolved2.environment, environment) || signal.aborted)
20903
+ if (!(0, import_node_util15.isDeepStrictEqual)(resolved2.environment, environment) || signal.aborted)
20742
20904
  throw new Error("Capacity submission binding changed.");
20743
- if (!(0, import_node_util14.isDeepStrictEqual)(readCapacityPolicy(options.directory), current))
20905
+ if (!(0, import_node_util15.isDeepStrictEqual)(readCapacityPolicy(options.directory), current))
20744
20906
  throw new Error("Capacity submission policy changed during resolution.");
20745
20907
  const runtime = current.domains[resolved2.enrollment.runtimeDomain];
20746
- if (runtime?.kind !== "runtime" || runtime.hostDomain !== resolved2.enrollment.hostDomain || current.domains[runtime.hostDomain]?.kind !== "host" || !current.enrollments.some((entry) => (0, import_node_util14.isDeepStrictEqual)(entry, resolved2.enrollment)))
20908
+ if (runtime?.kind !== "runtime" || runtime.hostDomain !== resolved2.enrollment.hostDomain || current.domains[runtime.hostDomain]?.kind !== "host" || !current.enrollments.some((entry) => (0, import_node_util15.isDeepStrictEqual)(entry, resolved2.enrollment)))
20747
20909
  throw new Error("Capacity pool does not match policy enrollment.");
20748
20910
  const pool = {
20749
20911
  daemonId: runtime.daemonId,
@@ -20780,7 +20942,8 @@ function createCapacityController(options) {
20780
20942
  profile: environment.profile,
20781
20943
  consumer: initialConsumer,
20782
20944
  runtimeRunning: request2.kind === "exec" && Boolean(resolveRunningWorkspaceContainer(environment.repoPath)),
20783
- ...request2.kind === "exec" ? { command: request2.command } : {}
20945
+ ...request2.kind === "exec" ? { command: request2.command } : {},
20946
+ ...request2.kind === "ensure" && request2.initializeNew ? { initializeNew: true } : {}
20784
20947
  });
20785
20948
  if (prepared.request && request2.kind === "ensure") {
20786
20949
  try {
@@ -20798,13 +20961,14 @@ function createCapacityController(options) {
20798
20961
  throw error;
20799
20962
  }
20800
20963
  }
20801
- const signature = (0, import_node_crypto26.createHmac)("sha256", payloadKey).update(
20964
+ const signature = (0, import_node_crypto27.createHmac)("sha256", payloadKey).update(
20802
20965
  JSON.stringify({
20803
20966
  repoPath: identity2.repoPath,
20804
20967
  profile: environment.profile,
20805
20968
  kind: request2.kind,
20806
20969
  operation: request2.operation ?? null,
20807
- command: request2.kind === "exec" ? request2.command : null
20970
+ command: request2.kind === "exec" ? request2.command : null,
20971
+ ...request2.kind === "ensure" && request2.initializeNew ? { initializeNew: true } : {}
20808
20972
  })
20809
20973
  ).digest("hex");
20810
20974
  const previousPayload = acceptedPayloads.get(prepared.operationId);
@@ -20818,7 +20982,7 @@ function createCapacityController(options) {
20818
20982
  {
20819
20983
  ...charge,
20820
20984
  operationId: prepared.operationId,
20821
- reservationId: (0, import_node_crypto26.randomUUID)(),
20985
+ reservationId: (0, import_node_crypto27.randomUUID)(),
20822
20986
  policyRevision: current.revision
20823
20987
  },
20824
20988
  { estimates: resolved2.estimates, enrollment: resolved2.enrollment, pool }
@@ -20877,7 +21041,7 @@ function createCapacityController(options) {
20877
21041
  if (signal.aborted || !policy.recovery.enabled) return;
20878
21042
  if (failedCapabilities.length === 0 || failedCapabilities.length > 64) return;
20879
21043
  const current = readCapacityPolicy(options.directory);
20880
- if (current?.admissions !== "enabled" || !(0, import_node_util14.isDeepStrictEqual)(current, policy)) return;
21044
+ if (current?.admissions !== "enabled" || !(0, import_node_util15.isDeepStrictEqual)(current, policy)) return;
20881
21045
  let observed;
20882
21046
  try {
20883
21047
  observed = revalidate();
@@ -20897,10 +21061,10 @@ function createCapacityController(options) {
20897
21061
  } catch {
20898
21062
  return;
20899
21063
  }
20900
- if (signal.aborted || !(0, import_node_util14.isDeepStrictEqual)(resolved2.environment, environment)) return;
20901
- if (!(0, import_node_util14.isDeepStrictEqual)(readCapacityPolicy(options.directory), current)) return;
21064
+ if (signal.aborted || !(0, import_node_util15.isDeepStrictEqual)(resolved2.environment, environment)) return;
21065
+ if (!(0, import_node_util15.isDeepStrictEqual)(readCapacityPolicy(options.directory), current)) return;
20902
21066
  const runtime = current.domains[resolved2.enrollment.runtimeDomain];
20903
- if (runtime?.kind !== "runtime" || runtime.hostDomain !== resolved2.enrollment.hostDomain || current.domains[runtime.hostDomain]?.kind !== "host" || !current.enrollments.some((entry) => (0, import_node_util14.isDeepStrictEqual)(entry, resolved2.enrollment)))
21067
+ if (runtime?.kind !== "runtime" || runtime.hostDomain !== resolved2.enrollment.hostDomain || current.domains[runtime.hostDomain]?.kind !== "host" || !current.enrollments.some((entry) => (0, import_node_util15.isDeepStrictEqual)(entry, resolved2.enrollment)))
20904
21068
  return;
20905
21069
  const identity2 = environmentIdentity(environment);
20906
21070
  const record6 = readReliabilityOperation(identity2);
@@ -20953,7 +21117,7 @@ function createCapacityController(options) {
20953
21117
  // the budget module then falls back to the conservative wall duration.
20954
21118
  activeElapsedMs: null,
20955
21119
  profile: environment.profile,
20956
- incidentId: `incident-${(0, import_node_crypto26.randomUUID)()}`
21120
+ incidentId: `incident-${(0, import_node_crypto27.randomUUID)()}`
20957
21121
  });
20958
21122
  } catch {
20959
21123
  return;
@@ -20971,7 +21135,7 @@ function createCapacityController(options) {
20971
21135
  {
20972
21136
  ...charge,
20973
21137
  operationId: prepared.operationId,
20974
- reservationId: (0, import_node_crypto26.randomUUID)(),
21138
+ reservationId: (0, import_node_crypto27.randomUUID)(),
20975
21139
  policyRevision: current.revision
20976
21140
  },
20977
21141
  { estimates: resolved2.estimates, enrollment: resolved2.enrollment, pool }
@@ -20991,13 +21155,13 @@ function createCapacityController(options) {
20991
21155
  }
20992
21156
  };
20993
21157
  }
20994
- var import_node_crypto26, import_node_path48, import_node_util14;
21158
+ var import_node_crypto27, import_node_path49, import_node_util15;
20995
21159
  var init_capacity_controller = __esm({
20996
21160
  "src/core/capacity-controller.ts"() {
20997
21161
  "use strict";
20998
- import_node_crypto26 = require("crypto");
20999
- import_node_path48 = __toESM(require("path"));
21000
- import_node_util14 = require("util");
21162
+ import_node_crypto27 = require("crypto");
21163
+ import_node_path49 = __toESM(require("path"));
21164
+ import_node_util15 = require("util");
21001
21165
  init_capacity_accounting();
21002
21166
  init_capacity_docker_probe();
21003
21167
  init_capacity_enrollment();
@@ -21093,7 +21257,7 @@ printf '%s %s %s\n' "$pid" "$birth" "$fingerprint"
21093
21257
 
21094
21258
  // src/core/controller-observation.ts
21095
21259
  function digest2(value) {
21096
- return (0, import_node_crypto27.createHash)("sha256").update(JSON.stringify(value)).digest("hex");
21260
+ return (0, import_node_crypto28.createHash)("sha256").update(JSON.stringify(value)).digest("hex");
21097
21261
  }
21098
21262
  function readRoutes() {
21099
21263
  return readHostRouteStateReadOnly((file) => readControllerEvidence(file, 1048576));
@@ -21156,14 +21320,14 @@ function createControllerObservationCollector(resolve, fingerprint) {
21156
21320
  )).trim().split("\n");
21157
21321
  if (metadata.length !== 2) throw new Error("Observation ownership paths unavailable.");
21158
21322
  const [gitDir, commonDir] = metadata;
21159
- const sourcePath = import_node_path49.default.join(environment.repoPath, ".devcontainer/devcontainer.json");
21323
+ const sourcePath = import_node_path50.default.join(environment.repoPath, ".devcontainer/devcontainer.json");
21160
21324
  const files = [
21161
- import_node_path49.default.join(commonDir, "devrouter/workspaces", `${environment.workspace}.json`),
21162
- import_node_path49.default.join(environment.repoPath, ".devrouter.yml"),
21163
- import_node_path49.default.join(gitDir, "devrouter-workspace"),
21325
+ import_node_path50.default.join(commonDir, "devrouter/workspaces", `${environment.workspace}.json`),
21326
+ import_node_path50.default.join(environment.repoPath, ".devrouter.yml"),
21327
+ import_node_path50.default.join(gitDir, "devrouter-workspace"),
21164
21328
  managedRuntimeStatePath(environment.repoPath, environment.workspace),
21165
21329
  sourcePath,
21166
- import_node_path49.default.join(environment.repoPath, MANAGED_DEVCONTAINER_PATH)
21330
+ import_node_path50.default.join(environment.repoPath, MANAGED_DEVCONTAINER_PATH)
21167
21331
  ];
21168
21332
  const persisted = captureControllerEvidence(files);
21169
21333
  if (fingerprint(persisted.contents[0], persisted.contents[1]) !== environment.fingerprint)
@@ -21177,7 +21341,7 @@ function createControllerObservationCollector(resolve, fingerprint) {
21177
21341
  () => persisted.contents[3]
21178
21342
  );
21179
21343
  const source = parseDevcontainerConfig(persisted.contents[4], sourcePath);
21180
- if (!state || state.devpodId !== environment.providerId || state.profile !== environment.profile || typeof source.service !== "string" || !source.service || (0, import_node_crypto27.createHash)("sha256").update(persisted.contents[4]).digest("hex") !== state.sourceConfigSha256 || (0, import_node_crypto27.createHash)("sha256").update(persisted.contents[5]).digest("hex") !== state.effectiveConfigSha256)
21344
+ if (!state || state.devpodId !== environment.providerId || state.profile !== environment.profile || typeof source.service !== "string" || !source.service || (0, import_node_crypto28.createHash)("sha256").update(persisted.contents[4]).digest("hex") !== state.sourceConfigSha256 || (0, import_node_crypto28.createHash)("sha256").update(persisted.contents[5]).digest("hex") !== state.effectiveConfigSha256)
21181
21345
  throw new ControllerObservationBindingChanged("Observation retained configuration changed.");
21182
21346
  const identity2 = {
21183
21347
  repoPath: environment.repoPath,
@@ -21188,7 +21352,7 @@ function createControllerObservationCollector(resolve, fingerprint) {
21188
21352
  if (!journal) throw new Error("Observation journal is unavailable.");
21189
21353
  const routes = readRoutes().filter((route) => route.repoPath === environment.repoPath);
21190
21354
  const before = await containers(state.composeProject, signal);
21191
- const composeDirectory = import_node_path49.default.join(environment.repoPath, ".devcontainer");
21355
+ const composeDirectory = import_node_path50.default.join(environment.repoPath, ".devcontainer");
21192
21356
  const references = typeof source.dockerComposeFile === "string" ? [source.dockerComposeFile] : source.dockerComposeFile;
21193
21357
  if (!Array.isArray(references) || !references.length || references.some((value) => typeof value !== "string"))
21194
21358
  throw new Error("Observation Compose source is unavailable.");
@@ -21205,7 +21369,7 @@ function createControllerObservationCollector(resolve, fingerprint) {
21205
21369
  if (!recorded || container.labels["com.docker.compose.project.working_dir"] !== composeDirectory)
21206
21370
  throw new Error("Observation Compose ownership is unavailable.");
21207
21371
  const files2 = recorded.split(",").map((file) => file.trim());
21208
- if (files2.some((file) => !import_node_path49.default.isAbsolute(file)) || !hasExactComposeIdentity(container, {
21372
+ if (files2.some((file) => !import_node_path50.default.isAbsolute(file)) || !hasExactComposeIdentity(container, {
21209
21373
  repoPath: environment.repoPath,
21210
21374
  service: container.labels["com.docker.compose.service"] ?? "",
21211
21375
  composeProject: state.composeProject,
@@ -21352,12 +21516,12 @@ function createControllerObservationCollector(resolve, fingerprint) {
21352
21516
  };
21353
21517
  };
21354
21518
  }
21355
- var import_node_crypto27, import_node_path49, OBSERVATION_INSPECT_TEMPLATE;
21519
+ var import_node_crypto28, import_node_path50, OBSERVATION_INSPECT_TEMPLATE;
21356
21520
  var init_controller_observation = __esm({
21357
21521
  "src/core/controller-observation.ts"() {
21358
21522
  "use strict";
21359
- import_node_crypto27 = require("crypto");
21360
- import_node_path49 = __toESM(require("path"));
21523
+ import_node_crypto28 = require("crypto");
21524
+ import_node_path50 = __toESM(require("path"));
21361
21525
  init_controller_binding();
21362
21526
  init_controller_monitor();
21363
21527
  init_controller_probe();
@@ -21381,12 +21545,12 @@ var init_controller_observation = __esm({
21381
21545
  });
21382
21546
 
21383
21547
  // src/core/controller-sessions.ts
21384
- var import_node_crypto28, import_node_util15, ControllerSessions;
21548
+ var import_node_crypto29, import_node_util16, ControllerSessions;
21385
21549
  var init_controller_sessions = __esm({
21386
21550
  "src/core/controller-sessions.ts"() {
21387
21551
  "use strict";
21388
- import_node_crypto28 = require("crypto");
21389
- import_node_util15 = require("util");
21552
+ import_node_crypto29 = require("crypto");
21553
+ import_node_util16 = require("util");
21390
21554
  ControllerSessions = class {
21391
21555
  constructor(store, readWakeEpoch = () => void 0) {
21392
21556
  this.store = store;
@@ -21465,7 +21629,7 @@ var init_controller_sessions = __esm({
21465
21629
  }
21466
21630
  checkEnvironment(next, environment) {
21467
21631
  const shared = next.environments.find((entry) => entry.repoPath === environment.repoPath);
21468
- if (shared && !(0, import_node_util15.isDeepStrictEqual)(shared, environment))
21632
+ if (shared && !(0, import_node_util16.isDeepStrictEqual)(shared, environment))
21469
21633
  throw new Error("Controller environment binding conflicts.");
21470
21634
  const known = [
21471
21635
  ...next.environments,
@@ -21579,7 +21743,7 @@ var init_controller_sessions = __esm({
21579
21743
  const session = {
21580
21744
  id: id5,
21581
21745
  environmentId: environment.id,
21582
- generation: (0, import_node_crypto28.randomUUID)(),
21746
+ generation: (0, import_node_crypto29.randomUUID)(),
21583
21747
  requirements: required,
21584
21748
  renewedAtMs: wall,
21585
21749
  parkingConsent: "protected",
@@ -21601,13 +21765,13 @@ var init_controller_sessions = __esm({
21601
21765
  const retained = next.retainedSessions.find(
21602
21766
  (entry) => entry.id === previous.session && entry.epoch === previous.epoch && entry.generation === previous.generation
21603
21767
  );
21604
- if (previous.store !== next.store || next.version !== 3 || previous.epoch < next.fingerprint.enrolledEpoch || !retained || next.sessions.some((entry) => entry.id === previous.session) || !(0, import_node_util15.isDeepStrictEqual)(retained.environment, environment) || !(0, import_node_util15.isDeepStrictEqual)([...retained.requirements].sort(), required))
21768
+ if (previous.store !== next.store || next.version !== 3 || previous.epoch < next.fingerprint.enrolledEpoch || !retained || next.sessions.some((entry) => entry.id === previous.session) || !(0, import_node_util16.isDeepStrictEqual)(retained.environment, environment) || !(0, import_node_util16.isDeepStrictEqual)([...retained.requirements].sort(), required))
21605
21769
  throw new Error("Controller reconnect binding is unavailable or changed.");
21606
21770
  const shared = this.checkEnvironment(next, environment);
21607
21771
  const session = {
21608
21772
  id: previous.session,
21609
21773
  environmentId: environment.id,
21610
- generation: (0, import_node_crypto28.randomUUID)(),
21774
+ generation: (0, import_node_crypto29.randomUUID)(),
21611
21775
  requirements: required,
21612
21776
  renewedAtMs: wall,
21613
21777
  parkingConsent: "protected",
@@ -21767,26 +21931,26 @@ var init_controller_sessions = __esm({
21767
21931
 
21768
21932
  // src/core/controller-server.ts
21769
21933
  function privateDirectory2(directory) {
21770
- const absolute2 = import_node_path50.default.resolve(directory);
21771
- let current = import_node_path50.default.parse(absolute2).root;
21772
- for (const part of absolute2.slice(current.length).split(import_node_path50.default.sep)) {
21773
- current = import_node_path50.default.join(current, part);
21934
+ const absolute2 = import_node_path51.default.resolve(directory);
21935
+ let current = import_node_path51.default.parse(absolute2).root;
21936
+ for (const part of absolute2.slice(current.length).split(import_node_path51.default.sep)) {
21937
+ current = import_node_path51.default.join(current, part);
21774
21938
  try {
21775
- const stat2 = import_node_fs39.default.lstatSync(current);
21939
+ const stat2 = import_node_fs40.default.lstatSync(current);
21776
21940
  if (!stat2.isDirectory() || stat2.isSymbolicLink())
21777
21941
  throw new Error("Unsafe controller directory.");
21778
21942
  } catch (error) {
21779
21943
  if (error.code !== "ENOENT") throw error;
21780
- import_node_fs39.default.mkdirSync(current, { mode: 448 });
21944
+ import_node_fs40.default.mkdirSync(current, { mode: 448 });
21781
21945
  }
21782
21946
  }
21783
- const stat = import_node_fs39.default.lstatSync(absolute2);
21947
+ const stat = import_node_fs40.default.lstatSync(absolute2);
21784
21948
  if (stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
21785
21949
  throw new Error("Controller directory is not private.");
21786
21950
  }
21787
21951
  function validateOwnedFile(file, socket) {
21788
21952
  try {
21789
- const stat = import_node_fs39.default.lstatSync(file);
21953
+ const stat = import_node_fs40.default.lstatSync(file);
21790
21954
  if (stat.uid !== process.getuid?.() || (socket ? !stat.isSocket() : !stat.isFile()) || (stat.mode & 63) !== 0)
21791
21955
  throw new Error("Unsafe controller artifact.");
21792
21956
  return true;
@@ -21806,12 +21970,12 @@ async function runController(options) {
21806
21970
  throw new Error("Controller bindings require one owner.");
21807
21971
  if (options.operations && options.createOperations)
21808
21972
  throw new Error("Controller operations have multiple owners.");
21809
- const socketPath = import_node_path50.default.join(options.directory, "control.sock");
21973
+ const socketPath = import_node_path51.default.join(options.directory, "control.sock");
21810
21974
  if (Buffer.byteLength(socketPath) > 103) throw new Error("Controller socket path is too long.");
21811
21975
  privateDirectory2(options.directory);
21812
- const lockPath = import_node_path50.default.join(options.directory, "owner.lock");
21976
+ const lockPath = import_node_path51.default.join(options.directory, "owner.lock");
21813
21977
  try {
21814
- const stat = import_node_fs39.default.lstatSync(lockPath);
21978
+ const stat = import_node_fs40.default.lstatSync(lockPath);
21815
21979
  if (!stat.isFile() || stat.uid !== process.getuid?.())
21816
21980
  throw new Error("Unsafe controller owner lock.");
21817
21981
  } catch (error) {
@@ -21822,7 +21986,7 @@ async function runController(options) {
21822
21986
  await withFileLock(lockPath, { activity: "controller ownership", waitMs: 0 }, async () => {
21823
21987
  const staleSocket = validateOwnedFile(socketPath, true);
21824
21988
  const sessions = new ControllerSessions(store, options.wakeEpoch ?? readHostWakeEpoch);
21825
- if (staleSocket) import_node_fs39.default.unlinkSync(socketPath);
21989
+ if (staleSocket) import_node_fs40.default.unlinkSync(socketPath);
21826
21990
  const incarnation = sessions.read();
21827
21991
  const { resolve, collect } = options.createBindings?.(store.bindingFingerprint()) ?? {
21828
21992
  resolve: options.resolve,
@@ -22368,7 +22532,7 @@ async function runController(options) {
22368
22532
  resolve2();
22369
22533
  });
22370
22534
  });
22371
- import_node_fs39.default.chmodSync(socketPath, 384);
22535
+ import_node_fs40.default.chmodSync(socketPath, 384);
22372
22536
  await new Promise((resolve2) => {
22373
22537
  finish = resolve2;
22374
22538
  options.signal.addEventListener("abort", shutdown, { once: true });
@@ -22387,13 +22551,13 @@ async function runController(options) {
22387
22551
  }
22388
22552
  });
22389
22553
  }
22390
- var import_node_fs39, import_node_net4, import_node_path50, FRAME_BYTES;
22554
+ var import_node_fs40, import_node_net4, import_node_path51, FRAME_BYTES;
22391
22555
  var init_controller_server = __esm({
22392
22556
  "src/core/controller-server.ts"() {
22393
22557
  "use strict";
22394
- import_node_fs39 = __toESM(require("fs"));
22558
+ import_node_fs40 = __toESM(require("fs"));
22395
22559
  import_node_net4 = __toESM(require("net"));
22396
- import_node_path50 = __toESM(require("path"));
22560
+ import_node_path51 = __toESM(require("path"));
22397
22561
  init_controller_monitor();
22398
22562
  init_controller_protocol();
22399
22563
  init_controller_sessions();
@@ -22412,7 +22576,7 @@ __export(controller_exports, {
22412
22576
  runControllerCommand: () => runControllerCommand
22413
22577
  });
22414
22578
  async function runControllerCommand(method, options, repo, dependencies = {}) {
22415
- const directory = import_node_path51.default.join(DEVROUTER_HOME, "controller");
22579
+ const directory = import_node_path52.default.join(DEVROUTER_HOME, "controller");
22416
22580
  try {
22417
22581
  if (method === "run") {
22418
22582
  const controller = new AbortController();
@@ -22458,7 +22622,7 @@ async function runControllerCommand(method, options, repo, dependencies = {}) {
22458
22622
  if (fields2.timeout !== void 0) fields2.timeout = Number(fields2.timeout);
22459
22623
  await controllerRequest(
22460
22624
  directory,
22461
- { method, ...fields2, ...repo ? { path: import_node_path51.default.resolve(repo) } : {} },
22625
+ { method, ...fields2, ...repo ? { path: import_node_path52.default.resolve(repo) } : {} },
22462
22626
  (value) => process.stdout.write(`${JSON.stringify(value)}
22463
22627
  `)
22464
22628
  );
@@ -22473,11 +22637,11 @@ async function runControllerCommand(method, options, repo, dependencies = {}) {
22473
22637
  process.exitCode = 1;
22474
22638
  }
22475
22639
  }
22476
- var import_node_path51;
22640
+ var import_node_path52;
22477
22641
  var init_controller = __esm({
22478
22642
  "src/commands/controller.ts"() {
22479
22643
  "use strict";
22480
- import_node_path51 = __toESM(require("path"));
22644
+ import_node_path52 = __toESM(require("path"));
22481
22645
  init_capacity_collector();
22482
22646
  init_capacity_controller();
22483
22647
  init_capacity_policy();
@@ -22561,7 +22725,7 @@ function refusalLines(refusal) {
22561
22725
  }
22562
22726
  async function runCapacityCommand(method, options, dependencies = {}) {
22563
22727
  if (method !== "reconcile") throw new Error(`Unsupported capacity command: ${method}`);
22564
- const directory = dependencies.directory ?? import_node_path52.default.join(DEVROUTER_HOME, "controller");
22728
+ const directory = dependencies.directory ?? import_node_path53.default.join(DEVROUTER_HOME, "controller");
22565
22729
  const json = Boolean(options.json);
22566
22730
  const emit = (payload, human) => {
22567
22731
  process.stdout.write(json ? `${JSON.stringify(payload, null, 2)}
@@ -22617,11 +22781,11 @@ async function runCapacityCommand(method, options, dependencies = {}) {
22617
22781
  );
22618
22782
  }
22619
22783
  }
22620
- var import_node_path52, POOL_PROBE_TIMEOUT_MS, CapacityReconcileRefusal;
22784
+ var import_node_path53, POOL_PROBE_TIMEOUT_MS, CapacityReconcileRefusal;
22621
22785
  var init_capacity = __esm({
22622
22786
  "src/commands/capacity.ts"() {
22623
22787
  "use strict";
22624
- import_node_path52 = __toESM(require("path"));
22788
+ import_node_path53 = __toESM(require("path"));
22625
22789
  init_capacity_docker_probe();
22626
22790
  init_capacity_policy();
22627
22791
  init_reliability_operation_store();
@@ -22677,28 +22841,28 @@ function replaceDevrouterSection(content) {
22677
22841
  return `${content.slice(0, start)}${buildDevrouterSection()}${content.slice(end)}`;
22678
22842
  }
22679
22843
  function writeRepoFile(repoPath, relPath, content) {
22680
- const absolutePath = (0, import_node_path53.join)(repoPath, relPath);
22681
- (0, import_node_fs40.mkdirSync)((0, import_node_path53.dirname)(absolutePath), { recursive: true });
22682
- (0, import_node_fs40.writeFileSync)(absolutePath, content, "utf-8");
22844
+ const absolutePath = (0, import_node_path54.join)(repoPath, relPath);
22845
+ (0, import_node_fs41.mkdirSync)((0, import_node_path54.dirname)(absolutePath), { recursive: true });
22846
+ (0, import_node_fs41.writeFileSync)(absolutePath, content, "utf-8");
22683
22847
  return absolutePath;
22684
22848
  }
22685
22849
  function ensureAgentsMdSection(repoPath) {
22686
- const filePath = (0, import_node_path53.join)(repoPath, AGENTS_MD);
22687
- if ((0, import_node_fs40.existsSync)(filePath)) {
22688
- const content = (0, import_node_fs40.readFileSync)(filePath, "utf-8");
22850
+ const filePath = (0, import_node_path54.join)(repoPath, AGENTS_MD);
22851
+ if ((0, import_node_fs41.existsSync)(filePath)) {
22852
+ const content = (0, import_node_fs41.readFileSync)(filePath, "utf-8");
22689
22853
  if (content.includes(DEVROUTER_SENTINEL)) {
22690
22854
  const updated = replaceDevrouterSection(content);
22691
22855
  if (updated === void 0 || updated === content) return { path: filePath, written: false };
22692
- (0, import_node_fs40.writeFileSync)(filePath, updated, "utf-8");
22856
+ (0, import_node_fs41.writeFileSync)(filePath, updated, "utf-8");
22693
22857
  return { path: filePath, written: true };
22694
22858
  }
22695
- (0, import_node_fs40.writeFileSync)(filePath, `${content.trimEnd()}
22859
+ (0, import_node_fs41.writeFileSync)(filePath, `${content.trimEnd()}
22696
22860
 
22697
22861
  ${buildDevrouterSection()}
22698
22862
  `, "utf-8");
22699
22863
  return { path: filePath, written: true };
22700
22864
  }
22701
- (0, import_node_fs40.writeFileSync)(filePath, `# AGENTS.md
22865
+ (0, import_node_fs41.writeFileSync)(filePath, `# AGENTS.md
22702
22866
 
22703
22867
  ${buildDevrouterSection()}
22704
22868
  `, "utf-8");
@@ -22708,12 +22872,12 @@ function ensureSkillFile(repoPath) {
22708
22872
  const filePath = writeRepoFile(repoPath, DEVROUTER_SKILL_REL_PATH, DEVROUTER_SKILL_CONTENT);
22709
22873
  return { path: filePath, written: true };
22710
22874
  }
22711
- var import_node_fs40, import_node_path53, DEVROUTER_SENTINEL, DEVROUTER_END_SENTINEL, LEGACY_DEVROUTER_SECTION_END, AGENTS_MD, DEVROUTER_SKILL_REL_PATH, DEVROUTER_SKILL_CONTENT;
22875
+ var import_node_fs41, import_node_path54, DEVROUTER_SENTINEL, DEVROUTER_END_SENTINEL, LEGACY_DEVROUTER_SECTION_END, AGENTS_MD, DEVROUTER_SKILL_REL_PATH, DEVROUTER_SKILL_CONTENT;
22712
22876
  var init_agents_md = __esm({
22713
22877
  "src/core/agents-md.ts"() {
22714
22878
  "use strict";
22715
- import_node_fs40 = require("fs");
22716
- import_node_path53 = require("path");
22879
+ import_node_fs41 = require("fs");
22880
+ import_node_path54 = require("path");
22717
22881
  DEVROUTER_SENTINEL = "<!-- devrouter -->";
22718
22882
  DEVROUTER_END_SENTINEL = "<!-- /devrouter -->";
22719
22883
  LEGACY_DEVROUTER_SECTION_END = "- `devrouter ls`";
@@ -23335,6 +23499,7 @@ function buildOnboardingPrompt(options = {}) {
23335
23499
  "- Managed process reuse fingerprints command argv, workspace identity, and the exact adapter snapshot. Variant and current host-port identity always compose with an explicit --fingerprint. If a non-secret runtime value also affects reuse, set `DEVROUTER_PROCESS_FINGERPRINT_ENV` to its comma-separated environment names; secret-like names are rejected and raw values are never persisted.",
23336
23500
  "- Managed adapters may use devrouter-process ensure --prepare-command <command> for synchronous dependency preparation under the process lock before app launch. Unchanged owned processes skip preparation; it participates in default fingerprints and must not daemonize or detach.",
23337
23501
  "- Host-generated Compose inputs may use managedRuntime.devcontainer.prepareCommand as literal argv. Ensure runs it once from the checkout root before Compose inspection, with a 60-second bound and no implicit shell. Keep .devrouter.yml unchanged. Diagnostics never run it; changed warm mounts require separate verification.",
23502
+ "- Explicit first initialization: managedRuntime.devcontainer.freshBootstrap declares literal command argv, dataVolumes and optional sharedCaches keys. ensure <new-checkout> --initialize-new requires qualified Devsy 1.19.0 or 1.24.0, new local data volumes and repository-owned database identity/emptiness checks. A durable attempt prevents replay after failure, unknown completion, cancellation, stop, replacement or settlement. Ordinary hooks must preserve data. Consult docs/DEVCONTAINER.md for context and mount prerequisites; never clear the attempt fence or infer reset authority.",
23338
23503
  "- Managed Devsy startup on supported local Unix Docker endpoints captures exact stop ownership before application readiness. With that baseline, canonical stop tolerates changed or missing repository configuration and preserves containers and volumes. Older records without a baseline retain configuration-dependent stop; installing a new CLI does not establish historical ownership. Invalid or changed ownership never falls back. Do not replay destructive bootstrap hooks to obtain a baseline.",
23339
23504
  "- Owner status is `present`, `missing`, `locked`, or `conflict`. Dirty or locked full down fails before side effects. `workspace gc` is a dry run; only `--yes` deletes exact eligible missing resources and records, never Git worktrees, branches, or prune state.",
23340
23505
  "- `devrouter workspace cleanup --repo . --inactive-for 30d --check-merged --json` is report-only and never mutates the workspace runtime, routes, ownership, Git, Docker, applications, worktrees, or branches. It reports ownership, workspace runtime registration, runtime state (`running|stopped|busy|not-found|absent|unknown`), checkout, advisory activity, route, and integration evidence. Local DevPod/Devsy list/status checks always run; `--check-merged` alone enables read-only origin and matching GitHub/GitLab checks. Treat `not-found` as stale runtime after Docker pruning; busy, unavailable, or conflicting evidence suppresses destructive suggestions. Explicit `gc`/`down` can remove exact stale registration only after expected-ID `NotFound` proof and ownership revalidation. Runtime `lastUsed` remains advisory.",
@@ -23504,7 +23669,7 @@ var init_ai_prompt = __esm({
23504
23669
  purpose: "Write/update devrouter section in the repo's AGENTS.md and install the devrouter skill."
23505
23670
  },
23506
23671
  {
23507
- command: "devrouter ensure [path] [--profile <name>] [--variant <name>] [--repair] [--open] [--json]",
23672
+ command: "devrouter ensure [path] [--profile <name>] [--variant <name>] [--repair | --initialize-new] [--open] [--json]",
23508
23673
  purpose: "Canonical startup and proof for an exact primary or linked checkout; atomically publishes routes after readiness."
23509
23674
  },
23510
23675
  {
@@ -24222,11 +24387,11 @@ function extractCurrentVersionFromRepoConfig(repoPath) {
24222
24387
  };
24223
24388
  }
24224
24389
  function readPromptDirectory(promptDirPath) {
24225
- if (!import_node_fs41.default.existsSync(promptDirPath)) {
24390
+ if (!import_node_fs42.default.existsSync(promptDirPath)) {
24226
24391
  throw new Error(`Missing ${UPGRADE_PROMPTS_DIR} directory at ${promptDirPath}.`);
24227
24392
  }
24228
24393
  const releases = [];
24229
- const files = import_node_fs41.default.readdirSync(promptDirPath, { withFileTypes: true });
24394
+ const files = import_node_fs42.default.readdirSync(promptDirPath, { withFileTypes: true });
24230
24395
  for (const file of files) {
24231
24396
  if (!file.isFile() || !file.name.endsWith(".md")) {
24232
24397
  continue;
@@ -24236,8 +24401,8 @@ function readPromptDirectory(promptDirPath) {
24236
24401
  continue;
24237
24402
  }
24238
24403
  const version = normalizeVersion(basename);
24239
- const promptPath = import_node_path54.default.join(promptDirPath, file.name);
24240
- const prompt = import_node_fs41.default.readFileSync(promptPath, "utf-8").trim();
24404
+ const promptPath = import_node_path55.default.join(promptDirPath, file.name);
24405
+ const prompt = import_node_fs42.default.readFileSync(promptPath, "utf-8").trim();
24241
24406
  releases.push({ version, prompt, promptPath });
24242
24407
  }
24243
24408
  const deduped = /* @__PURE__ */ new Map();
@@ -24258,19 +24423,19 @@ function listAvailableUpgradeTargets(currentVersion, releases) {
24258
24423
  }
24259
24424
  function resolvePromptDirectory(explicitPath) {
24260
24425
  if (explicitPath) {
24261
- return import_node_path54.default.resolve(explicitPath);
24426
+ return import_node_path55.default.resolve(explicitPath);
24262
24427
  }
24263
- const entryFile = process.argv[1] ? import_node_path54.default.resolve(process.argv[1]) : __filename;
24264
- const entryDir = import_node_path54.default.dirname(entryFile);
24265
- const resolvedEntryDir = import_node_fs41.default.existsSync(entryFile) ? import_node_path54.default.dirname(import_node_fs41.default.realpathSync(entryFile)) : entryDir;
24428
+ const entryFile = process.argv[1] ? import_node_path55.default.resolve(process.argv[1]) : __filename;
24429
+ const entryDir = import_node_path55.default.dirname(entryFile);
24430
+ const resolvedEntryDir = import_node_fs42.default.existsSync(entryFile) ? import_node_path55.default.dirname(import_node_fs42.default.realpathSync(entryFile)) : entryDir;
24266
24431
  const candidates = [
24267
- import_node_path54.default.resolve(resolvedEntryDir, "..", UPGRADE_PROMPTS_DIR),
24268
- import_node_path54.default.resolve(entryDir, "..", UPGRADE_PROMPTS_DIR),
24269
- import_node_path54.default.resolve(__dirname, "..", UPGRADE_PROMPTS_DIR),
24270
- import_node_path54.default.resolve(__dirname, "..", "..", UPGRADE_PROMPTS_DIR),
24271
- import_node_path54.default.resolve(process.cwd(), UPGRADE_PROMPTS_DIR)
24432
+ import_node_path55.default.resolve(resolvedEntryDir, "..", UPGRADE_PROMPTS_DIR),
24433
+ import_node_path55.default.resolve(entryDir, "..", UPGRADE_PROMPTS_DIR),
24434
+ import_node_path55.default.resolve(__dirname, "..", UPGRADE_PROMPTS_DIR),
24435
+ import_node_path55.default.resolve(__dirname, "..", "..", UPGRADE_PROMPTS_DIR),
24436
+ import_node_path55.default.resolve(process.cwd(), UPGRADE_PROMPTS_DIR)
24272
24437
  ];
24273
- const existing = candidates.find((candidate) => import_node_fs41.default.existsSync(candidate));
24438
+ const existing = candidates.find((candidate) => import_node_fs42.default.existsSync(candidate));
24274
24439
  return existing ?? candidates[0];
24275
24440
  }
24276
24441
  function loadUpgradeCatalog(options = {}) {
@@ -24286,12 +24451,12 @@ function loadUpgradeCatalog(options = {}) {
24286
24451
  releases
24287
24452
  };
24288
24453
  }
24289
- var import_node_fs41, import_node_path54, SEMVER_RE, UPGRADE_PROMPTS_DIR;
24454
+ var import_node_fs42, import_node_path55, SEMVER_RE, UPGRADE_PROMPTS_DIR;
24290
24455
  var init_upgrade = __esm({
24291
24456
  "src/core/upgrade.ts"() {
24292
24457
  "use strict";
24293
- import_node_fs41 = __toESM(require("fs"));
24294
- import_node_path54 = __toESM(require("path"));
24458
+ import_node_fs42 = __toESM(require("fs"));
24459
+ import_node_path55 = __toESM(require("path"));
24295
24460
  init_repo_config();
24296
24461
  SEMVER_RE = /^v?(\d+)\.(\d+)\.(\d+)$/;
24297
24462
  UPGRADE_PROMPTS_DIR = "upgrade-prompts";
@@ -24444,7 +24609,7 @@ function isLoopbackHost(host) {
24444
24609
  return host === "127.0.0.1" || host === "localhost" || host === "host.docker.internal";
24445
24610
  }
24446
24611
  function inspectCompose(composeFile, _workspace) {
24447
- if (!import_node_fs42.default.existsSync(composeFile)) {
24612
+ if (!import_node_fs43.default.existsSync(composeFile)) {
24448
24613
  return {
24449
24614
  aliases: [],
24450
24615
  publishedPorts: [],
@@ -24453,8 +24618,8 @@ function inspectCompose(composeFile, _workspace) {
24453
24618
  };
24454
24619
  }
24455
24620
  try {
24456
- const raw = import_node_fs42.default.readFileSync(composeFile, "utf-8");
24457
- const parsed = import_yaml5.default.parse(raw);
24621
+ const raw = import_node_fs43.default.readFileSync(composeFile, "utf-8");
24622
+ const parsed = import_yaml6.default.parse(raw);
24458
24623
  const root = asRecord(parsed);
24459
24624
  const services = asRecord(root?.services);
24460
24625
  const networks = asRecord(root?.networks);
@@ -24550,11 +24715,11 @@ function buildHostPortClaimsCheck(repoPath, workspace) {
24550
24715
  }
24551
24716
  }
24552
24717
  function buildDevcontainerChecks(repoPath, config, workspace) {
24553
- const devcontainerDir = import_node_path55.default.join(repoPath, ".devcontainer");
24554
- if (!import_node_fs42.default.existsSync(devcontainerDir)) {
24718
+ const devcontainerDir = import_node_path56.default.join(repoPath, ".devcontainer");
24719
+ if (!import_node_fs43.default.existsSync(devcontainerDir)) {
24555
24720
  return [];
24556
24721
  }
24557
- const compose = inspectCompose(import_node_path55.default.join(devcontainerDir, "docker-compose.yml"), workspace);
24722
+ const compose = inspectCompose(import_node_path56.default.join(devcontainerDir, "docker-compose.yml"), workspace);
24558
24723
  const checks = [];
24559
24724
  if (compose.parseError) {
24560
24725
  checks.push({
@@ -24581,9 +24746,9 @@ function buildDevcontainerChecks(repoPath, config, workspace) {
24581
24746
  details: compose.publishedPorts.length > 0 ? compose.publishedPorts.join(", ") : void 0,
24582
24747
  suggestion: compose.parseError || compose.publishedPorts.length > 0 ? "Remove published ports and route services through devnet aliases with devrouter proxy apps." : void 0
24583
24748
  });
24584
- const dockerfilePath = import_node_path55.default.join(devcontainerDir, "Dockerfile");
24585
- const dockerfileExists = import_node_fs42.default.existsSync(dockerfilePath);
24586
- const dockerfile = dockerfileExists ? import_node_fs42.default.readFileSync(dockerfilePath, "utf-8") : "";
24749
+ const dockerfilePath = import_node_path56.default.join(devcontainerDir, "Dockerfile");
24750
+ const dockerfileExists = import_node_fs43.default.existsSync(dockerfilePath);
24751
+ const dockerfile = dockerfileExists ? import_node_fs43.default.readFileSync(dockerfilePath, "utf-8") : "";
24587
24752
  const devrouterArtifacts = ["@devrouter/cli", "devrouter-process"].filter(
24588
24753
  (artifact) => dockerfile.includes(artifact)
24589
24754
  );
@@ -24641,13 +24806,13 @@ function buildDevcontainerChecks(repoPath, config, workspace) {
24641
24806
  });
24642
24807
  return checks;
24643
24808
  }
24644
- var import_node_fs42, import_node_path55, import_yaml5;
24809
+ var import_node_fs43, import_node_path56, import_yaml6;
24645
24810
  var init_devcontainer_diagnostics = __esm({
24646
24811
  "src/core/devcontainer-diagnostics.ts"() {
24647
24812
  "use strict";
24648
- import_node_fs42 = __toESM(require("fs"));
24649
- import_node_path55 = __toESM(require("path"));
24650
- import_yaml5 = __toESM(require("yaml"));
24813
+ import_node_fs43 = __toESM(require("fs"));
24814
+ import_node_path56 = __toESM(require("path"));
24815
+ import_yaml6 = __toESM(require("yaml"));
24651
24816
  init_devcontainer_profile();
24652
24817
  init_host_port_claims();
24653
24818
  init_managed_post_start();
@@ -24742,11 +24907,11 @@ function reconcileRouteRunConflict(repoPath, app) {
24742
24907
  }
24743
24908
  }
24744
24909
  }
24745
- var import_node_fs43;
24910
+ var import_node_fs44;
24746
24911
  var init_route_state = __esm({
24747
24912
  "src/core/route-state.ts"() {
24748
24913
  "use strict";
24749
- import_node_fs43 = __toESM(require("fs"));
24914
+ import_node_fs44 = __toESM(require("fs"));
24750
24915
  init_host_routes();
24751
24916
  init_workspace();
24752
24917
  }
@@ -24921,7 +25086,7 @@ function toRepoStatus(repoPath) {
24921
25086
  const resolvedRepoPath = resolveRepoPath(repoPath);
24922
25087
  const configPath = getRepoConfigPath(resolvedRepoPath);
24923
25088
  const explicitRepo = typeof repoPath === "string" && repoPath.trim().length > 0;
24924
- const configExists = import_node_fs44.default.existsSync(configPath);
25089
+ const configExists = import_node_fs45.default.existsSync(configPath);
24925
25090
  if (!explicitRepo && !configExists) {
24926
25091
  return void 0;
24927
25092
  }
@@ -25074,11 +25239,11 @@ async function collectRouterStatus(repoPath) {
25074
25239
  }
25075
25240
  };
25076
25241
  }
25077
- var import_node_fs44;
25242
+ var import_node_fs45;
25078
25243
  var init_status = __esm({
25079
25244
  "src/core/status.ts"() {
25080
25245
  "use strict";
25081
- import_node_fs44 = __toESM(require("fs"));
25246
+ import_node_fs45 = __toESM(require("fs"));
25082
25247
  init_docker();
25083
25248
  init_managed_runtime_state();
25084
25249
  init_managed_runtime_status();
@@ -25088,14 +25253,14 @@ var init_status = __esm({
25088
25253
  });
25089
25254
 
25090
25255
  // src/core/idle-sweep.ts
25091
- function idleSweepPaths(home = import_node_os6.default.homedir(), devrouterHome = DEVROUTER_HOME) {
25092
- const directory = import_node_path56.default.join(devrouterHome, "idle");
25256
+ function idleSweepPaths(home = import_node_os7.default.homedir(), devrouterHome = DEVROUTER_HOME) {
25257
+ const directory = import_node_path57.default.join(devrouterHome, "idle");
25093
25258
  return {
25094
- plist: import_node_path56.default.join(home, "Library", "LaunchAgents", `${IDLE_SWEEP_LABEL}.plist`),
25259
+ plist: import_node_path57.default.join(home, "Library", "LaunchAgents", `${IDLE_SWEEP_LABEL}.plist`),
25095
25260
  directory,
25096
- log: import_node_path56.default.join(directory, "sweep.log"),
25097
- lastSweep: import_node_path56.default.join(directory, "last-sweep.json"),
25098
- lock: import_node_path56.default.join(directory, "sweep.lock")
25261
+ log: import_node_path57.default.join(directory, "sweep.log"),
25262
+ lastSweep: import_node_path57.default.join(directory, "last-sweep.json"),
25263
+ lock: import_node_path57.default.join(directory, "sweep.lock")
25099
25264
  };
25100
25265
  }
25101
25266
  function xml(value) {
@@ -25140,14 +25305,14 @@ ${env}
25140
25305
  function readInstalledIdleFor(plistPath) {
25141
25306
  let contents;
25142
25307
  try {
25143
- contents = import_node_fs45.default.readFileSync(plistPath, "utf-8");
25308
+ contents = import_node_fs46.default.readFileSync(plistPath, "utf-8");
25144
25309
  } catch {
25145
25310
  return void 0;
25146
25311
  }
25147
25312
  return /<string>--idle-for<\/string>\s*<string>([^<]+)<\/string>/.exec(contents)?.[1] ?? "unknown";
25148
25313
  }
25149
25314
  function locateTool(name) {
25150
- const result = (0, import_node_child_process34.spawnSync)("/bin/sh", ["-c", `command -v ${name}`], { encoding: "utf-8" });
25315
+ const result = (0, import_node_child_process35.spawnSync)("/bin/sh", ["-c", `command -v ${name}`], { encoding: "utf-8" });
25151
25316
  const found = result.status === 0 ? result.stdout.trim() : "";
25152
25317
  return found.startsWith("/") ? found : void 0;
25153
25318
  }
@@ -25155,7 +25320,7 @@ function resolveIdleSweepEnvironment(env = process.env, locate = locateTool) {
25155
25320
  const docker = locate("docker");
25156
25321
  if (!docker)
25157
25322
  throw new Error("docker is not on PATH; the idle sweep cannot inspect environments.");
25158
- const toolDirs = [docker, locate("devsy"), locate("devpod"), process.execPath].filter((tool) => Boolean(tool)).map((tool) => import_node_path56.default.dirname(tool));
25323
+ const toolDirs = [docker, locate("devsy"), locate("devpod"), process.execPath].filter((tool) => Boolean(tool)).map((tool) => import_node_path57.default.dirname(tool));
25159
25324
  const environment = {
25160
25325
  PATH: [.../* @__PURE__ */ new Set([...toolDirs, ...SYSTEM_PATH])].join(":")
25161
25326
  };
@@ -25173,9 +25338,9 @@ function installIdleSweep(input2) {
25173
25338
  throw new Error("The idle sweep uses launchd and is only available on macOS.");
25174
25339
  const paths = input2.paths ?? idleSweepPaths();
25175
25340
  const launchctl = input2.launchctl ?? defaultLaunchctl;
25176
- const entryPath = input2.entryPath ?? import_node_fs45.default.realpathSync(process.argv[1]);
25177
- import_node_fs45.default.mkdirSync(paths.directory, { recursive: true, mode: 448 });
25178
- import_node_fs45.default.mkdirSync(import_node_path56.default.dirname(paths.plist), { recursive: true });
25341
+ const entryPath = input2.entryPath ?? import_node_fs46.default.realpathSync(process.argv[1]);
25342
+ import_node_fs46.default.mkdirSync(paths.directory, { recursive: true, mode: 448 });
25343
+ import_node_fs46.default.mkdirSync(import_node_path57.default.dirname(paths.plist), { recursive: true });
25179
25344
  const contents = renderIdleSweepPlist({
25180
25345
  nodePath: process.execPath,
25181
25346
  entryPath,
@@ -25184,7 +25349,7 @@ function installIdleSweep(input2) {
25184
25349
  logPath: paths.log
25185
25350
  });
25186
25351
  launchctl(["bootout", `${launchDomain()}/${IDLE_SWEEP_LABEL}`]);
25187
- import_node_fs45.default.writeFileSync(paths.plist, contents, { mode: 420 });
25352
+ import_node_fs46.default.writeFileSync(paths.plist, contents, { mode: 420 });
25188
25353
  const loaded = launchctl(["bootstrap", launchDomain(), paths.plist]);
25189
25354
  if (loaded.status !== 0)
25190
25355
  throw new Error(
@@ -25194,9 +25359,9 @@ function installIdleSweep(input2) {
25194
25359
  }
25195
25360
  function removeIdleSweep(input2 = {}) {
25196
25361
  const paths = input2.paths ?? idleSweepPaths();
25197
- if (!import_node_fs45.default.existsSync(paths.plist)) return false;
25362
+ if (!import_node_fs46.default.existsSync(paths.plist)) return false;
25198
25363
  (input2.launchctl ?? defaultLaunchctl)(["bootout", `${launchDomain()}/${IDLE_SWEEP_LABEL}`]);
25199
- import_node_fs45.default.rmSync(paths.plist, { force: true });
25364
+ import_node_fs46.default.rmSync(paths.plist, { force: true });
25200
25365
  return true;
25201
25366
  }
25202
25367
  function summarizeSweep(idleFor, stops, error) {
@@ -25211,13 +25376,13 @@ function summarizeSweep(idleFor, stops, error) {
25211
25376
  };
25212
25377
  }
25213
25378
  function writeLastSweep(record6, paths = idleSweepPaths()) {
25214
- import_node_fs45.default.mkdirSync(paths.directory, { recursive: true, mode: 448 });
25215
- import_node_fs45.default.writeFileSync(paths.lastSweep, `${JSON.stringify(record6, null, 2)}
25379
+ import_node_fs46.default.mkdirSync(paths.directory, { recursive: true, mode: 448 });
25380
+ import_node_fs46.default.writeFileSync(paths.lastSweep, `${JSON.stringify(record6, null, 2)}
25216
25381
  `, { mode: 384 });
25217
25382
  }
25218
25383
  function readLastSweep(paths) {
25219
25384
  try {
25220
- return JSON.parse(import_node_fs45.default.readFileSync(paths.lastSweep, "utf-8"));
25385
+ return JSON.parse(import_node_fs46.default.readFileSync(paths.lastSweep, "utf-8"));
25221
25386
  } catch {
25222
25387
  return void 0;
25223
25388
  }
@@ -25262,20 +25427,20 @@ function idleSweepCheck(paths = idleSweepPaths(), nowMs = Date.now()) {
25262
25427
  ...stale || last.failed > 0 ? { suggestion: `Inspect: ${paths.log}` } : {}
25263
25428
  };
25264
25429
  }
25265
- var import_node_child_process34, import_node_fs45, import_node_os6, import_node_path56, IDLE_SWEEP_LABEL, SWEEP_INTERVAL_SECONDS, SYSTEM_PATH, defaultLaunchctl;
25430
+ var import_node_child_process35, import_node_fs46, import_node_os7, import_node_path57, IDLE_SWEEP_LABEL, SWEEP_INTERVAL_SECONDS, SYSTEM_PATH, defaultLaunchctl;
25266
25431
  var init_idle_sweep = __esm({
25267
25432
  "src/core/idle-sweep.ts"() {
25268
25433
  "use strict";
25269
- import_node_child_process34 = require("child_process");
25270
- import_node_fs45 = __toESM(require("fs"));
25271
- import_node_os6 = __toESM(require("os"));
25272
- import_node_path56 = __toESM(require("path"));
25434
+ import_node_child_process35 = require("child_process");
25435
+ import_node_fs46 = __toESM(require("fs"));
25436
+ import_node_os7 = __toESM(require("os"));
25437
+ import_node_path57 = __toESM(require("path"));
25273
25438
  init_router();
25274
25439
  IDLE_SWEEP_LABEL = "dev.devrouter.idle-stop";
25275
25440
  SWEEP_INTERVAL_SECONDS = 300;
25276
25441
  SYSTEM_PATH = ["/usr/bin", "/bin", "/usr/sbin", "/sbin"];
25277
25442
  defaultLaunchctl = (args) => {
25278
- const result = (0, import_node_child_process34.spawnSync)("launchctl", args, { encoding: "utf-8" });
25443
+ const result = (0, import_node_child_process35.spawnSync)("launchctl", args, { encoding: "utf-8" });
25279
25444
  return { status: result.status, stderr: result.stderr || result.error?.message || "" };
25280
25445
  };
25281
25446
  }
@@ -25289,7 +25454,7 @@ function outputFromResult(result) {
25289
25454
  return output2.length > 0 ? output2 : void 0;
25290
25455
  }
25291
25456
  function runTool(command, args = []) {
25292
- const result = (0, import_node_child_process35.spawnSync)(command, args, {
25457
+ const result = (0, import_node_child_process36.spawnSync)(command, args, {
25293
25458
  encoding: "utf-8"
25294
25459
  });
25295
25460
  if (result.error) {
@@ -25346,12 +25511,12 @@ function parseMinimumNodeMajor(value) {
25346
25511
  return Number(match[1]);
25347
25512
  }
25348
25513
  function readPackageJson(repoPath) {
25349
- const packagePath = import_node_path57.default.join(repoPath, "package.json");
25350
- if (!import_node_fs46.default.existsSync(packagePath)) {
25514
+ const packagePath = import_node_path58.default.join(repoPath, "package.json");
25515
+ if (!import_node_fs47.default.existsSync(packagePath)) {
25351
25516
  return void 0;
25352
25517
  }
25353
25518
  try {
25354
- return JSON.parse(import_node_fs46.default.readFileSync(packagePath, "utf-8"));
25519
+ return JSON.parse(import_node_fs47.default.readFileSync(packagePath, "utf-8"));
25355
25520
  } catch {
25356
25521
  return void 0;
25357
25522
  }
@@ -25456,17 +25621,17 @@ function realpathOrUndefined(value) {
25456
25621
  return void 0;
25457
25622
  }
25458
25623
  try {
25459
- return import_node_fs46.default.realpathSync(value);
25624
+ return import_node_fs47.default.realpathSync(value);
25460
25625
  } catch {
25461
25626
  return void 0;
25462
25627
  }
25463
25628
  }
25464
25629
  function isExecutableFile(filePath) {
25465
25630
  try {
25466
- if (!import_node_fs46.default.statSync(filePath).isFile()) {
25631
+ if (!import_node_fs47.default.statSync(filePath).isFile()) {
25467
25632
  return false;
25468
25633
  }
25469
- import_node_fs46.default.accessSync(filePath, import_node_fs46.default.constants.X_OK);
25634
+ import_node_fs47.default.accessSync(filePath, import_node_fs47.default.constants.X_OK);
25470
25635
  return true;
25471
25636
  } catch {
25472
25637
  return false;
@@ -25474,13 +25639,13 @@ function isExecutableFile(filePath) {
25474
25639
  }
25475
25640
  function displayPath(filePath) {
25476
25641
  const home = process.env.HOME;
25477
- if (!home || home.length < 2 || !filePath.startsWith(`${home}${import_node_path57.default.sep}`)) {
25642
+ if (!home || home.length < 2 || !filePath.startsWith(`${home}${import_node_path58.default.sep}`)) {
25478
25643
  return filePath;
25479
25644
  }
25480
25645
  return `~${filePath.slice(home.length)}`;
25481
25646
  }
25482
25647
  function probeInstalledCliVersion(executable) {
25483
- const result = (0, import_node_child_process35.spawnSync)(executable, ["-V"], {
25648
+ const result = (0, import_node_child_process36.spawnSync)(executable, ["-V"], {
25484
25649
  encoding: "utf-8",
25485
25650
  timeout: CLI_PATH_PROBE_TIMEOUT_MS
25486
25651
  });
@@ -25491,7 +25656,7 @@ function probeInstalledCliVersion(executable) {
25491
25656
  }
25492
25657
  function buildCliPathCheck(options = {}) {
25493
25658
  const id5 = "global.cli-path";
25494
- const runningVersion = options.runningVersion ?? (true ? "0.6.1" : "0.0.0-dev");
25659
+ const runningVersion = options.runningVersion ?? (true ? "0.6.3" : "0.0.0-dev");
25495
25660
  if (runningVersion === "0.0.0-dev") {
25496
25661
  return {
25497
25662
  id: id5,
@@ -25504,11 +25669,11 @@ function buildCliPathCheck(options = {}) {
25504
25669
  const probeVersion = options.probeVersion ?? probeInstalledCliVersion;
25505
25670
  const entries = [];
25506
25671
  const seen = /* @__PURE__ */ new Set();
25507
- for (const dir of (options.pathValue ?? process.env.PATH ?? "").split(import_node_path57.default.delimiter)) {
25672
+ for (const dir of (options.pathValue ?? process.env.PATH ?? "").split(import_node_path58.default.delimiter)) {
25508
25673
  if (dir.length === 0) {
25509
25674
  continue;
25510
25675
  }
25511
- const executable = import_node_path57.default.join(dir, "devrouter");
25676
+ const executable = import_node_path58.default.join(dir, "devrouter");
25512
25677
  if (!isExecutableFile(executable)) {
25513
25678
  continue;
25514
25679
  }
@@ -25665,13 +25830,13 @@ function buildGlobalToolChecksWithoutTelemetry(repoPath) {
25665
25830
  checks.push(nodeToolchainCheck(repoPath));
25666
25831
  return checks;
25667
25832
  }
25668
- var import_node_child_process35, import_node_fs46, import_node_path57, DEVSY_DISABLE_TELEMETRY, CLI_PATH_PROBE_TIMEOUT_MS, INSTALLED_CLI_VERSION_RE;
25833
+ var import_node_child_process36, import_node_fs47, import_node_path58, DEVSY_DISABLE_TELEMETRY, CLI_PATH_PROBE_TIMEOUT_MS, INSTALLED_CLI_VERSION_RE;
25669
25834
  var init_tool_diagnostics = __esm({
25670
25835
  "src/core/tool-diagnostics.ts"() {
25671
25836
  "use strict";
25672
- import_node_child_process35 = require("child_process");
25673
- import_node_fs46 = __toESM(require("fs"));
25674
- import_node_path57 = __toESM(require("path"));
25837
+ import_node_child_process36 = require("child_process");
25838
+ import_node_fs47 = __toESM(require("fs"));
25839
+ import_node_path58 = __toESM(require("path"));
25675
25840
  init_devsy_agent();
25676
25841
  init_idle_sweep();
25677
25842
  init_repo_config();
@@ -25692,10 +25857,10 @@ function inRepositoryWorkspaceScope(repoPath, worktreePath, livePaths) {
25692
25857
  if (livePaths.some((candidate) => sameWorkspacePath(candidate, worktreePath))) return true;
25693
25858
  const comparableRepo = comparableWorkspacePath(repoPath);
25694
25859
  const comparableWorktree = comparableWorkspacePath(worktreePath);
25695
- const localRoot = import_node_path58.default.join(comparableRepo, "trees") + import_node_path58.default.sep;
25860
+ const localRoot = import_node_path59.default.join(comparableRepo, "trees") + import_node_path59.default.sep;
25696
25861
  if (comparableWorktree.startsWith(localRoot)) return true;
25697
- const legacyPrefix = `${import_node_path58.default.basename(comparableRepo)}-`;
25698
- return import_node_path58.default.dirname(comparableWorktree) === import_node_path58.default.dirname(comparableRepo) && import_node_path58.default.basename(comparableWorktree).startsWith(legacyPrefix);
25862
+ const legacyPrefix = `${import_node_path59.default.basename(comparableRepo)}-`;
25863
+ return import_node_path59.default.dirname(comparableWorktree) === import_node_path59.default.dirname(comparableRepo) && import_node_path59.default.basename(comparableWorktree).startsWith(legacyPrefix);
25699
25864
  }
25700
25865
  function previewActions(devpodStatus, routeCount, includeRecord) {
25701
25866
  const actions = [
@@ -25947,11 +26112,11 @@ function applyWorkspaceGc(plan) {
25947
26112
  candidates
25948
26113
  };
25949
26114
  }
25950
- var import_node_path58;
26115
+ var import_node_path59;
25951
26116
  var init_workspace_gc = __esm({
25952
26117
  "src/core/workspace-gc.ts"() {
25953
26118
  "use strict";
25954
- import_node_path58 = __toESM(require("path"));
26119
+ import_node_path59 = __toESM(require("path"));
25955
26120
  init_devpod_mutation();
25956
26121
  init_devpod_workspaces();
25957
26122
  init_host_routes();
@@ -26021,12 +26186,12 @@ function inspectPostgresCredentials(repoPath, config) {
26021
26186
  parseErrors.push(`${app.name}: ${composeFile} (${message})`);
26022
26187
  continue;
26023
26188
  }
26024
- if (!import_node_fs47.default.existsSync(absolutePath)) {
26189
+ if (!import_node_fs48.default.existsSync(absolutePath)) {
26025
26190
  continue;
26026
26191
  }
26027
26192
  try {
26028
- const raw = import_node_fs47.default.readFileSync(absolutePath, "utf-8");
26029
- const parsed = import_yaml6.default.parse(raw);
26193
+ const raw = import_node_fs48.default.readFileSync(absolutePath, "utf-8");
26194
+ const parsed = import_yaml7.default.parse(raw);
26030
26195
  const root = asRecord2(parsed);
26031
26196
  const services = asRecord2(root?.services);
26032
26197
  const service = asRecord2(services?.[app.docker.service]);
@@ -26389,7 +26554,7 @@ async function buildDoctorReport(options = {}) {
26389
26554
  suggestion: `Remedy ${expansionNotice.remedy.code}: set a named default profile other than '${expansionNotice.profile}'${remedyCandidates}.`
26390
26555
  });
26391
26556
  }
26392
- const cliVersion = true ? "0.6.1" : "0.0.0-dev";
26557
+ const cliVersion = true ? "0.6.3" : "0.0.0-dev";
26393
26558
  const configVersion = config.devrouter?.version;
26394
26559
  const installedKnown = cliVersion !== "0.0.0-dev";
26395
26560
  const versionComparison = installedKnown && configVersion ? compareSemver(configVersion, cliVersion) : void 0;
@@ -26425,9 +26590,9 @@ async function buildDoctorReport(options = {}) {
26425
26590
  (app) => app.docker.composeFiles.map((filePath) => ({
26426
26591
  app: app.name,
26427
26592
  filePath,
26428
- absolutePath: import_node_path59.default.resolve(repo.path, filePath)
26593
+ absolutePath: import_node_path60.default.resolve(repo.path, filePath)
26429
26594
  }))
26430
- ).filter((entry) => !import_node_fs47.default.existsSync(entry.absolutePath));
26595
+ ).filter((entry) => !import_node_fs48.default.existsSync(entry.absolutePath));
26431
26596
  addCheck(checks, {
26432
26597
  id: "repo.compose-files",
26433
26598
  level: missingComposeFiles.length === 0 ? "ok" : "error",
@@ -26470,8 +26635,8 @@ async function buildDoctorReport(options = {}) {
26470
26635
  const missingHostCwds = config.apps.filter((app) => app.runtime === "host").map((app) => ({
26471
26636
  app: app.name,
26472
26637
  cwd: app.hostRun.cwd,
26473
- absolutePath: import_node_path59.default.resolve(repo.path, app.hostRun.cwd)
26474
- })).filter((entry) => !import_node_fs47.default.existsSync(entry.absolutePath));
26638
+ absolutePath: import_node_path60.default.resolve(repo.path, app.hostRun.cwd)
26639
+ })).filter((entry) => !import_node_fs48.default.existsSync(entry.absolutePath));
26475
26640
  addCheck(checks, {
26476
26641
  id: "repo.host-cwd",
26477
26642
  level: missingHostCwds.length === 0 ? "ok" : "error",
@@ -26615,13 +26780,13 @@ async function buildDoctorReport(options = {}) {
26615
26780
  nextSteps
26616
26781
  };
26617
26782
  }
26618
- var import_node_fs47, import_node_path59, import_yaml6, POSTGRES_DEFAULTS, CAPACITY_RECOVERY_BY_CAUSE;
26783
+ var import_node_fs48, import_node_path60, import_yaml7, POSTGRES_DEFAULTS, CAPACITY_RECOVERY_BY_CAUSE;
26619
26784
  var init_doctor = __esm({
26620
26785
  "src/core/doctor.ts"() {
26621
26786
  "use strict";
26622
- import_node_fs47 = __toESM(require("fs"));
26623
- import_node_path59 = __toESM(require("path"));
26624
- import_yaml6 = __toESM(require("yaml"));
26787
+ import_node_fs48 = __toESM(require("fs"));
26788
+ import_node_path60 = __toESM(require("path"));
26789
+ import_yaml7 = __toESM(require("yaml"));
26625
26790
  init_capacity_store();
26626
26791
  init_docker();
26627
26792
  init_host_routes();
@@ -27016,12 +27181,12 @@ function parseSsPortListeners(stdout, port) {
27016
27181
  return listeners;
27017
27182
  }
27018
27183
  function findPortListeners(port) {
27019
- const result = (0, import_node_child_process36.spawnSync)("lsof", ["-nP", `-iTCP:${port}`, "-sTCP:LISTEN"], {
27184
+ const result = (0, import_node_child_process37.spawnSync)("lsof", ["-nP", `-iTCP:${port}`, "-sTCP:LISTEN"], {
27020
27185
  encoding: "utf-8"
27021
27186
  });
27022
27187
  if (result.error && result.error.code === "ENOENT") {
27023
27188
  if (process.platform === "linux") {
27024
- const ssResult = (0, import_node_child_process36.spawnSync)("ss", ["-H", "-lntp", "-p"], { encoding: "utf-8" });
27189
+ const ssResult = (0, import_node_child_process37.spawnSync)("ss", ["-H", "-lntp", "-p"], { encoding: "utf-8" });
27025
27190
  if (ssResult.status === 0 && ssResult.stdout) {
27026
27191
  return parseSsPortListeners(ssResult.stdout, port);
27027
27192
  }
@@ -27050,11 +27215,11 @@ function findPortListeners(port) {
27050
27215
  };
27051
27216
  });
27052
27217
  }
27053
- var import_node_child_process36;
27218
+ var import_node_child_process37;
27054
27219
  var init_ports = __esm({
27055
27220
  "src/util/ports.ts"() {
27056
27221
  "use strict";
27057
- import_node_child_process36 = require("child_process");
27222
+ import_node_child_process37 = require("child_process");
27058
27223
  }
27059
27224
  });
27060
27225
 
@@ -27140,6 +27305,7 @@ async function runEnsureCommand(options) {
27140
27305
  quiet: Boolean(options.json),
27141
27306
  profile: options.profile,
27142
27307
  ...options.repair ? { repair: true } : {},
27308
+ ...options.initializeNew ? { initializeNew: true } : {},
27143
27309
  ...options.variant !== void 0 ? { variant: options.variant } : {}
27144
27310
  };
27145
27311
  const result = await superviseLifecycle(
@@ -27327,7 +27493,7 @@ function resolveLoopback(app, repoPath, evidence) {
27327
27493
  if (!evidence.containers) {
27328
27494
  return { loopback: null, loopbackReason: "unavailable" };
27329
27495
  }
27330
- const devcontainerDir = import_node_path60.default.join(repoPath, ".devcontainer");
27496
+ const devcontainerDir = import_node_path61.default.join(repoPath, ".devcontainer");
27331
27497
  const owners = evidence.containers.filter(
27332
27498
  (container) => sameWorkspacePath(
27333
27499
  container.labels["com.docker.compose.project.working_dir"] ?? "",
@@ -27387,13 +27553,13 @@ function buildHostEnvReport(input2) {
27387
27553
  };
27388
27554
  }
27389
27555
  function listDevcontainerContainers(repoPath) {
27390
- const listed = (0, import_node_child_process37.spawnSync)(
27556
+ const listed = (0, import_node_child_process38.spawnSync)(
27391
27557
  "docker",
27392
27558
  [
27393
27559
  "ps",
27394
27560
  "-a",
27395
27561
  "--filter",
27396
- `label=com.docker.compose.project.working_dir=${import_node_path60.default.join(repoPath, ".devcontainer")}`,
27562
+ `label=com.docker.compose.project.working_dir=${import_node_path61.default.join(repoPath, ".devcontainer")}`,
27397
27563
  "--format",
27398
27564
  "{{.ID}}"
27399
27565
  ],
@@ -27408,7 +27574,7 @@ function listDevcontainerContainers(repoPath) {
27408
27574
  }
27409
27575
  }
27410
27576
  function dockerPortBindings(containerId, port) {
27411
- const result = (0, import_node_child_process37.spawnSync)("docker", ["port", containerId, `${port}/tcp`], {
27577
+ const result = (0, import_node_child_process38.spawnSync)("docker", ["port", containerId, `${port}/tcp`], {
27412
27578
  encoding: "utf-8",
27413
27579
  ...networkDockerOptions()
27414
27580
  });
@@ -27523,12 +27689,12 @@ function hostEnvVariables(report, processEnv) {
27523
27689
  }
27524
27690
  return variables;
27525
27691
  }
27526
- var import_node_child_process37, import_node_path60, LOOPBACK_UPSTREAM_HOSTS, ROUTED_TCP_PARAMS;
27692
+ var import_node_child_process38, import_node_path61, LOOPBACK_UPSTREAM_HOSTS, ROUTED_TCP_PARAMS;
27527
27693
  var init_host_env = __esm({
27528
27694
  "src/core/host-env.ts"() {
27529
27695
  "use strict";
27530
- import_node_child_process37 = require("child_process");
27531
- import_node_path60 = __toESM(require("path"));
27696
+ import_node_child_process38 = require("child_process");
27697
+ import_node_path61 = __toESM(require("path"));
27532
27698
  init_devcontainer_profile();
27533
27699
  init_devpod_environment();
27534
27700
  init_docker();
@@ -27625,7 +27791,7 @@ function hostCommandEnvironment(report, processEnv) {
27625
27791
  }
27626
27792
  function runHostCommand(command, env) {
27627
27793
  return new Promise((resolve) => {
27628
- const child = (0, import_node_child_process38.spawn)(command[0], command.slice(1), { env, stdio: "inherit" });
27794
+ const child = (0, import_node_child_process39.spawn)(command[0], command.slice(1), { env, stdio: "inherit" });
27629
27795
  const forward = (signal) => () => child.kill(signal);
27630
27796
  const handlers = [
27631
27797
  ["SIGINT", forward("SIGINT")],
@@ -27645,7 +27811,7 @@ function runHostCommand(command, env) {
27645
27811
  settle(error.code === "ENOENT" ? 127 : 126);
27646
27812
  });
27647
27813
  child.on("exit", (code, signal) => {
27648
- settle(code ?? 128 + (signal ? import_node_os7.constants.signals[signal] ?? 0 : 0));
27814
+ settle(code ?? 128 + (signal ? import_node_os8.constants.signals[signal] ?? 0 : 0));
27649
27815
  });
27650
27816
  });
27651
27817
  }
@@ -27665,12 +27831,12 @@ async function runEnvCommand(invocation) {
27665
27831
  }
27666
27832
  process.stdout.write(formatEnvReport(report));
27667
27833
  }
27668
- var import_node_child_process38, import_node_os7, HOST_PORT_ENV_PREFIX;
27834
+ var import_node_child_process39, import_node_os8, HOST_PORT_ENV_PREFIX;
27669
27835
  var init_env = __esm({
27670
27836
  "src/commands/env.ts"() {
27671
27837
  "use strict";
27672
- import_node_child_process38 = require("child_process");
27673
- import_node_os7 = require("os");
27838
+ import_node_child_process39 = require("child_process");
27839
+ import_node_os8 = require("os");
27674
27840
  init_host_env();
27675
27841
  init_managed_host_ports();
27676
27842
  init_output();
@@ -27719,8 +27885,8 @@ var init_status2 = __esm({
27719
27885
 
27720
27886
  // src/core/harness-continuation.ts
27721
27887
  function harnessContinuationPath(repoRoot) {
27722
- const key = (0, import_node_crypto29.createHash)("sha256").update(comparableWorkspacePath(repoRoot)).digest("hex");
27723
- return import_node_path61.default.join(DEVROUTER_HOME, "harness", `${key}.json`);
27888
+ const key = (0, import_node_crypto30.createHash)("sha256").update(comparableWorkspacePath(repoRoot)).digest("hex");
27889
+ return import_node_path62.default.join(DEVROUTER_HOME, "harness", `${key}.json`);
27724
27890
  }
27725
27891
  function counter4(value) {
27726
27892
  return Number.isSafeInteger(value) && value >= 0;
@@ -27733,7 +27899,7 @@ function validEntry(value) {
27733
27899
  function readLedgerSync(file, nowMs) {
27734
27900
  let stat;
27735
27901
  try {
27736
- stat = import_node_fs48.default.lstatSync(file);
27902
+ stat = import_node_fs49.default.lstatSync(file);
27737
27903
  } catch {
27738
27904
  return [];
27739
27905
  }
@@ -27741,7 +27907,7 @@ function readLedgerSync(file, nowMs) {
27741
27907
  if (stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0) return [];
27742
27908
  let parsed;
27743
27909
  try {
27744
- parsed = JSON.parse(import_node_fs48.default.readFileSync(file, "utf-8"));
27910
+ parsed = JSON.parse(import_node_fs49.default.readFileSync(file, "utf-8"));
27745
27911
  } catch {
27746
27912
  return [];
27747
27913
  }
@@ -27761,7 +27927,7 @@ function claimHarnessContinuation(repoRoot, claim2) {
27761
27927
  const nowMs = claim2.nowMs ?? Date.now();
27762
27928
  try {
27763
27929
  const file = harnessContinuationPath(repoRoot);
27764
- import_node_fs48.default.mkdirSync(import_node_path61.default.dirname(file), { recursive: true, mode: 448 });
27930
+ import_node_fs49.default.mkdirSync(import_node_path62.default.dirname(file), { recursive: true, mode: 448 });
27765
27931
  return withFileLockSync(
27766
27932
  `${file}.lock`,
27767
27933
  { activity: "harness continuation claim", waitMs: HARNESS_CONTINUATION_LOCK_WAIT_MS },
@@ -27793,7 +27959,7 @@ function settleHarnessContinuation(repoRoot, toolUseId, settlement) {
27793
27959
  const nowMs = settlement.nowMs ?? Date.now();
27794
27960
  try {
27795
27961
  const file = harnessContinuationPath(repoRoot);
27796
- import_node_fs48.default.mkdirSync(import_node_path61.default.dirname(file), { recursive: true, mode: 448 });
27962
+ import_node_fs49.default.mkdirSync(import_node_path62.default.dirname(file), { recursive: true, mode: 448 });
27797
27963
  withFileLockSync(
27798
27964
  `${file}.lock`,
27799
27965
  { activity: "harness continuation settlement", waitMs: HARNESS_CONTINUATION_LOCK_WAIT_MS },
@@ -27817,13 +27983,13 @@ function settleHarnessContinuation(repoRoot, toolUseId, settlement) {
27817
27983
  } catch {
27818
27984
  }
27819
27985
  }
27820
- var import_node_crypto29, import_node_fs48, import_node_path61, HARNESS_CONTINUATION_MAX_ENTRIES, HARNESS_CONTINUATION_TTL_MS, HARNESS_CONTINUATION_LOCK_WAIT_MS, MAX_LEDGER_BYTES, MAX_TOOL_USE_ID_LENGTH, MAX_PHASE_LENGTH;
27986
+ var import_node_crypto30, import_node_fs49, import_node_path62, HARNESS_CONTINUATION_MAX_ENTRIES, HARNESS_CONTINUATION_TTL_MS, HARNESS_CONTINUATION_LOCK_WAIT_MS, MAX_LEDGER_BYTES, MAX_TOOL_USE_ID_LENGTH, MAX_PHASE_LENGTH;
27821
27987
  var init_harness_continuation = __esm({
27822
27988
  "src/core/harness-continuation.ts"() {
27823
27989
  "use strict";
27824
- import_node_crypto29 = require("crypto");
27825
- import_node_fs48 = __toESM(require("fs"));
27826
- import_node_path61 = __toESM(require("path"));
27990
+ import_node_crypto30 = require("crypto");
27991
+ import_node_fs49 = __toESM(require("fs"));
27992
+ import_node_path62 = __toESM(require("path"));
27827
27993
  init_atomic_file();
27828
27994
  init_file_lock();
27829
27995
  init_router();
@@ -27842,12 +28008,12 @@ function isTransitionalHarnessPhase(phase) {
27842
28008
  return TRANSITIONAL_PHASES.includes(phase);
27843
28009
  }
27844
28010
  function findHarnessGateRepoRoot(cwd) {
27845
- let current = import_node_path62.default.resolve(cwd);
28011
+ let current = import_node_path63.default.resolve(cwd);
27846
28012
  for (let depth = 0; depth < REPO_ROOT_MAX_DEPTH; depth += 1) {
27847
- if (import_node_fs49.default.existsSync(import_node_path62.default.join(current, ".devrouter.yml"))) {
28013
+ if (import_node_fs50.default.existsSync(import_node_path63.default.join(current, ".devrouter.yml"))) {
27848
28014
  return current;
27849
28015
  }
27850
- const parent = import_node_path62.default.dirname(current);
28016
+ const parent = import_node_path63.default.dirname(current);
27851
28017
  if (parent === current) {
27852
28018
  return void 0;
27853
28019
  }
@@ -27939,12 +28105,12 @@ function parseHarnessGateBudget(value) {
27939
28105
  }
27940
28106
  return Math.min(Math.trunc(parsed), HARNESS_GATE_MAX_BUDGET_MS);
27941
28107
  }
27942
- var import_node_fs49, import_node_path62, HARNESS_GATE_DEFAULT_BUDGET_MS, HARNESS_GATE_MAX_BUDGET_MS, HARNESS_GATE_POLL_INTERVAL_MS, TRANSITIONAL_PHASES, REPO_ROOT_MAX_DEPTH;
28108
+ var import_node_fs50, import_node_path63, HARNESS_GATE_DEFAULT_BUDGET_MS, HARNESS_GATE_MAX_BUDGET_MS, HARNESS_GATE_POLL_INTERVAL_MS, TRANSITIONAL_PHASES, REPO_ROOT_MAX_DEPTH;
27943
28109
  var init_harness_gate = __esm({
27944
28110
  "src/core/harness-gate.ts"() {
27945
28111
  "use strict";
27946
- import_node_fs49 = __toESM(require("fs"));
27947
- import_node_path62 = __toESM(require("path"));
28112
+ import_node_fs50 = __toESM(require("fs"));
28113
+ import_node_path63 = __toESM(require("path"));
27948
28114
  init_reliability_operation_store();
27949
28115
  init_workspace();
27950
28116
  init_workspace_runtime();
@@ -28203,7 +28369,7 @@ async function runHarnessCommand(method, options, repoPath, dependencies = {}) {
28203
28369
  }
28204
28370
  const budgetMs = parseHarnessGateBudget(options.waitBudgetMs);
28205
28371
  const toolUseId = typeof payload.tool_use_id === "string" && payload.tool_use_id.length > 0 && payload.tool_use_id.length <= MAX_TOOL_USE_ID_LENGTH2 ? payload.tool_use_id : void 0;
28206
- const payloadSha256 = (0, import_node_crypto30.createHash)("sha256").update(JSON.stringify(toolInput)).digest("hex");
28372
+ const payloadSha256 = (0, import_node_crypto31.createHash)("sha256").update(JSON.stringify(toolInput)).digest("hex");
28207
28373
  const claim2 = dependencies.claim ?? ((root, input2) => {
28208
28374
  const outcome = claimHarnessContinuation(root, input2);
28209
28375
  if (outcome.kind === "claimed") {
@@ -28311,11 +28477,11 @@ async function runHarnessCommand(method, options, repoPath, dependencies = {}) {
28311
28477
  `
28312
28478
  );
28313
28479
  }
28314
- var import_node_crypto30, MAX_HOOK_PAYLOAD_BYTES, STDIN_TIMEOUT_MS, MAX_TOOL_USE_ID_LENGTH2, DEVROUTER_EXECUTABLES, LAUNCHER_WORDS, LAUNCHER_SUBCOMMANDS, COMMAND_WORD_LIMIT;
28480
+ var import_node_crypto31, MAX_HOOK_PAYLOAD_BYTES, STDIN_TIMEOUT_MS, MAX_TOOL_USE_ID_LENGTH2, DEVROUTER_EXECUTABLES, LAUNCHER_WORDS, LAUNCHER_SUBCOMMANDS, COMMAND_WORD_LIMIT;
28315
28481
  var init_harness = __esm({
28316
28482
  "src/commands/harness.ts"() {
28317
28483
  "use strict";
28318
- import_node_crypto30 = require("crypto");
28484
+ import_node_crypto31 = require("crypto");
28319
28485
  init_harness_continuation();
28320
28486
  init_harness_gate();
28321
28487
  MAX_HOOK_PAYLOAD_BYTES = 256 * 1024;
@@ -28431,7 +28597,7 @@ async function runOpenCommand(name) {
28431
28597
  );
28432
28598
  return;
28433
28599
  }
28434
- const result = (0, import_node_child_process39.spawnSync)("open", [url], { encoding: "utf-8" });
28600
+ const result = (0, import_node_child_process40.spawnSync)("open", [url], { encoding: "utf-8" });
28435
28601
  if (result.status !== 0) {
28436
28602
  const details = [result.stdout, result.stderr].filter(Boolean).join("\n").trim();
28437
28603
  throw new Error(`Unable to open '${url}': ${details || "unknown error"}`);
@@ -28439,11 +28605,11 @@ async function runOpenCommand(name) {
28439
28605
  process.stdout.write(`Opened ${url}
28440
28606
  `);
28441
28607
  }
28442
- var import_node_child_process39;
28608
+ var import_node_child_process40;
28443
28609
  var init_open = __esm({
28444
28610
  "src/commands/open.ts"() {
28445
28611
  "use strict";
28446
- import_node_child_process39 = require("child_process");
28612
+ import_node_child_process40 = require("child_process");
28447
28613
  init_docker();
28448
28614
  init_host_routes();
28449
28615
  init_repo_config();
@@ -28487,7 +28653,7 @@ async function runLogsCommand(options) {
28487
28653
  const args = ["logs", "--tail", tail, ROUTER_CONTAINER_NAME];
28488
28654
  if (options.follow) {
28489
28655
  args.splice(1, 0, "-f");
28490
- const child = (0, import_node_child_process40.spawn)("docker", args, { stdio: "inherit" });
28656
+ const child = (0, import_node_child_process41.spawn)("docker", args, { stdio: "inherit" });
28491
28657
  const onSignal = () => {
28492
28658
  child.kill("SIGTERM");
28493
28659
  };
@@ -28501,17 +28667,17 @@ async function runLogsCommand(options) {
28501
28667
  });
28502
28668
  });
28503
28669
  } else {
28504
- const result = (0, import_node_child_process40.spawnSync)("docker", args, { stdio: "inherit" });
28670
+ const result = (0, import_node_child_process41.spawnSync)("docker", args, { stdio: "inherit" });
28505
28671
  if (result.status !== 0) {
28506
28672
  throw new Error("Failed to retrieve router logs.");
28507
28673
  }
28508
28674
  }
28509
28675
  }
28510
- var import_node_child_process40;
28676
+ var import_node_child_process41;
28511
28677
  var init_logs = __esm({
28512
28678
  "src/commands/logs.ts"() {
28513
28679
  "use strict";
28514
- import_node_child_process40 = require("child_process");
28680
+ import_node_child_process41 = require("child_process");
28515
28681
  init_docker();
28516
28682
  init_lifecycle_diagnostics();
28517
28683
  init_router();
@@ -28555,17 +28721,17 @@ function asRecord3(value) {
28555
28721
  return value;
28556
28722
  }
28557
28723
  function readJson(filePath) {
28558
- if (!import_node_fs50.default.existsSync(filePath)) {
28724
+ if (!import_node_fs51.default.existsSync(filePath)) {
28559
28725
  return void 0;
28560
28726
  }
28561
28727
  try {
28562
- return JSON.parse(import_node_fs50.default.readFileSync(filePath, "utf-8"));
28728
+ return JSON.parse(import_node_fs51.default.readFileSync(filePath, "utf-8"));
28563
28729
  } catch {
28564
28730
  return void 0;
28565
28731
  }
28566
28732
  }
28567
28733
  function relative(repoPath, filePath) {
28568
- return import_node_path63.default.relative(repoPath, filePath) || ".";
28734
+ return import_node_path64.default.relative(repoPath, filePath) || ".";
28569
28735
  }
28570
28736
  function redactEnvAssignments(value) {
28571
28737
  return value.replace(
@@ -28606,7 +28772,7 @@ function inspectPackageManager(repoPath, pkg) {
28606
28772
  ["bun.lock", "bun"]
28607
28773
  ];
28608
28774
  for (const [fileName, name] of lockfiles) {
28609
- if (import_node_fs50.default.existsSync(import_node_path63.default.join(repoPath, fileName))) {
28775
+ if (import_node_fs51.default.existsSync(import_node_path64.default.join(repoPath, fileName))) {
28610
28776
  return { name, source: fileName };
28611
28777
  }
28612
28778
  }
@@ -28621,9 +28787,9 @@ function inspectNode(repoPath, pkg) {
28621
28787
  if (typeof engines?.node === "string") {
28622
28788
  return { version: engines.node, source: "package.json:engines.node" };
28623
28789
  }
28624
- const nvmrc = import_node_path63.default.join(repoPath, ".nvmrc");
28625
- if (import_node_fs50.default.existsSync(nvmrc)) {
28626
- const version = import_node_fs50.default.readFileSync(nvmrc, "utf-8").trim();
28790
+ const nvmrc = import_node_path64.default.join(repoPath, ".nvmrc");
28791
+ if (import_node_fs51.default.existsSync(nvmrc)) {
28792
+ const version = import_node_fs51.default.readFileSync(nvmrc, "utf-8").trim();
28627
28793
  return { version, source: ".nvmrc" };
28628
28794
  }
28629
28795
  return void 0;
@@ -28684,7 +28850,7 @@ function configuredComposeFiles(repoPath) {
28684
28850
  const files = config.apps.filter(
28685
28851
  (app) => app.runtime === "docker"
28686
28852
  ).flatMap((app) => app.docker.composeFiles).filter(
28687
- (fileName) => !import_node_path63.default.isAbsolute(fileName) && !import_node_path63.default.normalize(fileName).startsWith("..")
28853
+ (fileName) => !import_node_path64.default.isAbsolute(fileName) && !import_node_path64.default.normalize(fileName).startsWith("..")
28688
28854
  );
28689
28855
  return Array.from(new Set(files));
28690
28856
  } catch {
@@ -28702,7 +28868,7 @@ function composeFiles(repoPath) {
28702
28868
  ...configuredComposeFiles(repoPath)
28703
28869
  ];
28704
28870
  return Array.from(new Set(candidates)).filter(
28705
- (fileName) => import_node_fs50.default.existsSync(import_node_path63.default.join(repoPath, fileName))
28871
+ (fileName) => import_node_fs51.default.existsSync(import_node_path64.default.join(repoPath, fileName))
28706
28872
  );
28707
28873
  }
28708
28874
  function stringArray2(value) {
@@ -28740,7 +28906,7 @@ function inspectServices(repoPath) {
28740
28906
  const services = [];
28741
28907
  for (const fileName of composeFiles(repoPath)) {
28742
28908
  try {
28743
- const parsed = import_yaml7.default.parse(import_node_fs50.default.readFileSync(import_node_path63.default.join(repoPath, fileName), "utf-8"));
28909
+ const parsed = import_yaml8.default.parse(import_node_fs51.default.readFileSync(import_node_path64.default.join(repoPath, fileName), "utf-8"));
28744
28910
  const serviceMap = asRecord3(asRecord3(parsed)?.services);
28745
28911
  for (const [name, value] of Object.entries(serviceMap ?? {})) {
28746
28912
  const service = asRecord3(value);
@@ -28775,8 +28941,8 @@ function inspectServices(repoPath) {
28775
28941
  return services;
28776
28942
  }
28777
28943
  function inspectEnvFiles(repoPath) {
28778
- const files = import_node_fs50.default.readdirSync(repoPath).filter((fileName) => /^\.env(\.|$)/.test(fileName)).sort().map((fileName) => {
28779
- const content = import_node_fs50.default.readFileSync(import_node_path63.default.join(repoPath, fileName), "utf-8");
28944
+ const files = import_node_fs51.default.readdirSync(repoPath).filter((fileName) => /^\.env(\.|$)/.test(fileName)).sort().map((fileName) => {
28945
+ const content = import_node_fs51.default.readFileSync(import_node_path64.default.join(repoPath, fileName), "utf-8");
28780
28946
  const names = content.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#") && line.includes("=")).map((line) => line.split("=")[0]?.trim()).filter((name) => Boolean(name)).sort();
28781
28947
  return { path: fileName, names };
28782
28948
  });
@@ -28790,18 +28956,18 @@ function inspectEnvFiles(repoPath) {
28790
28956
  };
28791
28957
  }
28792
28958
  function inspectDevcontainer(repoPath) {
28793
- const dir = import_node_path63.default.join(repoPath, ".devcontainer");
28794
- if (!import_node_fs50.default.existsSync(dir)) {
28959
+ const dir = import_node_path64.default.join(repoPath, ".devcontainer");
28960
+ if (!import_node_fs51.default.existsSync(dir)) {
28795
28961
  return { exists: false, files: [] };
28796
28962
  }
28797
28963
  return {
28798
28964
  exists: true,
28799
- files: import_node_fs50.default.readdirSync(dir).filter((fileName) => import_node_fs50.default.statSync(import_node_path63.default.join(dir, fileName)).isFile()).sort().map((fileName) => `.devcontainer/${fileName}`)
28965
+ files: import_node_fs51.default.readdirSync(dir).filter((fileName) => import_node_fs51.default.statSync(import_node_path64.default.join(dir, fileName)).isFile()).sort().map((fileName) => `.devcontainer/${fileName}`)
28800
28966
  };
28801
28967
  }
28802
28968
  function inspectDevrouter(repoPath) {
28803
28969
  const configPath = getRepoConfigPath(repoPath);
28804
- if (!import_node_fs50.default.existsSync(configPath)) {
28970
+ if (!import_node_fs51.default.existsSync(configPath)) {
28805
28971
  return {
28806
28972
  exists: false,
28807
28973
  configPath,
@@ -28845,15 +29011,15 @@ function inspectAgentGuidance(repoPath) {
28845
29011
  ["AGENTS.md", "agents"],
28846
29012
  ["CLAUDE.md", "claude"]
28847
29013
  ]) {
28848
- if (import_node_fs50.default.existsSync(import_node_path63.default.join(repoPath, fileName))) {
29014
+ if (import_node_fs51.default.existsSync(import_node_path64.default.join(repoPath, fileName))) {
28849
29015
  results.push({ path: fileName, kind });
28850
29016
  }
28851
29017
  }
28852
- const skillsDir = import_node_path63.default.join(repoPath, ".agents", "skills");
28853
- if (import_node_fs50.default.existsSync(skillsDir)) {
28854
- for (const name of import_node_fs50.default.readdirSync(skillsDir).sort()) {
28855
- const skillPath = import_node_path63.default.join(skillsDir, name, "SKILL.md");
28856
- if (import_node_fs50.default.existsSync(skillPath)) {
29018
+ const skillsDir = import_node_path64.default.join(repoPath, ".agents", "skills");
29019
+ if (import_node_fs51.default.existsSync(skillsDir)) {
29020
+ for (const name of import_node_fs51.default.readdirSync(skillsDir).sort()) {
29021
+ const skillPath = import_node_path64.default.join(skillsDir, name, "SKILL.md");
29022
+ if (import_node_fs51.default.existsSync(skillPath)) {
28857
29023
  results.push({ path: relative(repoPath, skillPath), kind: "skill" });
28858
29024
  }
28859
29025
  }
@@ -28896,7 +29062,7 @@ function buildIssues(report) {
28896
29062
  }
28897
29063
  function inspectRepo(options = {}) {
28898
29064
  const repoPath = resolveRepoPath(options.repo);
28899
- const pkg = readJson(import_node_path63.default.join(repoPath, "package.json"));
29065
+ const pkg = readJson(import_node_path64.default.join(repoPath, "package.json"));
28900
29066
  const scripts = inspectScripts(pkg);
28901
29067
  const reportWithoutIssues = {
28902
29068
  repoPath,
@@ -28915,13 +29081,13 @@ function inspectRepo(options = {}) {
28915
29081
  issues: buildIssues(reportWithoutIssues)
28916
29082
  };
28917
29083
  }
28918
- var import_node_fs50, import_node_path63, import_yaml7;
29084
+ var import_node_fs51, import_node_path64, import_yaml8;
28919
29085
  var init_repo_inspect = __esm({
28920
29086
  "src/core/repo-inspect.ts"() {
28921
29087
  "use strict";
28922
- import_node_fs50 = __toESM(require("fs"));
28923
- import_node_path63 = __toESM(require("path"));
28924
- import_yaml7 = __toESM(require("yaml"));
29088
+ import_node_fs51 = __toESM(require("fs"));
29089
+ import_node_path64 = __toESM(require("path"));
29090
+ import_yaml8 = __toESM(require("yaml"));
28925
29091
  init_repo_config();
28926
29092
  }
28927
29093
  });
@@ -29025,7 +29191,7 @@ function requiredFileChecks(repoPath) {
29025
29191
  ".devcontainer/docker-compose.yml",
29026
29192
  ".devrouter.yml"
29027
29193
  ];
29028
- const missing = required.filter((fileName) => !import_node_fs51.default.existsSync(import_node_path64.default.join(repoPath, fileName)));
29194
+ const missing = required.filter((fileName) => !import_node_fs52.default.existsSync(import_node_path65.default.join(repoPath, fileName)));
29029
29195
  return {
29030
29196
  id: "repo.devcontainer.verify-files",
29031
29197
  level: missing.length === 0 ? "ok" : "error",
@@ -29250,12 +29416,12 @@ async function verifyDevcontainer(options = {}) {
29250
29416
  nextSteps: collectNextSteps3(checks)
29251
29417
  };
29252
29418
  }
29253
- var import_node_fs51, import_node_path64;
29419
+ var import_node_fs52, import_node_path65;
29254
29420
  var init_devcontainer_verify = __esm({
29255
29421
  "src/core/devcontainer-verify.ts"() {
29256
29422
  "use strict";
29257
- import_node_fs51 = __toESM(require("fs"));
29258
- import_node_path64 = __toESM(require("path"));
29423
+ import_node_fs52 = __toESM(require("fs"));
29424
+ import_node_path65 = __toESM(require("path"));
29259
29425
  init_capabilities();
29260
29426
  init_doctor();
29261
29427
  init_host_routes();
@@ -29552,7 +29718,7 @@ function packageManagerIssues(repo) {
29552
29718
  }
29553
29719
  function plannedFiles(repoPath, version) {
29554
29720
  const repo = inspectRepo({ repo: repoPath });
29555
- const projectName = sanitizeProjectName(import_node_path65.default.basename(repo.repoPath));
29721
+ const projectName = sanitizeProjectName(import_node_path66.default.basename(repo.repoPath));
29556
29722
  const nodeMajor = majorVersion(repo.node?.version, "24");
29557
29723
  const pnpmVersion = repo.packageManager?.name === "pnpm" && repo.packageManager.version ? repo.packageManager.version : DEFAULT_PNPM_VERSION;
29558
29724
  const port = inferPort2(repo);
@@ -29597,8 +29763,8 @@ function plannedFiles(repoPath, version) {
29597
29763
  };
29598
29764
  }
29599
29765
  function classifyFile(repoPath, file) {
29600
- const absolutePath = import_node_path65.default.join(repoPath, file.relativePath);
29601
- if (!import_node_fs52.default.existsSync(absolutePath)) {
29766
+ const absolutePath = import_node_path66.default.join(repoPath, file.relativePath);
29767
+ if (!import_node_fs53.default.existsSync(absolutePath)) {
29602
29768
  return {
29603
29769
  path: file.relativePath,
29604
29770
  action: "create",
@@ -29606,7 +29772,7 @@ function classifyFile(repoPath, file) {
29606
29772
  bytes: Buffer.byteLength(file.content)
29607
29773
  };
29608
29774
  }
29609
- const current = import_node_fs52.default.readFileSync(absolutePath, "utf-8");
29775
+ const current = import_node_fs53.default.readFileSync(absolutePath, "utf-8");
29610
29776
  if (!current.includes(MANAGED_MARKER2)) {
29611
29777
  return {
29612
29778
  path: file.relativePath,
@@ -29663,11 +29829,11 @@ function buildPlan(repoPath, dryRun, version) {
29663
29829
  };
29664
29830
  }
29665
29831
  function writeFile(repoPath, file) {
29666
- const absolutePath = import_node_path65.default.join(repoPath, file.relativePath);
29667
- import_node_fs52.default.mkdirSync(import_node_path65.default.dirname(absolutePath), { recursive: true });
29668
- import_node_fs52.default.writeFileSync(absolutePath, file.content, "utf-8");
29832
+ const absolutePath = import_node_path66.default.join(repoPath, file.relativePath);
29833
+ import_node_fs53.default.mkdirSync(import_node_path66.default.dirname(absolutePath), { recursive: true });
29834
+ import_node_fs53.default.writeFileSync(absolutePath, file.content, "utf-8");
29669
29835
  if (file.executable) {
29670
- import_node_fs52.default.chmodSync(absolutePath, 493);
29836
+ import_node_fs53.default.chmodSync(absolutePath, 493);
29671
29837
  }
29672
29838
  }
29673
29839
  function writeDevcontainer(options = {}) {
@@ -29705,12 +29871,12 @@ function writeDevcontainer(options = {}) {
29705
29871
  nextSteps: postWriteNextSteps(repoPath)
29706
29872
  };
29707
29873
  }
29708
- var import_node_fs52, import_node_path65, MANAGED_MARKER2, DEFAULT_DEVROUTER_VERSION, DEFAULT_PNPM_VERSION, VALID_PACKAGE_VERSION_RE;
29874
+ var import_node_fs53, import_node_path66, MANAGED_MARKER2, DEFAULT_DEVROUTER_VERSION, DEFAULT_PNPM_VERSION, VALID_PACKAGE_VERSION_RE;
29709
29875
  var init_devcontainer_write = __esm({
29710
29876
  "src/core/devcontainer-write.ts"() {
29711
29877
  "use strict";
29712
- import_node_fs52 = __toESM(require("fs"));
29713
- import_node_path65 = __toESM(require("path"));
29878
+ import_node_fs53 = __toESM(require("fs"));
29879
+ import_node_path66 = __toESM(require("path"));
29714
29880
  init_repo_config();
29715
29881
  init_repo_inspect();
29716
29882
  MANAGED_MARKER2 = "devrouter:managed devcontainer";
@@ -29895,7 +30061,7 @@ function parseMappings(value) {
29895
30061
  return Object.fromEntries(mappings);
29896
30062
  }
29897
30063
  function parseProfilePlanContract(source) {
29898
- const document = (0, import_yaml8.parseDocument)(source, {
30064
+ const document = (0, import_yaml9.parseDocument)(source, {
29899
30065
  merge: false,
29900
30066
  schema: "core",
29901
30067
  uniqueKeys: true
@@ -30044,13 +30210,13 @@ function buildProfilePlanReport(options) {
30044
30210
  return { ...report, contractPath, bindings: bindings2 };
30045
30211
  }
30046
30212
  function loadContract(repoPath, contractPath) {
30047
- if (import_node_path66.default.isAbsolute(contractPath)) {
30213
+ if (import_node_path67.default.isAbsolute(contractPath)) {
30048
30214
  fail3("profile plan contract path must be relative to the repository.");
30049
30215
  }
30050
30216
  const absolutePath = assertPathWithinRepo(contractPath, repoPath, "Profile plan contract");
30051
30217
  let metadata;
30052
30218
  try {
30053
- metadata = import_node_fs53.default.lstatSync(absolutePath);
30219
+ metadata = import_node_fs54.default.lstatSync(absolutePath);
30054
30220
  } catch (error) {
30055
30221
  fail3(`could not inspect profile plan contract '${contractPath}': ${error.message}`);
30056
30222
  }
@@ -30060,16 +30226,16 @@ function loadContract(repoPath, contractPath) {
30060
30226
  if (metadata.size > MAX_CONTRACT_BYTES) {
30061
30227
  fail3(`profile plan contract exceeds the ${MAX_CONTRACT_BYTES}-byte limit.`);
30062
30228
  }
30063
- const realRepo = import_node_fs53.default.realpathSync(repoPath);
30064
- const realContract = import_node_fs53.default.realpathSync(absolutePath);
30229
+ const realRepo = import_node_fs54.default.realpathSync(repoPath);
30230
+ const realContract = import_node_fs54.default.realpathSync(absolutePath);
30065
30231
  assertPathWithinRepo(realContract, realRepo, "Profile plan contract");
30066
- const source = import_node_fs53.default.readFileSync(absolutePath, "utf-8");
30232
+ const source = import_node_fs54.default.readFileSync(absolutePath, "utf-8");
30067
30233
  if (Buffer.byteLength(source, "utf-8") > MAX_CONTRACT_BYTES) {
30068
30234
  fail3(`profile plan contract exceeds the ${MAX_CONTRACT_BYTES}-byte limit.`);
30069
30235
  }
30070
30236
  return {
30071
30237
  contract: parseProfilePlanContract(source),
30072
- contractPath: import_node_path66.default.relative(repoPath, absolutePath).split(import_node_path66.default.sep).join("/")
30238
+ contractPath: import_node_path67.default.relative(repoPath, absolutePath).split(import_node_path67.default.sep).join("/")
30073
30239
  };
30074
30240
  }
30075
30241
  function resolveProfilePlan(options) {
@@ -30083,13 +30249,13 @@ function resolveProfilePlan(options) {
30083
30249
  contractPath: loaded.contractPath
30084
30250
  });
30085
30251
  }
30086
- var import_node_fs53, import_node_path66, import_yaml8, CONTRACT_VERSION, MAX_CONTRACT_BYTES, MAX_APP_MAPPINGS, MAX_BINDING_KEYS, MAX_BINDING_VALUES, MAX_LITERAL_LENGTH, BINDING_KEY;
30252
+ var import_node_fs54, import_node_path67, import_yaml9, CONTRACT_VERSION, MAX_CONTRACT_BYTES, MAX_APP_MAPPINGS, MAX_BINDING_KEYS, MAX_BINDING_VALUES, MAX_LITERAL_LENGTH, BINDING_KEY;
30087
30253
  var init_profile_plan = __esm({
30088
30254
  "src/core/profile-plan.ts"() {
30089
30255
  "use strict";
30090
- import_node_fs53 = __toESM(require("fs"));
30091
- import_node_path66 = __toESM(require("path"));
30092
- import_yaml8 = require("yaml");
30256
+ import_node_fs54 = __toESM(require("fs"));
30257
+ import_node_path67 = __toESM(require("path"));
30258
+ import_yaml9 = require("yaml");
30093
30259
  init_paths();
30094
30260
  init_profile_resolution();
30095
30261
  init_repo_config();
@@ -30120,7 +30286,7 @@ async function runProfileResolveCommand(options) {
30120
30286
  async function runProfilePlanCommand(options) {
30121
30287
  const report = resolveProfilePlan(options);
30122
30288
  if (options.output) {
30123
- writeFileAtomically(import_node_path67.default.resolve(options.output), `${JSON.stringify(report, null, 2)}
30289
+ writeFileAtomically(import_node_path68.default.resolve(options.output), `${JSON.stringify(report, null, 2)}
30124
30290
  `);
30125
30291
  }
30126
30292
  if (options.json) {
@@ -30167,14 +30333,14 @@ function printProfilePlanSummary(report, output2) {
30167
30333
  process.stdout.write(`Binding ${key}: ${renderValues(report.bindings[key] ?? [])}
30168
30334
  `);
30169
30335
  }
30170
- if (output2) process.stdout.write(`Output: ${import_node_path67.default.resolve(output2)}
30336
+ if (output2) process.stdout.write(`Output: ${import_node_path68.default.resolve(output2)}
30171
30337
  `);
30172
30338
  }
30173
- var import_node_path67;
30339
+ var import_node_path68;
30174
30340
  var init_profile = __esm({
30175
30341
  "src/commands/profile.ts"() {
30176
30342
  "use strict";
30177
- import_node_path67 = __toESM(require("path"));
30343
+ import_node_path68 = __toESM(require("path"));
30178
30344
  init_atomic_file();
30179
30345
  init_output();
30180
30346
  init_profile_plan();
@@ -30524,7 +30690,7 @@ function assertResourceGraph(resources) {
30524
30690
  for (const id5 of Object.keys(resources).sort()) visit(id5);
30525
30691
  }
30526
30692
  function parsePreviewManifest(source) {
30527
- const document = (0, import_yaml9.parseDocument)(source, {
30693
+ const document = (0, import_yaml10.parseDocument)(source, {
30528
30694
  merge: false,
30529
30695
  schema: "core",
30530
30696
  uniqueKeys: true
@@ -30557,11 +30723,11 @@ function parsePreviewManifest(source) {
30557
30723
  return { version: 1, aliases, bindings: bindings2, resources, selection };
30558
30724
  }
30559
30725
  function loadPreviewManifest(repoPath) {
30560
- const resolvedRepo = import_node_path68.default.resolve(repoPath);
30726
+ const resolvedRepo = import_node_path69.default.resolve(repoPath);
30561
30727
  const manifestPath = assertPathWithinRepo(MANIFEST_FILE_NAME, resolvedRepo, "Preview manifest");
30562
30728
  let metadata;
30563
30729
  try {
30564
- metadata = import_node_fs54.default.lstatSync(manifestPath);
30730
+ metadata = import_node_fs55.default.lstatSync(manifestPath);
30565
30731
  } catch (error) {
30566
30732
  fail4(`could not inspect preview manifest '${MANIFEST_FILE_NAME}': ${error.message}`);
30567
30733
  }
@@ -30571,26 +30737,26 @@ function loadPreviewManifest(repoPath) {
30571
30737
  if (metadata.size > MAX_MANIFEST_BYTES) {
30572
30738
  fail4(`preview manifest exceeds the ${MAX_MANIFEST_BYTES}-byte limit.`);
30573
30739
  }
30574
- const realRepo = import_node_fs54.default.realpathSync(resolvedRepo);
30575
- const realManifest = import_node_fs54.default.realpathSync(manifestPath);
30740
+ const realRepo = import_node_fs55.default.realpathSync(resolvedRepo);
30741
+ const realManifest = import_node_fs55.default.realpathSync(manifestPath);
30576
30742
  assertPathWithinRepo(realManifest, realRepo, "Preview manifest");
30577
- const source = import_node_fs54.default.readFileSync(manifestPath, "utf-8");
30743
+ const source = import_node_fs55.default.readFileSync(manifestPath, "utf-8");
30578
30744
  if (Buffer.byteLength(source, "utf-8") > MAX_MANIFEST_BYTES) {
30579
30745
  fail4(`preview manifest exceeds the ${MAX_MANIFEST_BYTES}-byte limit.`);
30580
30746
  }
30581
30747
  return {
30582
30748
  manifest: parsePreviewManifest(source),
30583
- digest: (0, import_node_crypto31.createHash)("sha256").update(source, "utf-8").digest("hex")
30749
+ digest: (0, import_node_crypto32.createHash)("sha256").update(source, "utf-8").digest("hex")
30584
30750
  };
30585
30751
  }
30586
- var import_node_crypto31, import_node_fs54, import_node_path68, import_yaml9, MANIFEST_FILE_NAME, MAX_MANIFEST_BYTES, MAX_COLLECTION_ENTRIES, MAX_BINDING_VALUES2, MAX_COMMAND_ARGS, MAX_LITERAL_LENGTH2, MAX_REPO_PATH_LENGTH, NAME_RE, IMAGE_REFERENCE_RE;
30752
+ var import_node_crypto32, import_node_fs55, import_node_path69, import_yaml10, MANIFEST_FILE_NAME, MAX_MANIFEST_BYTES, MAX_COLLECTION_ENTRIES, MAX_BINDING_VALUES2, MAX_COMMAND_ARGS, MAX_LITERAL_LENGTH2, MAX_REPO_PATH_LENGTH, NAME_RE, IMAGE_REFERENCE_RE;
30587
30753
  var init_manifest = __esm({
30588
30754
  "src/core/preview/manifest.ts"() {
30589
30755
  "use strict";
30590
- import_node_crypto31 = require("crypto");
30591
- import_node_fs54 = __toESM(require("fs"));
30592
- import_node_path68 = __toESM(require("path"));
30593
- import_yaml9 = require("yaml");
30756
+ import_node_crypto32 = require("crypto");
30757
+ import_node_fs55 = __toESM(require("fs"));
30758
+ import_node_path69 = __toESM(require("path"));
30759
+ import_yaml10 = require("yaml");
30594
30760
  init_paths();
30595
30761
  MANIFEST_FILE_NAME = ".devrouter.preview.yml";
30596
30762
  MAX_MANIFEST_BYTES = 1024 * 1024;
@@ -31385,7 +31551,7 @@ function createPreviewPool(installationId) {
31385
31551
  processedCommands: []
31386
31552
  });
31387
31553
  }
31388
- function object4(value, required, optional = []) {
31554
+ function object5(value, required, optional = []) {
31389
31555
  if (!value || typeof value !== "object" || Array.isArray(value))
31390
31556
  throw new Error("Invalid preview state object.");
31391
31557
  const record6 = value;
@@ -31412,7 +31578,7 @@ function oneOf3(value, allowed) {
31412
31578
  if (!allowed.includes(value)) throw new Error("Invalid preview state enum.");
31413
31579
  }
31414
31580
  function decodeSession(value) {
31415
- const row = object4(
31581
+ const row = object5(
31416
31582
  value,
31417
31583
  [
31418
31584
  "generation",
@@ -31463,7 +31629,7 @@ function decodeSession(value) {
31463
31629
  if (row.stopReason !== void 0)
31464
31630
  oneOf3(row.stopReason, ["stop", "closed", "expiry", "setup-timeout"]);
31465
31631
  if (row.pendingDeadline !== void 0) {
31466
- const pending = object4(row.pendingDeadline, ["target", "commandId"]);
31632
+ const pending = object5(row.pendingDeadline, ["target", "commandId"]);
31467
31633
  integer(pending.target);
31468
31634
  text4(pending.commandId);
31469
31635
  }
@@ -31491,7 +31657,7 @@ function decodeSession(value) {
31491
31657
  return session;
31492
31658
  }
31493
31659
  function decodePlan(value) {
31494
- const row = object4(
31660
+ const row = object5(
31495
31661
  value,
31496
31662
  [
31497
31663
  "schemaVersion",
@@ -31520,7 +31686,7 @@ function decodePlan(value) {
31520
31686
  if (!/^[a-f0-9]{64}$/.test(row[key]))
31521
31687
  throw new Error("Invalid preview config digest.");
31522
31688
  }
31523
- const source = object4(row.source, ["head", "target", "mergeBase", "complete", "changes"]);
31689
+ const source = object5(row.source, ["head", "target", "mergeBase", "complete", "changes"]);
31524
31690
  for (const key of ["head", "target", "mergeBase"]) {
31525
31691
  text4(source[key], 64);
31526
31692
  if (!/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(source[key]))
@@ -31529,7 +31695,7 @@ function decodePlan(value) {
31529
31695
  if (source.complete !== true || !Array.isArray(source.changes) || source.changes.length > 1e4)
31530
31696
  throw new Error("Incomplete stored preview source.");
31531
31697
  for (const change of source.changes) {
31532
- const item = object4(change, ["status", "path"], ["oldPath"]);
31698
+ const item = object5(change, ["status", "path"], ["oldPath"]);
31533
31699
  oneOf3(item.status, ["A", "M", "D", "R", "C", "T"]);
31534
31700
  for (const key of ["path", ...item.oldPath === void 0 ? [] : ["oldPath"]]) {
31535
31701
  text4(item[key], 4096);
@@ -31543,7 +31709,7 @@ function decodePlan(value) {
31543
31709
  throw new Error("Invalid stored preview graph.");
31544
31710
  const resources = {};
31545
31711
  for (const resource of row.resources) {
31546
- const item = object4(
31712
+ const item = object5(
31547
31713
  resource,
31548
31714
  ["id", "kind"],
31549
31715
  ["dependsOn", "image", "imageTarget", "build", "ports", "command"]
@@ -31559,7 +31725,7 @@ function decodePlan(value) {
31559
31725
  if (!aliases.length || row.reasons.includes("source-unbound"))
31560
31726
  throw new Error("Unbound stored preview plan.");
31561
31727
  parsePreviewManifest(
31562
- (0, import_yaml10.stringify)({
31728
+ (0, import_yaml11.stringify)({
31563
31729
  version: 1,
31564
31730
  aliases: Object.fromEntries(
31565
31731
  aliases.map((alias) => [alias, { resources: Object.keys(resources) }])
@@ -31574,7 +31740,7 @@ function decodePlan(value) {
31574
31740
  function decodePreviewPool(value) {
31575
31741
  if (Buffer.byteLength(JSON.stringify(value) ?? "") > 1024 * 1024)
31576
31742
  throw new Error("Preview pool exceeds its bound.");
31577
- const root = object4(value, [
31743
+ const root = object5(value, [
31578
31744
  "schemaVersion",
31579
31745
  "installationId",
31580
31746
  "sessions",
@@ -31588,7 +31754,7 @@ function decodePreviewPool(value) {
31588
31754
  throw new Error("Invalid preview command window.");
31589
31755
  const receiptIds = /* @__PURE__ */ new Set();
31590
31756
  for (const receipt of root.processedCommands) {
31591
- const item = object4(receipt, ["id", "admittedAt"]);
31757
+ const item = object5(receipt, ["id", "admittedAt"]);
31592
31758
  text4(item.id, 512);
31593
31759
  integer(item.admittedAt);
31594
31760
  if (receiptIds.has(item.id)) throw new Error("Duplicate preview command receipt.");
@@ -31599,7 +31765,7 @@ function decodePreviewPool(value) {
31599
31765
  throw new Error("Invalid preview stop fence window.");
31600
31766
  for (const [key, raw] of Object.entries(fences)) {
31601
31767
  if (!/^[a-f0-9]{64}$/.test(key)) throw new Error("Invalid stop fence identity.");
31602
- const fence = object4(raw, ["createdAt", "recordedAt", "commandId"]);
31768
+ const fence = object5(raw, ["createdAt", "recordedAt", "commandId"]);
31603
31769
  text4(fence.commandId, 128);
31604
31770
  integer(fence.createdAt);
31605
31771
  integer(fence.recordedAt);
@@ -31609,7 +31775,7 @@ function decodePreviewPool(value) {
31609
31775
  throw new Error("Preview pool session bound exceeded.");
31610
31776
  for (const [id5, value2] of Object.entries(sessions)) {
31611
31777
  if (!/^[a-f0-9]{64}$/.test(id5)) throw new Error("Invalid preview identity key.");
31612
- const entry = object4(
31778
+ const entry = object5(
31613
31779
  value2,
31614
31780
  ["session", "plan", "reservedCents", "hourlyCents", "operations", "effectSequence"],
31615
31781
  [
@@ -31623,7 +31789,7 @@ function decodePreviewPool(value) {
31623
31789
  ]
31624
31790
  );
31625
31791
  if (entry.identity !== void 0) {
31626
- const identity2 = object4(entry.identity, ["forgeHost", "projectId", "change"]);
31792
+ const identity2 = object5(entry.identity, ["forgeHost", "projectId", "change"]);
31627
31793
  text4(identity2.forgeHost, 253);
31628
31794
  text4(identity2.projectId, 128);
31629
31795
  integer(identity2.change, 1);
@@ -31649,7 +31815,7 @@ function decodePreviewPool(value) {
31649
31815
  if (!entry.operations || typeof entry.operations !== "object" || Array.isArray(entry.operations) || Object.keys(entry.operations).length > 1024)
31650
31816
  throw new Error("Invalid preview operation registry.");
31651
31817
  for (const [operationId, raw] of Object.entries(entry.operations)) {
31652
- const effect2 = object4(
31818
+ const effect2 = object5(
31653
31819
  raw,
31654
31820
  [
31655
31821
  "id",
@@ -31687,7 +31853,7 @@ function decodePreviewPool(value) {
31687
31853
  throw new Error("Preview provider step journal exceeds its bound.");
31688
31854
  const names = /* @__PURE__ */ new Set();
31689
31855
  for (const rawStep of effect2.steps) {
31690
- const step = object4(rawStep, ["name", "mode", "state", "at"], ["ref", "fingerprint"]);
31856
+ const step = object5(rawStep, ["name", "mode", "state", "at"], ["ref", "fingerprint"]);
31691
31857
  text4(step.name, 64);
31692
31858
  if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(step.name) || names.has(step.name))
31693
31859
  throw new Error("Invalid preview provider step identity.");
@@ -31713,11 +31879,11 @@ function decodePreviewPool(value) {
31713
31879
  }
31714
31880
  return structuredClone(root);
31715
31881
  }
31716
- var import_yaml10, PreviewStateConflict;
31882
+ var import_yaml11, PreviewStateConflict;
31717
31883
  var init_state = __esm({
31718
31884
  "src/core/preview/state.ts"() {
31719
31885
  "use strict";
31720
- import_yaml10 = require("yaml");
31886
+ import_yaml11 = require("yaml");
31721
31887
  init_manifest();
31722
31888
  PreviewStateConflict = class extends Error {
31723
31889
  constructor() {
@@ -31732,7 +31898,7 @@ var init_state = __esm({
31732
31898
  function previewIdentityKey(identity2) {
31733
31899
  if (!/^[a-z0-9.-]+$/.test(identity2.forgeHost) || identity2.forgeHost.length > 253 || !/^[A-Za-z0-9_-]{1,128}$/.test(identity2.projectId) || !Number.isSafeInteger(identity2.change) || identity2.change < 1)
31734
31900
  throw new Error("Invalid immutable preview identity.");
31735
- return (0, import_node_crypto32.createHash)("sha256").update(JSON.stringify([identity2.forgeHost, identity2.projectId, identity2.change])).digest("hex");
31901
+ return (0, import_node_crypto33.createHash)("sha256").update(JSON.stringify([identity2.forgeHost, identity2.projectId, identity2.change])).digest("hex");
31736
31902
  }
31737
31903
  function costs(pool) {
31738
31904
  return Object.values(pool.sessions).reduce((total, entry) => total + entry.reservedCents, 0);
@@ -31788,11 +31954,11 @@ function sessionReady(entry, now, expiresAt) {
31788
31954
  ...expiresAt === void 0 ? {} : { expiresAt }
31789
31955
  });
31790
31956
  }
31791
- var import_node_crypto32, PreviewAdmissionSuperseded, PreviewEngine;
31957
+ var import_node_crypto33, PreviewAdmissionSuperseded, PreviewEngine;
31792
31958
  var init_engine = __esm({
31793
31959
  "src/core/preview/engine.ts"() {
31794
31960
  "use strict";
31795
- import_node_crypto32 = require("crypto");
31961
+ import_node_crypto33 = require("crypto");
31796
31962
  init_command();
31797
31963
  init_lifecycle();
31798
31964
  init_state();
@@ -31926,7 +32092,7 @@ var init_engine = __esm({
31926
32092
  } else {
31927
32093
  if (current && current.session.status !== "stopped" && current.session.status !== "expired")
31928
32094
  throw new Error("Previous generation cleanup is incomplete.");
31929
- const generation = (0, import_node_crypto32.createHash)("sha256").update(receipt).digest("hex").slice(0, 24);
32095
+ const generation = (0, import_node_crypto33.createHash)("sha256").update(receipt).digest("hex").slice(0, 24);
31930
32096
  pool2.sessions[id5] = {
31931
32097
  identity: input2.identity,
31932
32098
  session: createPreviewSession({
@@ -32459,10 +32625,10 @@ var init_status3 = __esm({
32459
32625
  throw new Error("Forge requires a trusted HTTPS API base.");
32460
32626
  }
32461
32627
  base;
32462
- async request(method, path79, body, maxBytes) {
32463
- if (!path79 || path79.startsWith("/") || path79.split("?")[0].split("/").some((part) => [".", ".."].includes(decodeURIComponent(part))) || path79.includes("\\") || path79.includes("#"))
32628
+ async request(method, path80, body, maxBytes) {
32629
+ if (!path80 || path80.startsWith("/") || path80.split("?")[0].split("/").some((part) => [".", ".."].includes(decodeURIComponent(part))) || path80.includes("\\") || path80.includes("#"))
32464
32630
  throw new Error("Invalid forge API path.");
32465
- const url = new URL(path79, this.base);
32631
+ const url = new URL(path80, this.base);
32466
32632
  if (url.origin !== this.base.origin || !url.pathname.startsWith(this.base.pathname))
32467
32633
  throw new Error("Forge request escaped its configured API.");
32468
32634
  let token;
@@ -32502,13 +32668,13 @@ var init_status3 = __esm({
32502
32668
  throw new Error("Preview forge response is unreadable or exceeds its bound.");
32503
32669
  }
32504
32670
  }
32505
- async pages(path79, options = {}) {
32671
+ async pages(path80, options = {}) {
32506
32672
  const rows2 = [];
32507
32673
  for (let page = 1; page <= 100; page++) {
32508
- const separator = path79.includes("?") ? "&" : "?";
32674
+ const separator = path80.includes("?") ? "&" : "?";
32509
32675
  const result = await this.request(
32510
32676
  "GET",
32511
- `${path79}${separator}per_page=100&page=${page}`,
32677
+ `${path80}${separator}per_page=100&page=${page}`,
32512
32678
  void 0,
32513
32679
  options.maxBytes
32514
32680
  );
@@ -32534,14 +32700,14 @@ var init_status3 = __esm({
32534
32700
 
32535
32701
  // src/core/preview/controller.ts
32536
32702
  function decodeInbox(raw) {
32537
- const row = object4(
32703
+ const row = object5(
32538
32704
  raw,
32539
32705
  ["schemaVersion", "identity", "scannedThrough", "closedThrough", "processed"],
32540
32706
  ["status", "selection", "needsStatus", "refusal"]
32541
32707
  );
32542
32708
  if (row.schemaVersion !== 1 || Buffer.byteLength(JSON.stringify(raw)) > 128 * 1024)
32543
32709
  throw new Error("Invalid preview inbox.");
32544
- const identity2 = object4(row.identity, ["forgeHost", "projectId", "change"]);
32710
+ const identity2 = object5(row.identity, ["forgeHost", "projectId", "change"]);
32545
32711
  previewIdentityKey(identity2);
32546
32712
  for (const key of ["scannedThrough", "closedThrough"])
32547
32713
  if (!Number.isSafeInteger(row[key]) || row[key] < 0)
@@ -32550,7 +32716,7 @@ function decodeInbox(raw) {
32550
32716
  throw new Error("Preview inbox exceeds its command bound.");
32551
32717
  const ids2 = /* @__PURE__ */ new Set();
32552
32718
  for (const item of row.processed) {
32553
- const receipt = object4(item, ["id", "at"]);
32719
+ const receipt = object5(item, ["id", "at"]);
32554
32720
  forgeId(receipt.id);
32555
32721
  if (ids2.has(receipt.id) || !Number.isSafeInteger(receipt.at) || receipt.at < 0)
32556
32722
  throw new Error("Invalid preview inbox receipt.");
@@ -32565,7 +32731,7 @@ function decodeInbox(raw) {
32565
32731
  if (row.refusal !== void 0 && !previewRefusalReasons.includes(row.refusal))
32566
32732
  throw new Error("Invalid preview refusal outcome.");
32567
32733
  if (row.status) {
32568
- const status = object4(
32734
+ const status = object5(
32569
32735
  row.status,
32570
32736
  ["intentAt"],
32571
32737
  ["commentId", "bodyDigest", "publishedAt", "intentId"]
@@ -32582,11 +32748,11 @@ function decodeInbox(raw) {
32582
32748
  }
32583
32749
  return structuredClone(row);
32584
32750
  }
32585
- var import_node_crypto33, PreviewController;
32751
+ var import_node_crypto34, PreviewController;
32586
32752
  var init_controller2 = __esm({
32587
32753
  "src/core/preview/controller.ts"() {
32588
32754
  "use strict";
32589
- import_node_crypto33 = require("crypto");
32755
+ import_node_crypto34 = require("crypto");
32590
32756
  init_command();
32591
32757
  init_engine();
32592
32758
  init_status3();
@@ -32793,7 +32959,7 @@ Command refused: ${inbox.refusal}. Post a new command when the condition is reso
32793
32959
  Available profiles: ${this.options.aliases.map((alias) => `\`${alias}\``).join(", ")}.` : ""}
32794
32960
 
32795
32961
  ${marker}`;
32796
- const fingerprint = (0, import_node_crypto33.createHash)("sha256").update(body).digest("hex"), now = this.options.now();
32962
+ const fingerprint = (0, import_node_crypto34.createHash)("sha256").update(body).digest("hex"), now = this.options.now();
32797
32963
  if (inbox.status?.bodyDigest === fingerprint) {
32798
32964
  if (inbox.needsStatus) await this.update(change, (row) => ({ ...row, needsStatus: false }));
32799
32965
  return;
@@ -32804,7 +32970,7 @@ ${marker}`;
32804
32970
  let commentId = inbox.status?.commentId;
32805
32971
  if (!commentId) commentId = await this.options.forge.findStatus(change, marker) ?? void 0;
32806
32972
  let create = false;
32807
- const intentId = (0, import_node_crypto33.randomUUID)();
32973
+ const intentId = (0, import_node_crypto34.randomUUID)();
32808
32974
  if (!inbox.status)
32809
32975
  inbox = await this.update(change, (row) => {
32810
32976
  create = !row.status;
@@ -33140,11 +33306,11 @@ var init_github = __esm({
33140
33306
  throw new Error("Preview status has no ownership marker.");
33141
33307
  if (Buffer.byteLength(body) > 32 * 1024) throw new Error("Preview status exceeds its bound.");
33142
33308
  if (previousId) {
33143
- const path79 = `${this.repo}/issues/comments/${forgeId(previousId)}`;
33144
- const comment = forgeObject((await this.http.request("GET", path79)).data);
33309
+ const path80 = `${this.repo}/issues/comments/${forgeId(previousId)}`;
33310
+ const comment = forgeObject((await this.http.request("GET", path80)).data);
33145
33311
  if (forgeId(forgeObject(comment.user).id) !== this.options.botId || typeof comment.body !== "string" || !comment.body.startsWith(PREVIEW_STATUS_MARKER) || comment.issue_url !== `${this.issues}${forgeId(change)}`)
33146
33312
  throw new Error("Preview status comment ownership differs.");
33147
- await this.http.request("PATCH", path79, { body });
33313
+ await this.http.request("PATCH", path80, { body });
33148
33314
  return previousId;
33149
33315
  }
33150
33316
  const created = forgeObject(
@@ -33320,11 +33486,11 @@ var init_gitlab = __esm({
33320
33486
  const row = forgeObject(raw);
33321
33487
  if (row.collapsed !== false || row.too_large !== false || ["new_file", "deleted_file", "renamed_file"].some((key) => typeof row[key] !== "boolean"))
33322
33488
  throw new Error("GitLab file diff is incomplete.");
33323
- const path79 = assertRepoRelativePath(row.new_path, "GitLab changed file"), oldPath = assertRepoRelativePath(row.old_path, "GitLab old file");
33324
- if (row.renamed_file) return { status: "R", path: path79, oldPath };
33489
+ const path80 = assertRepoRelativePath(row.new_path, "GitLab changed file"), oldPath = assertRepoRelativePath(row.old_path, "GitLab old file");
33490
+ if (row.renamed_file) return { status: "R", path: path80, oldPath };
33325
33491
  return {
33326
33492
  status: row.new_file ? "A" : row.deleted_file ? "D" : row.a_mode !== row.b_mode ? "T" : "M",
33327
- path: path79
33493
+ path: path80
33328
33494
  };
33329
33495
  });
33330
33496
  const current = await this.merge(change);
@@ -33554,7 +33720,7 @@ function canonicalizeImageConfiguration(configuration) {
33554
33720
  return JSON.stringify(normalizeConfiguration(configuration, "image configuration"));
33555
33721
  }
33556
33722
  function imageConfigurationDigest(configuration) {
33557
- return `sha256:${(0, import_node_crypto34.createHash)("sha256").update(canonicalizeImageConfiguration(configuration), "utf-8").digest("hex")}`;
33723
+ return `sha256:${(0, import_node_crypto35.createHash)("sha256").update(canonicalizeImageConfiguration(configuration), "utf-8").digest("hex")}`;
33558
33724
  }
33559
33725
  function targetKey(target, configurationDigest) {
33560
33726
  return `${target}\0${configurationDigest}`;
@@ -33809,11 +33975,11 @@ function resolvePreviewImages(plan, prepared, expectedProducer, value) {
33809
33975
  images: Object.fromEntries(resourceReceipts)
33810
33976
  };
33811
33977
  }
33812
- var import_node_crypto34, MAX_COLLECTION_ENTRIES2, MAX_CONFIGURATION_ENTRIES, MAX_CONFIGURATION_KEY_LENGTH, MAX_CONFIGURATION_VALUE_BYTES, MAX_IDENTIFIER_LENGTH, NAME_RE2, REVISION_RE2, DIGEST_RE, IMAGE_COMPONENT_RE, RECEIPT_FIELDS;
33978
+ var import_node_crypto35, MAX_COLLECTION_ENTRIES2, MAX_CONFIGURATION_ENTRIES, MAX_CONFIGURATION_KEY_LENGTH, MAX_CONFIGURATION_VALUE_BYTES, MAX_IDENTIFIER_LENGTH, NAME_RE2, REVISION_RE2, DIGEST_RE, IMAGE_COMPONENT_RE, RECEIPT_FIELDS;
33813
33979
  var init_images = __esm({
33814
33980
  "src/core/preview/images.ts"() {
33815
33981
  "use strict";
33816
- import_node_crypto34 = require("crypto");
33982
+ import_node_crypto35 = require("crypto");
33817
33983
  MAX_COLLECTION_ENTRIES2 = 256;
33818
33984
  MAX_CONFIGURATION_ENTRIES = 128;
33819
33985
  MAX_CONFIGURATION_KEY_LENGTH = 128;
@@ -33847,8 +34013,8 @@ function commandImageBackend(options) {
33847
34013
  throw new Error("Invalid installed image resolver command.");
33848
34014
  const verify = () => {
33849
34015
  for (const file of options.files) {
33850
- const stat = import_node_fs55.default.lstatSync(file.path);
33851
- if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 1024 * 1024 || (0, import_node_crypto35.createHash)("sha256").update(import_node_fs55.default.readFileSync(file.path)).digest("hex") !== file.sha256)
34016
+ const stat = import_node_fs56.default.lstatSync(file.path);
34017
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 1024 * 1024 || (0, import_node_crypto36.createHash)("sha256").update(import_node_fs56.default.readFileSync(file.path)).digest("hex") !== file.sha256)
33852
34018
  throw new Error("Installed image resolver fingerprint differs.");
33853
34019
  }
33854
34020
  };
@@ -33860,7 +34026,7 @@ function commandImageBackend(options) {
33860
34026
  platform: options.platform,
33861
34027
  configurations: options.context(entry)
33862
34028
  });
33863
- const correlation = (0, import_node_crypto35.createHash)("sha256").update(
34029
+ const correlation = (0, import_node_crypto36.createHash)("sha256").update(
33864
34030
  JSON.stringify([
33865
34031
  entry.identity,
33866
34032
  entry.session.generation,
@@ -33884,7 +34050,7 @@ function commandImageBackend(options) {
33884
34050
  });
33885
34051
  if (Buffer.byteLength(input2) > 1024 * 1024)
33886
34052
  throw new Error("Image request exceeds its bound.");
33887
- const result = (0, import_node_child_process41.spawnSync)(options.command[0], options.command.slice(1), {
34053
+ const result = (0, import_node_child_process42.spawnSync)(options.command[0], options.command.slice(1), {
33888
34054
  shell: false,
33889
34055
  cwd: options.cwd,
33890
34056
  encoding: "utf8",
@@ -33897,7 +34063,7 @@ function commandImageBackend(options) {
33897
34063
  throw new Error("Installed image resolver failed; output suppressed.");
33898
34064
  return JSON.parse(result.stdout);
33899
34065
  };
33900
- const resolved2 = object4(invoke("resolve"), ["receipts"], ["pending"]);
34066
+ const resolved2 = object5(invoke("resolve"), ["receipts"], ["pending"]);
33901
34067
  const outcome = resolvePreviewImages(entry.plan, prepared, options.producer, {
33902
34068
  receipts: resolved2.receipts
33903
34069
  });
@@ -33922,13 +34088,13 @@ function commandImageBackend(options) {
33922
34088
  }
33923
34089
  };
33924
34090
  }
33925
- var import_node_child_process41, import_node_crypto35, import_node_fs55;
34091
+ var import_node_child_process42, import_node_crypto36, import_node_fs56;
33926
34092
  var init_image_command = __esm({
33927
34093
  "src/core/preview/image-command.ts"() {
33928
34094
  "use strict";
33929
- import_node_child_process41 = require("child_process");
33930
- import_node_crypto35 = require("crypto");
33931
- import_node_fs55 = __toESM(require("fs"));
34095
+ import_node_child_process42 = require("child_process");
34096
+ import_node_crypto36 = require("crypto");
34097
+ import_node_fs56 = __toESM(require("fs"));
33932
34098
  init_images();
33933
34099
  init_state();
33934
34100
  }
@@ -34621,9 +34787,9 @@ function compileNorthflankGraph(entry, policy, phase, owned = {
34621
34787
  const resource = resources.get(id5);
34622
34788
  if (!resource?.ports?.some((item) => item.name === port && item.public))
34623
34789
  throw new Error("Northflank origin binding requires a selected public port.");
34624
- return `https://${prefix}--${id5.slice(0, 16)}-${port}-${(0, import_node_crypto36.createHash)("sha256").update(id5).digest("hex").slice(0, 6)}.${policy.baseDomain}`;
34790
+ return `https://${prefix}--${id5.slice(0, 16)}-${port}-${(0, import_node_crypto37.createHash)("sha256").update(id5).digest("hex").slice(0, 6)}.${policy.baseDomain}`;
34625
34791
  };
34626
- const resourceName = (id5) => `${northflankAllocationPrefix(policy, entry.session.generation)}${(0, import_node_crypto36.createHash)("sha256").update(id5).digest("hex").slice(0, 10)}`;
34792
+ const resourceName = (id5) => `${northflankAllocationPrefix(policy, entry.session.generation)}${(0, import_node_crypto37.createHash)("sha256").update(id5).digest("hex").slice(0, 10)}`;
34627
34793
  const suffix = (value) => {
34628
34794
  if (value === void 0) return "";
34629
34795
  if (typeof value !== "string" || value.length > 1024 || !value.startsWith("/") || value.startsWith("//") || value.includes("\\") || new RegExp("\\s|\\p{Cc}", "u").test(value))
@@ -35050,7 +35216,7 @@ function compileNorthflankGraph(entry, policy, phase, owned = {
35050
35216
  ]
35051
35217
  }
35052
35218
  };
35053
- const digest5 = (0, import_node_crypto36.createHash)("sha256").update(JSON.stringify({ spec, runtimeSecrets })).digest("hex");
35219
+ const digest5 = (0, import_node_crypto37.createHash)("sha256").update(JSON.stringify({ spec, runtimeSecrets })).digest("hex");
35054
35220
  return {
35055
35221
  spec,
35056
35222
  digest: digest5,
@@ -35061,11 +35227,11 @@ function compileNorthflankGraph(entry, policy, phase, owned = {
35061
35227
  publicBuildEnvironment
35062
35228
  };
35063
35229
  }
35064
- var import_node_crypto36, import_node_net5, ALLOCATION_PLAN, DATABASE_NAME, SOURCE_NAME, MAX_SOURCE_NAMES, MAX_OUTPUTS, MAX_SECRET_GROUPS, MAX_OUTPUT_ARGUMENTS, MAX_OUTPUT_ARGUMENT_BYTES, MAX_OUTPUT_COMMAND_BYTES, MAX_OUTPUT_BYTES2, MAX_PATTERN_BYTES, boundedStorage;
35230
+ var import_node_crypto37, import_node_net5, ALLOCATION_PLAN, DATABASE_NAME, SOURCE_NAME, MAX_SOURCE_NAMES, MAX_OUTPUTS, MAX_SECRET_GROUPS, MAX_OUTPUT_ARGUMENTS, MAX_OUTPUT_ARGUMENT_BYTES, MAX_OUTPUT_COMMAND_BYTES, MAX_OUTPUT_BYTES2, MAX_PATTERN_BYTES, boundedStorage;
35065
35231
  var init_northflank_graph = __esm({
35066
35232
  "src/core/preview/providers/northflank-graph.ts"() {
35067
35233
  "use strict";
35068
- import_node_crypto36 = require("crypto");
35234
+ import_node_crypto37 = require("crypto");
35069
35235
  import_node_net5 = require("net");
35070
35236
  ALLOCATION_PLAN = /^[a-zA-Z0-9-]{3,100}$/;
35071
35237
  DATABASE_NAME = /^[a-z][a-z0-9_]{0,62}$/;
@@ -35090,7 +35256,7 @@ function identifier(value) {
35090
35256
  function decodeNorthflankOwnership(value) {
35091
35257
  if (Buffer.byteLength(JSON.stringify(value) ?? "") > 128 * 1024)
35092
35258
  throw new Error("Northflank ownership exceeds its bound.");
35093
- const row = object4(
35259
+ const row = object5(
35094
35260
  value,
35095
35261
  ["schemaVersion", "generation", "policyDigest", "resources", "absent"],
35096
35262
  [
@@ -35119,7 +35285,7 @@ function decodeNorthflankOwnership(value) {
35119
35285
  )))
35120
35286
  throw new Error("Invalid Northflank completed-job metadata.");
35121
35287
  if (row.apiWait !== void 0) {
35122
- const wait = object4(row.apiWait, ["until", "exhausted"]);
35288
+ const wait = object5(row.apiWait, ["until", "exhausted"]);
35123
35289
  if (!Number.isSafeInteger(wait.until) || wait.until < 1 || typeof wait.exhausted !== "boolean")
35124
35290
  throw new Error("Invalid Northflank API wait metadata.");
35125
35291
  }
@@ -35128,7 +35294,7 @@ function decodeNorthflankOwnership(value) {
35128
35294
  const references = /* @__PURE__ */ new Set();
35129
35295
  const ids2 = /* @__PURE__ */ new Set();
35130
35296
  for (const raw of row.resources) {
35131
- const resource = object4(raw, ["reference", "kind", "id", "name"], ["uid"]);
35297
+ const resource = object5(raw, ["reference", "kind", "id", "name"], ["uid"]);
35132
35298
  identifier(resource.id);
35133
35299
  identifier(resource.reference);
35134
35300
  identifier(resource.name);
@@ -35169,7 +35335,7 @@ function specDigest(value) {
35169
35335
  return digest3(definition(value));
35170
35336
  }
35171
35337
  function digest3(value) {
35172
- return (0, import_node_crypto37.createHash)("sha256").update(JSON.stringify(value)).digest("hex");
35338
+ return (0, import_node_crypto38.createHash)("sha256").update(JSON.stringify(value)).digest("hex");
35173
35339
  }
35174
35340
  function bindings(owned) {
35175
35341
  return {
@@ -35194,11 +35360,11 @@ function outputNames(graph) {
35194
35360
  )
35195
35361
  ].sort();
35196
35362
  }
35197
- var import_node_crypto37, resourceKinds, NorthflankOwnershipStore, northflankQualificationKeys, NorthflankFailure, NorthflankFenced, NorthflankPreviewProvider, nativeKinds, emptySpec;
35363
+ var import_node_crypto38, resourceKinds, NorthflankOwnershipStore, northflankQualificationKeys, NorthflankFailure, NorthflankFenced, NorthflankPreviewProvider, nativeKinds, emptySpec;
35198
35364
  var init_northflank = __esm({
35199
35365
  "src/core/preview/providers/northflank.ts"() {
35200
35366
  "use strict";
35201
- import_node_crypto37 = require("crypto");
35367
+ import_node_crypto38 = require("crypto");
35202
35368
  init_command();
35203
35369
  init_state();
35204
35370
  init_northflank_api();
@@ -35831,7 +35997,7 @@ var init_northflank = __esm({
35831
35997
  const settings = this.options.graph.generated?.[name];
35832
35998
  if (!settings)
35833
35999
  throw new NorthflankFailure("Northflank generated value is undeclared.");
35834
- value = (0, import_node_crypto37.randomBytes)(settings.bytes).toString(settings.encoding);
36000
+ value = (0, import_node_crypto38.randomBytes)(settings.bytes).toString(settings.encoding);
35835
36001
  }
35836
36002
  if (kind === "output" && value === void 0)
35837
36003
  throw new NorthflankFailure("Northflank output has no verified recipient.");
@@ -35908,10 +36074,10 @@ var init_northflank = __esm({
35908
36074
  for (const step of steps.filter((item) => item.name.endsWith("-run"))) {
35909
36075
  if (!owned.blueprintId) return this.receipt(owned);
35910
36076
  const operation = `${step.name.slice(0, -4)}-${digest3(effect2.id).slice(0, 20)}`;
35911
- const path79 = ["preview-blueprints", owned.blueprintId];
35912
- const id5 = step.ref ?? await this.findRun(path79, operation);
36077
+ const path80 = ["preview-blueprints", owned.blueprintId];
36078
+ const id5 = step.ref ?? await this.findRun(path80, operation);
35913
36079
  if (!id5) return this.receipt(owned);
35914
- const result = await this.options.api.request("GET", [...path79, "runs", id5]);
36080
+ const result = await this.options.api.request("GET", [...path80, "runs", id5]);
35915
36081
  if (!result) return this.receipt(owned);
35916
36082
  const run = data(result);
35917
36083
  if (!run.concluded || specDigest(run.spec) !== step.fingerprint || northflankRecord(run.arguments).devrouterOperation !== operation || run.environmentId !== (owned.previewId ?? `preview-${entry.session.generation}`))
@@ -35934,11 +36100,11 @@ var init_northflank = __esm({
35934
36100
  }
35935
36101
  };
35936
36102
  }
35937
- async findRun(path79, operation) {
36103
+ async findRun(path80, operation) {
35938
36104
  let match;
35939
- for (const summary of await this.options.api.list([...path79, "runs"], "runs")) {
36105
+ for (const summary of await this.options.api.list([...path80, "runs"], "runs")) {
35940
36106
  const id5 = northflankIdentifier(summary.id);
35941
- const candidate = data(await this.options.api.request("GET", [...path79, "runs", id5]));
36107
+ const candidate = data(await this.options.api.request("GET", [...path80, "runs", id5]));
35942
36108
  if (northflankRecord(candidate.arguments ?? {}).devrouterOperation !== operation) continue;
35943
36109
  if (match) throw new NorthflankFailure("Ambiguous Northflank run outcome.");
35944
36110
  match = id5;
@@ -35947,13 +36113,13 @@ var init_northflank = __esm({
35947
36113
  }
35948
36114
  async run(entry, effect2, gate, owned, stage, spec) {
35949
36115
  const api = this.options.api;
35950
- const path79 = ["preview-blueprints", northflankIdentifier(owned.blueprintId)];
36116
+ const path80 = ["preview-blueprints", northflankIdentifier(owned.blueprintId)];
35951
36117
  let fingerprint = specDigest(spec);
35952
36118
  const operation = `${stage}-${digest3(effect2.id).slice(0, 20)}`;
35953
36119
  const recorded = (await gate.read()).find((step2) => step2.name === `${stage}-run`);
35954
36120
  if (!recorded) {
35955
36121
  if (await begin(gate, `${stage}-spec`, "once", fingerprint) === "write") {
35956
- await api.request("POST", path79, {
36122
+ await api.request("POST", path80, {
35957
36123
  name: this.name(entry),
35958
36124
  apiVersion: "v1.2",
35959
36125
  spec,
@@ -35962,14 +36128,14 @@ var init_northflank = __esm({
35962
36128
  });
35963
36129
  await gate.record(`${stage}-spec`, "accepted");
35964
36130
  }
35965
- const detail = data(await api.request("GET", path79));
36131
+ const detail = data(await api.request("GET", path80));
35966
36132
  if (specDigest(detail.spec) !== fingerprint)
35967
36133
  throw new NorthflankFailure("Northflank blueprint specification drifted.");
35968
36134
  await gate.record(`${stage}-spec`, "accepted");
35969
36135
  if (await begin(gate, `${stage}-run`, "once", fingerprint) === "write") {
35970
36136
  try {
35971
36137
  const run2 = data(
35972
- await api.request("POST", [...path79, "runs"], {
36138
+ await api.request("POST", [...path80, "runs"], {
35973
36139
  name: owned.previewId ?? `preview-${entry.session.generation}`,
35974
36140
  arguments: { devrouterOperation: operation }
35975
36141
  })
@@ -35990,15 +36156,15 @@ var init_northflank = __esm({
35990
36156
  if (!step) return null;
35991
36157
  let id5 = step.ref;
35992
36158
  if (!id5) {
35993
- id5 = await this.findRun(path79, operation);
36159
+ id5 = await this.findRun(path80, operation);
35994
36160
  if (!id5) return null;
35995
36161
  await gate.record(`${stage}-run`, "accepted", id5);
35996
36162
  }
35997
- const run = data(await api.request("GET", [...path79, "runs", id5]));
36163
+ const run = data(await api.request("GET", [...path80, "runs", id5]));
35998
36164
  if (specDigest(run.spec) !== fingerprint || northflankRecord(run.arguments).devrouterOperation !== operation || run.environmentId !== (owned.previewId ?? `preview-${entry.session.generation}`))
35999
36165
  throw new NorthflankFailure("Northflank executed an unexpected specification or operation.");
36000
36166
  if (owned.previewId) {
36001
- const previews = await api.list([...path79, "previews"], "previewEnvironments");
36167
+ const previews = await api.list([...path80, "previews"], "previewEnvironments");
36002
36168
  if (previews.length !== 1 || previews[0].id !== owned.previewId)
36003
36169
  throw new NorthflankFailure("Northflank run changed the generation environment.");
36004
36170
  }
@@ -36073,33 +36239,33 @@ var init_northflank = __esm({
36073
36239
  const candidates = await this.namedBlueprints(entry);
36074
36240
  return this.receipt(owned, candidates.length === 0);
36075
36241
  }
36076
- const path79 = ["preview-blueprints", owned.blueprintId];
36077
- const blueprint = await this.options.api.request("GET", path79);
36242
+ const path80 = ["preview-blueprints", owned.blueprintId];
36243
+ const blueprint = await this.options.api.request("GET", path80);
36078
36244
  if (!blueprint && !owned.blueprintObserved && !owned.blueprintRejected)
36079
36245
  return this.receipt(owned);
36080
36246
  if (blueprint) {
36081
36247
  if (!owned.blueprintObserved)
36082
36248
  owned = await store.update((current) => ({ ...current, blueprintObserved: true }));
36083
- const runs = await this.options.api.list([...path79, "runs"], "runs");
36249
+ const runs = await this.options.api.list([...path80, "runs"], "runs");
36084
36250
  for (const run of runs.filter((row) => row.concluded !== true)) {
36085
36251
  const id5 = northflankIdentifier(run.id);
36086
36252
  const step = `abort-${digest3(id5).slice(0, 12)}`;
36087
36253
  if (await begin(gate, step, "convergent") === "write") {
36088
- await this.options.api.request("POST", [...path79, "runs", id5, "abort"]);
36254
+ await this.options.api.request("POST", [...path80, "runs", id5, "abort"]);
36089
36255
  await gate.record(step, "accepted", id5);
36090
36256
  }
36091
36257
  }
36092
36258
  if (effect2.kind === "cancel") return this.receipt(owned, true);
36093
- const previews = await this.options.api.list([...path79, "previews"], "previewEnvironments");
36259
+ const previews = await this.options.api.list([...path80, "previews"], "previewEnvironments");
36094
36260
  for (const preview of previews) {
36095
36261
  const id5 = northflankIdentifier(preview.id);
36096
36262
  const step = `delete-${digest3(id5).slice(0, 12)}`;
36097
36263
  if (await begin(gate, step, "convergent") === "write") {
36098
- await this.options.api.request("DELETE", [...path79, "previews", id5]);
36264
+ await this.options.api.request("DELETE", [...path80, "previews", id5]);
36099
36265
  await gate.record(step, "accepted", id5);
36100
36266
  }
36101
36267
  }
36102
- if ((await this.options.api.list([...path79, "previews"], "previewEnvironments")).length)
36268
+ if ((await this.options.api.list([...path80, "previews"], "previewEnvironments")).length)
36103
36269
  return this.receipt(owned);
36104
36270
  let cleanedDomains = 0;
36105
36271
  for (const resource of owned.resources.filter((item) => item.kind === "domains")) {
@@ -36112,7 +36278,7 @@ var init_northflank = __esm({
36112
36278
  }
36113
36279
  }
36114
36280
  if (await begin(gate, "delete-blueprint", "convergent") === "write") {
36115
- await this.options.api.request("DELETE", path79);
36281
+ await this.options.api.request("DELETE", path80);
36116
36282
  await gate.record("delete-blueprint", "accepted");
36117
36283
  }
36118
36284
  }
@@ -36334,10 +36500,10 @@ __export(installation_exports, {
36334
36500
  loadPreviewInstallation: () => loadPreviewInstallation
36335
36501
  });
36336
36502
  function readFile(file) {
36337
- const stat = import_node_fs56.default.lstatSync(file);
36503
+ const stat = import_node_fs57.default.lstatSync(file);
36338
36504
  if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 1024 * 1024)
36339
36505
  throw new Error("Preview control files must be regular files of at most 1 MiB.");
36340
- return import_node_fs56.default.readFileSync(file, "utf8");
36506
+ return import_node_fs57.default.readFileSync(file, "utf8");
36341
36507
  }
36342
36508
  function text5(value, pattern) {
36343
36509
  if (typeof value !== "string" || !pattern.test(value))
@@ -36350,8 +36516,8 @@ function integer2(value, min, max) {
36350
36516
  return value;
36351
36517
  }
36352
36518
  function loadPreviewInstallation(filename, options = {}) {
36353
- const root = import_node_path69.default.dirname(import_node_fs56.default.realpathSync(filename));
36354
- const raw = object4(JSON.parse(readFile(filename)), [
36519
+ const root = import_node_path70.default.dirname(import_node_fs57.default.realpathSync(filename));
36520
+ const raw = object5(JSON.parse(readFile(filename)), [
36355
36521
  "version",
36356
36522
  "installationId",
36357
36523
  "state",
@@ -36386,10 +36552,10 @@ function loadPreviewInstallation(filename, options = {}) {
36386
36552
  return value;
36387
36553
  };
36388
36554
  };
36389
- const statePolicy = object4(raw.state, ["containerUrl"], ["tokenEnv", "federated"]);
36555
+ const statePolicy = object5(raw.state, ["containerUrl"], ["tokenEnv", "federated"]);
36390
36556
  if (statePolicy.tokenEnv === void 0 === (statePolicy.federated === void 0))
36391
36557
  throw new Error("Preview state requires exactly one installed authentication method.");
36392
- const federated = statePolicy.federated === void 0 ? void 0 : object4(statePolicy.federated, ["tenantIdEnv", "clientIdEnv", "assertionEnv"]);
36558
+ const federated = statePolicy.federated === void 0 ? void 0 : object5(statePolicy.federated, ["tenantIdEnv", "clientIdEnv", "assertionEnv"]);
36393
36559
  const accessToken = federated ? azureFederatedPreviewToken({
36394
36560
  tenantId: credential(federated.tenantIdEnv),
36395
36561
  clientId: credential(federated.clientIdEnv),
@@ -36405,13 +36571,13 @@ function loadPreviewInstallation(filename, options = {}) {
36405
36571
  if (!Array.isArray(raw.projects) || raw.projects.length < 1 || raw.projects.length > 16)
36406
36572
  throw new Error("Preview installation requires 1-16 registered projects.");
36407
36573
  const projects = raw.projects.map((input2) => {
36408
- const row = object4(
36574
+ const row = object5(
36409
36575
  input2,
36410
36576
  ["key", "forge", "config", "manifest", "northflank", "hourlyCents", "acceptance"],
36411
36577
  ["images"]
36412
36578
  );
36413
36579
  const key = text5(row.key, /^[a-z][a-z0-9-]{0,62}$/);
36414
- const forgePolicy = object4(
36580
+ const forgePolicy = object5(
36415
36581
  row.forge,
36416
36582
  ["kind", "host", "apiUrl", "repository", "projectId", "botId", "tokenEnv"],
36417
36583
  ["controllerProjectId"]
@@ -36431,10 +36597,10 @@ function loadPreviewInstallation(filename, options = {}) {
36431
36597
  fetch: options.fetch
36432
36598
  });
36433
36599
  const control = (input3) => {
36434
- const ref2 = object4(input3, ["file", "sha256"]);
36600
+ const ref2 = object5(input3, ["file", "sha256"]);
36435
36601
  const filename2 = text5(ref2.file, /^(?!\/)(?!.*(?:^|\/)\.\.(?:\/|$))[^\\\0]+$/);
36436
- const file = import_node_fs56.default.realpathSync(import_node_path69.default.join(root, filename2));
36437
- if (!file.startsWith(`${root}${import_node_path69.default.sep}`))
36602
+ const file = import_node_fs57.default.realpathSync(import_node_path70.default.join(root, filename2));
36603
+ if (!file.startsWith(`${root}${import_node_path70.default.sep}`))
36438
36604
  throw new Error("Preview control file escapes installation directory.");
36439
36605
  const source = readFile(file), expected = text5(ref2.sha256, /^[a-f0-9]{64}$/);
36440
36606
  if (digest4(source) !== expected)
@@ -36442,7 +36608,7 @@ function loadPreviewInstallation(filename, options = {}) {
36442
36608
  return { source, digest: expected };
36443
36609
  };
36444
36610
  const local = control(row.config), remote = control(row.manifest), config = parseRepoConfigSource(local.source, ".devrouter.yml"), manifest = parsePreviewManifest(remote.source);
36445
- const northflank = object4(row.northflank, [
36611
+ const northflank = object5(row.northflank, [
36446
36612
  "projectId",
36447
36613
  "tokenEnv",
36448
36614
  "graph",
@@ -36450,7 +36616,7 @@ function loadPreviewInstallation(filename, options = {}) {
36450
36616
  "runtimeSecrets"
36451
36617
  ]);
36452
36618
  const graph = northflank.graph;
36453
- const qualifications = object4(
36619
+ const qualifications = object5(
36454
36620
  northflank.qualification,
36455
36621
  [...northflankQualificationKeys],
36456
36622
  ["outputExec"]
@@ -36470,7 +36636,7 @@ function loadPreviewInstallation(filename, options = {}) {
36470
36636
  for (const id5 of Object.keys(manifest.resources)) integer2(rates[id5], 1, 1e5);
36471
36637
  if (Object.keys(rates).some((id5) => !manifest.resources[id5]))
36472
36638
  throw new Error("Preview cost names an unknown resource.");
36473
- const acceptance = object4(row.acceptance, ["command", "environment"]);
36639
+ const acceptance = object5(row.acceptance, ["command", "environment"]);
36474
36640
  if (!Array.isArray(acceptance.command) || acceptance.command.length < 1 || acceptance.command.length > 32 || acceptance.command.some(
36475
36641
  (arg) => typeof arg !== "string" || !arg || arg.length > 4096 || arg.includes("\0")
36476
36642
  ))
@@ -36496,7 +36662,7 @@ function loadPreviewInstallation(filename, options = {}) {
36496
36662
  return credential(secrets[ref2])();
36497
36663
  },
36498
36664
  accept: async (entry, urls) => {
36499
- const result = (0, import_node_child_process42.spawnSync)(command[0], command.slice(1), {
36665
+ const result = (0, import_node_child_process43.spawnSync)(command[0], command.slice(1), {
36500
36666
  cwd: root,
36501
36667
  shell: false,
36502
36668
  timeout: 3e4,
@@ -36514,7 +36680,7 @@ function loadPreviewInstallation(filename, options = {}) {
36514
36680
  });
36515
36681
  if (result.error || result.status !== 0) return false;
36516
36682
  try {
36517
- const proof = object4(JSON.parse(result.stdout), [
36683
+ const proof = object5(JSON.parse(result.stdout), [
36518
36684
  "generation",
36519
36685
  "revision",
36520
36686
  "urls",
@@ -36537,7 +36703,7 @@ function loadPreviewInstallation(filename, options = {}) {
36537
36703
  source,
36538
36704
  ...aliases2?.length ? { aliases: aliases2 } : {}
36539
36705
  });
36540
- const imagePolicy = row.images === void 0 ? void 0 : object4(row.images, [
36706
+ const imagePolicy = row.images === void 0 ? void 0 : object5(row.images, [
36541
36707
  "platform",
36542
36708
  "platformQualified",
36543
36709
  "producer",
@@ -36551,14 +36717,14 @@ function loadPreviewInstallation(filename, options = {}) {
36551
36717
  if (!Array.isArray(imageEnvironment) || imageEnvironment.length > 32)
36552
36718
  throw new Error("Invalid image resolver environment.");
36553
36719
  const imageEnvNames = imageEnvironment.map(envName);
36554
- const producer = imagePolicy ? object4(imagePolicy.producer, ["repository", "workflow", "controlRevision"]) : void 0;
36720
+ const producer = imagePolicy ? object5(imagePolicy.producer, ["repository", "workflow", "controlRevision"]) : void 0;
36555
36721
  if (imagePolicy && (!Array.isArray(imagePolicy.files) || imagePolicy.files.length > 32))
36556
36722
  throw new Error("Invalid reviewed image resolver files.");
36557
36723
  const imageFiles = imagePolicy ? imagePolicy.files.map((input3) => {
36558
- const item = object4(input3, ["file", "sha256"]);
36724
+ const item = object5(input3, ["file", "sha256"]);
36559
36725
  const verified = control(input3);
36560
36726
  return {
36561
- path: import_node_fs56.default.realpathSync(import_node_path69.default.join(root, item.file)),
36727
+ path: import_node_fs57.default.realpathSync(import_node_path70.default.join(root, item.file)),
36562
36728
  sha256: verified.digest
36563
36729
  };
36564
36730
  }) : [];
@@ -36673,7 +36839,7 @@ function loadPreviewInstallation(filename, options = {}) {
36673
36839
  }
36674
36840
  },
36675
36841
  now,
36676
- workerId: (0, import_node_crypto38.randomUUID)(),
36842
+ workerId: (0, import_node_crypto39.randomUUID)(),
36677
36843
  policy: {
36678
36844
  installationId,
36679
36845
  maxActive,
@@ -36780,7 +36946,7 @@ function loadPreviewInstallation(filename, options = {}) {
36780
36946
  }
36781
36947
  async function initializePreviewInstallation(installationId, store) {
36782
36948
  const poolKey = `installations/${installationId}/pool`, witnessKey = `installations/${installationId}/initialized`;
36783
- if (await store.read(witnessKey, (raw) => object4(raw, ["installationId"])))
36949
+ if (await store.read(witnessKey, (raw) => object5(raw, ["installationId"])))
36784
36950
  throw new Error(
36785
36951
  "Preview installation is already initialized; missing state requires operator recovery."
36786
36952
  );
@@ -36789,14 +36955,14 @@ async function initializePreviewInstallation(installationId, store) {
36789
36955
  await store.compareAndSwap(witnessKey, null, { installationId });
36790
36956
  await store.compareAndSwap(poolKey, null, createPreviewPool(installationId));
36791
36957
  }
36792
- var import_node_child_process42, import_node_crypto38, import_node_fs56, import_node_path69, MAX_COMMAND_AGE_MS, digest4, envName;
36958
+ var import_node_child_process43, import_node_crypto39, import_node_fs57, import_node_path70, MAX_COMMAND_AGE_MS, digest4, envName;
36793
36959
  var init_installation = __esm({
36794
36960
  "src/core/preview/installation.ts"() {
36795
36961
  "use strict";
36796
- import_node_child_process42 = require("child_process");
36797
- import_node_crypto38 = require("crypto");
36798
- import_node_fs56 = __toESM(require("fs"));
36799
- import_node_path69 = __toESM(require("path"));
36962
+ import_node_child_process43 = require("child_process");
36963
+ import_node_crypto39 = require("crypto");
36964
+ import_node_fs57 = __toESM(require("fs"));
36965
+ import_node_path70 = __toESM(require("path"));
36800
36966
  init_repo_config();
36801
36967
  init_command();
36802
36968
  init_controller2();
@@ -36813,7 +36979,7 @@ var init_installation = __esm({
36813
36979
  init_state();
36814
36980
  init_azure_blob();
36815
36981
  MAX_COMMAND_AGE_MS = 15 * 6e4;
36816
- digest4 = (source) => (0, import_node_crypto38.createHash)("sha256").update(source).digest("hex");
36982
+ digest4 = (source) => (0, import_node_crypto39.createHash)("sha256").update(source).digest("hex");
36817
36983
  envName = (value) => {
36818
36984
  if (typeof value !== "string" || !/^DEVROUTER_PREVIEW_[A-Z0-9_]+$/.test(value))
36819
36985
  throw new Error("Preview credential references require scoped environment names.");
@@ -36830,16 +36996,16 @@ __export(preview_exports, {
36830
36996
  runPreviewPlanCommand: () => runPreviewPlanCommand
36831
36997
  });
36832
36998
  function readSource(repoPath, filename) {
36833
- if (import_node_path70.default.isAbsolute(filename))
36999
+ if (import_node_path71.default.isAbsolute(filename))
36834
37000
  throw new Error("Preview source path must be repository-relative.");
36835
- const root = import_node_fs57.default.realpathSync(repoPath);
37001
+ const root = import_node_fs58.default.realpathSync(repoPath);
36836
37002
  const file = assertPathWithinRepo(filename, root, "preview source");
36837
- const stat = import_node_fs57.default.lstatSync(file);
37003
+ const stat = import_node_fs58.default.lstatSync(file);
36838
37004
  if (stat.isSymbolicLink() || !stat.isFile() || stat.size > 1024 * 1024) {
36839
37005
  throw new Error("Preview source must be a regular file of at most 1 MiB.");
36840
37006
  }
36841
- assertPathWithinRepo(import_node_fs57.default.realpathSync(file), root, "preview source");
36842
- return JSON.parse(import_node_fs57.default.readFileSync(file, "utf8"));
37007
+ assertPathWithinRepo(import_node_fs58.default.realpathSync(file), root, "preview source");
37008
+ return JSON.parse(import_node_fs58.default.readFileSync(file, "utf8"));
36843
37009
  }
36844
37010
  async function runPreviewPlanCommand(options) {
36845
37011
  const repoPath = resolveRepoPath(options.repo);
@@ -36854,13 +37020,13 @@ async function runPreviewPlanCommand(options) {
36854
37020
  `);
36855
37021
  return;
36856
37022
  }
36857
- const localConfig = import_node_fs57.default.readFileSync(import_node_path70.default.join(repoPath, ".devrouter.yml"), "utf8");
37023
+ const localConfig = import_node_fs58.default.readFileSync(import_node_path71.default.join(repoPath, ".devrouter.yml"), "utf8");
36858
37024
  const report = buildPreviewPlan({
36859
37025
  repoPath,
36860
37026
  config: loadRepoConfig(repoPath, () => localConfig),
36861
37027
  manifest,
36862
37028
  manifestDigest: digest5,
36863
- localConfigDigest: (0, import_node_crypto39.createHash)("sha256").update(localConfig).digest("hex"),
37029
+ localConfigDigest: (0, import_node_crypto40.createHash)("sha256").update(localConfig).digest("hex"),
36864
37030
  ...options.alias === void 0 ? {} : { aliases: options.alias.split(",") },
36865
37031
  ...options.source === void 0 ? {} : { source: readSource(repoPath, options.source) }
36866
37032
  });
@@ -36879,7 +37045,7 @@ async function runPreviewPlanCommand(options) {
36879
37045
  }
36880
37046
  }
36881
37047
  async function runPreviewInitCommand(options) {
36882
- const repoPath = import_node_fs57.default.realpathSync(resolveRepoPath(options.repo));
37048
+ const repoPath = import_node_fs58.default.realpathSync(resolveRepoPath(options.repo));
36883
37049
  if (options.integration === "gitlab") return renderGitLabPreviewIntegration(repoPath, options);
36884
37050
  if (options.integration) return renderPreviewIntegration(repoPath, options);
36885
37051
  loadRepoConfig(repoPath);
@@ -36902,10 +37068,10 @@ async function runPreviewInitCommand(options) {
36902
37068
  allowedUnions: []
36903
37069
  }
36904
37070
  };
36905
- const content = (0, import_yaml11.stringify)(manifest);
37071
+ const content = (0, import_yaml12.stringify)(manifest);
36906
37072
  if (options.write) {
36907
- const file = import_node_path70.default.join(repoPath, ".devrouter.preview.yml");
36908
- import_node_fs57.default.writeFileSync(file, content, { flag: "wx", mode: 384 });
37073
+ const file = import_node_path71.default.join(repoPath, ".devrouter.preview.yml");
37074
+ import_node_fs58.default.writeFileSync(file, content, { flag: "wx", mode: 384 });
36909
37075
  }
36910
37076
  if (options.json) {
36911
37077
  printJSON({
@@ -36970,12 +37136,12 @@ async function runPreviewInstallationCommand(action2, options) {
36970
37136
  }
36971
37137
  function previewAsset(name) {
36972
37138
  const candidates = [
36973
- import_node_path70.default.resolve(__dirname, "..", "assets", "preview", name),
36974
- import_node_path70.default.resolve(__dirname, "..", "..", "assets", "preview", name)
37139
+ import_node_path71.default.resolve(__dirname, "..", "assets", "preview", name),
37140
+ import_node_path71.default.resolve(__dirname, "..", "..", "assets", "preview", name)
36975
37141
  ];
36976
- const file = candidates.find((file2) => import_node_fs57.default.existsSync(file2));
37142
+ const file = candidates.find((file2) => import_node_fs58.default.existsSync(file2));
36977
37143
  if (!file) throw new Error("Installed preview integration asset is missing.");
36978
- return import_node_fs57.default.readFileSync(file, "utf8");
37144
+ return import_node_fs58.default.readFileSync(file, "utf8");
36979
37145
  }
36980
37146
  function renderPreviewAsset(name, replacements) {
36981
37147
  return previewAsset(name).replace(/__([A-Z_]+)__/g, (_match, key) => {
@@ -37010,9 +37176,9 @@ async function renderPreviewIntegration(repoPath, options) {
37010
37176
  const filename = `.github/workflows/devrouter-preview${options.sweeper ? "-sweep" : ""}.yml`;
37011
37177
  if (options.write) {
37012
37178
  const file = assertPathWithinRepo(filename, repoPath, "preview workflow");
37013
- import_node_fs57.default.mkdirSync(import_node_path70.default.dirname(file), { recursive: true });
37014
- assertPathWithinRepo(import_node_fs57.default.realpathSync(import_node_path70.default.dirname(file)), repoPath, "preview workflow parent");
37015
- import_node_fs57.default.writeFileSync(file, content, { flag: "wx", mode: 384 });
37179
+ import_node_fs58.default.mkdirSync(import_node_path71.default.dirname(file), { recursive: true });
37180
+ assertPathWithinRepo(import_node_fs58.default.realpathSync(import_node_path71.default.dirname(file)), repoPath, "preview workflow parent");
37181
+ import_node_fs58.default.writeFileSync(file, content, { flag: "wx", mode: 384 });
37016
37182
  }
37017
37183
  if (options.json)
37018
37184
  printJSON({
@@ -37030,7 +37196,7 @@ async function renderGitLabPreviewIntegration(repoPath, options) {
37030
37196
  throw new Error(
37031
37197
  "GitLab integration requires reviewed --controller-ref and --controller-image digest."
37032
37198
  );
37033
- const integrity = `sha256-${(0, import_node_crypto39.createHash)("sha256").update(previewAsset("gitlab-template.yml")).digest("base64")}`;
37199
+ const integrity = `sha256-${(0, import_node_crypto40.createHash)("sha256").update(previewAsset("gitlab-template.yml")).digest("base64")}`;
37034
37200
  const replacements = {
37035
37201
  CONTROLLER_REF: String(options.controllerRef),
37036
37202
  CONTROLLER_IMAGE: String(options.controllerImage),
@@ -37039,7 +37205,7 @@ async function renderGitLabPreviewIntegration(repoPath, options) {
37039
37205
  const content = renderPreviewAsset("gitlab.yml", replacements);
37040
37206
  const filename = ".gitlab-ci.yml";
37041
37207
  if (options.write)
37042
- import_node_fs57.default.writeFileSync(assertPathWithinRepo(filename, repoPath, "GitLab preview caller"), content, {
37208
+ import_node_fs58.default.writeFileSync(assertPathWithinRepo(filename, repoPath, "GitLab preview caller"), content, {
37043
37209
  flag: "wx",
37044
37210
  mode: 384
37045
37211
  });
@@ -37054,14 +37220,14 @@ async function renderGitLabPreviewIntegration(repoPath, options) {
37054
37220
  });
37055
37221
  else process.stdout.write(content);
37056
37222
  }
37057
- var import_node_crypto39, import_node_fs57, import_node_path70, import_yaml11;
37223
+ var import_node_crypto40, import_node_fs58, import_node_path71, import_yaml12;
37058
37224
  var init_preview = __esm({
37059
37225
  "src/commands/preview.ts"() {
37060
37226
  "use strict";
37061
- import_node_crypto39 = require("crypto");
37062
- import_node_fs57 = __toESM(require("fs"));
37063
- import_node_path70 = __toESM(require("path"));
37064
- import_yaml11 = require("yaml");
37227
+ import_node_crypto40 = require("crypto");
37228
+ import_node_fs58 = __toESM(require("fs"));
37229
+ import_node_path71 = __toESM(require("path"));
37230
+ import_yaml12 = require("yaml");
37065
37231
  init_output();
37066
37232
  init_paths();
37067
37233
  init_manifest();
@@ -37352,7 +37518,7 @@ function sanitizeRouterId(value) {
37352
37518
  return value.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
37353
37519
  }
37354
37520
  function repoHash(repoPath) {
37355
- return (0, import_node_crypto40.createHash)("sha1").update(import_node_path71.default.resolve(repoPath)).digest("hex").slice(0, 12);
37521
+ return (0, import_node_crypto41.createHash)("sha1").update(import_node_path72.default.resolve(repoPath)).digest("hex").slice(0, 12);
37356
37522
  }
37357
37523
  function asDockerApp(app) {
37358
37524
  return app.runtime === "docker";
@@ -37431,11 +37597,11 @@ function prepareDockerOverlay(repoPath, appName, apps, publishTcpPorts = false)
37431
37597
  if (dockerApps.length === 0) {
37432
37598
  throw new Error("No docker apps selected to prepare compose overlay.");
37433
37599
  }
37434
- const cachePath = import_node_path71.default.join(CACHE_DIR, repoHash(repoPath), sanitizeRouterId(appName));
37435
- import_node_fs58.default.mkdirSync(cachePath, { recursive: true });
37436
- const overlayPath = import_node_path71.default.join(cachePath, "compose.devrouter.yml");
37600
+ const cachePath = import_node_path72.default.join(CACHE_DIR, repoHash(repoPath), sanitizeRouterId(appName));
37601
+ import_node_fs59.default.mkdirSync(cachePath, { recursive: true });
37602
+ const overlayPath = import_node_path72.default.join(cachePath, "compose.devrouter.yml");
37437
37603
  const overlayDocument = buildOverlayDocument(dockerApps, publishTcpPorts);
37438
- import_node_fs58.default.writeFileSync(overlayPath, import_yaml12.default.stringify(overlayDocument, { lineWidth: 0 }), "utf-8");
37604
+ import_node_fs59.default.writeFileSync(overlayPath, import_yaml13.default.stringify(overlayDocument, { lineWidth: 0 }), "utf-8");
37439
37605
  return {
37440
37606
  overlayPath,
37441
37607
  composeFiles: ensureComposeFiles(dockerApps),
@@ -37449,7 +37615,7 @@ function runDockerComposeUp(repoPath, composeFiles2, overlayPath, services) {
37449
37615
  fileArgs.push("-f", resolved2);
37450
37616
  }
37451
37617
  const args = ["compose", ...fileArgs, "-f", overlayPath, "up", "-d", "--wait", ...services];
37452
- const result = (0, import_node_child_process43.spawnSync)("docker", args, {
37618
+ const result = (0, import_node_child_process44.spawnSync)("docker", args, {
37453
37619
  encoding: "utf-8",
37454
37620
  cwd: repoPath
37455
37621
  });
@@ -37477,7 +37643,7 @@ function queryRunningComposeServices(repoPath, composeFiles2, overlayPath, servi
37477
37643
  "--services",
37478
37644
  ...services
37479
37645
  ];
37480
- const result = (0, import_node_child_process43.spawnSync)("docker", args, {
37646
+ const result = (0, import_node_child_process44.spawnSync)("docker", args, {
37481
37647
  encoding: "utf-8",
37482
37648
  cwd: repoPath
37483
37649
  });
@@ -37503,7 +37669,7 @@ function runDockerComposeStop(repoPath, composeFiles2, overlayPath, services) {
37503
37669
  fileArgs.push("-f", resolved2);
37504
37670
  }
37505
37671
  const args = ["compose", ...fileArgs, "-f", overlayPath, "stop", ...services];
37506
- const result = (0, import_node_child_process43.spawnSync)("docker", args, {
37672
+ const result = (0, import_node_child_process44.spawnSync)("docker", args, {
37507
37673
  encoding: "utf-8",
37508
37674
  cwd: repoPath
37509
37675
  });
@@ -37529,7 +37695,7 @@ function runDockerComposeLogs(repoPath, composeFiles2, overlayPath, services, ta
37529
37695
  String(tail),
37530
37696
  ...services
37531
37697
  ];
37532
- (0, import_node_child_process43.spawnSync)("docker", args, {
37698
+ (0, import_node_child_process44.spawnSync)("docker", args, {
37533
37699
  stdio: "inherit",
37534
37700
  cwd: repoPath
37535
37701
  });
@@ -37541,7 +37707,7 @@ function queryMappedPort(repoPath, composeFiles2, overlayPath, service, internal
37541
37707
  fileArgs.push("-f", resolved2);
37542
37708
  }
37543
37709
  const args = ["compose", ...fileArgs, "-f", overlayPath, "port", service, String(internalPort)];
37544
- const result = (0, import_node_child_process43.spawnSync)("docker", args, {
37710
+ const result = (0, import_node_child_process44.spawnSync)("docker", args, {
37545
37711
  encoding: "utf-8",
37546
37712
  cwd: repoPath
37547
37713
  });
@@ -37555,15 +37721,15 @@ function queryMappedPort(repoPath, composeFiles2, overlayPath, service, internal
37555
37721
  const port = Number(match[1]);
37556
37722
  return Number.isInteger(port) && port > 0 ? port : void 0;
37557
37723
  }
37558
- var import_node_child_process43, import_node_crypto40, import_node_fs58, import_node_path71, import_yaml12;
37724
+ var import_node_child_process44, import_node_crypto41, import_node_fs59, import_node_path72, import_yaml13;
37559
37725
  var init_docker_run = __esm({
37560
37726
  "src/core/docker-run.ts"() {
37561
37727
  "use strict";
37562
- import_node_child_process43 = require("child_process");
37563
- import_node_crypto40 = require("crypto");
37564
- import_node_fs58 = __toESM(require("fs"));
37565
- import_node_path71 = __toESM(require("path"));
37566
- import_yaml12 = __toESM(require("yaml"));
37728
+ import_node_child_process44 = require("child_process");
37729
+ import_node_crypto41 = require("crypto");
37730
+ import_node_fs59 = __toESM(require("fs"));
37731
+ import_node_path72 = __toESM(require("path"));
37732
+ import_yaml13 = __toESM(require("yaml"));
37567
37733
  init_docker_error_guidance();
37568
37734
  init_paths();
37569
37735
  init_router();
@@ -37652,7 +37818,7 @@ function isProcessRunning(pid) {
37652
37818
  }
37653
37819
  }
37654
37820
  function readProcessTree(rootPid) {
37655
- const result = (0, import_node_child_process44.spawnSync)("ps", ["-ax", "-o", "pid=,ppid="], { encoding: "utf-8" });
37821
+ const result = (0, import_node_child_process45.spawnSync)("ps", ["-ax", "-o", "pid=,ppid="], { encoding: "utf-8" });
37656
37822
  if (result.status !== 0) {
37657
37823
  return [rootPid];
37658
37824
  }
@@ -37763,12 +37929,12 @@ function detectListeningPorts(pids) {
37763
37929
  if (pids.length === 0) {
37764
37930
  return [];
37765
37931
  }
37766
- const result = (0, import_node_child_process44.spawnSync)("lsof", ["-nP", "-iTCP", "-sTCP:LISTEN", "-a", "-p", pids.join(",")], {
37932
+ const result = (0, import_node_child_process45.spawnSync)("lsof", ["-nP", "-iTCP", "-sTCP:LISTEN", "-a", "-p", pids.join(",")], {
37767
37933
  encoding: "utf-8"
37768
37934
  });
37769
37935
  if (result.error && result.error.code === "ENOENT") {
37770
37936
  if (process.platform === "linux") {
37771
- const ssResult = (0, import_node_child_process44.spawnSync)("ss", ["-H", "-lntp", "-p"], { encoding: "utf-8" });
37937
+ const ssResult = (0, import_node_child_process45.spawnSync)("ss", ["-H", "-lntp", "-p"], { encoding: "utf-8" });
37772
37938
  if (ssResult.status === 0 && ssResult.stdout) {
37773
37939
  return parseSsListeningPorts(ssResult.stdout, new Set(pids));
37774
37940
  }
@@ -37827,7 +37993,7 @@ async function runHostApp(repoPath, app, extraEnv = {}, secretManager, env, work
37827
37993
  app.hostRun.command,
37828
37994
  true
37829
37995
  ) : app.hostRun.command;
37830
- const child = (0, import_node_child_process44.spawn)(spawnCommand, {
37996
+ const child = (0, import_node_child_process45.spawn)(spawnCommand, {
37831
37997
  cwd: commandCwd,
37832
37998
  stdio: "inherit",
37833
37999
  shell: true,
@@ -38195,7 +38361,7 @@ async function execWithAppEnv(options) {
38195
38361
  options.command[0],
38196
38362
  true
38197
38363
  );
38198
- child = (0, import_node_child_process44.spawn)(wrapped, {
38364
+ child = (0, import_node_child_process45.spawn)(wrapped, {
38199
38365
  cwd: deps.repoPath,
38200
38366
  stdio: "inherit",
38201
38367
  shell: true,
@@ -38209,7 +38375,7 @@ async function execWithAppEnv(options) {
38209
38375
  false
38210
38376
  );
38211
38377
  const [cmd, ...wrappedArgs] = wrapped;
38212
- child = (0, import_node_child_process44.spawn)(cmd, wrappedArgs, {
38378
+ child = (0, import_node_child_process45.spawn)(cmd, wrappedArgs, {
38213
38379
  cwd: deps.repoPath,
38214
38380
  stdio: "inherit",
38215
38381
  shell: false,
@@ -38217,7 +38383,7 @@ async function execWithAppEnv(options) {
38217
38383
  });
38218
38384
  }
38219
38385
  } else if (options.shell) {
38220
- child = (0, import_node_child_process44.spawn)(options.command[0], {
38386
+ child = (0, import_node_child_process45.spawn)(options.command[0], {
38221
38387
  cwd: deps.repoPath,
38222
38388
  stdio: "inherit",
38223
38389
  shell: true,
@@ -38225,7 +38391,7 @@ async function execWithAppEnv(options) {
38225
38391
  });
38226
38392
  } else {
38227
38393
  const [command, ...args] = options.command;
38228
- child = (0, import_node_child_process44.spawn)(command, args, {
38394
+ child = (0, import_node_child_process45.spawn)(command, args, {
38229
38395
  cwd: deps.repoPath,
38230
38396
  stdio: "inherit",
38231
38397
  shell: false,
@@ -38246,11 +38412,11 @@ async function execWithAppEnv(options) {
38246
38412
  deps.stopDeps();
38247
38413
  }
38248
38414
  }
38249
- var import_node_child_process44, import_node_net6, import_node_process, import_promises, POLL_INTERVAL_MS, DEFAULT_PORT_TIMEOUT_MS, PROCESS_TERMINATION_GRACE_MS;
38415
+ var import_node_child_process45, import_node_net6, import_node_process, import_promises, POLL_INTERVAL_MS, DEFAULT_PORT_TIMEOUT_MS, PROCESS_TERMINATION_GRACE_MS;
38250
38416
  var init_app_run = __esm({
38251
38417
  "src/core/app-run.ts"() {
38252
38418
  "use strict";
38253
- import_node_child_process44 = require("child_process");
38419
+ import_node_child_process45 = require("child_process");
38254
38420
  import_node_net6 = __toESM(require("net"));
38255
38421
  import_node_process = require("process");
38256
38422
  import_promises = require("readline/promises");
@@ -38396,7 +38562,7 @@ function canonicalJson(value) {
38396
38562
  `;
38397
38563
  }
38398
38564
  function sha256Hex(raw) {
38399
- return (0, import_node_crypto41.createHash)("sha256").update(raw).digest("hex");
38565
+ return (0, import_node_crypto42.createHash)("sha256").update(raw).digest("hex");
38400
38566
  }
38401
38567
  function candidateId(candidate) {
38402
38568
  const identity2 = {
@@ -38458,7 +38624,7 @@ function validateCandidate(value, index, action2, legacy) {
38458
38624
  }
38459
38625
  }
38460
38626
  for (const key of ["repo", "path", "commonDir", "gitDir"]) {
38461
- if (!import_node_path72.default.isAbsolute(candidate[key])) {
38627
+ if (!import_node_path73.default.isAbsolute(candidate[key])) {
38462
38628
  throw new Error(`${label2}.${key} must be an absolute path`);
38463
38629
  }
38464
38630
  }
@@ -38564,7 +38730,7 @@ function validateManifest(value) {
38564
38730
  return root;
38565
38731
  }
38566
38732
  function readManifest(file) {
38567
- const raw = import_node_fs59.default.readFileSync(file);
38733
+ const raw = import_node_fs60.default.readFileSync(file);
38568
38734
  let parsed;
38569
38735
  try {
38570
38736
  parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(raw));
@@ -38603,7 +38769,7 @@ function selectManifest(file, expectedSha256, ids2) {
38603
38769
  return canonicalJson(subset);
38604
38770
  }
38605
38771
  function gitLine(cwd, args) {
38606
- const result = (0, import_node_child_process45.spawnSync)("git", ["-C", cwd, ...args], {
38772
+ const result = (0, import_node_child_process46.spawnSync)("git", ["-C", cwd, ...args], {
38607
38773
  encoding: "utf-8",
38608
38774
  env: { ...process.env, GIT_OPTIONAL_LOCKS: "0", LC_ALL: "C" }
38609
38775
  });
@@ -38611,7 +38777,7 @@ function gitLine(cwd, args) {
38611
38777
  return result.status === 0 && !result.error && output2 ? output2 : void 0;
38612
38778
  }
38613
38779
  function readWorktreeIdentity(worktreePath) {
38614
- if (!import_node_fs59.default.existsSync(worktreePath)) return void 0;
38780
+ if (!import_node_fs60.default.existsSync(worktreePath)) return void 0;
38615
38781
  const real = comparableWorkspacePath(worktreePath);
38616
38782
  const commonDir = gitLine(real, ["rev-parse", "--path-format=absolute", "--git-common-dir"]);
38617
38783
  const gitDir = gitLine(real, ["rev-parse", "--path-format=absolute", "--git-dir"]);
@@ -38631,13 +38797,13 @@ function identityMismatch(expected, actual) {
38631
38797
  function unmanagedActivityEpoch(worktreePath, gitDir) {
38632
38798
  let latest;
38633
38799
  for (const file of [
38634
- import_node_path72.default.join(gitDir, "index"),
38635
- import_node_path72.default.join(gitDir, "HEAD"),
38636
- import_node_path72.default.join(gitDir, "logs", "HEAD"),
38800
+ import_node_path73.default.join(gitDir, "index"),
38801
+ import_node_path73.default.join(gitDir, "HEAD"),
38802
+ import_node_path73.default.join(gitDir, "logs", "HEAD"),
38637
38803
  worktreePath
38638
38804
  ]) {
38639
38805
  try {
38640
- const seconds = Math.floor(import_node_fs59.default.statSync(file).mtimeMs / 1e3);
38806
+ const seconds = Math.floor(import_node_fs60.default.statSync(file).mtimeMs / 1e3);
38641
38807
  latest = latest === void 0 ? seconds : Math.max(latest, seconds);
38642
38808
  } catch {
38643
38809
  }
@@ -38691,12 +38857,12 @@ function buildReclaimManifest(report, createdAtEpoch) {
38691
38857
  return canonicalJson(manifest);
38692
38858
  }
38693
38859
  function receiptPath(commonDir, manifestSha256, id5) {
38694
- return import_node_path72.default.join(commonDir, "worktree-reclaim", "receipts", manifestSha256, `${id5}.json`);
38860
+ return import_node_path73.default.join(commonDir, "worktree-reclaim", "receipts", manifestSha256, `${id5}.json`);
38695
38861
  }
38696
38862
  function matchingReceipt(candidate, manifestSha256) {
38697
38863
  try {
38698
38864
  const receipt = JSON.parse(
38699
- import_node_fs59.default.readFileSync(receiptPath(candidate.commonDir, manifestSha256, candidate.id), "utf-8")
38865
+ import_node_fs60.default.readFileSync(receiptPath(candidate.commonDir, manifestSha256, candidate.id), "utf-8")
38700
38866
  );
38701
38867
  return receipt.kind === RECEIPT_KIND && receipt.schemaVersion === SCHEMA_VERSION && receipt.manifestSha256 === manifestSha256 && receipt.candidateId === candidate.id && receipt.path === candidate.path && receipt.head === candidate.head;
38702
38868
  } catch {
@@ -38705,7 +38871,7 @@ function matchingReceipt(candidate, manifestSha256) {
38705
38871
  }
38706
38872
  function writeReceipt(candidate, manifestSha256, steps, nowEpoch) {
38707
38873
  const file = receiptPath(candidate.commonDir, manifestSha256, candidate.id);
38708
- import_node_fs59.default.mkdirSync(import_node_path72.default.dirname(file), { recursive: true });
38874
+ import_node_fs60.default.mkdirSync(import_node_path73.default.dirname(file), { recursive: true });
38709
38875
  writeFileAtomically(
38710
38876
  file,
38711
38877
  canonicalJson({
@@ -38723,7 +38889,7 @@ function writeReceipt(candidate, manifestSha256, steps, nowEpoch) {
38723
38889
  );
38724
38890
  }
38725
38891
  function runVetoCommand(command, worktreePath) {
38726
- const result = (0, import_node_child_process45.spawnSync)(command, [worktreePath], {
38892
+ const result = (0, import_node_child_process46.spawnSync)(command, [worktreePath], {
38727
38893
  stdio: ["ignore", "ignore", "inherit"],
38728
38894
  timeout: VETO_TIMEOUT_MS
38729
38895
  });
@@ -38736,9 +38902,9 @@ function runVetoCommand(command, worktreePath) {
38736
38902
  return result.status === 0 && !result.error && result.signal === null;
38737
38903
  }
38738
38904
  function assertVetoCommand(command) {
38739
- if (!import_node_path72.default.isAbsolute(command)) throw new Error("--veto-command must be an absolute path");
38905
+ if (!import_node_path73.default.isAbsolute(command)) throw new Error("--veto-command must be an absolute path");
38740
38906
  try {
38741
- import_node_fs59.default.accessSync(command, import_node_fs59.default.constants.X_OK);
38907
+ import_node_fs60.default.accessSync(command, import_node_fs60.default.constants.X_OK);
38742
38908
  } catch {
38743
38909
  throw new Error(`--veto-command '${command}' is missing or not executable`);
38744
38910
  }
@@ -38764,7 +38930,7 @@ function preflightManifestApply(options, action2, nowEpoch) {
38764
38930
  return { manifest, manifestSha256, now };
38765
38931
  }
38766
38932
  function deleteBranchIfUnmoved(candidate, steps) {
38767
- const exists2 = (0, import_node_child_process45.spawnSync)(
38933
+ const exists2 = (0, import_node_child_process46.spawnSync)(
38768
38934
  "git",
38769
38935
  ["-C", candidate.repo, "show-ref", "--verify", "--quiet", candidate.branchRef],
38770
38936
  { encoding: "utf-8" }
@@ -38773,7 +38939,7 @@ function deleteBranchIfUnmoved(candidate, steps) {
38773
38939
  if (exists2.status !== 0) {
38774
38940
  throw new Error(`cannot read branch ${candidate.branch}: ${(exists2.stderr ?? "").trim()}`);
38775
38941
  }
38776
- const result = (0, import_node_child_process45.spawnSync)(
38942
+ const result = (0, import_node_child_process46.spawnSync)(
38777
38943
  "git",
38778
38944
  ["-C", candidate.repo, "update-ref", "-d", candidate.branchRef, candidate.head],
38779
38945
  { encoding: "utf-8" }
@@ -38782,7 +38948,7 @@ function deleteBranchIfUnmoved(candidate, steps) {
38782
38948
  return `branch ${candidate.branch} no longer points at the approved HEAD; it was kept for review`;
38783
38949
  }
38784
38950
  steps.push("branch-deleted");
38785
- (0, import_node_child_process45.spawnSync)(
38951
+ (0, import_node_child_process46.spawnSync)(
38786
38952
  "git",
38787
38953
  ["-C", candidate.repo, "config", "--remove-section", `branch.${candidate.branch}`],
38788
38954
  { encoding: "utf-8" }
@@ -38820,7 +38986,7 @@ async function applyReclaimManifest(options, dependencies) {
38820
38986
  outcome.status = "not-attempted";
38821
38987
  continue;
38822
38988
  }
38823
- if (!import_node_fs59.default.existsSync(candidate.path) && matchingReceipt(candidate, manifestSha256)) {
38989
+ if (!import_node_fs60.default.existsSync(candidate.path) && matchingReceipt(candidate, manifestSha256)) {
38824
38990
  outcome.status = "already-done";
38825
38991
  continue;
38826
38992
  }
@@ -38889,14 +39055,14 @@ async function applyReclaimManifest(options, dependencies) {
38889
39055
  }
38890
39056
  return { manifestSha256, dryRun: !options.yes, targets, stoppedEarly };
38891
39057
  }
38892
- var import_node_child_process45, import_node_crypto41, import_node_fs59, import_node_path72, MANIFEST_KIND, LEGACY_MANIFEST_KIND, RECEIPT_KIND, SCHEMA_VERSION, HEX_SHA, HEX_ID, VETO_TIMEOUT_MS, FUTURE_TOLERANCE_SECONDS, ROOT_KEYS, SESSION_ROOT_KEYS, LEGACY_ROOT_KEYS, CANDIDATE_KEYS, SESSION_CANDIDATE_KEYS, OPTIONAL_CANDIDATE_KEYS, ReclaimSkip;
39058
+ var import_node_child_process46, import_node_crypto42, import_node_fs60, import_node_path73, MANIFEST_KIND, LEGACY_MANIFEST_KIND, RECEIPT_KIND, SCHEMA_VERSION, HEX_SHA, HEX_ID, VETO_TIMEOUT_MS, FUTURE_TOLERANCE_SECONDS, ROOT_KEYS, SESSION_ROOT_KEYS, LEGACY_ROOT_KEYS, CANDIDATE_KEYS, SESSION_CANDIDATE_KEYS, OPTIONAL_CANDIDATE_KEYS, ReclaimSkip;
38893
39059
  var init_worktree_reclaim = __esm({
38894
39060
  "src/core/worktree-reclaim.ts"() {
38895
39061
  "use strict";
38896
- import_node_child_process45 = require("child_process");
38897
- import_node_crypto41 = require("crypto");
38898
- import_node_fs59 = __toESM(require("fs"));
38899
- import_node_path72 = __toESM(require("path"));
39062
+ import_node_child_process46 = require("child_process");
39063
+ import_node_crypto42 = require("crypto");
39064
+ import_node_fs60 = __toESM(require("fs"));
39065
+ import_node_path73 = __toESM(require("path"));
38900
39066
  init_atomic_file();
38901
39067
  init_workspace();
38902
39068
  MANIFEST_KIND = "worktree-reclaim-manifest";
@@ -39016,8 +39182,8 @@ function checkoutOf(workingDir) {
39016
39182
  return workingDir.replace(/\/+$/, "").replace(/\/\.devcontainer$/, "");
39017
39183
  }
39018
39184
  function isInsideOrEqual(parent, child) {
39019
- const relative2 = import_node_path73.default.relative(parent, child);
39020
- return relative2 === "" || !relative2.startsWith("..") && !import_node_path73.default.isAbsolute(relative2);
39185
+ const relative2 = import_node_path74.default.relative(parent, child);
39186
+ return relative2 === "" || !relative2.startsWith("..") && !import_node_path74.default.isAbsolute(relative2);
39021
39187
  }
39022
39188
  function isAnonymousVolume(volume) {
39023
39189
  return /^[0-9a-f]{64}$/.test(volume.name) && !volume.labels[PROJECT_LABEL];
@@ -39029,9 +39195,9 @@ function vanishedInsideTrees(checkout, context) {
39029
39195
  (prefix) => checkout !== prefix && isInsideOrEqual(prefix, checkout)
39030
39196
  );
39031
39197
  if (!lexical || context.exists(checkout)) return "out-of-scope";
39032
- let ancestor = import_node_path73.default.dirname(checkout);
39198
+ let ancestor = import_node_path74.default.dirname(checkout);
39033
39199
  while (!context.exists(ancestor)) {
39034
- const parent = import_node_path73.default.dirname(ancestor);
39200
+ const parent = import_node_path74.default.dirname(ancestor);
39035
39201
  if (parent === ancestor) return "out-of-scope";
39036
39202
  ancestor = parent;
39037
39203
  }
@@ -39039,16 +39205,16 @@ function vanishedInsideTrees(checkout, context) {
39039
39205
  if (ancestorReal === void 0 || !isInsideOrEqual(treesReal, ancestorReal)) {
39040
39206
  return "out-of-scope";
39041
39207
  }
39042
- for (let current = ancestor; ; current = import_node_path73.default.dirname(current)) {
39208
+ for (let current = ancestor; ; current = import_node_path74.default.dirname(current)) {
39043
39209
  const real = context.realpath(current);
39044
39210
  if (real === void 0 || real === treesReal || !isInsideOrEqual(treesReal, real)) break;
39045
- if (context.exists(import_node_path73.default.join(current, ".git"))) return "inside-worktree";
39211
+ if (context.exists(import_node_path74.default.join(current, ".git"))) return "inside-worktree";
39046
39212
  }
39047
39213
  return "vanished";
39048
39214
  }
39049
39215
  function resolved(file, context) {
39050
39216
  const real = context.realpath(file);
39051
- return real && real !== import_node_path73.default.resolve(file) ? [import_node_path73.default.resolve(file), real] : [import_node_path73.default.resolve(file)];
39217
+ return real && real !== import_node_path74.default.resolve(file) ? [import_node_path74.default.resolve(file), real] : [import_node_path74.default.resolve(file)];
39052
39218
  }
39053
39219
  function relation(entry, checkout, context) {
39054
39220
  const entries = resolved(entry, context);
@@ -39153,21 +39319,21 @@ function readLeftoverContext(repoPath) {
39153
39319
  resetWorkspaceRuntimeCaches();
39154
39320
  const snapshots = getWorkspaceRegistrySnapshots();
39155
39321
  return {
39156
- treesPath: import_node_path73.default.join(repoPath, "trees"),
39322
+ treesPath: import_node_path74.default.join(repoPath, "trees"),
39157
39323
  worktreePaths: listGitWorktrees(repoPath).map((worktree) => worktree.path),
39158
39324
  ownershipPaths: listWorkspaceOwnership(repoPath).map((record6) => record6.worktreePath),
39159
39325
  providerPaths: listMergedRuntimeWorkspaces().map((workspace) => workspace.source.localFolder),
39160
39326
  registriesReadable: snapshots.unavailable.length === 0,
39161
39327
  realpath: (file) => {
39162
39328
  try {
39163
- return import_node_fs60.default.realpathSync.native(file);
39329
+ return import_node_fs61.default.realpathSync.native(file);
39164
39330
  } catch {
39165
39331
  return void 0;
39166
39332
  }
39167
39333
  },
39168
39334
  exists: (file) => {
39169
39335
  try {
39170
- import_node_fs60.default.lstatSync(file);
39336
+ import_node_fs61.default.lstatSync(file);
39171
39337
  return true;
39172
39338
  } catch (error) {
39173
39339
  return error.code !== "ENOENT";
@@ -39231,7 +39397,7 @@ function validateLeftoverManifest(value) {
39231
39397
  throw new Error(`manifest schemaVersion must be ${SCHEMA_VERSION2}`);
39232
39398
  }
39233
39399
  requireString(root.repo, "manifest repo");
39234
- if (!import_node_path73.default.isAbsolute(root.repo)) throw new Error("manifest repo must be an absolute path");
39400
+ if (!import_node_path74.default.isAbsolute(root.repo)) throw new Error("manifest repo must be an absolute path");
39235
39401
  requireString(root.daemonId, "manifest daemonId");
39236
39402
  if (!Number.isInteger(root.createdAtEpoch) || root.createdAtEpoch < 0) {
39237
39403
  throw new Error("manifest createdAtEpoch must be a non-negative integer");
@@ -39253,7 +39419,7 @@ function validateLeftoverManifest(value) {
39253
39419
  );
39254
39420
  requireString(candidate.project, `${label2}.project`);
39255
39421
  requireString(candidate.checkout, `${label2}.checkout`);
39256
- if (!import_node_path73.default.isAbsolute(candidate.checkout)) {
39422
+ if (!import_node_path74.default.isAbsolute(candidate.checkout)) {
39257
39423
  throw new Error(`${label2}.checkout must be an absolute path`);
39258
39424
  }
39259
39425
  const members = [
@@ -39291,7 +39457,7 @@ function validateLeftoverManifest(value) {
39291
39457
  return root;
39292
39458
  }
39293
39459
  function readLeftoverManifest(file) {
39294
- const raw = import_node_fs60.default.readFileSync(file);
39460
+ const raw = import_node_fs61.default.readFileSync(file);
39295
39461
  let parsed;
39296
39462
  try {
39297
39463
  parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(raw));
@@ -39305,7 +39471,7 @@ function readLeftoverManifest(file) {
39305
39471
  return { raw, manifest };
39306
39472
  }
39307
39473
  function gitCommonDir(repo) {
39308
- const result = (0, import_node_child_process46.spawnSync)(
39474
+ const result = (0, import_node_child_process47.spawnSync)(
39309
39475
  "git",
39310
39476
  ["-C", repo, "rev-parse", "--path-format=absolute", "--git-common-dir"],
39311
39477
  { encoding: "utf-8" }
@@ -39334,7 +39500,7 @@ function currentRemovals(candidate, inventory, context) {
39334
39500
  throw new LeftoverSkip("changed", "the checkout folder exists again");
39335
39501
  }
39336
39502
  const mounted = new Set(inventory.containers.flatMap((container) => container.volumes));
39337
- const ours = (object5) => object5.labels[PROJECT_LABEL] === candidate.project && object5.name.startsWith(`${candidate.project}_`);
39503
+ const ours = (object6) => object6.labels[PROJECT_LABEL] === candidate.project && object6.name.startsWith(`${candidate.project}_`);
39338
39504
  current = {
39339
39505
  project: candidate.project,
39340
39506
  checkout: candidate.checkout,
@@ -39473,11 +39639,11 @@ function applyLeftoverManifest(options, dependencies = {}) {
39473
39639
  return { manifestSha256, dryRun: !options.yes, targets };
39474
39640
  }
39475
39641
  function leftoverReceiptPath(commonDir, manifestSha256, id5) {
39476
- return import_node_path73.default.join(commonDir, "devrouter-leftovers", "receipts", manifestSha256, `${id5}.json`);
39642
+ return import_node_path74.default.join(commonDir, "devrouter-leftovers", "receipts", manifestSha256, `${id5}.json`);
39477
39643
  }
39478
39644
  function writeLeftoverReceipt(commonDir, manifestSha256, candidate, steps, nowEpoch) {
39479
39645
  const file = leftoverReceiptPath(commonDir, manifestSha256, candidate.id);
39480
- import_node_fs60.default.mkdirSync(import_node_path73.default.dirname(file), { recursive: true });
39646
+ import_node_fs61.default.mkdirSync(import_node_path74.default.dirname(file), { recursive: true });
39481
39647
  writeFileAtomically(
39482
39648
  file,
39483
39649
  canonicalJson({
@@ -39493,13 +39659,13 @@ function writeLeftoverReceipt(commonDir, manifestSha256, candidate, steps, nowEp
39493
39659
  })
39494
39660
  );
39495
39661
  }
39496
- var import_node_child_process46, import_node_fs60, import_node_path73, LEFTOVER_MANIFEST_KIND, LEFTOVER_RECEIPT_KIND, SCHEMA_VERSION2, MAX_DOCKER_OBJECTS, FUTURE_TOLERANCE_SECONDS2, PROJECT_LABEL, WORKING_DIR_LABEL, PROVIDER_LABEL, STOPPED_STATES, HEX_ID2, DockerListingBoundError, defaultDockerRunner, LeftoverSkip, defaultWithLocks;
39662
+ var import_node_child_process47, import_node_fs61, import_node_path74, LEFTOVER_MANIFEST_KIND, LEFTOVER_RECEIPT_KIND, SCHEMA_VERSION2, MAX_DOCKER_OBJECTS, FUTURE_TOLERANCE_SECONDS2, PROJECT_LABEL, WORKING_DIR_LABEL, PROVIDER_LABEL, STOPPED_STATES, HEX_ID2, DockerListingBoundError, defaultDockerRunner, LeftoverSkip, defaultWithLocks;
39497
39663
  var init_docker_leftovers = __esm({
39498
39664
  "src/core/docker-leftovers.ts"() {
39499
39665
  "use strict";
39500
- import_node_child_process46 = require("child_process");
39501
- import_node_fs60 = __toESM(require("fs"));
39502
- import_node_path73 = __toESM(require("path"));
39666
+ import_node_child_process47 = require("child_process");
39667
+ import_node_fs61 = __toESM(require("fs"));
39668
+ import_node_path74 = __toESM(require("path"));
39503
39669
  init_atomic_file();
39504
39670
  init_devpod_mutation();
39505
39671
  init_devpod_workspaces();
@@ -39520,7 +39686,7 @@ var init_docker_leftovers = __esm({
39520
39686
  DockerListingBoundError = class extends Error {
39521
39687
  };
39522
39688
  defaultDockerRunner = (args) => {
39523
- const result = (0, import_node_child_process46.spawnSync)("docker", args, {
39689
+ const result = (0, import_node_child_process47.spawnSync)("docker", args, {
39524
39690
  encoding: "utf-8",
39525
39691
  timeout: 6e4,
39526
39692
  maxBuffer: 256 * 1024 * 1024
@@ -39551,13 +39717,13 @@ function measureWorktreeConsumption(worktreePath, options) {
39551
39717
  const startedAt = Date.now();
39552
39718
  let rootStat;
39553
39719
  try {
39554
- rootStat = import_node_fs61.default.lstatSync(worktreePath);
39720
+ rootStat = import_node_fs62.default.lstatSync(worktreePath);
39555
39721
  } catch (error) {
39556
39722
  return { status: "unknown", reason: describeError(error, worktreePath) };
39557
39723
  }
39558
39724
  let rootEntries;
39559
39725
  try {
39560
- rootEntries = import_node_fs61.default.readdirSync(worktreePath, { withFileTypes: true });
39726
+ rootEntries = import_node_fs62.default.readdirSync(worktreePath, { withFileTypes: true });
39561
39727
  } catch (error) {
39562
39728
  return { status: "unknown", reason: describeError(error, worktreePath) };
39563
39729
  }
@@ -39583,10 +39749,10 @@ function measureWorktreeConsumption(worktreePath, options) {
39583
39749
  timedOut = true;
39584
39750
  break;
39585
39751
  }
39586
- const entryPath = import_node_path74.default.join(dirPath, entry.name);
39752
+ const entryPath = import_node_path75.default.join(dirPath, entry.name);
39587
39753
  let entryStat;
39588
39754
  try {
39589
- entryStat = import_node_fs61.default.lstatSync(entryPath);
39755
+ entryStat = import_node_fs62.default.lstatSync(entryPath);
39590
39756
  } catch (error) {
39591
39757
  if (error?.code === "ENOENT") continue;
39592
39758
  unreadableReason = describeIncompleteWalk(error);
@@ -39596,7 +39762,7 @@ function measureWorktreeConsumption(worktreePath, options) {
39596
39762
  if (!entryStat.isDirectory()) continue;
39597
39763
  let childEntries;
39598
39764
  try {
39599
- childEntries = import_node_fs61.default.readdirSync(entryPath, { withFileTypes: true });
39765
+ childEntries = import_node_fs62.default.readdirSync(entryPath, { withFileTypes: true });
39600
39766
  } catch (error) {
39601
39767
  unreadableReason = describeIncompleteWalk(error);
39602
39768
  break;
@@ -39660,12 +39826,12 @@ function describeError(error, worktreePath) {
39660
39826
  }
39661
39827
  return `could not stat worktree path '${worktreePath}': ${error?.message ?? String(error)}`;
39662
39828
  }
39663
- var import_node_fs61, import_node_path74, DEFAULT_DEADLINE_MS, BLOCK_SIZE_BYTES;
39829
+ var import_node_fs62, import_node_path75, DEFAULT_DEADLINE_MS, BLOCK_SIZE_BYTES;
39664
39830
  var init_workspace_consumption = __esm({
39665
39831
  "src/core/workspace-consumption.ts"() {
39666
39832
  "use strict";
39667
- import_node_fs61 = __toESM(require("fs"));
39668
- import_node_path74 = __toESM(require("path"));
39833
+ import_node_fs62 = __toESM(require("fs"));
39834
+ import_node_path75 = __toESM(require("path"));
39669
39835
  init_devpod_environment();
39670
39836
  DEFAULT_DEADLINE_MS = 1e4;
39671
39837
  BLOCK_SIZE_BYTES = 512;
@@ -39751,7 +39917,7 @@ function evaluateWorkspaceActivity(evidence, cutoff) {
39751
39917
  }
39752
39918
  function readGitSnapshot(worktree, commandRunner) {
39753
39919
  const comparablePath = comparableWorkspacePath(worktree.path);
39754
- if (worktree.prunable || !import_node_fs62.default.existsSync(comparablePath)) {
39920
+ if (worktree.prunable || !import_node_fs63.default.existsSync(comparablePath)) {
39755
39921
  return { worktree, checkout: "missing", head: null, committerDate: null };
39756
39922
  }
39757
39923
  const head = gitOutput(comparablePath, ["rev-parse", "--verify", "HEAD"], commandRunner);
@@ -40324,7 +40490,7 @@ function buildWorkspaceCleanupReport(options = {}, dependencies = {}) {
40324
40490
  containers2 = measureContainersFn(worktreePaths);
40325
40491
  } catch (error) {
40326
40492
  const reason2 = `container measurement failed: ${describeCause(error)}`;
40327
- containers2 = new Map(worktreePaths.map((path79) => [path79, unknownContainers(reason2)]));
40493
+ containers2 = new Map(worktreePaths.map((path80) => [path80, unknownContainers(reason2)]));
40328
40494
  }
40329
40495
  }
40330
40496
  const rows2 = records.map((record6) => {
@@ -40383,7 +40549,7 @@ function buildWorkspaceCleanupReport(options = {}, dependencies = {}) {
40383
40549
  dependencies.listForge ?? ((identity2) => listForgeChanges(identity2))
40384
40550
  );
40385
40551
  const sources = (dependencies.readRuntimeSources ?? readRuntimeSources)(repoPath);
40386
- const disposable = import_node_fs62.default.existsSync(import_node_path75.default.join(repoPath, ".devrouter.yml")) ? loadRepoConfig(repoPath).worktrees?.disposable ?? [] : [];
40552
+ const disposable = import_node_fs63.default.existsSync(import_node_path76.default.join(repoPath, ".devrouter.yml")) ? loadRepoConfig(repoPath).worktrees?.disposable ?? [] : [];
40387
40553
  const [primary, ...linked] = worktrees;
40388
40554
  const safetyFor = (worktree, worktreePath, evidence) => toSafety(
40389
40555
  classify({
@@ -40391,7 +40557,7 @@ function buildWorkspaceCleanupReport(options = {}, dependencies = {}) {
40391
40557
  branch: worktree?.branch,
40392
40558
  primary: primary !== void 0 && sameWorkspacePath(primary.path, worktreePath),
40393
40559
  locked: worktree?.locked ?? false,
40394
- missing: !worktree || worktree.prunable || !import_node_fs62.default.existsSync(worktreePath),
40560
+ missing: !worktree || worktree.prunable || !import_node_fs63.default.existsSync(worktreePath),
40395
40561
  runtimeEvidence: evidence,
40396
40562
  forge,
40397
40563
  extraDisposablePatterns: disposable
@@ -40452,7 +40618,7 @@ function readPathEvidence(repoPath, worktreePath, inactiveFor, dependencies) {
40452
40618
  }
40453
40619
  const sources = (dependencies.readRuntimeSources ?? readRuntimeSources)(repoPath);
40454
40620
  const cutoffEpoch = Math.floor(Date.now() / 1e3) - parseInactiveFor(inactiveFor).seconds;
40455
- const gitDir = (0, import_node_child_process47.spawnSync)(
40621
+ const gitDir = (0, import_node_child_process48.spawnSync)(
40456
40622
  "git",
40457
40623
  ["-C", worktreePath, "rev-parse", "--path-format=absolute", "--git-dir"],
40458
40624
  { encoding: "utf-8", env: READ_ONLY_GIT_ENV2 }
@@ -40472,7 +40638,7 @@ function classifyExactPath(repoPath, worktreePath, evidence, forge, extraDisposa
40472
40638
  branch: worktree?.branch,
40473
40639
  primary: worktrees[0] !== void 0 && sameWorkspacePath(worktrees[0].path, worktreePath),
40474
40640
  locked: worktree?.locked ?? false,
40475
- missing: !worktree || worktree.prunable || !import_node_fs62.default.existsSync(worktreePath),
40641
+ missing: !worktree || worktree.prunable || !import_node_fs63.default.existsSync(worktreePath),
40476
40642
  runtimeEvidence: evidence.runtimeEvidence,
40477
40643
  forge,
40478
40644
  extraDisposablePatterns
@@ -40498,7 +40664,7 @@ function prepareReclaimVerdicts(repo, inactiveFor, dependencies = {}) {
40498
40664
  dependencies.listForge ?? ((identity2) => listForgeChanges(identity2))
40499
40665
  );
40500
40666
  const forge = evidence.status === "not-checked" ? { status: "unavailable", reason: "forge not checked" } : evidence;
40501
- const disposable = import_node_fs62.default.existsSync(import_node_path75.default.join(repoPath, ".devrouter.yml")) ? loadRepoConfig(repoPath).worktrees?.disposable ?? [] : [];
40667
+ const disposable = import_node_fs63.default.existsSync(import_node_path76.default.join(repoPath, ".devrouter.yml")) ? loadRepoConfig(repoPath).worktrees?.disposable ?? [] : [];
40502
40668
  const paths = evidenceCache(
40503
40669
  (worktreePath) => readPathEvidence(repoPath, worktreePath, inactiveFor, dependencies)
40504
40670
  );
@@ -40517,7 +40683,7 @@ function prepareReclaimVerdicts(repo, inactiveFor, dependencies = {}) {
40517
40683
  }
40518
40684
  function prepareTrimChecks(repo, inactiveFor, dependencies = {}) {
40519
40685
  const repoPath = resolveRepoPath(repo);
40520
- const configured = import_node_fs62.default.existsSync(import_node_path75.default.join(repoPath, ".devrouter.yml")) ? loadRepoConfig(repoPath).worktrees : void 0;
40686
+ const configured = import_node_fs63.default.existsSync(import_node_path76.default.join(repoPath, ".devrouter.yml")) ? loadRepoConfig(repoPath).worktrees : void 0;
40521
40687
  const patterns = configured?.trim ?? [];
40522
40688
  const paths = evidenceCache(
40523
40689
  (worktreePath) => readPathEvidence(repoPath, worktreePath, inactiveFor, dependencies)
@@ -40542,13 +40708,13 @@ function prepareTrimChecks(repo, inactiveFor, dependencies = {}) {
40542
40708
  isActive: (worktreePath) => paths.last(worktreePath).active
40543
40709
  };
40544
40710
  }
40545
- var import_node_child_process47, import_node_fs62, import_node_path75, READ_ONLY_GIT_ENV2, ACTIVITY_SOURCES, defaultCommandRunner;
40711
+ var import_node_child_process48, import_node_fs63, import_node_path76, READ_ONLY_GIT_ENV2, ACTIVITY_SOURCES, defaultCommandRunner;
40546
40712
  var init_workspace_cleanup = __esm({
40547
40713
  "src/core/workspace-cleanup.ts"() {
40548
40714
  "use strict";
40549
- import_node_child_process47 = require("child_process");
40550
- import_node_fs62 = __toESM(require("fs"));
40551
- import_node_path75 = __toESM(require("path"));
40715
+ import_node_child_process48 = require("child_process");
40716
+ import_node_fs63 = __toESM(require("fs"));
40717
+ import_node_path76 = __toESM(require("path"));
40552
40718
  init_devpod_workspaces();
40553
40719
  init_docker_leftovers();
40554
40720
  init_duration();
@@ -40571,7 +40737,7 @@ var init_workspace_cleanup = __esm({
40571
40737
  ];
40572
40738
  defaultCommandRunner = (command, args, input2) => {
40573
40739
  const environment = command === "git" ? READ_ONLY_GIT_ENV2 : { ...process.env, LC_ALL: "C" };
40574
- const result = (0, import_node_child_process47.spawnSync)(command, args, {
40740
+ const result = (0, import_node_child_process48.spawnSync)(command, args, {
40575
40741
  encoding: "utf-8",
40576
40742
  env: environment,
40577
40743
  ...input2 === void 0 ? {} : { input: input2 }
@@ -40696,7 +40862,7 @@ function journalVetoes(record6) {
40696
40862
  }
40697
40863
  function readTraefikWindow(cutoffMs) {
40698
40864
  const since = String(Math.floor(cutoffMs / 1e3));
40699
- const run = (args, maxBuffer) => (0, import_node_child_process48.spawnSync)("docker", ["logs", "--timestamps", ...args, ROUTER_CONTAINER_NAME], {
40865
+ const run = (args, maxBuffer) => (0, import_node_child_process49.spawnSync)("docker", ["logs", "--timestamps", ...args, ROUTER_CONTAINER_NAME], {
40700
40866
  encoding: "utf-8",
40701
40867
  maxBuffer,
40702
40868
  ...networkDockerOptions()
@@ -40865,13 +41031,13 @@ function setIdlePin(repoPath, pinned) {
40865
41031
  }
40866
41032
  );
40867
41033
  }
40868
- var import_node_child_process48, import_node_fs63, import_node_path76, DEFAULT_IDLE_FOR, ACTIVITY_FILES, IdleStopSkipped, defaultDependencies;
41034
+ var import_node_child_process49, import_node_fs64, import_node_path77, DEFAULT_IDLE_FOR, ACTIVITY_FILES, IdleStopSkipped, defaultDependencies;
40869
41035
  var init_workspace_idle = __esm({
40870
41036
  "src/core/workspace-idle.ts"() {
40871
41037
  "use strict";
40872
- import_node_child_process48 = require("child_process");
40873
- import_node_fs63 = __toESM(require("fs"));
40874
- import_node_path76 = __toESM(require("path"));
41038
+ import_node_child_process49 = require("child_process");
41039
+ import_node_fs64 = __toESM(require("fs"));
41040
+ import_node_path77 = __toESM(require("path"));
40875
41041
  init_devpod_environment();
40876
41042
  init_devpod_workspaces();
40877
41043
  init_duration();
@@ -40892,7 +41058,7 @@ var init_workspace_idle = __esm({
40892
41058
  readJournals: () => listReliabilityOperations(),
40893
41059
  journalModifiedMs: (record6) => {
40894
41060
  try {
40895
- return import_node_fs63.default.statSync(reliabilityOperationPath(record6.identity)).mtimeMs;
41061
+ return import_node_fs64.default.statSync(reliabilityOperationPath(record6.identity)).mtimeMs;
40896
41062
  } catch {
40897
41063
  return void 0;
40898
41064
  }
@@ -40900,7 +41066,7 @@ var init_workspace_idle = __esm({
40900
41066
  registryPaths: () => listMergedRuntimeWorkspaces().map((workspace) => workspace.source.localFolder),
40901
41067
  inspectContainers: () => inspectWorkspaceContainers(),
40902
41068
  statActivity: (containerId) => {
40903
- const result = (0, import_node_child_process48.spawnSync)(
41069
+ const result = (0, import_node_child_process49.spawnSync)(
40904
41070
  "docker",
40905
41071
  ["exec", containerId, "stat", "-c", "%Y", ...ACTIVITY_FILES],
40906
41072
  {
@@ -40915,7 +41081,7 @@ var init_workspace_idle = __esm({
40915
41081
  };
40916
41082
  },
40917
41083
  readTraefik: readTraefikWindow,
40918
- isConfiguredCheckout: (repoPath) => import_node_fs63.default.existsSync(import_node_path76.default.join(repoPath, ".devrouter.yml")),
41084
+ isConfiguredCheckout: (repoPath) => import_node_fs64.default.existsSync(import_node_path77.default.join(repoPath, ".devrouter.yml")),
40919
41085
  gitCommonDir: (repoPath) => {
40920
41086
  try {
40921
41087
  return resolveGitCommonDir(repoPath);
@@ -41006,20 +41172,20 @@ function warnMissingWorkspaceOwnership(repoPath) {
41006
41172
  );
41007
41173
  }
41008
41174
  function defaultWorktreePath(mainRepo, ws) {
41009
- return import_node_path77.default.join(mainRepo, "trees", ws);
41175
+ return import_node_path78.default.join(mainRepo, "trees", ws);
41010
41176
  }
41011
41177
  function assertDefaultWorktreeRootIgnored(mainRepo) {
41012
- const ignored = (0, import_node_child_process49.spawnSync)("git", ["-C", mainRepo, "check-ignore", "-q", "--no-index", "trees/"], {
41178
+ const ignored = (0, import_node_child_process50.spawnSync)("git", ["-C", mainRepo, "check-ignore", "-q", "--no-index", "trees/"], {
41013
41179
  encoding: "utf-8"
41014
41180
  });
41015
41181
  if (ignored.status !== 0) {
41016
41182
  throw new Error(
41017
- `Default worktree root '${import_node_path77.default.join(mainRepo, "trees")}' is not ignored. Add 'trees/' to '${import_node_path77.default.join(mainRepo, ".gitignore")}' or use --path.`
41183
+ `Default worktree root '${import_node_path78.default.join(mainRepo, "trees")}' is not ignored. Add 'trees/' to '${import_node_path78.default.join(mainRepo, ".gitignore")}' or use --path.`
41018
41184
  );
41019
41185
  }
41020
41186
  }
41021
41187
  function legacyDefaultWorktreePath(mainRepo, ws) {
41022
- return import_node_path77.default.join(import_node_path77.default.dirname(mainRepo), `${import_node_path77.default.basename(mainRepo)}-${ws}`);
41188
+ return import_node_path78.default.join(import_node_path78.default.dirname(mainRepo), `${import_node_path78.default.basename(mainRepo)}-${ws}`);
41023
41189
  }
41024
41190
  function teardownFallbackPath(mainRepo, workspace) {
41025
41191
  const candidates = [
@@ -41131,7 +41297,7 @@ function assertFullDownPreflight(mainRepo, target) {
41131
41297
  if (sameWorkspacePath(target.worktreePath, mainRepo)) {
41132
41298
  throw new Error("Refusing to remove the primary Git checkout.");
41133
41299
  }
41134
- if (!target.worktree || target.worktree.prunable || !import_node_fs64.default.existsSync(target.worktreePath)) return;
41300
+ if (!target.worktree || target.worktree.prunable || !import_node_fs65.default.existsSync(target.worktreePath)) return;
41135
41301
  if (target.worktree.locked) {
41136
41302
  throw new Error(
41137
41303
  `Worktree '${target.worktreePath}' is locked; unlock it before workspace down.`
@@ -41163,9 +41329,9 @@ async function workspaceUp(branch, opts = {}) {
41163
41329
  (candidate) => defaultWorktreePath(mainRepo, candidate)
41164
41330
  );
41165
41331
  const generatedPath = candidatePaths.find(
41166
- (candidate) => !import_node_fs64.default.existsSync(candidate) && !worktrees.some((worktree) => sameWorkspacePath(worktree.path, candidate))
41332
+ (candidate) => !import_node_fs65.default.existsSync(candidate) && !worktrees.some((worktree) => sameWorkspacePath(worktree.path, candidate))
41167
41333
  );
41168
- const selectedPath = opts.path ? import_node_path77.default.resolve(opts.path) : existingBranch?.path ?? generatedPath;
41334
+ const selectedPath = opts.path ? import_node_path78.default.resolve(opts.path) : existingBranch?.path ?? generatedPath;
41169
41335
  if (!selectedPath) {
41170
41336
  throw new Error(`Could not allocate a collision-safe worktree path for branch '${branch}'.`);
41171
41337
  }
@@ -41174,7 +41340,7 @@ async function workspaceUp(branch, opts = {}) {
41174
41340
  `Branch '${branch}' already uses worktree '${existingBranch.path}', not '${selectedPath}'.`
41175
41341
  );
41176
41342
  }
41177
- if (import_node_fs64.default.existsSync(selectedPath)) {
41343
+ if (import_node_fs65.default.existsSync(selectedPath)) {
41178
41344
  const registered = worktrees.find(
41179
41345
  (worktree) => sameWorkspacePath(worktree.path, selectedPath)
41180
41346
  );
@@ -41193,11 +41359,11 @@ async function workspaceUp(branch, opts = {}) {
41193
41359
  return selectedPath;
41194
41360
  }
41195
41361
  if (!opts.path) assertDefaultWorktreeRootIgnored(mainRepo);
41196
- const add2 = (0, import_node_child_process49.spawnSync)("git", ["-C", mainRepo, "worktree", "add", selectedPath, branch], {
41362
+ const add2 = (0, import_node_child_process50.spawnSync)("git", ["-C", mainRepo, "worktree", "add", selectedPath, branch], {
41197
41363
  encoding: "utf-8"
41198
41364
  });
41199
41365
  if (add2.status !== 0) {
41200
- const addNew = (0, import_node_child_process49.spawnSync)(
41366
+ const addNew = (0, import_node_child_process50.spawnSync)(
41201
41367
  "git",
41202
41368
  ["-C", mainRepo, "worktree", "add", "-b", branch, selectedPath],
41203
41369
  { encoding: "utf-8" }
@@ -41337,8 +41503,8 @@ async function runWorkspaceLifecycle(action2, target, opts = {}) {
41337
41503
  }
41338
41504
  const result = await mutateWorkspaceRuntime("delete", resolved2, worktrees, opts.quiet);
41339
41505
  if (removeWorktree) {
41340
- if (resolved2.worktree && !resolved2.worktree.prunable && import_node_fs64.default.existsSync(resolved2.worktreePath)) {
41341
- const rm = (0, import_node_child_process49.spawnSync)("git", ["-C", mainRepo, "worktree", "remove", resolved2.worktreePath], {
41506
+ if (resolved2.worktree && !resolved2.worktree.prunable && import_node_fs65.default.existsSync(resolved2.worktreePath)) {
41507
+ const rm = (0, import_node_child_process50.spawnSync)("git", ["-C", mainRepo, "worktree", "remove", resolved2.worktreePath], {
41342
41508
  encoding: "utf-8"
41343
41509
  });
41344
41510
  if (rm.status !== 0) {
@@ -41356,7 +41522,7 @@ async function runWorkspaceLifecycle(action2, target, opts = {}) {
41356
41522
  }
41357
41523
  return result;
41358
41524
  };
41359
- return resolved2.worktree && !resolved2.worktree.prunable && import_node_fs64.default.existsSync(resolved2.worktreePath) ? withLifecycleOperationLock(resolved2.worktreePath, operation) : operation();
41525
+ return resolved2.worktree && !resolved2.worktree.prunable && import_node_fs65.default.existsSync(resolved2.worktreePath) ? withLifecycleOperationLock(resolved2.worktreePath, operation) : operation();
41360
41526
  }
41361
41527
  async function reclaimWorktreeExactPath(mainRepo, worktreePath, verify, afterRemove, steps) {
41362
41528
  await withLifecycleOperationLock(worktreePath, async () => {
@@ -41382,7 +41548,7 @@ async function reclaimWorktreeExactPath(mainRepo, worktreePath, verify, afterRem
41382
41548
  );
41383
41549
  steps.push("runtime-deleted");
41384
41550
  }
41385
- const rm = (0, import_node_child_process49.spawnSync)("git", ["-C", mainRepo, "worktree", "remove", worktreePath], {
41551
+ const rm = (0, import_node_child_process50.spawnSync)("git", ["-C", mainRepo, "worktree", "remove", worktreePath], {
41386
41552
  encoding: "utf-8"
41387
41553
  });
41388
41554
  if (rm.status !== 0) {
@@ -41403,13 +41569,13 @@ async function workspaceStop(target, opts = {}) {
41403
41569
  async function workspaceDown(target, opts = {}) {
41404
41570
  return runWorkspaceLifecycle("down", target, opts);
41405
41571
  }
41406
- var import_node_child_process49, import_node_fs64, import_node_path77, WORKSPACE_ALLOCATION_LOCK_WAIT_MS;
41572
+ var import_node_child_process50, import_node_fs65, import_node_path78, WORKSPACE_ALLOCATION_LOCK_WAIT_MS;
41407
41573
  var init_workspace_lifecycle = __esm({
41408
41574
  "src/core/workspace-lifecycle.ts"() {
41409
41575
  "use strict";
41410
- import_node_child_process49 = require("child_process");
41411
- import_node_fs64 = __toESM(require("fs"));
41412
- import_node_path77 = __toESM(require("path"));
41576
+ import_node_child_process50 = require("child_process");
41577
+ import_node_fs65 = __toESM(require("fs"));
41578
+ import_node_path78 = __toESM(require("path"));
41413
41579
  init_devpod_mutation();
41414
41580
  init_devpod_workspaces();
41415
41581
  init_devsy_mutation();
@@ -41428,7 +41594,7 @@ var init_workspace_lifecycle = __esm({
41428
41594
 
41429
41595
  // src/core/worktree-trim.ts
41430
41596
  function listIgnoredPaths(worktreePath) {
41431
- const result = (0, import_node_child_process50.spawnSync)(
41597
+ const result = (0, import_node_child_process51.spawnSync)(
41432
41598
  "git",
41433
41599
  [
41434
41600
  "-C",
@@ -41512,19 +41678,19 @@ function buildTrimManifest(report, patterns, createdAtEpoch) {
41512
41678
  return canonicalJson(manifest);
41513
41679
  }
41514
41680
  function isInside(parent, child) {
41515
- const relative2 = import_node_path78.default.relative(parent, child);
41516
- return relative2 !== "" && !relative2.startsWith("..") && !import_node_path78.default.isAbsolute(relative2);
41681
+ const relative2 = import_node_path79.default.relative(parent, child);
41682
+ return relative2 !== "" && !relative2.startsWith("..") && !import_node_path79.default.isAbsolute(relative2);
41517
41683
  }
41518
41684
  function exists(file) {
41519
41685
  try {
41520
- import_node_fs65.default.lstatSync(file);
41686
+ import_node_fs66.default.lstatSync(file);
41521
41687
  return true;
41522
41688
  } catch {
41523
41689
  return false;
41524
41690
  }
41525
41691
  }
41526
41692
  function hasTrackedFiles(worktreePath, relative2) {
41527
- const result = (0, import_node_child_process50.spawnSync)(
41693
+ const result = (0, import_node_child_process51.spawnSync)(
41528
41694
  "git",
41529
41695
  ["-C", worktreePath, "--literal-pathspecs", "ls-files", "-z", "--", relative2],
41530
41696
  { encoding: "utf-8", env: { ...process.env, GIT_OPTIONAL_LOCKS: "0" } }
@@ -41532,10 +41698,10 @@ function hasTrackedFiles(worktreePath, relative2) {
41532
41698
  return result.status !== 0 || Boolean(result.error) || result.stdout.length > 0;
41533
41699
  }
41534
41700
  function trimOnePath(candidate, relative2, patterns, steps) {
41535
- const target = import_node_path78.default.join(candidate.path, relative2);
41701
+ const target = import_node_path79.default.join(candidate.path, relative2);
41536
41702
  let stat;
41537
41703
  try {
41538
- stat = import_node_fs65.default.lstatSync(target);
41704
+ stat = import_node_fs66.default.lstatSync(target);
41539
41705
  } catch (error) {
41540
41706
  if (error.code === "ENOENT") {
41541
41707
  steps.push(`gone:${relative2}`);
@@ -41547,7 +41713,7 @@ function trimOnePath(candidate, relative2, patterns, steps) {
41547
41713
  steps.push(`skipped-symlink:${relative2}`);
41548
41714
  return;
41549
41715
  }
41550
- if (!isInside(import_node_fs65.default.realpathSync.native(candidate.path), import_node_fs65.default.realpathSync.native(target))) {
41716
+ if (!isInside(import_node_fs66.default.realpathSync.native(candidate.path), import_node_fs66.default.realpathSync.native(target))) {
41551
41717
  steps.push(`skipped-outside:${relative2}`);
41552
41718
  return;
41553
41719
  }
@@ -41560,7 +41726,7 @@ function trimOnePath(candidate, relative2, patterns, steps) {
41560
41726
  return;
41561
41727
  }
41562
41728
  try {
41563
- import_node_fs65.default.rmSync(target, { recursive: true, force: true });
41729
+ import_node_fs66.default.rmSync(target, { recursive: true, force: true });
41564
41730
  } catch (error) {
41565
41731
  steps.push(`partial:${relative2}`);
41566
41732
  throw error;
@@ -41600,7 +41766,7 @@ async function applyTrimManifest(options, dependencies) {
41600
41766
  continue;
41601
41767
  }
41602
41768
  const listed = candidate.paths ?? [];
41603
- if (listed.every((relative2) => !exists(import_node_path78.default.join(candidate.path, relative2))) && matchingReceipt(candidate, manifestSha256)) {
41769
+ if (listed.every((relative2) => !exists(import_node_path79.default.join(candidate.path, relative2))) && matchingReceipt(candidate, manifestSha256)) {
41604
41770
  outcome.status = "already-done";
41605
41771
  continue;
41606
41772
  }
@@ -41624,7 +41790,7 @@ async function applyTrimManifest(options, dependencies) {
41624
41790
  if (!options.yes) {
41625
41791
  verify();
41626
41792
  outcome.status = "would-trim";
41627
- const present = listed.filter((relative2) => exists(import_node_path78.default.join(candidate.path, relative2)));
41793
+ const present = listed.filter((relative2) => exists(import_node_path79.default.join(candidate.path, relative2)));
41628
41794
  outcome.reason = present.length ? `would remove: ${present.join(", ")}` : "nothing present";
41629
41795
  continue;
41630
41796
  }
@@ -41661,13 +41827,13 @@ async function applyTrimManifest(options, dependencies) {
41661
41827
  }
41662
41828
  return { manifestSha256, dryRun: !options.yes, targets, stoppedEarly };
41663
41829
  }
41664
- var import_node_child_process50, import_node_fs65, import_node_path78;
41830
+ var import_node_child_process51, import_node_fs66, import_node_path79;
41665
41831
  var init_worktree_trim = __esm({
41666
41832
  "src/core/worktree-trim.ts"() {
41667
41833
  "use strict";
41668
- import_node_child_process50 = require("child_process");
41669
- import_node_fs65 = __toESM(require("fs"));
41670
- import_node_path78 = __toESM(require("path"));
41834
+ import_node_child_process51 = require("child_process");
41835
+ import_node_fs66 = __toESM(require("fs"));
41836
+ import_node_path79 = __toESM(require("path"));
41671
41837
  init_reliability_lifecycle();
41672
41838
  init_worktree_reclaim();
41673
41839
  init_worktree_safety();
@@ -41735,7 +41901,7 @@ function runWorkspaceLsCommand(options) {
41735
41901
  }
41736
41902
  function writeManifestOutput(manifest, output2) {
41737
41903
  if (output2) {
41738
- import_node_fs66.default.writeFileSync(output2, manifest, { flag: "wx" });
41904
+ import_node_fs67.default.writeFileSync(output2, manifest, { flag: "wx" });
41739
41905
  process.stdout.write(`${sha256Hex(manifest)} ${output2}
41740
41906
  `);
41741
41907
  } else {
@@ -41765,7 +41931,7 @@ function runWorkspaceCleanupCommand(options) {
41765
41931
  throw new Error("--manifest requires --all-worktrees and --check-merged.");
41766
41932
  }
41767
41933
  const createdAtEpoch = Math.floor(Date.now() / 1e3);
41768
- const worktrees = import_node_fs66.default.existsSync(import_node_path79.default.join(repoPath, ".devrouter.yml")) ? loadRepoConfig(repoPath).worktrees : void 0;
41934
+ const worktrees = import_node_fs67.default.existsSync(import_node_path80.default.join(repoPath, ".devrouter.yml")) ? loadRepoConfig(repoPath).worktrees : void 0;
41769
41935
  const manifest = action2 === "trim" ? buildTrimManifest(
41770
41936
  report,
41771
41937
  { trim: worktrees?.trim ?? [], disposable: worktrees?.disposable },
@@ -41803,7 +41969,7 @@ async function runWorkspaceIdleCommand(options) {
41803
41969
  if (options.stop) {
41804
41970
  const paths = idleSweepPaths();
41805
41971
  if (!process.stderr.isTTY) muteLifecycleProgressHeartbeat();
41806
- import_node_fs66.default.mkdirSync(paths.directory, { recursive: true, mode: 448 });
41972
+ import_node_fs67.default.mkdirSync(paths.directory, { recursive: true, mode: 448 });
41807
41973
  const result = await withFileLock(paths.lock, { activity: "idle sweep" }, async () => {
41808
41974
  try {
41809
41975
  const swept = await stopIdleEnvironments(
@@ -41905,7 +42071,7 @@ async function runWorkspaceJournalSettleCommand(options) {
41905
42071
  function runWorkspaceManifestHashCommand(file) {
41906
42072
  let leftover = false;
41907
42073
  try {
41908
- leftover = isLeftoverManifest(JSON.parse(import_node_fs66.default.readFileSync(file, "utf-8")));
42074
+ leftover = isLeftoverManifest(JSON.parse(import_node_fs67.default.readFileSync(file, "utf-8")));
41909
42075
  } catch {
41910
42076
  }
41911
42077
  const { raw } = leftover ? readLeftoverManifest(file) : readManifest(file);
@@ -42002,12 +42168,12 @@ async function runWorkspaceTrimCommand(options) {
42002
42168
  process.exitCode = 1;
42003
42169
  }
42004
42170
  }
42005
- var import_node_fs66, import_node_path79;
42171
+ var import_node_fs67, import_node_path80;
42006
42172
  var init_workspace2 = __esm({
42007
42173
  "src/commands/workspace.ts"() {
42008
42174
  "use strict";
42009
- import_node_fs66 = __toESM(require("fs"));
42010
- import_node_path79 = __toESM(require("path"));
42175
+ import_node_fs67 = __toESM(require("fs"));
42176
+ import_node_path80 = __toESM(require("path"));
42011
42177
  init_docker_leftovers();
42012
42178
  init_file_lock();
42013
42179
  init_idle_sweep();
@@ -42065,7 +42231,7 @@ var init_version = __esm({
42065
42231
 
42066
42232
  // src/cli.ts
42067
42233
  var import_commander = require("commander");
42068
- var CLI_VERSION = true ? "0.6.1" : "0.0.0-dev";
42234
+ var CLI_VERSION = true ? "0.6.3" : "0.0.0-dev";
42069
42235
  var VERSION_FLAGS = /* @__PURE__ */ new Set(["-V", "--version"]);
42070
42236
  function withErrorHandling(action2) {
42071
42237
  return async (...args) => {
@@ -42159,7 +42325,7 @@ program.command("up").description("Ensure devnet and start shared Traefik (reser
42159
42325
  await runUpCommand2();
42160
42326
  })
42161
42327
  );
42162
- program.command("ensure").description("Start and prove a primary or linked checkout's environment and routes").argument("[path]", "Git checkout path (defaults to current directory)").option("--profile <name>", "Start only this profile's apps/dependencies from .devrouter.yml").option("--variant <name>", "Select a declared managed runtime variant").option("--repair", "Repair a degraded managed runtime from its recorded profile").option("--open", "Open HTTP routes after readiness succeeds").option("--json", "Output JSON").action(
42328
+ program.command("ensure").description("Start and prove a primary or linked checkout's environment and routes").argument("[path]", "Git checkout path (defaults to current directory)").option("--profile <name>", "Start only this profile's apps/dependencies from .devrouter.yml").option("--variant <name>", "Select a declared managed runtime variant").option("--repair", "Repair a degraded managed runtime from its recorded profile").option("--initialize-new", "Initialize one fresh disposable managed environment explicitly").option("--open", "Open HTTP routes after readiness succeeds").option("--json", "Output JSON").action(
42163
42329
  withErrorHandling(async (repoPath, _options, command) => {
42164
42330
  const options = command.opts();
42165
42331
  const { runEnsureCommand: runEnsureCommand2 } = await Promise.resolve().then(() => (init_ensure(), ensure_exports));
@@ -42168,6 +42334,7 @@ program.command("ensure").description("Start and prove a primary or linked check
42168
42334
  profile: options.profile,
42169
42335
  variant: options.variant,
42170
42336
  repair: options.repair,
42337
+ initializeNew: options.initializeNew,
42171
42338
  open: Boolean(options.open),
42172
42339
  json: Boolean(options.json)
42173
42340
  });
@@ -42432,7 +42599,7 @@ workspaceCommand2.command("up").description(
42432
42599
  );
42433
42600
  workspaceCommand2.command("ensure").description(
42434
42601
  "Start and prove a primary or linked checkout's workspace runtime, upstreams, and routes"
42435
- ).argument("[path]", "Git checkout path (defaults to current directory)").option("--profile <name>", "Start only this profile's apps/dependencies from .devrouter.yml").option("--variant <name>", "Select a declared managed runtime variant").option("--repair", "Repair a degraded managed runtime from its recorded profile").option("--open", "Open HTTP routes after readiness succeeds").option("--json", "Output JSON").action(
42602
+ ).argument("[path]", "Git checkout path (defaults to current directory)").option("--profile <name>", "Start only this profile's apps/dependencies from .devrouter.yml").option("--variant <name>", "Select a declared managed runtime variant").option("--repair", "Repair a degraded managed runtime from its recorded profile").option("--initialize-new", "Initialize one fresh disposable managed environment explicitly").option("--open", "Open HTTP routes after readiness succeeds").option("--json", "Output JSON").action(
42436
42603
  withErrorHandling(
42437
42604
  async (worktreePath, _options, command) => {
42438
42605
  const options = command.opts();
@@ -42442,6 +42609,7 @@ workspaceCommand2.command("ensure").description(
42442
42609
  profile: options.profile,
42443
42610
  variant: options.variant,
42444
42611
  repair: options.repair,
42612
+ initializeNew: options.initializeNew,
42445
42613
  open: Boolean(options.open),
42446
42614
  json: Boolean(options.json)
42447
42615
  });
@@ -42588,10 +42756,10 @@ workspaceCommand2.command("down").description("Delete a workspace's runtime and
42588
42756
  workspaceCommand2.command("journal").description("Inspect and recover workspace lifecycle journals").command("settle").description(
42589
42757
  "Settle an interrupted lifecycle operation as unobservable so ensure and stop can proceed"
42590
42758
  ).argument("[path]", "Checkout path (defaults to current directory)").option("--json", "Output JSON").action(
42591
- withErrorHandling(async (path79, _options, command) => {
42759
+ withErrorHandling(async (path80, _options, command) => {
42592
42760
  const options = command.opts();
42593
42761
  const { runWorkspaceJournalSettleCommand: runWorkspaceJournalSettleCommand2 } = await Promise.resolve().then(() => (init_workspace2(), workspace_exports));
42594
- await runWorkspaceJournalSettleCommand2({ ...path79 ? { path: path79 } : {}, ...options });
42762
+ await runWorkspaceJournalSettleCommand2({ ...path80 ? { path: path80 } : {}, ...options });
42595
42763
  })
42596
42764
  );
42597
42765
  function parseVersionRequest(argv) {