@devrouter/cli 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/devrouter.js CHANGED
@@ -4275,7 +4275,7 @@ function loadRepoConfig(repoPath, read2 = (file) => import_node_fs7.default.read
4275
4275
  const config = parseConfig(parsed ?? {}, configPath);
4276
4276
  const requiredVersion = config.devrouter?.version;
4277
4277
  if (requiredVersion && !hasWarnedVersionMismatch) {
4278
- const cliVersion = true ? "0.1.2" : "0.0.0-dev";
4278
+ const cliVersion = true ? "0.1.3" : "0.0.0-dev";
4279
4279
  if (cliVersion !== "0.0.0-dev" && compareSemver(requiredVersion, cliVersion) > 0) {
4280
4280
  hasWarnedVersionMismatch = true;
4281
4281
  process.stderr.write(
@@ -6273,7 +6273,7 @@ var init_reliability_contract = __esm({
6273
6273
 
6274
6274
  // src/core/reliability-operation-store.ts
6275
6275
  function installedCliVersion() {
6276
- return true ? "0.1.2" : "0.0.0-dev";
6276
+ return true ? "0.1.3" : "0.0.0-dev";
6277
6277
  }
6278
6278
  function compareCliVersions(left, right) {
6279
6279
  const parts = (value) => value.split(".").map((part) => Number.parseInt(part, 10) || 0);
@@ -6406,6 +6406,7 @@ function validate2(record5, identity2) {
6406
6406
  "revision",
6407
6407
  "state",
6408
6408
  "worker",
6409
+ "stopWorkStarted",
6409
6410
  "effectSequence",
6410
6411
  "outcome",
6411
6412
  "result",
@@ -6587,6 +6588,9 @@ function validate2(record5, identity2) {
6587
6588
  throw new Error("Reliability worker identity is invalid.");
6588
6589
  }
6589
6590
  }
6591
+ if (record5.stopWorkStarted !== void 0 && record5.stopWorkStarted !== null) {
6592
+ if (record5.stopWorkStarted !== true) throw new Error("Invalid stop work marker.");
6593
+ }
6590
6594
  }
6591
6595
  function journalEntryLocation(name) {
6592
6596
  const printable = name.replace(/[^A-Za-z0-9._-]/g, "?");
@@ -9186,9 +9190,11 @@ function listDevsyWorkspaces() {
9186
9190
  );
9187
9191
  if (result.status !== 0) {
9188
9192
  const details = [result.error?.message, result.stdout, result.stderr].filter(Boolean).join("\n").trim();
9189
- throw new Error(
9193
+ const failure2 = new Error(
9190
9194
  `devsy workspace list failed: ${details || "devsy is not installed or unavailable"}`
9191
9195
  );
9196
+ if (result.error) failure2.code = result.error.code;
9197
+ throw failure2;
9192
9198
  }
9193
9199
  let parsed;
9194
9200
  try {
@@ -10254,6 +10260,9 @@ ${JSON.stringify(effective, null, 2)}
10254
10260
  contents
10255
10261
  };
10256
10262
  }
10263
+ function writeManagedDevcontainerConfig(plan) {
10264
+ writeFileAtomically(plan.generatedPath, plan.contents);
10265
+ }
10257
10266
  function inspectManagedDevcontainerGeneratedConfig(plan) {
10258
10267
  let stat;
10259
10268
  try {
@@ -10422,7 +10431,11 @@ function listDevpodWorkspacesRaw(options = {}) {
10422
10431
  }
10423
10432
  if (result.error || result.status !== 0) {
10424
10433
  const details = [result.error?.message, result.stdout, result.stderr].filter(Boolean).join("\n").trim();
10425
- throw new Error(`devpod list failed: ${details || "devpod is not installed or unavailable"}`);
10434
+ const failure2 = new Error(
10435
+ `devpod list failed: ${details || "devpod is not installed or unavailable"}`
10436
+ );
10437
+ if (result.error) failure2.code = result.error.code;
10438
+ throw failure2;
10426
10439
  }
10427
10440
  let parsed;
10428
10441
  try {
@@ -10714,25 +10727,23 @@ function writeWorkspaceRuntimeConfig(config) {
10714
10727
  writeFileAtomically(RUNTIME_CONFIG_FILE, `${JSON.stringify(next, null, 2)}
10715
10728
  `);
10716
10729
  }
10730
+ function readRegistrySnapshot(runtime, read2, snapshots) {
10731
+ try {
10732
+ return read2();
10733
+ } catch (error) {
10734
+ if (error?.code === "ENOENT") return void 0;
10735
+ snapshots.unavailable.push(runtime);
10736
+ snapshots.errors[runtime] = error instanceof Error ? error.message : String(error);
10737
+ return void 0;
10738
+ }
10739
+ }
10717
10740
  function getWorkspaceRegistrySnapshots() {
10718
10741
  if (cachedSnapshots) return cachedSnapshots;
10719
10742
  const snapshots = { unavailable: [], errors: {} };
10720
- if (isRuntimeInstalled("devsy")) {
10721
- try {
10722
- snapshots.devsy = listDevsyWorkspaces();
10723
- } catch (error) {
10724
- snapshots.unavailable.push("devsy");
10725
- snapshots.errors.devsy = error instanceof Error ? error.message : String(error);
10726
- }
10727
- }
10728
- if (isRuntimeInstalled("devpod")) {
10729
- try {
10730
- snapshots.devpod = listDevpodWorkspacesRaw();
10731
- } catch (error) {
10732
- snapshots.unavailable.push("devpod");
10733
- snapshots.errors.devpod = error instanceof Error ? error.message : String(error);
10734
- }
10735
- }
10743
+ const devsy = readRegistrySnapshot("devsy", listDevsyWorkspaces, snapshots);
10744
+ if (devsy !== void 0) snapshots.devsy = devsy;
10745
+ const devpod = readRegistrySnapshot("devpod", listDevpodWorkspacesRaw, snapshots);
10746
+ if (devpod !== void 0) snapshots.devpod = devpod;
10736
10747
  cachedSnapshots = snapshots;
10737
10748
  return snapshots;
10738
10749
  }
@@ -11511,11 +11522,19 @@ function absentRegistrationIdentity(state) {
11511
11522
  resetWorkspaceRuntimeCaches();
11512
11523
  if (resolveWorkspaceRuntimeOrDefault(state.repoPath) !== "devsy")
11513
11524
  throw new Error("Stop provider changed.");
11514
- if (!state.workspace || !isLinkedWorktree(state.repoPath))
11515
- throw new Error("Absent stop requires a linked workspace.");
11516
- const record5 = readWorkspaceOwnership(state.repoPath, state.workspace);
11517
- if (!record5 || record5.workspace !== state.workspace || record5.devpodId !== state.devpodId || !sameWorkspacePath(record5.worktreePath, state.repoPath) || resolveWorktreeWorkspace(state.repoPath) !== state.workspace || inspectWorkspaceOwnership(record5, listGitWorktrees(state.repoPath), void 0).ownerStatus !== "present")
11518
- throw new Error("Absent stop workspace ownership changed.");
11525
+ const linked = isLinkedWorktree(state.repoPath);
11526
+ if (linked !== (state.workspace !== void 0))
11527
+ throw new Error("Absent stop requires the exact workspace identity.");
11528
+ let identity2;
11529
+ if (linked) {
11530
+ const workspace = state.workspace;
11531
+ const record5 = readWorkspaceOwnership(state.repoPath, workspace);
11532
+ if (!record5 || record5.workspace !== workspace || record5.devpodId !== state.devpodId || !sameWorkspacePath(record5.worktreePath, state.repoPath) || resolveWorktreeWorkspace(state.repoPath) !== workspace || inspectWorkspaceOwnership(record5, listGitWorktrees(state.repoPath), void 0).ownerStatus !== "present")
11533
+ throw new Error("Absent stop workspace ownership changed.");
11534
+ identity2 = { kind: "linked", record: record5, gitCommonDir: resolveGitCommonDir(state.repoPath) };
11535
+ } else {
11536
+ identity2 = { kind: "primary", repoPath: state.repoPath, devpodId: state.devpodId };
11537
+ }
11519
11538
  for (const entries of [
11520
11539
  listDevsyWorkspaces(),
11521
11540
  listDevpodWorkspacesRaw({ allowMissingExecutable: true })
@@ -11525,7 +11544,7 @@ function absentRegistrationIdentity(state) {
11525
11544
  ))
11526
11545
  throw new Error("Absent stop requires both provider registrations to remain absent.");
11527
11546
  }
11528
- return { record: record5, gitCommonDir: resolveGitCommonDir(state.repoPath) };
11547
+ return identity2;
11529
11548
  }
11530
11549
  function replacementObservation(state) {
11531
11550
  resetWorkspaceRuntimeCaches();
@@ -11635,7 +11654,7 @@ function proveManagedStop(state) {
11635
11654
  const identityKeys = ["context", "providerId", "uid", "sourcePath", "sourceContainer"];
11636
11655
  const current = identityOf(owner.workspace);
11637
11656
  const changed = identityKeys.filter((key) => baseline[key] !== current[key]);
11638
- if (changed.length === 0 && state.workspace !== void 0 && isLinkedWorktree(state.repoPath) && inspectManagedStopContainers(baseline.project, baseline.endpoint).length === 0) {
11657
+ if (changed.length === 0 && inspectManagedStopContainers(baseline.project, baseline.endpoint).length === 0) {
11639
11658
  proveAbsentPopulationStop(state, baseline, current);
11640
11659
  return { status: "proven-absent", containers: [] };
11641
11660
  }
@@ -12586,6 +12605,41 @@ function stopInitialManagedDevsyWorkspace(repoPath, devsyId) {
12586
12605
  throw new Error("Initial managed stop left a workload running.");
12587
12606
  return true;
12588
12607
  }
12608
+ function restoreRecordedManagedDevcontainerConfig(repoPath) {
12609
+ const plan = recordedManagedDevcontainerRestorePlan(repoPath);
12610
+ if (!plan) return false;
12611
+ writeManagedDevcontainerConfig(plan);
12612
+ if (inspectManagedDevcontainerGeneratedConfig(plan).status !== "valid") {
12613
+ throw new Error(
12614
+ `Managed Dev Container path '${plan.generatedPath}' could not be restored from the recorded state.`
12615
+ );
12616
+ }
12617
+ return true;
12618
+ }
12619
+ function recordedManagedDevcontainerRestorePlan(repoPath) {
12620
+ try {
12621
+ if (!import_node_fs25.default.existsSync(import_node_path33.default.join(repoPath, ".devrouter.yml"))) return void 0;
12622
+ const linked = isLinkedWorktree(repoPath);
12623
+ const workspace = linked ? resolveWorktreeWorkspace(repoPath) : void 0;
12624
+ const state = readManagedRuntimeState(repoPath, workspace);
12625
+ if (!state) return void 0;
12626
+ const runtime = loadRuntimeConfig(repoPath, workspace ?? "", state.profile);
12627
+ if (!runtime.config.managedRuntime || runtime.profile !== state.profile || runtime.workspace !== workspace)
12628
+ return void 0;
12629
+ const plan = inspectManagedDevcontainerConfig({
12630
+ repoPath,
12631
+ config: runtime.config,
12632
+ profile: runtime.resolvedProfile,
12633
+ linked
12634
+ });
12635
+ if (inspectManagedDevcontainerGeneratedConfig(plan).status !== "missing") return void 0;
12636
+ if (plan.sourceConfigSha256 !== state.sourceConfigSha256 || plan.effectiveConfigSha256 !== state.effectiveConfigSha256)
12637
+ return void 0;
12638
+ return plan;
12639
+ } catch {
12640
+ return void 0;
12641
+ }
12642
+ }
12589
12643
  function stopRetainedManagedDevsyWorkspace(options) {
12590
12644
  const { repoPath, devsyId } = options;
12591
12645
  const linked = isLinkedWorktree(repoPath);
@@ -13984,6 +14038,7 @@ function inspectExactOwnership(devsyId, worktreePath) {
13984
14038
  }
13985
14039
  function mutateOwnedDevsyWorkspace(action2, devsyId, worktreePath) {
13986
14040
  return withMutationLock(`Devsy ${action2}`, worktreePath, () => {
14041
+ restoreRecordedManagedDevcontainerConfig(worktreePath);
13987
14042
  if (action2 === "stop") {
13988
14043
  const stopped = stopRetainedManagedDevsyWorkspace({
13989
14044
  repoPath: worktreePath,
@@ -14392,7 +14447,7 @@ function handleOperationRequest(state, event) {
14392
14447
  if (state.phase === "stopping" || state.desired === "parked-for-capacity")
14393
14448
  return blocked(
14394
14449
  state,
14395
- `phase is '${state.phase}' with desired '${state.desired}'; wait for the running stop to finish.`
14450
+ `phase is '${state.phase}' with desired '${state.desired}'; wait for the running stop to finish, or when no stop worker is running rerun 'devrouter stop <path> --delete', removing the exact provider registration first when the stop cannot prove the recorded population.`
14396
14451
  );
14397
14452
  if (event.kind === "exec" && (!event.runtimeRunning || state.desired !== "running" && state.intentRevision !== 0))
14398
14453
  return blocked(
@@ -16027,6 +16082,103 @@ var init_managed_post_start = __esm({
16027
16082
  }
16028
16083
  });
16029
16084
 
16085
+ // src/core/managed-mount-nesting.ts
16086
+ function normalizeDestination(value) {
16087
+ const trimmed = value.trim().replace(/\/+$/, "");
16088
+ return trimmed === "" ? "/" : trimmed;
16089
+ }
16090
+ function isNestedWithin(destination, candidate) {
16091
+ return candidate === "/" ? destination !== "/" : destination.startsWith(`${candidate}/`);
16092
+ }
16093
+ function unescapeMountField(value) {
16094
+ return value.replace(
16095
+ /\\([0-7]{3})/g,
16096
+ (_match, octal) => String.fromCharCode(Number.parseInt(octal, 8))
16097
+ );
16098
+ }
16099
+ function parseMountinfoDestinations(text4) {
16100
+ const destinations = /* @__PURE__ */ new Set();
16101
+ for (const line of text4.split("\n")) {
16102
+ const fields2 = line.split(" ");
16103
+ if (fields2.length < 5 || !/^\d+$/.test(fields2[0] ?? "")) continue;
16104
+ destinations.add(normalizeDestination(unescapeMountField(fields2[4] ?? "")));
16105
+ }
16106
+ return [...destinations];
16107
+ }
16108
+ function isNestedIndex(destinations, index) {
16109
+ return destinations.some(
16110
+ (candidate, other) => other !== index && isNestedWithin(destinations[index] ?? "", candidate)
16111
+ );
16112
+ }
16113
+ function configuredDestinations(configured) {
16114
+ return configured.map((mount) => normalizeDestination(mount.Destination));
16115
+ }
16116
+ function classifyManagedMountNesting(configured, effectiveDestinations, containerId) {
16117
+ const destinations = configuredDestinations(configured);
16118
+ const nested = configured.filter((_mount, index) => isNestedIndex(destinations, index));
16119
+ const identity2 = containerId ? { container: containerId } : {};
16120
+ if (nested.length === 0) {
16121
+ return { status: "not-applicable", checked: [], unwound: [], reason: NESTED_MOUNT_REASON };
16122
+ }
16123
+ if (effectiveDestinations === null) {
16124
+ return {
16125
+ status: "unverified",
16126
+ checked: [],
16127
+ unwound: [],
16128
+ ...identity2,
16129
+ reason: UNREADABLE_MOUNT_REASON
16130
+ };
16131
+ }
16132
+ const effective = new Set(effectiveDestinations.map(normalizeDestination));
16133
+ const checked = [];
16134
+ const unwound = [];
16135
+ for (const mount of nested) {
16136
+ const destination = normalizeDestination(mount.Destination);
16137
+ if (effective.has(destination)) {
16138
+ checked.push(mount.Destination);
16139
+ continue;
16140
+ }
16141
+ const nestedWithin = destinations.filter((candidate) => isNestedWithin(destination, candidate)).sort((a, b) => a.length - b.length)[0];
16142
+ unwound.push({
16143
+ destination: mount.Destination,
16144
+ source: mount.Source,
16145
+ nestedWithin: nestedWithin ?? "/"
16146
+ });
16147
+ }
16148
+ return {
16149
+ status: unwound.length > 0 ? "unwound" : "effective",
16150
+ checked,
16151
+ unwound,
16152
+ ...identity2
16153
+ };
16154
+ }
16155
+ function readContainerMountinfo(containerId, options) {
16156
+ const result = (0, import_node_child_process24.spawnSync)("docker", ["exec", containerId, "cat", "/proc/self/mountinfo"], {
16157
+ encoding: "utf-8",
16158
+ timeout: options.timeoutMs ?? 5e3,
16159
+ stdio: ["ignore", "pipe", "pipe"],
16160
+ ...networkDockerOptions()
16161
+ });
16162
+ if (result.status !== 0 || typeof result.stdout !== "string") return null;
16163
+ const destinations = parseMountinfoDestinations(result.stdout);
16164
+ return destinations.length > 0 ? destinations : null;
16165
+ }
16166
+ function observeManagedMountNesting(container, options = {}) {
16167
+ const destinations = configuredDestinations(container.mounts);
16168
+ const effective = destinations.some((_destination, index) => isNestedIndex(destinations, index)) ? readContainerMountinfo(container.id, options) : null;
16169
+ return classifyManagedMountNesting(container.mounts, effective, container.id);
16170
+ }
16171
+ var import_node_child_process24, NESTED_MOUNT_REASON, UNREADABLE_MOUNT_REASON;
16172
+ var init_managed_mount_nesting = __esm({
16173
+ "src/core/managed-mount-nesting.ts"() {
16174
+ "use strict";
16175
+ import_node_child_process24 = require("child_process");
16176
+ init_network_effect_scope();
16177
+ NESTED_MOUNT_REASON = "no configured mount is nested inside another configured mount";
16178
+ UNREADABLE_MOUNT_REASON = "the container's own mount table could not be read";
16179
+ }
16180
+ });
16181
+
16030
16182
  // src/core/managed-runtime-status.ts
16031
16183
  function recoveryPath(repoPath) {
16032
16184
  if (/^[A-Za-z0-9_@%+=:,./-]+$/.test(repoPath)) return repoPath;
@@ -16302,6 +16454,7 @@ function inspectManagedRuntime(options) {
16302
16454
  (mount) => mount.Type === "bind" && sameWorkspacePath(mount.Source, repoPath)
16303
16455
  )?.Destination;
16304
16456
  const primaryActive = Boolean(primary?.state.Running);
16457
+ const mountNesting = primary && primaryActive ? observeManagedMountNesting(primary) : void 0;
16305
16458
  if (generatedConfigMissing && (state || primaryActive || managedServicesActive)) {
16306
16459
  drift.push("managed generated Dev Container configuration is missing");
16307
16460
  }
@@ -16368,7 +16521,8 @@ function inspectManagedRuntime(options) {
16368
16521
  processStatuses,
16369
16522
  activeServices: sortedUnique2(activeServices),
16370
16523
  activeProcesses: sortedUnique2(activeProcesses),
16371
- drift
16524
+ drift,
16525
+ ...mountNesting ? { mountNesting } : {}
16372
16526
  };
16373
16527
  }
16374
16528
  function collectManagedRuntimeStatus(options) {
@@ -16516,6 +16670,7 @@ function collectManagedRuntimeStatus(options) {
16516
16670
  baseServiceStatuses: inspection.baseStatuses,
16517
16671
  processStatuses: inspection.processStatuses,
16518
16672
  drift: sortedUnique2(drift),
16673
+ ...inspection.mountNesting ? { mountNesting: inspection.mountNesting } : {},
16519
16674
  ...inspection.plan?.sourceConfigSha256 || state?.sourceConfigSha256 ? { sourceConfigSha256: inspection.plan?.sourceConfigSha256 ?? state?.sourceConfigSha256 } : {},
16520
16675
  ...inspection.plan?.effectiveConfigSha256 || state?.effectiveConfigSha256 ? {
16521
16676
  effectiveConfigSha256: inspection.plan?.effectiveConfigSha256 ?? state?.effectiveConfigSha256
@@ -16530,6 +16685,7 @@ var init_managed_runtime_status = __esm({
16530
16685
  init_devcontainer_profile();
16531
16686
  init_devpod_environment();
16532
16687
  init_host_routes();
16688
+ init_managed_mount_nesting();
16533
16689
  init_managed_post_start();
16534
16690
  init_managed_runtime_state();
16535
16691
  init_reliability_operation_store();
@@ -16549,7 +16705,7 @@ function readDocker(endpoint, args, timeoutMs) {
16549
16705
  const env = { ...process.env };
16550
16706
  delete env.DOCKER_CONTEXT;
16551
16707
  delete env.DOCKER_HOST;
16552
- const result = (0, import_node_child_process24.spawnSync)("docker", ["--host", endpoint, ...args], {
16708
+ const result = (0, import_node_child_process25.spawnSync)("docker", ["--host", endpoint, ...args], {
16553
16709
  env,
16554
16710
  encoding: "utf-8",
16555
16711
  timeout: timeoutMs,
@@ -16580,7 +16736,14 @@ function id4(value) {
16580
16736
  }
16581
16737
  function rows(output2) {
16582
16738
  const trimmed = output2.trim();
16583
- return trimmed ? trimmed.split(/\r?\n/).map((line) => JSON.parse(line)) : [];
16739
+ return trimmed ? trimmed.split(/\r?\n/).map((line) => parseRecord(line)) : [];
16740
+ }
16741
+ function parseRecord(line) {
16742
+ try {
16743
+ return JSON.parse(line);
16744
+ } catch {
16745
+ throw new NetworkInventoryError("Docker network inventory contains a malformed record.");
16746
+ }
16584
16747
  }
16585
16748
  function ids(output2) {
16586
16749
  const values = output2.trim() ? output2.trim().split(/\r?\n/).map(id4) : [];
@@ -16625,7 +16788,7 @@ function collectDockerNetworkInventory(options = {}, dependencies = {}) {
16625
16788
  return output2;
16626
16789
  };
16627
16790
  const info = record4(
16628
- JSON.parse(
16791
+ parseRecord(
16629
16792
  read2(["info", "--format", '{"id":{{json .ID}},"pools":{{json .DefaultAddressPools}}}'])
16630
16793
  )
16631
16794
  );
@@ -16704,18 +16867,18 @@ function collectDockerNetworkInventory(options = {}, dependencies = {}) {
16704
16867
  }
16705
16868
  return result;
16706
16869
  }
16707
- var import_node_child_process24, NetworkInventoryError, MAX_IDS, MAX_OUTPUT_BYTES, FULL_ID, NETWORK_TEMPLATE, CONTAINER_TEMPLATE;
16870
+ var import_node_child_process25, NetworkInventoryError, MAX_IDS, MAX_OUTPUT_BYTES, FULL_ID, NETWORK_TEMPLATE, CONTAINER_TEMPLATE;
16708
16871
  var init_network_inventory = __esm({
16709
16872
  "src/core/network-inventory.ts"() {
16710
16873
  "use strict";
16711
- import_node_child_process24 = require("child_process");
16874
+ import_node_child_process25 = require("child_process");
16712
16875
  init_devpod_environment();
16713
16876
  NetworkInventoryError = class extends Error {
16714
16877
  };
16715
16878
  MAX_IDS = 4096;
16716
16879
  MAX_OUTPUT_BYTES = 2 * 1024 * 1024;
16717
16880
  FULL_ID = /^[a-f0-9]{64}$/;
16718
- NETWORK_TEMPLATE = '{"id":{{json .Id}},"name":{{json .Name}},"driver":{{json .Driver}},"ipam":{{json .IPAM.Config}},"activeEndpoints":{{len .Containers}},"project":{{json (index .Labels "com.docker.compose.project")}},"network":{{json (index .Labels "com.docker.compose.network")}}';
16881
+ NETWORK_TEMPLATE = '{"id":{{json .Id}},"name":{{json .Name}},"driver":{{json .Driver}},"ipam":{{json .IPAM.Config}},"activeEndpoints":{{len .Containers}},"project":{{json (index .Labels "com.docker.compose.project")}},"network":{{json (index .Labels "com.docker.compose.network")}}}';
16719
16882
  CONTAINER_TEMPLATE = '{"id":{{json .Id}},"networks":{{json .NetworkSettings.Networks}}}';
16720
16883
  }
16721
16884
  });
@@ -17102,7 +17265,7 @@ function parseLinuxNetworkRoutes(output2) {
17102
17265
  function collectNetworkRoutes(options) {
17103
17266
  if ((options.platform ?? process.platform) !== "linux" || !hasLocalDockerNetworkNamespace(options.endpoint))
17104
17267
  return { status: "unknown", routes: [] };
17105
- const result = (0, import_node_child_process25.spawnSync)("ip", ["-j", "-4", "route", "show", "table", "all"], {
17268
+ const result = (0, import_node_child_process26.spawnSync)("ip", ["-j", "-4", "route", "show", "table", "all"], {
17106
17269
  encoding: "utf8",
17107
17270
  timeout: 5e3,
17108
17271
  maxBuffer: 1024 * 1024
@@ -17110,11 +17273,11 @@ function collectNetworkRoutes(options) {
17110
17273
  if (result.error || result.status !== 0) return { status: "unknown", routes: [] };
17111
17274
  return parseLinuxNetworkRoutes(result.stdout);
17112
17275
  }
17113
- var import_node_child_process25, import_node_net3;
17276
+ var import_node_child_process26, import_node_net3;
17114
17277
  var init_network_routes = __esm({
17115
17278
  "src/core/network-routes.ts"() {
17116
17279
  "use strict";
17117
- import_node_child_process25 = require("child_process");
17280
+ import_node_child_process26 = require("child_process");
17118
17281
  import_node_net3 = require("net");
17119
17282
  init_network_capacity();
17120
17283
  init_network_local_daemon();
@@ -17192,11 +17355,11 @@ var init_network_compose = __esm({
17192
17355
  });
17193
17356
 
17194
17357
  // src/core/network-compose-files.ts
17195
- var import_node_child_process26, import_node_crypto21, import_node_fs35, import_node_path43, import_yaml4, MAX_BYTES3;
17358
+ var import_node_child_process27, import_node_crypto21, import_node_fs35, import_node_path43, import_yaml4, MAX_BYTES3;
17196
17359
  var init_network_compose_files = __esm({
17197
17360
  "src/core/network-compose-files.ts"() {
17198
17361
  "use strict";
17199
- import_node_child_process26 = require("child_process");
17362
+ import_node_child_process27 = require("child_process");
17200
17363
  import_node_crypto21 = require("crypto");
17201
17364
  import_node_fs35 = __toESM(require("fs"));
17202
17365
  import_node_path43 = __toESM(require("path"));
@@ -17209,11 +17372,11 @@ var init_network_compose_files = __esm({
17209
17372
  });
17210
17373
 
17211
17374
  // src/core/network-connected-route.ts
17212
- var import_node_child_process27;
17375
+ var import_node_child_process28;
17213
17376
  var init_network_connected_route = __esm({
17214
17377
  "src/core/network-connected-route.ts"() {
17215
17378
  "use strict";
17216
- import_node_child_process27 = require("child_process");
17379
+ import_node_child_process28 = require("child_process");
17217
17380
  init_network_capacity();
17218
17381
  }
17219
17382
  });
@@ -17303,11 +17466,11 @@ function resolveLinkedTarget(repoPath) {
17303
17466
  gitCommonDir: resolveGitCommonDir(repoPath)
17304
17467
  };
17305
17468
  }
17306
- var import_node_child_process28, import_node_fs36, import_node_path44;
17469
+ var import_node_child_process29, import_node_fs36, import_node_path44;
17307
17470
  var init_workspace_ensure = __esm({
17308
17471
  "src/core/workspace-ensure.ts"() {
17309
17472
  "use strict";
17310
- import_node_child_process28 = require("child_process");
17473
+ import_node_child_process29 = require("child_process");
17311
17474
  import_node_fs36 = __toESM(require("fs"));
17312
17475
  import_node_path44 = __toESM(require("path"));
17313
17476
  init_devcontainer_profile();
@@ -17587,6 +17750,44 @@ function restoreParkedIntentAfterFailedResume(input2) {
17587
17750
  record5.state = transition2.state;
17588
17751
  });
17589
17752
  }
17753
+ function snapshotLifecycleStopIntent(state) {
17754
+ return {
17755
+ intentRevision: state.intentRevision,
17756
+ observationsAfterMs: state.observationsAfterMs,
17757
+ desired: state.desired,
17758
+ phase: state.phase,
17759
+ profile: state.profile,
17760
+ admission: state.admission,
17761
+ stopProof: { ...state.stopProof },
17762
+ requests: state.requests.map((request2) => ({ ...request2 })),
17763
+ observations: state.observations.map((observation) => ({ ...observation })),
17764
+ operation: state.operation ? { ...state.operation } : null
17765
+ };
17766
+ }
17767
+ function withdrawUnstartedLifecycleStop(input2) {
17768
+ return updateReliabilityOperation(input2.identity, (record5) => {
17769
+ const state = record5.state;
17770
+ const intent = input2.intent;
17771
+ if (!intent.wroteIntent) return false;
17772
+ if (!matchesFence(record5, intent.fence)) return false;
17773
+ if (record5.worker || record5.stopWorkStarted) return false;
17774
+ if (state.desired !== "stopped-by-user" || state.phase !== "stopping") return false;
17775
+ if (state.stopProof.workloadsStopped || state.stopProof.routesRemoved) return false;
17776
+ if (!(0, import_node_util11.isDeepStrictEqual)(state.operation, intent.recordedOperation)) return false;
17777
+ const prior = intent.prior;
17778
+ state.intentRevision = prior.intentRevision;
17779
+ state.observationsAfterMs = prior.observationsAfterMs;
17780
+ state.desired = prior.desired;
17781
+ state.phase = prior.phase;
17782
+ state.profile = prior.profile;
17783
+ state.admission = prior.admission;
17784
+ state.stopProof = { ...prior.stopProof };
17785
+ state.requests = prior.requests.map((request2) => ({ ...request2 }));
17786
+ state.observations = prior.observations.map((observation) => ({ ...observation }));
17787
+ state.operation = prior.operation ? { ...prior.operation } : null;
17788
+ return true;
17789
+ });
17790
+ }
17590
17791
  function prepareResumeLifecycleOperation(input2) {
17591
17792
  const ids2 = newLifecycleIds();
17592
17793
  return updateReliabilityOperation(input2.identity, (record5) => {
@@ -17739,12 +17940,37 @@ async function superviseLifecycle(kind, repoPath, options = {}, command) {
17739
17940
  const copiedOptions = { ...options };
17740
17941
  const copiedCommand = command ? [...command] : void 0;
17741
17942
  if (kind === "stop") {
17742
- const fence = updateReliabilityOperation(identity2, (record5) => {
17943
+ const intent = updateReliabilityOperation(identity2, (record5) => {
17743
17944
  reconcileDrained(record5);
17945
+ const prior = snapshotLifecycleStopIntent(record5.state);
17744
17946
  stepRecord(record5, { ...reliabilityFence(record5.state), type: "stop" });
17745
- return reliabilityFence(record5.state);
17947
+ return {
17948
+ fence: reliabilityFence(record5.state),
17949
+ prior,
17950
+ recordedOperation: record5.state.operation ? { ...record5.state.operation } : null,
17951
+ wroteIntent: record5.state.intentRevision !== prior.intentRevision
17952
+ };
17746
17953
  });
17747
- return runLifecycleWorker({ kind, repoPath, identity: identity2, ...ids2, fence, options: copiedOptions });
17954
+ try {
17955
+ return await runLifecycleWorker({
17956
+ kind,
17957
+ repoPath,
17958
+ identity: identity2,
17959
+ ...ids2,
17960
+ fence: intent.fence,
17961
+ options: copiedOptions
17962
+ });
17963
+ } catch (error) {
17964
+ try {
17965
+ withdrawUnstartedLifecycleStop({ identity: identity2, intent });
17966
+ } catch (withdrawal) {
17967
+ process.stderr.write(
17968
+ `Lifecycle stop intent could not be withdrawn: ${withdrawal instanceof Error ? withdrawal.message : String(withdrawal)}
17969
+ `
17970
+ );
17971
+ }
17972
+ throw error;
17973
+ }
17748
17974
  }
17749
17975
  const initial = readReliabilityOperation(identity2) ?? updateReliabilityOperation(identity2, (record5) => record5);
17750
17976
  if (initial.state.executionPolicy === "capacity-managed" || capacityPolicyTargets(repoPath))
@@ -21995,6 +22221,13 @@ function appendManagedRuntimeRows(rows2, managedRuntime) {
21995
22221
  rows2.push(["Effective config SHA-256", managedRuntime.effectiveConfigSha256 ?? "-"]);
21996
22222
  rows2.push(["Transition phase", managedRuntime.transitionPhase ?? "-"]);
21997
22223
  rows2.push(["Runtime drift", formatResourceNames(managedRuntime.drift)]);
22224
+ if (managedRuntime.mountNesting && managedRuntime.mountNesting.status !== "not-applicable") {
22225
+ const nesting = managedRuntime.mountNesting;
22226
+ rows2.push([
22227
+ "Mount nesting",
22228
+ nesting.status === "unwound" ? `unwound: ${nesting.unwound.map((entry) => entry.destination).join(", ")} (restart the container to re-apply)` : nesting.status === "unverified" ? `unverified (${nesting.reason ?? "unknown"})` : `effective (${nesting.checked.join(", ")})`
22229
+ ]);
22230
+ }
21998
22231
  if (managedRuntime.reliability) {
21999
22232
  const reliability = managedRuntime.reliability;
22000
22233
  rows2.push(["Lifecycle intent", reliability.desired]);
@@ -23106,6 +23339,11 @@ async function collectRouterStatus(repoPath) {
23106
23339
  nextSteps.push(`Run: dev app ls --repo ${repo.path}`);
23107
23340
  nextSteps.push("Run: dev app run <name> --repo <path> --yes");
23108
23341
  nextSteps.push("Run: dev ls");
23342
+ if (repo.managedRuntime?.mountNesting?.status === "unwound") {
23343
+ nextSteps.push(
23344
+ `Run: devrouter stop ${repo.path} and then devrouter ensure ${repo.path} to re-apply the unwound nested mount`
23345
+ );
23346
+ }
23109
23347
  }
23110
23348
  const files = getRouterFileLayout();
23111
23349
  if (files.missing.length > 0) {
@@ -23149,7 +23387,7 @@ function outputFromResult(result) {
23149
23387
  return output2.length > 0 ? output2 : void 0;
23150
23388
  }
23151
23389
  function runTool(command, args = []) {
23152
- const result = (0, import_node_child_process29.spawnSync)(command, args, {
23390
+ const result = (0, import_node_child_process30.spawnSync)(command, args, {
23153
23391
  encoding: "utf-8"
23154
23392
  });
23155
23393
  if (result.error) {
@@ -23340,7 +23578,7 @@ function displayPath(filePath) {
23340
23578
  return `~${filePath.slice(home.length)}`;
23341
23579
  }
23342
23580
  function probeInstalledCliVersion(executable) {
23343
- const result = (0, import_node_child_process29.spawnSync)(executable, ["-V"], {
23581
+ const result = (0, import_node_child_process30.spawnSync)(executable, ["-V"], {
23344
23582
  encoding: "utf-8",
23345
23583
  timeout: CLI_PATH_PROBE_TIMEOUT_MS
23346
23584
  });
@@ -23351,7 +23589,7 @@ function probeInstalledCliVersion(executable) {
23351
23589
  }
23352
23590
  function buildCliPathCheck(options = {}) {
23353
23591
  const id5 = "global.cli-path";
23354
- const runningVersion = options.runningVersion ?? (true ? "0.1.2" : "0.0.0-dev");
23592
+ const runningVersion = options.runningVersion ?? (true ? "0.1.3" : "0.0.0-dev");
23355
23593
  if (runningVersion === "0.0.0-dev") {
23356
23594
  return {
23357
23595
  id: id5,
@@ -23526,11 +23764,11 @@ function buildGlobalToolChecksWithoutTelemetry(repoPath) {
23526
23764
  checks.push(nodeToolchainCheck(repoPath));
23527
23765
  return checks;
23528
23766
  }
23529
- var import_node_child_process29, import_node_fs43, import_node_path54, DEVSY_DISABLE_TELEMETRY, CLI_PATH_PROBE_TIMEOUT_MS, INSTALLED_CLI_VERSION_RE;
23767
+ var import_node_child_process30, import_node_fs43, import_node_path54, DEVSY_DISABLE_TELEMETRY, CLI_PATH_PROBE_TIMEOUT_MS, INSTALLED_CLI_VERSION_RE;
23530
23768
  var init_tool_diagnostics = __esm({
23531
23769
  "src/core/tool-diagnostics.ts"() {
23532
23770
  "use strict";
23533
- import_node_child_process29 = require("child_process");
23771
+ import_node_child_process30 = require("child_process");
23534
23772
  import_node_fs43 = __toESM(require("fs"));
23535
23773
  import_node_path54 = __toESM(require("path"));
23536
23774
  init_devsy_agent();
@@ -24186,6 +24424,43 @@ async function buildDoctorReport(options = {}) {
24186
24424
  ...details ? { details } : {},
24187
24425
  suggestion: managedRuntime.mode === "legacy" ? void 0 : managedRuntime.status === "failed-transition" ? `Inspect: dev status --repo ${repo.path}; resolve the reported drift before retrying ensure.` : managedRuntime.status === "ready" ? void 0 : `Run: dev ensure ${repo.path}`
24188
24426
  });
24427
+ const mountNesting = managedRuntime.mountNesting;
24428
+ if (mountNesting && mountNesting.status !== "not-applicable") {
24429
+ const container = mountNesting.container ?? "unknown";
24430
+ const shortContainer = container === "unknown" ? container : container.slice(0, 12);
24431
+ if (mountNesting.status === "unwound") {
24432
+ const first = mountNesting.unwound[0];
24433
+ addCheck(checks, {
24434
+ id: "repo.managed-mount-nesting",
24435
+ level: "warn",
24436
+ summary: `Managed container ${shortContainer} no longer reports ${first?.destination ?? "a nested mount"} in its own mount table, so the mount underneath it is visible in the container.`,
24437
+ details: [
24438
+ `container=${container}`,
24439
+ ...mountNesting.unwound.map(
24440
+ (entry) => `unwound=${entry.destination} (source ${entry.source} inside ${entry.nestedWithin})`
24441
+ )
24442
+ ].join("; "),
24443
+ suggestion: `Run: devrouter stop ${repo.path} and then devrouter ensure ${repo.path} to restart the exact container, which re-applies the nested mount.`
24444
+ });
24445
+ } else if (mountNesting.status === "unverified") {
24446
+ addCheck(checks, {
24447
+ id: "repo.managed-mount-nesting",
24448
+ level: "warn",
24449
+ summary: `Managed container ${shortContainer} did not report its own mount table, so its nested mounts are unverified.`,
24450
+ details: [
24451
+ `container=${container}`,
24452
+ `reason=${mountNesting.reason ?? "unknown"}`
24453
+ ].join("; "),
24454
+ suggestion: `Run: devrouter exec ${repo.path} -- cat /proc/self/mountinfo to read the container's own mount table.`
24455
+ });
24456
+ } else {
24457
+ addCheck(checks, {
24458
+ id: "repo.managed-mount-nesting",
24459
+ level: "ok",
24460
+ summary: `Managed container ${shortContainer} still reports ${mountNesting.checked.join(", ")} in its own mount table.`
24461
+ });
24462
+ }
24463
+ }
24189
24464
  }
24190
24465
  if (repo.appCount === 0) {
24191
24466
  addCheck(checks, {
@@ -24210,7 +24485,7 @@ async function buildDoctorReport(options = {}) {
24210
24485
  suggestion: `Remedy ${expansionNotice.remedy.code}: set a named default profile other than '${expansionNotice.profile}'${remedyCandidates}.`
24211
24486
  });
24212
24487
  }
24213
- const cliVersion = true ? "0.1.2" : "0.0.0-dev";
24488
+ const cliVersion = true ? "0.1.3" : "0.0.0-dev";
24214
24489
  const configVersion = config.devrouter?.version;
24215
24490
  const installedKnown = cliVersion !== "0.0.0-dev";
24216
24491
  const versionComparison = installedKnown && configVersion ? compareSemver(configVersion, cliVersion) : void 0;
@@ -24774,12 +25049,12 @@ function parseSsPortListeners(stdout, port) {
24774
25049
  return listeners;
24775
25050
  }
24776
25051
  function findPortListeners(port) {
24777
- const result = (0, import_node_child_process30.spawnSync)("lsof", ["-nP", `-iTCP:${port}`, "-sTCP:LISTEN"], {
25052
+ const result = (0, import_node_child_process31.spawnSync)("lsof", ["-nP", `-iTCP:${port}`, "-sTCP:LISTEN"], {
24778
25053
  encoding: "utf-8"
24779
25054
  });
24780
25055
  if (result.error && result.error.code === "ENOENT") {
24781
25056
  if (process.platform === "linux") {
24782
- const ssResult = (0, import_node_child_process30.spawnSync)("ss", ["-H", "-lntp", "-p"], { encoding: "utf-8" });
25057
+ const ssResult = (0, import_node_child_process31.spawnSync)("ss", ["-H", "-lntp", "-p"], { encoding: "utf-8" });
24783
25058
  if (ssResult.status === 0 && ssResult.stdout) {
24784
25059
  return parseSsPortListeners(ssResult.stdout, port);
24785
25060
  }
@@ -24808,11 +25083,11 @@ function findPortListeners(port) {
24808
25083
  };
24809
25084
  });
24810
25085
  }
24811
- var import_node_child_process30;
25086
+ var import_node_child_process31;
24812
25087
  var init_ports = __esm({
24813
25088
  "src/util/ports.ts"() {
24814
25089
  "use strict";
24815
- import_node_child_process30 = require("child_process");
25090
+ import_node_child_process31 = require("child_process");
24816
25091
  }
24817
25092
  });
24818
25093
 
@@ -25793,7 +26068,7 @@ async function runOpenCommand(name) {
25793
26068
  );
25794
26069
  return;
25795
26070
  }
25796
- const result = (0, import_node_child_process31.spawnSync)("open", [url], { encoding: "utf-8" });
26071
+ const result = (0, import_node_child_process32.spawnSync)("open", [url], { encoding: "utf-8" });
25797
26072
  if (result.status !== 0) {
25798
26073
  const details = [result.stdout, result.stderr].filter(Boolean).join("\n").trim();
25799
26074
  throw new Error(`Unable to open '${url}': ${details || "unknown error"}`);
@@ -25801,11 +26076,11 @@ async function runOpenCommand(name) {
25801
26076
  process.stdout.write(`Opened ${url}
25802
26077
  `);
25803
26078
  }
25804
- var import_node_child_process31;
26079
+ var import_node_child_process32;
25805
26080
  var init_open = __esm({
25806
26081
  "src/commands/open.ts"() {
25807
26082
  "use strict";
25808
- import_node_child_process31 = require("child_process");
26083
+ import_node_child_process32 = require("child_process");
25809
26084
  init_docker();
25810
26085
  init_host_routes();
25811
26086
  init_repo_config();
@@ -25828,7 +26103,7 @@ async function runLogsCommand(options) {
25828
26103
  const args = ["logs", "--tail", tail, ROUTER_CONTAINER_NAME];
25829
26104
  if (options.follow) {
25830
26105
  args.splice(1, 0, "-f");
25831
- const child = (0, import_node_child_process32.spawn)("docker", args, { stdio: "inherit" });
26106
+ const child = (0, import_node_child_process33.spawn)("docker", args, { stdio: "inherit" });
25832
26107
  const onSignal = () => {
25833
26108
  child.kill("SIGTERM");
25834
26109
  };
@@ -25842,17 +26117,17 @@ async function runLogsCommand(options) {
25842
26117
  });
25843
26118
  });
25844
26119
  } else {
25845
- const result = (0, import_node_child_process32.spawnSync)("docker", args, { stdio: "inherit" });
26120
+ const result = (0, import_node_child_process33.spawnSync)("docker", args, { stdio: "inherit" });
25846
26121
  if (result.status !== 0) {
25847
26122
  throw new Error("Failed to retrieve router logs.");
25848
26123
  }
25849
26124
  }
25850
26125
  }
25851
- var import_node_child_process32;
26126
+ var import_node_child_process33;
25852
26127
  var init_logs = __esm({
25853
26128
  "src/commands/logs.ts"() {
25854
26129
  "use strict";
25855
- import_node_child_process32 = require("child_process");
26130
+ import_node_child_process33 = require("child_process");
25856
26131
  init_docker();
25857
26132
  init_router();
25858
26133
  }
@@ -27898,7 +28173,7 @@ function runDockerComposeUp(repoPath, composeFiles2, overlayPath, services) {
27898
28173
  fileArgs.push("-f", resolved);
27899
28174
  }
27900
28175
  const args = ["compose", ...fileArgs, "-f", overlayPath, "up", "-d", "--wait", ...services];
27901
- const result = (0, import_node_child_process33.spawnSync)("docker", args, {
28176
+ const result = (0, import_node_child_process34.spawnSync)("docker", args, {
27902
28177
  encoding: "utf-8",
27903
28178
  cwd: repoPath
27904
28179
  });
@@ -27926,7 +28201,7 @@ function queryRunningComposeServices(repoPath, composeFiles2, overlayPath, servi
27926
28201
  "--services",
27927
28202
  ...services
27928
28203
  ];
27929
- const result = (0, import_node_child_process33.spawnSync)("docker", args, {
28204
+ const result = (0, import_node_child_process34.spawnSync)("docker", args, {
27930
28205
  encoding: "utf-8",
27931
28206
  cwd: repoPath
27932
28207
  });
@@ -27952,7 +28227,7 @@ function runDockerComposeStop(repoPath, composeFiles2, overlayPath, services) {
27952
28227
  fileArgs.push("-f", resolved);
27953
28228
  }
27954
28229
  const args = ["compose", ...fileArgs, "-f", overlayPath, "stop", ...services];
27955
- const result = (0, import_node_child_process33.spawnSync)("docker", args, {
28230
+ const result = (0, import_node_child_process34.spawnSync)("docker", args, {
27956
28231
  encoding: "utf-8",
27957
28232
  cwd: repoPath
27958
28233
  });
@@ -27978,7 +28253,7 @@ function runDockerComposeLogs(repoPath, composeFiles2, overlayPath, services, ta
27978
28253
  String(tail),
27979
28254
  ...services
27980
28255
  ];
27981
- (0, import_node_child_process33.spawnSync)("docker", args, {
28256
+ (0, import_node_child_process34.spawnSync)("docker", args, {
27982
28257
  stdio: "inherit",
27983
28258
  cwd: repoPath
27984
28259
  });
@@ -27990,7 +28265,7 @@ function queryMappedPort(repoPath, composeFiles2, overlayPath, service, internal
27990
28265
  fileArgs.push("-f", resolved);
27991
28266
  }
27992
28267
  const args = ["compose", ...fileArgs, "-f", overlayPath, "port", service, String(internalPort)];
27993
- const result = (0, import_node_child_process33.spawnSync)("docker", args, {
28268
+ const result = (0, import_node_child_process34.spawnSync)("docker", args, {
27994
28269
  encoding: "utf-8",
27995
28270
  cwd: repoPath
27996
28271
  });
@@ -28004,11 +28279,11 @@ function queryMappedPort(repoPath, composeFiles2, overlayPath, service, internal
28004
28279
  const port = Number(match[1]);
28005
28280
  return Number.isInteger(port) && port > 0 ? port : void 0;
28006
28281
  }
28007
- var import_node_child_process33, import_node_crypto29, import_node_fs51, import_node_path64, import_yaml9;
28282
+ var import_node_child_process34, import_node_crypto29, import_node_fs51, import_node_path64, import_yaml9;
28008
28283
  var init_docker_run = __esm({
28009
28284
  "src/core/docker-run.ts"() {
28010
28285
  "use strict";
28011
- import_node_child_process33 = require("child_process");
28286
+ import_node_child_process34 = require("child_process");
28012
28287
  import_node_crypto29 = require("crypto");
28013
28288
  import_node_fs51 = __toESM(require("fs"));
28014
28289
  import_node_path64 = __toESM(require("path"));
@@ -28101,7 +28376,7 @@ function isProcessRunning(pid) {
28101
28376
  }
28102
28377
  }
28103
28378
  function readProcessTree(rootPid) {
28104
- const result = (0, import_node_child_process34.spawnSync)("ps", ["-ax", "-o", "pid=,ppid="], { encoding: "utf-8" });
28379
+ const result = (0, import_node_child_process35.spawnSync)("ps", ["-ax", "-o", "pid=,ppid="], { encoding: "utf-8" });
28105
28380
  if (result.status !== 0) {
28106
28381
  return [rootPid];
28107
28382
  }
@@ -28212,12 +28487,12 @@ function detectListeningPorts(pids) {
28212
28487
  if (pids.length === 0) {
28213
28488
  return [];
28214
28489
  }
28215
- const result = (0, import_node_child_process34.spawnSync)("lsof", ["-nP", "-iTCP", "-sTCP:LISTEN", "-a", "-p", pids.join(",")], {
28490
+ const result = (0, import_node_child_process35.spawnSync)("lsof", ["-nP", "-iTCP", "-sTCP:LISTEN", "-a", "-p", pids.join(",")], {
28216
28491
  encoding: "utf-8"
28217
28492
  });
28218
28493
  if (result.error && result.error.code === "ENOENT") {
28219
28494
  if (process.platform === "linux") {
28220
- const ssResult = (0, import_node_child_process34.spawnSync)("ss", ["-H", "-lntp", "-p"], { encoding: "utf-8" });
28495
+ const ssResult = (0, import_node_child_process35.spawnSync)("ss", ["-H", "-lntp", "-p"], { encoding: "utf-8" });
28221
28496
  if (ssResult.status === 0 && ssResult.stdout) {
28222
28497
  return parseSsListeningPorts(ssResult.stdout, new Set(pids));
28223
28498
  }
@@ -28276,7 +28551,7 @@ async function runHostApp(repoPath, app, extraEnv = {}, secretManager, env, work
28276
28551
  app.hostRun.command,
28277
28552
  true
28278
28553
  ) : app.hostRun.command;
28279
- const child = (0, import_node_child_process34.spawn)(spawnCommand, {
28554
+ const child = (0, import_node_child_process35.spawn)(spawnCommand, {
28280
28555
  cwd: commandCwd,
28281
28556
  stdio: "inherit",
28282
28557
  shell: true,
@@ -28644,7 +28919,7 @@ async function execWithAppEnv(options) {
28644
28919
  options.command[0],
28645
28920
  true
28646
28921
  );
28647
- child = (0, import_node_child_process34.spawn)(wrapped, {
28922
+ child = (0, import_node_child_process35.spawn)(wrapped, {
28648
28923
  cwd: deps.repoPath,
28649
28924
  stdio: "inherit",
28650
28925
  shell: true,
@@ -28658,7 +28933,7 @@ async function execWithAppEnv(options) {
28658
28933
  false
28659
28934
  );
28660
28935
  const [cmd, ...wrappedArgs] = wrapped;
28661
- child = (0, import_node_child_process34.spawn)(cmd, wrappedArgs, {
28936
+ child = (0, import_node_child_process35.spawn)(cmd, wrappedArgs, {
28662
28937
  cwd: deps.repoPath,
28663
28938
  stdio: "inherit",
28664
28939
  shell: false,
@@ -28666,7 +28941,7 @@ async function execWithAppEnv(options) {
28666
28941
  });
28667
28942
  }
28668
28943
  } else if (options.shell) {
28669
- child = (0, import_node_child_process34.spawn)(options.command[0], {
28944
+ child = (0, import_node_child_process35.spawn)(options.command[0], {
28670
28945
  cwd: deps.repoPath,
28671
28946
  stdio: "inherit",
28672
28947
  shell: true,
@@ -28674,7 +28949,7 @@ async function execWithAppEnv(options) {
28674
28949
  });
28675
28950
  } else {
28676
28951
  const [command, ...args] = options.command;
28677
- child = (0, import_node_child_process34.spawn)(command, args, {
28952
+ child = (0, import_node_child_process35.spawn)(command, args, {
28678
28953
  cwd: deps.repoPath,
28679
28954
  stdio: "inherit",
28680
28955
  shell: false,
@@ -28695,11 +28970,11 @@ async function execWithAppEnv(options) {
28695
28970
  deps.stopDeps();
28696
28971
  }
28697
28972
  }
28698
- var import_node_child_process34, import_node_net5, import_node_process, import_promises, POLL_INTERVAL_MS, DEFAULT_PORT_TIMEOUT_MS, PROCESS_TERMINATION_GRACE_MS;
28973
+ var import_node_child_process35, import_node_net5, import_node_process, import_promises, POLL_INTERVAL_MS, DEFAULT_PORT_TIMEOUT_MS, PROCESS_TERMINATION_GRACE_MS;
28699
28974
  var init_app_run = __esm({
28700
28975
  "src/core/app-run.ts"() {
28701
28976
  "use strict";
28702
- import_node_child_process34 = require("child_process");
28977
+ import_node_child_process35 = require("child_process");
28703
28978
  import_node_net5 = __toESM(require("net"));
28704
28979
  import_node_process = require("process");
28705
28980
  import_promises = require("readline/promises");
@@ -29623,11 +29898,11 @@ function buildWorkspaceCleanupReport(options = {}, dependencies = {}) {
29623
29898
  networkCapacity: (dependencies.inspectNetworkCapacity ?? inspectNetworkCapacity)()
29624
29899
  };
29625
29900
  }
29626
- var import_node_child_process35, import_node_fs53, DEFAULT_INACTIVE_FOR, READ_ONLY_GIT_ENV2, ACTIVITY_SOURCES, defaultCommandRunner;
29901
+ var import_node_child_process36, import_node_fs53, DEFAULT_INACTIVE_FOR, READ_ONLY_GIT_ENV2, ACTIVITY_SOURCES, defaultCommandRunner;
29627
29902
  var init_workspace_cleanup = __esm({
29628
29903
  "src/core/workspace-cleanup.ts"() {
29629
29904
  "use strict";
29630
- import_node_child_process35 = require("child_process");
29905
+ import_node_child_process36 = require("child_process");
29631
29906
  import_node_fs53 = __toESM(require("fs"));
29632
29907
  init_devpod_workspaces();
29633
29908
  init_host_routes();
@@ -29646,7 +29921,7 @@ var init_workspace_cleanup = __esm({
29646
29921
  ];
29647
29922
  defaultCommandRunner = (command, args, input2) => {
29648
29923
  const environment = command === "git" ? READ_ONLY_GIT_ENV2 : { ...process.env, LC_ALL: "C" };
29649
- const result = (0, import_node_child_process35.spawnSync)(command, args, {
29924
+ const result = (0, import_node_child_process36.spawnSync)(command, args, {
29650
29925
  encoding: "utf-8",
29651
29926
  env: environment,
29652
29927
  ...input2 === void 0 ? {} : { input: input2 }
@@ -29743,7 +30018,7 @@ function defaultWorktreePath(mainRepo, ws) {
29743
30018
  return import_node_path66.default.join(mainRepo, "trees", ws);
29744
30019
  }
29745
30020
  function assertDefaultWorktreeRootIgnored(mainRepo) {
29746
- const ignored = (0, import_node_child_process36.spawnSync)("git", ["-C", mainRepo, "check-ignore", "-q", "--no-index", "trees/"], {
30021
+ const ignored = (0, import_node_child_process37.spawnSync)("git", ["-C", mainRepo, "check-ignore", "-q", "--no-index", "trees/"], {
29747
30022
  encoding: "utf-8"
29748
30023
  });
29749
30024
  if (ignored.status !== 0) {
@@ -29871,7 +30146,7 @@ function assertFullDownPreflight(mainRepo, target) {
29871
30146
  `Worktree '${target.worktreePath}' is locked; unlock it before workspace down.`
29872
30147
  );
29873
30148
  }
29874
- const status = (0, import_node_child_process36.spawnSync)(
30149
+ const status = (0, import_node_child_process37.spawnSync)(
29875
30150
  "git",
29876
30151
  ["-C", target.worktreePath, "status", "--porcelain", "--untracked-files=normal"],
29877
30152
  { encoding: "utf-8" }
@@ -29935,11 +30210,11 @@ async function workspaceUp(branch, opts = {}) {
29935
30210
  return selectedPath;
29936
30211
  }
29937
30212
  if (!opts.path) assertDefaultWorktreeRootIgnored(mainRepo);
29938
- const add = (0, import_node_child_process36.spawnSync)("git", ["-C", mainRepo, "worktree", "add", selectedPath, branch], {
30213
+ const add = (0, import_node_child_process37.spawnSync)("git", ["-C", mainRepo, "worktree", "add", selectedPath, branch], {
29939
30214
  encoding: "utf-8"
29940
30215
  });
29941
30216
  if (add.status !== 0) {
29942
- const addNew = (0, import_node_child_process36.spawnSync)(
30217
+ const addNew = (0, import_node_child_process37.spawnSync)(
29943
30218
  "git",
29944
30219
  ["-C", mainRepo, "worktree", "add", "-b", branch, selectedPath],
29945
30220
  { encoding: "utf-8" }
@@ -30080,7 +30355,7 @@ async function runWorkspaceLifecycle(action2, target, opts = {}) {
30080
30355
  const result = await mutateWorkspaceRuntime("delete", resolved, worktrees, opts.quiet);
30081
30356
  if (removeWorktree) {
30082
30357
  if (resolved.worktree && !resolved.worktree.prunable && import_node_fs54.default.existsSync(resolved.worktreePath)) {
30083
- const rm = (0, import_node_child_process36.spawnSync)("git", ["-C", mainRepo, "worktree", "remove", resolved.worktreePath], {
30358
+ const rm = (0, import_node_child_process37.spawnSync)("git", ["-C", mainRepo, "worktree", "remove", resolved.worktreePath], {
30084
30359
  encoding: "utf-8"
30085
30360
  });
30086
30361
  if (rm.status !== 0) {
@@ -30106,11 +30381,11 @@ async function workspaceStop(target, opts = {}) {
30106
30381
  async function workspaceDown(target, opts = {}) {
30107
30382
  return runWorkspaceLifecycle("down", target, opts);
30108
30383
  }
30109
- var import_node_child_process36, import_node_fs54, import_node_path66, WORKSPACE_ALLOCATION_LOCK_WAIT_MS;
30384
+ var import_node_child_process37, import_node_fs54, import_node_path66, WORKSPACE_ALLOCATION_LOCK_WAIT_MS;
30110
30385
  var init_workspace_lifecycle = __esm({
30111
30386
  "src/core/workspace-lifecycle.ts"() {
30112
30387
  "use strict";
30113
- import_node_child_process36 = require("child_process");
30388
+ import_node_child_process37 = require("child_process");
30114
30389
  import_node_fs54 = __toESM(require("fs"));
30115
30390
  import_node_path66 = __toESM(require("path"));
30116
30391
  init_devpod_mutation();
@@ -30296,7 +30571,7 @@ var init_version = __esm({
30296
30571
 
30297
30572
  // src/cli.ts
30298
30573
  var import_commander = require("commander");
30299
- var CLI_VERSION = true ? "0.1.2" : "0.0.0-dev";
30574
+ var CLI_VERSION = true ? "0.1.3" : "0.0.0-dev";
30300
30575
  var VERSION_FLAGS = /* @__PURE__ */ new Set(["-V", "--version"]);
30301
30576
  function withErrorHandling(action2) {
30302
30577
  return async (...args) => {