@adhdev/daemon-core 1.0.18-rc.8 → 1.0.18-rc.9

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.
@@ -46,6 +46,10 @@ export declare function createDefaultWindowsAtomicHooks(options: {
46
46
  cwd: string;
47
47
  env: NodeJS.ProcessEnv;
48
48
  log: (message: string) => void;
49
+ /** Loopback IPC port to probe for health/version. Defaults to the daemon's local IPC port. */
50
+ healthPort?: number;
51
+ /** How long to poll for the replacement daemon to report the target version. */
52
+ healthTimeoutMs?: number;
49
53
  }): WindowsAtomicUpgradeHooks;
50
54
  export declare function boundedCleanupInactivePrefixes(layout: WindowsInstallerLayout, activePrefix: string, log: (message: string) => void): void;
51
55
  export declare function cleanupInactivePrefixesWithGuard(options: {
package/dist/index.js CHANGED
@@ -442,10 +442,10 @@ function readInjected(value) {
442
442
  }
443
443
  function getDaemonBuildInfo() {
444
444
  if (cached) return cached;
445
- const commit = readInjected(true ? "ebf45e12869e32286272a11046f70914784f3f04" : void 0) ?? "unknown";
446
- const commitShort = readInjected(true ? "ebf45e12" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
447
- const version = readInjected(true ? "1.0.18-rc.8" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
448
- const builtAt = readInjected(true ? "2026-07-22T04:22:40.678Z" : void 0);
445
+ const commit = readInjected(true ? "3a1a0d16c0dd7ce3df15591fbe30b7e8638ab876" : void 0) ?? "unknown";
446
+ const commitShort = readInjected(true ? "3a1a0d16" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
447
+ const version = readInjected(true ? "1.0.18-rc.9" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
448
+ const builtAt = readInjected(true ? "2026-07-22T05:27:14.328Z" : void 0);
449
449
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
450
450
  return cached;
451
451
  }
@@ -16639,6 +16639,33 @@ function isTargetNodeTransientlyUnresolved(mesh, task) {
16639
16639
  }
16640
16640
  return isWithinCloneBootstrapGrace(targetNodeId);
16641
16641
  }
16642
+ function resolveDeadTargetVerdict(components, meshId, mesh, task) {
16643
+ const NOT_DEAD = { dead: false, nodeDead: false, reason: "" };
16644
+ const targetSessionId = readNonEmptyString(task.targetSessionId);
16645
+ const targetNodeId = readNonEmptyString(task.targetNodeId);
16646
+ if (!targetSessionId && !targetNodeId) return NOT_DEAD;
16647
+ const lastUpdateMs = Date.parse(task.updatedAt || task.createdAt || "");
16648
+ if (Number.isFinite(lastUpdateMs) && Date.now() - lastUpdateMs < DEAD_TARGET_GRACE_MS) return NOT_DEAD;
16649
+ const nodes = Array.isArray(mesh?.nodes) ? mesh.nodes : [];
16650
+ if (targetNodeId) {
16651
+ const nodePresent = nodes.some((n) => meshNodeIdMatches(n, targetNodeId));
16652
+ if (!nodePresent) {
16653
+ if (isTargetNodeTransientlyUnresolved(mesh, task)) return NOT_DEAD;
16654
+ return { dead: true, nodeDead: true, reason: "dead_target_node_absent" };
16655
+ }
16656
+ }
16657
+ if (targetSessionId) {
16658
+ const node = targetNodeId ? nodes.find((n) => meshNodeIdMatches(n, targetNodeId)) : void 0;
16659
+ const nodeIsLocal = node ? isLocalAutoLaunchNode(node) : true;
16660
+ if (nodeIsLocal) {
16661
+ const verdict = resolveSessionBusyVerdict(components, targetSessionId);
16662
+ if (verdict === "UNKNOWN") {
16663
+ return { dead: true, nodeDead: false, reason: "dead_target_session_absent" };
16664
+ }
16665
+ }
16666
+ }
16667
+ return NOT_DEAD;
16668
+ }
16642
16669
  function retractActionableSkipIfPreviouslyNotified(meshId, taskId) {
16643
16670
  const dedupKey = `${meshId}:${taskId}`;
16644
16671
  if (!lastActionableSkipNotified.delete(dedupKey)) return;
@@ -17156,6 +17183,21 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
17156
17183
  continue;
17157
17184
  }
17158
17185
  if (task.targetSessionId) {
17186
+ const deadTarget = resolveDeadTargetVerdict(components, meshId, mesh, task);
17187
+ if (deadTarget.dead) {
17188
+ const requeued = requeueTask(meshId, task.id, {
17189
+ reason: deadTarget.reason,
17190
+ clearTargetSession: true,
17191
+ // Keep the node pin if only the SESSION died on a still-live node; clear it
17192
+ // when the NODE itself is absent (nothing to pin to).
17193
+ clearTargetNode: deadTarget.nodeDead
17194
+ });
17195
+ if (requeued) {
17196
+ LOG.warn("MeshQueue", `DEAD-TARGET-SELFHEAL: task ${task.id} (mesh ${meshId}) was pinned to a dead target (${deadTarget.reason}); requeued${deadTarget.nodeDead ? " and unpinned node" : ""} (requeueCount=${requeued.requeueCount ?? "?"}, status=${requeued.status}).`);
17197
+ }
17198
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "target_session_dead_requeued" });
17199
+ continue;
17200
+ }
17159
17201
  markAutoLaunch(meshId, task.id, { status: "skipped", reason: "target_session_constraint" });
17160
17202
  continue;
17161
17203
  }
@@ -17727,7 +17769,7 @@ function runIdleMaintenanceThenAssignQueue(components, args) {
17727
17769
  });
17728
17770
  });
