@devrouter/cli 0.1.0 → 0.1.1
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 +15 -3
- package/dist/devrouter.js +20 -8
- package/package.json +2 -1
- package/upgrade-prompts/0.1.1.md +48 -0
|
@@ -5347,7 +5347,7 @@ function retain(session, previous) {
|
|
|
5347
5347
|
|
|
5348
5348
|
// src/core/reliability-operation-store.ts
|
|
5349
5349
|
function installedCliVersion() {
|
|
5350
|
-
return true ? "0.1.
|
|
5350
|
+
return true ? "0.1.1" : "0.0.0-dev";
|
|
5351
5351
|
}
|
|
5352
5352
|
function compareCliVersions(left, right) {
|
|
5353
5353
|
const parts = (value) => value.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
@@ -7163,7 +7163,7 @@ function loadRepoConfig(repoPath, read2 = (file) => import_node_fs18.default.rea
|
|
|
7163
7163
|
const config = parseConfig(parsed ?? {}, configPath);
|
|
7164
7164
|
const requiredVersion = config.devrouter?.version;
|
|
7165
7165
|
if (requiredVersion && !hasWarnedVersionMismatch) {
|
|
7166
|
-
const cliVersion = true ? "0.1.
|
|
7166
|
+
const cliVersion = true ? "0.1.1" : "0.0.0-dev";
|
|
7167
7167
|
if (cliVersion !== "0.0.0-dev" && compareSemver2(requiredVersion, cliVersion) > 0) {
|
|
7168
7168
|
hasWarnedVersionMismatch = true;
|
|
7169
7169
|
process.stderr.write(
|
|
@@ -11957,6 +11957,11 @@ function reliabilityRecovery(reason2, repoPath) {
|
|
|
11957
11957
|
case "stop-incomplete":
|
|
11958
11958
|
case "operation-unknown":
|
|
11959
11959
|
return [`Run: devrouter stop ${repoPath}`, `Run: devrouter ensure ${repoPath} --repair`];
|
|
11960
|
+
case "start-refused":
|
|
11961
|
+
return [
|
|
11962
|
+
`Run: devrouter doctor ${repoPath} to read the refused fixed host-port claim and its holder`,
|
|
11963
|
+
`Stop the holding workspace or change this repository's own published binding, then run: devrouter ensure ${repoPath}`
|
|
11964
|
+
];
|
|
11960
11965
|
case "unadmittable":
|
|
11961
11966
|
return [
|
|
11962
11967
|
`Run: devrouter stop ${repoPath}`,
|
|
@@ -11970,6 +11975,13 @@ function reliabilityRecovery(reason2, repoPath) {
|
|
|
11970
11975
|
return [];
|
|
11971
11976
|
}
|
|
11972
11977
|
}
|
|
11978
|
+
function refusedManagedStart(result) {
|
|
11979
|
+
if (result?.ok !== true) return false;
|
|
11980
|
+
const value = result.value;
|
|
11981
|
+
if (typeof value !== "object" || value === null) return false;
|
|
11982
|
+
const conflicts = value.hostPortConflicts;
|
|
11983
|
+
return Array.isArray(conflicts) && conflicts.length > 0;
|
|
11984
|
+
}
|
|
11973
11985
|
function readReliabilityStatus(options) {
|
|
11974
11986
|
let provider;
|
|
11975
11987
|
try {
|
|
@@ -11989,7 +12001,7 @@ function readReliabilityStatus(options) {
|
|
|
11989
12001
|
return void 0;
|
|
11990
12002
|
}
|
|
11991
12003
|
if (!record5) return void 0;
|
|
11992
|
-
const reason2 = reliabilityAttention(record5.state);
|
|
12004
|
+
const reason2 = reliabilityAttention(record5.state) ?? (record5.state.desired === "running" && refusedManagedStart(record5.result) ? "start-refused" : void 0);
|
|
11993
12005
|
return {
|
|
11994
12006
|
desired: record5.state.desired,
|
|
11995
12007
|
phase: record5.state.phase,
|
package/dist/devrouter.js
CHANGED
|
@@ -4275,7 +4275,7 @@ function loadRepoConfig(repoPath, read2 = (file) => import_node_fs7.default.read
|
|
|
4275
4275
|
const config = parseConfig(parsed ?? {}, configPath);
|
|
4276
4276
|
const requiredVersion = config.devrouter?.version;
|
|
4277
4277
|
if (requiredVersion && !hasWarnedVersionMismatch) {
|
|
4278
|
-
const cliVersion = true ? "0.1.
|
|
4278
|
+
const cliVersion = true ? "0.1.1" : "0.0.0-dev";
|
|
4279
4279
|
if (cliVersion !== "0.0.0-dev" && compareSemver(requiredVersion, cliVersion) > 0) {
|
|
4280
4280
|
hasWarnedVersionMismatch = true;
|
|
4281
4281
|
process.stderr.write(
|
|
@@ -6259,7 +6259,7 @@ var init_reliability_contract = __esm({
|
|
|
6259
6259
|
|
|
6260
6260
|
// src/core/reliability-operation-store.ts
|
|
6261
6261
|
function installedCliVersion() {
|
|
6262
|
-
return true ? "0.1.
|
|
6262
|
+
return true ? "0.1.1" : "0.0.0-dev";
|
|
6263
6263
|
}
|
|
6264
6264
|
function compareCliVersions(left, right) {
|
|
6265
6265
|
const parts = (value) => value.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
@@ -15902,6 +15902,11 @@ function reliabilityRecovery(reason, repoPath) {
|
|
|
15902
15902
|
case "stop-incomplete":
|
|
15903
15903
|
case "operation-unknown":
|
|
15904
15904
|
return [`Run: devrouter stop ${repoPath}`, `Run: devrouter ensure ${repoPath} --repair`];
|
|
15905
|
+
case "start-refused":
|
|
15906
|
+
return [
|
|
15907
|
+
`Run: devrouter doctor ${repoPath} to read the refused fixed host-port claim and its holder`,
|
|
15908
|
+
`Stop the holding workspace or change this repository's own published binding, then run: devrouter ensure ${repoPath}`
|
|
15909
|
+
];
|
|
15905
15910
|
case "unadmittable":
|
|
15906
15911
|
return [
|
|
15907
15912
|
`Run: devrouter stop ${repoPath}`,
|
|
@@ -15915,6 +15920,13 @@ function reliabilityRecovery(reason, repoPath) {
|
|
|
15915
15920
|
return [];
|
|
15916
15921
|
}
|
|
15917
15922
|
}
|
|
15923
|
+
function refusedManagedStart(result) {
|
|
15924
|
+
if (result?.ok !== true) return false;
|
|
15925
|
+
const value = result.value;
|
|
15926
|
+
if (typeof value !== "object" || value === null) return false;
|
|
15927
|
+
const conflicts = value.hostPortConflicts;
|
|
15928
|
+
return Array.isArray(conflicts) && conflicts.length > 0;
|
|
15929
|
+
}
|
|
15918
15930
|
function readReliabilityStatus(options) {
|
|
15919
15931
|
let provider;
|
|
15920
15932
|
try {
|
|
@@ -15934,7 +15946,7 @@ function readReliabilityStatus(options) {
|
|
|
15934
15946
|
return void 0;
|
|
15935
15947
|
}
|
|
15936
15948
|
if (!record5) return void 0;
|
|
15937
|
-
const reason = reliabilityAttention(record5.state);
|
|
15949
|
+
const reason = reliabilityAttention(record5.state) ?? (record5.state.desired === "running" && refusedManagedStart(record5.result) ? "start-refused" : void 0);
|
|
15938
15950
|
return {
|
|
15939
15951
|
desired: record5.state.desired,
|
|
15940
15952
|
phase: record5.state.phase,
|
|
@@ -21024,7 +21036,7 @@ Run several worktrees of one repo in parallel without host/route collisions. A *
|
|
|
21024
21036
|
- **TLS**: namespaced hosts (\`web.<ws>.localhost\`) are not covered by the \`*.localhost\` wildcard; devrouter auto-extends the mkcert cert SANs for active hosts when TLS is enabled.
|
|
21025
21037
|
- **devcontainer integration**: managed scaffolds list the base compose file, then \`\${localEnv:DEVCONTAINER_COMPOSE_OVERLAY:docker-compose.default.yml}\`; custom repositories may keep another default overlay. Selecting \`.devcontainer/docker-compose.devrouter.yml\` for linked worktrees must pass \`WORKSPACE\` and \`DEVROUTER_WORKSPACE\` across the combined base/overlay config and bind-mount \`\${DEVROUTER_GIT_COMMON_DIR}\` to the same absolute app-container path. The app exposes \`\${WORKSPACE}-app\`; the proxy uses \`upstream: \${WORKSPACE}-app:<port>\`.
|
|
21026
21038
|
- **Lifecycle**: after one-time \`setup\`, use \`ensure .\` for both primary and linked checkouts; never branch manually on checkout kind or use live verify as startup. \`stop .\` is non-destructive; \`stop . --delete\` is explicit exact-owner cleanup without worktree removal; and \`exec . -- <command...>\` runs one-shot commands only in the exact running workspace runtime (DevPod or Devsy). Never substitute raw \`devpod up\`, \`stop\`, \`delete\`, or the Devsy equivalents: they bypass devrouter's machine-global ownership lock, which serializes provider mutations in a fair arrival-order queue and lets contenders wait up to thirty minutes with throttled stderr progress before failing with the queue position or holder PID and true durations. Runtime selection is path-aware: \`DEVROUTER_WORKSPACE_RUNTIME=devpod|devsy\` forces one runtime, an exact-path registry owner wins next, then the machine preference from \`devrouter setup --workspace-runtime\`, then installed-CLI auto-detection. \`workspace up\` creates linked worktrees; destructive worktree removal and GC remain ledger-scoped. Dirty or locked full down fails before side effects. For Devsy, 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 Devsy releases in the range >=1.16.2 <2.0.0 and refuses a release whose published asset has no SHA-256 digest. \`doctor\` reports \`ready\`, \`missing\`, \`stale\`, or \`invalid\` without network access, and \`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 release.
|
|
21027
|
-
- **Interrupted lifecycle**: ensure reconciles an interrupted or never-dispatched ensure after positive worker drainage and automatically repairs retained degraded runtimes. Unknown arbitrary exec is never replayed; explicit stop reconciles that uncertainty. Preserve journals and retained configuration when evidence is unavailable. This does not provide OOM prevention or capacity-managed automatic recovery.
|
|
21039
|
+
- **Interrupted lifecycle**: ensure reconciles an interrupted or never-dispatched ensure after positive worker drainage and automatically repairs retained degraded runtimes. Unknown arbitrary exec is never replayed; explicit stop reconciles that uncertainty. A start refused before any provider mutation \u2014 a fixed published host port already held by a running container \u2014 stays visible in \`status\` as a \`start-refused\` attention reason naming the read-only \`doctor\` check and the consumer-side fix; an explicit \`stop\` releases the intent and the reason, and a later admitted ensure clears it. Preserve journals and retained configuration when evidence is unavailable. This does not provide OOM prevention or capacity-managed automatic recovery.
|
|
21028
21040
|
- **Managed process identity**: \`ensure\` executes an exact captured adapter snapshot. Default reuse includes command argv, workspace, and adapter SHA-256. Set \`DEVROUTER_PROCESS_FINGERPRINT_ENV\` only to comma-separated non-secret environment names whose values affect runtime identity; secret-like names are rejected and raw values are never persisted.
|
|
21029
21041
|
- **Route state**: the versioned Traefik dynamic file is authoritative for both metadata and rendering. JSON is a compatibility mirror; valid headerless generations migrate automatically, while corrupt canonical metadata fails closed.
|
|
21030
21042
|
- **Cleanup**: \`workspace cleanup --repo . --inactive-for 30d --json\` is a report-only, no-\`--yes\` report for managed linked workspaces. It joins ownership (\`present|missing|locked|conflict\`), workspace runtime registration, runtime state (\`running|stopped|busy|not-found|absent|unknown\`), checkout, route, advisory activity, and integration evidence without mutating the workspace runtime, routes, ownership, Git, Docker, applications, worktrees, or branches. 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. GC never removes Git worktrees, branches, or prune state. Git has no worktree-removal hook.
|
|
@@ -21299,7 +21311,7 @@ function buildOnboardingPrompt(options = {}) {
|
|
|
21299
21311
|
"- if kind=app and runtime=proxy:",
|
|
21300
21312
|
" - HTTP-only optional readiness: { path: /api/health, statuses: [200], contentType: application/json }; statuses default to [200], explicit lists allow unique 2xx/4xx codes, never redirects. Paths stay on the configured host with no query, fragment, percent escapes, backslash or dot segments. Media types match their case-insensitive base type.",
|
|
21301
21313
|
" - Declared application readiness failures return applicationReadiness.status=application-error and a nonzero ensure exit, while retaining tooling and routes for application fixes; do not reset caches or recreate the provider. Absent readiness means legacy route liveness, not full application readiness.",
|
|
21302
|
-
" - A configured FIXED published host binding already held by a running container refuses managed ensure before any provider start (hostPortConflicts in --json, nonzero exit) naming the port, holder container, compose project, and owning workspace when attributable. Ephemeral host::container bindings are exempt. Never rewrite consumer ports to resolve a conflict: stop the holding workspace or use the consumer's own override, then ensure again. Unverifiable evidence also refuses.",
|
|
21314
|
+
" - A configured FIXED published host binding already held by a running container refuses managed ensure before any provider start (hostPortConflicts in --json, nonzero exit) naming the port, holder container, compose project, and owning workspace when attributable. Ephemeral host::container bindings are exempt. Never rewrite consumer ports to resolve a conflict: stop the holding workspace or use the consumer's own override, then ensure again. The refused start stays visible in `devrouter status` as a `start-refused` attention reason that names the read-only `doctor` check and the consumer-side fix; an explicit `stop` releases the intent and the reason, and a later admitted ensure clears it. Unverifiable evidence also refuses.",
|
|
21303
21315
|
' - upstream: "host:port" (an already-running port, e.g. a devcontainer published on 127.0.0.1:3000, or a container reachable by name on a shared Docker network such as `derivatives-db:5432`)',
|
|
21304
21316
|
` - upstream may use the \`${WORKSPACE_PLACEHOLDER}\` placeholder (e.g. \`${WORKSPACE_PLACEHOLDER}-app:3000\`) to target a per-workspace devcontainer alias; it is substituted with the resolved workspace token at runtime and re-validated. Do NOT put \`${WORKSPACE_PLACEHOLDER}\` in \`host\` (rejected) \u2014 the host is auto-namespaced.`,
|
|
21305
21317
|
" - managed `devrouter ensure` requires every HTTP/TCP proxy upstream to begin with the exact resolved workspace/project alias prefix before it mutates DevPod or routes",
|
|
@@ -23193,7 +23205,7 @@ function probeInstalledCliVersion(executable) {
|
|
|
23193
23205
|
}
|
|
23194
23206
|
function buildCliPathCheck(options = {}) {
|
|
23195
23207
|
const id5 = "global.cli-path";
|
|
23196
|
-
const runningVersion = options.runningVersion ?? (true ? "0.1.
|
|
23208
|
+
const runningVersion = options.runningVersion ?? (true ? "0.1.1" : "0.0.0-dev");
|
|
23197
23209
|
if (runningVersion === "0.0.0-dev") {
|
|
23198
23210
|
return {
|
|
23199
23211
|
id: id5,
|
|
@@ -24041,7 +24053,7 @@ async function buildDoctorReport(options = {}) {
|
|
|
24041
24053
|
suggestion: `Remedy ${expansionNotice.remedy.code}: set a named default profile other than '${expansionNotice.profile}'${remedyCandidates}.`
|
|
24042
24054
|
});
|
|
24043
24055
|
}
|
|
24044
|
-
const cliVersion = true ? "0.1.
|
|
24056
|
+
const cliVersion = true ? "0.1.1" : "0.0.0-dev";
|
|
24045
24057
|
const configVersion = config.devrouter?.version;
|
|
24046
24058
|
const installedKnown = cliVersion !== "0.0.0-dev";
|
|
24047
24059
|
const versionComparison = installedKnown && configVersion ? compareSemver(configVersion, cliVersion) : void 0;
|
|
@@ -30021,7 +30033,7 @@ var init_version = __esm({
|
|
|
30021
30033
|
|
|
30022
30034
|
// src/cli.ts
|
|
30023
30035
|
var import_commander = require("commander");
|
|
30024
|
-
var CLI_VERSION = true ? "0.1.
|
|
30036
|
+
var CLI_VERSION = true ? "0.1.1" : "0.0.0-dev";
|
|
30025
30037
|
var VERSION_FLAGS = /* @__PURE__ */ new Set(["-V", "--version"]);
|
|
30026
30038
|
function withErrorHandling(action2) {
|
|
30027
30039
|
return async (...args) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devrouter/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
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",
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"qualify:harness-journey": "./scripts/qualify-harness-journey.sh",
|
|
55
55
|
"qualify:codex-journey": "DR_JOURNEY_HARNESS=codex ./scripts/qualify-harness-journey.sh",
|
|
56
56
|
"qualify:non-node": "./scripts/qualify-non-node-consumer.sh",
|
|
57
|
+
"qualify:slow-dependency": "./scripts/qualify-slow-dependency-recovery.sh",
|
|
57
58
|
"test:process": "bash scripts/test-devrouter-process.sh",
|
|
58
59
|
"check": "biome check .",
|
|
59
60
|
"check:fix": "biome check --write .",
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Upgrade to devrouter 0.1.1
|
|
2
|
+
|
|
3
|
+
This is a patch release on top of 0.1.0. It closes one way a managed environment
|
|
4
|
+
could sit blocked without saying so: a start that admission refused on a fixed
|
|
5
|
+
published host-port claim left the journal recording the intent as `running`
|
|
6
|
+
while no runtime existed, so `devrouter status` reported a stable, drifting-free
|
|
7
|
+
environment that could not progress.
|
|
8
|
+
|
|
9
|
+
## What changed for a consumer
|
|
10
|
+
|
|
11
|
+
- A managed `ensure` refused because a running container already holds a fixed
|
|
12
|
+
published host port now stays visible after the command exits. `devrouter
|
|
13
|
+
status` reports a `start-refused` attention reason with two recovery
|
|
14
|
+
commands: the read-only `devrouter doctor <repo>` check that names the
|
|
15
|
+
conflicting consumer binding and its holder, and the consumer-side fix
|
|
16
|
+
followed by `devrouter ensure <repo>`.
|
|
17
|
+
- An explicit `devrouter stop <repo>` releases that intent and the reason, and a
|
|
18
|
+
later admitted ensure clears it. Nothing else about the refusal changed: the
|
|
19
|
+
start is still refused before any provider mutation, and live-worker,
|
|
20
|
+
unknown-ownership and surviving-resource checks still refuse fail-closed.
|
|
21
|
+
- The release also carries `pnpm qualify:slow-dependency`, the developer
|
|
22
|
+
qualifier behind the Q06 dependency-recovery evidence. It is a repository
|
|
23
|
+
script and has no consumer-facing effect.
|
|
24
|
+
|
|
25
|
+
## Task
|
|
26
|
+
|
|
27
|
+
Use the published 0.1.1 CLI. Update the repository `devrouter.version` metadata
|
|
28
|
+
only when consumer source changes are authorized, and refresh generated agent
|
|
29
|
+
guidance only with its owner's authority. No schema, command, flag, environment
|
|
30
|
+
or journal migration is needed, and the upgrade rewrites no runtime state.
|
|
31
|
+
|
|
32
|
+
## Validation
|
|
33
|
+
|
|
34
|
+
Verify the resolved executable and installed version, then run `devrouter
|
|
35
|
+
doctor` for the checkout and confirm `global.cli-path` reports one
|
|
36
|
+
authoritative install. Run `devrouter status` and confirm the reported intent,
|
|
37
|
+
phase and attention reason agree with the runtime you observe. When the reported
|
|
38
|
+
reason is `start-refused`, resolve the named binding as the owner of that
|
|
39
|
+
port — the shared router is never the side to stop — and keep each lifecycle
|
|
40
|
+
operation's own terminal evidence separate from a status block or progress line.
|
|
41
|
+
|
|
42
|
+
## Report
|
|
43
|
+
|
|
44
|
+
Report the release revision, the executable and installed version, the status
|
|
45
|
+
block for any affected checkout, and the terminal result of any authorized
|
|
46
|
+
lifecycle operation. Keep source and package validation separate from consumer
|
|
47
|
+
readiness or recovery, and record unknown ownership or surviving resources as
|
|
48
|
+
unresolved rather than as recovered.
|