@devrouter/cli 0.6.3 → 0.6.4

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
@@ -843,6 +843,52 @@ function runManagedStopDocker(args, endpoint, timeoutMs = MANAGED_STOP_DOCKER_TI
843
843
  }
844
844
  return result.stdout;
845
845
  }
846
+ function inspectAppReplacementInventory(endpoint) {
847
+ const list2 = () => {
848
+ const ids3 = parseDockerLines(
849
+ runManagedStopDocker(["ps", "--all", "--quiet", "--no-trunc"], endpoint)
850
+ ).sort();
851
+ if (ids3.length > 1024) throw new Error("App replacement daemon inventory exceeds its bound.");
852
+ ids3.forEach(assertFullContainerId);
853
+ assertUniqueContainerIds(ids3);
854
+ return ids3;
855
+ };
856
+ const ids2 = list2();
857
+ const template = '{"id":{{json .Id}},"name":{{json .Name}},"image":{{json .Image}},"namespaces":{"NetworkMode":{{json .HostConfig.NetworkMode}},"PidMode":{{json .HostConfig.PidMode}},"IpcMode":{{json .HostConfig.IpcMode}}},"mounts":{{json .Mounts}}}';
858
+ const containers2 = ids2.length ? parseDockerLines(
859
+ runManagedStopDocker(["inspect", "--format", template, ...ids2], endpoint)
860
+ ).map((line) => {
861
+ const c = JSON.parse(line);
862
+ if (!isRecord(c)) throw new Error("Invalid replacement container inventory.");
863
+ assertFullContainerId(c.id);
864
+ if (typeof c.name !== "string" || !/^\/[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(c.name) || typeof c.image !== "string" || !/^sha256:[a-f0-9]{64}$/.test(c.image) || !isRecord(c.namespaces) || !["NetworkMode", "PidMode", "IpcMode"].every(
865
+ (key) => typeof c.namespaces[key] === "string"
866
+ ) || !Array.isArray(c.mounts) || c.mounts.some(
867
+ (m) => !isRecord(m) || typeof m.Type !== "string" || typeof m.Source !== "string" || typeof m.Destination !== "string" || m.Type === "volume" && (typeof m.Name !== "string" || !m.Name)
868
+ ))
869
+ throw new Error("Incomplete app replacement inventory.");
870
+ return c;
871
+ }) : [];
872
+ if (JSON.stringify(ids2) !== JSON.stringify(containers2.map((c) => c.id).sort()) || JSON.stringify(ids2) !== JSON.stringify(list2()) || new Set(containers2.map((c) => c.name)).size !== containers2.length)
873
+ throw new Error("App replacement daemon inventory changed during inspection.");
874
+ return containers2;
875
+ }
876
+ function assertNoAppNamespaceConsumers(containers2, primaryId) {
877
+ const primary = containers2.find((c) => c.id === primaryId);
878
+ if (!primary) throw new Error("App replacement lost the exact primary container.");
879
+ for (const c of containers2)
880
+ for (const mode of Object.values(c.namespaces)) {
881
+ if (!mode.startsWith("container:")) continue;
882
+ const reference = mode.slice("container:".length).replace(/^\//, "");
883
+ const matches = containers2.filter(
884
+ (candidate) => candidate.name.slice(1) === reference || /^[a-f0-9]+$/.test(reference) && candidate.id.startsWith(reference)
885
+ );
886
+ if (matches.length !== 1 || c.id === primaryId || matches[0].id === primaryId)
887
+ throw new Error(
888
+ "App replacement refuses unresolved or primary-dependent network, PID or IPC namespaces."
889
+ );
890
+ }
891
+ }
846
892
  function listManagedStopContainerIds(composeProject, endpoint) {
847
893
  const stdout = runManagedStopDocker(
848
894
  [
@@ -4898,7 +4944,7 @@ function loadRepoConfig(repoPath, read2 = (file) => import_node_fs8.default.read
4898
4944
  const config = parseRepoConfigSource(raw, configPath);
4899
4945
  const requiredVersion = config.devrouter?.version;
4900
4946
  if (requiredVersion && !hasWarnedVersionMismatch) {
4901
- const cliVersion = true ? "0.6.3" : "0.0.0-dev";
4947
+ const cliVersion = true ? "0.6.4" : "0.0.0-dev";
4902
4948
  if (cliVersion !== "0.0.0-dev" && compareSemver(requiredVersion, cliVersion) > 0) {
4903
4949
  hasWarnedVersionMismatch = true;
4904
4950
  process.stderr.write(
@@ -7170,8 +7216,57 @@ var init_reliability_contract = __esm({
7170
7216
  });
7171
7217
 
7172
7218
  // src/core/reliability-operation-store.ts
7219
+ function appReplacementPath(identity2) {
7220
+ const directory = import_node_path16.default.join(DEVROUTER_HOME, "replacement-attempts");
7221
+ if (import_node_fs13.default.existsSync(directory)) {
7222
+ const stat = import_node_fs13.default.lstatSync(directory);
7223
+ if (!stat.isDirectory() || stat.isSymbolicLink() || stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
7224
+ throw new Error("App replacement directory must be private and not symlinked.");
7225
+ }
7226
+ return import_node_path16.default.join(directory, `${identityKey(identity2)}.json`);
7227
+ }
7228
+ function readAppReplacementAttempt(identity2) {
7229
+ const value = readBoundedPrivateJson(appReplacementPath(identity2), "App replacement attempt");
7230
+ if (value === void 0) return void 0;
7231
+ return validateAppReplacementAttempt(value, identity2);
7232
+ }
7233
+ function validateAppReplacementAttempt(value, identity2) {
7234
+ const attempt = value;
7235
+ keys(attempt, [
7236
+ "version",
7237
+ "identity",
7238
+ "operationId",
7239
+ "workerId",
7240
+ "fence",
7241
+ "stoppedFence",
7242
+ "oldPrimaryId",
7243
+ "preflightSha256",
7244
+ "stage",
7245
+ "newPrimaryId"
7246
+ ]);
7247
+ exactKeys(
7248
+ attempt.identity,
7249
+ ["repoPath", "workspace", "provider"],
7250
+ "Invalid replacement identity."
7251
+ );
7252
+ identityKey(attempt.identity);
7253
+ for (const fence of [attempt.fence, attempt.stoppedFence]) {
7254
+ exactKeys(
7255
+ fence,
7256
+ ["environmentId", "intentRevision", "runtimeGeneration", "controllerEpoch"],
7257
+ "Invalid replacement fence."
7258
+ );
7259
+ if (!isReliabilityId(fence.environmentId) || ![fence.intentRevision, fence.runtimeGeneration, fence.controllerEpoch].every(
7260
+ isReliabilityCounter
7261
+ ))
7262
+ throw new Error("Invalid replacement fence.");
7263
+ }
7264
+ if (attempt.version !== 1 || !(0, import_node_util3.isDeepStrictEqual)(attempt.identity, identity2) || !isReliabilityId(attempt.operationId) || !isReliabilityId(attempt.workerId) || !/^[a-f0-9]{64}$/.test(attempt.oldPrimaryId) || !/^[a-f0-9]{64}$/.test(attempt.preflightSha256) || !["claimed", "dispatched", "replaced", "ready"].includes(attempt.stage) || attempt.fence.environmentId !== attempt.stoppedFence.environmentId || attempt.fence.runtimeGeneration !== attempt.stoppedFence.runtimeGeneration || attempt.fence.controllerEpoch !== attempt.stoppedFence.controllerEpoch || attempt.fence.intentRevision !== attempt.stoppedFence.intentRevision + 1 || attempt.newPrimaryId !== void 0 && (!/^[a-f0-9]{64}$/.test(attempt.newPrimaryId) || attempt.newPrimaryId === attempt.oldPrimaryId) || ["claimed", "dispatched"].includes(attempt.stage) && attempt.newPrimaryId !== void 0 || ["replaced", "ready"].includes(attempt.stage) && !attempt.newPrimaryId)
7265
+ throw new Error("Invalid app replacement attempt.");
7266
+ return attempt;
7267
+ }
7173
7268
  function installedCliVersion() {
7174
- return true ? "0.6.3" : "0.0.0-dev";
7269
+ return true ? "0.6.4" : "0.0.0-dev";
7175
7270
  }
7176
7271
  function compareCliVersions(left, right) {
7177
7272
  const parts = (value) => value.split(".").map((part) => Number.parseInt(part, 10) || 0);
@@ -7372,8 +7467,10 @@ function validate2(record6, identity2) {
7372
7467
  }
7373
7468
  }
7374
7469
  if (record6.writtenByVersion !== void 0 && compareCliVersions(record6.writtenByVersion, installedCliVersion()) > 0) {
7375
- throw new Error(
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.`
7470
+ throw new ReliabilityJournalError(
7471
+ "journal-writer-newer",
7472
+ journalEntryLocation(import_node_path16.default.basename(reliabilityOperationPath(identity2))),
7473
+ "Reliability record was written by a newer devrouter CLI; use a matching or newer CLI before retrying lifecycle commands. Preserve the journal."
7377
7474
  );
7378
7475
  }
7379
7476
  keys(record6.identity, ["repoPath", "workspace", "provider"]);
@@ -11722,6 +11819,11 @@ async function prepareDevsyAgent(options = {}) {
11722
11819
  return { ...preparedFromInspection(published), changed: true, transport };
11723
11820
  });
11724
11821
  }
11822
+ function requireReadyDevsyAgent(options = {}) {
11823
+ const inspection = inspectDevsyAgent(options);
11824
+ if (!isReadyInspection(inspection)) throw new DevsyAgentReadinessError(inspection);
11825
+ return preparedFromInspection(inspection);
11826
+ }
11725
11827
  var import_node_child_process13, import_node_crypto13, import_node_fs21, import_node_path28, PINNED_DEVSY_AGENT_VERSION, SUPPORTED_DEVSY_MIN_VERSION, SUPPORTED_DEVSY_MAX_VERSION, SUPPORTED_DEVSY_RANGE, DEVSY_AGENT_SETUP_COMMAND, DEVSY_RELEASE_TAG_PREFIX, DEVSY_RELEASE_METADATA_BASE_URL, DEVSY_AGENT_ASSETS, DevsyAgentReadinessError, DEVSY_AGENT_CACHE_LOCK, DEVSY_AGENT_LOCK_WAIT_MS, DEVSY_AGENT_MANIFEST_FILE, DEVSY_AGENT_ASSET_NAMES, GITHUB_CLI_STDERR_TAIL_BYTES;
11726
11828
  var init_devsy_agent = __esm({
11727
11829
  "src/core/devsy-agent.ts"() {
@@ -11818,6 +11920,412 @@ var init_managed_compose_population = __esm({
11818
11920
  }
11819
11921
  });
11820
11922
 
11923
+ // src/core/managed-host-ports.ts
11924
+ function unavailableEntry(declaration, reason2) {
11925
+ return { ...declaration, status: "unavailable", reason: reason2 };
11926
+ }
11927
+ function livePublishedBindings(containerId, target) {
11928
+ const result = (0, import_node_child_process14.spawnSync)("docker", ["port", containerId, `${target}/tcp`], {
11929
+ encoding: "utf-8",
11930
+ timeout: PORT_LOOKUP_TIMEOUT_MS,
11931
+ maxBuffer: PORT_LOOKUP_MAX_BUFFER,
11932
+ ...networkDockerOptions()
11933
+ });
11934
+ if (result.error || result.status === null) return void 0;
11935
+ if (result.status !== 0) return "";
11936
+ return typeof result.stdout === "string" ? result.stdout : void 0;
11937
+ }
11938
+ function parseLoopbackBinding(output2) {
11939
+ const lines = output2.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
11940
+ if (lines.length === 0) return { reason: "not-published" };
11941
+ if (lines.length > 1) return { reason: "ambiguous" };
11942
+ const match = LOOPBACK_BINDING.exec(lines[0]);
11943
+ if (!match) return { reason: "not-published" };
11944
+ const port = Number(match[1]);
11945
+ if (!Number.isInteger(port) || port < 1 || port > 65535) return { reason: "not-published" };
11946
+ return { endpoint: { host: "127.0.0.1", port } };
11947
+ }
11948
+ function revalidateLiveOwner(containerId, input2, composeProject, composeFiles2, service) {
11949
+ try {
11950
+ const current = inspectWorkspaceContainers({ ids: [containerId] });
11951
+ if (current.length !== 1 || current[0].id !== containerId) return "unavailable";
11952
+ if (!hasExactComposeIdentity(current[0], {
11953
+ repoPath: input2.repoPath,
11954
+ service,
11955
+ composeProject,
11956
+ composeFiles: composeFiles2
11957
+ })) {
11958
+ return "unavailable";
11959
+ }
11960
+ return current[0].state.Running ? "available" : "not-running";
11961
+ } catch {
11962
+ return "unavailable";
11963
+ }
11964
+ }
11965
+ function resolveManagedHostPorts(input2) {
11966
+ const report = {};
11967
+ const selectedServices = new Set(input2.selectedServices);
11968
+ const selectedKeys = [];
11969
+ for (const [key, declaration] of Object.entries(input2.declarations)) {
11970
+ if (!selectedServices.has(declaration.service)) {
11971
+ report[key] = { ...declaration, status: "inactive" };
11972
+ continue;
11973
+ }
11974
+ selectedKeys.push(key);
11975
+ report[key] = { ...declaration, status: "unavailable", reason: "unavailable" };
11976
+ }
11977
+ if (selectedKeys.length === 0) return report;
11978
+ const composeProject = input2.composeProject;
11979
+ const composeFiles2 = input2.composeFiles;
11980
+ if (typeof composeProject !== "string" || composeProject.length === 0 || composeFiles2 === void 0 || composeFiles2.length === 0 || composeFiles2.some((file) => file.length === 0)) {
11981
+ return report;
11982
+ }
11983
+ if (selectedKeys.some((key) => {
11984
+ const target = input2.declarations[key].target;
11985
+ return !Number.isInteger(target) || target < 1 || target > 65535;
11986
+ })) {
11987
+ return report;
11988
+ }
11989
+ const liveContainers = input2.containers === void 0;
11990
+ let containers2 = input2.containers;
11991
+ if (containers2 === void 0) {
11992
+ try {
11993
+ containers2 = inspectWorkspaceContainers({ composeProject });
11994
+ } catch {
11995
+ return report;
11996
+ }
11997
+ }
11998
+ const publishedBindings = input2.publishedBindings ?? livePublishedBindings;
11999
+ for (const key of selectedKeys) {
12000
+ const declaration = input2.declarations[key];
12001
+ let owners;
12002
+ try {
12003
+ owners = containers2.filter(
12004
+ (container) => hasExactComposeIdentity(container, {
12005
+ repoPath: input2.repoPath,
12006
+ service: declaration.service,
12007
+ composeProject,
12008
+ composeFiles: composeFiles2
12009
+ })
12010
+ );
12011
+ } catch {
12012
+ report[key] = unavailableEntry(declaration, "unavailable");
12013
+ continue;
12014
+ }
12015
+ if (owners.length === 0) {
12016
+ report[key] = unavailableEntry(declaration, "not-found");
12017
+ continue;
12018
+ }
12019
+ if (owners.length > 1) {
12020
+ report[key] = unavailableEntry(declaration, "ambiguous");
12021
+ continue;
12022
+ }
12023
+ const owner = owners[0];
12024
+ if (!owner.state.Running) {
12025
+ report[key] = unavailableEntry(declaration, "not-running");
12026
+ continue;
12027
+ }
12028
+ let output2;
12029
+ try {
12030
+ output2 = publishedBindings(owner.id, declaration.target);
12031
+ } catch {
12032
+ output2 = void 0;
12033
+ }
12034
+ if (typeof output2 !== "string") {
12035
+ report[key] = unavailableEntry(declaration, "unavailable");
12036
+ continue;
12037
+ }
12038
+ const binding = parseLoopbackBinding(output2);
12039
+ if (!binding.endpoint) {
12040
+ report[key] = unavailableEntry(declaration, binding.reason ?? "unavailable");
12041
+ continue;
12042
+ }
12043
+ if (liveContainers) {
12044
+ const proof = revalidateLiveOwner(
12045
+ owner.id,
12046
+ input2,
12047
+ composeProject,
12048
+ composeFiles2,
12049
+ declaration.service
12050
+ );
12051
+ if (proof !== "available") {
12052
+ report[key] = unavailableEntry(declaration, proof);
12053
+ continue;
12054
+ }
12055
+ }
12056
+ report[key] = {
12057
+ ...declaration,
12058
+ status: "available",
12059
+ endpoint: binding.endpoint
12060
+ };
12061
+ }
12062
+ return report;
12063
+ }
12064
+ function managedHostPortDeclarationsIdentity(declarations) {
12065
+ const keys2 = Object.keys(declarations).sort();
12066
+ if (keys2.length === 0) return void 0;
12067
+ const canonical = keys2.map((key) => [key, declarations[key].service, declarations[key].target]);
12068
+ return (0, import_node_crypto14.createHash)("sha256").update(JSON.stringify(canonical)).digest("hex");
12069
+ }
12070
+ function assertManagedHostPortsAvailable(report) {
12071
+ const unavailable = Object.entries(report).filter(([, entry]) => entry.status === "unavailable").map(([key]) => key).sort();
12072
+ if (unavailable.length > 0) {
12073
+ throw new Error(`Selected managed host ports are unavailable: ${unavailable.join(", ")}.`);
12074
+ }
12075
+ }
12076
+ var import_node_child_process14, import_node_crypto14, PORT_LOOKUP_TIMEOUT_MS, PORT_LOOKUP_MAX_BUFFER, LOOPBACK_BINDING;
12077
+ var init_managed_host_ports = __esm({
12078
+ "src/core/managed-host-ports.ts"() {
12079
+ "use strict";
12080
+ import_node_child_process14 = require("child_process");
12081
+ import_node_crypto14 = require("crypto");
12082
+ init_devpod_environment();
12083
+ init_network_effect_scope();
12084
+ PORT_LOOKUP_TIMEOUT_MS = 1e4;
12085
+ PORT_LOOKUP_MAX_BUFFER = 64 * 1024;
12086
+ LOOPBACK_BINDING = /^127\.0\.0\.1:(\d+)$/;
12087
+ }
12088
+ });
12089
+
12090
+ // src/core/managed-post-start.ts
12091
+ function readRegularFileBytes(filePath) {
12092
+ if (!import_node_fs23.default.existsSync(filePath) || !import_node_fs23.default.lstatSync(filePath).isFile()) return void 0;
12093
+ return import_node_fs23.default.readFileSync(filePath);
12094
+ }
12095
+ function readRegularFile(filePath) {
12096
+ return readRegularFileBytes(filePath)?.toString("utf-8");
12097
+ }
12098
+ function adapterFingerprint(adapter) {
12099
+ return (0, import_node_crypto15.createHash)("sha256").update(adapter).digest("hex");
12100
+ }
12101
+ function resolveManagedPostStartPlan(repoPath) {
12102
+ const adapterPath = import_node_path30.default.join(repoPath, MANAGED_ADAPTER_PATH);
12103
+ const adapterBytes = readRegularFileBytes(adapterPath);
12104
+ const adapterText = adapterBytes?.toString("utf-8");
12105
+ const dockerfilePath = import_node_path30.default.join(repoPath, ".devcontainer", "Dockerfile");
12106
+ const devcontainerPath = import_node_path30.default.join(repoPath, ".devcontainer", "devcontainer.json");
12107
+ const dockerfile = readRegularFile(dockerfilePath) ?? "";
12108
+ const devcontainer = readRegularFile(devcontainerPath) ?? "";
12109
+ const devrouterPattern = [adapterText, dockerfile, devcontainer].filter((value) => value !== void 0).some(
12110
+ (value) => value.includes("DEVROUTER_PROCESS_HELPER") || value.includes("devrouter-process")
12111
+ );
12112
+ if (adapterBytes === void 0) {
12113
+ if (devrouterPattern) {
12114
+ throw new Error(
12115
+ "Devrouter lifecycle wiring is incomplete: add the managed post-start adapter or remove the stale devrouter-process references."
12116
+ );
12117
+ }
12118
+ return { kind: "unmanaged" };
12119
+ }
12120
+ const adapter = adapterBytes.toString("utf-8");
12121
+ if (!adapter.includes(MANAGED_MARKER)) {
12122
+ if (devrouterPattern) {
12123
+ throw new Error(
12124
+ "Devrouter-looking post-start adapter is missing the 'devrouter:managed devcontainer' marker. Regenerate or migrate the devcontainer, then retry devrouter ensure."
12125
+ );
12126
+ }
12127
+ return { kind: "unmanaged" };
12128
+ }
12129
+ assertManagedDevcontainerLifecycle(repoPath);
12130
+ if (adapter.includes("DEVROUTER_PROCESS_HELPER")) {
12131
+ return {
12132
+ kind: "runtime",
12133
+ adapterPath: MANAGED_ADAPTER_PATH,
12134
+ adapterSha256: adapterFingerprint(adapterBytes),
12135
+ adapterContents: adapterBytes
12136
+ };
12137
+ }
12138
+ if (dockerfile.includes("devrouter-process") && devcontainer.includes("postStartCommand")) {
12139
+ return { kind: "legacy" };
12140
+ }
12141
+ throw new Error(
12142
+ "Managed post-start must use DEVROUTER_PROCESS_HELPER before removing the legacy image helper and postStartCommand. Regenerate or migrate the devcontainer, then retry devrouter ensure."
12143
+ );
12144
+ }
12145
+ function assertSafeProcessName(name) {
12146
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(name)) {
12147
+ throw new Error(`Managed process '${name}' is not a safe exact identifier.`);
12148
+ }
12149
+ }
12150
+ function runManagedProcessAction(options) {
12151
+ assertSafeProcessName(options.name);
12152
+ const result = (0, import_node_child_process15.spawnSync)(
12153
+ "docker",
12154
+ [
12155
+ "exec",
12156
+ "--workdir",
12157
+ options.container.workspacePath,
12158
+ "--env",
12159
+ `DEVROUTER_PROCESS_HELPER=${RUNTIME_HELPER_PATH}`,
12160
+ options.container.id,
12161
+ "bash",
12162
+ "-c",
12163
+ 'exec "$DEVROUTER_PROCESS_HELPER" "$1" --name "$2"',
12164
+ "devrouter-process",
12165
+ options.action,
12166
+ options.name
12167
+ ],
12168
+ {
12169
+ encoding: "utf-8",
12170
+ stdio: options.action === "status" ? ["ignore", "pipe", "pipe"] : ["ignore", 2, "inherit"],
12171
+ ...networkDockerOptions()
12172
+ }
12173
+ );
12174
+ if (options.action === "stop") {
12175
+ if (result.status !== 0) {
12176
+ throw new Error(`Could not stop managed process '${options.name}'.`);
12177
+ }
12178
+ return "stopped";
12179
+ }
12180
+ try {
12181
+ const parsed = JSON.parse(result.stdout.trim());
12182
+ if (parsed.status === "running" || parsed.status === "stopped" || parsed.status === "foreign" || parsed.status === "drifted") {
12183
+ return parsed.status;
12184
+ }
12185
+ } catch {
12186
+ }
12187
+ return "drifted";
12188
+ }
12189
+ var import_node_child_process15, import_node_crypto15, import_node_fs23, import_node_path30, MANAGED_MARKER, MANAGED_ADAPTER_PATH, RUNTIME_HELPER_PATH;
12190
+ var init_managed_post_start = __esm({
12191
+ "src/core/managed-post-start.ts"() {
12192
+ "use strict";
12193
+ import_node_child_process15 = require("child_process");
12194
+ import_node_crypto15 = require("crypto");
12195
+ import_node_fs23 = __toESM(require("fs"));
12196
+ import_node_path30 = __toESM(require("path"));
12197
+ init_devcontainer_config();
12198
+ init_managed_host_ports();
12199
+ init_network_effect_scope();
12200
+ MANAGED_MARKER = "devrouter:managed devcontainer";
12201
+ MANAGED_ADAPTER_PATH = ".devcontainer/post-start.sh";
12202
+ RUNTIME_HELPER_PATH = "/tmp/devrouter/bin/devrouter-process";
12203
+ }
12204
+ });
12205
+
12206
+ // src/core/network-provider-inspect.ts
12207
+ function object3(value) {
12208
+ if (!value || typeof value !== "object" || Array.isArray(value))
12209
+ throw new Error("Invalid provider evidence.");
12210
+ return value;
12211
+ }
12212
+ function qualifyNetworkProviderDefinition(value, provider) {
12213
+ const definition = object3(value);
12214
+ const agent = object3(definition.agent);
12215
+ const docker = object3(agent.docker);
12216
+ const env = object3(docker.env);
12217
+ const options = object3(definition.options);
12218
+ const host = object3(options.DOCKER_HOST);
12219
+ const dockerPath = object3(options.DOCKER_PATH);
12220
+ const exec = object3(definition.exec);
12221
+ const command = provider === "devsy" ? '"${DEVSY}" internal sh -c "${COMMAND}"' : '"${DEVPOD}" helper sh -c "${COMMAND}"';
12222
+ const actualCommand = Array.isArray(exec.command) ? exec.command : [exec.command];
12223
+ if (definition.name !== "docker" || ![true, "true"].includes(agent.local) || agent.driver !== void 0 && agent.driver !== "docker" && agent.driver !== "" || docker.path !== "${DOCKER_PATH}" || docker.builder !== "${DOCKER_BUILDER}" || ![false, "false"].includes(docker.install) || (provider === "devsy" ? docker.elevation !== "${DOCKER_ELEVATION}" : docker.elevation !== void 0 && docker.elevation !== "") || env.DOCKER_HOST !== "${DOCKER_HOST}" || Object.keys(env).some((key) => key !== "DOCKER_HOST") || host.global !== true || dockerPath.default !== "docker" || actualCommand.length !== 1 || typeof actualCommand[0] !== "string" || actualCommand[0].trim() !== command || Object.keys(exec).some((key) => key !== "command") || agent.path !== void 0 && agent.path !== "" || agent.downloadURL !== void 0 && agent.downloadURL !== "") {
12224
+ throw new Error("Docker provider definition is not qualified for network allocation.");
12225
+ }
12226
+ return (0, import_node_crypto16.createHash)("sha256").update(JSON.stringify({ name: definition.name, agent, options, exec })).digest("hex");
12227
+ }
12228
+ function read(provider, args) {
12229
+ const result = (0, import_node_child_process16.spawnSync)(provider, args, {
12230
+ encoding: "utf8",
12231
+ timeout: 5e3,
12232
+ maxBuffer: 2 * 1024 * 1024
12233
+ });
12234
+ if (result.error || result.status !== 0)
12235
+ throw new Error("Network provider evidence is unavailable.");
12236
+ return result.stdout;
12237
+ }
12238
+ function inspectNetworkProviderBinding(input2) {
12239
+ try {
12240
+ const { provider, providerId, repoPath } = input2;
12241
+ const contexts = JSON.parse(
12242
+ read(provider, [
12243
+ "context",
12244
+ "list",
12245
+ ...provider === "devsy" ? ["--result-format", "json"] : ["--output", "json"]
12246
+ ])
12247
+ );
12248
+ if (!Array.isArray(contexts) || contexts.length > 128) throw new Error();
12249
+ const selected = contexts.map(object3).filter(
12250
+ (row) => input2.providerContext ? row.name === input2.providerContext : row.default === true
12251
+ );
12252
+ if (selected.length !== 1 || typeof selected[0].name !== "string") throw new Error();
12253
+ const providerContext = selected[0].name;
12254
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,127}$/.test(providerContext)) throw new Error();
12255
+ const contextArgs = ["--context", providerContext];
12256
+ const version = read(provider, provider === "devsy" ? ["--version"] : ["version"]);
12257
+ const parsedVersion = version.match(/\b(?:v)?(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)\b/)?.[1];
12258
+ if (provider === "devsy" ? !isSupportedDevsyVersion(parsedVersion) : parsedVersion !== "0.6.15")
12259
+ throw new Error();
12260
+ const definitions = object3(
12261
+ JSON.parse(
12262
+ read(
12263
+ provider,
12264
+ provider === "devsy" ? ["provider", "list", "--result-format", "json", ...contextArgs] : ["provider", "list", "--output", "json", ...contextArgs]
12265
+ )
12266
+ )
12267
+ );
12268
+ const entry = object3(definitions.docker);
12269
+ const definitionSha256 = qualifyNetworkProviderDefinition(entry.config, provider);
12270
+ const rows2 = JSON.parse(
12271
+ read(
12272
+ provider,
12273
+ provider === "devsy" ? ["workspace", "list", "--result-format", "json", "--skip-pro", ...contextArgs] : ["list", "--output", "json", "--skip-pro", ...contextArgs]
12274
+ )
12275
+ );
12276
+ if (!Array.isArray(rows2) || rows2.length > 4096) throw new Error();
12277
+ const matching = rows2.map(object3).filter((row) => row.id === providerId || object3(row.source).localFolder === repoPath);
12278
+ if (matching.length > 1) throw new Error();
12279
+ const workspace = matching[0];
12280
+ if (workspace && (workspace.id !== providerId || object3(workspace.source).localFolder !== repoPath))
12281
+ throw new Error();
12282
+ const workspaceProvider = workspace ? object3(workspace.provider) : void 0;
12283
+ const state = object3(entry.state);
12284
+ const settings = object3(state.options ?? {});
12285
+ const stored = workspaceProvider ? object3(workspaceProvider.options ?? {}) : {};
12286
+ const option = (key, localOnly = false) => {
12287
+ const value = localOnly ? stored[key] : stored[key] ?? settings[key];
12288
+ if (value === void 0) return null;
12289
+ const text6 = object3(value).value;
12290
+ if (typeof text6 !== "string") throw new Error();
12291
+ return text6 || null;
12292
+ };
12293
+ if (workspaceProvider && workspaceProvider.name !== "docker") throw new Error();
12294
+ if (option("DOCKER_ELEVATION") && option("DOCKER_ELEVATION") !== "none") throw new Error();
12295
+ const endpoint = input2.endpoint ?? option("DOCKER_HOST");
12296
+ if (!endpoint || !input2.endpoint && (process.env.DOCKER_CONTEXT || option("DOCKER_CONTEXT") || (option("DOCKER_PATH") ?? "docker") !== "docker" || !workspace && entry.default !== true))
12297
+ throw new Error();
12298
+ return {
12299
+ provider,
12300
+ providerContext,
12301
+ providerId,
12302
+ endpoint,
12303
+ daemonId: inspectManagedStopDaemon(endpoint),
12304
+ versionQualified: true,
12305
+ definitionSha256,
12306
+ providerName: "docker",
12307
+ dockerPath: option("DOCKER_PATH") ?? "docker",
12308
+ persistedEndpoint: workspace ? option("DOCKER_HOST", true) : null,
12309
+ persistedContext: option("DOCKER_CONTEXT"),
12310
+ registration: workspace ? "owned" : "absent"
12311
+ };
12312
+ } catch {
12313
+ throw new Error(
12314
+ "Network provider destination could not be qualified; no allocation is permitted."
12315
+ );
12316
+ }
12317
+ }
12318
+ var import_node_child_process16, import_node_crypto16;
12319
+ var init_network_provider_inspect = __esm({
12320
+ "src/core/network-provider-inspect.ts"() {
12321
+ "use strict";
12322
+ import_node_child_process16 = require("child_process");
12323
+ import_node_crypto16 = require("crypto");
12324
+ init_devpod_environment();
12325
+ init_devsy_agent();
12326
+ }
12327
+ });
12328
+
11821
12329
  // src/core/reliability-context.ts
11822
12330
  function claimLifecycleEffect() {
11823
12331
  claim?.();
@@ -11838,7 +12346,7 @@ function commandError(command, repoPath, stderr) {
11838
12346
  );
11839
12347
  }
11840
12348
  function resolveGitCommonDir(repoPath) {
11841
- const result = (0, import_node_child_process14.spawnSync)("git", ["-C", repoPath, "rev-parse", "--git-common-dir"], {
12349
+ const result = (0, import_node_child_process17.spawnSync)("git", ["-C", repoPath, "rev-parse", "--git-common-dir"], {
11842
12350
  encoding: "utf-8",
11843
12351
  env: READ_ONLY_GIT_ENV
11844
12352
  });
@@ -11846,10 +12354,10 @@ function resolveGitCommonDir(repoPath) {
11846
12354
  if (result.status !== 0 || !output2) {
11847
12355
  throw commandError("Could not resolve the Git common directory", repoPath, result.stderr);
11848
12356
  }
11849
- return comparableWorkspacePath(import_node_path30.default.isAbsolute(output2) ? output2 : import_node_path30.default.resolve(repoPath, output2));
12357
+ return comparableWorkspacePath(import_node_path31.default.isAbsolute(output2) ? output2 : import_node_path31.default.resolve(repoPath, output2));
11850
12358
  }
11851
12359
  function resolveGitTopLevel(repoPath) {
11852
- const result = (0, import_node_child_process14.spawnSync)("git", ["-C", repoPath, "rev-parse", "--show-toplevel"], {
12360
+ const result = (0, import_node_child_process17.spawnSync)("git", ["-C", repoPath, "rev-parse", "--show-toplevel"], {
11853
12361
  encoding: "utf-8",
11854
12362
  env: READ_ONLY_GIT_ENV
11855
12363
  });
@@ -11860,7 +12368,7 @@ function resolveGitTopLevel(repoPath) {
11860
12368
  return comparableWorkspacePath(output2);
11861
12369
  }
11862
12370
  function listGitWorktrees(repoPath) {
11863
- const result = (0, import_node_child_process14.spawnSync)("git", ["-C", repoPath, "worktree", "list", "--porcelain"], {
12371
+ const result = (0, import_node_child_process17.spawnSync)("git", ["-C", repoPath, "worktree", "list", "--porcelain"], {
11864
12372
  encoding: "utf-8",
11865
12373
  env: READ_ONLY_GIT_ENV
11866
12374
  });
@@ -11897,7 +12405,7 @@ function listGitWorktrees(repoPath) {
11897
12405
  return worktrees;
11898
12406
  }
11899
12407
  function ownershipDirectory(repoPath) {
11900
- return import_node_path30.default.join(resolveGitCommonDir(repoPath), OWNERSHIP_DIR);
12408
+ return import_node_path31.default.join(resolveGitCommonDir(repoPath), OWNERSHIP_DIR);
11901
12409
  }
11902
12410
  function validateWorkspace(value, label2) {
11903
12411
  if (typeof value !== "string" || wsFromBranch(value) !== value) {
@@ -11925,7 +12433,7 @@ function validateRecord(value, expectedWorkspace) {
11925
12433
  `workspace ownership file '${expectedWorkspace}' contains identity '${workspace}'`
11926
12434
  );
11927
12435
  }
11928
- if (typeof candidate.worktreePath !== "string" || !import_node_path30.default.isAbsolute(candidate.worktreePath)) {
12436
+ if (typeof candidate.worktreePath !== "string" || !import_node_path31.default.isAbsolute(candidate.worktreePath)) {
11929
12437
  throw new Error("invalid workspace ownership worktreePath");
11930
12438
  }
11931
12439
  if (candidate.branch !== null && typeof candidate.branch !== "string") {
@@ -11943,7 +12451,7 @@ function validateRecord(value, expectedWorkspace) {
11943
12451
  };
11944
12452
  }
11945
12453
  function recordPath(repoPath, workspace) {
11946
- return import_node_path30.default.join(
12454
+ return import_node_path31.default.join(
11947
12455
  ownershipDirectory(repoPath),
11948
12456
  `${validateWorkspace(workspace, "workspace")}.json`
11949
12457
  );
@@ -11951,7 +12459,7 @@ function recordPath(repoPath, workspace) {
11951
12459
  function readRecordFile(filePath, expectedWorkspace) {
11952
12460
  let parsed;
11953
12461
  try {
11954
- parsed = JSON.parse(import_node_fs23.default.readFileSync(filePath, "utf-8"));
12462
+ parsed = JSON.parse(import_node_fs24.default.readFileSync(filePath, "utf-8"));
11955
12463
  } catch (error) {
11956
12464
  if (error instanceof SyntaxError) {
11957
12465
  throw new Error(`invalid workspace ownership JSON at '${filePath}'`);
@@ -11976,7 +12484,7 @@ function listWorkspaceOwnership(repoPath) {
11976
12484
  function listWorkspaceOwnershipInDirectory(directory) {
11977
12485
  let entries;
11978
12486
  try {
11979
- entries = import_node_fs23.default.readdirSync(directory, { withFileTypes: true });
12487
+ entries = import_node_fs24.default.readdirSync(directory, { withFileTypes: true });
11980
12488
  } catch (error) {
11981
12489
  if (error.code === "ENOENT") return [];
11982
12490
  throw error;
@@ -11984,14 +12492,14 @@ function listWorkspaceOwnershipInDirectory(directory) {
11984
12492
  return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).sort((left, right) => left.name.localeCompare(right.name)).map((entry) => {
11985
12493
  const workspace = entry.name.slice(0, -".json".length);
11986
12494
  validateWorkspace(workspace, "filename");
11987
- return readRecordFile(import_node_path30.default.join(directory, entry.name), workspace);
12495
+ return readRecordFile(import_node_path31.default.join(directory, entry.name), workspace);
11988
12496
  });
11989
12497
  }
11990
12498
  function writeWorkspaceOwnershipInDirectory(directory, input2) {
11991
12499
  const workspace = validateWorkspace(input2.workspace, "workspace");
11992
12500
  const devpodId = validateWorkspace(input2.devpodId, "devpodId");
11993
12501
  const worktreePath = comparableWorkspacePath(input2.worktreePath);
11994
- const filePath = import_node_path30.default.join(directory, `${workspace}.json`);
12502
+ const filePath = import_node_path31.default.join(directory, `${workspace}.json`);
11995
12503
  const now = (/* @__PURE__ */ new Date()).toISOString();
11996
12504
  const records = listWorkspaceOwnershipInDirectory(directory);
11997
12505
  const existing = records.find((record7) => record7.workspace === workspace);
@@ -12027,9 +12535,9 @@ function writeWorkspaceOwnershipInDirectory(directory, input2) {
12027
12535
  return record6;
12028
12536
  }
12029
12537
  function removeWorkspaceOwnershipInDirectory(directory, workspace) {
12030
- const filePath = import_node_path30.default.join(directory, `${validateWorkspace(workspace, "workspace")}.json`);
12538
+ const filePath = import_node_path31.default.join(directory, `${validateWorkspace(workspace, "workspace")}.json`);
12031
12539
  try {
12032
- import_node_fs23.default.rmSync(filePath);
12540
+ import_node_fs24.default.rmSync(filePath);
12033
12541
  return true;
12034
12542
  } catch (error) {
12035
12543
  if (error.code === "ENOENT") return false;
@@ -12040,7 +12548,7 @@ function sameOwnershipRecord(left, right) {
12040
12548
  return left.version === right.version && left.workspace === right.workspace && sameWorkspacePath(left.worktreePath, right.worktreePath) && left.branch === right.branch && left.devpodId === right.devpodId && left.createdAt === right.createdAt && left.updatedAt === right.updatedAt;
12041
12549
  }
12042
12550
  function removeWorkspaceOwnershipIfMatchesInDirectory(directory, expected) {
12043
- const filePath = import_node_path30.default.join(
12551
+ const filePath = import_node_path31.default.join(
12044
12552
  directory,
12045
12553
  `${validateWorkspace(expected.workspace, "workspace")}.json`
12046
12554
  );
@@ -12052,14 +12560,14 @@ function removeWorkspaceOwnershipIfMatchesInDirectory(directory, expected) {
12052
12560
  throw error;
12053
12561
  }
12054
12562
  if (!sameOwnershipRecord(current, expected)) return "changed";
12055
- import_node_fs23.default.rmSync(filePath);
12563
+ import_node_fs24.default.rmSync(filePath);
12056
12564
  return "removed";
12057
12565
  }
12058
12566
  function withWorkspaceOwnershipTransaction(repoPath, operation, options = {}) {
12059
12567
  const directory = ownershipDirectory(repoPath);
12060
- import_node_fs23.default.mkdirSync(directory, { recursive: true });
12568
+ import_node_fs24.default.mkdirSync(directory, { recursive: true });
12061
12569
  return withFileLockSync(
12062
- import_node_path30.default.join(directory, ".lock"),
12570
+ import_node_path31.default.join(directory, ".lock"),
12063
12571
  {
12064
12572
  activity: "workspace ownership transaction",
12065
12573
  target: `'${repoPath}'`,
@@ -12092,7 +12600,7 @@ function persistedWorkspaceOwners(repoPath, worktreePath) {
12092
12600
  try {
12093
12601
  workspace = readPersistedWorkspace(worktree.path);
12094
12602
  } catch (error) {
12095
- if (!import_node_fs23.default.existsSync(worktree.path)) continue;
12603
+ if (!import_node_fs24.default.existsSync(worktree.path)) continue;
12096
12604
  throw error;
12097
12605
  }
12098
12606
  if (!workspace) continue;
@@ -12255,7 +12763,7 @@ function inspectWorkspaceOwnership(record6, worktrees, devpods) {
12255
12763
  if (worktree?.locked) {
12256
12764
  return { ownerStatus: "locked", devpodStatus, worktree };
12257
12765
  }
12258
- if ((!worktree || worktree.prunable) && import_node_fs23.default.existsSync(record6.worktreePath)) {
12766
+ if ((!worktree || worktree.prunable) && import_node_fs24.default.existsSync(record6.worktreePath)) {
12259
12767
  return { ownerStatus: "conflict", devpodStatus, worktree };
12260
12768
  }
12261
12769
  if (!worktree || worktree.prunable) {
@@ -12279,20 +12787,20 @@ function listMissingWorkspaceOwnership(repoPath) {
12279
12787
  (record6) => inspectWorkspaceOwnership(record6, worktrees, void 0).ownerStatus === "missing"
12280
12788
  );
12281
12789
  }
12282
- var import_node_child_process14, import_node_fs23, import_node_path30, READ_ONLY_GIT_ENV, OWNERSHIP_VERSION, OWNERSHIP_DIR;
12790
+ var import_node_child_process17, import_node_fs24, import_node_path31, READ_ONLY_GIT_ENV, OWNERSHIP_VERSION, OWNERSHIP_DIR;
12283
12791
  var init_workspace_ownership = __esm({
12284
12792
  "src/core/workspace-ownership.ts"() {
12285
12793
  "use strict";
12286
- import_node_child_process14 = require("child_process");
12287
- import_node_fs23 = __toESM(require("fs"));
12288
- import_node_path30 = __toESM(require("path"));
12794
+ import_node_child_process17 = require("child_process");
12795
+ import_node_fs24 = __toESM(require("fs"));
12796
+ import_node_path31 = __toESM(require("path"));
12289
12797
  init_atomic_file();
12290
12798
  init_devpod_workspaces();
12291
12799
  init_file_lock();
12292
12800
  init_workspace();
12293
12801
  READ_ONLY_GIT_ENV = { ...process.env, GIT_OPTIONAL_LOCKS: "0" };
12294
12802
  OWNERSHIP_VERSION = 1;
12295
- OWNERSHIP_DIR = import_node_path30.default.join("devrouter", "workspaces");
12803
+ OWNERSHIP_DIR = import_node_path31.default.join("devrouter", "workspaces");
12296
12804
  }
12297
12805
  });
12298
12806
 
@@ -12395,6 +12903,148 @@ function proveRetainedManagedStop(state) {
12395
12903
  }
12396
12904
  return result;
12397
12905
  }
12906
+ function replacementSourceDigest(state, plan) {
12907
+ const git2 = (args) => {
12908
+ const result = (0, import_node_child_process18.spawnSync)("git", ["-C", state.repoPath, ...args], {
12909
+ encoding: "utf8",
12910
+ timeout: 5e3,
12911
+ maxBuffer: 1024 * 1024
12912
+ });
12913
+ if (result.status !== 0 || result.error)
12914
+ throw new Error("Replacement cannot freeze the source checkout.");
12915
+ return result.stdout;
12916
+ };
12917
+ if (git2(["status", "--porcelain", "--untracked-files=no"]).trim() || git2([
12918
+ "ls-files",
12919
+ "--others",
12920
+ "--exclude-standard",
12921
+ "--",
12922
+ ".devcontainer",
12923
+ ".devrouter.yml"
12924
+ ]).trim())
12925
+ throw new Error("Replacement requires committed, unchanged managed source and hooks.");
12926
+ const adapter = resolveManagedPostStartPlan(state.repoPath);
12927
+ if (adapter.kind !== "runtime")
12928
+ throw new Error("Replacement requires a preserving managed adapter.");
12929
+ const files = [
12930
+ import_node_path32.default.join(state.repoPath, ".devrouter.yml"),
12931
+ plan.sourcePath,
12932
+ ...plan.composeFiles
12933
+ ];
12934
+ return (0, import_node_crypto17.createHash)("sha256").update(
12935
+ JSON.stringify([
12936
+ git2(["rev-parse", "HEAD"]).trim(),
12937
+ adapter.adapterSha256,
12938
+ ...files.map((file) => [
12939
+ file,
12940
+ (0, import_node_crypto17.createHash)("sha256").update(import_node_fs25.default.readFileSync(file)).digest("hex")
12941
+ ])
12942
+ ])
12943
+ ).digest("hex");
12944
+ }
12945
+ function captureManagedAppReplacementProof(state) {
12946
+ if (state.status !== "ready" || requireReadyDevsyAgent().version !== "1.24.0")
12947
+ throw new Error("App replacement requires a successful retained generation and Devsy 1.24.0.");
12948
+ const baseline = validateManagedStopBaseline(state.stopBaseline, state);
12949
+ if (baseline.sourceContainer)
12950
+ throw new Error("Replacement refuses a container-sourced provider.");
12951
+ const runtime = loadRuntimeConfig(state.repoPath, state.workspace ?? "", state.profile);
12952
+ if (!runtime.config.managedRuntime || runtime.config.managedRuntime.network)
12953
+ throw new Error("Replacement requires manual managed Compose without network allocation.");
12954
+ const plan = inspectManagedDevcontainerConfig({
12955
+ repoPath: state.repoPath,
12956
+ config: runtime.config,
12957
+ profile: runtime.resolvedProfile,
12958
+ linked: isLinkedWorktree(state.repoPath)
12959
+ });
12960
+ if (state.sourceConfigSha256 !== plan.sourceConfigSha256 || state.effectiveConfigSha256 !== plan.effectiveConfigSha256 || inspectManagedDevcontainerGeneratedConfig(plan).status !== "valid")
12961
+ throw new Error("Replacement refuses changed or unavailable managed configuration.");
12962
+ const containers2 = proveRetainedManagedStop(state);
12963
+ if (!baseline.containers.some((c) => c.service !== baseline.primaryService))
12964
+ throw new Error(
12965
+ "App replacement requires a retained dependency to preserve released-client recovery proof."
12966
+ );
12967
+ if (containers2.some((c) => c.state.Running || !["created", "exited"].includes(c.state.Status)))
12968
+ throw new Error("App replacement requires every retained container stopped.");
12969
+ const workspace = state.workspace ? { token: state.workspace, gitCommonDir: resolveGitCommonDir(state.repoPath) } : void 0;
12970
+ assertManagedContainerConfigUnchanged({ plan, containers: containers2, workspace });
12971
+ const source = replacementSourceDigest(state, plan);
12972
+ const evidence = inspectNetworkProviderBinding({
12973
+ provider: "devsy",
12974
+ providerId: state.devpodId,
12975
+ repoPath: state.repoPath,
12976
+ endpoint: baseline.endpoint,
12977
+ providerContext: baseline.context
12978
+ });
12979
+ if (!evidence.versionQualified || evidence.registration !== "owned" || evidence.providerName !== "docker" || evidence.dockerPath !== "docker" || evidence.persistedContext !== null || evidence.persistedEndpoint !== null && evidence.persistedEndpoint !== baseline.endpoint || evidence.daemonId !== baseline.daemonId)
12980
+ throw new Error("Replacement provider destination is unqualified or changed.");
12981
+ const inventory = inspectAppReplacementInventory(baseline.endpoint);
12982
+ const primaryId = baseline.containers.find((c) => c.service === baseline.primaryService)?.id;
12983
+ if (!primaryId) throw new Error("Replacement has no exact retained primary.");
12984
+ assertNoAppNamespaceConsumers(inventory, primaryId);
12985
+ const primary = inventory.find((c) => c.id === primaryId);
12986
+ const env = managedComposeEnvironment(workspace);
12987
+ delete env.DOCKER_CONTEXT;
12988
+ delete env.DOCKER_HOST;
12989
+ const result = (0, import_node_child_process18.spawnSync)(
12990
+ "docker",
12991
+ [
12992
+ "--host",
12993
+ baseline.endpoint,
12994
+ "compose",
12995
+ "--project-name",
12996
+ baseline.project,
12997
+ "--project-directory",
12998
+ plan.composeDirectory,
12999
+ ...baseline.composeFiles.flatMap((f) => ["-f", f]),
13000
+ "config",
13001
+ "--format",
13002
+ "json",
13003
+ "--no-env-resolution"
13004
+ ],
13005
+ {
13006
+ cwd: plan.composeDirectory,
13007
+ env,
13008
+ encoding: "utf8",
13009
+ timeout: 15e3,
13010
+ maxBuffer: 4 * 1024 * 1024
13011
+ }
13012
+ );
13013
+ if (result.status !== 0 || result.error)
13014
+ throw new Error("Replacement Compose evidence is unavailable.");
13015
+ const model = JSON.parse(result.stdout);
13016
+ const service = model?.services?.[plan.primaryService];
13017
+ if (!service || !Array.isArray(service.volumes))
13018
+ throw new Error("Replacement requires declared persistent mounts.");
13019
+ for (const mount of primary.mounts) {
13020
+ if (!["bind", "volume"].includes(mount.Type))
13021
+ throw new Error("Replacement refuses unattributed primary mounts.");
13022
+ if (mount.Type === "volume" && !service.volumes.some(
13023
+ (v) => v.type === "volume" && v.source && v.target === mount.Destination && model.volumes?.[v.source]?.name === mount.Name
13024
+ ))
13025
+ throw new Error("Replacement refuses anonymous or undeclared primary volumes.");
13026
+ }
13027
+ const binding = {
13028
+ provider: "devsy",
13029
+ providerId: state.devpodId,
13030
+ endpoint: baseline.endpoint,
13031
+ daemonId: baseline.daemonId,
13032
+ definitionSha256: evidence.definitionSha256,
13033
+ providerContext: baseline.context
13034
+ };
13035
+ const digest5 = (0, import_node_crypto17.createHash)("sha256").update(
13036
+ JSON.stringify({
13037
+ state,
13038
+ plan,
13039
+ source,
13040
+ binding,
13041
+ primary,
13042
+ population: containers2,
13043
+ modelSha256: (0, import_node_crypto17.createHash)("sha256").update(result.stdout).digest("hex")
13044
+ })
13045
+ ).digest("hex");
13046
+ return { state, plan, primary, binding, sourceSha256: source, digest: digest5 };
13047
+ }
12398
13048
  function absentRegistrationIdentity(state) {
12399
13049
  resetWorkspaceRuntimeCaches();
12400
13050
  if (resolveWorkspaceRuntimeOrDefault(state.repoPath) !== "devsy")
@@ -12592,22 +13242,29 @@ function managedStopRouteReferences(state) {
12592
13242
  (name) => ["http", "tcp"].map((protocol) => ({ repoPath: state.repoPath, name, protocol }))
12593
13243
  );
12594
13244
  }
12595
- var import_node_os4, import_node_path31, import_node_util9;
13245
+ var import_node_child_process18, import_node_crypto17, import_node_fs25, import_node_os4, import_node_path32, import_node_util9;
12596
13246
  var init_managed_stop_recovery = __esm({
12597
13247
  "src/core/managed-stop-recovery.ts"() {
12598
13248
  "use strict";
13249
+ import_node_child_process18 = require("child_process");
13250
+ import_node_crypto17 = require("crypto");
13251
+ import_node_fs25 = __toESM(require("fs"));
12599
13252
  import_node_os4 = __toESM(require("os"));
12600
- import_node_path31 = __toESM(require("path"));
13253
+ import_node_path32 = __toESM(require("path"));
12601
13254
  import_node_util9 = require("util");
12602
13255
  init_devcontainer_profile();
12603
13256
  init_devpod_environment();
12604
13257
  init_devpod_registry();
13258
+ init_devsy_agent();
12605
13259
  init_devsy_exec_proof();
12606
13260
  init_devsy_workspaces();
12607
13261
  init_managed_compose_population();
13262
+ init_managed_post_start();
12608
13263
  init_managed_runtime_state();
12609
13264
  init_managed_stop_baseline();
13265
+ init_network_provider_inspect();
12610
13266
  init_reliability_context();
13267
+ init_repo_config();
12611
13268
  init_workspace();
12612
13269
  init_workspace_ownership();
12613
13270
  init_workspace_runtime();
@@ -12625,7 +13282,7 @@ async function getDockerodeConstructor() {
12625
13282
  return dockerodeConstructorPromise;
12626
13283
  }
12627
13284
  function runDockerContextCommand(args) {
12628
- const result = (0, import_node_child_process15.spawnSync)("docker", ["context", ...args], {
13285
+ const result = (0, import_node_child_process19.spawnSync)("docker", ["context", ...args], {
12629
13286
  encoding: "utf-8"
12630
13287
  });
12631
13288
  if (result.status !== 0) {
@@ -12638,7 +13295,7 @@ function getCurrentDockerContext() {
12638
13295
  return runDockerContextCommand(["show"]);
12639
13296
  }
12640
13297
  function getDockerHostFromContext(context) {
12641
- const result = (0, import_node_child_process15.spawnSync)(
13298
+ const result = (0, import_node_child_process19.spawnSync)(
12642
13299
  "docker",
12643
13300
  ["context", "inspect", context, "--format", "{{ .Endpoints.docker.Host }}"],
12644
13301
  { encoding: "utf-8" }
@@ -12705,11 +13362,11 @@ async function networkExists(name) {
12705
13362
  return false;
12706
13363
  }
12707
13364
  }
12708
- var import_node_child_process15, dockerodeConstructorPromise;
13365
+ var import_node_child_process19, dockerodeConstructorPromise;
12709
13366
  var init_docker = __esm({
12710
13367
  "src/core/docker.ts"() {
12711
13368
  "use strict";
12712
- import_node_child_process15 = require("child_process");
13369
+ import_node_child_process19 = require("child_process");
12713
13370
  init_network_effect_scope();
12714
13371
  dockerodeConstructorPromise = null;
12715
13372
  }
@@ -12717,7 +13374,7 @@ var init_docker = __esm({
12717
13374
 
12718
13375
  // src/core/tls.ts
12719
13376
  function runOrThrow(command, args) {
12720
- const result = (0, import_node_child_process16.spawnSync)(command, args, {
13377
+ const result = (0, import_node_child_process20.spawnSync)(command, args, {
12721
13378
  encoding: "utf-8"
12722
13379
  });
12723
13380
  if (result.status !== 0) {
@@ -12727,7 +13384,7 @@ function runOrThrow(command, args) {
12727
13384
  return result.stdout.trim();
12728
13385
  }
12729
13386
  function commandExists(command) {
12730
- const result = (0, import_node_child_process16.spawnSync)("sh", ["-c", `command -v ${command}`], { encoding: "utf-8" });
13387
+ const result = (0, import_node_child_process20.spawnSync)("sh", ["-c", `command -v ${command}`], { encoding: "utf-8" });
12731
13388
  return result.status === 0;
12732
13389
  }
12733
13390
  function ensureMkcert() {
@@ -12745,8 +13402,8 @@ function tlsSetupCommand(repoPath) {
12745
13402
  }
12746
13403
  function getMkcertRootCAPath(options = {}) {
12747
13404
  ensureMkcert();
12748
- const rootCAPath = import_node_path32.default.join(runOrThrow("mkcert", ["-CAROOT"]), "rootCA.pem");
12749
- if (!import_node_fs24.default.existsSync(rootCAPath)) {
13405
+ const rootCAPath = import_node_path33.default.join(runOrThrow("mkcert", ["-CAROOT"]), "rootCA.pem");
13406
+ if (!import_node_fs26.default.existsSync(rootCAPath)) {
12750
13407
  throw new Error(
12751
13408
  `mkcert root CA was not found at '${rootCAPath}'. Run: ${tlsSetupCommand(options.repoPath)}`
12752
13409
  );
@@ -12779,7 +13436,7 @@ function parseDnsHostsFromSubjectAltName(subjectAltName) {
12779
13436
  return normalizeUniqueHosts(names);
12780
13437
  }
12781
13438
  function parseCertificateDnsHosts(pem) {
12782
- const certificate = new import_node_crypto14.X509Certificate(pem);
13439
+ const certificate = new import_node_crypto18.X509Certificate(pem);
12783
13440
  const subjectAltName = certificate.subjectAltName ?? "";
12784
13441
  if (subjectAltName.length === 0) {
12785
13442
  return [];
@@ -12847,11 +13504,11 @@ function compactTLSCertificateHosts(hosts) {
12847
13504
  );
12848
13505
  }
12849
13506
  function readCurrentCertificateHosts(options = {}) {
12850
- if (!import_node_fs24.default.existsSync(CERT_FILE)) {
13507
+ if (!import_node_fs26.default.existsSync(CERT_FILE)) {
12851
13508
  return [];
12852
13509
  }
12853
13510
  try {
12854
- const pem = import_node_fs24.default.readFileSync(CERT_FILE, "utf-8");
13511
+ const pem = import_node_fs26.default.readFileSync(CERT_FILE, "utf-8");
12855
13512
  return parseCertificateDnsHosts(pem);
12856
13513
  } catch (error) {
12857
13514
  if (options.replaceMalformed) {
@@ -12979,20 +13636,20 @@ Run: ${tlsSetupCommand(options.repoPath)}`
12979
13636
  );
12980
13637
  }
12981
13638
  }
12982
- var import_node_child_process16, import_node_crypto14, import_node_fs24, import_node_path32, DEFAULT_TLS_CERT_HOSTS, TLS_CERTIFICATE_LOCK_FILE, TLS_CERTIFICATE_LOCK_WAIT_MS, TLS_CERTIFICATE_WRITE_ATTEMPTS;
13639
+ var import_node_child_process20, import_node_crypto18, import_node_fs26, import_node_path33, DEFAULT_TLS_CERT_HOSTS, TLS_CERTIFICATE_LOCK_FILE, TLS_CERTIFICATE_LOCK_WAIT_MS, TLS_CERTIFICATE_WRITE_ATTEMPTS;
12983
13640
  var init_tls = __esm({
12984
13641
  "src/core/tls.ts"() {
12985
13642
  "use strict";
12986
- import_node_child_process16 = require("child_process");
12987
- import_node_crypto14 = require("crypto");
12988
- import_node_fs24 = __toESM(require("fs"));
12989
- import_node_path32 = __toESM(require("path"));
13643
+ import_node_child_process20 = require("child_process");
13644
+ import_node_crypto18 = require("crypto");
13645
+ import_node_fs26 = __toESM(require("fs"));
13646
+ import_node_path33 = __toESM(require("path"));
12990
13647
  init_docker();
12991
13648
  init_file_lock();
12992
13649
  init_host_routes();
12993
13650
  init_router();
12994
13651
  DEFAULT_TLS_CERT_HOSTS = ["localhost", "*.localhost"];
12995
- TLS_CERTIFICATE_LOCK_FILE = import_node_path32.default.join(DEVROUTER_HOME, "tls-certificate.lock");
13652
+ TLS_CERTIFICATE_LOCK_FILE = import_node_path33.default.join(DEVROUTER_HOME, "tls-certificate.lock");
12996
13653
  TLS_CERTIFICATE_LOCK_WAIT_MS = 6e4;
12997
13654
  TLS_CERTIFICATE_WRITE_ATTEMPTS = 2;
12998
13655
  }
@@ -13078,7 +13735,7 @@ function sleep(ms) {
13078
13735
  return new Promise((resolve) => setTimeout(resolve, ms));
13079
13736
  }
13080
13737
  function inspectRouterApi(protocol) {
13081
- const result = (0, import_node_child_process17.spawnSync)(
13738
+ const result = (0, import_node_child_process21.spawnSync)(
13082
13739
  "curl",
13083
13740
  [
13084
13741
  "--silent",
@@ -13185,7 +13842,7 @@ async function ensureTraefikRouteExpectation(routes, expectation, options) {
13185
13842
  throw new Error(
13186
13843
  "Traefik routes did not reload; shared router restart is disabled for this operation."
13187
13844
  );
13188
- import_node_fs25.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
13845
+ import_node_fs27.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
13189
13846
  return withFileLock(
13190
13847
  ROUTER_RELOAD_LOCK_FILE,
13191
13848
  {
@@ -13220,19 +13877,19 @@ function assertTraefikRoutesRemoved(routes) {
13220
13877
  const result = inspectExpectedRoutes(routes, "removed");
13221
13878
  if (!result.ok) throw new Error(`Workspace route cessation is not proven: ${result.details}`);
13222
13879
  }
13223
- var import_node_child_process17, import_node_fs25, import_node_path33, ROUTER_API_BASE, ROUTER_RELOAD_LOCK_FILE, ROUTER_API_REQUEST_TIMEOUT_SECONDS, DEFAULT_INITIAL_TIMEOUT_MS, DEFAULT_RECOVERY_TIMEOUT_MS, DEFAULT_POLL_INTERVAL_MS, ROUTER_API_PAGE_SIZE;
13880
+ var import_node_child_process21, import_node_fs27, import_node_path34, ROUTER_API_BASE, ROUTER_RELOAD_LOCK_FILE, ROUTER_API_REQUEST_TIMEOUT_SECONDS, DEFAULT_INITIAL_TIMEOUT_MS, DEFAULT_RECOVERY_TIMEOUT_MS, DEFAULT_POLL_INTERVAL_MS, ROUTER_API_PAGE_SIZE;
13224
13881
  var init_traefik_route_health = __esm({
13225
13882
  "src/core/traefik-route-health.ts"() {
13226
13883
  "use strict";
13227
- import_node_child_process17 = require("child_process");
13228
- import_node_fs25 = __toESM(require("fs"));
13229
- import_node_path33 = __toESM(require("path"));
13884
+ import_node_child_process21 = require("child_process");
13885
+ import_node_fs27 = __toESM(require("fs"));
13886
+ import_node_path34 = __toESM(require("path"));
13230
13887
  init_file_lock();
13231
13888
  init_host_routes();
13232
13889
  init_reliability_context();
13233
13890
  init_router();
13234
13891
  ROUTER_API_BASE = "http://127.0.0.1:8080/api";
13235
- ROUTER_RELOAD_LOCK_FILE = import_node_path33.default.join(DEVROUTER_HOME, "router-reload.lock");
13892
+ ROUTER_RELOAD_LOCK_FILE = import_node_path34.default.join(DEVROUTER_HOME, "router-reload.lock");
13236
13893
  ROUTER_API_REQUEST_TIMEOUT_SECONDS = "2";
13237
13894
  DEFAULT_INITIAL_TIMEOUT_MS = 3e3;
13238
13895
  DEFAULT_RECOVERY_TIMEOUT_MS = 1e4;
@@ -13257,7 +13914,7 @@ function proveInitialManagedDevsyAbsence(repoPath, expectedId) {
13257
13914
  if (!isLinkedWorktree(repoPath)) return void 0;
13258
13915
  const workspace = resolveWorktreeWorkspace(repoPath);
13259
13916
  if (!workspace || readManagedRuntimeState(repoPath, workspace)) return void 0;
13260
- if (!import_node_fs26.default.existsSync(import_node_path34.default.join(repoPath, ".devrouter.yml"))) return void 0;
13917
+ if (!import_node_fs28.default.existsSync(import_node_path35.default.join(repoPath, ".devrouter.yml"))) return void 0;
13261
13918
  const runtime = loadRuntimeConfig(repoPath, workspace);
13262
13919
  if (!runtime.config.managedRuntime) return void 0;
13263
13920
  const routes = proxyAppsFromConfig(runtime.config).flatMap(
@@ -13317,7 +13974,7 @@ function proveInitialManagedDevsyAbsence(repoPath, expectedId) {
13317
13974
  };
13318
13975
  }
13319
13976
  function stopInitialManagedDevsyWorkspace(repoPath, devsyId) {
13320
- if (!import_node_fs26.default.existsSync(import_node_path34.default.join(repoPath, ".devrouter.yml"))) return false;
13977
+ if (!import_node_fs28.default.existsSync(import_node_path35.default.join(repoPath, ".devrouter.yml"))) return false;
13321
13978
  if (!loadRepoConfig(repoPath).managedRuntime) return false;
13322
13979
  const linked = isLinkedWorktree(repoPath);
13323
13980
  const workspace = linked ? resolveWorktreeWorkspace(repoPath) : void 0;
@@ -13374,8 +14031,8 @@ function stopInitialManagedDevsyWorkspace(repoPath, devsyId) {
13374
14031
  profile: runtime.resolvedProfile,
13375
14032
  linked
13376
14033
  });
13377
- const providerRoot = import_node_path34.default.resolve(process.env.DEVSY_HOME || import_node_path34.default.join(import_node_os5.default.homedir(), ".devsy"));
13378
- const featureDirectory = import_node_path34.default.join(
14034
+ const providerRoot = import_node_path35.default.resolve(process.env.DEVSY_HOME || import_node_path35.default.join(import_node_os5.default.homedir(), ".devsy"));
14035
+ const featureDirectory = import_node_path35.default.join(
13379
14036
  providerRoot,
13380
14037
  "contexts",
13381
14038
  owner.context,
@@ -13497,7 +14154,7 @@ function restoreRecordedManagedDevcontainerConfig(repoPath) {
13497
14154
  }
13498
14155
  function recordedManagedDevcontainerRestorePlan(repoPath) {
13499
14156
  try {
13500
- if (!import_node_fs26.default.existsSync(import_node_path34.default.join(repoPath, ".devrouter.yml"))) return void 0;
14157
+ if (!import_node_fs28.default.existsSync(import_node_path35.default.join(repoPath, ".devrouter.yml"))) return void 0;
13501
14158
  const linked = isLinkedWorktree(repoPath);
13502
14159
  const workspace = linked ? resolveWorktreeWorkspace(repoPath) : void 0;
13503
14160
  const state = readManagedRuntimeState(repoPath, workspace);
@@ -13588,7 +14245,7 @@ function stopRetainedManagedDevsyWorkspace(options) {
13588
14245
  const owned = inspectWorkspaceContainers().filter(
13589
14246
  (container) => sameWorkspacePath(
13590
14247
  container.labels["com.docker.compose.project.working_dir"] ?? "",
13591
- import_node_path34.default.join(repoPath, ".devcontainer")
14248
+ import_node_path35.default.join(repoPath, ".devcontainer")
13592
14249
  )
13593
14250
  );
13594
14251
  if (owned.length !== 0) {
@@ -13617,8 +14274,8 @@ function stopRetainedManagedDevsyWorkspace(options) {
13617
14274
  if (!context || !/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(context) || !/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(devsyId)) {
13618
14275
  throw new Error("Managed stop requires a valid exact Devsy context and workspace ID.");
13619
14276
  }
13620
- const devsyRoot = import_node_path34.default.resolve(process.env.DEVSY_HOME || import_node_path34.default.join(import_node_os5.default.homedir(), ".devsy"));
13621
- const featureDirectory = import_node_path34.default.join(
14277
+ const devsyRoot = import_node_path35.default.resolve(process.env.DEVSY_HOME || import_node_path35.default.join(import_node_os5.default.homedir(), ".devsy"));
14278
+ const featureDirectory = import_node_path35.default.join(
13622
14279
  devsyRoot,
13623
14280
  "contexts",
13624
14281
  context,
@@ -13722,13 +14379,13 @@ function stopRetainedManagedDevsyWorkspace(options) {
13722
14379
  if (providerFailed) throw providerError;
13723
14380
  return true;
13724
14381
  }
13725
- var import_node_fs26, import_node_os5, import_node_path34, import_node_util10;
14382
+ var import_node_fs28, import_node_os5, import_node_path35, import_node_util10;
13726
14383
  var init_managed_devsy_stop = __esm({
13727
14384
  "src/core/managed-devsy-stop.ts"() {
13728
14385
  "use strict";
13729
- import_node_fs26 = __toESM(require("fs"));
14386
+ import_node_fs28 = __toESM(require("fs"));
13730
14387
  import_node_os5 = __toESM(require("os"));
13731
- import_node_path34 = __toESM(require("path"));
14388
+ import_node_path35 = __toESM(require("path"));
13732
14389
  import_node_util10 = require("util");
13733
14390
  init_devcontainer_profile();
13734
14391
  init_devpod_environment();
@@ -14290,7 +14947,7 @@ function hasControlCharacter2(value) {
14290
14947
  function readPolicyFile(filePath) {
14291
14948
  let initialStats;
14292
14949
  try {
14293
- initialStats = import_node_fs27.default.lstatSync(filePath);
14950
+ initialStats = import_node_fs29.default.lstatSync(filePath);
14294
14951
  } catch (error) {
14295
14952
  if (isErrno(error, "ENOENT")) return { status: "absent" };
14296
14953
  return { status: "invalid", error: "network policy could not be read" };
@@ -14303,16 +14960,16 @@ function readPolicyFile(filePath) {
14303
14960
  }
14304
14961
  let fileDescriptor;
14305
14962
  try {
14306
- fileDescriptor = import_node_fs27.default.openSync(
14963
+ fileDescriptor = import_node_fs29.default.openSync(
14307
14964
  filePath,
14308
- import_node_fs27.default.constants.O_RDONLY | import_node_fs27.default.constants.O_NONBLOCK | (import_node_fs27.default.constants.O_NOFOLLOW ?? 0)
14965
+ import_node_fs29.default.constants.O_RDONLY | import_node_fs29.default.constants.O_NONBLOCK | (import_node_fs29.default.constants.O_NOFOLLOW ?? 0)
14309
14966
  );
14310
14967
  } catch (error) {
14311
14968
  if (isErrno(error, "ENOENT")) return { status: "absent" };
14312
14969
  return { status: "invalid", error: "network policy could not be read" };
14313
14970
  }
14314
14971
  try {
14315
- const openedStats = import_node_fs27.default.fstatSync(fileDescriptor);
14972
+ const openedStats = import_node_fs29.default.fstatSync(fileDescriptor);
14316
14973
  if (!openedStats.isFile()) {
14317
14974
  return { status: "invalid", error: "network policy must be a regular file" };
14318
14975
  }
@@ -14325,12 +14982,12 @@ function readPolicyFile(filePath) {
14325
14982
  const buffer = Buffer.alloc(openedStats.size);
14326
14983
  let offset = 0;
14327
14984
  while (offset < buffer.length) {
14328
- const bytesRead = import_node_fs27.default.readSync(fileDescriptor, buffer, offset, buffer.length - offset, null);
14985
+ const bytesRead = import_node_fs29.default.readSync(fileDescriptor, buffer, offset, buffer.length - offset, null);
14329
14986
  if (bytesRead === 0)
14330
14987
  return { status: "invalid", error: "network policy changed during read" };
14331
14988
  offset += bytesRead;
14332
14989
  }
14333
- const finalStats = import_node_fs27.default.fstatSync(fileDescriptor);
14990
+ const finalStats = import_node_fs29.default.fstatSync(fileDescriptor);
14334
14991
  if (!finalStats.isFile() || finalStats.size !== openedStats.size) {
14335
14992
  return { status: "invalid", error: "network policy changed during read" };
14336
14993
  }
@@ -14339,7 +14996,7 @@ function readPolicyFile(filePath) {
14339
14996
  return { status: "invalid", error: "network policy could not be read" };
14340
14997
  } finally {
14341
14998
  try {
14342
- import_node_fs27.default.closeSync(fileDescriptor);
14999
+ import_node_fs29.default.closeSync(fileDescriptor);
14343
15000
  } catch {
14344
15001
  }
14345
15002
  }
@@ -14356,16 +15013,16 @@ function assertDisjoint(cidrs, message) {
14356
15013
  function isErrno(error, code) {
14357
15014
  return typeof error === "object" && error !== null && "code" in error && error.code === code;
14358
15015
  }
14359
- var import_node_fs27, import_node_path35, NETWORK_POLICY_VERSION, NETWORK_POLICY_FILE, DEFAULT_NETWORK_POLICY_ENDPOINT_RESERVE, NETWORK_POLICY_PREFIX_LENGTHS, MAX_NETWORK_POLICY_FILE_BYTES, MAX_NETWORK_POLICY_ARRAY_ENTRIES, MAX_NETWORK_POLICY_DAEMON_ID_LENGTH, NetworkPolicyValidationError, NETWORK_POLICY_KEYS, PRIVATE_IPV4_RANGES;
15016
+ var import_node_fs29, import_node_path36, NETWORK_POLICY_VERSION, NETWORK_POLICY_FILE, DEFAULT_NETWORK_POLICY_ENDPOINT_RESERVE, NETWORK_POLICY_PREFIX_LENGTHS, MAX_NETWORK_POLICY_FILE_BYTES, MAX_NETWORK_POLICY_ARRAY_ENTRIES, MAX_NETWORK_POLICY_DAEMON_ID_LENGTH, NetworkPolicyValidationError, NETWORK_POLICY_KEYS, PRIVATE_IPV4_RANGES;
14360
15017
  var init_network_policy = __esm({
14361
15018
  "src/core/network-policy.ts"() {
14362
15019
  "use strict";
14363
- import_node_fs27 = __toESM(require("fs"));
14364
- import_node_path35 = __toESM(require("path"));
15020
+ import_node_fs29 = __toESM(require("fs"));
15021
+ import_node_path36 = __toESM(require("path"));
14365
15022
  init_network_capacity();
14366
15023
  init_router();
14367
15024
  NETWORK_POLICY_VERSION = 1;
14368
- NETWORK_POLICY_FILE = import_node_path35.default.join(DEVROUTER_HOME, "network-policy.json");
15025
+ NETWORK_POLICY_FILE = import_node_path36.default.join(DEVROUTER_HOME, "network-policy.json");
14369
15026
  DEFAULT_NETWORK_POLICY_ENDPOINT_RESERVE = 8;
14370
15027
  NETWORK_POLICY_PREFIX_LENGTHS = [24, 25, 26];
14371
15028
  MAX_NETWORK_POLICY_FILE_BYTES = 64 * 1024;
@@ -14466,27 +15123,27 @@ function assertExactKeys(value, keys2) {
14466
15123
  function readDocument(store) {
14467
15124
  let descriptor;
14468
15125
  try {
14469
- descriptor = import_node_fs28.default.openSync(
15126
+ descriptor = import_node_fs30.default.openSync(
14470
15127
  store.filePath,
14471
- import_node_fs28.default.constants.O_RDONLY | import_node_fs28.default.constants.O_NONBLOCK | (import_node_fs28.default.constants.O_NOFOLLOW ?? 0)
15128
+ import_node_fs30.default.constants.O_RDONLY | import_node_fs30.default.constants.O_NONBLOCK | (import_node_fs30.default.constants.O_NOFOLLOW ?? 0)
14472
15129
  );
14473
15130
  } catch (error) {
14474
15131
  if (isErrno2(error, "ENOENT")) return void 0;
14475
15132
  throw claimError("invalid-state", "network claim state could not be read");
14476
15133
  }
14477
15134
  try {
14478
- const stat = import_node_fs28.default.fstatSync(descriptor);
15135
+ const stat = import_node_fs30.default.fstatSync(descriptor);
14479
15136
  if (!stat.isFile() || stat.size > NETWORK_CLAIMS_MAX_FILE_BYTES || (stat.mode & 63) !== 0) {
14480
15137
  throw claimError("invalid-state", "network claim state is not a bounded private file");
14481
15138
  }
14482
15139
  const buffer = Buffer.alloc(NETWORK_CLAIMS_MAX_FILE_BYTES + 1);
14483
15140
  let count = 0;
14484
15141
  while (count < buffer.length) {
14485
- const bytesRead = import_node_fs28.default.readSync(descriptor, buffer, count, buffer.length - count, null);
15142
+ const bytesRead = import_node_fs30.default.readSync(descriptor, buffer, count, buffer.length - count, null);
14486
15143
  if (bytesRead === 0) break;
14487
15144
  count += bytesRead;
14488
15145
  }
14489
- const finalStat = import_node_fs28.default.fstatSync(descriptor);
15146
+ const finalStat = import_node_fs30.default.fstatSync(descriptor);
14490
15147
  if (count > NETWORK_CLAIMS_MAX_FILE_BYTES || finalStat.size !== stat.size) {
14491
15148
  throw claimError("invalid-state", "network claim state changed during read");
14492
15149
  }
@@ -14498,7 +15155,7 @@ function readDocument(store) {
14498
15155
  }
14499
15156
  return validateDocument(parsed, store.daemonId);
14500
15157
  } finally {
14501
- import_node_fs28.default.closeSync(descriptor);
15158
+ import_node_fs30.default.closeSync(descriptor);
14502
15159
  }
14503
15160
  }
14504
15161
  function validateDocument(value, daemonId) {
@@ -14565,21 +15222,21 @@ function createStore(daemonId, options) {
14565
15222
  return {
14566
15223
  root,
14567
15224
  daemonId,
14568
- filePath: import_node_path36.default.join(root, `${key}.json`),
14569
- lockPath: import_node_path36.default.join(root, `${key}.lock`),
15225
+ filePath: import_node_path37.default.join(root, `${key}.json`),
15226
+ lockPath: import_node_path37.default.join(root, `${key}.lock`),
14570
15227
  writeAtomic: options.writeAtomic ?? writeFileAtomically
14571
15228
  };
14572
15229
  }
14573
15230
  function validateRoot(root) {
14574
- if (typeof root !== "string" || !import_node_path36.default.isAbsolute(root) || root.includes("\0")) {
15231
+ if (typeof root !== "string" || !import_node_path37.default.isAbsolute(root) || root.includes("\0")) {
14575
15232
  throw claimError("invalid-input", "network claim root is invalid");
14576
15233
  }
14577
- const resolved2 = import_node_path36.default.resolve(root);
15234
+ const resolved2 = import_node_path37.default.resolve(root);
14578
15235
  if (resolved2 !== root) throw claimError("invalid-input", "network claim root is invalid");
14579
15236
  return resolved2;
14580
15237
  }
14581
15238
  function hashKey(value) {
14582
- return (0, import_node_crypto15.createHash)("sha256").update(value).digest("hex");
15239
+ return (0, import_node_crypto19.createHash)("sha256").update(value).digest("hex");
14583
15240
  }
14584
15241
  function cloneFence(fence) {
14585
15242
  return { ...fence };
@@ -14602,20 +15259,20 @@ function isErrno2(error, code) {
14602
15259
  function claimError(code, message) {
14603
15260
  return new NetworkClaimError(code, message);
14604
15261
  }
14605
- var import_node_crypto15, import_node_fs28, import_node_path36, NETWORK_CLAIMS_VERSION, NETWORK_CLAIMS_ROOT, NETWORK_CLAIMS_MAX_RECORDS, NETWORK_CLAIMS_MAX_FILE_BYTES, NetworkClaimError, CLAIM_CORE_KEYS, CLAIM_KEYS, FENCE_KEYS, DOCUMENT_KEYS, MAX_CLAIM_TEXT, MAX_ENDPOINT_TEXT;
15262
+ var import_node_crypto19, import_node_fs30, import_node_path37, NETWORK_CLAIMS_VERSION, NETWORK_CLAIMS_ROOT, NETWORK_CLAIMS_MAX_RECORDS, NETWORK_CLAIMS_MAX_FILE_BYTES, NetworkClaimError, CLAIM_CORE_KEYS, CLAIM_KEYS, FENCE_KEYS, DOCUMENT_KEYS, MAX_CLAIM_TEXT, MAX_ENDPOINT_TEXT;
14606
15263
  var init_network_claims = __esm({
14607
15264
  "src/core/network-claims.ts"() {
14608
15265
  "use strict";
14609
- import_node_crypto15 = require("crypto");
14610
- import_node_fs28 = __toESM(require("fs"));
14611
- import_node_path36 = __toESM(require("path"));
15266
+ import_node_crypto19 = require("crypto");
15267
+ import_node_fs30 = __toESM(require("fs"));
15268
+ import_node_path37 = __toESM(require("path"));
14612
15269
  init_atomic_file();
14613
15270
  init_file_lock();
14614
15271
  init_network_capacity();
14615
15272
  init_network_policy();
14616
15273
  init_router();
14617
15274
  NETWORK_CLAIMS_VERSION = 1;
14618
- NETWORK_CLAIMS_ROOT = import_node_path36.default.join(DEVROUTER_HOME, "networks");
15275
+ NETWORK_CLAIMS_ROOT = import_node_path37.default.join(DEVROUTER_HOME, "networks");
14619
15276
  NETWORK_CLAIMS_MAX_RECORDS = 256;
14620
15277
  NETWORK_CLAIMS_MAX_FILE_BYTES = 512 * 1024;
14621
15278
  NetworkClaimError = class extends Error {
@@ -14657,14 +15314,14 @@ var init_network_claims = __esm({
14657
15314
 
14658
15315
  // src/core/network-claim-lookup.ts
14659
15316
  function networkOwnerKey(repoPath) {
14660
- if (!import_node_path37.default.isAbsolute(repoPath) || import_node_path37.default.resolve(repoPath) !== repoPath)
15317
+ if (!import_node_path38.default.isAbsolute(repoPath) || import_node_path38.default.resolve(repoPath) !== repoPath)
14661
15318
  throw new Error("Invalid network owner path.");
14662
- return (0, import_node_crypto16.createHash)("sha256").update(repoPath).digest("hex");
15319
+ return (0, import_node_crypto20.createHash)("sha256").update(repoPath).digest("hex");
14663
15320
  }
14664
15321
  function findOwnedNetworkClaim(repoPath, root = NETWORK_CLAIMS_ROOT) {
14665
15322
  let names;
14666
15323
  try {
14667
- names = import_node_fs29.default.readdirSync(root);
15324
+ names = import_node_fs31.default.readdirSync(root);
14668
15325
  } catch (error) {
14669
15326
  if (error.code === "ENOENT") return void 0;
14670
15327
  throw new Error("Network claims cannot be inspected; repair is required.");
@@ -14673,21 +15330,21 @@ function findOwnedNetworkClaim(repoPath, root = NETWORK_CLAIMS_ROOT) {
14673
15330
  if (files.length > 128) throw new Error("Network daemon inventory exceeds its bound.");
14674
15331
  let found;
14675
15332
  for (const file of files) {
14676
- const full = import_node_path37.default.join(root, file);
14677
- const stat = import_node_fs29.default.lstatSync(full);
15333
+ const full = import_node_path38.default.join(root, file);
15334
+ const stat = import_node_fs31.default.lstatSync(full);
14678
15335
  if (!stat.isFile() || stat.size > NETWORK_CLAIMS_MAX_FILE_BYTES)
14679
15336
  throw new Error("Invalid retained network claim file.");
14680
15337
  let value;
14681
- const descriptor = import_node_fs29.default.openSync(
15338
+ const descriptor = import_node_fs31.default.openSync(
14682
15339
  full,
14683
- import_node_fs29.default.constants.O_RDONLY | import_node_fs29.default.constants.O_NONBLOCK | import_node_fs29.default.constants.O_NOFOLLOW
15340
+ import_node_fs31.default.constants.O_RDONLY | import_node_fs31.default.constants.O_NONBLOCK | import_node_fs31.default.constants.O_NOFOLLOW
14684
15341
  );
14685
15342
  try {
14686
- if (!import_node_fs29.default.fstatSync(descriptor).isFile()) throw new Error();
15343
+ if (!import_node_fs31.default.fstatSync(descriptor).isFile()) throw new Error();
14687
15344
  const bytes2 = Buffer.alloc(NETWORK_CLAIMS_MAX_FILE_BYTES + 1);
14688
15345
  let size = 0;
14689
15346
  while (size < bytes2.length) {
14690
- const count = import_node_fs29.default.readSync(descriptor, bytes2, size, bytes2.length - size, null);
15347
+ const count = import_node_fs31.default.readSync(descriptor, bytes2, size, bytes2.length - size, null);
14691
15348
  if (!count) break;
14692
15349
  size += count;
14693
15350
  }
@@ -14697,9 +15354,9 @@ function findOwnedNetworkClaim(repoPath, root = NETWORK_CLAIMS_ROOT) {
14697
15354
  } catch {
14698
15355
  throw new Error("Retained network claim could not be read safely.");
14699
15356
  } finally {
14700
- import_node_fs29.default.closeSync(descriptor);
15357
+ import_node_fs31.default.closeSync(descriptor);
14701
15358
  }
14702
- if (typeof value.daemonId !== "string" || `${(0, import_node_crypto16.createHash)("sha256").update(value.daemonId).digest("hex")}.json` !== file)
15359
+ if (typeof value.daemonId !== "string" || `${(0, import_node_crypto20.createHash)("sha256").update(value.daemonId).digest("hex")}.json` !== file)
14703
15360
  throw new Error("Retained network daemon identity is invalid.");
14704
15361
  const claim2 = readNetworkClaim({
14705
15362
  daemonId: value.daemonId,
@@ -14713,140 +15370,17 @@ function findOwnedNetworkClaim(repoPath, root = NETWORK_CLAIMS_ROOT) {
14713
15370
  }
14714
15371
  return found;
14715
15372
  }
14716
- var import_node_crypto16, import_node_fs29, import_node_path37;
15373
+ var import_node_crypto20, import_node_fs31, import_node_path38;
14717
15374
  var init_network_claim_lookup = __esm({
14718
15375
  "src/core/network-claim-lookup.ts"() {
14719
15376
  "use strict";
14720
- import_node_crypto16 = require("crypto");
14721
- import_node_fs29 = __toESM(require("fs"));
14722
- import_node_path37 = __toESM(require("path"));
15377
+ import_node_crypto20 = require("crypto");
15378
+ import_node_fs31 = __toESM(require("fs"));
15379
+ import_node_path38 = __toESM(require("path"));
14723
15380
  init_network_claims();
14724
15381
  }
14725
15382
  });
14726
15383
 
14727
- // src/core/network-provider-inspect.ts
14728
- function object3(value) {
14729
- if (!value || typeof value !== "object" || Array.isArray(value))
14730
- throw new Error("Invalid provider evidence.");
14731
- return value;
14732
- }
14733
- function qualifyNetworkProviderDefinition(value, provider) {
14734
- const definition = object3(value);
14735
- const agent = object3(definition.agent);
14736
- const docker = object3(agent.docker);
14737
- const env = object3(docker.env);
14738
- const options = object3(definition.options);
14739
- const host = object3(options.DOCKER_HOST);
14740
- const dockerPath = object3(options.DOCKER_PATH);
14741
- const exec = object3(definition.exec);
14742
- const command = provider === "devsy" ? '"${DEVSY}" internal sh -c "${COMMAND}"' : '"${DEVPOD}" helper sh -c "${COMMAND}"';
14743
- const actualCommand = Array.isArray(exec.command) ? exec.command : [exec.command];
14744
- if (definition.name !== "docker" || ![true, "true"].includes(agent.local) || agent.driver !== void 0 && agent.driver !== "docker" && agent.driver !== "" || docker.path !== "${DOCKER_PATH}" || docker.builder !== "${DOCKER_BUILDER}" || ![false, "false"].includes(docker.install) || (provider === "devsy" ? docker.elevation !== "${DOCKER_ELEVATION}" : docker.elevation !== void 0 && docker.elevation !== "") || env.DOCKER_HOST !== "${DOCKER_HOST}" || Object.keys(env).some((key) => key !== "DOCKER_HOST") || host.global !== true || dockerPath.default !== "docker" || actualCommand.length !== 1 || typeof actualCommand[0] !== "string" || actualCommand[0].trim() !== command || Object.keys(exec).some((key) => key !== "command") || agent.path !== void 0 && agent.path !== "" || agent.downloadURL !== void 0 && agent.downloadURL !== "") {
14745
- throw new Error("Docker provider definition is not qualified for network allocation.");
14746
- }
14747
- return (0, import_node_crypto17.createHash)("sha256").update(JSON.stringify({ name: definition.name, agent, options, exec })).digest("hex");
14748
- }
14749
- function read(provider, args) {
14750
- const result = (0, import_node_child_process18.spawnSync)(provider, args, {
14751
- encoding: "utf8",
14752
- timeout: 5e3,
14753
- maxBuffer: 2 * 1024 * 1024
14754
- });
14755
- if (result.error || result.status !== 0)
14756
- throw new Error("Network provider evidence is unavailable.");
14757
- return result.stdout;
14758
- }
14759
- function inspectNetworkProviderBinding(input2) {
14760
- try {
14761
- const { provider, providerId, repoPath } = input2;
14762
- const contexts = JSON.parse(
14763
- read(provider, [
14764
- "context",
14765
- "list",
14766
- ...provider === "devsy" ? ["--result-format", "json"] : ["--output", "json"]
14767
- ])
14768
- );
14769
- if (!Array.isArray(contexts) || contexts.length > 128) throw new Error();
14770
- const selected = contexts.map(object3).filter(
14771
- (row) => input2.providerContext ? row.name === input2.providerContext : row.default === true
14772
- );
14773
- if (selected.length !== 1 || typeof selected[0].name !== "string") throw new Error();
14774
- const providerContext = selected[0].name;
14775
- if (!/^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,127}$/.test(providerContext)) throw new Error();
14776
- const contextArgs = ["--context", providerContext];
14777
- const version = read(provider, provider === "devsy" ? ["--version"] : ["version"]);
14778
- const parsedVersion = version.match(/\b(?:v)?(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)\b/)?.[1];
14779
- if (provider === "devsy" ? !isSupportedDevsyVersion(parsedVersion) : parsedVersion !== "0.6.15")
14780
- throw new Error();
14781
- const definitions = object3(
14782
- JSON.parse(
14783
- read(
14784
- provider,
14785
- provider === "devsy" ? ["provider", "list", "--result-format", "json", ...contextArgs] : ["provider", "list", "--output", "json", ...contextArgs]
14786
- )
14787
- )
14788
- );
14789
- const entry = object3(definitions.docker);
14790
- const definitionSha256 = qualifyNetworkProviderDefinition(entry.config, provider);
14791
- const rows2 = JSON.parse(
14792
- read(
14793
- provider,
14794
- provider === "devsy" ? ["workspace", "list", "--result-format", "json", "--skip-pro", ...contextArgs] : ["list", "--output", "json", "--skip-pro", ...contextArgs]
14795
- )
14796
- );
14797
- if (!Array.isArray(rows2) || rows2.length > 4096) throw new Error();
14798
- const matching = rows2.map(object3).filter((row) => row.id === providerId || object3(row.source).localFolder === repoPath);
14799
- if (matching.length > 1) throw new Error();
14800
- const workspace = matching[0];
14801
- if (workspace && (workspace.id !== providerId || object3(workspace.source).localFolder !== repoPath))
14802
- throw new Error();
14803
- const workspaceProvider = workspace ? object3(workspace.provider) : void 0;
14804
- const state = object3(entry.state);
14805
- const settings = object3(state.options ?? {});
14806
- const stored = workspaceProvider ? object3(workspaceProvider.options ?? {}) : {};
14807
- const option = (key, localOnly = false) => {
14808
- const value = localOnly ? stored[key] : stored[key] ?? settings[key];
14809
- if (value === void 0) return null;
14810
- const text6 = object3(value).value;
14811
- if (typeof text6 !== "string") throw new Error();
14812
- return text6 || null;
14813
- };
14814
- if (workspaceProvider && workspaceProvider.name !== "docker") throw new Error();
14815
- if (option("DOCKER_ELEVATION") && option("DOCKER_ELEVATION") !== "none") throw new Error();
14816
- const endpoint = input2.endpoint ?? option("DOCKER_HOST");
14817
- if (!endpoint || !input2.endpoint && (process.env.DOCKER_CONTEXT || option("DOCKER_CONTEXT") || (option("DOCKER_PATH") ?? "docker") !== "docker" || !workspace && entry.default !== true))
14818
- throw new Error();
14819
- return {
14820
- provider,
14821
- providerContext,
14822
- providerId,
14823
- endpoint,
14824
- daemonId: inspectManagedStopDaemon(endpoint),
14825
- versionQualified: true,
14826
- definitionSha256,
14827
- providerName: "docker",
14828
- dockerPath: option("DOCKER_PATH") ?? "docker",
14829
- persistedEndpoint: workspace ? option("DOCKER_HOST", true) : null,
14830
- persistedContext: option("DOCKER_CONTEXT"),
14831
- registration: workspace ? "owned" : "absent"
14832
- };
14833
- } catch {
14834
- throw new Error(
14835
- "Network provider destination could not be qualified; no allocation is permitted."
14836
- );
14837
- }
14838
- }
14839
- var import_node_child_process18, import_node_crypto17;
14840
- var init_network_provider_inspect = __esm({
14841
- "src/core/network-provider-inspect.ts"() {
14842
- "use strict";
14843
- import_node_child_process18 = require("child_process");
14844
- import_node_crypto17 = require("crypto");
14845
- init_devpod_environment();
14846
- init_devsy_agent();
14847
- }
14848
- });
14849
-
14850
15384
  // src/core/network-provider-effect.ts
14851
15385
  function networkProviderEffectOptions(provider, providerId, repoPath) {
14852
15386
  const claim2 = findOwnedNetworkClaim(repoPath);
@@ -14880,7 +15414,7 @@ var init_network_provider_effect = __esm({
14880
15414
 
14881
15415
  // src/core/devsy-mutation.ts
14882
15416
  function withMutationLock(activity, target, operation) {
14883
- import_node_fs30.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
15417
+ import_node_fs32.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
14884
15418
  return withFileLockSync(
14885
15419
  DEVSY_MUTATION_LOCK_FILE,
14886
15420
  {
@@ -14900,7 +15434,7 @@ function runDevsyAction(action2, devsyId, force = false, repoPath) {
14900
15434
  const args = action2 === "delete" ? ["delete", devsyId, ...force ? ["--force"] : [], "--ignore-not-found"] : ["stop", devsyId];
14901
15435
  claimLifecycleEffect();
14902
15436
  const binding = repoPath ? networkProviderEffectOptions("devsy", devsyId, repoPath) : { args: [] };
14903
- const result = (0, import_node_child_process19.spawnSync)("devsy", ["workspace", ...args, ...binding.args], {
15437
+ const result = (0, import_node_child_process22.spawnSync)("devsy", ["workspace", ...args, ...binding.args], {
14904
15438
  encoding: "utf-8",
14905
15439
  ...binding.env ? { env: binding.env } : {}
14906
15440
  });
@@ -14917,7 +15451,7 @@ function inspectExactOwnership(devsyId, worktreePath) {
14917
15451
  }
14918
15452
  function checkoutVanished(worktreePath) {
14919
15453
  try {
14920
- import_node_fs30.default.lstatSync(worktreePath);
15454
+ import_node_fs32.default.lstatSync(worktreePath);
14921
15455
  return false;
14922
15456
  } catch (error) {
14923
15457
  return error.code === "ENOENT";
@@ -14987,13 +15521,13 @@ function stopOwnedDevsyWorkspace(devsyId, worktreePath) {
14987
15521
  function deleteOwnedDevsyWorkspace(devsyId, worktreePath) {
14988
15522
  return mutateOwnedDevsyWorkspace("delete", devsyId, worktreePath);
14989
15523
  }
14990
- var import_node_child_process19, import_node_fs30, import_node_path38, DEVSY_MUTATION_LOCK_FILE, DEVSY_MUTATION_WAIT_MS;
15524
+ var import_node_child_process22, import_node_fs32, import_node_path39, DEVSY_MUTATION_LOCK_FILE, DEVSY_MUTATION_WAIT_MS;
14991
15525
  var init_devsy_mutation = __esm({
14992
15526
  "src/core/devsy-mutation.ts"() {
14993
15527
  "use strict";
14994
- import_node_child_process19 = require("child_process");
14995
- import_node_fs30 = __toESM(require("fs"));
14996
- import_node_path38 = __toESM(require("path"));
15528
+ import_node_child_process22 = require("child_process");
15529
+ import_node_fs32 = __toESM(require("fs"));
15530
+ import_node_path39 = __toESM(require("path"));
14997
15531
  init_devsy_agent();
14998
15532
  init_devsy_workspaces();
14999
15533
  init_file_lock();
@@ -15002,7 +15536,7 @@ var init_devsy_mutation = __esm({
15002
15536
  init_network_provider_effect();
15003
15537
  init_reliability_context();
15004
15538
  init_router();
15005
- DEVSY_MUTATION_LOCK_FILE = import_node_path38.default.join(DEVROUTER_HOME, "devsy-mutation.lock");
15539
+ DEVSY_MUTATION_LOCK_FILE = import_node_path39.default.join(DEVROUTER_HOME, "devsy-mutation.lock");
15006
15540
  DEVSY_MUTATION_WAIT_MS = 18e5;
15007
15541
  }
15008
15542
  });
@@ -15079,15 +15613,15 @@ function assertFreshBootstrapRequest(identity2, options) {
15079
15613
  throw new Error("Fresh bootstrap refuses a retained runtime or provider registration.");
15080
15614
  qualifiedContext(readDevcontainerConfig(identity2.repoPath).source);
15081
15615
  }
15082
- var import_node_child_process20, import_node_crypto18, import_node_fs31, import_node_os6, import_node_path39, import_node_util11, import_yaml4;
15616
+ var import_node_child_process23, import_node_crypto21, import_node_fs33, import_node_os6, import_node_path40, import_node_util11, import_yaml4;
15083
15617
  var init_managed_fresh_bootstrap = __esm({
15084
15618
  "src/core/managed-fresh-bootstrap.ts"() {
15085
15619
  "use strict";
15086
- import_node_child_process20 = require("child_process");
15087
- import_node_crypto18 = require("crypto");
15088
- import_node_fs31 = __toESM(require("fs"));
15620
+ import_node_child_process23 = require("child_process");
15621
+ import_node_crypto21 = require("crypto");
15622
+ import_node_fs33 = __toESM(require("fs"));
15089
15623
  import_node_os6 = __toESM(require("os"));
15090
- import_node_path39 = __toESM(require("path"));
15624
+ import_node_path40 = __toESM(require("path"));
15091
15625
  import_node_util11 = require("util");
15092
15626
  import_yaml4 = __toESM(require("yaml"));
15093
15627
  init_devcontainer_config();
@@ -15108,289 +15642,6 @@ var init_managed_fresh_bootstrap = __esm({
15108
15642
  }
15109
15643
  });
15110
15644
 
15111
- // src/core/managed-host-ports.ts
15112
- function unavailableEntry(declaration, reason2) {
15113
- return { ...declaration, status: "unavailable", reason: reason2 };
15114
- }
15115
- function livePublishedBindings(containerId, target) {
15116
- const result = (0, import_node_child_process21.spawnSync)("docker", ["port", containerId, `${target}/tcp`], {
15117
- encoding: "utf-8",
15118
- timeout: PORT_LOOKUP_TIMEOUT_MS,
15119
- maxBuffer: PORT_LOOKUP_MAX_BUFFER,
15120
- ...networkDockerOptions()
15121
- });
15122
- if (result.error || result.status === null) return void 0;
15123
- if (result.status !== 0) return "";
15124
- return typeof result.stdout === "string" ? result.stdout : void 0;
15125
- }
15126
- function parseLoopbackBinding(output2) {
15127
- const lines = output2.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
15128
- if (lines.length === 0) return { reason: "not-published" };
15129
- if (lines.length > 1) return { reason: "ambiguous" };
15130
- const match = LOOPBACK_BINDING.exec(lines[0]);
15131
- if (!match) return { reason: "not-published" };
15132
- const port = Number(match[1]);
15133
- if (!Number.isInteger(port) || port < 1 || port > 65535) return { reason: "not-published" };
15134
- return { endpoint: { host: "127.0.0.1", port } };
15135
- }
15136
- function revalidateLiveOwner(containerId, input2, composeProject, composeFiles2, service) {
15137
- try {
15138
- const current = inspectWorkspaceContainers({ ids: [containerId] });
15139
- if (current.length !== 1 || current[0].id !== containerId) return "unavailable";
15140
- if (!hasExactComposeIdentity(current[0], {
15141
- repoPath: input2.repoPath,
15142
- service,
15143
- composeProject,
15144
- composeFiles: composeFiles2
15145
- })) {
15146
- return "unavailable";
15147
- }
15148
- return current[0].state.Running ? "available" : "not-running";
15149
- } catch {
15150
- return "unavailable";
15151
- }
15152
- }
15153
- function resolveManagedHostPorts(input2) {
15154
- const report = {};
15155
- const selectedServices = new Set(input2.selectedServices);
15156
- const selectedKeys = [];
15157
- for (const [key, declaration] of Object.entries(input2.declarations)) {
15158
- if (!selectedServices.has(declaration.service)) {
15159
- report[key] = { ...declaration, status: "inactive" };
15160
- continue;
15161
- }
15162
- selectedKeys.push(key);
15163
- report[key] = { ...declaration, status: "unavailable", reason: "unavailable" };
15164
- }
15165
- if (selectedKeys.length === 0) return report;
15166
- const composeProject = input2.composeProject;
15167
- const composeFiles2 = input2.composeFiles;
15168
- if (typeof composeProject !== "string" || composeProject.length === 0 || composeFiles2 === void 0 || composeFiles2.length === 0 || composeFiles2.some((file) => file.length === 0)) {
15169
- return report;
15170
- }
15171
- if (selectedKeys.some((key) => {
15172
- const target = input2.declarations[key].target;
15173
- return !Number.isInteger(target) || target < 1 || target > 65535;
15174
- })) {
15175
- return report;
15176
- }
15177
- const liveContainers = input2.containers === void 0;
15178
- let containers2 = input2.containers;
15179
- if (containers2 === void 0) {
15180
- try {
15181
- containers2 = inspectWorkspaceContainers({ composeProject });
15182
- } catch {
15183
- return report;
15184
- }
15185
- }
15186
- const publishedBindings = input2.publishedBindings ?? livePublishedBindings;
15187
- for (const key of selectedKeys) {
15188
- const declaration = input2.declarations[key];
15189
- let owners;
15190
- try {
15191
- owners = containers2.filter(
15192
- (container) => hasExactComposeIdentity(container, {
15193
- repoPath: input2.repoPath,
15194
- service: declaration.service,
15195
- composeProject,
15196
- composeFiles: composeFiles2
15197
- })
15198
- );
15199
- } catch {
15200
- report[key] = unavailableEntry(declaration, "unavailable");
15201
- continue;
15202
- }
15203
- if (owners.length === 0) {
15204
- report[key] = unavailableEntry(declaration, "not-found");
15205
- continue;
15206
- }
15207
- if (owners.length > 1) {
15208
- report[key] = unavailableEntry(declaration, "ambiguous");
15209
- continue;
15210
- }
15211
- const owner = owners[0];
15212
- if (!owner.state.Running) {
15213
- report[key] = unavailableEntry(declaration, "not-running");
15214
- continue;
15215
- }
15216
- let output2;
15217
- try {
15218
- output2 = publishedBindings(owner.id, declaration.target);
15219
- } catch {
15220
- output2 = void 0;
15221
- }
15222
- if (typeof output2 !== "string") {
15223
- report[key] = unavailableEntry(declaration, "unavailable");
15224
- continue;
15225
- }
15226
- const binding = parseLoopbackBinding(output2);
15227
- if (!binding.endpoint) {
15228
- report[key] = unavailableEntry(declaration, binding.reason ?? "unavailable");
15229
- continue;
15230
- }
15231
- if (liveContainers) {
15232
- const proof = revalidateLiveOwner(
15233
- owner.id,
15234
- input2,
15235
- composeProject,
15236
- composeFiles2,
15237
- declaration.service
15238
- );
15239
- if (proof !== "available") {
15240
- report[key] = unavailableEntry(declaration, proof);
15241
- continue;
15242
- }
15243
- }
15244
- report[key] = {
15245
- ...declaration,
15246
- status: "available",
15247
- endpoint: binding.endpoint
15248
- };
15249
- }
15250
- return report;
15251
- }
15252
- function managedHostPortDeclarationsIdentity(declarations) {
15253
- const keys2 = Object.keys(declarations).sort();
15254
- if (keys2.length === 0) return void 0;
15255
- const canonical = keys2.map((key) => [key, declarations[key].service, declarations[key].target]);
15256
- return (0, import_node_crypto19.createHash)("sha256").update(JSON.stringify(canonical)).digest("hex");
15257
- }
15258
- function assertManagedHostPortsAvailable(report) {
15259
- const unavailable = Object.entries(report).filter(([, entry]) => entry.status === "unavailable").map(([key]) => key).sort();
15260
- if (unavailable.length > 0) {
15261
- throw new Error(`Selected managed host ports are unavailable: ${unavailable.join(", ")}.`);
15262
- }
15263
- }
15264
- var import_node_child_process21, import_node_crypto19, PORT_LOOKUP_TIMEOUT_MS, PORT_LOOKUP_MAX_BUFFER, LOOPBACK_BINDING;
15265
- var init_managed_host_ports = __esm({
15266
- "src/core/managed-host-ports.ts"() {
15267
- "use strict";
15268
- import_node_child_process21 = require("child_process");
15269
- import_node_crypto19 = require("crypto");
15270
- init_devpod_environment();
15271
- init_network_effect_scope();
15272
- PORT_LOOKUP_TIMEOUT_MS = 1e4;
15273
- PORT_LOOKUP_MAX_BUFFER = 64 * 1024;
15274
- LOOPBACK_BINDING = /^127\.0\.0\.1:(\d+)$/;
15275
- }
15276
- });
15277
-
15278
- // src/core/managed-post-start.ts
15279
- function readRegularFileBytes(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);
15282
- }
15283
- function readRegularFile(filePath) {
15284
- return readRegularFileBytes(filePath)?.toString("utf-8");
15285
- }
15286
- function adapterFingerprint(adapter) {
15287
- return (0, import_node_crypto20.createHash)("sha256").update(adapter).digest("hex");
15288
- }
15289
- function resolveManagedPostStartPlan(repoPath) {
15290
- const adapterPath = import_node_path40.default.join(repoPath, MANAGED_ADAPTER_PATH);
15291
- const adapterBytes = readRegularFileBytes(adapterPath);
15292
- const adapterText = adapterBytes?.toString("utf-8");
15293
- const dockerfilePath = import_node_path40.default.join(repoPath, ".devcontainer", "Dockerfile");
15294
- const devcontainerPath = import_node_path40.default.join(repoPath, ".devcontainer", "devcontainer.json");
15295
- const dockerfile = readRegularFile(dockerfilePath) ?? "";
15296
- const devcontainer = readRegularFile(devcontainerPath) ?? "";
15297
- const devrouterPattern = [adapterText, dockerfile, devcontainer].filter((value) => value !== void 0).some(
15298
- (value) => value.includes("DEVROUTER_PROCESS_HELPER") || value.includes("devrouter-process")
15299
- );
15300
- if (adapterBytes === void 0) {
15301
- if (devrouterPattern) {
15302
- throw new Error(
15303
- "Devrouter lifecycle wiring is incomplete: add the managed post-start adapter or remove the stale devrouter-process references."
15304
- );
15305
- }
15306
- return { kind: "unmanaged" };
15307
- }
15308
- const adapter = adapterBytes.toString("utf-8");
15309
- if (!adapter.includes(MANAGED_MARKER)) {
15310
- if (devrouterPattern) {
15311
- throw new Error(
15312
- "Devrouter-looking post-start adapter is missing the 'devrouter:managed devcontainer' marker. Regenerate or migrate the devcontainer, then retry devrouter ensure."
15313
- );
15314
- }
15315
- return { kind: "unmanaged" };
15316
- }
15317
- assertManagedDevcontainerLifecycle(repoPath);
15318
- if (adapter.includes("DEVROUTER_PROCESS_HELPER")) {
15319
- return {
15320
- kind: "runtime",
15321
- adapterPath: MANAGED_ADAPTER_PATH,
15322
- adapterSha256: adapterFingerprint(adapterBytes),
15323
- adapterContents: adapterBytes
15324
- };
15325
- }
15326
- if (dockerfile.includes("devrouter-process") && devcontainer.includes("postStartCommand")) {
15327
- return { kind: "legacy" };
15328
- }
15329
- throw new Error(
15330
- "Managed post-start must use DEVROUTER_PROCESS_HELPER before removing the legacy image helper and postStartCommand. Regenerate or migrate the devcontainer, then retry devrouter ensure."
15331
- );
15332
- }
15333
- function assertSafeProcessName(name) {
15334
- if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(name)) {
15335
- throw new Error(`Managed process '${name}' is not a safe exact identifier.`);
15336
- }
15337
- }
15338
- function runManagedProcessAction(options) {
15339
- assertSafeProcessName(options.name);
15340
- const result = (0, import_node_child_process22.spawnSync)(
15341
- "docker",
15342
- [
15343
- "exec",
15344
- "--workdir",
15345
- options.container.workspacePath,
15346
- "--env",
15347
- `DEVROUTER_PROCESS_HELPER=${RUNTIME_HELPER_PATH}`,
15348
- options.container.id,
15349
- "bash",
15350
- "-c",
15351
- 'exec "$DEVROUTER_PROCESS_HELPER" "$1" --name "$2"',
15352
- "devrouter-process",
15353
- options.action,
15354
- options.name
15355
- ],
15356
- {
15357
- encoding: "utf-8",
15358
- stdio: options.action === "status" ? ["ignore", "pipe", "pipe"] : ["ignore", 2, "inherit"],
15359
- ...networkDockerOptions()
15360
- }
15361
- );
15362
- if (options.action === "stop") {
15363
- if (result.status !== 0) {
15364
- throw new Error(`Could not stop managed process '${options.name}'.`);
15365
- }
15366
- return "stopped";
15367
- }
15368
- try {
15369
- const parsed = JSON.parse(result.stdout.trim());
15370
- if (parsed.status === "running" || parsed.status === "stopped" || parsed.status === "foreign" || parsed.status === "drifted") {
15371
- return parsed.status;
15372
- }
15373
- } catch {
15374
- }
15375
- return "drifted";
15376
- }
15377
- var import_node_child_process22, import_node_crypto20, import_node_fs32, import_node_path40, MANAGED_MARKER, MANAGED_ADAPTER_PATH, RUNTIME_HELPER_PATH;
15378
- var init_managed_post_start = __esm({
15379
- "src/core/managed-post-start.ts"() {
15380
- "use strict";
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"));
15385
- init_devcontainer_config();
15386
- init_managed_host_ports();
15387
- init_network_effect_scope();
15388
- MANAGED_MARKER = "devrouter:managed devcontainer";
15389
- MANAGED_ADAPTER_PATH = ".devcontainer/post-start.sh";
15390
- RUNTIME_HELPER_PATH = "/tmp/devrouter/bin/devrouter-process";
15391
- }
15392
- });
15393
-
15394
15645
  // src/core/recovery-budget.ts
15395
15646
  function recoverySelectors(input2) {
15396
15647
  const declared = new Set(input2.services);
@@ -16247,14 +16498,14 @@ var init_reliability_model = __esm({
16247
16498
 
16248
16499
  // src/core/lifecycle-diagnostics.ts
16249
16500
  function privateDirectory(directory) {
16250
- const stat = import_node_fs33.default.lstatSync(directory);
16501
+ const stat = import_node_fs34.default.lstatSync(directory);
16251
16502
  return stat.isDirectory() && stat.uid === process.getuid?.() && (stat.mode & 63) === 0;
16252
16503
  }
16253
16504
  function lifecycleDiagnosticsPath(identity2) {
16254
16505
  return import_node_path41.default.join(
16255
16506
  DEVROUTER_HOME,
16256
16507
  "lifecycle-diagnostics",
16257
- `${(0, import_node_crypto21.createHash)("sha256").update(identity2.repoPath).digest("hex")}.json`
16508
+ `${(0, import_node_crypto22.createHash)("sha256").update(identity2.repoPath).digest("hex")}.json`
16258
16509
  );
16259
16510
  }
16260
16511
  function readReceipt(identity2) {
@@ -16262,15 +16513,15 @@ function readReceipt(identity2) {
16262
16513
  try {
16263
16514
  const file = lifecycleDiagnosticsPath(identity2);
16264
16515
  if (!privateDirectory(import_node_path41.default.dirname(file))) return void 0;
16265
- descriptor = import_node_fs33.default.openSync(
16516
+ descriptor = import_node_fs34.default.openSync(
16266
16517
  file,
16267
- import_node_fs33.default.constants.O_RDONLY | import_node_fs33.default.constants.O_NOFOLLOW | import_node_fs33.default.constants.O_NONBLOCK
16518
+ import_node_fs34.default.constants.O_RDONLY | import_node_fs34.default.constants.O_NOFOLLOW | import_node_fs34.default.constants.O_NONBLOCK
16268
16519
  );
16269
- const stat = import_node_fs33.default.fstatSync(descriptor);
16520
+ const stat = import_node_fs34.default.fstatSync(descriptor);
16270
16521
  if (!stat.isFile() || stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0 || stat.size > MAX_RECEIPT_BYTES)
16271
16522
  return void 0;
16272
16523
  const buffer = Buffer.alloc(MAX_RECEIPT_BYTES + 1);
16273
- const size = import_node_fs33.default.readSync(descriptor, buffer, 0, buffer.length, 0);
16524
+ const size = import_node_fs34.default.readSync(descriptor, buffer, 0, buffer.length, 0);
16274
16525
  if (size > MAX_RECEIPT_BYTES) return void 0;
16275
16526
  const receipt = JSON.parse(buffer.subarray(0, size).toString("utf8"));
16276
16527
  const timestamp = (value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
@@ -16287,7 +16538,7 @@ function readReceipt(identity2) {
16287
16538
  } catch {
16288
16539
  return void 0;
16289
16540
  } finally {
16290
- if (descriptor !== void 0) import_node_fs33.default.closeSync(descriptor);
16541
+ if (descriptor !== void 0) import_node_fs34.default.closeSync(descriptor);
16291
16542
  }
16292
16543
  }
16293
16544
  function readLifecycleDiagnostics(identity2, now = Date.now()) {
@@ -16325,12 +16576,12 @@ function readLifecycleDiagnostics(identity2, now = Date.now()) {
16325
16576
  return void 0;
16326
16577
  }
16327
16578
  }
16328
- var import_node_crypto21, import_node_fs33, import_node_path41, import_node_util12, MAX_TAIL_BYTES, MAX_RECEIPT_BYTES, PACKAGE_TASK, LifecycleDiagnosticRecorder;
16579
+ var import_node_crypto22, import_node_fs34, import_node_path41, import_node_util12, MAX_TAIL_BYTES, MAX_RECEIPT_BYTES, PACKAGE_TASK, LifecycleDiagnosticRecorder;
16329
16580
  var init_lifecycle_diagnostics = __esm({
16330
16581
  "src/core/lifecycle-diagnostics.ts"() {
16331
16582
  "use strict";
16332
- import_node_crypto21 = require("crypto");
16333
- import_node_fs33 = __toESM(require("fs"));
16583
+ import_node_crypto22 = require("crypto");
16584
+ import_node_fs34 = __toESM(require("fs"));
16334
16585
  import_node_path41 = __toESM(require("path"));
16335
16586
  import_node_util12 = require("util");
16336
16587
  init_atomic_file();
@@ -16426,7 +16677,7 @@ var init_lifecycle_diagnostics = __esm({
16426
16677
  this.receipt.output = chunks;
16427
16678
  this.receipt.updatedAtMs = Date.now();
16428
16679
  const file = lifecycleDiagnosticsPath(this.request.identity);
16429
- import_node_fs33.default.mkdirSync(import_node_path41.default.dirname(file), { recursive: true, mode: 448 });
16680
+ import_node_fs34.default.mkdirSync(import_node_path41.default.dirname(file), { recursive: true, mode: 448 });
16430
16681
  if (!privateDirectory(import_node_path41.default.dirname(file))) return;
16431
16682
  writeFileAtomically(file, `${JSON.stringify(this.receipt)}
16432
16683
  `);
@@ -16460,11 +16711,11 @@ function workerGroupAbsent(pid) {
16460
16711
  if (process.platform !== "linux") return false;
16461
16712
  let observedZombie = false;
16462
16713
  try {
16463
- for (const entry of import_node_fs34.default.readdirSync("/proc")) {
16714
+ for (const entry of import_node_fs35.default.readdirSync("/proc")) {
16464
16715
  if (!/^\d+$/.test(entry)) continue;
16465
16716
  let stat;
16466
16717
  try {
16467
- stat = import_node_fs34.default.readFileSync(`/proc/${entry}/stat`, "utf8");
16718
+ stat = import_node_fs35.default.readFileSync(`/proc/${entry}/stat`, "utf8");
16468
16719
  } catch (error) {
16469
16720
  if (error.code === "ENOENT") continue;
16470
16721
  return false;
@@ -16496,9 +16747,9 @@ async function runLifecycleWorker(request2, supervision, beforeAdmission) {
16496
16747
  if (process.platform === "win32")
16497
16748
  throw new Error("Lifecycle workers require POSIX process-group ownership.");
16498
16749
  const workerPath = import_node_path42.default.join(__dirname, "devrouter-lifecycle-worker.js");
16499
- if (!import_node_fs34.default.existsSync(workerPath))
16750
+ if (!import_node_fs35.default.existsSync(workerPath))
16500
16751
  throw new Error("The packaged lifecycle worker is missing; rebuild the CLI.");
16501
- const child = (0, import_node_child_process23.fork)(workerPath, [], {
16752
+ const child = (0, import_node_child_process24.fork)(workerPath, [], {
16502
16753
  detached: true,
16503
16754
  stdio: supervision ? ["ignore", "pipe", "pipe", "ipc"] : request2.kind === "ensure" ? ["inherit", "pipe", "pipe", "ipc"] : ["inherit", "inherit", "inherit", "ipc"],
16504
16755
  execArgv: []
@@ -16787,15 +17038,15 @@ async function runLifecycleWorker(request2, supervision, beforeAdmission) {
16787
17038
  }
16788
17039
  }
16789
17040
  function newLifecycleIds() {
16790
- return { requestId: (0, import_node_crypto22.randomUUID)(), operationId: (0, import_node_crypto22.randomUUID)(), workerId: (0, import_node_crypto22.randomUUID)() };
17041
+ return { requestId: (0, import_node_crypto23.randomUUID)(), operationId: (0, import_node_crypto23.randomUUID)(), workerId: (0, import_node_crypto23.randomUUID)() };
16791
17042
  }
16792
- var import_node_child_process23, import_node_crypto22, import_node_fs34, import_node_path42, OUTPUT_BUFFER_LIMIT, DEFAULT_OUTPUT_PAGE_BYTES, LifecycleOutput, LifecycleWorkerAdmissionBusyError;
17043
+ var import_node_child_process24, import_node_crypto23, import_node_fs35, import_node_path42, OUTPUT_BUFFER_LIMIT, DEFAULT_OUTPUT_PAGE_BYTES, LifecycleOutput, LifecycleWorkerAdmissionBusyError;
16793
17044
  var init_reliability_worker = __esm({
16794
17045
  "src/core/reliability-worker.ts"() {
16795
17046
  "use strict";
16796
- import_node_child_process23 = require("child_process");
16797
- import_node_crypto22 = require("crypto");
16798
- import_node_fs34 = __toESM(require("fs"));
17047
+ import_node_child_process24 = require("child_process");
17048
+ import_node_crypto23 = require("crypto");
17049
+ import_node_fs35 = __toESM(require("fs"));
16799
17050
  import_node_path42 = __toESM(require("path"));
16800
17051
  init_file_lock();
16801
17052
  init_lifecycle_diagnostics();
@@ -16915,7 +17166,7 @@ var init_reliability_worker = __esm({
16915
17166
 
16916
17167
  // src/core/devpod-mutation.ts
16917
17168
  function withMutationLock2(activity, target, operation) {
16918
- import_node_fs35.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
17169
+ import_node_fs36.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
16919
17170
  return withFileLockSync(
16920
17171
  DEVPOD_MUTATION_LOCK_FILE,
16921
17172
  {
@@ -16935,7 +17186,7 @@ function runDevpodAction(action2, devpodId, force = false, repoPath) {
16935
17186
  const args = action2 === "delete" ? [action2, devpodId, ...force ? ["--force"] : [], "--ignore-not-found"] : [action2, devpodId];
16936
17187
  claimLifecycleEffect();
16937
17188
  const binding = repoPath ? networkProviderEffectOptions("devpod", devpodId, repoPath) : { args: [] };
16938
- const result = (0, import_node_child_process24.spawnSync)("devpod", [...args, ...binding.args], {
17189
+ const result = (0, import_node_child_process25.spawnSync)("devpod", [...args, ...binding.args], {
16939
17190
  encoding: "utf-8",
16940
17191
  ...binding.env ? { env: binding.env } : {}
16941
17192
  });
@@ -17005,12 +17256,12 @@ function deleteOwnedDevpodWorkspace(devpodId, worktreePath) {
17005
17256
  resetWorkspaceRuntimeCaches();
17006
17257
  return result;
17007
17258
  }
17008
- var import_node_child_process24, import_node_fs35, import_node_path43, DEVPOD_MUTATION_LOCK_FILE, DEVPOD_MUTATION_WAIT_MS;
17259
+ var import_node_child_process25, import_node_fs36, import_node_path43, DEVPOD_MUTATION_LOCK_FILE, DEVPOD_MUTATION_WAIT_MS;
17009
17260
  var init_devpod_mutation = __esm({
17010
17261
  "src/core/devpod-mutation.ts"() {
17011
17262
  "use strict";
17012
- import_node_child_process24 = require("child_process");
17013
- import_node_fs35 = __toESM(require("fs"));
17263
+ import_node_child_process25 = require("child_process");
17264
+ import_node_fs36 = __toESM(require("fs"));
17014
17265
  import_node_path43 = __toESM(require("path"));
17015
17266
  init_devpod_workspaces();
17016
17267
  init_devsy_mutation();
@@ -17121,7 +17372,7 @@ function hostPortBindingsConflict(desired, holder) {
17121
17372
  (desired.hostIp === null || holder.hostIp === null || desired.hostIp === holder.hostIp);
17122
17373
  }
17123
17374
  function runDocker(args) {
17124
- const result = (0, import_node_child_process25.spawnSync)("docker", args, {
17375
+ const result = (0, import_node_child_process26.spawnSync)("docker", args, {
17125
17376
  ...networkDockerOptions(),
17126
17377
  encoding: "utf-8",
17127
17378
  timeout: DOCKER_TIMEOUT_MS,
@@ -17191,7 +17442,7 @@ function listHolderSnapshots() {
17191
17442
  }
17192
17443
  function renderManagedComposeModel(plan, workspace) {
17193
17444
  const fileArgs = plan.composeFiles.flatMap((file) => ["-f", file]);
17194
- const result = (0, import_node_child_process25.spawnSync)(
17445
+ const result = (0, import_node_child_process26.spawnSync)(
17195
17446
  "docker",
17196
17447
  [
17197
17448
  "compose",
@@ -17284,11 +17535,11 @@ function detectHostPortClaimConflicts(options) {
17284
17535
  }
17285
17536
  return conflicts;
17286
17537
  }
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;
17538
+ var import_node_child_process26, 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;
17288
17539
  var init_host_port_claims = __esm({
17289
17540
  "src/core/host-port-claims.ts"() {
17290
17541
  "use strict";
17291
- import_node_child_process25 = require("child_process");
17542
+ import_node_child_process26 = require("child_process");
17292
17543
  import_node_path44 = __toESM(require("path"));
17293
17544
  init_devcontainer_profile();
17294
17545
  init_network_effect_scope();
@@ -17331,7 +17582,7 @@ function probeHttpRoute(host, options = {}) {
17331
17582
  args.push("--cacert", CERT_FILE);
17332
17583
  }
17333
17584
  args.push(url);
17334
- const result = (0, import_node_child_process26.spawnSync)("curl", args, { encoding: "utf-8" });
17585
+ const result = (0, import_node_child_process27.spawnSync)("curl", args, { encoding: "utf-8" });
17335
17586
  const stdout = result.stdout?.trim() ?? "";
17336
17587
  const status = /^\d{3}$/.test(stdout) ? Number(stdout) : void 0;
17337
17588
  const ok = result.status === 0 && status !== void 0 && status >= 100 && status < 500;
@@ -17366,7 +17617,7 @@ function probeHttpReadinessSync(host, readiness, maxTimeSeconds, repoPath) {
17366
17617
  }
17367
17618
  let result;
17368
17619
  try {
17369
- result = (0, import_node_child_process26.spawnSync)("curl", args, {
17620
+ result = (0, import_node_child_process27.spawnSync)("curl", args, {
17370
17621
  encoding: "utf-8",
17371
17622
  maxBuffer: CONTRACT_MAX_BUFFER,
17372
17623
  timeout: Math.ceil(parentTimeoutMs)
@@ -17448,11 +17699,11 @@ function transportFailure() {
17448
17699
  classification: "transport"
17449
17700
  };
17450
17701
  }
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;
17702
+ var import_node_child_process27, DEFAULT_MAX_TIME_SECONDS, CONTROLLER_CURL_MAX_TIME_SECONDS, PARENT_TIMEOUT_PADDING_MS, CONTRACT_MAX_BUFFER, MIME_TOKEN_RE2;
17452
17703
  var init_http_route_probe = __esm({
17453
17704
  "src/core/http-route-probe.ts"() {
17454
17705
  "use strict";
17455
- import_node_child_process26 = require("child_process");
17706
+ import_node_child_process27 = require("child_process");
17456
17707
  init_router();
17457
17708
  init_tls();
17458
17709
  DEFAULT_MAX_TIME_SECONDS = 5;
@@ -17464,12 +17715,12 @@ var init_http_route_probe = __esm({
17464
17715
  });
17465
17716
 
17466
17717
  // src/core/managed-host-preparation.ts
17467
- var import_node_child_process27, import_node_fs36;
17718
+ var import_node_child_process28, import_node_fs37;
17468
17719
  var init_managed_host_preparation = __esm({
17469
17720
  "src/core/managed-host-preparation.ts"() {
17470
17721
  "use strict";
17471
- import_node_child_process27 = require("child_process");
17472
- import_node_fs36 = __toESM(require("fs"));
17722
+ import_node_child_process28 = require("child_process");
17723
+ import_node_fs37 = __toESM(require("fs"));
17473
17724
  init_repo_config();
17474
17725
  }
17475
17726
  });
@@ -17594,7 +17845,7 @@ function classifyManagedMountNesting(configured, effectiveDestinations, containe
17594
17845
  };
17595
17846
  }
17596
17847
  function readContainerMountinfo(containerId, options) {
17597
- const result = (0, import_node_child_process28.spawnSync)("docker", ["exec", containerId, "cat", "/proc/self/mountinfo"], {
17848
+ const result = (0, import_node_child_process29.spawnSync)("docker", ["exec", containerId, "cat", "/proc/self/mountinfo"], {
17598
17849
  encoding: "utf-8",
17599
17850
  timeout: options.timeoutMs ?? 5e3,
17600
17851
  stdio: ["ignore", "pipe", "pipe"],
@@ -17609,11 +17860,11 @@ function observeManagedMountNesting(container, options = {}) {
17609
17860
  const effective = destinations.some((_destination, index) => isNestedIndex(destinations, index)) ? readContainerMountinfo(container.id, options) : null;
17610
17861
  return classifyManagedMountNesting(container.mounts, effective, container.id);
17611
17862
  }
17612
- var import_node_child_process28, NESTED_MOUNT_REASON, UNREADABLE_MOUNT_REASON;
17863
+ var import_node_child_process29, NESTED_MOUNT_REASON, UNREADABLE_MOUNT_REASON;
17613
17864
  var init_managed_mount_nesting = __esm({
17614
17865
  "src/core/managed-mount-nesting.ts"() {
17615
17866
  "use strict";
17616
- import_node_child_process28 = require("child_process");
17867
+ import_node_child_process29 = require("child_process");
17617
17868
  init_network_effect_scope();
17618
17869
  NESTED_MOUNT_REASON = "no configured mount is nested inside another configured mount";
17619
17870
  UNREADABLE_MOUNT_REASON = "the container's own mount table could not be read";
@@ -18175,7 +18426,7 @@ function readDocker(endpoint, args, timeoutMs) {
18175
18426
  const env = { ...process.env };
18176
18427
  delete env.DOCKER_CONTEXT;
18177
18428
  delete env.DOCKER_HOST;
18178
- const result = (0, import_node_child_process29.spawnSync)("docker", ["--host", endpoint, ...args], {
18429
+ const result = (0, import_node_child_process30.spawnSync)("docker", ["--host", endpoint, ...args], {
18179
18430
  env,
18180
18431
  encoding: "utf-8",
18181
18432
  timeout: timeoutMs,
@@ -18337,11 +18588,11 @@ function collectDockerNetworkInventory(options = {}, dependencies = {}) {
18337
18588
  }
18338
18589
  return result;
18339
18590
  }
18340
- var import_node_child_process29, NetworkInventoryError, MAX_IDS, MAX_OUTPUT_BYTES, FULL_ID, NETWORK_TEMPLATE, CONTAINER_TEMPLATE;
18591
+ var import_node_child_process30, NetworkInventoryError, MAX_IDS, MAX_OUTPUT_BYTES, FULL_ID, NETWORK_TEMPLATE, CONTAINER_TEMPLATE;
18341
18592
  var init_network_inventory = __esm({
18342
18593
  "src/core/network-inventory.ts"() {
18343
18594
  "use strict";
18344
- import_node_child_process29 = require("child_process");
18595
+ import_node_child_process30 = require("child_process");
18345
18596
  init_devpod_environment();
18346
18597
  NetworkInventoryError = class extends Error {
18347
18598
  };
@@ -18669,26 +18920,26 @@ function hasLocalDockerNetworkNamespace(endpoint, procRoot = "/proc") {
18669
18920
  try {
18670
18921
  if (!endpoint.startsWith("unix:///")) return false;
18671
18922
  const socket = endpoint.slice("unix://".length);
18672
- const table = import_node_fs37.default.readFileSync(import_node_path45.default.join(procRoot, "net/unix"), "utf8");
18923
+ const table = import_node_fs38.default.readFileSync(import_node_path45.default.join(procRoot, "net/unix"), "utf8");
18673
18924
  if (Buffer.byteLength(table) > 1024 * 1024) return false;
18674
18925
  const rows2 = table.split("\n").map((line) => line.trim().split(/\s+/)).filter((row) => row[7] === socket && row[3] === "00010000");
18675
18926
  if (rows2.length !== 1 || !/^\d+$/.test(rows2[0][6])) return false;
18676
18927
  const inode = `socket:[${rows2[0][6]}]`;
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));
18928
+ const ownNamespace = import_node_fs38.default.readlinkSync(import_node_path45.default.join(procRoot, "self/ns/net"));
18929
+ const pids = import_node_fs38.default.readdirSync(procRoot).filter((name) => /^\d+$/.test(name));
18679
18930
  if (pids.length > 4096) return false;
18680
18931
  for (const pid of pids) {
18681
18932
  if (Date.now() > deadline) return false;
18682
18933
  try {
18683
18934
  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"));
18935
+ if (import_node_path45.default.basename(import_node_fs38.default.readlinkSync(import_node_path45.default.join(root, "exe"))) !== "dockerd") continue;
18936
+ if (import_node_fs38.default.readlinkSync(import_node_path45.default.join(root, "ns/net")) !== ownNamespace) continue;
18937
+ const descriptors = import_node_fs38.default.readdirSync(import_node_path45.default.join(root, "fd"));
18687
18938
  if (descriptors.length > 4096) continue;
18688
18939
  if (descriptors.some((fd) => {
18689
18940
  if (Date.now() > deadline) return false;
18690
18941
  try {
18691
- return import_node_fs37.default.readlinkSync(import_node_path45.default.join(root, "fd", fd)) === inode;
18942
+ return import_node_fs38.default.readlinkSync(import_node_path45.default.join(root, "fd", fd)) === inode;
18692
18943
  } catch {
18693
18944
  return false;
18694
18945
  }
@@ -18702,11 +18953,11 @@ function hasLocalDockerNetworkNamespace(endpoint, procRoot = "/proc") {
18702
18953
  }
18703
18954
  return false;
18704
18955
  }
18705
- var import_node_fs37, import_node_path45;
18956
+ var import_node_fs38, import_node_path45;
18706
18957
  var init_network_local_daemon = __esm({
18707
18958
  "src/core/network-local-daemon.ts"() {
18708
18959
  "use strict";
18709
- import_node_fs37 = __toESM(require("fs"));
18960
+ import_node_fs38 = __toESM(require("fs"));
18710
18961
  import_node_path45 = __toESM(require("path"));
18711
18962
  }
18712
18963
  });
@@ -18735,7 +18986,7 @@ function parseLinuxNetworkRoutes(output2) {
18735
18986
  function collectNetworkRoutes(options) {
18736
18987
  if ((options.platform ?? process.platform) !== "linux" || !hasLocalDockerNetworkNamespace(options.endpoint))
18737
18988
  return { status: "unknown", routes: [] };
18738
- const result = (0, import_node_child_process30.spawnSync)("ip", ["-j", "-4", "route", "show", "table", "all"], {
18989
+ const result = (0, import_node_child_process31.spawnSync)("ip", ["-j", "-4", "route", "show", "table", "all"], {
18739
18990
  encoding: "utf8",
18740
18991
  timeout: 5e3,
18741
18992
  maxBuffer: 1024 * 1024
@@ -18743,11 +18994,11 @@ function collectNetworkRoutes(options) {
18743
18994
  if (result.error || result.status !== 0) return { status: "unknown", routes: [] };
18744
18995
  return parseLinuxNetworkRoutes(result.stdout);
18745
18996
  }
18746
- var import_node_child_process30, import_node_net3;
18997
+ var import_node_child_process31, import_node_net3;
18747
18998
  var init_network_routes = __esm({
18748
18999
  "src/core/network-routes.ts"() {
18749
19000
  "use strict";
18750
- import_node_child_process30 = require("child_process");
19001
+ import_node_child_process31 = require("child_process");
18751
19002
  import_node_net3 = require("net");
18752
19003
  init_network_capacity();
18753
19004
  init_network_local_daemon();
@@ -18915,11 +19166,11 @@ function extendDockerComposeFile(nativeConfig, overlayPath) {
18915
19166
  const config = { ...nativeConfig, dockerComposeFile };
18916
19167
  return { status: "ready", config, reasons: [] };
18917
19168
  }
18918
- var import_node_crypto23;
19169
+ var import_node_crypto24;
18919
19170
  var init_network_compose = __esm({
18920
19171
  "src/core/network-compose.ts"() {
18921
19172
  "use strict";
18922
- import_node_crypto23 = require("crypto");
19173
+ import_node_crypto24 = require("crypto");
18923
19174
  }
18924
19175
  });
18925
19176
 
@@ -18928,7 +19179,7 @@ function prepareNetworkComposeFiles(plan, subnet) {
18928
19179
  const overlay = deriveComposeNetworkOverlay(subnet);
18929
19180
  if (overlay.status !== "ready" || !overlay.overlay) throw new Error("Network subnet is invalid.");
18930
19181
  const overlayPath = import_node_path46.default.join(plan.composeDirectory, OVERLAY);
18931
- const ignored = (0, import_node_child_process31.spawnSync)("git", [
19182
+ const ignored = (0, import_node_child_process32.spawnSync)("git", [
18932
19183
  "-C",
18933
19184
  plan.composeDirectory,
18934
19185
  "check-ignore",
@@ -18942,9 +19193,9 @@ function prepareNetworkComposeFiles(plan, subnet) {
18942
19193
  const bytes2 = `${MARKER}
18943
19194
  ${import_yaml5.default.stringify(overlay.overlay)}`;
18944
19195
  const assertOwnership = () => {
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)
19196
+ if (!import_node_fs39.default.existsSync(overlayPath)) return;
19197
+ const stat = import_node_fs39.default.lstatSync(overlayPath);
19198
+ if (!stat.isFile() || stat.size > MAX_BYTES3 || import_node_fs39.default.readFileSync(overlayPath, "utf8") !== bytes2)
18948
19199
  throw new Error("Retained network overlay changed; repair is required.");
18949
19200
  };
18950
19201
  assertOwnership();
@@ -18958,7 +19209,7 @@ ${JSON.stringify(extended.config, null, 2)}
18958
19209
  ...plan,
18959
19210
  contents,
18960
19211
  composeFiles: [...plan.composeFiles, overlayPath],
18961
- effectiveConfigSha256: (0, import_node_crypto24.createHash)("sha256").update(contents).digest("hex")
19212
+ effectiveConfigSha256: (0, import_node_crypto25.createHash)("sha256").update(contents).digest("hex")
18962
19213
  };
18963
19214
  return {
18964
19215
  plan: result,
@@ -18968,13 +19219,13 @@ ${JSON.stringify(extended.config, null, 2)}
18968
19219
  }
18969
19220
  };
18970
19221
  }
18971
- var import_node_child_process31, import_node_crypto24, import_node_fs38, import_node_path46, import_yaml5, OVERLAY, MARKER, MAX_BYTES3;
19222
+ var import_node_child_process32, import_node_crypto25, import_node_fs39, import_node_path46, import_yaml5, OVERLAY, MARKER, MAX_BYTES3;
18972
19223
  var init_network_compose_files = __esm({
18973
19224
  "src/core/network-compose-files.ts"() {
18974
19225
  "use strict";
18975
- import_node_child_process31 = require("child_process");
18976
- import_node_crypto24 = require("crypto");
18977
- import_node_fs38 = __toESM(require("fs"));
19226
+ import_node_child_process32 = require("child_process");
19227
+ import_node_crypto25 = require("crypto");
19228
+ import_node_fs39 = __toESM(require("fs"));
18978
19229
  import_node_path46 = __toESM(require("path"));
18979
19230
  import_yaml5 = __toESM(require("yaml"));
18980
19231
  init_atomic_file();
@@ -18987,11 +19238,11 @@ var init_network_compose_files = __esm({
18987
19238
  });
18988
19239
 
18989
19240
  // src/core/network-connected-route.ts
18990
- var import_node_child_process32;
19241
+ var import_node_child_process33;
18991
19242
  var init_network_connected_route = __esm({
18992
19243
  "src/core/network-connected-route.ts"() {
18993
19244
  "use strict";
18994
- import_node_child_process32 = require("child_process");
19245
+ import_node_child_process33 = require("child_process");
18995
19246
  init_network_capacity();
18996
19247
  }
18997
19248
  });
@@ -19007,11 +19258,11 @@ var init_network_lifecycle = __esm({
19007
19258
  });
19008
19259
 
19009
19260
  // src/core/network-managed.ts
19010
- var import_node_crypto25;
19261
+ var import_node_crypto26;
19011
19262
  var init_network_managed = __esm({
19012
19263
  "src/core/network-managed.ts"() {
19013
19264
  "use strict";
19014
- import_node_crypto25 = require("crypto");
19265
+ import_node_crypto26 = require("crypto");
19015
19266
  init_devpod_environment();
19016
19267
  init_network_capacity();
19017
19268
  init_network_claim_lookup();
@@ -19081,12 +19332,12 @@ function resolveLinkedTarget(repoPath) {
19081
19332
  gitCommonDir: resolveGitCommonDir(repoPath)
19082
19333
  };
19083
19334
  }
19084
- var import_node_child_process33, import_node_fs39, import_node_path47;
19335
+ var import_node_child_process34, import_node_fs40, import_node_path47;
19085
19336
  var init_workspace_ensure = __esm({
19086
19337
  "src/core/workspace-ensure.ts"() {
19087
19338
  "use strict";
19088
- import_node_child_process33 = require("child_process");
19089
- import_node_fs39 = __toESM(require("fs"));
19339
+ import_node_child_process34 = require("child_process");
19340
+ import_node_fs40 = __toESM(require("fs"));
19090
19341
  import_node_path47 = __toESM(require("path"));
19091
19342
  init_devcontainer_profile();
19092
19343
  init_devpod_environment();
@@ -19110,6 +19361,7 @@ var init_workspace_ensure = __esm({
19110
19361
  init_network_managed();
19111
19362
  init_network_recovery();
19112
19363
  init_reliability_lifecycle();
19364
+ init_reliability_operation_store();
19113
19365
  init_repo_config();
19114
19366
  init_route_publication();
19115
19367
  init_router();
@@ -19199,6 +19451,29 @@ function preflightRuntimeVariant(repoPath, options) {
19199
19451
  }
19200
19452
  return variant;
19201
19453
  }
19454
+ function preflightAppReplacement(identity2, options) {
19455
+ if (!options.replaceApp) return void 0;
19456
+ if (identity2.provider !== "devsy" || options.repair || options.initializeNew)
19457
+ throw new Error("--replace-app requires managed Devsy without --repair or --initialize-new.");
19458
+ const record6 = readReliabilityOperation(identity2);
19459
+ if (record6?.version !== 1 || record6.enrollment || record6.capacity || capacityPolicyTargets(identity2.repoPath) || record6.state.executionPolicy !== "manual" || record6.worker || record6.state.desired !== "stopped-by-user" || record6.state.phase === "stopping" || !record6.state.stopProof.workloadsStopped || !record6.state.stopProof.routesRemoved || record6.state.operation && !record6.state.operation.drained || findOwnedNetworkClaim(identity2.repoPath) || readAppReplacementAttempt(identity2))
19460
+ throw new Error(
19461
+ "App replacement requires a proved manual canonical stop without an earlier replacement attempt, enrollment or network allocation."
19462
+ );
19463
+ const state = readManagedRuntimeState(identity2.repoPath, identity2.workspace ?? void 0);
19464
+ if (!state || options.profile !== void 0 && options.profile !== state.profile || (options.variant ?? "default") !== (state.variant ?? "default") || listHostRouteState().some((r) => sameWorkspacePath(r.repoPath, identity2.repoPath)))
19465
+ throw new Error(
19466
+ "App replacement requires the retained profile, variant and zero checkout routes."
19467
+ );
19468
+ const proof = withMutationLock(
19469
+ "Prove stopped app replacement",
19470
+ identity2.repoPath,
19471
+ () => captureManagedAppReplacementProof(state)
19472
+ );
19473
+ if (!(0, import_node_util13.isDeepStrictEqual)(readReliabilityOperation(identity2), record6))
19474
+ throw new Error("Stopped replacement generation changed during preflight.");
19475
+ return { stoppedFence: reliabilityFence(record6.state), preflightSha256: proof.digest };
19476
+ }
19202
19477
  function prepareManagedLifecycleOperation(input2) {
19203
19478
  if (input2.kind === "exec" ? !Array.isArray(input2.command) || input2.command.length === 0 || input2.command.some((arg) => typeof arg !== "string" || arg.includes("\0")) : input2.command !== void 0)
19204
19479
  throw new Error("Invalid managed operation command.");
@@ -19620,6 +19895,9 @@ async function superviseLifecycle(kind, repoPath, options = {}, command, hooks =
19620
19895
  provider: resolveWorkspaceRuntimeOrDefault(repoPath)
19621
19896
  };
19622
19897
  const ids2 = newLifecycleIds();
19898
+ if (options.replaceApp && kind !== "ensure")
19899
+ throw new Error("App replacement is an ensure intent.");
19900
+ const replacement = preflightAppReplacement(identity2, options);
19623
19901
  const copiedOptions = { ...options };
19624
19902
  if (kind === "ensure") assertFreshBootstrapRequest(identity2, options);
19625
19903
  const copiedCommand = command ? [...command] : void 0;
@@ -19745,6 +20023,7 @@ async function superviseLifecycle(kind, repoPath, options = {}, command, hooks =
19745
20023
  ...ids2,
19746
20024
  fence: { ...expectedFence },
19747
20025
  options: copiedOptions,
20026
+ ...replacement ? { replacement } : {},
19748
20027
  ...copiedCommand ? { command: copiedCommand } : {},
19749
20028
  ...retainedExecProof ? { retainedExecProof } : {},
19750
20029
  admission: {
@@ -19905,7 +20184,7 @@ async function superviseThroughController(input2) {
19905
20184
  try {
19906
20185
  const bind = () => observeControllerBinding(directory, {
19907
20186
  path: input2.repoPath,
19908
- session: (0, import_node_crypto26.randomUUID)(),
20187
+ session: (0, import_node_crypto27.randomUUID)(),
19909
20188
  profile: input2.profile ?? "full",
19910
20189
  // The protocol requires at least one readiness selector, and every
19911
20190
  // supervised operation depends on runtime readiness; "runtime" is the
@@ -20061,11 +20340,11 @@ function bindLifecycleCapacity(request2, binding, directory = import_node_path48
20061
20340
  stepRecord(record6, { ...request2.fence, type: "admission", result: "admitted" });
20062
20341
  });
20063
20342
  }
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;
20343
+ var import_node_crypto27, import_node_path48, import_node_util13, activeWorker, lockHeld, BUSY_LIFECYCLE_WAIT_MS, BUSY_LIFECYCLE_POLL_MS, CAPACITY_CLIENT_WAIT_MS, BUSY_LIFECYCLE_PROGRESS_MS;
20065
20344
  var init_reliability_lifecycle = __esm({
20066
20345
  "src/core/reliability-lifecycle.ts"() {
20067
20346
  "use strict";
20068
- import_node_crypto26 = require("crypto");
20347
+ import_node_crypto27 = require("crypto");
20069
20348
  import_node_path48 = __toESM(require("path"));
20070
20349
  import_node_util13 = require("util");
20071
20350
  init_capacity_accounting();
@@ -20087,6 +20366,7 @@ var init_reliability_lifecycle = __esm({
20087
20366
  init_managed_post_start();
20088
20367
  init_managed_runtime_state();
20089
20368
  init_managed_stop_recovery();
20369
+ init_network_claim_lookup();
20090
20370
  init_recovery_budget();
20091
20371
  init_reliability_context();
20092
20372
  init_reliability_contract();
@@ -20448,7 +20728,7 @@ var init_capacity_startup_witness = __esm({
20448
20728
  function readHostWakeEpoch(platform = process.platform) {
20449
20729
  if (platform !== "darwin") return void 0;
20450
20730
  try {
20451
- const result = (0, import_node_child_process34.spawnSync)("/usr/sbin/sysctl", ["-n", "kern.wake_abs_time"], {
20731
+ const result = (0, import_node_child_process35.spawnSync)("/usr/sbin/sysctl", ["-n", "kern.wake_abs_time"], {
20452
20732
  encoding: "utf8",
20453
20733
  stdio: ["ignore", "pipe", "pipe"],
20454
20734
  timeout: 250,
@@ -20463,11 +20743,11 @@ function readHostWakeEpoch(platform = process.platform) {
20463
20743
  return null;
20464
20744
  }
20465
20745
  }
20466
- var import_node_child_process34;
20746
+ var import_node_child_process35;
20467
20747
  var init_host_wake = __esm({
20468
20748
  "src/core/host-wake.ts"() {
20469
20749
  "use strict";
20470
- import_node_child_process34 = require("child_process");
20750
+ import_node_child_process35 = require("child_process");
20471
20751
  }
20472
20752
  });
20473
20753
 
@@ -20693,7 +20973,7 @@ function createCapacityController(options) {
20693
20973
  controller,
20694
20974
  policyRevision: policy.revision
20695
20975
  });
20696
- const payloadKey = (0, import_node_crypto27.randomBytes)(32);
20976
+ const payloadKey = (0, import_node_crypto28.randomBytes)(32);
20697
20977
  const acceptedPayloads = /* @__PURE__ */ new Map();
20698
20978
  const settlePreparations = () => {
20699
20979
  if (lifetime.signal.aborted) return;
@@ -20857,7 +21137,7 @@ function createCapacityController(options) {
20857
21137
  {
20858
21138
  ...charge,
20859
21139
  operationId: prepared.operationId,
20860
- reservationId: (0, import_node_crypto27.randomUUID)(),
21140
+ reservationId: (0, import_node_crypto28.randomUUID)(),
20861
21141
  policyRevision: target.current.revision
20862
21142
  },
20863
21143
  {
@@ -20961,7 +21241,7 @@ function createCapacityController(options) {
20961
21241
  throw error;
20962
21242
  }
20963
21243
  }
20964
- const signature = (0, import_node_crypto27.createHmac)("sha256", payloadKey).update(
21244
+ const signature = (0, import_node_crypto28.createHmac)("sha256", payloadKey).update(
20965
21245
  JSON.stringify({
20966
21246
  repoPath: identity2.repoPath,
20967
21247
  profile: environment.profile,
@@ -20982,7 +21262,7 @@ function createCapacityController(options) {
20982
21262
  {
20983
21263
  ...charge,
20984
21264
  operationId: prepared.operationId,
20985
- reservationId: (0, import_node_crypto27.randomUUID)(),
21265
+ reservationId: (0, import_node_crypto28.randomUUID)(),
20986
21266
  policyRevision: current.revision
20987
21267
  },
20988
21268
  { estimates: resolved2.estimates, enrollment: resolved2.enrollment, pool }
@@ -21117,7 +21397,7 @@ function createCapacityController(options) {
21117
21397
  // the budget module then falls back to the conservative wall duration.
21118
21398
  activeElapsedMs: null,
21119
21399
  profile: environment.profile,
21120
- incidentId: `incident-${(0, import_node_crypto27.randomUUID)()}`
21400
+ incidentId: `incident-${(0, import_node_crypto28.randomUUID)()}`
21121
21401
  });
21122
21402
  } catch {
21123
21403
  return;
@@ -21135,7 +21415,7 @@ function createCapacityController(options) {
21135
21415
  {
21136
21416
  ...charge,
21137
21417
  operationId: prepared.operationId,
21138
- reservationId: (0, import_node_crypto27.randomUUID)(),
21418
+ reservationId: (0, import_node_crypto28.randomUUID)(),
21139
21419
  policyRevision: current.revision
21140
21420
  },
21141
21421
  { estimates: resolved2.estimates, enrollment: resolved2.enrollment, pool }
@@ -21155,11 +21435,11 @@ function createCapacityController(options) {
21155
21435
  }
21156
21436
  };
21157
21437
  }
21158
- var import_node_crypto27, import_node_path49, import_node_util15;
21438
+ var import_node_crypto28, import_node_path49, import_node_util15;
21159
21439
  var init_capacity_controller = __esm({
21160
21440
  "src/core/capacity-controller.ts"() {
21161
21441
  "use strict";
21162
- import_node_crypto27 = require("crypto");
21442
+ import_node_crypto28 = require("crypto");
21163
21443
  import_node_path49 = __toESM(require("path"));
21164
21444
  import_node_util15 = require("util");
21165
21445
  init_capacity_accounting();
@@ -21257,7 +21537,7 @@ printf '%s %s %s\n' "$pid" "$birth" "$fingerprint"
21257
21537
 
21258
21538
  // src/core/controller-observation.ts
21259
21539
  function digest2(value) {
21260
- return (0, import_node_crypto28.createHash)("sha256").update(JSON.stringify(value)).digest("hex");
21540
+ return (0, import_node_crypto29.createHash)("sha256").update(JSON.stringify(value)).digest("hex");
21261
21541
  }
21262
21542
  function readRoutes() {
21263
21543
  return readHostRouteStateReadOnly((file) => readControllerEvidence(file, 1048576));
@@ -21341,7 +21621,7 @@ function createControllerObservationCollector(resolve, fingerprint) {
21341
21621
  () => persisted.contents[3]
21342
21622
  );
21343
21623
  const source = parseDevcontainerConfig(persisted.contents[4], sourcePath);
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)
21624
+ if (!state || state.devpodId !== environment.providerId || state.profile !== environment.profile || typeof source.service !== "string" || !source.service || (0, import_node_crypto29.createHash)("sha256").update(persisted.contents[4]).digest("hex") !== state.sourceConfigSha256 || (0, import_node_crypto29.createHash)("sha256").update(persisted.contents[5]).digest("hex") !== state.effectiveConfigSha256)
21345
21625
  throw new ControllerObservationBindingChanged("Observation retained configuration changed.");
21346
21626
  const identity2 = {
21347
21627
  repoPath: environment.repoPath,
@@ -21516,11 +21796,11 @@ function createControllerObservationCollector(resolve, fingerprint) {
21516
21796
  };
21517
21797
  };
21518
21798
  }
21519
- var import_node_crypto28, import_node_path50, OBSERVATION_INSPECT_TEMPLATE;
21799
+ var import_node_crypto29, import_node_path50, OBSERVATION_INSPECT_TEMPLATE;
21520
21800
  var init_controller_observation = __esm({
21521
21801
  "src/core/controller-observation.ts"() {
21522
21802
  "use strict";
21523
- import_node_crypto28 = require("crypto");
21803
+ import_node_crypto29 = require("crypto");
21524
21804
  import_node_path50 = __toESM(require("path"));
21525
21805
  init_controller_binding();
21526
21806
  init_controller_monitor();
@@ -21545,11 +21825,11 @@ var init_controller_observation = __esm({
21545
21825
  });
21546
21826
 
21547
21827
  // src/core/controller-sessions.ts
21548
- var import_node_crypto29, import_node_util16, ControllerSessions;
21828
+ var import_node_crypto30, import_node_util16, ControllerSessions;
21549
21829
  var init_controller_sessions = __esm({
21550
21830
  "src/core/controller-sessions.ts"() {
21551
21831
  "use strict";
21552
- import_node_crypto29 = require("crypto");
21832
+ import_node_crypto30 = require("crypto");
21553
21833
  import_node_util16 = require("util");
21554
21834
  ControllerSessions = class {
21555
21835
  constructor(store, readWakeEpoch = () => void 0) {
@@ -21743,7 +22023,7 @@ var init_controller_sessions = __esm({
21743
22023
  const session = {
21744
22024
  id: id5,
21745
22025
  environmentId: environment.id,
21746
- generation: (0, import_node_crypto29.randomUUID)(),
22026
+ generation: (0, import_node_crypto30.randomUUID)(),
21747
22027
  requirements: required,
21748
22028
  renewedAtMs: wall,
21749
22029
  parkingConsent: "protected",
@@ -21771,7 +22051,7 @@ var init_controller_sessions = __esm({
21771
22051
  const session = {
21772
22052
  id: previous.session,
21773
22053
  environmentId: environment.id,
21774
- generation: (0, import_node_crypto29.randomUUID)(),
22054
+ generation: (0, import_node_crypto30.randomUUID)(),
21775
22055
  requirements: required,
21776
22056
  renewedAtMs: wall,
21777
22057
  parkingConsent: "protected",
@@ -21936,21 +22216,21 @@ function privateDirectory2(directory) {
21936
22216
  for (const part of absolute2.slice(current.length).split(import_node_path51.default.sep)) {
21937
22217
  current = import_node_path51.default.join(current, part);
21938
22218
  try {
21939
- const stat2 = import_node_fs40.default.lstatSync(current);
22219
+ const stat2 = import_node_fs41.default.lstatSync(current);
21940
22220
  if (!stat2.isDirectory() || stat2.isSymbolicLink())
21941
22221
  throw new Error("Unsafe controller directory.");
21942
22222
  } catch (error) {
21943
22223
  if (error.code !== "ENOENT") throw error;
21944
- import_node_fs40.default.mkdirSync(current, { mode: 448 });
22224
+ import_node_fs41.default.mkdirSync(current, { mode: 448 });
21945
22225
  }
21946
22226
  }
21947
- const stat = import_node_fs40.default.lstatSync(absolute2);
22227
+ const stat = import_node_fs41.default.lstatSync(absolute2);
21948
22228
  if (stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
21949
22229
  throw new Error("Controller directory is not private.");
21950
22230
  }
21951
22231
  function validateOwnedFile(file, socket) {
21952
22232
  try {
21953
- const stat = import_node_fs40.default.lstatSync(file);
22233
+ const stat = import_node_fs41.default.lstatSync(file);
21954
22234
  if (stat.uid !== process.getuid?.() || (socket ? !stat.isSocket() : !stat.isFile()) || (stat.mode & 63) !== 0)
21955
22235
  throw new Error("Unsafe controller artifact.");
21956
22236
  return true;
@@ -21975,7 +22255,7 @@ async function runController(options) {
21975
22255
  privateDirectory2(options.directory);
21976
22256
  const lockPath = import_node_path51.default.join(options.directory, "owner.lock");
21977
22257
  try {
21978
- const stat = import_node_fs40.default.lstatSync(lockPath);
22258
+ const stat = import_node_fs41.default.lstatSync(lockPath);
21979
22259
  if (!stat.isFile() || stat.uid !== process.getuid?.())
21980
22260
  throw new Error("Unsafe controller owner lock.");
21981
22261
  } catch (error) {
@@ -21986,7 +22266,7 @@ async function runController(options) {
21986
22266
  await withFileLock(lockPath, { activity: "controller ownership", waitMs: 0 }, async () => {
21987
22267
  const staleSocket = validateOwnedFile(socketPath, true);
21988
22268
  const sessions = new ControllerSessions(store, options.wakeEpoch ?? readHostWakeEpoch);
21989
- if (staleSocket) import_node_fs40.default.unlinkSync(socketPath);
22269
+ if (staleSocket) import_node_fs41.default.unlinkSync(socketPath);
21990
22270
  const incarnation = sessions.read();
21991
22271
  const { resolve, collect } = options.createBindings?.(store.bindingFingerprint()) ?? {
21992
22272
  resolve: options.resolve,
@@ -22532,7 +22812,7 @@ async function runController(options) {
22532
22812
  resolve2();
22533
22813
  });
22534
22814
  });
22535
- import_node_fs40.default.chmodSync(socketPath, 384);
22815
+ import_node_fs41.default.chmodSync(socketPath, 384);
22536
22816
  await new Promise((resolve2) => {
22537
22817
  finish = resolve2;
22538
22818
  options.signal.addEventListener("abort", shutdown, { once: true });
@@ -22551,11 +22831,11 @@ async function runController(options) {
22551
22831
  }
22552
22832
  });
22553
22833
  }
22554
- var import_node_fs40, import_node_net4, import_node_path51, FRAME_BYTES;
22834
+ var import_node_fs41, import_node_net4, import_node_path51, FRAME_BYTES;
22555
22835
  var init_controller_server = __esm({
22556
22836
  "src/core/controller-server.ts"() {
22557
22837
  "use strict";
22558
- import_node_fs40 = __toESM(require("fs"));
22838
+ import_node_fs41 = __toESM(require("fs"));
22559
22839
  import_node_net4 = __toESM(require("net"));
22560
22840
  import_node_path51 = __toESM(require("path"));
22561
22841
  init_controller_monitor();
@@ -22842,27 +23122,27 @@ function replaceDevrouterSection(content) {
22842
23122
  }
22843
23123
  function writeRepoFile(repoPath, relPath, content) {
22844
23124
  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");
23125
+ (0, import_node_fs42.mkdirSync)((0, import_node_path54.dirname)(absolutePath), { recursive: true });
23126
+ (0, import_node_fs42.writeFileSync)(absolutePath, content, "utf-8");
22847
23127
  return absolutePath;
22848
23128
  }
22849
23129
  function ensureAgentsMdSection(repoPath) {
22850
23130
  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");
23131
+ if ((0, import_node_fs42.existsSync)(filePath)) {
23132
+ const content = (0, import_node_fs42.readFileSync)(filePath, "utf-8");
22853
23133
  if (content.includes(DEVROUTER_SENTINEL)) {
22854
23134
  const updated = replaceDevrouterSection(content);
22855
23135
  if (updated === void 0 || updated === content) return { path: filePath, written: false };
22856
- (0, import_node_fs41.writeFileSync)(filePath, updated, "utf-8");
23136
+ (0, import_node_fs42.writeFileSync)(filePath, updated, "utf-8");
22857
23137
  return { path: filePath, written: true };
22858
23138
  }
22859
- (0, import_node_fs41.writeFileSync)(filePath, `${content.trimEnd()}
23139
+ (0, import_node_fs42.writeFileSync)(filePath, `${content.trimEnd()}
22860
23140
 
22861
23141
  ${buildDevrouterSection()}
22862
23142
  `, "utf-8");
22863
23143
  return { path: filePath, written: true };
22864
23144
  }
22865
- (0, import_node_fs41.writeFileSync)(filePath, `# AGENTS.md
23145
+ (0, import_node_fs42.writeFileSync)(filePath, `# AGENTS.md
22866
23146
 
22867
23147
  ${buildDevrouterSection()}
22868
23148
  `, "utf-8");
@@ -22872,11 +23152,11 @@ function ensureSkillFile(repoPath) {
22872
23152
  const filePath = writeRepoFile(repoPath, DEVROUTER_SKILL_REL_PATH, DEVROUTER_SKILL_CONTENT);
22873
23153
  return { path: filePath, written: true };
22874
23154
  }
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;
23155
+ var import_node_fs42, import_node_path54, DEVROUTER_SENTINEL, DEVROUTER_END_SENTINEL, LEGACY_DEVROUTER_SECTION_END, AGENTS_MD, DEVROUTER_SKILL_REL_PATH, DEVROUTER_SKILL_CONTENT;
22876
23156
  var init_agents_md = __esm({
22877
23157
  "src/core/agents-md.ts"() {
22878
23158
  "use strict";
22879
- import_node_fs41 = require("fs");
23159
+ import_node_fs42 = require("fs");
22880
23160
  import_node_path54 = require("path");
22881
23161
  DEVROUTER_SENTINEL = "<!-- devrouter -->";
22882
23162
  DEVROUTER_END_SENTINEL = "<!-- /devrouter -->";
@@ -23499,6 +23779,8 @@ function buildOnboardingPrompt(options = {}) {
23499
23779
  "- 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.",
23500
23780
  "- 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.",
23501
23781
  "- 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.",
23782
+ "- Explicit retained replacement: after canonical stop, ensure <checkout> --replace-app replaces only the proved primary writable layer. It requires manual Devsy 1.24.0, committed unchanged managed source, qualified preserving hooks, a retained dependency and named storage. Anonymous mounts, namespace consumers, allocated networks and capacity enrollment refuse. One durable attempt survives cancellation, stop and rollover; never clear it or retry through a raw provider. Proved stop/stop --repair retains data. This does not apply changed Compose overlays.",
23783
+ "- Lifecycle journal compatibility: older CLIs refuse records written by a newer release, including stop and global enumeration. Never edit a writer stamp or durable receipt to bypass refusal.",
23502
23784
  "- 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.",
23503
23785
  "- 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.",
23504
23786
  "- 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.",
@@ -23669,7 +23951,7 @@ var init_ai_prompt = __esm({
23669
23951
  purpose: "Write/update devrouter section in the repo's AGENTS.md and install the devrouter skill."
23670
23952
  },
23671
23953
  {
23672
- command: "devrouter ensure [path] [--profile <name>] [--variant <name>] [--repair | --initialize-new] [--open] [--json]",
23954
+ command: "devrouter ensure [path] [--profile <name>] [--variant <name>] [--repair | --initialize-new | --replace-app] [--open] [--json]",
23673
23955
  purpose: "Canonical startup and proof for an exact primary or linked checkout; atomically publishes routes after readiness."
23674
23956
  },
23675
23957
  {
@@ -24387,11 +24669,11 @@ function extractCurrentVersionFromRepoConfig(repoPath) {
24387
24669
  };
24388
24670
  }
24389
24671
  function readPromptDirectory(promptDirPath) {
24390
- if (!import_node_fs42.default.existsSync(promptDirPath)) {
24672
+ if (!import_node_fs43.default.existsSync(promptDirPath)) {
24391
24673
  throw new Error(`Missing ${UPGRADE_PROMPTS_DIR} directory at ${promptDirPath}.`);
24392
24674
  }
24393
24675
  const releases = [];
24394
- const files = import_node_fs42.default.readdirSync(promptDirPath, { withFileTypes: true });
24676
+ const files = import_node_fs43.default.readdirSync(promptDirPath, { withFileTypes: true });
24395
24677
  for (const file of files) {
24396
24678
  if (!file.isFile() || !file.name.endsWith(".md")) {
24397
24679
  continue;
@@ -24402,7 +24684,7 @@ function readPromptDirectory(promptDirPath) {
24402
24684
  }
24403
24685
  const version = normalizeVersion(basename);
24404
24686
  const promptPath = import_node_path55.default.join(promptDirPath, file.name);
24405
- const prompt = import_node_fs42.default.readFileSync(promptPath, "utf-8").trim();
24687
+ const prompt = import_node_fs43.default.readFileSync(promptPath, "utf-8").trim();
24406
24688
  releases.push({ version, prompt, promptPath });
24407
24689
  }
24408
24690
  const deduped = /* @__PURE__ */ new Map();
@@ -24427,7 +24709,7 @@ function resolvePromptDirectory(explicitPath) {
24427
24709
  }
24428
24710
  const entryFile = process.argv[1] ? import_node_path55.default.resolve(process.argv[1]) : __filename;
24429
24711
  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;
24712
+ const resolvedEntryDir = import_node_fs43.default.existsSync(entryFile) ? import_node_path55.default.dirname(import_node_fs43.default.realpathSync(entryFile)) : entryDir;
24431
24713
  const candidates = [
24432
24714
  import_node_path55.default.resolve(resolvedEntryDir, "..", UPGRADE_PROMPTS_DIR),
24433
24715
  import_node_path55.default.resolve(entryDir, "..", UPGRADE_PROMPTS_DIR),
@@ -24435,7 +24717,7 @@ function resolvePromptDirectory(explicitPath) {
24435
24717
  import_node_path55.default.resolve(__dirname, "..", "..", UPGRADE_PROMPTS_DIR),
24436
24718
  import_node_path55.default.resolve(process.cwd(), UPGRADE_PROMPTS_DIR)
24437
24719
  ];
24438
- const existing = candidates.find((candidate) => import_node_fs42.default.existsSync(candidate));
24720
+ const existing = candidates.find((candidate) => import_node_fs43.default.existsSync(candidate));
24439
24721
  return existing ?? candidates[0];
24440
24722
  }
24441
24723
  function loadUpgradeCatalog(options = {}) {
@@ -24451,11 +24733,11 @@ function loadUpgradeCatalog(options = {}) {
24451
24733
  releases
24452
24734
  };
24453
24735
  }
24454
- var import_node_fs42, import_node_path55, SEMVER_RE, UPGRADE_PROMPTS_DIR;
24736
+ var import_node_fs43, import_node_path55, SEMVER_RE, UPGRADE_PROMPTS_DIR;
24455
24737
  var init_upgrade = __esm({
24456
24738
  "src/core/upgrade.ts"() {
24457
24739
  "use strict";
24458
- import_node_fs42 = __toESM(require("fs"));
24740
+ import_node_fs43 = __toESM(require("fs"));
24459
24741
  import_node_path55 = __toESM(require("path"));
24460
24742
  init_repo_config();
24461
24743
  SEMVER_RE = /^v?(\d+)\.(\d+)\.(\d+)$/;
@@ -24609,7 +24891,7 @@ function isLoopbackHost(host) {
24609
24891
  return host === "127.0.0.1" || host === "localhost" || host === "host.docker.internal";
24610
24892
  }
24611
24893
  function inspectCompose(composeFile, _workspace) {
24612
- if (!import_node_fs43.default.existsSync(composeFile)) {
24894
+ if (!import_node_fs44.default.existsSync(composeFile)) {
24613
24895
  return {
24614
24896
  aliases: [],
24615
24897
  publishedPorts: [],
@@ -24618,7 +24900,7 @@ function inspectCompose(composeFile, _workspace) {
24618
24900
  };
24619
24901
  }
24620
24902
  try {
24621
- const raw = import_node_fs43.default.readFileSync(composeFile, "utf-8");
24903
+ const raw = import_node_fs44.default.readFileSync(composeFile, "utf-8");
24622
24904
  const parsed = import_yaml6.default.parse(raw);
24623
24905
  const root = asRecord(parsed);
24624
24906
  const services = asRecord(root?.services);
@@ -24716,7 +24998,7 @@ function buildHostPortClaimsCheck(repoPath, workspace) {
24716
24998
  }
24717
24999
  function buildDevcontainerChecks(repoPath, config, workspace) {
24718
25000
  const devcontainerDir = import_node_path56.default.join(repoPath, ".devcontainer");
24719
- if (!import_node_fs43.default.existsSync(devcontainerDir)) {
25001
+ if (!import_node_fs44.default.existsSync(devcontainerDir)) {
24720
25002
  return [];
24721
25003
  }
24722
25004
  const compose = inspectCompose(import_node_path56.default.join(devcontainerDir, "docker-compose.yml"), workspace);
@@ -24747,8 +25029,8 @@ function buildDevcontainerChecks(repoPath, config, workspace) {
24747
25029
  suggestion: compose.parseError || compose.publishedPorts.length > 0 ? "Remove published ports and route services through devnet aliases with devrouter proxy apps." : void 0
24748
25030
  });
24749
25031
  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") : "";
25032
+ const dockerfileExists = import_node_fs44.default.existsSync(dockerfilePath);
25033
+ const dockerfile = dockerfileExists ? import_node_fs44.default.readFileSync(dockerfilePath, "utf-8") : "";
24752
25034
  const devrouterArtifacts = ["@devrouter/cli", "devrouter-process"].filter(
24753
25035
  (artifact) => dockerfile.includes(artifact)
24754
25036
  );
@@ -24806,11 +25088,11 @@ function buildDevcontainerChecks(repoPath, config, workspace) {
24806
25088
  });
24807
25089
  return checks;
24808
25090
  }
24809
- var import_node_fs43, import_node_path56, import_yaml6;
25091
+ var import_node_fs44, import_node_path56, import_yaml6;
24810
25092
  var init_devcontainer_diagnostics = __esm({
24811
25093
  "src/core/devcontainer-diagnostics.ts"() {
24812
25094
  "use strict";
24813
- import_node_fs43 = __toESM(require("fs"));
25095
+ import_node_fs44 = __toESM(require("fs"));
24814
25096
  import_node_path56 = __toESM(require("path"));
24815
25097
  import_yaml6 = __toESM(require("yaml"));
24816
25098
  init_devcontainer_profile();
@@ -24907,11 +25189,11 @@ function reconcileRouteRunConflict(repoPath, app) {
24907
25189
  }
24908
25190
  }
24909
25191
  }
24910
- var import_node_fs44;
25192
+ var import_node_fs45;
24911
25193
  var init_route_state = __esm({
24912
25194
  "src/core/route-state.ts"() {
24913
25195
  "use strict";
24914
- import_node_fs44 = __toESM(require("fs"));
25196
+ import_node_fs45 = __toESM(require("fs"));
24915
25197
  init_host_routes();
24916
25198
  init_workspace();
24917
25199
  }
@@ -25086,7 +25368,7 @@ function toRepoStatus(repoPath) {
25086
25368
  const resolvedRepoPath = resolveRepoPath(repoPath);
25087
25369
  const configPath = getRepoConfigPath(resolvedRepoPath);
25088
25370
  const explicitRepo = typeof repoPath === "string" && repoPath.trim().length > 0;
25089
- const configExists = import_node_fs45.default.existsSync(configPath);
25371
+ const configExists = import_node_fs46.default.existsSync(configPath);
25090
25372
  if (!explicitRepo && !configExists) {
25091
25373
  return void 0;
25092
25374
  }
@@ -25239,11 +25521,11 @@ async function collectRouterStatus(repoPath) {
25239
25521
  }
25240
25522
  };
25241
25523
  }
25242
- var import_node_fs45;
25524
+ var import_node_fs46;
25243
25525
  var init_status = __esm({
25244
25526
  "src/core/status.ts"() {
25245
25527
  "use strict";
25246
- import_node_fs45 = __toESM(require("fs"));
25528
+ import_node_fs46 = __toESM(require("fs"));
25247
25529
  init_docker();
25248
25530
  init_managed_runtime_state();
25249
25531
  init_managed_runtime_status();
@@ -25305,14 +25587,14 @@ ${env}
25305
25587
  function readInstalledIdleFor(plistPath) {
25306
25588
  let contents;
25307
25589
  try {
25308
- contents = import_node_fs46.default.readFileSync(plistPath, "utf-8");
25590
+ contents = import_node_fs47.default.readFileSync(plistPath, "utf-8");
25309
25591
  } catch {
25310
25592
  return void 0;
25311
25593
  }
25312
25594
  return /<string>--idle-for<\/string>\s*<string>([^<]+)<\/string>/.exec(contents)?.[1] ?? "unknown";
25313
25595
  }
25314
25596
  function locateTool(name) {
25315
- const result = (0, import_node_child_process35.spawnSync)("/bin/sh", ["-c", `command -v ${name}`], { encoding: "utf-8" });
25597
+ const result = (0, import_node_child_process36.spawnSync)("/bin/sh", ["-c", `command -v ${name}`], { encoding: "utf-8" });
25316
25598
  const found = result.status === 0 ? result.stdout.trim() : "";
25317
25599
  return found.startsWith("/") ? found : void 0;
25318
25600
  }
@@ -25338,9 +25620,9 @@ function installIdleSweep(input2) {
25338
25620
  throw new Error("The idle sweep uses launchd and is only available on macOS.");
25339
25621
  const paths = input2.paths ?? idleSweepPaths();
25340
25622
  const launchctl = input2.launchctl ?? defaultLaunchctl;
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 });
25623
+ const entryPath = input2.entryPath ?? import_node_fs47.default.realpathSync(process.argv[1]);
25624
+ import_node_fs47.default.mkdirSync(paths.directory, { recursive: true, mode: 448 });
25625
+ import_node_fs47.default.mkdirSync(import_node_path57.default.dirname(paths.plist), { recursive: true });
25344
25626
  const contents = renderIdleSweepPlist({
25345
25627
  nodePath: process.execPath,
25346
25628
  entryPath,
@@ -25349,7 +25631,7 @@ function installIdleSweep(input2) {
25349
25631
  logPath: paths.log
25350
25632
  });
25351
25633
  launchctl(["bootout", `${launchDomain()}/${IDLE_SWEEP_LABEL}`]);
25352
- import_node_fs46.default.writeFileSync(paths.plist, contents, { mode: 420 });
25634
+ import_node_fs47.default.writeFileSync(paths.plist, contents, { mode: 420 });
25353
25635
  const loaded = launchctl(["bootstrap", launchDomain(), paths.plist]);
25354
25636
  if (loaded.status !== 0)
25355
25637
  throw new Error(
@@ -25359,9 +25641,9 @@ function installIdleSweep(input2) {
25359
25641
  }
25360
25642
  function removeIdleSweep(input2 = {}) {
25361
25643
  const paths = input2.paths ?? idleSweepPaths();
25362
- if (!import_node_fs46.default.existsSync(paths.plist)) return false;
25644
+ if (!import_node_fs47.default.existsSync(paths.plist)) return false;
25363
25645
  (input2.launchctl ?? defaultLaunchctl)(["bootout", `${launchDomain()}/${IDLE_SWEEP_LABEL}`]);
25364
- import_node_fs46.default.rmSync(paths.plist, { force: true });
25646
+ import_node_fs47.default.rmSync(paths.plist, { force: true });
25365
25647
  return true;
25366
25648
  }
25367
25649
  function summarizeSweep(idleFor, stops, error) {
@@ -25376,13 +25658,13 @@ function summarizeSweep(idleFor, stops, error) {
25376
25658
  };
25377
25659
  }
25378
25660
  function writeLastSweep(record6, paths = idleSweepPaths()) {
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)}
25661
+ import_node_fs47.default.mkdirSync(paths.directory, { recursive: true, mode: 448 });
25662
+ import_node_fs47.default.writeFileSync(paths.lastSweep, `${JSON.stringify(record6, null, 2)}
25381
25663
  `, { mode: 384 });
25382
25664
  }
25383
25665
  function readLastSweep(paths) {
25384
25666
  try {
25385
- return JSON.parse(import_node_fs46.default.readFileSync(paths.lastSweep, "utf-8"));
25667
+ return JSON.parse(import_node_fs47.default.readFileSync(paths.lastSweep, "utf-8"));
25386
25668
  } catch {
25387
25669
  return void 0;
25388
25670
  }
@@ -25427,12 +25709,12 @@ function idleSweepCheck(paths = idleSweepPaths(), nowMs = Date.now()) {
25427
25709
  ...stale || last.failed > 0 ? { suggestion: `Inspect: ${paths.log}` } : {}
25428
25710
  };
25429
25711
  }
25430
- var import_node_child_process35, import_node_fs46, import_node_os7, import_node_path57, IDLE_SWEEP_LABEL, SWEEP_INTERVAL_SECONDS, SYSTEM_PATH, defaultLaunchctl;
25712
+ var import_node_child_process36, import_node_fs47, import_node_os7, import_node_path57, IDLE_SWEEP_LABEL, SWEEP_INTERVAL_SECONDS, SYSTEM_PATH, defaultLaunchctl;
25431
25713
  var init_idle_sweep = __esm({
25432
25714
  "src/core/idle-sweep.ts"() {
25433
25715
  "use strict";
25434
- import_node_child_process35 = require("child_process");
25435
- import_node_fs46 = __toESM(require("fs"));
25716
+ import_node_child_process36 = require("child_process");
25717
+ import_node_fs47 = __toESM(require("fs"));
25436
25718
  import_node_os7 = __toESM(require("os"));
25437
25719
  import_node_path57 = __toESM(require("path"));
25438
25720
  init_router();
@@ -25440,7 +25722,7 @@ var init_idle_sweep = __esm({
25440
25722
  SWEEP_INTERVAL_SECONDS = 300;
25441
25723
  SYSTEM_PATH = ["/usr/bin", "/bin", "/usr/sbin", "/sbin"];
25442
25724
  defaultLaunchctl = (args) => {
25443
- const result = (0, import_node_child_process35.spawnSync)("launchctl", args, { encoding: "utf-8" });
25725
+ const result = (0, import_node_child_process36.spawnSync)("launchctl", args, { encoding: "utf-8" });
25444
25726
  return { status: result.status, stderr: result.stderr || result.error?.message || "" };
25445
25727
  };
25446
25728
  }
@@ -25454,7 +25736,7 @@ function outputFromResult(result) {
25454
25736
  return output2.length > 0 ? output2 : void 0;
25455
25737
  }
25456
25738
  function runTool(command, args = []) {
25457
- const result = (0, import_node_child_process36.spawnSync)(command, args, {
25739
+ const result = (0, import_node_child_process37.spawnSync)(command, args, {
25458
25740
  encoding: "utf-8"
25459
25741
  });
25460
25742
  if (result.error) {
@@ -25512,11 +25794,11 @@ function parseMinimumNodeMajor(value) {
25512
25794
  }
25513
25795
  function readPackageJson(repoPath) {
25514
25796
  const packagePath = import_node_path58.default.join(repoPath, "package.json");
25515
- if (!import_node_fs47.default.existsSync(packagePath)) {
25797
+ if (!import_node_fs48.default.existsSync(packagePath)) {
25516
25798
  return void 0;
25517
25799
  }
25518
25800
  try {
25519
- return JSON.parse(import_node_fs47.default.readFileSync(packagePath, "utf-8"));
25801
+ return JSON.parse(import_node_fs48.default.readFileSync(packagePath, "utf-8"));
25520
25802
  } catch {
25521
25803
  return void 0;
25522
25804
  }
@@ -25621,17 +25903,17 @@ function realpathOrUndefined(value) {
25621
25903
  return void 0;
25622
25904
  }
25623
25905
  try {
25624
- return import_node_fs47.default.realpathSync(value);
25906
+ return import_node_fs48.default.realpathSync(value);
25625
25907
  } catch {
25626
25908
  return void 0;
25627
25909
  }
25628
25910
  }
25629
25911
  function isExecutableFile(filePath) {
25630
25912
  try {
25631
- if (!import_node_fs47.default.statSync(filePath).isFile()) {
25913
+ if (!import_node_fs48.default.statSync(filePath).isFile()) {
25632
25914
  return false;
25633
25915
  }
25634
- import_node_fs47.default.accessSync(filePath, import_node_fs47.default.constants.X_OK);
25916
+ import_node_fs48.default.accessSync(filePath, import_node_fs48.default.constants.X_OK);
25635
25917
  return true;
25636
25918
  } catch {
25637
25919
  return false;
@@ -25645,7 +25927,7 @@ function displayPath(filePath) {
25645
25927
  return `~${filePath.slice(home.length)}`;
25646
25928
  }
25647
25929
  function probeInstalledCliVersion(executable) {
25648
- const result = (0, import_node_child_process36.spawnSync)(executable, ["-V"], {
25930
+ const result = (0, import_node_child_process37.spawnSync)(executable, ["-V"], {
25649
25931
  encoding: "utf-8",
25650
25932
  timeout: CLI_PATH_PROBE_TIMEOUT_MS
25651
25933
  });
@@ -25656,7 +25938,7 @@ function probeInstalledCliVersion(executable) {
25656
25938
  }
25657
25939
  function buildCliPathCheck(options = {}) {
25658
25940
  const id5 = "global.cli-path";
25659
- const runningVersion = options.runningVersion ?? (true ? "0.6.3" : "0.0.0-dev");
25941
+ const runningVersion = options.runningVersion ?? (true ? "0.6.4" : "0.0.0-dev");
25660
25942
  if (runningVersion === "0.0.0-dev") {
25661
25943
  return {
25662
25944
  id: id5,
@@ -25830,12 +26112,12 @@ function buildGlobalToolChecksWithoutTelemetry(repoPath) {
25830
26112
  checks.push(nodeToolchainCheck(repoPath));
25831
26113
  return checks;
25832
26114
  }
25833
- var import_node_child_process36, import_node_fs47, import_node_path58, DEVSY_DISABLE_TELEMETRY, CLI_PATH_PROBE_TIMEOUT_MS, INSTALLED_CLI_VERSION_RE;
26115
+ var import_node_child_process37, import_node_fs48, import_node_path58, DEVSY_DISABLE_TELEMETRY, CLI_PATH_PROBE_TIMEOUT_MS, INSTALLED_CLI_VERSION_RE;
25834
26116
  var init_tool_diagnostics = __esm({
25835
26117
  "src/core/tool-diagnostics.ts"() {
25836
26118
  "use strict";
25837
- import_node_child_process36 = require("child_process");
25838
- import_node_fs47 = __toESM(require("fs"));
26119
+ import_node_child_process37 = require("child_process");
26120
+ import_node_fs48 = __toESM(require("fs"));
25839
26121
  import_node_path58 = __toESM(require("path"));
25840
26122
  init_devsy_agent();
25841
26123
  init_idle_sweep();
@@ -26186,11 +26468,11 @@ function inspectPostgresCredentials(repoPath, config) {
26186
26468
  parseErrors.push(`${app.name}: ${composeFile} (${message})`);
26187
26469
  continue;
26188
26470
  }
26189
- if (!import_node_fs48.default.existsSync(absolutePath)) {
26471
+ if (!import_node_fs49.default.existsSync(absolutePath)) {
26190
26472
  continue;
26191
26473
  }
26192
26474
  try {
26193
- const raw = import_node_fs48.default.readFileSync(absolutePath, "utf-8");
26475
+ const raw = import_node_fs49.default.readFileSync(absolutePath, "utf-8");
26194
26476
  const parsed = import_yaml7.default.parse(raw);
26195
26477
  const root = asRecord2(parsed);
26196
26478
  const services = asRecord2(root?.services);
@@ -26554,7 +26836,7 @@ async function buildDoctorReport(options = {}) {
26554
26836
  suggestion: `Remedy ${expansionNotice.remedy.code}: set a named default profile other than '${expansionNotice.profile}'${remedyCandidates}.`
26555
26837
  });
26556
26838
  }
26557
- const cliVersion = true ? "0.6.3" : "0.0.0-dev";
26839
+ const cliVersion = true ? "0.6.4" : "0.0.0-dev";
26558
26840
  const configVersion = config.devrouter?.version;
26559
26841
  const installedKnown = cliVersion !== "0.0.0-dev";
26560
26842
  const versionComparison = installedKnown && configVersion ? compareSemver(configVersion, cliVersion) : void 0;
@@ -26592,7 +26874,7 @@ async function buildDoctorReport(options = {}) {
26592
26874
  filePath,
26593
26875
  absolutePath: import_node_path60.default.resolve(repo.path, filePath)
26594
26876
  }))
26595
- ).filter((entry) => !import_node_fs48.default.existsSync(entry.absolutePath));
26877
+ ).filter((entry) => !import_node_fs49.default.existsSync(entry.absolutePath));
26596
26878
  addCheck(checks, {
26597
26879
  id: "repo.compose-files",
26598
26880
  level: missingComposeFiles.length === 0 ? "ok" : "error",
@@ -26636,7 +26918,7 @@ async function buildDoctorReport(options = {}) {
26636
26918
  app: app.name,
26637
26919
  cwd: app.hostRun.cwd,
26638
26920
  absolutePath: import_node_path60.default.resolve(repo.path, app.hostRun.cwd)
26639
- })).filter((entry) => !import_node_fs48.default.existsSync(entry.absolutePath));
26921
+ })).filter((entry) => !import_node_fs49.default.existsSync(entry.absolutePath));
26640
26922
  addCheck(checks, {
26641
26923
  id: "repo.host-cwd",
26642
26924
  level: missingHostCwds.length === 0 ? "ok" : "error",
@@ -26780,11 +27062,11 @@ async function buildDoctorReport(options = {}) {
26780
27062
  nextSteps
26781
27063
  };
26782
27064
  }
26783
- var import_node_fs48, import_node_path60, import_yaml7, POSTGRES_DEFAULTS, CAPACITY_RECOVERY_BY_CAUSE;
27065
+ var import_node_fs49, import_node_path60, import_yaml7, POSTGRES_DEFAULTS, CAPACITY_RECOVERY_BY_CAUSE;
26784
27066
  var init_doctor = __esm({
26785
27067
  "src/core/doctor.ts"() {
26786
27068
  "use strict";
26787
- import_node_fs48 = __toESM(require("fs"));
27069
+ import_node_fs49 = __toESM(require("fs"));
26788
27070
  import_node_path60 = __toESM(require("path"));
26789
27071
  import_yaml7 = __toESM(require("yaml"));
26790
27072
  init_capacity_store();
@@ -26814,6 +27096,7 @@ var init_doctor = __esm({
26814
27096
  "journal-directory-unsafe": "Restore private ownership and mode 0700 on the reliability journal directory, then retry.",
26815
27097
  "journal-directory-unreadable": "Restore read access to the reliability journal directory, then retry.",
26816
27098
  "journal-invalid": "Preserve the named journal entry and restore verified history before retrying admission.",
27099
+ "journal-writer-newer": "Use the devrouter CLI that wrote the named journal or a newer version before retrying admission; preserve the journal.",
26817
27100
  "journal-unreadable": "Preserve the named journal entry and restore verified history before retrying admission.",
26818
27101
  "journal-entry-unsafe": "Restore private ownership, mode 0600 and a bounded size on the named journal entry, then retry."
26819
27102
  };
@@ -27181,12 +27464,12 @@ function parseSsPortListeners(stdout, port) {
27181
27464
  return listeners;
27182
27465
  }
27183
27466
  function findPortListeners(port) {
27184
- const result = (0, import_node_child_process37.spawnSync)("lsof", ["-nP", `-iTCP:${port}`, "-sTCP:LISTEN"], {
27467
+ const result = (0, import_node_child_process38.spawnSync)("lsof", ["-nP", `-iTCP:${port}`, "-sTCP:LISTEN"], {
27185
27468
  encoding: "utf-8"
27186
27469
  });
27187
27470
  if (result.error && result.error.code === "ENOENT") {
27188
27471
  if (process.platform === "linux") {
27189
- const ssResult = (0, import_node_child_process37.spawnSync)("ss", ["-H", "-lntp", "-p"], { encoding: "utf-8" });
27472
+ const ssResult = (0, import_node_child_process38.spawnSync)("ss", ["-H", "-lntp", "-p"], { encoding: "utf-8" });
27190
27473
  if (ssResult.status === 0 && ssResult.stdout) {
27191
27474
  return parseSsPortListeners(ssResult.stdout, port);
27192
27475
  }
@@ -27215,11 +27498,11 @@ function findPortListeners(port) {
27215
27498
  };
27216
27499
  });
27217
27500
  }
27218
- var import_node_child_process37;
27501
+ var import_node_child_process38;
27219
27502
  var init_ports = __esm({
27220
27503
  "src/util/ports.ts"() {
27221
27504
  "use strict";
27222
- import_node_child_process37 = require("child_process");
27505
+ import_node_child_process38 = require("child_process");
27223
27506
  }
27224
27507
  });
27225
27508
 
@@ -27306,6 +27589,7 @@ async function runEnsureCommand(options) {
27306
27589
  profile: options.profile,
27307
27590
  ...options.repair ? { repair: true } : {},
27308
27591
  ...options.initializeNew ? { initializeNew: true } : {},
27592
+ ...options.replaceApp ? { replaceApp: true } : {},
27309
27593
  ...options.variant !== void 0 ? { variant: options.variant } : {}
27310
27594
  };
27311
27595
  const result = await superviseLifecycle(
@@ -27553,7 +27837,7 @@ function buildHostEnvReport(input2) {
27553
27837
  };
27554
27838
  }
27555
27839
  function listDevcontainerContainers(repoPath) {
27556
- const listed = (0, import_node_child_process38.spawnSync)(
27840
+ const listed = (0, import_node_child_process39.spawnSync)(
27557
27841
  "docker",
27558
27842
  [
27559
27843
  "ps",
@@ -27574,7 +27858,7 @@ function listDevcontainerContainers(repoPath) {
27574
27858
  }
27575
27859
  }
27576
27860
  function dockerPortBindings(containerId, port) {
27577
- const result = (0, import_node_child_process38.spawnSync)("docker", ["port", containerId, `${port}/tcp`], {
27861
+ const result = (0, import_node_child_process39.spawnSync)("docker", ["port", containerId, `${port}/tcp`], {
27578
27862
  encoding: "utf-8",
27579
27863
  ...networkDockerOptions()
27580
27864
  });
@@ -27689,11 +27973,11 @@ function hostEnvVariables(report, processEnv) {
27689
27973
  }
27690
27974
  return variables;
27691
27975
  }
27692
- var import_node_child_process38, import_node_path61, LOOPBACK_UPSTREAM_HOSTS, ROUTED_TCP_PARAMS;
27976
+ var import_node_child_process39, import_node_path61, LOOPBACK_UPSTREAM_HOSTS, ROUTED_TCP_PARAMS;
27693
27977
  var init_host_env = __esm({
27694
27978
  "src/core/host-env.ts"() {
27695
27979
  "use strict";
27696
- import_node_child_process38 = require("child_process");
27980
+ import_node_child_process39 = require("child_process");
27697
27981
  import_node_path61 = __toESM(require("path"));
27698
27982
  init_devcontainer_profile();
27699
27983
  init_devpod_environment();
@@ -27791,7 +28075,7 @@ function hostCommandEnvironment(report, processEnv) {
27791
28075
  }
27792
28076
  function runHostCommand(command, env) {
27793
28077
  return new Promise((resolve) => {
27794
- const child = (0, import_node_child_process39.spawn)(command[0], command.slice(1), { env, stdio: "inherit" });
28078
+ const child = (0, import_node_child_process40.spawn)(command[0], command.slice(1), { env, stdio: "inherit" });
27795
28079
  const forward = (signal) => () => child.kill(signal);
27796
28080
  const handlers = [
27797
28081
  ["SIGINT", forward("SIGINT")],
@@ -27831,11 +28115,11 @@ async function runEnvCommand(invocation) {
27831
28115
  }
27832
28116
  process.stdout.write(formatEnvReport(report));
27833
28117
  }
27834
- var import_node_child_process39, import_node_os8, HOST_PORT_ENV_PREFIX;
28118
+ var import_node_child_process40, import_node_os8, HOST_PORT_ENV_PREFIX;
27835
28119
  var init_env = __esm({
27836
28120
  "src/commands/env.ts"() {
27837
28121
  "use strict";
27838
- import_node_child_process39 = require("child_process");
28122
+ import_node_child_process40 = require("child_process");
27839
28123
  import_node_os8 = require("os");
27840
28124
  init_host_env();
27841
28125
  init_managed_host_ports();
@@ -27885,7 +28169,7 @@ var init_status2 = __esm({
27885
28169
 
27886
28170
  // src/core/harness-continuation.ts
27887
28171
  function harnessContinuationPath(repoRoot) {
27888
- const key = (0, import_node_crypto30.createHash)("sha256").update(comparableWorkspacePath(repoRoot)).digest("hex");
28172
+ const key = (0, import_node_crypto31.createHash)("sha256").update(comparableWorkspacePath(repoRoot)).digest("hex");
27889
28173
  return import_node_path62.default.join(DEVROUTER_HOME, "harness", `${key}.json`);
27890
28174
  }
27891
28175
  function counter4(value) {
@@ -27899,7 +28183,7 @@ function validEntry(value) {
27899
28183
  function readLedgerSync(file, nowMs) {
27900
28184
  let stat;
27901
28185
  try {
27902
- stat = import_node_fs49.default.lstatSync(file);
28186
+ stat = import_node_fs50.default.lstatSync(file);
27903
28187
  } catch {
27904
28188
  return [];
27905
28189
  }
@@ -27907,7 +28191,7 @@ function readLedgerSync(file, nowMs) {
27907
28191
  if (stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0) return [];
27908
28192
  let parsed;
27909
28193
  try {
27910
- parsed = JSON.parse(import_node_fs49.default.readFileSync(file, "utf-8"));
28194
+ parsed = JSON.parse(import_node_fs50.default.readFileSync(file, "utf-8"));
27911
28195
  } catch {
27912
28196
  return [];
27913
28197
  }
@@ -27927,7 +28211,7 @@ function claimHarnessContinuation(repoRoot, claim2) {
27927
28211
  const nowMs = claim2.nowMs ?? Date.now();
27928
28212
  try {
27929
28213
  const file = harnessContinuationPath(repoRoot);
27930
- import_node_fs49.default.mkdirSync(import_node_path62.default.dirname(file), { recursive: true, mode: 448 });
28214
+ import_node_fs50.default.mkdirSync(import_node_path62.default.dirname(file), { recursive: true, mode: 448 });
27931
28215
  return withFileLockSync(
27932
28216
  `${file}.lock`,
27933
28217
  { activity: "harness continuation claim", waitMs: HARNESS_CONTINUATION_LOCK_WAIT_MS },
@@ -27959,7 +28243,7 @@ function settleHarnessContinuation(repoRoot, toolUseId, settlement) {
27959
28243
  const nowMs = settlement.nowMs ?? Date.now();
27960
28244
  try {
27961
28245
  const file = harnessContinuationPath(repoRoot);
27962
- import_node_fs49.default.mkdirSync(import_node_path62.default.dirname(file), { recursive: true, mode: 448 });
28246
+ import_node_fs50.default.mkdirSync(import_node_path62.default.dirname(file), { recursive: true, mode: 448 });
27963
28247
  withFileLockSync(
27964
28248
  `${file}.lock`,
27965
28249
  { activity: "harness continuation settlement", waitMs: HARNESS_CONTINUATION_LOCK_WAIT_MS },
@@ -27983,12 +28267,12 @@ function settleHarnessContinuation(repoRoot, toolUseId, settlement) {
27983
28267
  } catch {
27984
28268
  }
27985
28269
  }
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;
28270
+ var import_node_crypto31, import_node_fs50, 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;
27987
28271
  var init_harness_continuation = __esm({
27988
28272
  "src/core/harness-continuation.ts"() {
27989
28273
  "use strict";
27990
- import_node_crypto30 = require("crypto");
27991
- import_node_fs49 = __toESM(require("fs"));
28274
+ import_node_crypto31 = require("crypto");
28275
+ import_node_fs50 = __toESM(require("fs"));
27992
28276
  import_node_path62 = __toESM(require("path"));
27993
28277
  init_atomic_file();
27994
28278
  init_file_lock();
@@ -28010,7 +28294,7 @@ function isTransitionalHarnessPhase(phase) {
28010
28294
  function findHarnessGateRepoRoot(cwd) {
28011
28295
  let current = import_node_path63.default.resolve(cwd);
28012
28296
  for (let depth = 0; depth < REPO_ROOT_MAX_DEPTH; depth += 1) {
28013
- if (import_node_fs50.default.existsSync(import_node_path63.default.join(current, ".devrouter.yml"))) {
28297
+ if (import_node_fs51.default.existsSync(import_node_path63.default.join(current, ".devrouter.yml"))) {
28014
28298
  return current;
28015
28299
  }
28016
28300
  const parent = import_node_path63.default.dirname(current);
@@ -28105,11 +28389,11 @@ function parseHarnessGateBudget(value) {
28105
28389
  }
28106
28390
  return Math.min(Math.trunc(parsed), HARNESS_GATE_MAX_BUDGET_MS);
28107
28391
  }
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;
28392
+ var import_node_fs51, import_node_path63, HARNESS_GATE_DEFAULT_BUDGET_MS, HARNESS_GATE_MAX_BUDGET_MS, HARNESS_GATE_POLL_INTERVAL_MS, TRANSITIONAL_PHASES, REPO_ROOT_MAX_DEPTH;
28109
28393
  var init_harness_gate = __esm({
28110
28394
  "src/core/harness-gate.ts"() {
28111
28395
  "use strict";
28112
- import_node_fs50 = __toESM(require("fs"));
28396
+ import_node_fs51 = __toESM(require("fs"));
28113
28397
  import_node_path63 = __toESM(require("path"));
28114
28398
  init_reliability_operation_store();
28115
28399
  init_workspace();
@@ -28369,7 +28653,7 @@ async function runHarnessCommand(method, options, repoPath, dependencies = {}) {
28369
28653
  }
28370
28654
  const budgetMs = parseHarnessGateBudget(options.waitBudgetMs);
28371
28655
  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;
28372
- const payloadSha256 = (0, import_node_crypto31.createHash)("sha256").update(JSON.stringify(toolInput)).digest("hex");
28656
+ const payloadSha256 = (0, import_node_crypto32.createHash)("sha256").update(JSON.stringify(toolInput)).digest("hex");
28373
28657
  const claim2 = dependencies.claim ?? ((root, input2) => {
28374
28658
  const outcome = claimHarnessContinuation(root, input2);
28375
28659
  if (outcome.kind === "claimed") {
@@ -28477,11 +28761,11 @@ async function runHarnessCommand(method, options, repoPath, dependencies = {}) {
28477
28761
  `
28478
28762
  );
28479
28763
  }
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;
28764
+ var import_node_crypto32, MAX_HOOK_PAYLOAD_BYTES, STDIN_TIMEOUT_MS, MAX_TOOL_USE_ID_LENGTH2, DEVROUTER_EXECUTABLES, LAUNCHER_WORDS, LAUNCHER_SUBCOMMANDS, COMMAND_WORD_LIMIT;
28481
28765
  var init_harness = __esm({
28482
28766
  "src/commands/harness.ts"() {
28483
28767
  "use strict";
28484
- import_node_crypto31 = require("crypto");
28768
+ import_node_crypto32 = require("crypto");
28485
28769
  init_harness_continuation();
28486
28770
  init_harness_gate();
28487
28771
  MAX_HOOK_PAYLOAD_BYTES = 256 * 1024;
@@ -28597,7 +28881,7 @@ async function runOpenCommand(name) {
28597
28881
  );
28598
28882
  return;
28599
28883
  }
28600
- const result = (0, import_node_child_process40.spawnSync)("open", [url], { encoding: "utf-8" });
28884
+ const result = (0, import_node_child_process41.spawnSync)("open", [url], { encoding: "utf-8" });
28601
28885
  if (result.status !== 0) {
28602
28886
  const details = [result.stdout, result.stderr].filter(Boolean).join("\n").trim();
28603
28887
  throw new Error(`Unable to open '${url}': ${details || "unknown error"}`);
@@ -28605,11 +28889,11 @@ async function runOpenCommand(name) {
28605
28889
  process.stdout.write(`Opened ${url}
28606
28890
  `);
28607
28891
  }
28608
- var import_node_child_process40;
28892
+ var import_node_child_process41;
28609
28893
  var init_open = __esm({
28610
28894
  "src/commands/open.ts"() {
28611
28895
  "use strict";
28612
- import_node_child_process40 = require("child_process");
28896
+ import_node_child_process41 = require("child_process");
28613
28897
  init_docker();
28614
28898
  init_host_routes();
28615
28899
  init_repo_config();
@@ -28653,7 +28937,7 @@ async function runLogsCommand(options) {
28653
28937
  const args = ["logs", "--tail", tail, ROUTER_CONTAINER_NAME];
28654
28938
  if (options.follow) {
28655
28939
  args.splice(1, 0, "-f");
28656
- const child = (0, import_node_child_process41.spawn)("docker", args, { stdio: "inherit" });
28940
+ const child = (0, import_node_child_process42.spawn)("docker", args, { stdio: "inherit" });
28657
28941
  const onSignal = () => {
28658
28942
  child.kill("SIGTERM");
28659
28943
  };
@@ -28667,17 +28951,17 @@ async function runLogsCommand(options) {
28667
28951
  });
28668
28952
  });
28669
28953
  } else {
28670
- const result = (0, import_node_child_process41.spawnSync)("docker", args, { stdio: "inherit" });
28954
+ const result = (0, import_node_child_process42.spawnSync)("docker", args, { stdio: "inherit" });
28671
28955
  if (result.status !== 0) {
28672
28956
  throw new Error("Failed to retrieve router logs.");
28673
28957
  }
28674
28958
  }
28675
28959
  }
28676
- var import_node_child_process41;
28960
+ var import_node_child_process42;
28677
28961
  var init_logs = __esm({
28678
28962
  "src/commands/logs.ts"() {
28679
28963
  "use strict";
28680
- import_node_child_process41 = require("child_process");
28964
+ import_node_child_process42 = require("child_process");
28681
28965
  init_docker();
28682
28966
  init_lifecycle_diagnostics();
28683
28967
  init_router();
@@ -28721,11 +29005,11 @@ function asRecord3(value) {
28721
29005
  return value;
28722
29006
  }
28723
29007
  function readJson(filePath) {
28724
- if (!import_node_fs51.default.existsSync(filePath)) {
29008
+ if (!import_node_fs52.default.existsSync(filePath)) {
28725
29009
  return void 0;
28726
29010
  }
28727
29011
  try {
28728
- return JSON.parse(import_node_fs51.default.readFileSync(filePath, "utf-8"));
29012
+ return JSON.parse(import_node_fs52.default.readFileSync(filePath, "utf-8"));
28729
29013
  } catch {
28730
29014
  return void 0;
28731
29015
  }
@@ -28772,7 +29056,7 @@ function inspectPackageManager(repoPath, pkg) {
28772
29056
  ["bun.lock", "bun"]
28773
29057
  ];
28774
29058
  for (const [fileName, name] of lockfiles) {
28775
- if (import_node_fs51.default.existsSync(import_node_path64.default.join(repoPath, fileName))) {
29059
+ if (import_node_fs52.default.existsSync(import_node_path64.default.join(repoPath, fileName))) {
28776
29060
  return { name, source: fileName };
28777
29061
  }
28778
29062
  }
@@ -28788,8 +29072,8 @@ function inspectNode(repoPath, pkg) {
28788
29072
  return { version: engines.node, source: "package.json:engines.node" };
28789
29073
  }
28790
29074
  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();
29075
+ if (import_node_fs52.default.existsSync(nvmrc)) {
29076
+ const version = import_node_fs52.default.readFileSync(nvmrc, "utf-8").trim();
28793
29077
  return { version, source: ".nvmrc" };
28794
29078
  }
28795
29079
  return void 0;
@@ -28868,7 +29152,7 @@ function composeFiles(repoPath) {
28868
29152
  ...configuredComposeFiles(repoPath)
28869
29153
  ];
28870
29154
  return Array.from(new Set(candidates)).filter(
28871
- (fileName) => import_node_fs51.default.existsSync(import_node_path64.default.join(repoPath, fileName))
29155
+ (fileName) => import_node_fs52.default.existsSync(import_node_path64.default.join(repoPath, fileName))
28872
29156
  );
28873
29157
  }
28874
29158
  function stringArray2(value) {
@@ -28906,7 +29190,7 @@ function inspectServices(repoPath) {
28906
29190
  const services = [];
28907
29191
  for (const fileName of composeFiles(repoPath)) {
28908
29192
  try {
28909
- const parsed = import_yaml8.default.parse(import_node_fs51.default.readFileSync(import_node_path64.default.join(repoPath, fileName), "utf-8"));
29193
+ const parsed = import_yaml8.default.parse(import_node_fs52.default.readFileSync(import_node_path64.default.join(repoPath, fileName), "utf-8"));
28910
29194
  const serviceMap = asRecord3(asRecord3(parsed)?.services);
28911
29195
  for (const [name, value] of Object.entries(serviceMap ?? {})) {
28912
29196
  const service = asRecord3(value);
@@ -28941,8 +29225,8 @@ function inspectServices(repoPath) {
28941
29225
  return services;
28942
29226
  }
28943
29227
  function inspectEnvFiles(repoPath) {
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");
29228
+ const files = import_node_fs52.default.readdirSync(repoPath).filter((fileName) => /^\.env(\.|$)/.test(fileName)).sort().map((fileName) => {
29229
+ const content = import_node_fs52.default.readFileSync(import_node_path64.default.join(repoPath, fileName), "utf-8");
28946
29230
  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();
28947
29231
  return { path: fileName, names };
28948
29232
  });
@@ -28957,17 +29241,17 @@ function inspectEnvFiles(repoPath) {
28957
29241
  }
28958
29242
  function inspectDevcontainer(repoPath) {
28959
29243
  const dir = import_node_path64.default.join(repoPath, ".devcontainer");
28960
- if (!import_node_fs51.default.existsSync(dir)) {
29244
+ if (!import_node_fs52.default.existsSync(dir)) {
28961
29245
  return { exists: false, files: [] };
28962
29246
  }
28963
29247
  return {
28964
29248
  exists: true,
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}`)
29249
+ files: import_node_fs52.default.readdirSync(dir).filter((fileName) => import_node_fs52.default.statSync(import_node_path64.default.join(dir, fileName)).isFile()).sort().map((fileName) => `.devcontainer/${fileName}`)
28966
29250
  };
28967
29251
  }
28968
29252
  function inspectDevrouter(repoPath) {
28969
29253
  const configPath = getRepoConfigPath(repoPath);
28970
- if (!import_node_fs51.default.existsSync(configPath)) {
29254
+ if (!import_node_fs52.default.existsSync(configPath)) {
28971
29255
  return {
28972
29256
  exists: false,
28973
29257
  configPath,
@@ -29011,15 +29295,15 @@ function inspectAgentGuidance(repoPath) {
29011
29295
  ["AGENTS.md", "agents"],
29012
29296
  ["CLAUDE.md", "claude"]
29013
29297
  ]) {
29014
- if (import_node_fs51.default.existsSync(import_node_path64.default.join(repoPath, fileName))) {
29298
+ if (import_node_fs52.default.existsSync(import_node_path64.default.join(repoPath, fileName))) {
29015
29299
  results.push({ path: fileName, kind });
29016
29300
  }
29017
29301
  }
29018
29302
  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()) {
29303
+ if (import_node_fs52.default.existsSync(skillsDir)) {
29304
+ for (const name of import_node_fs52.default.readdirSync(skillsDir).sort()) {
29021
29305
  const skillPath = import_node_path64.default.join(skillsDir, name, "SKILL.md");
29022
- if (import_node_fs51.default.existsSync(skillPath)) {
29306
+ if (import_node_fs52.default.existsSync(skillPath)) {
29023
29307
  results.push({ path: relative(repoPath, skillPath), kind: "skill" });
29024
29308
  }
29025
29309
  }
@@ -29081,11 +29365,11 @@ function inspectRepo(options = {}) {
29081
29365
  issues: buildIssues(reportWithoutIssues)
29082
29366
  };
29083
29367
  }
29084
- var import_node_fs51, import_node_path64, import_yaml8;
29368
+ var import_node_fs52, import_node_path64, import_yaml8;
29085
29369
  var init_repo_inspect = __esm({
29086
29370
  "src/core/repo-inspect.ts"() {
29087
29371
  "use strict";
29088
- import_node_fs51 = __toESM(require("fs"));
29372
+ import_node_fs52 = __toESM(require("fs"));
29089
29373
  import_node_path64 = __toESM(require("path"));
29090
29374
  import_yaml8 = __toESM(require("yaml"));
29091
29375
  init_repo_config();
@@ -29191,7 +29475,7 @@ function requiredFileChecks(repoPath) {
29191
29475
  ".devcontainer/docker-compose.yml",
29192
29476
  ".devrouter.yml"
29193
29477
  ];
29194
- const missing = required.filter((fileName) => !import_node_fs52.default.existsSync(import_node_path65.default.join(repoPath, fileName)));
29478
+ const missing = required.filter((fileName) => !import_node_fs53.default.existsSync(import_node_path65.default.join(repoPath, fileName)));
29195
29479
  return {
29196
29480
  id: "repo.devcontainer.verify-files",
29197
29481
  level: missing.length === 0 ? "ok" : "error",
@@ -29416,11 +29700,11 @@ async function verifyDevcontainer(options = {}) {
29416
29700
  nextSteps: collectNextSteps3(checks)
29417
29701
  };
29418
29702
  }
29419
- var import_node_fs52, import_node_path65;
29703
+ var import_node_fs53, import_node_path65;
29420
29704
  var init_devcontainer_verify = __esm({
29421
29705
  "src/core/devcontainer-verify.ts"() {
29422
29706
  "use strict";
29423
- import_node_fs52 = __toESM(require("fs"));
29707
+ import_node_fs53 = __toESM(require("fs"));
29424
29708
  import_node_path65 = __toESM(require("path"));
29425
29709
  init_capabilities();
29426
29710
  init_doctor();
@@ -29764,7 +30048,7 @@ function plannedFiles(repoPath, version) {
29764
30048
  }
29765
30049
  function classifyFile(repoPath, file) {
29766
30050
  const absolutePath = import_node_path66.default.join(repoPath, file.relativePath);
29767
- if (!import_node_fs53.default.existsSync(absolutePath)) {
30051
+ if (!import_node_fs54.default.existsSync(absolutePath)) {
29768
30052
  return {
29769
30053
  path: file.relativePath,
29770
30054
  action: "create",
@@ -29772,7 +30056,7 @@ function classifyFile(repoPath, file) {
29772
30056
  bytes: Buffer.byteLength(file.content)
29773
30057
  };
29774
30058
  }
29775
- const current = import_node_fs53.default.readFileSync(absolutePath, "utf-8");
30059
+ const current = import_node_fs54.default.readFileSync(absolutePath, "utf-8");
29776
30060
  if (!current.includes(MANAGED_MARKER2)) {
29777
30061
  return {
29778
30062
  path: file.relativePath,
@@ -29830,10 +30114,10 @@ function buildPlan(repoPath, dryRun, version) {
29830
30114
  }
29831
30115
  function writeFile(repoPath, file) {
29832
30116
  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");
30117
+ import_node_fs54.default.mkdirSync(import_node_path66.default.dirname(absolutePath), { recursive: true });
30118
+ import_node_fs54.default.writeFileSync(absolutePath, file.content, "utf-8");
29835
30119
  if (file.executable) {
29836
- import_node_fs53.default.chmodSync(absolutePath, 493);
30120
+ import_node_fs54.default.chmodSync(absolutePath, 493);
29837
30121
  }
29838
30122
  }
29839
30123
  function writeDevcontainer(options = {}) {
@@ -29871,11 +30155,11 @@ function writeDevcontainer(options = {}) {
29871
30155
  nextSteps: postWriteNextSteps(repoPath)
29872
30156
  };
29873
30157
  }
29874
- var import_node_fs53, import_node_path66, MANAGED_MARKER2, DEFAULT_DEVROUTER_VERSION, DEFAULT_PNPM_VERSION, VALID_PACKAGE_VERSION_RE;
30158
+ var import_node_fs54, import_node_path66, MANAGED_MARKER2, DEFAULT_DEVROUTER_VERSION, DEFAULT_PNPM_VERSION, VALID_PACKAGE_VERSION_RE;
29875
30159
  var init_devcontainer_write = __esm({
29876
30160
  "src/core/devcontainer-write.ts"() {
29877
30161
  "use strict";
29878
- import_node_fs53 = __toESM(require("fs"));
30162
+ import_node_fs54 = __toESM(require("fs"));
29879
30163
  import_node_path66 = __toESM(require("path"));
29880
30164
  init_repo_config();
29881
30165
  init_repo_inspect();
@@ -30216,7 +30500,7 @@ function loadContract(repoPath, contractPath) {
30216
30500
  const absolutePath = assertPathWithinRepo(contractPath, repoPath, "Profile plan contract");
30217
30501
  let metadata;
30218
30502
  try {
30219
- metadata = import_node_fs54.default.lstatSync(absolutePath);
30503
+ metadata = import_node_fs55.default.lstatSync(absolutePath);
30220
30504
  } catch (error) {
30221
30505
  fail3(`could not inspect profile plan contract '${contractPath}': ${error.message}`);
30222
30506
  }
@@ -30226,10 +30510,10 @@ function loadContract(repoPath, contractPath) {
30226
30510
  if (metadata.size > MAX_CONTRACT_BYTES) {
30227
30511
  fail3(`profile plan contract exceeds the ${MAX_CONTRACT_BYTES}-byte limit.`);
30228
30512
  }
30229
- const realRepo = import_node_fs54.default.realpathSync(repoPath);
30230
- const realContract = import_node_fs54.default.realpathSync(absolutePath);
30513
+ const realRepo = import_node_fs55.default.realpathSync(repoPath);
30514
+ const realContract = import_node_fs55.default.realpathSync(absolutePath);
30231
30515
  assertPathWithinRepo(realContract, realRepo, "Profile plan contract");
30232
- const source = import_node_fs54.default.readFileSync(absolutePath, "utf-8");
30516
+ const source = import_node_fs55.default.readFileSync(absolutePath, "utf-8");
30233
30517
  if (Buffer.byteLength(source, "utf-8") > MAX_CONTRACT_BYTES) {
30234
30518
  fail3(`profile plan contract exceeds the ${MAX_CONTRACT_BYTES}-byte limit.`);
30235
30519
  }
@@ -30249,11 +30533,11 @@ function resolveProfilePlan(options) {
30249
30533
  contractPath: loaded.contractPath
30250
30534
  });
30251
30535
  }
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;
30536
+ var import_node_fs55, import_node_path67, import_yaml9, CONTRACT_VERSION, MAX_CONTRACT_BYTES, MAX_APP_MAPPINGS, MAX_BINDING_KEYS, MAX_BINDING_VALUES, MAX_LITERAL_LENGTH, BINDING_KEY;
30253
30537
  var init_profile_plan = __esm({
30254
30538
  "src/core/profile-plan.ts"() {
30255
30539
  "use strict";
30256
- import_node_fs54 = __toESM(require("fs"));
30540
+ import_node_fs55 = __toESM(require("fs"));
30257
30541
  import_node_path67 = __toESM(require("path"));
30258
30542
  import_yaml9 = require("yaml");
30259
30543
  init_paths();
@@ -30727,7 +31011,7 @@ function loadPreviewManifest(repoPath) {
30727
31011
  const manifestPath = assertPathWithinRepo(MANIFEST_FILE_NAME, resolvedRepo, "Preview manifest");
30728
31012
  let metadata;
30729
31013
  try {
30730
- metadata = import_node_fs55.default.lstatSync(manifestPath);
31014
+ metadata = import_node_fs56.default.lstatSync(manifestPath);
30731
31015
  } catch (error) {
30732
31016
  fail4(`could not inspect preview manifest '${MANIFEST_FILE_NAME}': ${error.message}`);
30733
31017
  }
@@ -30737,24 +31021,24 @@ function loadPreviewManifest(repoPath) {
30737
31021
  if (metadata.size > MAX_MANIFEST_BYTES) {
30738
31022
  fail4(`preview manifest exceeds the ${MAX_MANIFEST_BYTES}-byte limit.`);
30739
31023
  }
30740
- const realRepo = import_node_fs55.default.realpathSync(resolvedRepo);
30741
- const realManifest = import_node_fs55.default.realpathSync(manifestPath);
31024
+ const realRepo = import_node_fs56.default.realpathSync(resolvedRepo);
31025
+ const realManifest = import_node_fs56.default.realpathSync(manifestPath);
30742
31026
  assertPathWithinRepo(realManifest, realRepo, "Preview manifest");
30743
- const source = import_node_fs55.default.readFileSync(manifestPath, "utf-8");
31027
+ const source = import_node_fs56.default.readFileSync(manifestPath, "utf-8");
30744
31028
  if (Buffer.byteLength(source, "utf-8") > MAX_MANIFEST_BYTES) {
30745
31029
  fail4(`preview manifest exceeds the ${MAX_MANIFEST_BYTES}-byte limit.`);
30746
31030
  }
30747
31031
  return {
30748
31032
  manifest: parsePreviewManifest(source),
30749
- digest: (0, import_node_crypto32.createHash)("sha256").update(source, "utf-8").digest("hex")
31033
+ digest: (0, import_node_crypto33.createHash)("sha256").update(source, "utf-8").digest("hex")
30750
31034
  };
30751
31035
  }
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;
31036
+ var import_node_crypto33, import_node_fs56, 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;
30753
31037
  var init_manifest = __esm({
30754
31038
  "src/core/preview/manifest.ts"() {
30755
31039
  "use strict";
30756
- import_node_crypto32 = require("crypto");
30757
- import_node_fs55 = __toESM(require("fs"));
31040
+ import_node_crypto33 = require("crypto");
31041
+ import_node_fs56 = __toESM(require("fs"));
30758
31042
  import_node_path69 = __toESM(require("path"));
30759
31043
  import_yaml10 = require("yaml");
30760
31044
  init_paths();
@@ -31898,7 +32182,7 @@ var init_state = __esm({
31898
32182
  function previewIdentityKey(identity2) {
31899
32183
  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)
31900
32184
  throw new Error("Invalid immutable preview identity.");
31901
- return (0, import_node_crypto33.createHash)("sha256").update(JSON.stringify([identity2.forgeHost, identity2.projectId, identity2.change])).digest("hex");
32185
+ return (0, import_node_crypto34.createHash)("sha256").update(JSON.stringify([identity2.forgeHost, identity2.projectId, identity2.change])).digest("hex");
31902
32186
  }
31903
32187
  function costs(pool) {
31904
32188
  return Object.values(pool.sessions).reduce((total, entry) => total + entry.reservedCents, 0);
@@ -31954,11 +32238,11 @@ function sessionReady(entry, now, expiresAt) {
31954
32238
  ...expiresAt === void 0 ? {} : { expiresAt }
31955
32239
  });
31956
32240
  }
31957
- var import_node_crypto33, PreviewAdmissionSuperseded, PreviewEngine;
32241
+ var import_node_crypto34, PreviewAdmissionSuperseded, PreviewEngine;
31958
32242
  var init_engine = __esm({
31959
32243
  "src/core/preview/engine.ts"() {
31960
32244
  "use strict";
31961
- import_node_crypto33 = require("crypto");
32245
+ import_node_crypto34 = require("crypto");
31962
32246
  init_command();
31963
32247
  init_lifecycle();
31964
32248
  init_state();
@@ -32092,7 +32376,7 @@ var init_engine = __esm({
32092
32376
  } else {
32093
32377
  if (current && current.session.status !== "stopped" && current.session.status !== "expired")
32094
32378
  throw new Error("Previous generation cleanup is incomplete.");
32095
- const generation = (0, import_node_crypto33.createHash)("sha256").update(receipt).digest("hex").slice(0, 24);
32379
+ const generation = (0, import_node_crypto34.createHash)("sha256").update(receipt).digest("hex").slice(0, 24);
32096
32380
  pool2.sessions[id5] = {
32097
32381
  identity: input2.identity,
32098
32382
  session: createPreviewSession({
@@ -32748,11 +33032,11 @@ function decodeInbox(raw) {
32748
33032
  }
32749
33033
  return structuredClone(row);
32750
33034
  }
32751
- var import_node_crypto34, PreviewController;
33035
+ var import_node_crypto35, PreviewController;
32752
33036
  var init_controller2 = __esm({
32753
33037
  "src/core/preview/controller.ts"() {
32754
33038
  "use strict";
32755
- import_node_crypto34 = require("crypto");
33039
+ import_node_crypto35 = require("crypto");
32756
33040
  init_command();
32757
33041
  init_engine();
32758
33042
  init_status3();
@@ -32959,7 +33243,7 @@ Command refused: ${inbox.refusal}. Post a new command when the condition is reso
32959
33243
  Available profiles: ${this.options.aliases.map((alias) => `\`${alias}\``).join(", ")}.` : ""}
32960
33244
 
32961
33245
  ${marker}`;
32962
- const fingerprint = (0, import_node_crypto34.createHash)("sha256").update(body).digest("hex"), now = this.options.now();
33246
+ const fingerprint = (0, import_node_crypto35.createHash)("sha256").update(body).digest("hex"), now = this.options.now();
32963
33247
  if (inbox.status?.bodyDigest === fingerprint) {
32964
33248
  if (inbox.needsStatus) await this.update(change, (row) => ({ ...row, needsStatus: false }));
32965
33249
  return;
@@ -32970,7 +33254,7 @@ ${marker}`;
32970
33254
  let commentId = inbox.status?.commentId;
32971
33255
  if (!commentId) commentId = await this.options.forge.findStatus(change, marker) ?? void 0;
32972
33256
  let create = false;
32973
- const intentId = (0, import_node_crypto34.randomUUID)();
33257
+ const intentId = (0, import_node_crypto35.randomUUID)();
32974
33258
  if (!inbox.status)
32975
33259
  inbox = await this.update(change, (row) => {
32976
33260
  create = !row.status;
@@ -33720,7 +34004,7 @@ function canonicalizeImageConfiguration(configuration) {
33720
34004
  return JSON.stringify(normalizeConfiguration(configuration, "image configuration"));
33721
34005
  }
33722
34006
  function imageConfigurationDigest(configuration) {
33723
- return `sha256:${(0, import_node_crypto35.createHash)("sha256").update(canonicalizeImageConfiguration(configuration), "utf-8").digest("hex")}`;
34007
+ return `sha256:${(0, import_node_crypto36.createHash)("sha256").update(canonicalizeImageConfiguration(configuration), "utf-8").digest("hex")}`;
33724
34008
  }
33725
34009
  function targetKey(target, configurationDigest) {
33726
34010
  return `${target}\0${configurationDigest}`;
@@ -33975,11 +34259,11 @@ function resolvePreviewImages(plan, prepared, expectedProducer, value) {
33975
34259
  images: Object.fromEntries(resourceReceipts)
33976
34260
  };
33977
34261
  }
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;
34262
+ var import_node_crypto36, 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;
33979
34263
  var init_images = __esm({
33980
34264
  "src/core/preview/images.ts"() {
33981
34265
  "use strict";
33982
- import_node_crypto35 = require("crypto");
34266
+ import_node_crypto36 = require("crypto");
33983
34267
  MAX_COLLECTION_ENTRIES2 = 256;
33984
34268
  MAX_CONFIGURATION_ENTRIES = 128;
33985
34269
  MAX_CONFIGURATION_KEY_LENGTH = 128;
@@ -34013,8 +34297,8 @@ function commandImageBackend(options) {
34013
34297
  throw new Error("Invalid installed image resolver command.");
34014
34298
  const verify = () => {
34015
34299
  for (const file of options.files) {
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)
34300
+ const stat = import_node_fs57.default.lstatSync(file.path);
34301
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 1024 * 1024 || (0, import_node_crypto37.createHash)("sha256").update(import_node_fs57.default.readFileSync(file.path)).digest("hex") !== file.sha256)
34018
34302
  throw new Error("Installed image resolver fingerprint differs.");
34019
34303
  }
34020
34304
  };
@@ -34026,7 +34310,7 @@ function commandImageBackend(options) {
34026
34310
  platform: options.platform,
34027
34311
  configurations: options.context(entry)
34028
34312
  });
34029
- const correlation = (0, import_node_crypto36.createHash)("sha256").update(
34313
+ const correlation = (0, import_node_crypto37.createHash)("sha256").update(
34030
34314
  JSON.stringify([
34031
34315
  entry.identity,
34032
34316
  entry.session.generation,
@@ -34050,7 +34334,7 @@ function commandImageBackend(options) {
34050
34334
  });
34051
34335
  if (Buffer.byteLength(input2) > 1024 * 1024)
34052
34336
  throw new Error("Image request exceeds its bound.");
34053
- const result = (0, import_node_child_process42.spawnSync)(options.command[0], options.command.slice(1), {
34337
+ const result = (0, import_node_child_process43.spawnSync)(options.command[0], options.command.slice(1), {
34054
34338
  shell: false,
34055
34339
  cwd: options.cwd,
34056
34340
  encoding: "utf8",
@@ -34088,13 +34372,13 @@ function commandImageBackend(options) {
34088
34372
  }
34089
34373
  };
34090
34374
  }
34091
- var import_node_child_process42, import_node_crypto36, import_node_fs56;
34375
+ var import_node_child_process43, import_node_crypto37, import_node_fs57;
34092
34376
  var init_image_command = __esm({
34093
34377
  "src/core/preview/image-command.ts"() {
34094
34378
  "use strict";
34095
- import_node_child_process42 = require("child_process");
34096
- import_node_crypto36 = require("crypto");
34097
- import_node_fs56 = __toESM(require("fs"));
34379
+ import_node_child_process43 = require("child_process");
34380
+ import_node_crypto37 = require("crypto");
34381
+ import_node_fs57 = __toESM(require("fs"));
34098
34382
  init_images();
34099
34383
  init_state();
34100
34384
  }
@@ -34787,9 +35071,9 @@ function compileNorthflankGraph(entry, policy, phase, owned = {
34787
35071
  const resource = resources.get(id5);
34788
35072
  if (!resource?.ports?.some((item) => item.name === port && item.public))
34789
35073
  throw new Error("Northflank origin binding requires a selected public port.");
34790
- return `https://${prefix}--${id5.slice(0, 16)}-${port}-${(0, import_node_crypto37.createHash)("sha256").update(id5).digest("hex").slice(0, 6)}.${policy.baseDomain}`;
35074
+ return `https://${prefix}--${id5.slice(0, 16)}-${port}-${(0, import_node_crypto38.createHash)("sha256").update(id5).digest("hex").slice(0, 6)}.${policy.baseDomain}`;
34791
35075
  };
34792
- const resourceName = (id5) => `${northflankAllocationPrefix(policy, entry.session.generation)}${(0, import_node_crypto37.createHash)("sha256").update(id5).digest("hex").slice(0, 10)}`;
35076
+ const resourceName = (id5) => `${northflankAllocationPrefix(policy, entry.session.generation)}${(0, import_node_crypto38.createHash)("sha256").update(id5).digest("hex").slice(0, 10)}`;
34793
35077
  const suffix = (value) => {
34794
35078
  if (value === void 0) return "";
34795
35079
  if (typeof value !== "string" || value.length > 1024 || !value.startsWith("/") || value.startsWith("//") || value.includes("\\") || new RegExp("\\s|\\p{Cc}", "u").test(value))
@@ -35216,7 +35500,7 @@ function compileNorthflankGraph(entry, policy, phase, owned = {
35216
35500
  ]
35217
35501
  }
35218
35502
  };
35219
- const digest5 = (0, import_node_crypto37.createHash)("sha256").update(JSON.stringify({ spec, runtimeSecrets })).digest("hex");
35503
+ const digest5 = (0, import_node_crypto38.createHash)("sha256").update(JSON.stringify({ spec, runtimeSecrets })).digest("hex");
35220
35504
  return {
35221
35505
  spec,
35222
35506
  digest: digest5,
@@ -35227,11 +35511,11 @@ function compileNorthflankGraph(entry, policy, phase, owned = {
35227
35511
  publicBuildEnvironment
35228
35512
  };
35229
35513
  }
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;
35514
+ var import_node_crypto38, 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;
35231
35515
  var init_northflank_graph = __esm({
35232
35516
  "src/core/preview/providers/northflank-graph.ts"() {
35233
35517
  "use strict";
35234
- import_node_crypto37 = require("crypto");
35518
+ import_node_crypto38 = require("crypto");
35235
35519
  import_node_net5 = require("net");
35236
35520
  ALLOCATION_PLAN = /^[a-zA-Z0-9-]{3,100}$/;
35237
35521
  DATABASE_NAME = /^[a-z][a-z0-9_]{0,62}$/;
@@ -35335,7 +35619,7 @@ function specDigest(value) {
35335
35619
  return digest3(definition(value));
35336
35620
  }
35337
35621
  function digest3(value) {
35338
- return (0, import_node_crypto38.createHash)("sha256").update(JSON.stringify(value)).digest("hex");
35622
+ return (0, import_node_crypto39.createHash)("sha256").update(JSON.stringify(value)).digest("hex");
35339
35623
  }
35340
35624
  function bindings(owned) {
35341
35625
  return {
@@ -35360,11 +35644,11 @@ function outputNames(graph) {
35360
35644
  )
35361
35645
  ].sort();
35362
35646
  }
35363
- var import_node_crypto38, resourceKinds, NorthflankOwnershipStore, northflankQualificationKeys, NorthflankFailure, NorthflankFenced, NorthflankPreviewProvider, nativeKinds, emptySpec;
35647
+ var import_node_crypto39, resourceKinds, NorthflankOwnershipStore, northflankQualificationKeys, NorthflankFailure, NorthflankFenced, NorthflankPreviewProvider, nativeKinds, emptySpec;
35364
35648
  var init_northflank = __esm({
35365
35649
  "src/core/preview/providers/northflank.ts"() {
35366
35650
  "use strict";
35367
- import_node_crypto38 = require("crypto");
35651
+ import_node_crypto39 = require("crypto");
35368
35652
  init_command();
35369
35653
  init_state();
35370
35654
  init_northflank_api();
@@ -35997,7 +36281,7 @@ var init_northflank = __esm({
35997
36281
  const settings = this.options.graph.generated?.[name];
35998
36282
  if (!settings)
35999
36283
  throw new NorthflankFailure("Northflank generated value is undeclared.");
36000
- value = (0, import_node_crypto38.randomBytes)(settings.bytes).toString(settings.encoding);
36284
+ value = (0, import_node_crypto39.randomBytes)(settings.bytes).toString(settings.encoding);
36001
36285
  }
36002
36286
  if (kind === "output" && value === void 0)
36003
36287
  throw new NorthflankFailure("Northflank output has no verified recipient.");
@@ -36500,10 +36784,10 @@ __export(installation_exports, {
36500
36784
  loadPreviewInstallation: () => loadPreviewInstallation
36501
36785
  });
36502
36786
  function readFile(file) {
36503
- const stat = import_node_fs57.default.lstatSync(file);
36787
+ const stat = import_node_fs58.default.lstatSync(file);
36504
36788
  if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 1024 * 1024)
36505
36789
  throw new Error("Preview control files must be regular files of at most 1 MiB.");
36506
- return import_node_fs57.default.readFileSync(file, "utf8");
36790
+ return import_node_fs58.default.readFileSync(file, "utf8");
36507
36791
  }
36508
36792
  function text5(value, pattern) {
36509
36793
  if (typeof value !== "string" || !pattern.test(value))
@@ -36516,7 +36800,7 @@ function integer2(value, min, max) {
36516
36800
  return value;
36517
36801
  }
36518
36802
  function loadPreviewInstallation(filename, options = {}) {
36519
- const root = import_node_path70.default.dirname(import_node_fs57.default.realpathSync(filename));
36803
+ const root = import_node_path70.default.dirname(import_node_fs58.default.realpathSync(filename));
36520
36804
  const raw = object5(JSON.parse(readFile(filename)), [
36521
36805
  "version",
36522
36806
  "installationId",
@@ -36599,7 +36883,7 @@ function loadPreviewInstallation(filename, options = {}) {
36599
36883
  const control = (input3) => {
36600
36884
  const ref2 = object5(input3, ["file", "sha256"]);
36601
36885
  const filename2 = text5(ref2.file, /^(?!\/)(?!.*(?:^|\/)\.\.(?:\/|$))[^\\\0]+$/);
36602
- const file = import_node_fs57.default.realpathSync(import_node_path70.default.join(root, filename2));
36886
+ const file = import_node_fs58.default.realpathSync(import_node_path70.default.join(root, filename2));
36603
36887
  if (!file.startsWith(`${root}${import_node_path70.default.sep}`))
36604
36888
  throw new Error("Preview control file escapes installation directory.");
36605
36889
  const source = readFile(file), expected = text5(ref2.sha256, /^[a-f0-9]{64}$/);
@@ -36662,7 +36946,7 @@ function loadPreviewInstallation(filename, options = {}) {
36662
36946
  return credential(secrets[ref2])();
36663
36947
  },
36664
36948
  accept: async (entry, urls) => {
36665
- const result = (0, import_node_child_process43.spawnSync)(command[0], command.slice(1), {
36949
+ const result = (0, import_node_child_process44.spawnSync)(command[0], command.slice(1), {
36666
36950
  cwd: root,
36667
36951
  shell: false,
36668
36952
  timeout: 3e4,
@@ -36724,7 +37008,7 @@ function loadPreviewInstallation(filename, options = {}) {
36724
37008
  const item = object5(input3, ["file", "sha256"]);
36725
37009
  const verified = control(input3);
36726
37010
  return {
36727
- path: import_node_fs57.default.realpathSync(import_node_path70.default.join(root, item.file)),
37011
+ path: import_node_fs58.default.realpathSync(import_node_path70.default.join(root, item.file)),
36728
37012
  sha256: verified.digest
36729
37013
  };
36730
37014
  }) : [];
@@ -36839,7 +37123,7 @@ function loadPreviewInstallation(filename, options = {}) {
36839
37123
  }
36840
37124
  },
36841
37125
  now,
36842
- workerId: (0, import_node_crypto39.randomUUID)(),
37126
+ workerId: (0, import_node_crypto40.randomUUID)(),
36843
37127
  policy: {
36844
37128
  installationId,
36845
37129
  maxActive,
@@ -36955,13 +37239,13 @@ async function initializePreviewInstallation(installationId, store) {
36955
37239
  await store.compareAndSwap(witnessKey, null, { installationId });
36956
37240
  await store.compareAndSwap(poolKey, null, createPreviewPool(installationId));
36957
37241
  }
36958
- var import_node_child_process43, import_node_crypto39, import_node_fs57, import_node_path70, MAX_COMMAND_AGE_MS, digest4, envName;
37242
+ var import_node_child_process44, import_node_crypto40, import_node_fs58, import_node_path70, MAX_COMMAND_AGE_MS, digest4, envName;
36959
37243
  var init_installation = __esm({
36960
37244
  "src/core/preview/installation.ts"() {
36961
37245
  "use strict";
36962
- import_node_child_process43 = require("child_process");
36963
- import_node_crypto39 = require("crypto");
36964
- import_node_fs57 = __toESM(require("fs"));
37246
+ import_node_child_process44 = require("child_process");
37247
+ import_node_crypto40 = require("crypto");
37248
+ import_node_fs58 = __toESM(require("fs"));
36965
37249
  import_node_path70 = __toESM(require("path"));
36966
37250
  init_repo_config();
36967
37251
  init_command();
@@ -36979,7 +37263,7 @@ var init_installation = __esm({
36979
37263
  init_state();
36980
37264
  init_azure_blob();
36981
37265
  MAX_COMMAND_AGE_MS = 15 * 6e4;
36982
- digest4 = (source) => (0, import_node_crypto39.createHash)("sha256").update(source).digest("hex");
37266
+ digest4 = (source) => (0, import_node_crypto40.createHash)("sha256").update(source).digest("hex");
36983
37267
  envName = (value) => {
36984
37268
  if (typeof value !== "string" || !/^DEVROUTER_PREVIEW_[A-Z0-9_]+$/.test(value))
36985
37269
  throw new Error("Preview credential references require scoped environment names.");
@@ -36998,14 +37282,14 @@ __export(preview_exports, {
36998
37282
  function readSource(repoPath, filename) {
36999
37283
  if (import_node_path71.default.isAbsolute(filename))
37000
37284
  throw new Error("Preview source path must be repository-relative.");
37001
- const root = import_node_fs58.default.realpathSync(repoPath);
37285
+ const root = import_node_fs59.default.realpathSync(repoPath);
37002
37286
  const file = assertPathWithinRepo(filename, root, "preview source");
37003
- const stat = import_node_fs58.default.lstatSync(file);
37287
+ const stat = import_node_fs59.default.lstatSync(file);
37004
37288
  if (stat.isSymbolicLink() || !stat.isFile() || stat.size > 1024 * 1024) {
37005
37289
  throw new Error("Preview source must be a regular file of at most 1 MiB.");
37006
37290
  }
37007
- assertPathWithinRepo(import_node_fs58.default.realpathSync(file), root, "preview source");
37008
- return JSON.parse(import_node_fs58.default.readFileSync(file, "utf8"));
37291
+ assertPathWithinRepo(import_node_fs59.default.realpathSync(file), root, "preview source");
37292
+ return JSON.parse(import_node_fs59.default.readFileSync(file, "utf8"));
37009
37293
  }
37010
37294
  async function runPreviewPlanCommand(options) {
37011
37295
  const repoPath = resolveRepoPath(options.repo);
@@ -37020,13 +37304,13 @@ async function runPreviewPlanCommand(options) {
37020
37304
  `);
37021
37305
  return;
37022
37306
  }
37023
- const localConfig = import_node_fs58.default.readFileSync(import_node_path71.default.join(repoPath, ".devrouter.yml"), "utf8");
37307
+ const localConfig = import_node_fs59.default.readFileSync(import_node_path71.default.join(repoPath, ".devrouter.yml"), "utf8");
37024
37308
  const report = buildPreviewPlan({
37025
37309
  repoPath,
37026
37310
  config: loadRepoConfig(repoPath, () => localConfig),
37027
37311
  manifest,
37028
37312
  manifestDigest: digest5,
37029
- localConfigDigest: (0, import_node_crypto40.createHash)("sha256").update(localConfig).digest("hex"),
37313
+ localConfigDigest: (0, import_node_crypto41.createHash)("sha256").update(localConfig).digest("hex"),
37030
37314
  ...options.alias === void 0 ? {} : { aliases: options.alias.split(",") },
37031
37315
  ...options.source === void 0 ? {} : { source: readSource(repoPath, options.source) }
37032
37316
  });
@@ -37045,7 +37329,7 @@ async function runPreviewPlanCommand(options) {
37045
37329
  }
37046
37330
  }
37047
37331
  async function runPreviewInitCommand(options) {
37048
- const repoPath = import_node_fs58.default.realpathSync(resolveRepoPath(options.repo));
37332
+ const repoPath = import_node_fs59.default.realpathSync(resolveRepoPath(options.repo));
37049
37333
  if (options.integration === "gitlab") return renderGitLabPreviewIntegration(repoPath, options);
37050
37334
  if (options.integration) return renderPreviewIntegration(repoPath, options);
37051
37335
  loadRepoConfig(repoPath);
@@ -37071,7 +37355,7 @@ async function runPreviewInitCommand(options) {
37071
37355
  const content = (0, import_yaml12.stringify)(manifest);
37072
37356
  if (options.write) {
37073
37357
  const file = import_node_path71.default.join(repoPath, ".devrouter.preview.yml");
37074
- import_node_fs58.default.writeFileSync(file, content, { flag: "wx", mode: 384 });
37358
+ import_node_fs59.default.writeFileSync(file, content, { flag: "wx", mode: 384 });
37075
37359
  }
37076
37360
  if (options.json) {
37077
37361
  printJSON({
@@ -37139,9 +37423,9 @@ function previewAsset(name) {
37139
37423
  import_node_path71.default.resolve(__dirname, "..", "assets", "preview", name),
37140
37424
  import_node_path71.default.resolve(__dirname, "..", "..", "assets", "preview", name)
37141
37425
  ];
37142
- const file = candidates.find((file2) => import_node_fs58.default.existsSync(file2));
37426
+ const file = candidates.find((file2) => import_node_fs59.default.existsSync(file2));
37143
37427
  if (!file) throw new Error("Installed preview integration asset is missing.");
37144
- return import_node_fs58.default.readFileSync(file, "utf8");
37428
+ return import_node_fs59.default.readFileSync(file, "utf8");
37145
37429
  }
37146
37430
  function renderPreviewAsset(name, replacements) {
37147
37431
  return previewAsset(name).replace(/__([A-Z_]+)__/g, (_match, key) => {
@@ -37176,9 +37460,9 @@ async function renderPreviewIntegration(repoPath, options) {
37176
37460
  const filename = `.github/workflows/devrouter-preview${options.sweeper ? "-sweep" : ""}.yml`;
37177
37461
  if (options.write) {
37178
37462
  const file = assertPathWithinRepo(filename, repoPath, "preview workflow");
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 });
37463
+ import_node_fs59.default.mkdirSync(import_node_path71.default.dirname(file), { recursive: true });
37464
+ assertPathWithinRepo(import_node_fs59.default.realpathSync(import_node_path71.default.dirname(file)), repoPath, "preview workflow parent");
37465
+ import_node_fs59.default.writeFileSync(file, content, { flag: "wx", mode: 384 });
37182
37466
  }
37183
37467
  if (options.json)
37184
37468
  printJSON({
@@ -37196,7 +37480,7 @@ async function renderGitLabPreviewIntegration(repoPath, options) {
37196
37480
  throw new Error(
37197
37481
  "GitLab integration requires reviewed --controller-ref and --controller-image digest."
37198
37482
  );
37199
- const integrity = `sha256-${(0, import_node_crypto40.createHash)("sha256").update(previewAsset("gitlab-template.yml")).digest("base64")}`;
37483
+ const integrity = `sha256-${(0, import_node_crypto41.createHash)("sha256").update(previewAsset("gitlab-template.yml")).digest("base64")}`;
37200
37484
  const replacements = {
37201
37485
  CONTROLLER_REF: String(options.controllerRef),
37202
37486
  CONTROLLER_IMAGE: String(options.controllerImage),
@@ -37205,7 +37489,7 @@ async function renderGitLabPreviewIntegration(repoPath, options) {
37205
37489
  const content = renderPreviewAsset("gitlab.yml", replacements);
37206
37490
  const filename = ".gitlab-ci.yml";
37207
37491
  if (options.write)
37208
- import_node_fs58.default.writeFileSync(assertPathWithinRepo(filename, repoPath, "GitLab preview caller"), content, {
37492
+ import_node_fs59.default.writeFileSync(assertPathWithinRepo(filename, repoPath, "GitLab preview caller"), content, {
37209
37493
  flag: "wx",
37210
37494
  mode: 384
37211
37495
  });
@@ -37220,12 +37504,12 @@ async function renderGitLabPreviewIntegration(repoPath, options) {
37220
37504
  });
37221
37505
  else process.stdout.write(content);
37222
37506
  }
37223
- var import_node_crypto40, import_node_fs58, import_node_path71, import_yaml12;
37507
+ var import_node_crypto41, import_node_fs59, import_node_path71, import_yaml12;
37224
37508
  var init_preview = __esm({
37225
37509
  "src/commands/preview.ts"() {
37226
37510
  "use strict";
37227
- import_node_crypto40 = require("crypto");
37228
- import_node_fs58 = __toESM(require("fs"));
37511
+ import_node_crypto41 = require("crypto");
37512
+ import_node_fs59 = __toESM(require("fs"));
37229
37513
  import_node_path71 = __toESM(require("path"));
37230
37514
  import_yaml12 = require("yaml");
37231
37515
  init_output();
@@ -37518,7 +37802,7 @@ function sanitizeRouterId(value) {
37518
37802
  return value.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
37519
37803
  }
37520
37804
  function repoHash(repoPath) {
37521
- return (0, import_node_crypto41.createHash)("sha1").update(import_node_path72.default.resolve(repoPath)).digest("hex").slice(0, 12);
37805
+ return (0, import_node_crypto42.createHash)("sha1").update(import_node_path72.default.resolve(repoPath)).digest("hex").slice(0, 12);
37522
37806
  }
37523
37807
  function asDockerApp(app) {
37524
37808
  return app.runtime === "docker";
@@ -37598,10 +37882,10 @@ function prepareDockerOverlay(repoPath, appName, apps, publishTcpPorts = false)
37598
37882
  throw new Error("No docker apps selected to prepare compose overlay.");
37599
37883
  }
37600
37884
  const cachePath = import_node_path72.default.join(CACHE_DIR, repoHash(repoPath), sanitizeRouterId(appName));
37601
- import_node_fs59.default.mkdirSync(cachePath, { recursive: true });
37885
+ import_node_fs60.default.mkdirSync(cachePath, { recursive: true });
37602
37886
  const overlayPath = import_node_path72.default.join(cachePath, "compose.devrouter.yml");
37603
37887
  const overlayDocument = buildOverlayDocument(dockerApps, publishTcpPorts);
37604
- import_node_fs59.default.writeFileSync(overlayPath, import_yaml13.default.stringify(overlayDocument, { lineWidth: 0 }), "utf-8");
37888
+ import_node_fs60.default.writeFileSync(overlayPath, import_yaml13.default.stringify(overlayDocument, { lineWidth: 0 }), "utf-8");
37605
37889
  return {
37606
37890
  overlayPath,
37607
37891
  composeFiles: ensureComposeFiles(dockerApps),
@@ -37615,7 +37899,7 @@ function runDockerComposeUp(repoPath, composeFiles2, overlayPath, services) {
37615
37899
  fileArgs.push("-f", resolved2);
37616
37900
  }
37617
37901
  const args = ["compose", ...fileArgs, "-f", overlayPath, "up", "-d", "--wait", ...services];
37618
- const result = (0, import_node_child_process44.spawnSync)("docker", args, {
37902
+ const result = (0, import_node_child_process45.spawnSync)("docker", args, {
37619
37903
  encoding: "utf-8",
37620
37904
  cwd: repoPath
37621
37905
  });
@@ -37643,7 +37927,7 @@ function queryRunningComposeServices(repoPath, composeFiles2, overlayPath, servi
37643
37927
  "--services",
37644
37928
  ...services
37645
37929
  ];
37646
- const result = (0, import_node_child_process44.spawnSync)("docker", args, {
37930
+ const result = (0, import_node_child_process45.spawnSync)("docker", args, {
37647
37931
  encoding: "utf-8",
37648
37932
  cwd: repoPath
37649
37933
  });
@@ -37669,7 +37953,7 @@ function runDockerComposeStop(repoPath, composeFiles2, overlayPath, services) {
37669
37953
  fileArgs.push("-f", resolved2);
37670
37954
  }
37671
37955
  const args = ["compose", ...fileArgs, "-f", overlayPath, "stop", ...services];
37672
- const result = (0, import_node_child_process44.spawnSync)("docker", args, {
37956
+ const result = (0, import_node_child_process45.spawnSync)("docker", args, {
37673
37957
  encoding: "utf-8",
37674
37958
  cwd: repoPath
37675
37959
  });
@@ -37695,7 +37979,7 @@ function runDockerComposeLogs(repoPath, composeFiles2, overlayPath, services, ta
37695
37979
  String(tail),
37696
37980
  ...services
37697
37981
  ];
37698
- (0, import_node_child_process44.spawnSync)("docker", args, {
37982
+ (0, import_node_child_process45.spawnSync)("docker", args, {
37699
37983
  stdio: "inherit",
37700
37984
  cwd: repoPath
37701
37985
  });
@@ -37707,7 +37991,7 @@ function queryMappedPort(repoPath, composeFiles2, overlayPath, service, internal
37707
37991
  fileArgs.push("-f", resolved2);
37708
37992
  }
37709
37993
  const args = ["compose", ...fileArgs, "-f", overlayPath, "port", service, String(internalPort)];
37710
- const result = (0, import_node_child_process44.spawnSync)("docker", args, {
37994
+ const result = (0, import_node_child_process45.spawnSync)("docker", args, {
37711
37995
  encoding: "utf-8",
37712
37996
  cwd: repoPath
37713
37997
  });
@@ -37721,13 +38005,13 @@ function queryMappedPort(repoPath, composeFiles2, overlayPath, service, internal
37721
38005
  const port = Number(match[1]);
37722
38006
  return Number.isInteger(port) && port > 0 ? port : void 0;
37723
38007
  }
37724
- var import_node_child_process44, import_node_crypto41, import_node_fs59, import_node_path72, import_yaml13;
38008
+ var import_node_child_process45, import_node_crypto42, import_node_fs60, import_node_path72, import_yaml13;
37725
38009
  var init_docker_run = __esm({
37726
38010
  "src/core/docker-run.ts"() {
37727
38011
  "use strict";
37728
- import_node_child_process44 = require("child_process");
37729
- import_node_crypto41 = require("crypto");
37730
- import_node_fs59 = __toESM(require("fs"));
38012
+ import_node_child_process45 = require("child_process");
38013
+ import_node_crypto42 = require("crypto");
38014
+ import_node_fs60 = __toESM(require("fs"));
37731
38015
  import_node_path72 = __toESM(require("path"));
37732
38016
  import_yaml13 = __toESM(require("yaml"));
37733
38017
  init_docker_error_guidance();
@@ -37818,7 +38102,7 @@ function isProcessRunning(pid) {
37818
38102
  }
37819
38103
  }
37820
38104
  function readProcessTree(rootPid) {
37821
- const result = (0, import_node_child_process45.spawnSync)("ps", ["-ax", "-o", "pid=,ppid="], { encoding: "utf-8" });
38105
+ const result = (0, import_node_child_process46.spawnSync)("ps", ["-ax", "-o", "pid=,ppid="], { encoding: "utf-8" });
37822
38106
  if (result.status !== 0) {
37823
38107
  return [rootPid];
37824
38108
  }
@@ -37929,12 +38213,12 @@ function detectListeningPorts(pids) {
37929
38213
  if (pids.length === 0) {
37930
38214
  return [];
37931
38215
  }
37932
- const result = (0, import_node_child_process45.spawnSync)("lsof", ["-nP", "-iTCP", "-sTCP:LISTEN", "-a", "-p", pids.join(",")], {
38216
+ const result = (0, import_node_child_process46.spawnSync)("lsof", ["-nP", "-iTCP", "-sTCP:LISTEN", "-a", "-p", pids.join(",")], {
37933
38217
  encoding: "utf-8"
37934
38218
  });
37935
38219
  if (result.error && result.error.code === "ENOENT") {
37936
38220
  if (process.platform === "linux") {
37937
- const ssResult = (0, import_node_child_process45.spawnSync)("ss", ["-H", "-lntp", "-p"], { encoding: "utf-8" });
38221
+ const ssResult = (0, import_node_child_process46.spawnSync)("ss", ["-H", "-lntp", "-p"], { encoding: "utf-8" });
37938
38222
  if (ssResult.status === 0 && ssResult.stdout) {
37939
38223
  return parseSsListeningPorts(ssResult.stdout, new Set(pids));
37940
38224
  }
@@ -37993,7 +38277,7 @@ async function runHostApp(repoPath, app, extraEnv = {}, secretManager, env, work
37993
38277
  app.hostRun.command,
37994
38278
  true
37995
38279
  ) : app.hostRun.command;
37996
- const child = (0, import_node_child_process45.spawn)(spawnCommand, {
38280
+ const child = (0, import_node_child_process46.spawn)(spawnCommand, {
37997
38281
  cwd: commandCwd,
37998
38282
  stdio: "inherit",
37999
38283
  shell: true,
@@ -38361,7 +38645,7 @@ async function execWithAppEnv(options) {
38361
38645
  options.command[0],
38362
38646
  true
38363
38647
  );
38364
- child = (0, import_node_child_process45.spawn)(wrapped, {
38648
+ child = (0, import_node_child_process46.spawn)(wrapped, {
38365
38649
  cwd: deps.repoPath,
38366
38650
  stdio: "inherit",
38367
38651
  shell: true,
@@ -38375,7 +38659,7 @@ async function execWithAppEnv(options) {
38375
38659
  false
38376
38660
  );
38377
38661
  const [cmd, ...wrappedArgs] = wrapped;
38378
- child = (0, import_node_child_process45.spawn)(cmd, wrappedArgs, {
38662
+ child = (0, import_node_child_process46.spawn)(cmd, wrappedArgs, {
38379
38663
  cwd: deps.repoPath,
38380
38664
  stdio: "inherit",
38381
38665
  shell: false,
@@ -38383,7 +38667,7 @@ async function execWithAppEnv(options) {
38383
38667
  });
38384
38668
  }
38385
38669
  } else if (options.shell) {
38386
- child = (0, import_node_child_process45.spawn)(options.command[0], {
38670
+ child = (0, import_node_child_process46.spawn)(options.command[0], {
38387
38671
  cwd: deps.repoPath,
38388
38672
  stdio: "inherit",
38389
38673
  shell: true,
@@ -38391,7 +38675,7 @@ async function execWithAppEnv(options) {
38391
38675
  });
38392
38676
  } else {
38393
38677
  const [command, ...args] = options.command;
38394
- child = (0, import_node_child_process45.spawn)(command, args, {
38678
+ child = (0, import_node_child_process46.spawn)(command, args, {
38395
38679
  cwd: deps.repoPath,
38396
38680
  stdio: "inherit",
38397
38681
  shell: false,
@@ -38412,11 +38696,11 @@ async function execWithAppEnv(options) {
38412
38696
  deps.stopDeps();
38413
38697
  }
38414
38698
  }
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;
38699
+ var import_node_child_process46, import_node_net6, import_node_process, import_promises, POLL_INTERVAL_MS, DEFAULT_PORT_TIMEOUT_MS, PROCESS_TERMINATION_GRACE_MS;
38416
38700
  var init_app_run = __esm({
38417
38701
  "src/core/app-run.ts"() {
38418
38702
  "use strict";
38419
- import_node_child_process45 = require("child_process");
38703
+ import_node_child_process46 = require("child_process");
38420
38704
  import_node_net6 = __toESM(require("net"));
38421
38705
  import_node_process = require("process");
38422
38706
  import_promises = require("readline/promises");
@@ -38562,7 +38846,7 @@ function canonicalJson(value) {
38562
38846
  `;
38563
38847
  }
38564
38848
  function sha256Hex(raw) {
38565
- return (0, import_node_crypto42.createHash)("sha256").update(raw).digest("hex");
38849
+ return (0, import_node_crypto43.createHash)("sha256").update(raw).digest("hex");
38566
38850
  }
38567
38851
  function candidateId(candidate) {
38568
38852
  const identity2 = {
@@ -38730,7 +39014,7 @@ function validateManifest(value) {
38730
39014
  return root;
38731
39015
  }
38732
39016
  function readManifest(file) {
38733
- const raw = import_node_fs60.default.readFileSync(file);
39017
+ const raw = import_node_fs61.default.readFileSync(file);
38734
39018
  let parsed;
38735
39019
  try {
38736
39020
  parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(raw));
@@ -38769,7 +39053,7 @@ function selectManifest(file, expectedSha256, ids2) {
38769
39053
  return canonicalJson(subset);
38770
39054
  }
38771
39055
  function gitLine(cwd, args) {
38772
- const result = (0, import_node_child_process46.spawnSync)("git", ["-C", cwd, ...args], {
39056
+ const result = (0, import_node_child_process47.spawnSync)("git", ["-C", cwd, ...args], {
38773
39057
  encoding: "utf-8",
38774
39058
  env: { ...process.env, GIT_OPTIONAL_LOCKS: "0", LC_ALL: "C" }
38775
39059
  });
@@ -38777,7 +39061,7 @@ function gitLine(cwd, args) {
38777
39061
  return result.status === 0 && !result.error && output2 ? output2 : void 0;
38778
39062
  }
38779
39063
  function readWorktreeIdentity(worktreePath) {
38780
- if (!import_node_fs60.default.existsSync(worktreePath)) return void 0;
39064
+ if (!import_node_fs61.default.existsSync(worktreePath)) return void 0;
38781
39065
  const real = comparableWorkspacePath(worktreePath);
38782
39066
  const commonDir = gitLine(real, ["rev-parse", "--path-format=absolute", "--git-common-dir"]);
38783
39067
  const gitDir = gitLine(real, ["rev-parse", "--path-format=absolute", "--git-dir"]);
@@ -38803,7 +39087,7 @@ function unmanagedActivityEpoch(worktreePath, gitDir) {
38803
39087
  worktreePath
38804
39088
  ]) {
38805
39089
  try {
38806
- const seconds = Math.floor(import_node_fs60.default.statSync(file).mtimeMs / 1e3);
39090
+ const seconds = Math.floor(import_node_fs61.default.statSync(file).mtimeMs / 1e3);
38807
39091
  latest = latest === void 0 ? seconds : Math.max(latest, seconds);
38808
39092
  } catch {
38809
39093
  }
@@ -38862,7 +39146,7 @@ function receiptPath(commonDir, manifestSha256, id5) {
38862
39146
  function matchingReceipt(candidate, manifestSha256) {
38863
39147
  try {
38864
39148
  const receipt = JSON.parse(
38865
- import_node_fs60.default.readFileSync(receiptPath(candidate.commonDir, manifestSha256, candidate.id), "utf-8")
39149
+ import_node_fs61.default.readFileSync(receiptPath(candidate.commonDir, manifestSha256, candidate.id), "utf-8")
38866
39150
  );
38867
39151
  return receipt.kind === RECEIPT_KIND && receipt.schemaVersion === SCHEMA_VERSION && receipt.manifestSha256 === manifestSha256 && receipt.candidateId === candidate.id && receipt.path === candidate.path && receipt.head === candidate.head;
38868
39152
  } catch {
@@ -38871,7 +39155,7 @@ function matchingReceipt(candidate, manifestSha256) {
38871
39155
  }
38872
39156
  function writeReceipt(candidate, manifestSha256, steps, nowEpoch) {
38873
39157
  const file = receiptPath(candidate.commonDir, manifestSha256, candidate.id);
38874
- import_node_fs60.default.mkdirSync(import_node_path73.default.dirname(file), { recursive: true });
39158
+ import_node_fs61.default.mkdirSync(import_node_path73.default.dirname(file), { recursive: true });
38875
39159
  writeFileAtomically(
38876
39160
  file,
38877
39161
  canonicalJson({
@@ -38889,7 +39173,7 @@ function writeReceipt(candidate, manifestSha256, steps, nowEpoch) {
38889
39173
  );
38890
39174
  }
38891
39175
  function runVetoCommand(command, worktreePath) {
38892
- const result = (0, import_node_child_process46.spawnSync)(command, [worktreePath], {
39176
+ const result = (0, import_node_child_process47.spawnSync)(command, [worktreePath], {
38893
39177
  stdio: ["ignore", "ignore", "inherit"],
38894
39178
  timeout: VETO_TIMEOUT_MS
38895
39179
  });
@@ -38904,7 +39188,7 @@ function runVetoCommand(command, worktreePath) {
38904
39188
  function assertVetoCommand(command) {
38905
39189
  if (!import_node_path73.default.isAbsolute(command)) throw new Error("--veto-command must be an absolute path");
38906
39190
  try {
38907
- import_node_fs60.default.accessSync(command, import_node_fs60.default.constants.X_OK);
39191
+ import_node_fs61.default.accessSync(command, import_node_fs61.default.constants.X_OK);
38908
39192
  } catch {
38909
39193
  throw new Error(`--veto-command '${command}' is missing or not executable`);
38910
39194
  }
@@ -38930,7 +39214,7 @@ function preflightManifestApply(options, action2, nowEpoch) {
38930
39214
  return { manifest, manifestSha256, now };
38931
39215
  }
38932
39216
  function deleteBranchIfUnmoved(candidate, steps) {
38933
- const exists2 = (0, import_node_child_process46.spawnSync)(
39217
+ const exists2 = (0, import_node_child_process47.spawnSync)(
38934
39218
  "git",
38935
39219
  ["-C", candidate.repo, "show-ref", "--verify", "--quiet", candidate.branchRef],
38936
39220
  { encoding: "utf-8" }
@@ -38939,7 +39223,7 @@ function deleteBranchIfUnmoved(candidate, steps) {
38939
39223
  if (exists2.status !== 0) {
38940
39224
  throw new Error(`cannot read branch ${candidate.branch}: ${(exists2.stderr ?? "").trim()}`);
38941
39225
  }
38942
- const result = (0, import_node_child_process46.spawnSync)(
39226
+ const result = (0, import_node_child_process47.spawnSync)(
38943
39227
  "git",
38944
39228
  ["-C", candidate.repo, "update-ref", "-d", candidate.branchRef, candidate.head],
38945
39229
  { encoding: "utf-8" }
@@ -38948,7 +39232,7 @@ function deleteBranchIfUnmoved(candidate, steps) {
38948
39232
  return `branch ${candidate.branch} no longer points at the approved HEAD; it was kept for review`;
38949
39233
  }
38950
39234
  steps.push("branch-deleted");
38951
- (0, import_node_child_process46.spawnSync)(
39235
+ (0, import_node_child_process47.spawnSync)(
38952
39236
  "git",
38953
39237
  ["-C", candidate.repo, "config", "--remove-section", `branch.${candidate.branch}`],
38954
39238
  { encoding: "utf-8" }
@@ -38986,7 +39270,7 @@ async function applyReclaimManifest(options, dependencies) {
38986
39270
  outcome.status = "not-attempted";
38987
39271
  continue;
38988
39272
  }
38989
- if (!import_node_fs60.default.existsSync(candidate.path) && matchingReceipt(candidate, manifestSha256)) {
39273
+ if (!import_node_fs61.default.existsSync(candidate.path) && matchingReceipt(candidate, manifestSha256)) {
38990
39274
  outcome.status = "already-done";
38991
39275
  continue;
38992
39276
  }
@@ -39055,13 +39339,13 @@ async function applyReclaimManifest(options, dependencies) {
39055
39339
  }
39056
39340
  return { manifestSha256, dryRun: !options.yes, targets, stoppedEarly };
39057
39341
  }
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;
39342
+ var import_node_child_process47, import_node_crypto43, import_node_fs61, 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;
39059
39343
  var init_worktree_reclaim = __esm({
39060
39344
  "src/core/worktree-reclaim.ts"() {
39061
39345
  "use strict";
39062
- import_node_child_process46 = require("child_process");
39063
- import_node_crypto42 = require("crypto");
39064
- import_node_fs60 = __toESM(require("fs"));
39346
+ import_node_child_process47 = require("child_process");
39347
+ import_node_crypto43 = require("crypto");
39348
+ import_node_fs61 = __toESM(require("fs"));
39065
39349
  import_node_path73 = __toESM(require("path"));
39066
39350
  init_atomic_file();
39067
39351
  init_workspace();
@@ -39326,14 +39610,14 @@ function readLeftoverContext(repoPath) {
39326
39610
  registriesReadable: snapshots.unavailable.length === 0,
39327
39611
  realpath: (file) => {
39328
39612
  try {
39329
- return import_node_fs61.default.realpathSync.native(file);
39613
+ return import_node_fs62.default.realpathSync.native(file);
39330
39614
  } catch {
39331
39615
  return void 0;
39332
39616
  }
39333
39617
  },
39334
39618
  exists: (file) => {
39335
39619
  try {
39336
- import_node_fs61.default.lstatSync(file);
39620
+ import_node_fs62.default.lstatSync(file);
39337
39621
  return true;
39338
39622
  } catch (error) {
39339
39623
  return error.code !== "ENOENT";
@@ -39457,7 +39741,7 @@ function validateLeftoverManifest(value) {
39457
39741
  return root;
39458
39742
  }
39459
39743
  function readLeftoverManifest(file) {
39460
- const raw = import_node_fs61.default.readFileSync(file);
39744
+ const raw = import_node_fs62.default.readFileSync(file);
39461
39745
  let parsed;
39462
39746
  try {
39463
39747
  parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(raw));
@@ -39471,7 +39755,7 @@ function readLeftoverManifest(file) {
39471
39755
  return { raw, manifest };
39472
39756
  }
39473
39757
  function gitCommonDir(repo) {
39474
- const result = (0, import_node_child_process47.spawnSync)(
39758
+ const result = (0, import_node_child_process48.spawnSync)(
39475
39759
  "git",
39476
39760
  ["-C", repo, "rev-parse", "--path-format=absolute", "--git-common-dir"],
39477
39761
  { encoding: "utf-8" }
@@ -39643,7 +39927,7 @@ function leftoverReceiptPath(commonDir, manifestSha256, id5) {
39643
39927
  }
39644
39928
  function writeLeftoverReceipt(commonDir, manifestSha256, candidate, steps, nowEpoch) {
39645
39929
  const file = leftoverReceiptPath(commonDir, manifestSha256, candidate.id);
39646
- import_node_fs61.default.mkdirSync(import_node_path74.default.dirname(file), { recursive: true });
39930
+ import_node_fs62.default.mkdirSync(import_node_path74.default.dirname(file), { recursive: true });
39647
39931
  writeFileAtomically(
39648
39932
  file,
39649
39933
  canonicalJson({
@@ -39659,12 +39943,12 @@ function writeLeftoverReceipt(commonDir, manifestSha256, candidate, steps, nowEp
39659
39943
  })
39660
39944
  );
39661
39945
  }
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;
39946
+ var import_node_child_process48, import_node_fs62, 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;
39663
39947
  var init_docker_leftovers = __esm({
39664
39948
  "src/core/docker-leftovers.ts"() {
39665
39949
  "use strict";
39666
- import_node_child_process47 = require("child_process");
39667
- import_node_fs61 = __toESM(require("fs"));
39950
+ import_node_child_process48 = require("child_process");
39951
+ import_node_fs62 = __toESM(require("fs"));
39668
39952
  import_node_path74 = __toESM(require("path"));
39669
39953
  init_atomic_file();
39670
39954
  init_devpod_mutation();
@@ -39686,7 +39970,7 @@ var init_docker_leftovers = __esm({
39686
39970
  DockerListingBoundError = class extends Error {
39687
39971
  };
39688
39972
  defaultDockerRunner = (args) => {
39689
- const result = (0, import_node_child_process47.spawnSync)("docker", args, {
39973
+ const result = (0, import_node_child_process48.spawnSync)("docker", args, {
39690
39974
  encoding: "utf-8",
39691
39975
  timeout: 6e4,
39692
39976
  maxBuffer: 256 * 1024 * 1024
@@ -39717,13 +40001,13 @@ function measureWorktreeConsumption(worktreePath, options) {
39717
40001
  const startedAt = Date.now();
39718
40002
  let rootStat;
39719
40003
  try {
39720
- rootStat = import_node_fs62.default.lstatSync(worktreePath);
40004
+ rootStat = import_node_fs63.default.lstatSync(worktreePath);
39721
40005
  } catch (error) {
39722
40006
  return { status: "unknown", reason: describeError(error, worktreePath) };
39723
40007
  }
39724
40008
  let rootEntries;
39725
40009
  try {
39726
- rootEntries = import_node_fs62.default.readdirSync(worktreePath, { withFileTypes: true });
40010
+ rootEntries = import_node_fs63.default.readdirSync(worktreePath, { withFileTypes: true });
39727
40011
  } catch (error) {
39728
40012
  return { status: "unknown", reason: describeError(error, worktreePath) };
39729
40013
  }
@@ -39752,7 +40036,7 @@ function measureWorktreeConsumption(worktreePath, options) {
39752
40036
  const entryPath = import_node_path75.default.join(dirPath, entry.name);
39753
40037
  let entryStat;
39754
40038
  try {
39755
- entryStat = import_node_fs62.default.lstatSync(entryPath);
40039
+ entryStat = import_node_fs63.default.lstatSync(entryPath);
39756
40040
  } catch (error) {
39757
40041
  if (error?.code === "ENOENT") continue;
39758
40042
  unreadableReason = describeIncompleteWalk(error);
@@ -39762,7 +40046,7 @@ function measureWorktreeConsumption(worktreePath, options) {
39762
40046
  if (!entryStat.isDirectory()) continue;
39763
40047
  let childEntries;
39764
40048
  try {
39765
- childEntries = import_node_fs62.default.readdirSync(entryPath, { withFileTypes: true });
40049
+ childEntries = import_node_fs63.default.readdirSync(entryPath, { withFileTypes: true });
39766
40050
  } catch (error) {
39767
40051
  unreadableReason = describeIncompleteWalk(error);
39768
40052
  break;
@@ -39826,11 +40110,11 @@ function describeError(error, worktreePath) {
39826
40110
  }
39827
40111
  return `could not stat worktree path '${worktreePath}': ${error?.message ?? String(error)}`;
39828
40112
  }
39829
- var import_node_fs62, import_node_path75, DEFAULT_DEADLINE_MS, BLOCK_SIZE_BYTES;
40113
+ var import_node_fs63, import_node_path75, DEFAULT_DEADLINE_MS, BLOCK_SIZE_BYTES;
39830
40114
  var init_workspace_consumption = __esm({
39831
40115
  "src/core/workspace-consumption.ts"() {
39832
40116
  "use strict";
39833
- import_node_fs62 = __toESM(require("fs"));
40117
+ import_node_fs63 = __toESM(require("fs"));
39834
40118
  import_node_path75 = __toESM(require("path"));
39835
40119
  init_devpod_environment();
39836
40120
  DEFAULT_DEADLINE_MS = 1e4;
@@ -39917,7 +40201,7 @@ function evaluateWorkspaceActivity(evidence, cutoff) {
39917
40201
  }
39918
40202
  function readGitSnapshot(worktree, commandRunner) {
39919
40203
  const comparablePath = comparableWorkspacePath(worktree.path);
39920
- if (worktree.prunable || !import_node_fs63.default.existsSync(comparablePath)) {
40204
+ if (worktree.prunable || !import_node_fs64.default.existsSync(comparablePath)) {
39921
40205
  return { worktree, checkout: "missing", head: null, committerDate: null };
39922
40206
  }
39923
40207
  const head = gitOutput(comparablePath, ["rev-parse", "--verify", "HEAD"], commandRunner);
@@ -40549,7 +40833,7 @@ function buildWorkspaceCleanupReport(options = {}, dependencies = {}) {
40549
40833
  dependencies.listForge ?? ((identity2) => listForgeChanges(identity2))
40550
40834
  );
40551
40835
  const sources = (dependencies.readRuntimeSources ?? readRuntimeSources)(repoPath);
40552
- const disposable = import_node_fs63.default.existsSync(import_node_path76.default.join(repoPath, ".devrouter.yml")) ? loadRepoConfig(repoPath).worktrees?.disposable ?? [] : [];
40836
+ const disposable = import_node_fs64.default.existsSync(import_node_path76.default.join(repoPath, ".devrouter.yml")) ? loadRepoConfig(repoPath).worktrees?.disposable ?? [] : [];
40553
40837
  const [primary, ...linked] = worktrees;
40554
40838
  const safetyFor = (worktree, worktreePath, evidence) => toSafety(
40555
40839
  classify({
@@ -40557,7 +40841,7 @@ function buildWorkspaceCleanupReport(options = {}, dependencies = {}) {
40557
40841
  branch: worktree?.branch,
40558
40842
  primary: primary !== void 0 && sameWorkspacePath(primary.path, worktreePath),
40559
40843
  locked: worktree?.locked ?? false,
40560
- missing: !worktree || worktree.prunable || !import_node_fs63.default.existsSync(worktreePath),
40844
+ missing: !worktree || worktree.prunable || !import_node_fs64.default.existsSync(worktreePath),
40561
40845
  runtimeEvidence: evidence,
40562
40846
  forge,
40563
40847
  extraDisposablePatterns: disposable
@@ -40618,7 +40902,7 @@ function readPathEvidence(repoPath, worktreePath, inactiveFor, dependencies) {
40618
40902
  }
40619
40903
  const sources = (dependencies.readRuntimeSources ?? readRuntimeSources)(repoPath);
40620
40904
  const cutoffEpoch = Math.floor(Date.now() / 1e3) - parseInactiveFor(inactiveFor).seconds;
40621
- const gitDir = (0, import_node_child_process48.spawnSync)(
40905
+ const gitDir = (0, import_node_child_process49.spawnSync)(
40622
40906
  "git",
40623
40907
  ["-C", worktreePath, "rev-parse", "--path-format=absolute", "--git-dir"],
40624
40908
  { encoding: "utf-8", env: READ_ONLY_GIT_ENV2 }
@@ -40638,7 +40922,7 @@ function classifyExactPath(repoPath, worktreePath, evidence, forge, extraDisposa
40638
40922
  branch: worktree?.branch,
40639
40923
  primary: worktrees[0] !== void 0 && sameWorkspacePath(worktrees[0].path, worktreePath),
40640
40924
  locked: worktree?.locked ?? false,
40641
- missing: !worktree || worktree.prunable || !import_node_fs63.default.existsSync(worktreePath),
40925
+ missing: !worktree || worktree.prunable || !import_node_fs64.default.existsSync(worktreePath),
40642
40926
  runtimeEvidence: evidence.runtimeEvidence,
40643
40927
  forge,
40644
40928
  extraDisposablePatterns
@@ -40664,7 +40948,7 @@ function prepareReclaimVerdicts(repo, inactiveFor, dependencies = {}) {
40664
40948
  dependencies.listForge ?? ((identity2) => listForgeChanges(identity2))
40665
40949
  );
40666
40950
  const forge = evidence.status === "not-checked" ? { status: "unavailable", reason: "forge not checked" } : evidence;
40667
- const disposable = import_node_fs63.default.existsSync(import_node_path76.default.join(repoPath, ".devrouter.yml")) ? loadRepoConfig(repoPath).worktrees?.disposable ?? [] : [];
40951
+ const disposable = import_node_fs64.default.existsSync(import_node_path76.default.join(repoPath, ".devrouter.yml")) ? loadRepoConfig(repoPath).worktrees?.disposable ?? [] : [];
40668
40952
  const paths = evidenceCache(
40669
40953
  (worktreePath) => readPathEvidence(repoPath, worktreePath, inactiveFor, dependencies)
40670
40954
  );
@@ -40683,7 +40967,7 @@ function prepareReclaimVerdicts(repo, inactiveFor, dependencies = {}) {
40683
40967
  }
40684
40968
  function prepareTrimChecks(repo, inactiveFor, dependencies = {}) {
40685
40969
  const repoPath = resolveRepoPath(repo);
40686
- const configured = import_node_fs63.default.existsSync(import_node_path76.default.join(repoPath, ".devrouter.yml")) ? loadRepoConfig(repoPath).worktrees : void 0;
40970
+ const configured = import_node_fs64.default.existsSync(import_node_path76.default.join(repoPath, ".devrouter.yml")) ? loadRepoConfig(repoPath).worktrees : void 0;
40687
40971
  const patterns = configured?.trim ?? [];
40688
40972
  const paths = evidenceCache(
40689
40973
  (worktreePath) => readPathEvidence(repoPath, worktreePath, inactiveFor, dependencies)
@@ -40708,12 +40992,12 @@ function prepareTrimChecks(repo, inactiveFor, dependencies = {}) {
40708
40992
  isActive: (worktreePath) => paths.last(worktreePath).active
40709
40993
  };
40710
40994
  }
40711
- var import_node_child_process48, import_node_fs63, import_node_path76, READ_ONLY_GIT_ENV2, ACTIVITY_SOURCES, defaultCommandRunner;
40995
+ var import_node_child_process49, import_node_fs64, import_node_path76, READ_ONLY_GIT_ENV2, ACTIVITY_SOURCES, defaultCommandRunner;
40712
40996
  var init_workspace_cleanup = __esm({
40713
40997
  "src/core/workspace-cleanup.ts"() {
40714
40998
  "use strict";
40715
- import_node_child_process48 = require("child_process");
40716
- import_node_fs63 = __toESM(require("fs"));
40999
+ import_node_child_process49 = require("child_process");
41000
+ import_node_fs64 = __toESM(require("fs"));
40717
41001
  import_node_path76 = __toESM(require("path"));
40718
41002
  init_devpod_workspaces();
40719
41003
  init_docker_leftovers();
@@ -40737,7 +41021,7 @@ var init_workspace_cleanup = __esm({
40737
41021
  ];
40738
41022
  defaultCommandRunner = (command, args, input2) => {
40739
41023
  const environment = command === "git" ? READ_ONLY_GIT_ENV2 : { ...process.env, LC_ALL: "C" };
40740
- const result = (0, import_node_child_process48.spawnSync)(command, args, {
41024
+ const result = (0, import_node_child_process49.spawnSync)(command, args, {
40741
41025
  encoding: "utf-8",
40742
41026
  env: environment,
40743
41027
  ...input2 === void 0 ? {} : { input: input2 }
@@ -40862,7 +41146,7 @@ function journalVetoes(record6) {
40862
41146
  }
40863
41147
  function readTraefikWindow(cutoffMs) {
40864
41148
  const since = String(Math.floor(cutoffMs / 1e3));
40865
- const run = (args, maxBuffer) => (0, import_node_child_process49.spawnSync)("docker", ["logs", "--timestamps", ...args, ROUTER_CONTAINER_NAME], {
41149
+ const run = (args, maxBuffer) => (0, import_node_child_process50.spawnSync)("docker", ["logs", "--timestamps", ...args, ROUTER_CONTAINER_NAME], {
40866
41150
  encoding: "utf-8",
40867
41151
  maxBuffer,
40868
41152
  ...networkDockerOptions()
@@ -41031,12 +41315,12 @@ function setIdlePin(repoPath, pinned) {
41031
41315
  }
41032
41316
  );
41033
41317
  }
41034
- var import_node_child_process49, import_node_fs64, import_node_path77, DEFAULT_IDLE_FOR, ACTIVITY_FILES, IdleStopSkipped, defaultDependencies;
41318
+ var import_node_child_process50, import_node_fs65, import_node_path77, DEFAULT_IDLE_FOR, ACTIVITY_FILES, IdleStopSkipped, defaultDependencies;
41035
41319
  var init_workspace_idle = __esm({
41036
41320
  "src/core/workspace-idle.ts"() {
41037
41321
  "use strict";
41038
- import_node_child_process49 = require("child_process");
41039
- import_node_fs64 = __toESM(require("fs"));
41322
+ import_node_child_process50 = require("child_process");
41323
+ import_node_fs65 = __toESM(require("fs"));
41040
41324
  import_node_path77 = __toESM(require("path"));
41041
41325
  init_devpod_environment();
41042
41326
  init_devpod_workspaces();
@@ -41058,7 +41342,7 @@ var init_workspace_idle = __esm({
41058
41342
  readJournals: () => listReliabilityOperations(),
41059
41343
  journalModifiedMs: (record6) => {
41060
41344
  try {
41061
- return import_node_fs64.default.statSync(reliabilityOperationPath(record6.identity)).mtimeMs;
41345
+ return import_node_fs65.default.statSync(reliabilityOperationPath(record6.identity)).mtimeMs;
41062
41346
  } catch {
41063
41347
  return void 0;
41064
41348
  }
@@ -41066,7 +41350,7 @@ var init_workspace_idle = __esm({
41066
41350
  registryPaths: () => listMergedRuntimeWorkspaces().map((workspace) => workspace.source.localFolder),
41067
41351
  inspectContainers: () => inspectWorkspaceContainers(),
41068
41352
  statActivity: (containerId) => {
41069
- const result = (0, import_node_child_process49.spawnSync)(
41353
+ const result = (0, import_node_child_process50.spawnSync)(
41070
41354
  "docker",
41071
41355
  ["exec", containerId, "stat", "-c", "%Y", ...ACTIVITY_FILES],
41072
41356
  {
@@ -41081,7 +41365,7 @@ var init_workspace_idle = __esm({
41081
41365
  };
41082
41366
  },
41083
41367
  readTraefik: readTraefikWindow,
41084
- isConfiguredCheckout: (repoPath) => import_node_fs64.default.existsSync(import_node_path77.default.join(repoPath, ".devrouter.yml")),
41368
+ isConfiguredCheckout: (repoPath) => import_node_fs65.default.existsSync(import_node_path77.default.join(repoPath, ".devrouter.yml")),
41085
41369
  gitCommonDir: (repoPath) => {
41086
41370
  try {
41087
41371
  return resolveGitCommonDir(repoPath);
@@ -41175,7 +41459,7 @@ function defaultWorktreePath(mainRepo, ws) {
41175
41459
  return import_node_path78.default.join(mainRepo, "trees", ws);
41176
41460
  }
41177
41461
  function assertDefaultWorktreeRootIgnored(mainRepo) {
41178
- const ignored = (0, import_node_child_process50.spawnSync)("git", ["-C", mainRepo, "check-ignore", "-q", "--no-index", "trees/"], {
41462
+ const ignored = (0, import_node_child_process51.spawnSync)("git", ["-C", mainRepo, "check-ignore", "-q", "--no-index", "trees/"], {
41179
41463
  encoding: "utf-8"
41180
41464
  });
41181
41465
  if (ignored.status !== 0) {
@@ -41297,7 +41581,7 @@ function assertFullDownPreflight(mainRepo, target) {
41297
41581
  if (sameWorkspacePath(target.worktreePath, mainRepo)) {
41298
41582
  throw new Error("Refusing to remove the primary Git checkout.");
41299
41583
  }
41300
- if (!target.worktree || target.worktree.prunable || !import_node_fs65.default.existsSync(target.worktreePath)) return;
41584
+ if (!target.worktree || target.worktree.prunable || !import_node_fs66.default.existsSync(target.worktreePath)) return;
41301
41585
  if (target.worktree.locked) {
41302
41586
  throw new Error(
41303
41587
  `Worktree '${target.worktreePath}' is locked; unlock it before workspace down.`
@@ -41329,7 +41613,7 @@ async function workspaceUp(branch, opts = {}) {
41329
41613
  (candidate) => defaultWorktreePath(mainRepo, candidate)
41330
41614
  );
41331
41615
  const generatedPath = candidatePaths.find(
41332
- (candidate) => !import_node_fs65.default.existsSync(candidate) && !worktrees.some((worktree) => sameWorkspacePath(worktree.path, candidate))
41616
+ (candidate) => !import_node_fs66.default.existsSync(candidate) && !worktrees.some((worktree) => sameWorkspacePath(worktree.path, candidate))
41333
41617
  );
41334
41618
  const selectedPath = opts.path ? import_node_path78.default.resolve(opts.path) : existingBranch?.path ?? generatedPath;
41335
41619
  if (!selectedPath) {
@@ -41340,7 +41624,7 @@ async function workspaceUp(branch, opts = {}) {
41340
41624
  `Branch '${branch}' already uses worktree '${existingBranch.path}', not '${selectedPath}'.`
41341
41625
  );
41342
41626
  }
41343
- if (import_node_fs65.default.existsSync(selectedPath)) {
41627
+ if (import_node_fs66.default.existsSync(selectedPath)) {
41344
41628
  const registered = worktrees.find(
41345
41629
  (worktree) => sameWorkspacePath(worktree.path, selectedPath)
41346
41630
  );
@@ -41359,11 +41643,11 @@ async function workspaceUp(branch, opts = {}) {
41359
41643
  return selectedPath;
41360
41644
  }
41361
41645
  if (!opts.path) assertDefaultWorktreeRootIgnored(mainRepo);
41362
- const add2 = (0, import_node_child_process50.spawnSync)("git", ["-C", mainRepo, "worktree", "add", selectedPath, branch], {
41646
+ const add2 = (0, import_node_child_process51.spawnSync)("git", ["-C", mainRepo, "worktree", "add", selectedPath, branch], {
41363
41647
  encoding: "utf-8"
41364
41648
  });
41365
41649
  if (add2.status !== 0) {
41366
- const addNew = (0, import_node_child_process50.spawnSync)(
41650
+ const addNew = (0, import_node_child_process51.spawnSync)(
41367
41651
  "git",
41368
41652
  ["-C", mainRepo, "worktree", "add", "-b", branch, selectedPath],
41369
41653
  { encoding: "utf-8" }
@@ -41503,8 +41787,8 @@ async function runWorkspaceLifecycle(action2, target, opts = {}) {
41503
41787
  }
41504
41788
  const result = await mutateWorkspaceRuntime("delete", resolved2, worktrees, opts.quiet);
41505
41789
  if (removeWorktree) {
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], {
41790
+ if (resolved2.worktree && !resolved2.worktree.prunable && import_node_fs66.default.existsSync(resolved2.worktreePath)) {
41791
+ const rm = (0, import_node_child_process51.spawnSync)("git", ["-C", mainRepo, "worktree", "remove", resolved2.worktreePath], {
41508
41792
  encoding: "utf-8"
41509
41793
  });
41510
41794
  if (rm.status !== 0) {
@@ -41522,7 +41806,7 @@ async function runWorkspaceLifecycle(action2, target, opts = {}) {
41522
41806
  }
41523
41807
  return result;
41524
41808
  };
41525
- return resolved2.worktree && !resolved2.worktree.prunable && import_node_fs65.default.existsSync(resolved2.worktreePath) ? withLifecycleOperationLock(resolved2.worktreePath, operation) : operation();
41809
+ return resolved2.worktree && !resolved2.worktree.prunable && import_node_fs66.default.existsSync(resolved2.worktreePath) ? withLifecycleOperationLock(resolved2.worktreePath, operation) : operation();
41526
41810
  }
41527
41811
  async function reclaimWorktreeExactPath(mainRepo, worktreePath, verify, afterRemove, steps) {
41528
41812
  await withLifecycleOperationLock(worktreePath, async () => {
@@ -41548,7 +41832,7 @@ async function reclaimWorktreeExactPath(mainRepo, worktreePath, verify, afterRem
41548
41832
  );
41549
41833
  steps.push("runtime-deleted");
41550
41834
  }
41551
- const rm = (0, import_node_child_process50.spawnSync)("git", ["-C", mainRepo, "worktree", "remove", worktreePath], {
41835
+ const rm = (0, import_node_child_process51.spawnSync)("git", ["-C", mainRepo, "worktree", "remove", worktreePath], {
41552
41836
  encoding: "utf-8"
41553
41837
  });
41554
41838
  if (rm.status !== 0) {
@@ -41569,12 +41853,12 @@ async function workspaceStop(target, opts = {}) {
41569
41853
  async function workspaceDown(target, opts = {}) {
41570
41854
  return runWorkspaceLifecycle("down", target, opts);
41571
41855
  }
41572
- var import_node_child_process50, import_node_fs65, import_node_path78, WORKSPACE_ALLOCATION_LOCK_WAIT_MS;
41856
+ var import_node_child_process51, import_node_fs66, import_node_path78, WORKSPACE_ALLOCATION_LOCK_WAIT_MS;
41573
41857
  var init_workspace_lifecycle = __esm({
41574
41858
  "src/core/workspace-lifecycle.ts"() {
41575
41859
  "use strict";
41576
- import_node_child_process50 = require("child_process");
41577
- import_node_fs65 = __toESM(require("fs"));
41860
+ import_node_child_process51 = require("child_process");
41861
+ import_node_fs66 = __toESM(require("fs"));
41578
41862
  import_node_path78 = __toESM(require("path"));
41579
41863
  init_devpod_mutation();
41580
41864
  init_devpod_workspaces();
@@ -41594,7 +41878,7 @@ var init_workspace_lifecycle = __esm({
41594
41878
 
41595
41879
  // src/core/worktree-trim.ts
41596
41880
  function listIgnoredPaths(worktreePath) {
41597
- const result = (0, import_node_child_process51.spawnSync)(
41881
+ const result = (0, import_node_child_process52.spawnSync)(
41598
41882
  "git",
41599
41883
  [
41600
41884
  "-C",
@@ -41683,14 +41967,14 @@ function isInside(parent, child) {
41683
41967
  }
41684
41968
  function exists(file) {
41685
41969
  try {
41686
- import_node_fs66.default.lstatSync(file);
41970
+ import_node_fs67.default.lstatSync(file);
41687
41971
  return true;
41688
41972
  } catch {
41689
41973
  return false;
41690
41974
  }
41691
41975
  }
41692
41976
  function hasTrackedFiles(worktreePath, relative2) {
41693
- const result = (0, import_node_child_process51.spawnSync)(
41977
+ const result = (0, import_node_child_process52.spawnSync)(
41694
41978
  "git",
41695
41979
  ["-C", worktreePath, "--literal-pathspecs", "ls-files", "-z", "--", relative2],
41696
41980
  { encoding: "utf-8", env: { ...process.env, GIT_OPTIONAL_LOCKS: "0" } }
@@ -41701,7 +41985,7 @@ function trimOnePath(candidate, relative2, patterns, steps) {
41701
41985
  const target = import_node_path79.default.join(candidate.path, relative2);
41702
41986
  let stat;
41703
41987
  try {
41704
- stat = import_node_fs66.default.lstatSync(target);
41988
+ stat = import_node_fs67.default.lstatSync(target);
41705
41989
  } catch (error) {
41706
41990
  if (error.code === "ENOENT") {
41707
41991
  steps.push(`gone:${relative2}`);
@@ -41713,7 +41997,7 @@ function trimOnePath(candidate, relative2, patterns, steps) {
41713
41997
  steps.push(`skipped-symlink:${relative2}`);
41714
41998
  return;
41715
41999
  }
41716
- if (!isInside(import_node_fs66.default.realpathSync.native(candidate.path), import_node_fs66.default.realpathSync.native(target))) {
42000
+ if (!isInside(import_node_fs67.default.realpathSync.native(candidate.path), import_node_fs67.default.realpathSync.native(target))) {
41717
42001
  steps.push(`skipped-outside:${relative2}`);
41718
42002
  return;
41719
42003
  }
@@ -41726,7 +42010,7 @@ function trimOnePath(candidate, relative2, patterns, steps) {
41726
42010
  return;
41727
42011
  }
41728
42012
  try {
41729
- import_node_fs66.default.rmSync(target, { recursive: true, force: true });
42013
+ import_node_fs67.default.rmSync(target, { recursive: true, force: true });
41730
42014
  } catch (error) {
41731
42015
  steps.push(`partial:${relative2}`);
41732
42016
  throw error;
@@ -41827,12 +42111,12 @@ async function applyTrimManifest(options, dependencies) {
41827
42111
  }
41828
42112
  return { manifestSha256, dryRun: !options.yes, targets, stoppedEarly };
41829
42113
  }
41830
- var import_node_child_process51, import_node_fs66, import_node_path79;
42114
+ var import_node_child_process52, import_node_fs67, import_node_path79;
41831
42115
  var init_worktree_trim = __esm({
41832
42116
  "src/core/worktree-trim.ts"() {
41833
42117
  "use strict";
41834
- import_node_child_process51 = require("child_process");
41835
- import_node_fs66 = __toESM(require("fs"));
42118
+ import_node_child_process52 = require("child_process");
42119
+ import_node_fs67 = __toESM(require("fs"));
41836
42120
  import_node_path79 = __toESM(require("path"));
41837
42121
  init_reliability_lifecycle();
41838
42122
  init_worktree_reclaim();
@@ -41901,7 +42185,7 @@ function runWorkspaceLsCommand(options) {
41901
42185
  }
41902
42186
  function writeManifestOutput(manifest, output2) {
41903
42187
  if (output2) {
41904
- import_node_fs67.default.writeFileSync(output2, manifest, { flag: "wx" });
42188
+ import_node_fs68.default.writeFileSync(output2, manifest, { flag: "wx" });
41905
42189
  process.stdout.write(`${sha256Hex(manifest)} ${output2}
41906
42190
  `);
41907
42191
  } else {
@@ -41931,7 +42215,7 @@ function runWorkspaceCleanupCommand(options) {
41931
42215
  throw new Error("--manifest requires --all-worktrees and --check-merged.");
41932
42216
  }
41933
42217
  const createdAtEpoch = Math.floor(Date.now() / 1e3);
41934
- const worktrees = import_node_fs67.default.existsSync(import_node_path80.default.join(repoPath, ".devrouter.yml")) ? loadRepoConfig(repoPath).worktrees : void 0;
42218
+ const worktrees = import_node_fs68.default.existsSync(import_node_path80.default.join(repoPath, ".devrouter.yml")) ? loadRepoConfig(repoPath).worktrees : void 0;
41935
42219
  const manifest = action2 === "trim" ? buildTrimManifest(
41936
42220
  report,
41937
42221
  { trim: worktrees?.trim ?? [], disposable: worktrees?.disposable },
@@ -41969,7 +42253,7 @@ async function runWorkspaceIdleCommand(options) {
41969
42253
  if (options.stop) {
41970
42254
  const paths = idleSweepPaths();
41971
42255
  if (!process.stderr.isTTY) muteLifecycleProgressHeartbeat();
41972
- import_node_fs67.default.mkdirSync(paths.directory, { recursive: true, mode: 448 });
42256
+ import_node_fs68.default.mkdirSync(paths.directory, { recursive: true, mode: 448 });
41973
42257
  const result = await withFileLock(paths.lock, { activity: "idle sweep" }, async () => {
41974
42258
  try {
41975
42259
  const swept = await stopIdleEnvironments(
@@ -42071,7 +42355,7 @@ async function runWorkspaceJournalSettleCommand(options) {
42071
42355
  function runWorkspaceManifestHashCommand(file) {
42072
42356
  let leftover = false;
42073
42357
  try {
42074
- leftover = isLeftoverManifest(JSON.parse(import_node_fs67.default.readFileSync(file, "utf-8")));
42358
+ leftover = isLeftoverManifest(JSON.parse(import_node_fs68.default.readFileSync(file, "utf-8")));
42075
42359
  } catch {
42076
42360
  }
42077
42361
  const { raw } = leftover ? readLeftoverManifest(file) : readManifest(file);
@@ -42168,11 +42452,11 @@ async function runWorkspaceTrimCommand(options) {
42168
42452
  process.exitCode = 1;
42169
42453
  }
42170
42454
  }
42171
- var import_node_fs67, import_node_path80;
42455
+ var import_node_fs68, import_node_path80;
42172
42456
  var init_workspace2 = __esm({
42173
42457
  "src/commands/workspace.ts"() {
42174
42458
  "use strict";
42175
- import_node_fs67 = __toESM(require("fs"));
42459
+ import_node_fs68 = __toESM(require("fs"));
42176
42460
  import_node_path80 = __toESM(require("path"));
42177
42461
  init_docker_leftovers();
42178
42462
  init_file_lock();
@@ -42231,7 +42515,7 @@ var init_version = __esm({
42231
42515
 
42232
42516
  // src/cli.ts
42233
42517
  var import_commander = require("commander");
42234
- var CLI_VERSION = true ? "0.6.3" : "0.0.0-dev";
42518
+ var CLI_VERSION = true ? "0.6.4" : "0.0.0-dev";
42235
42519
  var VERSION_FLAGS = /* @__PURE__ */ new Set(["-V", "--version"]);
42236
42520
  function withErrorHandling(action2) {
42237
42521
  return async (...args) => {
@@ -42325,7 +42609,7 @@ program.command("up").description("Ensure devnet and start shared Traefik (reser
42325
42609
  await runUpCommand2();
42326
42610
  })
42327
42611
  );
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(
42612
+ 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("--replace-app", "Replace one proved stopped managed app; discard its writable layer").option("--open", "Open HTTP routes after readiness succeeds").option("--json", "Output JSON").action(
42329
42613
  withErrorHandling(async (repoPath, _options, command) => {
42330
42614
  const options = command.opts();
42331
42615
  const { runEnsureCommand: runEnsureCommand2 } = await Promise.resolve().then(() => (init_ensure(), ensure_exports));
@@ -42335,6 +42619,7 @@ program.command("ensure").description("Start and prove a primary or linked check
42335
42619
  variant: options.variant,
42336
42620
  repair: options.repair,
42337
42621
  initializeNew: options.initializeNew,
42622
+ replaceApp: options.replaceApp,
42338
42623
  open: Boolean(options.open),
42339
42624
  json: Boolean(options.json)
42340
42625
  });
@@ -42599,7 +42884,7 @@ workspaceCommand2.command("up").description(
42599
42884
  );
42600
42885
  workspaceCommand2.command("ensure").description(
42601
42886
  "Start and prove a primary or linked checkout's workspace runtime, upstreams, and routes"
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(
42887
+ ).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("--replace-app", "Replace one proved stopped managed app; discard its writable layer").option("--open", "Open HTTP routes after readiness succeeds").option("--json", "Output JSON").action(
42603
42888
  withErrorHandling(
42604
42889
  async (worktreePath, _options, command) => {
42605
42890
  const options = command.opts();
@@ -42610,6 +42895,7 @@ workspaceCommand2.command("ensure").description(
42610
42895
  variant: options.variant,
42611
42896
  repair: options.repair,
42612
42897
  initializeNew: options.initializeNew,
42898
+ replaceApp: options.replaceApp,
42613
42899
  open: Boolean(options.open),
42614
42900
  json: Boolean(options.json)
42615
42901
  });