17729
17771
  }
17730
- var import_fs13, IDLE_AUTO_FAST_FORWARD_THROTTLE_MS, idleAutoFastForwardLastAttempt, CONTINUOUS_AUTO_FAST_FORWARD_SCAN_COOLDOWN_MS, continuousAutoFastForwardLastScan, autoFastForwardWorkspaceLease, BOOTSTRAP_TERMINAL_STATUSES, DISPATCH_CONFIRM_TIMEOUT_MS, DISPATCH_CONNECT_TIMEOUT_MS, dispatchWarmupGetterMissingWarned, LOCAL_LAUNCH_READY_TIMEOUT_MS, LOCAL_LAUNCH_READY_POLL_MS, autoLaunchInProgress, autoLaunchCooldownUntil, AUTO_LAUNCH_COOLDOWN_MS, AUTO_LAUNCH_AWAIT_CLAIM_MS, AUTO_LAUNCH_AWAIT_CLAIM_BACKOFF_CAP_CYCLES, AUTO_LAUNCH_REMOTE_IDLE_TTL_MS, autoLaunchAwaitClaimBackoff, lastAutoLaunchLedgerKey, AUTO_LAUNCH_LEDGER_DEDUP_MAX, ACTIONABLE_SKIP_REASON_PREFIXES, TRANSIENT_TARGET_NODE_BOOTSTRAP_PENDING_REASON, lastActionableSkipNotified;
17772
+ var import_fs13, IDLE_AUTO_FAST_FORWARD_THROTTLE_MS, idleAutoFastForwardLastAttempt, CONTINUOUS_AUTO_FAST_FORWARD_SCAN_COOLDOWN_MS, continuousAutoFastForwardLastScan, autoFastForwardWorkspaceLease, BOOTSTRAP_TERMINAL_STATUSES, DISPATCH_CONFIRM_TIMEOUT_MS, DISPATCH_CONNECT_TIMEOUT_MS, dispatchWarmupGetterMissingWarned, LOCAL_LAUNCH_READY_TIMEOUT_MS, LOCAL_LAUNCH_READY_POLL_MS, autoLaunchInProgress, autoLaunchCooldownUntil, AUTO_LAUNCH_COOLDOWN_MS, AUTO_LAUNCH_AWAIT_CLAIM_MS, AUTO_LAUNCH_AWAIT_CLAIM_BACKOFF_CAP_CYCLES, AUTO_LAUNCH_REMOTE_IDLE_TTL_MS, autoLaunchAwaitClaimBackoff, lastAutoLaunchLedgerKey, AUTO_LAUNCH_LEDGER_DEDUP_MAX, ACTIONABLE_SKIP_REASON_PREFIXES, TRANSIENT_TARGET_NODE_BOOTSTRAP_PENDING_REASON, lastActionableSkipNotified, DEAD_TARGET_GRACE_MS;
17731
17773
  var init_mesh_queue_assignment = __esm({
17732
17774
  "src/mesh/mesh-queue-assignment.ts"() {
17733
17775
  "use strict";
@@ -17789,6 +17831,7 @@ var init_mesh_queue_assignment = __esm({
17789
17831
  ];
17790
17832
  TRANSIENT_TARGET_NODE_BOOTSTRAP_PENDING_REASON = "target_node_bootstrap_pending";
17791
17833
  lastActionableSkipNotified = /* @__PURE__ */ new Map();
17834
+ DEAD_TARGET_GRACE_MS = 6e4;
17792
17835
  }
17793
17836
  });
17794
17837
 
@@ -43395,6 +43438,40 @@ async function stopOwnedProcessesForPrefixes(options) {
43395
43438
  var POINTER_NAME = ".adhdev-current";
43396
43439
  var STABLE_FILES = [POINTER_NAME, "adhdev.cmd", "adhdev.ps1", "adhdev"];
43397
43440
  var DEFAULT_HEALTH_PORT = 19222;
43441
+ function fetchLocalJson(port, pathname) {
43442
+ return new Promise((resolve27) => {
43443
+ const req = http2.get(`http://127.0.0.1:${port}${pathname}`, { timeout: 1500 }, (res) => {
43444
+ let body = "";
43445
+ res.setEncoding("utf8");
43446
+ res.on("data", (chunk) => {
43447
+ body += chunk;
43448
+ });
43449
+ res.on("end", () => resolve27({ ok: res.statusCode === 200, body }));
43450
+ });
43451
+ req.on("timeout", () => req.destroy());
43452
+ req.on("error", () => resolve27({ ok: false, body: "" }));
43453
+ });
43454
+ }
43455
+ async function fetchLocalHealth(port) {
43456
+ const { ok, body } = await fetchLocalJson(port, "/health");
43457
+ if (!ok) return { ok: false };
43458
+ try {
43459
+ const pid = Number(JSON.parse(body)?.pid);
43460
+ return { ok: true, pid: Number.isFinite(pid) ? pid : void 0 };
43461
+ } catch {
43462
+ return { ok: false };
43463
+ }
43464
+ }
43465
+ async function fetchLocalStatusVersion(port) {
43466
+ const { ok, body } = await fetchLocalJson(port, "/api/v1/status");
43467
+ if (!ok) return void 0;
43468
+ try {
43469
+ const version = JSON.parse(body)?.status?.version;
43470
+ return typeof version === "string" ? version : void 0;
43471
+ } catch {
43472
+ return void 0;
43473
+ }
43474
+ }
43398
43475
  var ADHDEV_OWNED_MARKERS = [
43399
43476
  "session-host-daemon",
43400
43477
  "node_modules/adhdev",
@@ -43672,6 +43749,8 @@ async function performWindowsAtomicUpgrade(options) {
43672
43749
  }
43673
43750
  }
43674
43751
  function createDefaultWindowsAtomicHooks(options) {
43752
+ const healthPort = options.healthPort ?? DEFAULT_HEALTH_PORT;
43753
+ const healthTimeoutMs = options.healthTimeoutMs ?? 3e4;
43675
43754
  return {
43676
43755
  install: (stagedPrefix, portableNode) => {
43677
43756
  const env = {
@@ -43682,7 +43761,7 @@ function createDefaultWindowsAtomicHooks(options) {
43682
43761
  };
43683
43762
  const pathKey = Object.keys(env).find((key2) => key2.toLowerCase() === "path") || "Path";
43684
43763
  env[pathKey] = `${path20.dirname(portableNode)};${env[pathKey] || ""}`;
43685
- (0, import_child_process6.execFileSync)(portableNode, [
43764
+ const installOutput = String((0, import_child_process6.execFileSync)(portableNode, [
43686
43765
  options.npmCliPath,
43687
43766
  "install",
43688
43767
  "-g",
@@ -43697,7 +43776,8 @@ function createDefaultWindowsAtomicHooks(options) {
43697
43776
  maxBuffer: 20 * 1024 * 1024,
43698
43777
  windowsHide: true,
43699
43778
  env
43700
- });
43779
+ }));
43780
+ if (installOutput.trim()) options.log(installOutput.trim());
43701
43781
  },
43702
43782
  restart: (portableNode, stagedCliEntry) => {
43703
43783
  const restartArgv = options.restartArgv.map((arg, index) => index === 0 ? stagedCliEntry : arg);
@@ -43724,28 +43804,12 @@ function createDefaultWindowsAtomicHooks(options) {
43724
43804
  child.unref();
43725
43805
  },
43726
43806
  waitForHealth: async (pid, targetVersion) => {
43727
- const deadline = Date.now() + 3e4;
43807
+ const deadline = Date.now() + healthTimeoutMs;
43728
43808
  while (Date.now() < deadline) {
43729
- const result = await new Promise((resolve27) => {
43730
- const req = http2.get(`http://127.0.0.1:${DEFAULT_HEALTH_PORT}/health`, { timeout: 1500 }, (res) => {
43731
- let body = "";
43732
- res.setEncoding("utf8");
43733
- res.on("data", (chunk) => {
43734
- body += chunk;
43735
- });
43736
- res.on("end", () => {
43737
- let responsePid;
43738
- try {
43739
- responsePid = Number(JSON.parse(body)?.pid);
43740
- } catch {
43741
- }
43742
- resolve27({ ok: res.statusCode === 200, pid: responsePid, body });
43743
- });
43744
- });
43745
- req.on("timeout", () => req.destroy());
43746
- req.on("error", () => resolve27({ ok: false }));
43747
- });
43748
- if (result.ok && result.pid === pid && result.body?.includes(targetVersion)) return true;
43809
+ const liveness = await fetchLocalHealth(healthPort);
43810
+ const alive = liveness.ok && liveness.pid === pid;
43811
+ const version = alive ? await fetchLocalStatusVersion(healthPort) : void 0;
43812
+ if (alive && version === targetVersion) return true;
43749
43813
  await new Promise((resolve27) => setTimeout(resolve27, 500));
43750
43814
  }
43751
43815
  return false;
@@ -44174,22 +44238,31 @@ async function runDaemonUpgradeHelper(payload) {
44174
44238
  `Cannot upgrade: owned processes still running under current prefix: ${preStop.survivors.map((s2) => s2.pid).join(", ")}`
44175
44239
  );
44176
44240
  }
44177
- await performWindowsAtomicUpgrade({
44178
- layout: windowsInstallerLayout,
44179
- packageName: payload.packageName,
44180
- targetVersion: payload.targetVersion,
44181
- portableNode,
44182
- excludePids: upgradePids,
44183
- hooks: createDefaultWindowsAtomicHooks({
44241
+ try {
44242
+ await performWindowsAtomicUpgrade({
44243
+ layout: windowsInstallerLayout,
44184
44244
  packageName: payload.packageName,
44185
44245
  targetVersion: payload.targetVersion,
44186
- npmCliPath,
44187
- restartArgv,
44188
- cwd: payload.cwd || process.cwd(),
44189
- env: Object.fromEntries(Object.entries(process.env).filter(([key2]) => key2 !== UPGRADE_HELPER_ENV)),
44190
- log: appendUpgradeLog
44191
- })
44192
- });
44246
+ portableNode,
44247
+ excludePids: upgradePids,
44248
+ hooks: createDefaultWindowsAtomicHooks({
44249
+ packageName: payload.packageName,
44250
+ targetVersion: payload.targetVersion,
44251
+ npmCliPath,
44252
+ restartArgv,
44253
+ cwd: payload.cwd || process.cwd(),
44254
+ env: Object.fromEntries(Object.entries(process.env).filter(([key2]) => key2 !== UPGRADE_HELPER_ENV)),
44255
+ log: appendUpgradeLog
44256
+ })
44257
+ });
44258
+ } catch (error) {
44259
+ emitUpgradeFailureNotice([
44260
+ `adhdev ${payload.packageName}@${payload.targetVersion} upgrade failed and was rolled back: ${error?.message || String(error)}`,
44261
+ `Previous version preserved (active prefix: ${windowsInstallerLayout.activePrefix}).`,
44262
+ "See daemon-upgrade.log for the full install/health trace. The next daemon start will retry."
44263
+ ]);
44264
+ throw error;
44265
+ }
44193
44266
  try {
44194
44267
  fs15.unlinkSync(getUpgradeFailureNoticePath());
44195
44268
  } catch {