@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.
- package/dist/commands/windows-atomic-upgrade.d.ts +4 -0
- package/dist/index.js +115 -42
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +115 -42
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/commands/upgrade-helper.ts +28 -14
- package/src/commands/windows-atomic-upgrade.ts +61 -18
- package/src/mesh/mesh-queue-assignment.ts +126 -1
|
@@ -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 ? "
|
|
446
|
-
const commitShort = readInjected(true ? "
|
|
447
|
-
const version = readInjected(true ? "1.0.18-rc.
|
|
448
|
-
const builtAt = readInjected(true ? "2026-07-
|
|
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() +
|
|
43807
|
+
const deadline = Date.now() + healthTimeoutMs;
|
|
43728
43808
|
while (Date.now() < deadline) {
|
|
43729
|
-
const
|
|
43730
|
-
|
|
43731
|
-
|
|
43732
|
-
|
|
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
|
-
|
|
44178
|
-
|
|
44179
|
-
|
|
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
|
-
|
|
44187
|
-
|
|
44188
|
-
|
|
44189
|
-
|
|
44190
|
-
|
|
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 {
|