@devrouter/cli 0.0.56 → 0.0.58

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
@@ -2291,6 +2291,38 @@ function parseRequiredUniqueStringArray(value, pathLabel) {
2291
2291
  }
2292
2292
  return parseUniqueStringArray(value, pathLabel);
2293
2293
  }
2294
+ function parsePrepareCommand(value, pathLabel) {
2295
+ if (value === void 0) {
2296
+ return void 0;
2297
+ }
2298
+ if (!Array.isArray(value)) {
2299
+ throw new Error(`${pathLabel} must be a non-empty array of strings.`);
2300
+ }
2301
+ if (value.length === 0) {
2302
+ throw new Error(`${pathLabel} must be a non-empty array of strings.`);
2303
+ }
2304
+ if (value.length > MAX_PREPARE_ARGS) {
2305
+ throw new Error(`${pathLabel} exceeds the maximum of ${MAX_PREPARE_ARGS} arguments.`);
2306
+ }
2307
+ return value.map((item, index) => {
2308
+ if (typeof item !== "string") {
2309
+ throw new Error(`${pathLabel}[${index}] must be a string.`);
2310
+ }
2311
+ if (index === 0 && item.length === 0) {
2312
+ throw new Error(`${pathLabel}[0] must be a non-empty executable.`);
2313
+ }
2314
+ if (item.includes("\0")) {
2315
+ throw new Error(`${pathLabel}[${index}] must not contain null bytes.`);
2316
+ }
2317
+ const byteLength = Buffer.byteLength(item, "utf8");
2318
+ if (byteLength > MAX_PREPARE_ARG_BYTES) {
2319
+ throw new Error(
2320
+ `${pathLabel}[${index}] exceeds the maximum of ${MAX_PREPARE_ARG_BYTES} UTF-8 bytes.`
2321
+ );
2322
+ }
2323
+ return item;
2324
+ });
2325
+ }
2294
2326
  function parseManagedRuntime(value, configPath) {
2295
2327
  if (value === void 0) {
2296
2328
  return void 0;
@@ -2303,7 +2335,7 @@ function parseManagedRuntime(value, configPath) {
2303
2335
  );
2304
2336
  ensureAllowedKeys(
2305
2337
  devcontainer,
2306
- ["baseServices", "profileServices"],
2338
+ ["baseServices", "profileServices", "prepareCommand"],
2307
2339
  `${configPath}.managedRuntime.devcontainer`
2308
2340
  );
2309
2341
  const baseServices = parseRequiredUniqueStringArray(
@@ -2314,6 +2346,10 @@ function parseManagedRuntime(value, configPath) {
2314
2346
  devcontainer.profileServices,
2315
2347
  `${configPath}.managedRuntime.devcontainer.profileServices`
2316
2348
  );
2349
+ const prepareCommand = parsePrepareCommand(
2350
+ devcontainer.prepareCommand,
2351
+ `${configPath}.managedRuntime.devcontainer.prepareCommand`
2352
+ );
2317
2353
  const baseSet = new Set(baseServices);
2318
2354
  const overlappingServices = profileServices.filter((service) => baseSet.has(service));
2319
2355
  if (overlappingServices.length > 0) {
@@ -2334,7 +2370,11 @@ function parseManagedRuntime(value, configPath) {
2334
2370
  );
2335
2371
  }
2336
2372
  return {
2337
- devcontainer: { baseServices, profileServices },
2373
+ devcontainer: {
2374
+ baseServices,
2375
+ profileServices,
2376
+ ...prepareCommand ? { prepareCommand } : {}
2377
+ },
2338
2378
  processes
2339
2379
  };
2340
2380
  }
@@ -2781,7 +2821,7 @@ function loadRepoConfig(repoPath) {
2781
2821
  const config = parseConfig(parsed ?? {}, configPath);
2782
2822
  const requiredVersion = config.devrouter?.version;
2783
2823
  if (requiredVersion && !hasWarnedVersionMismatch) {
2784
- const cliVersion = true ? "0.0.56" : "0.0.0-dev";
2824
+ const cliVersion = true ? "0.0.58" : "0.0.0-dev";
2785
2825
  if (cliVersion !== "0.0.0-dev" && compareSemver(requiredVersion, cliVersion) > 0) {
2786
2826
  hasWarnedVersionMismatch = true;
2787
2827
  process.stderr.write(
@@ -3125,7 +3165,7 @@ function resolveAppDependencies(config, app) {
3125
3165
  }
3126
3166
  return results;
3127
3167
  }
3128
- var import_node_fs7, import_node_path7, import_yaml2, hasWarnedVersionMismatch, CONFIG_FILE_NAME, DEFAULT_TCP_PROTOCOL, VALID_HOSTNAME_RE, DEVROUTER_VERSION_RE, VALID_ENV_NAME_RE, VALID_ENV_VAR_RE, UPSTREAM_TEMPLATE_RE, MAX_COMMAND_LENGTH, DEFAULT_HOST_STRATEGY, MAX_READINESS_PATH_LENGTH, MIME_TOKEN_RE, PROFILE_NAME_RE, MAX_PROFILES;
3168
+ var import_node_fs7, import_node_path7, import_yaml2, hasWarnedVersionMismatch, CONFIG_FILE_NAME, DEFAULT_TCP_PROTOCOL, VALID_HOSTNAME_RE, DEVROUTER_VERSION_RE, VALID_ENV_NAME_RE, VALID_ENV_VAR_RE, UPSTREAM_TEMPLATE_RE, MAX_COMMAND_LENGTH, MAX_PREPARE_ARGS, MAX_PREPARE_ARG_BYTES, DEFAULT_HOST_STRATEGY, MAX_READINESS_PATH_LENGTH, MIME_TOKEN_RE, PROFILE_NAME_RE, MAX_PROFILES;
3129
3169
  var init_repo_config = __esm({
3130
3170
  "src/core/repo-config.ts"() {
3131
3171
  "use strict";
@@ -3144,6 +3184,8 @@ var init_repo_config = __esm({
3144
3184
  VALID_ENV_VAR_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
3145
3185
  UPSTREAM_TEMPLATE_RE = /^(?:\$\{WORKSPACE\}|[a-zA-Z0-9._-])+:\d{1,5}$/;
3146
3186
  MAX_COMMAND_LENGTH = 4096;
3187
+ MAX_PREPARE_ARGS = 64;
3188
+ MAX_PREPARE_ARG_BYTES = 4096;
3147
3189
  DEFAULT_HOST_STRATEGY = {
3148
3190
  type: "auto",
3149
3191
  denyPorts: [80, 443, 5432],
@@ -3323,6 +3365,7 @@ function buildOnboardingPrompt(options = {}) {
3323
3365
  "- A managed adapter plus `postCreateCommand` requires `waitFor` exactly `postCreateCommand` or `postStartCommand`; managed selective config preserves lifecycle fields and changes only `runServices`.",
3324
3366
  "- Managed process reuse fingerprints command argv, workspace identity, and the exact adapter snapshot. 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.",
3325
3367
  "- 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.",
3368
+ "- 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.",
3326
3369
  "- Exact managed Devsy stop validates complete retained container membership, ownership and configuration before stopping residual services after a stopped primary. Unknown evidence blocks cleanup; original provider failures remain errors even after successful residual cleanup.",
3327
3370
  "- 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.",
3328
3371
  "- `devrouter workspace cleanup --repo . --inactive-for 30d --check-merged --json` is report-only and never mutates the workspace runtime, routes, ownership, Git, Docker, applications, worktrees, or branches. It reports ownership, workspace runtime registration, runtime state (`running|stopped|busy|not-found|absent|unknown`), checkout, advisory activity, route, and integration evidence. Local DevPod/Devsy list/status checks always run; `--check-merged` alone enables read-only origin and matching GitHub/GitLab checks. Treat `not-found` as stale runtime after Docker pruning; busy, unavailable, or conflicting evidence suppresses destructive suggestions. Explicit `gc`/`down` can remove exact stale registration only after expected-ID `NotFound` proof and ownership revalidation. Runtime `lastUsed` remains advisory.",
@@ -3370,7 +3413,7 @@ function buildOnboardingPrompt(options = {}) {
3370
3413
  "- devrouter repo devcontainer write --repo <REPO_PATH> --yes",
3371
3414
  "- devrouter repo devcontainer verify --repo <REPO_PATH> --json",
3372
3415
  "- Normal primary or linked startup: devrouter ensure <REPO_PATH> --json",
3373
- "- Degraded recovery: ordinary ensure automatically repairs the recorded profile before the desired transition; --repair limits the invocation to recorded-profile repair. Both validate unchanged retained configuration; stopped primary requires all project containers stopped and zero checkout routes. It starts only retained IDs, never provider bootstrap/recreation. Inspect failures before retrying.",
3416
+ "- Degraded recovery: ordinary ensure validates retained ownership, then reconciles the requested profile without starting dropped processes first. Same-profile repair and --repair retain the recorded-profile path and start only retained IDs without provider bootstrap/recreation. Stopped primary requires all project containers stopped and zero checkout routes. Failed transitions preserve degraded state without replaying its broken adapter.",
3374
3417
  "- Managed selective startup: devrouter ensure <REPO_PATH> --profile <name> --json",
3375
3418
  "- Container seed/migration: devrouter exec <REPO_PATH> -- <command...>",
3376
3419
  "- Compatibility verification only after ensure: devrouter repo devcontainer verify --repo <REPO_PATH> --live --yes --json",
@@ -9166,7 +9209,7 @@ async function buildDoctorReport(options = {}) {
9166
9209
  const config = runtimeConfig.config;
9167
9210
  loadedConfig = config;
9168
9211
  loadedWorkspace = runtimeConfig.workspace;
9169
- const cliVersion = true ? "0.0.56" : "0.0.0-dev";
9212
+ const cliVersion = true ? "0.0.58" : "0.0.0-dev";
9170
9213
  const configVersion = config.devrouter?.version;
9171
9214
  if (configVersion && cliVersion !== "0.0.0-dev" && compareSemver(configVersion, cliVersion) > 0) {
9172
9215
  addCheck(checks, {
@@ -9803,6 +9846,9 @@ var init_up = __esm({
9803
9846
  function isReliabilityId(value) {
9804
9847
  return typeof value === "string" && /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,127}$/.test(value);
9805
9848
  }
9849
+ function isReliabilityProfile(value) {
9850
+ return typeof value === "string" && value.length <= 4096 && value.split(",").every((name) => isReliabilityId(name.trim()));
9851
+ }
9806
9852
  function isReliabilityCounter(value) {
9807
9853
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
9808
9854
  }
@@ -9833,7 +9879,7 @@ function validOperation(value) {
9833
9879
  function assertReliabilityState(value) {
9834
9880
  const intentRevision = record(value) ? value.intentRevision : void 0;
9835
9881
  const valid = record(value) && value.contractVersion === RELIABILITY_CONTRACT_VERSION && oneOf(value.executionPolicy, ["manual", "capacity-managed"]) && (value.executionPolicy === "manual" ? value.admission === "not-applicable" && value.chargeHeld === false : value.admission !== "not-applicable") && boundedArray(value.operationHistory) && uniqueIds(value.operationHistory, "key") && uniqueIds(value.operationHistory, "id") && value.operationHistory.every(
9836
- (entry) => record(entry) && isReliabilityId(entry.profile) && record(entry.consumer) && isReliabilityId(entry.consumer.id) && typeof entry.consumer.pinned === "boolean" && boundedArray(entry.consumer.requiredCapabilities) && entry.consumer.requiredCapabilities.every(isReliabilityId) && new Set(entry.consumer.requiredCapabilities).size === entry.consumer.requiredCapabilities.length && validOperation(entry)
9882
+ (entry) => record(entry) && isReliabilityProfile(entry.profile) && record(entry.consumer) && isReliabilityId(entry.consumer.id) && typeof entry.consumer.pinned === "boolean" && boundedArray(entry.consumer.requiredCapabilities) && entry.consumer.requiredCapabilities.every(isReliabilityId) && new Set(entry.consumer.requiredCapabilities).size === entry.consumer.requiredCapabilities.length && validOperation(entry)
9837
9883
  ) && isReliabilityCounter(value.observationsAfterMs) && isReliabilityId(value.environmentId) && isReliabilityCounter(value.intentRevision) && isReliabilityCounter(value.runtimeGeneration) && isReliabilityCounter(value.controllerEpoch) && oneOf(value.desired, ["running", "parked-for-capacity", "stopped-by-user"]) && oneOf(value.phase, [
9838
9884
  "idle",
9839
9885
  "queued",
@@ -9842,7 +9888,7 @@ function assertReliabilityState(value) {
9842
9888
  "stable",
9843
9889
  "recovering",
9844
9890
  "stopping"
9845
- ]) && (value.profile === null || isReliabilityId(value.profile)) && oneOf(value.admission, [
9891
+ ]) && (value.profile === null || isReliabilityProfile(value.profile)) && oneOf(value.admission, [
9846
9892
  "admitted",
9847
9893
  "waiting",
9848
9894
  "unknown",
@@ -9851,7 +9897,7 @@ function assertReliabilityState(value) {
9851
9897
  ]) && typeof value.chargeHeld === "boolean" && record(value.stopProof) && typeof value.stopProof.workloadsStopped === "boolean" && typeof value.stopProof.routesRemoved === "boolean" && boundedArray(value.consumers) && uniqueIds(value.consumers, "id") && value.consumers.every(
9852
9898
  (consumer) => record(consumer) && typeof consumer.pinned === "boolean" && boundedArray(consumer.requiredCapabilities) && consumer.requiredCapabilities.every(isReliabilityId) && new Set(consumer.requiredCapabilities).size === consumer.requiredCapabilities.length
9853
9899
  ) && boundedArray(value.requests) && uniqueIds(value.requests, "key") && value.requests.every(
9854
- (request) => record(request) && oneOf(request.mode, ["start", "attach"]) && isReliabilityId(request.consumerId) && isReliabilityId(request.operationId) && isReliabilityId(request.profile) && isReliabilityCounter(request.intentRevision) && isReliabilityCounter(intentRevision) && request.intentRevision <= intentRevision
9900
+ (request) => record(request) && oneOf(request.mode, ["start", "attach"]) && isReliabilityId(request.consumerId) && isReliabilityId(request.operationId) && isReliabilityProfile(request.profile) && isReliabilityCounter(request.intentRevision) && isReliabilityCounter(intentRevision) && request.intentRevision <= intentRevision
9855
9901
  ) && boundedArray(value.observations) && uniqueIds(value.observations, "capability") && value.observations.every(
9856
9902
  (observation) => record(observation) && oneOf(observation.infrastructure, ["healthy", "failed", "unknown"]) && oneOf(observation.application, ["verified", "unready", "unverified"]) && isReliabilityCounter(observation.observedAtMs) && isReliabilityCounter(observation.validForMs)
9857
9903
  ) && (value.operation === null || validOperation(value.operation)) && (value.executionPolicy !== "manual" || value.desired !== "parked-for-capacity" && value.incident === null) && (value.incident === null || record(value.incident) && isReliabilityId(value.incident.id) && isReliabilityCounter(value.incident.correctiveActionsTaken) && isReliabilityCounter(value.incident.actionLimit) && value.incident.actionLimit > 0 && value.incident.correctiveActionsTaken <= value.incident.actionLimit);
@@ -9925,10 +9971,10 @@ function assertReliabilityEvent(value) {
9925
9971
  let valid = false;
9926
9972
  switch (value.type) {
9927
9973
  case "request":
9928
- valid = oneOf2(value.mode, ["start", "attach"]) && isReliabilityId(value.key) && isReliabilityId(value.operationId) && isReliabilityId(value.profile) && isConsumer(value.consumer);
9974
+ valid = oneOf2(value.mode, ["start", "attach"]) && isReliabilityId(value.key) && isReliabilityId(value.operationId) && isReliabilityProfile(value.profile) && isConsumer(value.consumer);
9929
9975
  break;
9930
9976
  case "operation-request":
9931
- valid = oneOf2(value.kind, ["ensure", "exec"]) && isReliabilityId(value.key) && isReliabilityId(value.operationId) && isReliabilityId(value.profile) && isConsumer(value.consumer) && typeof value.runtimeRunning === "boolean";
9977
+ valid = oneOf2(value.kind, ["ensure", "exec"]) && isReliabilityId(value.key) && isReliabilityId(value.operationId) && isReliabilityProfile(value.profile) && isConsumer(value.consumer) && typeof value.runtimeRunning === "boolean";
9932
9978
  break;
9933
9979
  case "drained":
9934
9980
  valid = isReliabilityId(value.operationId);
@@ -11005,12 +11051,23 @@ var init_http_route_probe = __esm({
11005
11051
  }
11006
11052
  });
11007
11053
 
11054
+ // src/core/managed-host-preparation.ts
11055
+ var import_node_child_process21, import_node_fs27;
11056
+ var init_managed_host_preparation = __esm({
11057
+ "src/core/managed-host-preparation.ts"() {
11058
+ "use strict";
11059
+ import_node_child_process21 = require("child_process");
11060
+ import_node_fs27 = __toESM(require("fs"));
11061
+ init_repo_config();
11062
+ }
11063
+ });
11064
+
11008
11065
  // src/core/traefik-route-health.ts
11009
11066
  function sleep(ms) {
11010
11067
  return new Promise((resolve) => setTimeout(resolve, ms));
11011
11068
  }
11012
11069
  function inspectRouterApi(protocol) {
11013
- const result = (0, import_node_child_process21.spawnSync)(
11070
+ const result = (0, import_node_child_process22.spawnSync)(
11014
11071
  "curl",
11015
11072
  [
11016
11073
  "--silent",
@@ -11109,7 +11166,7 @@ async function ensureTraefikRouteExpectation(routes, expectation, options) {
11109
11166
  throw new Error(
11110
11167
  "Traefik routes did not reload; shared router restart is disabled for this operation."
11111
11168
  );
11112
- import_node_fs27.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
11169
+ import_node_fs28.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
11113
11170
  return withFileLock(
11114
11171
  ROUTER_RELOAD_LOCK_FILE,
11115
11172
  {
@@ -11140,12 +11197,12 @@ async function ensureTraefikRouteExpectation(routes, expectation, options) {
11140
11197
  async function ensureTraefikRoutesRemoved(routes, options = {}) {
11141
11198
  return ensureTraefikRouteExpectation(routes, "removed", options);
11142
11199
  }
11143
- var import_node_child_process21, import_node_fs27, import_node_path28, 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;
11200
+ var import_node_child_process22, import_node_fs28, import_node_path28, 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;
11144
11201
  var init_traefik_route_health = __esm({
11145
11202
  "src/core/traefik-route-health.ts"() {
11146
11203
  "use strict";
11147
- import_node_child_process21 = require("child_process");
11148
- import_node_fs27 = __toESM(require("fs"));
11204
+ import_node_child_process22 = require("child_process");
11205
+ import_node_fs28 = __toESM(require("fs"));
11149
11206
  import_node_path28 = __toESM(require("path"));
11150
11207
  init_file_lock();
11151
11208
  init_host_routes();
@@ -11191,12 +11248,12 @@ function resolveLinkedTarget(repoPath) {
11191
11248
  gitCommonDir: resolveGitCommonDir(repoPath)
11192
11249
  };
11193
11250
  }
11194
- var import_node_child_process22, import_node_fs28, import_node_path29;
11251
+ var import_node_child_process23, import_node_fs29, import_node_path29;
11195
11252
  var init_workspace_ensure = __esm({
11196
11253
  "src/core/workspace-ensure.ts"() {
11197
11254
  "use strict";
11198
- import_node_child_process22 = require("child_process");
11199
- import_node_fs28 = __toESM(require("fs"));
11255
+ import_node_child_process23 = require("child_process");
11256
+ import_node_fs29 = __toESM(require("fs"));
11200
11257
  import_node_path29 = __toESM(require("path"));
11201
11258
  init_devcontainer_profile();
11202
11259
  init_devpod_environment();
@@ -11205,6 +11262,7 @@ var init_workspace_ensure = __esm({
11205
11262
  init_file_lock();
11206
11263
  init_host_routes();
11207
11264
  init_http_route_probe();
11265
+ init_managed_host_preparation();
11208
11266
  init_managed_post_start();
11209
11267
  init_managed_runtime_state();
11210
11268
  init_managed_runtime_status();
@@ -11626,7 +11684,7 @@ async function runOpenCommand(name) {
11626
11684
  );
11627
11685
  return;
11628
11686
  }
11629
- const result = (0, import_node_child_process23.spawnSync)("open", [url], { encoding: "utf-8" });
11687
+ const result = (0, import_node_child_process24.spawnSync)("open", [url], { encoding: "utf-8" });
11630
11688
  if (result.status !== 0) {
11631
11689
  const details = [result.stdout, result.stderr].filter(Boolean).join("\n").trim();
11632
11690
  throw new Error(`Unable to open '${url}': ${details || "unknown error"}`);
@@ -11634,11 +11692,11 @@ async function runOpenCommand(name) {
11634
11692
  process.stdout.write(`Opened ${url}
11635
11693
  `);
11636
11694
  }
11637
- var import_node_child_process23;
11695
+ var import_node_child_process24;
11638
11696
  var init_open = __esm({
11639
11697
  "src/commands/open.ts"() {
11640
11698
  "use strict";
11641
- import_node_child_process23 = require("child_process");
11699
+ import_node_child_process24 = require("child_process");
11642
11700
  init_docker();
11643
11701
  init_host_routes();
11644
11702
  init_repo_config();
@@ -11661,7 +11719,7 @@ async function runLogsCommand(options) {
11661
11719
  const args = ["logs", "--tail", tail, ROUTER_CONTAINER_NAME];
11662
11720
  if (options.follow) {
11663
11721
  args.splice(1, 0, "-f");
11664
- const child = (0, import_node_child_process24.spawn)("docker", args, { stdio: "inherit" });
11722
+ const child = (0, import_node_child_process25.spawn)("docker", args, { stdio: "inherit" });
11665
11723
  const onSignal = () => {
11666
11724
  child.kill("SIGTERM");
11667
11725
  };
@@ -11675,17 +11733,17 @@ async function runLogsCommand(options) {
11675
11733
  });
11676
11734
  });
11677
11735
  } else {
11678
- const result = (0, import_node_child_process24.spawnSync)("docker", args, { stdio: "inherit" });
11736
+ const result = (0, import_node_child_process25.spawnSync)("docker", args, { stdio: "inherit" });
11679
11737
  if (result.status !== 0) {
11680
11738
  throw new Error("Failed to retrieve router logs.");
11681
11739
  }
11682
11740
  }
11683
11741
  }
11684
- var import_node_child_process24;
11742
+ var import_node_child_process25;
11685
11743
  var init_logs = __esm({
11686
11744
  "src/commands/logs.ts"() {
11687
11745
  "use strict";
11688
- import_node_child_process24 = require("child_process");
11746
+ import_node_child_process25 = require("child_process");
11689
11747
  init_docker();
11690
11748
  init_router();
11691
11749
  }
@@ -11725,11 +11783,11 @@ function asRecord3(value) {
11725
11783
  return value;
11726
11784
  }
11727
11785
  function readJson(filePath) {
11728
- if (!import_node_fs29.default.existsSync(filePath)) {
11786
+ if (!import_node_fs30.default.existsSync(filePath)) {
11729
11787
  return void 0;
11730
11788
  }
11731
11789
  try {
11732
- return JSON.parse(import_node_fs29.default.readFileSync(filePath, "utf-8"));
11790
+ return JSON.parse(import_node_fs30.default.readFileSync(filePath, "utf-8"));
11733
11791
  } catch {
11734
11792
  return void 0;
11735
11793
  }
@@ -11776,7 +11834,7 @@ function inspectPackageManager(repoPath, pkg) {
11776
11834
  ["bun.lock", "bun"]
11777
11835
  ];
11778
11836
  for (const [fileName, name] of lockfiles) {
11779
- if (import_node_fs29.default.existsSync(import_node_path31.default.join(repoPath, fileName))) {
11837
+ if (import_node_fs30.default.existsSync(import_node_path31.default.join(repoPath, fileName))) {
11780
11838
  return { name, source: fileName };
11781
11839
  }
11782
11840
  }
@@ -11792,8 +11850,8 @@ function inspectNode(repoPath, pkg) {
11792
11850
  return { version: engines.node, source: "package.json:engines.node" };
11793
11851
  }
11794
11852
  const nvmrc = import_node_path31.default.join(repoPath, ".nvmrc");
11795
- if (import_node_fs29.default.existsSync(nvmrc)) {
11796
- const version = import_node_fs29.default.readFileSync(nvmrc, "utf-8").trim();
11853
+ if (import_node_fs30.default.existsSync(nvmrc)) {
11854
+ const version = import_node_fs30.default.readFileSync(nvmrc, "utf-8").trim();
11797
11855
  return { version, source: ".nvmrc" };
11798
11856
  }
11799
11857
  return void 0;
@@ -11872,7 +11930,7 @@ function composeFiles(repoPath) {
11872
11930
  ...configuredComposeFiles(repoPath)
11873
11931
  ];
11874
11932
  return Array.from(new Set(candidates)).filter(
11875
- (fileName) => import_node_fs29.default.existsSync(import_node_path31.default.join(repoPath, fileName))
11933
+ (fileName) => import_node_fs30.default.existsSync(import_node_path31.default.join(repoPath, fileName))
11876
11934
  );
11877
11935
  }
11878
11936
  function stringArray2(value) {
@@ -11910,7 +11968,7 @@ function inspectServices(repoPath) {
11910
11968
  const services = [];
11911
11969
  for (const fileName of composeFiles(repoPath)) {
11912
11970
  try {
11913
- const parsed = import_yaml6.default.parse(import_node_fs29.default.readFileSync(import_node_path31.default.join(repoPath, fileName), "utf-8"));
11971
+ const parsed = import_yaml6.default.parse(import_node_fs30.default.readFileSync(import_node_path31.default.join(repoPath, fileName), "utf-8"));
11914
11972
  const serviceMap = asRecord3(asRecord3(parsed)?.services);
11915
11973
  for (const [name, value] of Object.entries(serviceMap ?? {})) {
11916
11974
  const service = asRecord3(value);
@@ -11945,8 +12003,8 @@ function inspectServices(repoPath) {
11945
12003
  return services;
11946
12004
  }
11947
12005
  function inspectEnvFiles(repoPath) {
11948
- const files = import_node_fs29.default.readdirSync(repoPath).filter((fileName) => /^\.env(\.|$)/.test(fileName)).sort().map((fileName) => {
11949
- const content = import_node_fs29.default.readFileSync(import_node_path31.default.join(repoPath, fileName), "utf-8");
12006
+ const files = import_node_fs30.default.readdirSync(repoPath).filter((fileName) => /^\.env(\.|$)/.test(fileName)).sort().map((fileName) => {
12007
+ const content = import_node_fs30.default.readFileSync(import_node_path31.default.join(repoPath, fileName), "utf-8");
11950
12008
  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();
11951
12009
  return { path: fileName, names };
11952
12010
  });
@@ -11961,17 +12019,17 @@ function inspectEnvFiles(repoPath) {
11961
12019
  }
11962
12020
  function inspectDevcontainer(repoPath) {
11963
12021
  const dir = import_node_path31.default.join(repoPath, ".devcontainer");
11964
- if (!import_node_fs29.default.existsSync(dir)) {
12022
+ if (!import_node_fs30.default.existsSync(dir)) {
11965
12023
  return { exists: false, files: [] };
11966
12024
  }
11967
12025
  return {
11968
12026
  exists: true,
11969
- files: import_node_fs29.default.readdirSync(dir).filter((fileName) => import_node_fs29.default.statSync(import_node_path31.default.join(dir, fileName)).isFile()).sort().map((fileName) => `.devcontainer/${fileName}`)
12027
+ files: import_node_fs30.default.readdirSync(dir).filter((fileName) => import_node_fs30.default.statSync(import_node_path31.default.join(dir, fileName)).isFile()).sort().map((fileName) => `.devcontainer/${fileName}`)
11970
12028
  };
11971
12029
  }
11972
12030
  function inspectDevrouter(repoPath) {
11973
12031
  const configPath = getRepoConfigPath(repoPath);
11974
- if (!import_node_fs29.default.existsSync(configPath)) {
12032
+ if (!import_node_fs30.default.existsSync(configPath)) {
11975
12033
  return {
11976
12034
  exists: false,
11977
12035
  configPath,
@@ -12015,15 +12073,15 @@ function inspectAgentGuidance(repoPath) {
12015
12073
  ["AGENTS.md", "agents"],
12016
12074
  ["CLAUDE.md", "claude"]
12017
12075
  ]) {
12018
- if (import_node_fs29.default.existsSync(import_node_path31.default.join(repoPath, fileName))) {
12076
+ if (import_node_fs30.default.existsSync(import_node_path31.default.join(repoPath, fileName))) {
12019
12077
  results.push({ path: fileName, kind });
12020
12078
  }
12021
12079
  }
12022
12080
  const skillsDir = import_node_path31.default.join(repoPath, ".agents", "skills");
12023
- if (import_node_fs29.default.existsSync(skillsDir)) {
12024
- for (const name of import_node_fs29.default.readdirSync(skillsDir).sort()) {
12081
+ if (import_node_fs30.default.existsSync(skillsDir)) {
12082
+ for (const name of import_node_fs30.default.readdirSync(skillsDir).sort()) {
12025
12083
  const skillPath = import_node_path31.default.join(skillsDir, name, "SKILL.md");
12026
- if (import_node_fs29.default.existsSync(skillPath)) {
12084
+ if (import_node_fs30.default.existsSync(skillPath)) {
12027
12085
  results.push({ path: relative(repoPath, skillPath), kind: "skill" });
12028
12086
  }
12029
12087
  }
@@ -12085,11 +12143,11 @@ function inspectRepo(options = {}) {
12085
12143
  issues: buildIssues(reportWithoutIssues)
12086
12144
  };
12087
12145
  }
12088
- var import_node_fs29, import_node_path31, import_yaml6;
12146
+ var import_node_fs30, import_node_path31, import_yaml6;
12089
12147
  var init_repo_inspect = __esm({
12090
12148
  "src/core/repo-inspect.ts"() {
12091
12149
  "use strict";
12092
- import_node_fs29 = __toESM(require("fs"));
12150
+ import_node_fs30 = __toESM(require("fs"));
12093
12151
  import_node_path31 = __toESM(require("path"));
12094
12152
  import_yaml6 = __toESM(require("yaml"));
12095
12153
  init_repo_config();
@@ -12195,7 +12253,7 @@ function requiredFileChecks(repoPath) {
12195
12253
  ".devcontainer/docker-compose.yml",
12196
12254
  ".devrouter.yml"
12197
12255
  ];
12198
- const missing = required.filter((fileName) => !import_node_fs30.default.existsSync(import_node_path32.default.join(repoPath, fileName)));
12256
+ const missing = required.filter((fileName) => !import_node_fs31.default.existsSync(import_node_path32.default.join(repoPath, fileName)));
12199
12257
  return {
12200
12258
  id: "repo.devcontainer.verify-files",
12201
12259
  level: missing.length === 0 ? "ok" : "error",
@@ -12420,11 +12478,11 @@ async function verifyDevcontainer(options = {}) {
12420
12478
  nextSteps: collectNextSteps3(checks)
12421
12479
  };
12422
12480
  }
12423
- var import_node_fs30, import_node_path32;
12481
+ var import_node_fs31, import_node_path32;
12424
12482
  var init_devcontainer_verify = __esm({
12425
12483
  "src/core/devcontainer-verify.ts"() {
12426
12484
  "use strict";
12427
- import_node_fs30 = __toESM(require("fs"));
12485
+ import_node_fs31 = __toESM(require("fs"));
12428
12486
  import_node_path32 = __toESM(require("path"));
12429
12487
  init_capabilities();
12430
12488
  init_doctor();
@@ -12768,7 +12826,7 @@ function plannedFiles(repoPath, version) {
12768
12826
  }
12769
12827
  function classifyFile(repoPath, file) {
12770
12828
  const absolutePath = import_node_path33.default.join(repoPath, file.relativePath);
12771
- if (!import_node_fs31.default.existsSync(absolutePath)) {
12829
+ if (!import_node_fs32.default.existsSync(absolutePath)) {
12772
12830
  return {
12773
12831
  path: file.relativePath,
12774
12832
  action: "create",
@@ -12776,7 +12834,7 @@ function classifyFile(repoPath, file) {
12776
12834
  bytes: Buffer.byteLength(file.content)
12777
12835
  };
12778
12836
  }
12779
- const current = import_node_fs31.default.readFileSync(absolutePath, "utf-8");
12837
+ const current = import_node_fs32.default.readFileSync(absolutePath, "utf-8");
12780
12838
  if (!current.includes(MANAGED_MARKER2)) {
12781
12839
  return {
12782
12840
  path: file.relativePath,
@@ -12834,10 +12892,10 @@ function buildPlan(repoPath, dryRun, version) {
12834
12892
  }
12835
12893
  function writeFile(repoPath, file) {
12836
12894
  const absolutePath = import_node_path33.default.join(repoPath, file.relativePath);
12837
- import_node_fs31.default.mkdirSync(import_node_path33.default.dirname(absolutePath), { recursive: true });
12838
- import_node_fs31.default.writeFileSync(absolutePath, file.content, "utf-8");
12895
+ import_node_fs32.default.mkdirSync(import_node_path33.default.dirname(absolutePath), { recursive: true });
12896
+ import_node_fs32.default.writeFileSync(absolutePath, file.content, "utf-8");
12839
12897
  if (file.executable) {
12840
- import_node_fs31.default.chmodSync(absolutePath, 493);
12898
+ import_node_fs32.default.chmodSync(absolutePath, 493);
12841
12899
  }
12842
12900
  }
12843
12901
  function writeDevcontainer(options = {}) {
@@ -12875,11 +12933,11 @@ function writeDevcontainer(options = {}) {
12875
12933
  nextSteps: postWriteNextSteps(repoPath)
12876
12934
  };
12877
12935
  }
12878
- var import_node_fs31, import_node_path33, MANAGED_MARKER2, DEFAULT_DEVROUTER_VERSION, DEFAULT_PNPM_VERSION, VALID_PACKAGE_VERSION_RE;
12936
+ var import_node_fs32, import_node_path33, MANAGED_MARKER2, DEFAULT_DEVROUTER_VERSION, DEFAULT_PNPM_VERSION, VALID_PACKAGE_VERSION_RE;
12879
12937
  var init_devcontainer_write = __esm({
12880
12938
  "src/core/devcontainer-write.ts"() {
12881
12939
  "use strict";
12882
- import_node_fs31 = __toESM(require("fs"));
12940
+ import_node_fs32 = __toESM(require("fs"));
12883
12941
  import_node_path33 = __toESM(require("path"));
12884
12942
  init_repo_config();
12885
12943
  init_repo_inspect();
@@ -13293,7 +13351,7 @@ function loadContract(repoPath, contractPath) {
13293
13351
  const absolutePath = assertPathWithinRepo(contractPath, repoPath, "Profile plan contract");
13294
13352
  let metadata;
13295
13353
  try {
13296
- metadata = import_node_fs32.default.lstatSync(absolutePath);
13354
+ metadata = import_node_fs33.default.lstatSync(absolutePath);
13297
13355
  } catch (error) {
13298
13356
  fail(`could not inspect profile plan contract '${contractPath}': ${error.message}`);
13299
13357
  }
@@ -13303,10 +13361,10 @@ function loadContract(repoPath, contractPath) {
13303
13361
  if (metadata.size > MAX_CONTRACT_BYTES) {
13304
13362
  fail(`profile plan contract exceeds the ${MAX_CONTRACT_BYTES}-byte limit.`);
13305
13363
  }
13306
- const realRepo = import_node_fs32.default.realpathSync(repoPath);
13307
- const realContract = import_node_fs32.default.realpathSync(absolutePath);
13364
+ const realRepo = import_node_fs33.default.realpathSync(repoPath);
13365
+ const realContract = import_node_fs33.default.realpathSync(absolutePath);
13308
13366
  assertPathWithinRepo(realContract, realRepo, "Profile plan contract");
13309
- const source = import_node_fs32.default.readFileSync(absolutePath, "utf-8");
13367
+ const source = import_node_fs33.default.readFileSync(absolutePath, "utf-8");
13310
13368
  if (Buffer.byteLength(source, "utf-8") > MAX_CONTRACT_BYTES) {
13311
13369
  fail(`profile plan contract exceeds the ${MAX_CONTRACT_BYTES}-byte limit.`);
13312
13370
  }
@@ -13326,11 +13384,11 @@ function resolveProfilePlan(options) {
13326
13384
  contractPath: loaded.contractPath
13327
13385
  });
13328
13386
  }
13329
- var import_node_fs32, import_node_path34, import_yaml7, CONTRACT_VERSION, MAX_CONTRACT_BYTES, MAX_APP_MAPPINGS, MAX_BINDING_KEYS, MAX_BINDING_VALUES, MAX_LITERAL_LENGTH, BINDING_KEY;
13387
+ var import_node_fs33, import_node_path34, import_yaml7, CONTRACT_VERSION, MAX_CONTRACT_BYTES, MAX_APP_MAPPINGS, MAX_BINDING_KEYS, MAX_BINDING_VALUES, MAX_LITERAL_LENGTH, BINDING_KEY;
13330
13388
  var init_profile_plan = __esm({
13331
13389
  "src/core/profile-plan.ts"() {
13332
13390
  "use strict";
13333
- import_node_fs32 = __toESM(require("fs"));
13391
+ import_node_fs33 = __toESM(require("fs"));
13334
13392
  import_node_path34 = __toESM(require("path"));
13335
13393
  import_yaml7 = require("yaml");
13336
13394
  init_paths();
@@ -13776,10 +13834,10 @@ function prepareDockerOverlay(repoPath, appName, apps, publishTcpPorts = false)
13776
13834
  throw new Error("No docker apps selected to prepare compose overlay.");
13777
13835
  }
13778
13836
  const cachePath = import_node_path36.default.join(CACHE_DIR, repoHash(repoPath), sanitizeRouterId(appName));
13779
- import_node_fs33.default.mkdirSync(cachePath, { recursive: true });
13837
+ import_node_fs34.default.mkdirSync(cachePath, { recursive: true });
13780
13838
  const overlayPath = import_node_path36.default.join(cachePath, "compose.devrouter.yml");
13781
13839
  const overlayDocument = buildOverlayDocument(dockerApps, publishTcpPorts);
13782
- import_node_fs33.default.writeFileSync(overlayPath, import_yaml8.default.stringify(overlayDocument, { lineWidth: 0 }), "utf-8");
13840
+ import_node_fs34.default.writeFileSync(overlayPath, import_yaml8.default.stringify(overlayDocument, { lineWidth: 0 }), "utf-8");
13783
13841
  return {
13784
13842
  overlayPath,
13785
13843
  composeFiles: ensureComposeFiles(dockerApps),
@@ -13793,7 +13851,7 @@ function runDockerComposeUp(repoPath, composeFiles2, overlayPath, services) {
13793
13851
  fileArgs.push("-f", resolved);
13794
13852
  }
13795
13853
  const args = ["compose", ...fileArgs, "-f", overlayPath, "up", "-d", "--wait", ...services];
13796
- const result = (0, import_node_child_process25.spawnSync)("docker", args, {
13854
+ const result = (0, import_node_child_process26.spawnSync)("docker", args, {
13797
13855
  encoding: "utf-8",
13798
13856
  cwd: repoPath
13799
13857
  });
@@ -13821,7 +13879,7 @@ function queryRunningComposeServices(repoPath, composeFiles2, overlayPath, servi
13821
13879
  "--services",
13822
13880
  ...services
13823
13881
  ];
13824
- const result = (0, import_node_child_process25.spawnSync)("docker", args, {
13882
+ const result = (0, import_node_child_process26.spawnSync)("docker", args, {
13825
13883
  encoding: "utf-8",
13826
13884
  cwd: repoPath
13827
13885
  });
@@ -13847,7 +13905,7 @@ function runDockerComposeStop(repoPath, composeFiles2, overlayPath, services) {
13847
13905
  fileArgs.push("-f", resolved);
13848
13906
  }
13849
13907
  const args = ["compose", ...fileArgs, "-f", overlayPath, "stop", ...services];
13850
- const result = (0, import_node_child_process25.spawnSync)("docker", args, {
13908
+ const result = (0, import_node_child_process26.spawnSync)("docker", args, {
13851
13909
  encoding: "utf-8",
13852
13910
  cwd: repoPath
13853
13911
  });
@@ -13873,7 +13931,7 @@ function runDockerComposeLogs(repoPath, composeFiles2, overlayPath, services, ta
13873
13931
  String(tail),
13874
13932
  ...services
13875
13933
  ];
13876
- (0, import_node_child_process25.spawnSync)("docker", args, {
13934
+ (0, import_node_child_process26.spawnSync)("docker", args, {
13877
13935
  stdio: "inherit",
13878
13936
  cwd: repoPath
13879
13937
  });
@@ -13885,7 +13943,7 @@ function queryMappedPort(repoPath, composeFiles2, overlayPath, service, internal
13885
13943
  fileArgs.push("-f", resolved);
13886
13944
  }
13887
13945
  const args = ["compose", ...fileArgs, "-f", overlayPath, "port", service, String(internalPort)];
13888
- const result = (0, import_node_child_process25.spawnSync)("docker", args, {
13946
+ const result = (0, import_node_child_process26.spawnSync)("docker", args, {
13889
13947
  encoding: "utf-8",
13890
13948
  cwd: repoPath
13891
13949
  });
@@ -13899,13 +13957,13 @@ function queryMappedPort(repoPath, composeFiles2, overlayPath, service, internal
13899
13957
  const port = Number(match[1]);
13900
13958
  return Number.isInteger(port) && port > 0 ? port : void 0;
13901
13959
  }
13902
- var import_node_child_process25, import_node_crypto11, import_node_fs33, import_node_path36, import_yaml8;
13960
+ var import_node_child_process26, import_node_crypto11, import_node_fs34, import_node_path36, import_yaml8;
13903
13961
  var init_docker_run = __esm({
13904
13962
  "src/core/docker-run.ts"() {
13905
13963
  "use strict";
13906
- import_node_child_process25 = require("child_process");
13964
+ import_node_child_process26 = require("child_process");
13907
13965
  import_node_crypto11 = require("crypto");
13908
- import_node_fs33 = __toESM(require("fs"));
13966
+ import_node_fs34 = __toESM(require("fs"));
13909
13967
  import_node_path36 = __toESM(require("path"));
13910
13968
  import_yaml8 = __toESM(require("yaml"));
13911
13969
  init_docker_error_guidance();
@@ -13996,7 +14054,7 @@ function isProcessRunning(pid) {
13996
14054
  }
13997
14055
  }
13998
14056
  function readProcessTree(rootPid) {
13999
- const result = (0, import_node_child_process26.spawnSync)("ps", ["-ax", "-o", "pid=,ppid="], { encoding: "utf-8" });
14057
+ const result = (0, import_node_child_process27.spawnSync)("ps", ["-ax", "-o", "pid=,ppid="], { encoding: "utf-8" });
14000
14058
  if (result.status !== 0) {
14001
14059
  return [rootPid];
14002
14060
  }
@@ -14107,12 +14165,12 @@ function detectListeningPorts(pids) {
14107
14165
  if (pids.length === 0) {
14108
14166
  return [];
14109
14167
  }
14110
- const result = (0, import_node_child_process26.spawnSync)("lsof", ["-nP", "-iTCP", "-sTCP:LISTEN", "-a", "-p", pids.join(",")], {
14168
+ const result = (0, import_node_child_process27.spawnSync)("lsof", ["-nP", "-iTCP", "-sTCP:LISTEN", "-a", "-p", pids.join(",")], {
14111
14169
  encoding: "utf-8"
14112
14170
  });
14113
14171
  if (result.error && result.error.code === "ENOENT") {
14114
14172
  if (process.platform === "linux") {
14115
- const ssResult = (0, import_node_child_process26.spawnSync)("ss", ["-H", "-lntp", "-p"], { encoding: "utf-8" });
14173
+ const ssResult = (0, import_node_child_process27.spawnSync)("ss", ["-H", "-lntp", "-p"], { encoding: "utf-8" });
14116
14174
  if (ssResult.status === 0 && ssResult.stdout) {
14117
14175
  return parseSsListeningPorts(ssResult.stdout, new Set(pids));
14118
14176
  }
@@ -14171,7 +14229,7 @@ async function runHostApp(repoPath, app, extraEnv = {}, secretManager, env, work
14171
14229
  app.hostRun.command,
14172
14230
  true
14173
14231
  ) : app.hostRun.command;
14174
- const child = (0, import_node_child_process26.spawn)(spawnCommand, {
14232
+ const child = (0, import_node_child_process27.spawn)(spawnCommand, {
14175
14233
  cwd: commandCwd,
14176
14234
  stdio: "inherit",
14177
14235
  shell: true,
@@ -14539,7 +14597,7 @@ async function execWithAppEnv(options) {
14539
14597
  options.command[0],
14540
14598
  true
14541
14599
  );
14542
- child = (0, import_node_child_process26.spawn)(wrapped, {
14600
+ child = (0, import_node_child_process27.spawn)(wrapped, {
14543
14601
  cwd: deps.repoPath,
14544
14602
  stdio: "inherit",
14545
14603
  shell: true,
@@ -14553,7 +14611,7 @@ async function execWithAppEnv(options) {
14553
14611
  false
14554
14612
  );
14555
14613
  const [cmd, ...wrappedArgs] = wrapped;
14556
- child = (0, import_node_child_process26.spawn)(cmd, wrappedArgs, {
14614
+ child = (0, import_node_child_process27.spawn)(cmd, wrappedArgs, {
14557
14615
  cwd: deps.repoPath,
14558
14616
  stdio: "inherit",
14559
14617
  shell: false,
@@ -14561,7 +14619,7 @@ async function execWithAppEnv(options) {
14561
14619
  });
14562
14620
  }
14563
14621
  } else if (options.shell) {
14564
- child = (0, import_node_child_process26.spawn)(options.command[0], {
14622
+ child = (0, import_node_child_process27.spawn)(options.command[0], {
14565
14623
  cwd: deps.repoPath,
14566
14624
  stdio: "inherit",
14567
14625
  shell: true,
@@ -14569,7 +14627,7 @@ async function execWithAppEnv(options) {
14569
14627
  });
14570
14628
  } else {
14571
14629
  const [command, ...args] = options.command;
14572
- child = (0, import_node_child_process26.spawn)(command, args, {
14630
+ child = (0, import_node_child_process27.spawn)(command, args, {
14573
14631
  cwd: deps.repoPath,
14574
14632
  stdio: "inherit",
14575
14633
  shell: false,
@@ -14590,11 +14648,11 @@ async function execWithAppEnv(options) {
14590
14648
  deps.stopDeps();
14591
14649
  }
14592
14650
  }
14593
- var import_node_child_process26, import_node_net, import_node_process, import_promises, POLL_INTERVAL_MS, DEFAULT_PORT_TIMEOUT_MS, PROCESS_TERMINATION_GRACE_MS;
14651
+ var import_node_child_process27, import_node_net, import_node_process, import_promises, POLL_INTERVAL_MS, DEFAULT_PORT_TIMEOUT_MS, PROCESS_TERMINATION_GRACE_MS;
14594
14652
  var init_app_run = __esm({
14595
14653
  "src/core/app-run.ts"() {
14596
14654
  "use strict";
14597
- import_node_child_process26 = require("child_process");
14655
+ import_node_child_process27 = require("child_process");
14598
14656
  import_node_net = __toESM(require("net"));
14599
14657
  import_node_process = require("process");
14600
14658
  import_promises = require("readline/promises");
@@ -14728,13 +14786,13 @@ function measureWorktreeConsumption(worktreePath, options) {
14728
14786
  const startedAt = Date.now();
14729
14787
  let rootStat;
14730
14788
  try {
14731
- rootStat = import_node_fs34.default.lstatSync(worktreePath);
14789
+ rootStat = import_node_fs35.default.lstatSync(worktreePath);
14732
14790
  } catch (error) {
14733
14791
  return { status: "unknown", reason: describeError(error, worktreePath) };
14734
14792
  }
14735
14793
  let rootEntries;
14736
14794
  try {
14737
- rootEntries = import_node_fs34.default.readdirSync(worktreePath, { withFileTypes: true });
14795
+ rootEntries = import_node_fs35.default.readdirSync(worktreePath, { withFileTypes: true });
14738
14796
  } catch (error) {
14739
14797
  return { status: "unknown", reason: describeError(error, worktreePath) };
14740
14798
  }
@@ -14763,7 +14821,7 @@ function measureWorktreeConsumption(worktreePath, options) {
14763
14821
  const entryPath = import_node_path37.default.join(dirPath, entry.name);
14764
14822
  let entryStat;
14765
14823
  try {
14766
- entryStat = import_node_fs34.default.lstatSync(entryPath);
14824
+ entryStat = import_node_fs35.default.lstatSync(entryPath);
14767
14825
  } catch (error) {
14768
14826
  if (error?.code === "ENOENT") continue;
14769
14827
  unreadableReason = describeIncompleteWalk(error);
@@ -14773,7 +14831,7 @@ function measureWorktreeConsumption(worktreePath, options) {
14773
14831
  if (!entryStat.isDirectory()) continue;
14774
14832
  let childEntries;
14775
14833
  try {
14776
- childEntries = import_node_fs34.default.readdirSync(entryPath, { withFileTypes: true });
14834
+ childEntries = import_node_fs35.default.readdirSync(entryPath, { withFileTypes: true });
14777
14835
  } catch (error) {
14778
14836
  unreadableReason = describeIncompleteWalk(error);
14779
14837
  break;
@@ -14837,11 +14895,11 @@ function describeError(error, worktreePath) {
14837
14895
  }
14838
14896
  return `could not stat worktree path '${worktreePath}': ${error?.message ?? String(error)}`;
14839
14897
  }
14840
- var import_node_fs34, import_node_path37, DEFAULT_DEADLINE_MS, BLOCK_SIZE_BYTES;
14898
+ var import_node_fs35, import_node_path37, DEFAULT_DEADLINE_MS, BLOCK_SIZE_BYTES;
14841
14899
  var init_workspace_consumption = __esm({
14842
14900
  "src/core/workspace-consumption.ts"() {
14843
14901
  "use strict";
14844
- import_node_fs34 = __toESM(require("fs"));
14902
+ import_node_fs35 = __toESM(require("fs"));
14845
14903
  import_node_path37 = __toESM(require("path"));
14846
14904
  init_devpod_environment();
14847
14905
  DEFAULT_DEADLINE_MS = 1e4;
@@ -14943,7 +15001,7 @@ function evaluateWorkspaceActivity(evidence, cutoff) {
14943
15001
  }
14944
15002
  function readGitSnapshot(worktree, commandRunner) {
14945
15003
  const comparablePath = comparableWorkspacePath(worktree.path);
14946
- if (worktree.prunable || !import_node_fs35.default.existsSync(comparablePath)) {
15004
+ if (worktree.prunable || !import_node_fs36.default.existsSync(comparablePath)) {
14947
15005
  return { worktree, checkout: "missing", head: null, committerDate: null };
14948
15006
  }
14949
15007
  const head = gitOutput(comparablePath, ["rev-parse", "--verify", "HEAD"], commandRunner);
@@ -15517,12 +15575,12 @@ function buildWorkspaceCleanupReport(options = {}, dependencies = {}) {
15517
15575
  workspaces: rows
15518
15576
  };
15519
15577
  }
15520
- var import_node_child_process27, import_node_fs35, DEFAULT_INACTIVE_FOR, READ_ONLY_GIT_ENV2, ACTIVITY_SOURCES, defaultCommandRunner;
15578
+ var import_node_child_process28, import_node_fs36, DEFAULT_INACTIVE_FOR, READ_ONLY_GIT_ENV2, ACTIVITY_SOURCES, defaultCommandRunner;
15521
15579
  var init_workspace_cleanup = __esm({
15522
15580
  "src/core/workspace-cleanup.ts"() {
15523
15581
  "use strict";
15524
- import_node_child_process27 = require("child_process");
15525
- import_node_fs35 = __toESM(require("fs"));
15582
+ import_node_child_process28 = require("child_process");
15583
+ import_node_fs36 = __toESM(require("fs"));
15526
15584
  init_devpod_workspaces();
15527
15585
  init_host_routes();
15528
15586
  init_repo_config();
@@ -15539,7 +15597,7 @@ var init_workspace_cleanup = __esm({
15539
15597
  ];
15540
15598
  defaultCommandRunner = (command, args, input2) => {
15541
15599
  const environment = command === "git" ? READ_ONLY_GIT_ENV2 : { ...process.env, LC_ALL: "C" };
15542
- const result = (0, import_node_child_process27.spawnSync)(command, args, {
15600
+ const result = (0, import_node_child_process28.spawnSync)(command, args, {
15543
15601
  encoding: "utf-8",
15544
15602
  env: environment,
15545
15603
  ...input2 === void 0 ? {} : { input: input2 }
@@ -15572,7 +15630,7 @@ function defaultWorktreePath(mainRepo, ws) {
15572
15630
  return import_node_path38.default.join(mainRepo, "trees", ws);
15573
15631
  }
15574
15632
  function assertDefaultWorktreeRootIgnored(mainRepo) {
15575
- const ignored = (0, import_node_child_process28.spawnSync)("git", ["-C", mainRepo, "check-ignore", "-q", "--no-index", "trees/"], {
15633
+ const ignored = (0, import_node_child_process29.spawnSync)("git", ["-C", mainRepo, "check-ignore", "-q", "--no-index", "trees/"], {
15576
15634
  encoding: "utf-8"
15577
15635
  });
15578
15636
  if (ignored.status !== 0) {
@@ -15694,13 +15752,13 @@ function assertFullDownPreflight(mainRepo, target) {
15694
15752
  if (sameWorkspacePath(target.worktreePath, mainRepo)) {
15695
15753
  throw new Error("Refusing to remove the primary Git checkout.");
15696
15754
  }
15697
- if (!target.worktree || target.worktree.prunable || !import_node_fs36.default.existsSync(target.worktreePath)) return;
15755
+ if (!target.worktree || target.worktree.prunable || !import_node_fs37.default.existsSync(target.worktreePath)) return;
15698
15756
  if (target.worktree.locked) {
15699
15757
  throw new Error(
15700
15758
  `Worktree '${target.worktreePath}' is locked; unlock it before workspace down.`
15701
15759
  );
15702
15760
  }
15703
- const status = (0, import_node_child_process28.spawnSync)(
15761
+ const status = (0, import_node_child_process29.spawnSync)(
15704
15762
  "git",
15705
15763
  ["-C", target.worktreePath, "status", "--porcelain", "--untracked-files=normal"],
15706
15764
  { encoding: "utf-8" }
@@ -15734,7 +15792,7 @@ async function workspaceUp(branch, opts = {}) {
15734
15792
  (candidate) => defaultWorktreePath(mainRepo, candidate)
15735
15793
  );
15736
15794
  const generatedPath = candidatePaths.find(
15737
- (candidate) => !import_node_fs36.default.existsSync(candidate) && !worktrees.some((worktree) => sameWorkspacePath(worktree.path, candidate))
15795
+ (candidate) => !import_node_fs37.default.existsSync(candidate) && !worktrees.some((worktree) => sameWorkspacePath(worktree.path, candidate))
15738
15796
  );
15739
15797
  const selectedPath = opts.path ? import_node_path38.default.resolve(opts.path) : existingBranch?.path ?? generatedPath;
15740
15798
  if (!selectedPath) {
@@ -15745,7 +15803,7 @@ async function workspaceUp(branch, opts = {}) {
15745
15803
  `Branch '${branch}' already uses worktree '${existingBranch.path}', not '${selectedPath}'.`
15746
15804
  );
15747
15805
  }
15748
- if (import_node_fs36.default.existsSync(selectedPath)) {
15806
+ if (import_node_fs37.default.existsSync(selectedPath)) {
15749
15807
  const registered = worktrees.find(
15750
15808
  (worktree) => sameWorkspacePath(worktree.path, selectedPath)
15751
15809
  );
@@ -15764,11 +15822,11 @@ async function workspaceUp(branch, opts = {}) {
15764
15822
  return selectedPath;
15765
15823
  }
15766
15824
  if (!opts.path) assertDefaultWorktreeRootIgnored(mainRepo);
15767
- const add = (0, import_node_child_process28.spawnSync)("git", ["-C", mainRepo, "worktree", "add", selectedPath, branch], {
15825
+ const add = (0, import_node_child_process29.spawnSync)("git", ["-C", mainRepo, "worktree", "add", selectedPath, branch], {
15768
15826
  encoding: "utf-8"
15769
15827
  });
15770
15828
  if (add.status !== 0) {
15771
- const addNew = (0, import_node_child_process28.spawnSync)(
15829
+ const addNew = (0, import_node_child_process29.spawnSync)(
15772
15830
  "git",
15773
15831
  ["-C", mainRepo, "worktree", "add", "-b", branch, selectedPath],
15774
15832
  { encoding: "utf-8" }
@@ -15885,8 +15943,8 @@ async function runWorkspaceLifecycle(action2, target, opts = {}) {
15885
15943
  }
15886
15944
  const result = await mutateWorkspaceRuntime("delete", resolved, worktrees, opts.quiet);
15887
15945
  if (removeWorktree) {
15888
- if (resolved.worktree && !resolved.worktree.prunable && import_node_fs36.default.existsSync(resolved.worktreePath)) {
15889
- const rm = (0, import_node_child_process28.spawnSync)("git", ["-C", mainRepo, "worktree", "remove", resolved.worktreePath], {
15946
+ if (resolved.worktree && !resolved.worktree.prunable && import_node_fs37.default.existsSync(resolved.worktreePath)) {
15947
+ const rm = (0, import_node_child_process29.spawnSync)("git", ["-C", mainRepo, "worktree", "remove", resolved.worktreePath], {
15890
15948
  encoding: "utf-8"
15891
15949
  });
15892
15950
  if (rm.status !== 0) {
@@ -15904,7 +15962,7 @@ async function runWorkspaceLifecycle(action2, target, opts = {}) {
15904
15962
  }
15905
15963
  return result;
15906
15964
  };
15907
- return resolved.worktree && !resolved.worktree.prunable && import_node_fs36.default.existsSync(resolved.worktreePath) ? withLifecycleOperationLock(resolved.worktreePath, operation) : operation();
15965
+ return resolved.worktree && !resolved.worktree.prunable && import_node_fs37.default.existsSync(resolved.worktreePath) ? withLifecycleOperationLock(resolved.worktreePath, operation) : operation();
15908
15966
  }
15909
15967
  async function workspaceStop(target, opts = {}) {
15910
15968
  return runWorkspaceLifecycle("stop", target, opts);
@@ -15912,12 +15970,12 @@ async function workspaceStop(target, opts = {}) {
15912
15970
  async function workspaceDown(target, opts = {}) {
15913
15971
  return runWorkspaceLifecycle("down", target, opts);
15914
15972
  }
15915
- var import_node_child_process28, import_node_fs36, import_node_path38, WORKSPACE_ALLOCATION_LOCK_WAIT_MS;
15973
+ var import_node_child_process29, import_node_fs37, import_node_path38, WORKSPACE_ALLOCATION_LOCK_WAIT_MS;
15916
15974
  var init_workspace_lifecycle = __esm({
15917
15975
  "src/core/workspace-lifecycle.ts"() {
15918
15976
  "use strict";
15919
- import_node_child_process28 = require("child_process");
15920
- import_node_fs36 = __toESM(require("fs"));
15977
+ import_node_child_process29 = require("child_process");
15978
+ import_node_fs37 = __toESM(require("fs"));
15921
15979
  import_node_path38 = __toESM(require("path"));
15922
15980
  init_devpod_mutation();
15923
15981
  init_devpod_workspaces();
@@ -16077,7 +16135,7 @@ var init_version = __esm({
16077
16135
 
16078
16136
  // src/cli.ts
16079
16137
  var import_commander = require("commander");
16080
- var CLI_VERSION = true ? "0.0.56" : "0.0.0-dev";
16138
+ var CLI_VERSION = true ? "0.0.58" : "0.0.0-dev";
16081
16139
  var VERSION_FLAGS = /* @__PURE__ */ new Set(["-V", "--version"]);
16082
16140
  function withErrorHandling(action2) {
16083
16141
  return async (...args) => {