@devrouter/cli 0.5.2 → 0.5.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.
@@ -936,6 +936,20 @@ function hasExactComposeIdentity(container, options) {
936
936
  const actualFiles = (container.labels["com.docker.compose.project.config_files"] ?? "").split(",").map((file) => file.trim()).filter(Boolean);
937
937
  const providerGeneratedFiles = actualFiles.filter((file) => {
938
938
  const normalized = file.replaceAll("\\", "/");
939
+ if (process.env.DEVPOD_HOME) {
940
+ const relative = import_node_path4.default.relative(import_node_path4.default.resolve(process.env.DEVPOD_HOME), file).replaceAll("\\", "/");
941
+ if (/^agent\/contexts\/[^/]+\/workspaces\/[^/]+\/\.docker-compose\/docker-compose\.devcontainer\.containerFeatures-[^/]+\.yml$/.test(
942
+ relative
943
+ ))
944
+ return true;
945
+ }
946
+ if (process.env.DEVSY_HOME) {
947
+ const relative = import_node_path4.default.relative(import_node_path4.default.resolve(process.env.DEVSY_HOME), file).replaceAll("\\", "/");
948
+ if (/^contexts\/[^/]+\/workspaces\/[^/]+\/agent\/\.docker-compose\/docker-compose\.devcontainer\.containerFeatures-[^/]+\.yml$/.test(
949
+ relative
950
+ ))
951
+ return true;
952
+ }
939
953
  return /\/(?:\.devpod\/agent\/contexts\/[^/]+\/workspaces\/[^/]+|\.devsy\/contexts\/[^/]+\/workspaces\/[^/]+\/agent)\/\.docker-compose\/docker-compose\.devcontainer\.containerFeatures-[^/]+\.yml$/.test(
940
954
  normalized
941
955
  );
@@ -2192,7 +2206,7 @@ function installedVersionOutput() {
2192
2206
  return [result.stdout, result.stderr].filter(Boolean).join("\n").trim() || void 0;
2193
2207
  }
2194
2208
  function nativeAssetName(platform, arch) {
2195
- if (platform !== "darwin") return void 0;
2209
+ if (platform !== "darwin" && platform !== "linux") return void 0;
2196
2210
  if (arch === "arm64") return "devsy-linux-arm64";
2197
2211
  if (arch === "x64") return "devsy-linux-amd64";
2198
2212
  return void 0;
@@ -5383,7 +5397,7 @@ function retain(session, previous) {
5383
5397
 
5384
5398
  // src/core/reliability-operation-store.ts
5385
5399
  function installedCliVersion() {
5386
- return true ? "0.5.2" : "0.0.0-dev";
5400
+ return true ? "0.5.3" : "0.0.0-dev";
5387
5401
  }
5388
5402
  function compareCliVersions(left, right) {
5389
5403
  const parts = (value) => value.split(".").map((part) => Number.parseInt(part, 10) || 0);
@@ -7393,7 +7407,7 @@ function loadRepoConfig(repoPath, read2 = (file) => import_node_fs19.default.rea
7393
7407
  const config = parseConfig(parsed ?? {}, configPath);
7394
7408
  const requiredVersion = config.devrouter?.version;
7395
7409
  if (requiredVersion && !hasWarnedVersionMismatch) {
7396
- const cliVersion = true ? "0.5.2" : "0.0.0-dev";
7410
+ const cliVersion = true ? "0.5.3" : "0.0.0-dev";
7397
7411
  if (cliVersion !== "0.0.0-dev" && compareSemver2(requiredVersion, cliVersion) > 0) {
7398
7412
  hasWarnedVersionMismatch = true;
7399
7413
  process.stderr.write(
@@ -11325,10 +11339,33 @@ var DEFAULT_OUTPUT_PAGE_BYTES = 48 * 1024;
11325
11339
  function workerGroupAbsent(pid) {
11326
11340
  try {
11327
11341
  process.kill(-pid, 0);
11328
- return false;
11329
11342
  } catch (error) {
11330
11343
  return error.code === "ESRCH";
11331
11344
  }
11345
+ if (process.platform !== "linux") return false;
11346
+ let observedZombie = false;
11347
+ try {
11348
+ for (const entry of import_node_fs29.default.readdirSync("/proc")) {
11349
+ if (!/^\d+$/.test(entry)) continue;
11350
+ let stat;
11351
+ try {
11352
+ stat = import_node_fs29.default.readFileSync(`/proc/${entry}/stat`, "utf8");
11353
+ } catch (error) {
11354
+ if (error.code === "ENOENT") continue;
11355
+ return false;
11356
+ }
11357
+ const end = stat.lastIndexOf(")");
11358
+ if (end < 0) return false;
11359
+ const fields2 = stat.slice(end + 1).trim().split(/\s+/);
11360
+ if (!/^\d+$/.test(fields2[2] ?? "") || !/^[A-Za-z]$/.test(fields2[0] ?? "")) return false;
11361
+ if (Number(fields2[2]) !== pid) continue;
11362
+ if (fields2[0] !== "Z") return false;
11363
+ observedZombie = true;
11364
+ }
11365
+ } catch {
11366
+ return false;
11367
+ }
11368
+ return observedZombie;
11332
11369
  }
11333
11370
 
11334
11371
  // src/core/workspace-ensure.ts
package/dist/devrouter.js CHANGED
@@ -1067,6 +1067,20 @@ function hasExactComposeIdentity(container, options) {
1067
1067
  const actualFiles = (container.labels["com.docker.compose.project.config_files"] ?? "").split(",").map((file) => file.trim()).filter(Boolean);
1068
1068
  const providerGeneratedFiles = actualFiles.filter((file) => {
1069
1069
  const normalized = file.replaceAll("\\", "/");
1070
+ if (process.env.DEVPOD_HOME) {
1071
+ const relative2 = import_node_path4.default.relative(import_node_path4.default.resolve(process.env.DEVPOD_HOME), file).replaceAll("\\", "/");
1072
+ if (/^agent\/contexts\/[^/]+\/workspaces\/[^/]+\/\.docker-compose\/docker-compose\.devcontainer\.containerFeatures-[^/]+\.yml$/.test(
1073
+ relative2
1074
+ ))
1075
+ return true;
1076
+ }
1077
+ if (process.env.DEVSY_HOME) {
1078
+ const relative2 = import_node_path4.default.relative(import_node_path4.default.resolve(process.env.DEVSY_HOME), file).replaceAll("\\", "/");
1079
+ if (/^contexts\/[^/]+\/workspaces\/[^/]+\/agent\/\.docker-compose\/docker-compose\.devcontainer\.containerFeatures-[^/]+\.yml$/.test(
1080
+ relative2
1081
+ ))
1082
+ return true;
1083
+ }
1070
1084
  return /\/(?:\.devpod\/agent\/contexts\/[^/]+\/workspaces\/[^/]+|\.devsy\/contexts\/[^/]+\/workspaces\/[^/]+\/agent)\/\.docker-compose\/docker-compose\.devcontainer\.containerFeatures-[^/]+\.yml$/.test(
1071
1085
  normalized
1072
1086
  );
@@ -4774,7 +4788,7 @@ function loadRepoConfig(repoPath, read2 = (file) => import_node_fs8.default.read
4774
4788
  const config = parseConfig(parsed ?? {}, configPath);
4775
4789
  const requiredVersion = config.devrouter?.version;
4776
4790
  if (requiredVersion && !hasWarnedVersionMismatch) {
4777
- const cliVersion = true ? "0.5.2" : "0.0.0-dev";
4791
+ const cliVersion = true ? "0.5.3" : "0.0.0-dev";
4778
4792
  if (cliVersion !== "0.0.0-dev" && compareSemver(requiredVersion, cliVersion) > 0) {
4779
4793
  hasWarnedVersionMismatch = true;
4780
4794
  process.stderr.write(
@@ -6773,7 +6787,7 @@ var init_reliability_contract = __esm({
6773
6787
 
6774
6788
  // src/core/reliability-operation-store.ts
6775
6789
  function installedCliVersion() {
6776
- return true ? "0.5.2" : "0.0.0-dev";
6790
+ return true ? "0.5.3" : "0.0.0-dev";
6777
6791
  }
6778
6792
  function compareCliVersions(left, right) {
6779
6793
  const parts = (value) => value.split(".").map((part) => Number.parseInt(part, 10) || 0);
@@ -10596,7 +10610,7 @@ function installedVersionOutput() {
10596
10610
  return [result.stdout, result.stderr].filter(Boolean).join("\n").trim() || void 0;
10597
10611
  }
10598
10612
  function nativeAssetName(platform, arch) {
10599
- if (platform !== "darwin") return void 0;
10613
+ if (platform !== "darwin" && platform !== "linux") return void 0;
10600
10614
  if (arch === "arm64") return "devsy-linux-arm64";
10601
10615
  if (arch === "x64") return "devsy-linux-amd64";
10602
10616
  return void 0;
@@ -15586,10 +15600,33 @@ function hasDuplicateOperation(record5, requestId, operationId) {
15586
15600
  function workerGroupAbsent(pid) {
15587
15601
  try {
15588
15602
  process.kill(-pid, 0);
15589
- return false;
15590
15603
  } catch (error) {
15591
15604
  return error.code === "ESRCH";
15592
15605
  }
15606
+ if (process.platform !== "linux") return false;
15607
+ let observedZombie = false;
15608
+ try {
15609
+ for (const entry of import_node_fs31.default.readdirSync("/proc")) {
15610
+ if (!/^\d+$/.test(entry)) continue;
15611
+ let stat;
15612
+ try {
15613
+ stat = import_node_fs31.default.readFileSync(`/proc/${entry}/stat`, "utf8");
15614
+ } catch (error) {
15615
+ if (error.code === "ENOENT") continue;
15616
+ return false;
15617
+ }
15618
+ const end = stat.lastIndexOf(")");
15619
+ if (end < 0) return false;
15620
+ const fields2 = stat.slice(end + 1).trim().split(/\s+/);
15621
+ if (!/^\d+$/.test(fields2[2] ?? "") || !/^[A-Za-z]$/.test(fields2[0] ?? "")) return false;
15622
+ if (Number(fields2[2]) !== pid) continue;
15623
+ if (fields2[0] !== "Z") return false;
15624
+ observedZombie = true;
15625
+ }
15626
+ } catch {
15627
+ return false;
15628
+ }
15629
+ return observedZombie;
15593
15630
  }
15594
15631
  function signalOwnedGroup(child, signal) {
15595
15632
  if (!child.pid || child.exitCode !== null || child.signalCode !== null) return;
@@ -22356,7 +22393,7 @@ function buildOnboardingPrompt(options = {}) {
22356
22393
  "- Ensure reconciles interrupted or never-dispatched ensure after positive worker drainage. Unknown arbitrary exec never replays; use explicit stop to reconcile that uncertainty. Preserve journals and generated configuration if evidence is unavailable. This does not detect or prevent OOM; bounded automatic recovery runs only when the operator enables the capacity recovery policy.",
22357
22394
  "- Capacity admission is opt-in: only an operator-enabled capacity policy with an enrolled checkout routes ensure and exec through controller admission. The CLI follows the decision with bounded reconnecting waits that survive controller restarts, worker results are journalled atomically, and stop bypasses admission. Without an enabled policy lifecycle behavior is unchanged. An optional recovery block adds bounded automatic recovery: one journal-admitted corrective ensure per positively failed required capability, bounded by per-scope restart counts, an aggregate corrective-action cap and a rolling observation window. It is inert unless enabled.",
22358
22395
  "- A lock that cannot prove its owner fails closed with the failing inspection stage, the exact lock path, and a portable reproduction command; run the canonical command in a permitted host context. No identity fallback is attempted, and `devrouter controller` prints the underlying cause on stderr while keeping the stable `controller-unavailable` JSON on stdout.",
22359
- "- Devsy agent readiness: run `devrouter setup --yes --workspace-runtime devsy` once per installed Devsy release, rerunning it after a Devsy update, so Devrouter acquires and verifies the official Linux agent in its own machine cache. Devrouter supports releases in the range >=1.16.2 <2.0.0, verifies a release's published asset against the SHA-256 digest GitHub reports, and never injects a release without one. `doctor` reports `ready`, `missing`, `stale`, or `invalid` without network access; a supported release with no recorded manifest is `ready` with a drift warning and no injected agent until `setup` records one, while a version outside the supported range or unparseable stays `stale`. `ensure` fails before the provider queue when readiness is not `ready`. An explicit `DEVSY_AGENT_BINARY` remains authoritative and must match the official asset for the installed supported release. `ensure` and `stop` stream bounded phase progress on stderr, so parse command JSON from stdout only.",
22396
+ "- Devsy agent readiness: run `devrouter setup --yes --workspace-runtime devsy` once per installed Devsy release, rerunning it after a Devsy update, so Devrouter acquires and verifies the official Linux agent in its own machine cache. Devrouter selects agents on macOS and Linux arm64/x64 hosts. Use Devsy 1.16.2 for warm profile changes; 1.23.2 refuses changed runServices without recreation, which devrouter never applies automatically. Devrouter supports agent releases in the range >=1.16.2 <2.0.0, verifies a release's published asset against the SHA-256 digest GitHub reports, and never injects a release without one. `doctor` reports `ready`, `missing`, `stale`, or `invalid` without network access; a supported release with no recorded manifest is `ready` with a drift warning and no injected agent until `setup` records one, while a version outside the supported range or unparseable stays `stale`. `ensure` fails before the provider queue when readiness is not `ready`. An explicit `DEVSY_AGENT_BINARY` remains authoritative and must match the official asset for the installed supported release. `ensure` and `stop` stream bounded phase progress on stderr, so parse command JSON from stdout only.",
22360
22397
  "- A managed adapter plus `postCreateCommand` requires `waitFor` exactly `postCreateCommand` or `postStartCommand`; managed selective config preserves lifecycle fields and changes only `runServices`.",
22361
22398
  "- 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.",
22362
22399
  "- 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.",
@@ -24486,7 +24523,7 @@ function probeInstalledCliVersion(executable) {
24486
24523
  }
24487
24524
  function buildCliPathCheck(options = {}) {
24488
24525
  const id5 = "global.cli-path";
24489
- const runningVersion = options.runningVersion ?? (true ? "0.5.2" : "0.0.0-dev");
24526
+ const runningVersion = options.runningVersion ?? (true ? "0.5.3" : "0.0.0-dev");
24490
24527
  if (runningVersion === "0.0.0-dev") {
24491
24528
  return {
24492
24529
  id: id5,
@@ -25382,7 +25419,7 @@ async function buildDoctorReport(options = {}) {
25382
25419
  suggestion: `Remedy ${expansionNotice.remedy.code}: set a named default profile other than '${expansionNotice.profile}'${remedyCandidates}.`
25383
25420
  });
25384
25421
  }
25385
- const cliVersion = true ? "0.5.2" : "0.0.0-dev";
25422
+ const cliVersion = true ? "0.5.3" : "0.0.0-dev";
25386
25423
  const configVersion = config.devrouter?.version;
25387
25424
  const installedKnown = cliVersion !== "0.0.0-dev";
25388
25425
  const versionComparison = installedKnown && configVersion ? compareSemver(configVersion, cliVersion) : void 0;
@@ -34045,7 +34082,7 @@ var init_version = __esm({
34045
34082
 
34046
34083
  // src/cli.ts
34047
34084
  var import_commander = require("commander");
34048
- var CLI_VERSION = true ? "0.5.2" : "0.0.0-dev";
34085
+ var CLI_VERSION = true ? "0.5.3" : "0.0.0-dev";
34049
34086
  var VERSION_FLAGS = /* @__PURE__ */ new Set(["-V", "--version"]);
34050
34087
  function withErrorHandling(action2) {
34051
34088
  return async (...args) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devrouter/cli",
3
- "version": "0.5.2",
3
+ "version": "0.5.3",
4
4
  "description": "Local dev routing CLI with shared Traefik reverse proxy",
5
5
  "author": "Roland Schlaefli",
6
6
  "homepage": "https://github.com/rschlaefli/devrouter#readme",
@@ -0,0 +1,36 @@
1
+ # Upgrade to devrouter 0.5.3
2
+
3
+ This patch release supports verified Devsy agents on Linux arm64/x64,
4
+ custom DevPod and Devsy runtime homes, and drainage of Linux process groups
5
+ proven to contain only zombies. There is no configuration schema migration.
6
+
7
+ ## Task
8
+
9
+ Install the published 0.5.3 CLI. Bump a consumer's `devrouter.version` only when
10
+ consumer source changes are authorized. Preserve exact checkout ownership,
11
+ containers, databases, dependency volumes, and existing runtime directories.
12
+ Changing the machine preference does not migrate a DevPod-owned checkout.
13
+
14
+ For Devsy, run `devrouter setup --yes --workspace-runtime devsy` to verify the
15
+ installed release's official Linux agent. Pin Devsy 1.16.2 when warm profile
16
+ changes are needed: 1.23.2 rejects a changed `runServices` list as structural.
17
+ Do not follow its recreation suggestion on a retained environment. Keep any
18
+ explicit `DEVSY_AGENT_BINARY` consistent with the installed Devsy release.
19
+
20
+ ## Validation
21
+
22
+ 1. Run `devrouter -V` from the consumer checkout and confirm 0.5.3.
23
+ 2. Run `devrouter doctor --json` and inspect runtime ownership and agent readiness.
24
+ 3. Run `devrouter ensure .` with the consumer's intended profile, then an existing
25
+ smoke or browser test. Do not replay destructive bootstrap hooks for a
26
+ retained database just to test an upgrade.
27
+ 4. If testing profile changes or stop/resume, verify the exact container and
28
+ volumes are retained. Never bypass lifecycle fences or edit ownership records.
29
+
30
+ A zombie lifecycle worker retaining its recorded birth identity remains a
31
+ known recovery limitation on hosts whose PID 1 does not reap children.
32
+
33
+ ## Report
34
+
35
+ Report the published version and resolved executable, selected runtime and
36
+ version, tested profile and functional outcomes, and any remaining limitations.