@devrouter/cli 0.0.76 → 0.0.78

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.
@@ -350,9 +350,25 @@ function acquireFileLock(lockPath, options) {
350
350
  }
351
351
  if (options.onWait && now - lastProgressAt >= progressIntervalMs) {
352
352
  lastProgressAt = now;
353
+ let holder;
354
+ try {
355
+ const bytes2 = import_node_fs2.default.readFileSync(lockPath, "utf-8");
356
+ const record6 = parseLockRecord(bytes2);
357
+ if (record6.owner && isLockOwnerLive(record6.owner, livenessCache) && import_node_fs2.default.readFileSync(lockPath, "utf-8") === bytes2) {
358
+ holder = {
359
+ kind: "live",
360
+ pid: record6.owner.pid,
361
+ acquiredAtMs: record6.acquiredAtMs
362
+ };
363
+ }
364
+ } catch {
365
+ }
353
366
  options.onWait({
354
367
  waitingMs: now - waitStartedAt,
355
- holderPid: queueState.leaderPid,
368
+ remainingWaitMs: Math.max(0, deadline - now),
369
+ queueLeaderPid: queueState.leaderPid,
370
+ holderPid: holder?.pid,
371
+ holderHeldMs: holder?.acquiredAtMs !== void 0 ? Math.max(0, now - holder.acquiredAtMs) : void 0,
356
372
  queuePosition: queueState.position,
357
373
  waitingOn: "queue"
358
374
  });
@@ -390,6 +406,7 @@ function acquireFileLock(lockPath, options) {
390
406
  lastProgressAt = now;
391
407
  options.onWait({
392
408
  waitingMs: now - waitStartedAt,
409
+ remainingWaitMs: Math.max(0, deadline - now),
393
410
  holderPid: state.pid,
394
411
  holderHeldMs: state.acquiredAtMs !== void 0 ? Math.max(0, now - state.acquiredAtMs) : void 0,
395
412
  queuePosition: queueState?.position,
@@ -444,9 +461,11 @@ async function withFileLock(lockPath, options, operation) {
444
461
  function createStderrWaitReporter(activity, target) {
445
462
  return (progress) => {
446
463
  const heldSeconds = progress.holderHeldMs !== void 0 ? `, held for ${Math.round(progress.holderHeldMs / 1e3)}s` : "";
447
- const status = progress.waitingOn === "queue" ? `waiting in provider queue position ${progress.queuePosition} led by PID ${progress.holderPid}` : progress.queuePosition !== void 0 && progress.queuePosition > 1 ? `waiting in provider queue position ${progress.queuePosition}; provider lock held by PID ${progress.holderPid}${heldSeconds}` : `waiting for the provider lock held by PID ${progress.holderPid}${heldSeconds}`;
464
+ const holder = progress.holderPid !== void 0 ? `provider lock held by PID ${progress.holderPid}${heldSeconds}` : "provider lock holder unknown";
465
+ const remaining = progress.remainingWaitMs !== void 0 ? `; queue acquisition timeout in ${Math.ceil(progress.remainingWaitMs / 1e3)}s (does not stop the holder)` : "";
466
+ const status = progress.waitingOn === "queue" ? `waiting in provider queue position ${progress.queuePosition}; first waiter PID ${progress.queueLeaderPid}; ${holder}` : progress.queuePosition !== void 0 && progress.queuePosition > 1 ? `waiting in provider queue position ${progress.queuePosition}; provider lock held by PID ${progress.holderPid}${heldSeconds}` : `waiting for the provider lock held by PID ${progress.holderPid}${heldSeconds}`;
448
467
  process.stderr.write(
449
- `${activity} for ${target}: ${status}; waited ${Math.round(progress.waitingMs / 1e3)}s so far
468
+ `${activity} for ${target}: ${status}; waited ${Math.round(progress.waitingMs / 1e3)}s so far${remaining}
450
469
  `
451
470
  );
452
471
  };
@@ -4683,7 +4702,7 @@ var ControllerStore = class {
4683
4702
 
4684
4703
  // src/core/reliability-operation-store.ts
4685
4704
  function installedCliVersion() {
4686
- return true ? "0.0.76" : "0.0.0-dev";
4705
+ return true ? "0.0.78" : "0.0.0-dev";
4687
4706
  }
4688
4707
  function compareCliVersions(left, right) {
4689
4708
  const parts = (value) => value.split(".").map((part) => Number.parseInt(part, 10) || 0);
@@ -6383,7 +6402,7 @@ function loadRepoConfig(repoPath, read2 = (file) => import_node_fs18.default.rea
6383
6402
  const config = parseConfig(parsed ?? {}, configPath);
6384
6403
  const requiredVersion = config.devrouter?.version;
6385
6404
  if (requiredVersion && !hasWarnedVersionMismatch) {
6386
- const cliVersion = true ? "0.0.76" : "0.0.0-dev";
6405
+ const cliVersion = true ? "0.0.78" : "0.0.0-dev";
6387
6406
  if (cliVersion !== "0.0.0-dev" && compareSemver(requiredVersion, cliVersion) > 0) {
6388
6407
  hasWarnedVersionMismatch = true;
6389
6408
  process.stderr.write(
@@ -13911,6 +13930,14 @@ async function workspaceEnsure(requestedRepoPath, options = {}) {
13911
13930
  "Claimed network repair requires exact retained-network reconciliation; automatic repair is unavailable."
13912
13931
  );
13913
13932
  }
13933
+ let earlyTlsRefreshed = false;
13934
+ if (!options.repair) {
13935
+ claimLifecycleEffect();
13936
+ earlyTlsRefreshed = (await ensureTLSHostsCovered(
13937
+ apps.map((app) => app.host),
13938
+ { repoPath }
13939
+ )).refreshed;
13940
+ }
13914
13941
  if (managedPlan && !options.repair && !network) {
13915
13942
  if (!target.hadExactDevpod && devpodId && inspectLegacyNetworkCapacity({
13916
13943
  provider: resolveWorkspaceRuntimeOrDefault(repoPath),
@@ -14333,7 +14360,7 @@ async function workspaceEnsure(requestedRepoPath, options = {}) {
14333
14360
  devpodId,
14334
14361
  urls,
14335
14362
  recreated,
14336
- tlsRefreshed: publication.tlsRefreshed,
14363
+ tlsRefreshed: earlyTlsRefreshed || publication.tlsRefreshed,
14337
14364
  ...applicationReadiness ? { applicationReadiness } : {},
14338
14365
  ...managedRuntimeStatus ? { managedRuntime: managedRuntimeStatus } : {}
14339
14366
  };
package/dist/devrouter.js CHANGED
@@ -474,9 +474,25 @@ function acquireFileLock(lockPath, options) {
474
474
  }
475
475
  if (options.onWait && now - lastProgressAt >= progressIntervalMs) {
476
476
  lastProgressAt = now;
477
+ let holder;
478
+ try {
479
+ const bytes2 = import_node_fs2.default.readFileSync(lockPath, "utf-8");
480
+ const record6 = parseLockRecord(bytes2);
481
+ if (record6.owner && isLockOwnerLive(record6.owner, livenessCache) && import_node_fs2.default.readFileSync(lockPath, "utf-8") === bytes2) {
482
+ holder = {
483
+ kind: "live",
484
+ pid: record6.owner.pid,
485
+ acquiredAtMs: record6.acquiredAtMs
486
+ };
487
+ }
488
+ } catch {
489
+ }
477
490
  options.onWait({
478
491
  waitingMs: now - waitStartedAt,
479
- holderPid: queueState.leaderPid,
492
+ remainingWaitMs: Math.max(0, deadline - now),
493
+ queueLeaderPid: queueState.leaderPid,
494
+ holderPid: holder?.pid,
495
+ holderHeldMs: holder?.acquiredAtMs !== void 0 ? Math.max(0, now - holder.acquiredAtMs) : void 0,
480
496
  queuePosition: queueState.position,
481
497
  waitingOn: "queue"
482
498
  });
@@ -514,6 +530,7 @@ function acquireFileLock(lockPath, options) {
514
530
  lastProgressAt = now;
515
531
  options.onWait({
516
532
  waitingMs: now - waitStartedAt,
533
+ remainingWaitMs: Math.max(0, deadline - now),
517
534
  holderPid: state.pid,
518
535
  holderHeldMs: state.acquiredAtMs !== void 0 ? Math.max(0, now - state.acquiredAtMs) : void 0,
519
536
  queuePosition: queueState?.position,
@@ -568,9 +585,11 @@ async function withFileLock(lockPath, options, operation) {
568
585
  function createStderrWaitReporter(activity, target) {
569
586
  return (progress) => {
570
587
  const heldSeconds = progress.holderHeldMs !== void 0 ? `, held for ${Math.round(progress.holderHeldMs / 1e3)}s` : "";
571
- const status = progress.waitingOn === "queue" ? `waiting in provider queue position ${progress.queuePosition} led by PID ${progress.holderPid}` : progress.queuePosition !== void 0 && progress.queuePosition > 1 ? `waiting in provider queue position ${progress.queuePosition}; provider lock held by PID ${progress.holderPid}${heldSeconds}` : `waiting for the provider lock held by PID ${progress.holderPid}${heldSeconds}`;
588
+ const holder = progress.holderPid !== void 0 ? `provider lock held by PID ${progress.holderPid}${heldSeconds}` : "provider lock holder unknown";
589
+ const remaining = progress.remainingWaitMs !== void 0 ? `; queue acquisition timeout in ${Math.ceil(progress.remainingWaitMs / 1e3)}s (does not stop the holder)` : "";
590
+ const status = progress.waitingOn === "queue" ? `waiting in provider queue position ${progress.queuePosition}; first waiter PID ${progress.queueLeaderPid}; ${holder}` : progress.queuePosition !== void 0 && progress.queuePosition > 1 ? `waiting in provider queue position ${progress.queuePosition}; provider lock held by PID ${progress.holderPid}${heldSeconds}` : `waiting for the provider lock held by PID ${progress.holderPid}${heldSeconds}`;
572
591
  process.stderr.write(
573
- `${activity} for ${target}: ${status}; waited ${Math.round(progress.waitingMs / 1e3)}s so far
592
+ `${activity} for ${target}: ${status}; waited ${Math.round(progress.waitingMs / 1e3)}s so far${remaining}
574
593
  `
575
594
  );
576
595
  };
@@ -4256,7 +4275,7 @@ function loadRepoConfig(repoPath, read2 = (file) => import_node_fs7.default.read
4256
4275
  const config = parseConfig(parsed ?? {}, configPath);
4257
4276
  const requiredVersion = config.devrouter?.version;
4258
4277
  if (requiredVersion && !hasWarnedVersionMismatch) {
4259
- const cliVersion = true ? "0.0.76" : "0.0.0-dev";
4278
+ const cliVersion = true ? "0.0.78" : "0.0.0-dev";
4260
4279
  if (cliVersion !== "0.0.0-dev" && compareSemver(requiredVersion, cliVersion) > 0) {
4261
4280
  hasWarnedVersionMismatch = true;
4262
4281
  process.stderr.write(
@@ -5509,7 +5528,7 @@ var init_reliability_contract = __esm({
5509
5528
 
5510
5529
  // src/core/reliability-operation-store.ts
5511
5530
  function installedCliVersion() {
5512
- return true ? "0.0.76" : "0.0.0-dev";
5531
+ return true ? "0.0.78" : "0.0.0-dev";
5513
5532
  }
5514
5533
  function compareCliVersions(left, right) {
5515
5534
  const parts = (value) => value.split(".").map((part) => Number.parseInt(part, 10) || 0);
@@ -15261,6 +15280,7 @@ var init_workspace_ensure = __esm({
15261
15280
  init_repo_config();
15262
15281
  init_route_publication();
15263
15282
  init_router();
15283
+ init_tls();
15264
15284
  init_traefik_route_health();
15265
15285
  init_workspace();
15266
15286
  init_workspace_ownership();
@@ -21122,7 +21142,7 @@ async function buildDoctorReport(options = {}) {
21122
21142
  const config = runtimeConfig.config;
21123
21143
  loadedConfig = config;
21124
21144
  loadedWorkspace = runtimeConfig.workspace;
21125
- const cliVersion = true ? "0.0.76" : "0.0.0-dev";
21145
+ const cliVersion = true ? "0.0.78" : "0.0.0-dev";
21126
21146
  const configVersion = config.devrouter?.version;
21127
21147
  if (configVersion && cliVersion !== "0.0.0-dev" && compareSemver(configVersion, cliVersion) > 0) {
21128
21148
  addCheck(checks, {
@@ -26563,7 +26583,7 @@ var init_version = __esm({
26563
26583
 
26564
26584
  // src/cli.ts
26565
26585
  var import_commander = require("commander");
26566
- var CLI_VERSION = true ? "0.0.76" : "0.0.0-dev";
26586
+ var CLI_VERSION = true ? "0.0.78" : "0.0.0-dev";
26567
26587
  var VERSION_FLAGS = /* @__PURE__ */ new Set(["-V", "--version"]);
26568
26588
  function withErrorHandling(action2) {
26569
26589
  return async (...args) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devrouter/cli",
3
- "version": "0.0.76",
3
+ "version": "0.0.78",
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,35 @@
1
+ # Upgrade to devrouter 0.0.77
2
+
3
+ Ordinary ensure now prepares TLS coverage for resolved HTTP and TCP proxy hosts
4
+ before provider startup and container-side repository hooks. The existing
5
+ certificate lock preserves coverage for other checkouts. Late publication checks
6
+ remain, and retained-runtime repair keeps its infrastructure-free behavior.
7
+ No schema, command, flag, or environment-variable migration is needed.
8
+
9
+ ## Task
10
+
11
+ Use the published 0.0.77 CLI. Update `.devrouter.yml` devrouter.version only when
12
+ consumer source changes are authorized. Preserve staged work owned by another
13
+ task. Refresh generated agent guidance only with the repository owner's authority.
14
+ No generated runtime configuration or journal rewrite is needed.
15
+
16
+ For an affected checkout, retain one owner for its supported ensure/check/stop
17
+ sequence. Historical degraded state can select retained-runtime repair, which
18
+ skips shared infrastructure. If certificate coverage is missing, reconcile it
19
+ through the supported TLS setup or refresh path with the relevant machine-change
20
+ authority; do not bypass TLS verification or repeat lifecycle mutations blindly.
21
+
22
+ ## Validation
23
+
24
+ Verify the installed version and exact checkout ownership. Check that the served
25
+ certificate validates the resolved hostname using the trusted local CA; certificate
26
+ file coverage alone does not prove live serving. Verify application readiness
27
+ separately. Finish with canonical stop and exact provider-stopped/zero-route proof
28
+ unless the owner explicitly retains the runtime. Preserve live-worker, ownership,
29
+ configuration and surviving-resource refusals.
30
+
31
+ ## Report
32
+
33
+ Report release revision, installed executable, exact checkout and producing-run
34
+ results. Separate source/CI and TLS proof from application readiness and runtime
35
+ shutdown. A trusted TLS handshake or an HTTP404 is not successful app recovery.
@@ -0,0 +1,35 @@
1
+ # Upgrade to devrouter 0.0.78
2
+
3
+ Provider queue diagnostics now distinguish the first waiter from the observed
4
+ lock holder. They report held duration and remaining acquisition time, including
5
+ that acquisition timeout does not stop the holder. Missing or changing evidence
6
+ reports an unknown holder. Acquisition, ownership, cancellation and startup
7
+ behavior remain unchanged. No schema, command, flag or environment migration is
8
+ needed.
9
+
10
+ ## Task
11
+
12
+ Use the published 0.0.78 CLI. Update the repository devrouter.version metadata
13
+ only when consumer source changes are authorized. Preserve staged work and the
14
+ existing runtime owner. Refresh generated agent guidance only with its owner's
15
+ authority. No generated runtime configuration or journal rewrite is needed.
16
+
17
+ For a queued operation, distinguish its queue leader from the observed provider
18
+ lock holder. An acquisition timeout describes the waiting operation; it does not
19
+ cancel the holder. Unknown holder evidence requires inspection, not lock removal.
20
+ Historical configuration-drift repair remains subject to its existing proof gates.
21
+
22
+ ## Validation
23
+
24
+ Verify the resolved executable and installed version. Use existing captured or
25
+ synthetic queue evidence to check holder attribution and timeout semantics; do
26
+ not start an unrelated workspace to exercise diagnostics. Require independent
27
+ terminal evidence for any already-authorized lifecycle operation. Preserve
28
+ live-worker, exact ownership, configuration and surviving-resource checks.
29
+
30
+ ## Report
31
+
32
+ Report release revision, executable, installed version, queue versus holder
33
+ evidence, and terminal operation result where available. Keep source and package
34
+ validation separate from consumer readiness or recovery. Record unknowns without
35
+ interpreting diagnostic messages as permission to cancel or remove resources.