@devrouter/cli 0.5.2 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/devrouter-lifecycle-worker.js +42 -5
- package/dist/devrouter.js +46 -9
- package/package.json +1 -1
- package/upgrade-prompts/0.5.3.md +36 -0
- package/upgrade-prompts/0.5.4.md +29 -0
|
@@ -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;
|
|
@@ -2738,7 +2752,7 @@ function assertManagedContainerConfigUnchanged(options) {
|
|
|
2738
2752
|
cwd: workingDirectory,
|
|
2739
2753
|
encoding: "utf-8",
|
|
2740
2754
|
env,
|
|
2741
|
-
input: rendered.stdout,
|
|
2755
|
+
input: rendered.stdout.replaceAll("$$", "$"),
|
|
2742
2756
|
stdio: ["pipe", "pipe", "ignore"],
|
|
2743
2757
|
timeout: COMPOSE_HASH_TIMEOUT_MS,
|
|
2744
2758
|
maxBuffer: 1024 * 1024
|
|
@@ -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.
|
|
5400
|
+
return true ? "0.5.4" : "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.
|
|
7410
|
+
const cliVersion = true ? "0.5.4" : "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.
|
|
4791
|
+
const cliVersion = true ? "0.5.4" : "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.
|
|
6790
|
+
return true ? "0.5.4" : "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;
|
|
@@ -11385,7 +11399,7 @@ function assertManagedContainerConfigUnchanged(options) {
|
|
|
11385
11399
|
cwd: workingDirectory,
|
|
11386
11400
|
encoding: "utf-8",
|
|
11387
11401
|
env,
|
|
11388
|
-
input: rendered.stdout,
|
|
11402
|
+
input: rendered.stdout.replaceAll("$$", "$"),
|
|
11389
11403
|
stdio: ["pipe", "pipe", "ignore"],
|
|
11390
11404
|
timeout: COMPOSE_HASH_TIMEOUT_MS,
|
|
11391
11405
|
maxBuffer: 1024 * 1024
|
|
@@ -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.
|
|
24526
|
+
const runningVersion = options.runningVersion ?? (true ? "0.5.4" : "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.
|
|
25422
|
+
const cliVersion = true ? "0.5.4" : "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.
|
|
34085
|
+
var CLI_VERSION = true ? "0.5.4" : "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
|
@@ -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.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Upgrade to devrouter 0.5.4
|
|
2
|
+
|
|
3
|
+
This patch release fixes false Compose identity drift when provider-generated
|
|
4
|
+
entrypoints or configuration values contain literal dollar signs. Devrouter
|
|
5
|
+
undoes Compose serialization escaping before hashing the resolved configuration,
|
|
6
|
+
without another interpolation pass. There is no configuration schema migration.
|
|
7
|
+
|
|
8
|
+
## Task
|
|
9
|
+
|
|
10
|
+
Install the published 0.5.4 CLI. Remove any temporary local patch for the Compose
|
|
11
|
+
dollar-escaping fix in PR #146 by replacing it with the published package.
|
|
12
|
+
Bump a consumer's `devrouter.version` only when consumer source changes are
|
|
13
|
+
authorized. No generated configuration or agent artifacts require changes.
|
|
14
|
+
Preserve existing runtime ownership, containers, databases and dependency volumes.
|
|
15
|
+
|
|
16
|
+
## Validation
|
|
17
|
+
|
|
18
|
+
1. Run `devrouter -V` from the consumer checkout and confirm 0.5.4.
|
|
19
|
+
2. Run `devrouter doctor --json` and inspect runtime ownership and readiness.
|
|
20
|
+
3. Run `devrouter ensure .` with the intended profile and verify that an existing
|
|
21
|
+
app container is retained without a false Compose drift failure.
|
|
22
|
+
4. Run an existing application smoke test. If stop/resume is tested, confirm
|
|
23
|
+
the same container and volumes are retained. Do not replay destructive
|
|
24
|
+
bootstrap hooks or bypass lifecycle ownership checks to test an upgrade.
|
|
25
|
+
|
|
26
|
+
## Report
|
|
27
|
+
|
|
28
|
+
Report the installed version, selected runtime, tested profile, container
|
|
29
|
+
retention and functional results, and any remaining limitations.
|