@adhdev/daemon-core 0.9.82-rc.358 → 0.9.82-rc.359
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/upgrade-helper.d.ts +28 -0
- package/dist/index.js +122 -15
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +122 -15
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/commands/WINDOWS-UPGRADE-LOCK-FAILURE.md +198 -0
- package/src/commands/router.ts +6 -1
- package/src/commands/upgrade-helper.ts +172 -1
- package/src/mesh/mesh-events-coordinator.ts +6 -5
- package/src/mesh/mesh-reconcile-loop.ts +4 -4
|
@@ -40,6 +40,34 @@ export declare function buildPinnedGlobalInstallCommand(options: {
|
|
|
40
40
|
export declare function getNpmExecOptions(platform?: NodeJS.Platform): NpmExecOptions;
|
|
41
41
|
export declare function execNpmCommandSync(args: string[], options?: ExecFileSyncOptions, surface?: Pick<CurrentGlobalInstallSurface, 'npmExecutable' | 'npmArgsPrefix' | 'execOptions'>): Buffer | string;
|
|
42
42
|
export declare function stopSessionHostProcesses(appName: string): Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* Enumerate processes that have a locked native addon (conpty.node /
|
|
45
|
+
* ghostty-vt.dll) of *this* install memory-mapped.
|
|
46
|
+
*
|
|
47
|
+
* `stopSessionHostProcesses()` only knows the single managed session-host pid, so
|
|
48
|
+
* any *foreign* holder — e.g. an orphaned `pty_*probe*.cjs` left in `%TEMP%` — is
|
|
49
|
+
* invisible to it and keeps the addon locked through every install retry, dooming
|
|
50
|
+
* the upgrade with EBUSY. This scans by the module's full path so we only ever
|
|
51
|
+
* target a holder of the exact `packageRoot` being replaced (never an unrelated
|
|
52
|
+
* install's copy). Windows-only — these locks don't exist on POSIX.
|
|
53
|
+
*/
|
|
54
|
+
export declare function listForeignNativeAddonHolders(packageRoot: string | null | undefined): Array<{
|
|
55
|
+
pid: number;
|
|
56
|
+
commandLine: string | null;
|
|
57
|
+
}>;
|
|
58
|
+
/**
|
|
59
|
+
* Terminate every foreign process holding this install's native addon mapped,
|
|
60
|
+
* then wait for each to actually exit so the mapping is released before npm
|
|
61
|
+
* copies the file into its staging dir. Returns what it found/killed so the
|
|
62
|
+
* caller can surface an actionable recovery message on failure.
|
|
63
|
+
*/
|
|
64
|
+
export declare function stopForeignNativeAddonHolders(packageRoot: string | null | undefined, options?: {
|
|
65
|
+
parentPid?: number;
|
|
66
|
+
}): Promise<Array<{
|
|
67
|
+
pid: number;
|
|
68
|
+
commandLine: string | null;
|
|
69
|
+
killed: boolean;
|
|
70
|
+
}>>;
|
|
43
71
|
/**
|
|
44
72
|
* Best-effort removal of a leftover npm staging entry.
|
|
45
73
|
*
|
package/dist/index.js
CHANGED
|
@@ -316,10 +316,10 @@ function readInjected(value) {
|
|
|
316
316
|
}
|
|
317
317
|
function getDaemonBuildInfo() {
|
|
318
318
|
if (cached) return cached;
|
|
319
|
-
const commit = readInjected(true ? "
|
|
320
|
-
const commitShort = readInjected(true ? "
|
|
321
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
322
|
-
const builtAt = readInjected(true ? "2026-06-
|
|
319
|
+
const commit = readInjected(true ? "56a1f2a986ab2c2bb7888189d9b027d6067f2317" : void 0) ?? "unknown";
|
|
320
|
+
const commitShort = readInjected(true ? "56a1f2a9" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
321
|
+
const version = readInjected(true ? "0.9.82-rc.359" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
322
|
+
const builtAt = readInjected(true ? "2026-06-23T05:03:11.133Z" : void 0);
|
|
323
323
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
324
324
|
return cached;
|
|
325
325
|
}
|
|
@@ -13015,9 +13015,7 @@ function isLocalAutoLaunchNode(node) {
|
|
|
13015
13015
|
const machineId = readNonEmptyString2(node?.machineId);
|
|
13016
13016
|
const appConfig = loadConfig();
|
|
13017
13017
|
const localMachineId = readNonEmptyString2(appConfig.machineId) || readNonEmptyString2(appConfig.registeredMachineId);
|
|
13018
|
-
const
|
|
13019
|
-
const standaloneDaemonId = localMachineId ? `standalone_${localMachineId}` : "";
|
|
13020
|
-
const daemonMatchesLocal = !daemonId || daemonId === cloudDaemonId || daemonId === standaloneDaemonId;
|
|
13018
|
+
const daemonMatchesLocal = !daemonId || daemonIdsEquivalent(daemonId, localMachineId);
|
|
13021
13019
|
const machineMatchesLocal = !machineId || !!localMachineId && machineId === localMachineId;
|
|
13022
13020
|
if (node?.isLocalWorktree === true) {
|
|
13023
13021
|
return daemonMatchesLocal && machineMatchesLocal;
|
|
@@ -13953,7 +13951,7 @@ function injectMeshSystemMessage(components, args) {
|
|
|
13953
13951
|
targetNodeId: autoNodeId
|
|
13954
13952
|
});
|
|
13955
13953
|
LOG.info("MeshRecovery", `Auto-requeued failed task: ${task.id} for node ${autoNodeId}`);
|
|
13956
|
-
const node = mesh?.nodes.find((n) => n
|
|
13954
|
+
const node = mesh?.nodes.find((n) => meshNodeIdMatches(n, autoNodeId));
|
|
13957
13955
|
if (node) {
|
|
13958
13956
|
components.cliManager.handleCliCommand("launch_cli", {
|
|
13959
13957
|
cliType: recoveryContext.failedProviderType,
|
|
@@ -14683,7 +14681,7 @@ async function pullRemoteNodeQueues(components, mesh, localDaemonId, candidateDa
|
|
|
14683
14681
|
for (const node of mesh.nodes) {
|
|
14684
14682
|
const nodeDaemonId = readNonEmptyString2(node.daemonId);
|
|
14685
14683
|
if (!nodeDaemonId) continue;
|
|
14686
|
-
if (
|
|
14684
|
+
if (daemonIdsEquivalent(nodeDaemonId, localDaemonId)) continue;
|
|
14687
14685
|
if (candidateDaemonIds.includes(nodeDaemonId)) continue;
|
|
14688
14686
|
for (const pendingEventArgs of pulls) {
|
|
14689
14687
|
let events;
|
|
@@ -14740,7 +14738,7 @@ async function reconcileUnterminatedDirectDispatches(components, mesh, selfIds,
|
|
|
14740
14738
|
if (!sessionId || !nodeId || !taskId) continue;
|
|
14741
14739
|
const node = nodeById.get(nodeId);
|
|
14742
14740
|
const nodeDaemonId = readNonEmptyString2(node?.daemonId);
|
|
14743
|
-
const isLocalNode = !nodeDaemonId || selfIds.includes(nodeDaemonId) ||
|
|
14741
|
+
const isLocalNode = !nodeDaemonId || selfIds.includes(nodeDaemonId) || daemonIdsEquivalent(nodeDaemonId, localDaemonId) || !!components.instanceManager.getInstance(sessionId);
|
|
14744
14742
|
const providerType = readNonEmptyString2(dispatch.providerType);
|
|
14745
14743
|
const readArgs = {
|
|
14746
14744
|
sessionId,
|
|
@@ -14815,7 +14813,7 @@ async function collectLiveNodesWithSessions(components, mesh, selfIds, localDaem
|
|
|
14815
14813
|
const dispatchMeshCommand = components.dispatchMeshCommand;
|
|
14816
14814
|
return Promise.all(mesh.nodes.map(async (node) => {
|
|
14817
14815
|
const nodeDaemonId = readNonEmptyString2(node.daemonId);
|
|
14818
|
-
const isLocalNode = !nodeDaemonId || selfIds.includes(nodeDaemonId) ||
|
|
14816
|
+
const isLocalNode = !nodeDaemonId || selfIds.includes(nodeDaemonId) || daemonIdsEquivalent(nodeDaemonId, localDaemonId);
|
|
14819
14817
|
let statusResult;
|
|
14820
14818
|
try {
|
|
14821
14819
|
if (isLocalNode) {
|
|
@@ -44872,6 +44870,7 @@ function buildInstallEnvWithNodeOnPath(baseEnv = process.env) {
|
|
|
44872
44870
|
const pathKey = Object.keys(env).find((k) => k.toLowerCase() === "path") || "PATH";
|
|
44873
44871
|
const current = env[pathKey] || "";
|
|
44874
44872
|
env[pathKey] = current ? `${nodeBinDir};${current}` : nodeBinDir;
|
|
44873
|
+
env.ADHDEV_BOOTSTRAP = "1";
|
|
44875
44874
|
return env;
|
|
44876
44875
|
}
|
|
44877
44876
|
function getNpmExecOptions(platform10 = process.platform) {
|
|
@@ -44981,6 +44980,94 @@ async function stopSessionHostProcesses(appName) {
|
|
|
44981
44980
|
await waitForPidExit(killedPid, 15e3);
|
|
44982
44981
|
}
|
|
44983
44982
|
}
|
|
44983
|
+
var LOCKED_NATIVE_ADDON_BASENAMES = ["conpty.node", "ghostty-vt.dll"];
|
|
44984
|
+
function listForeignNativeAddonHolders(packageRoot) {
|
|
44985
|
+
if (process.platform !== "win32" || !packageRoot) return [];
|
|
44986
|
+
const rootLower = packageRoot.replace(/\//g, "\\").replace(/'/g, "''").toLowerCase();
|
|
44987
|
+
const endsWithChecks = LOCKED_NATIVE_ADDON_BASENAMES.map((name) => `$lf.EndsWith('${name}')`).join(" -or ");
|
|
44988
|
+
const script = [
|
|
44989
|
+
`$root = '${rootLower}'`,
|
|
44990
|
+
`Get-Process node -ErrorAction SilentlyContinue | ForEach-Object {`,
|
|
44991
|
+
` $p = $_`,
|
|
44992
|
+
` try {`,
|
|
44993
|
+
` foreach ($m in $p.Modules) {`,
|
|
44994
|
+
` $fn = $m.FileName`,
|
|
44995
|
+
` if ($fn) {`,
|
|
44996
|
+
` $lf = $fn.ToLower()`,
|
|
44997
|
+
` if ($lf.StartsWith($root) -and (${endsWithChecks})) { $p.Id; break }`,
|
|
44998
|
+
` }`,
|
|
44999
|
+
` }`,
|
|
45000
|
+
` } catch {}`,
|
|
45001
|
+
`}`
|
|
45002
|
+
].join("\n");
|
|
45003
|
+
let out = "";
|
|
45004
|
+
try {
|
|
45005
|
+
out = String((0, import_child_process8.execFileSync)("powershell.exe", [
|
|
45006
|
+
"-NoProfile",
|
|
45007
|
+
"-NonInteractive",
|
|
45008
|
+
"-ExecutionPolicy",
|
|
45009
|
+
"Bypass",
|
|
45010
|
+
"-Command",
|
|
45011
|
+
script
|
|
45012
|
+
], { encoding: "utf8", timeout: 8e3, stdio: ["ignore", "pipe", "ignore"], windowsHide: true })).trim();
|
|
45013
|
+
} catch {
|
|
45014
|
+
return [];
|
|
45015
|
+
}
|
|
45016
|
+
const selfPid = process.pid;
|
|
45017
|
+
const seen = /* @__PURE__ */ new Set();
|
|
45018
|
+
const holders = [];
|
|
45019
|
+
for (const line of out.split(/\r?\n/)) {
|
|
45020
|
+
const pid = Number.parseInt(line.trim(), 10);
|
|
45021
|
+
if (!Number.isFinite(pid) || pid <= 0 || pid === selfPid || seen.has(pid)) continue;
|
|
45022
|
+
seen.add(pid);
|
|
45023
|
+
holders.push({ pid, commandLine: getProcessCommandLine(pid) });
|
|
45024
|
+
}
|
|
45025
|
+
return holders;
|
|
45026
|
+
}
|
|
45027
|
+
async function stopForeignNativeAddonHolders(packageRoot, options = {}) {
|
|
45028
|
+
if (process.platform !== "win32" || !packageRoot) return [];
|
|
45029
|
+
const parentPid = Number.isFinite(options.parentPid) ? Number(options.parentPid) : -1;
|
|
45030
|
+
const holders = listForeignNativeAddonHolders(packageRoot);
|
|
45031
|
+
const results = [];
|
|
45032
|
+
for (const holder of holders) {
|
|
45033
|
+
if (holder.pid === parentPid) continue;
|
|
45034
|
+
appendUpgradeLog(
|
|
45035
|
+
`Foreign native-addon holder found: pid ${holder.pid}${holder.commandLine ? ` \u2014 ${holder.commandLine}` : ""}`
|
|
45036
|
+
);
|
|
45037
|
+
const killed = killPid(holder.pid);
|
|
45038
|
+
if (killed) {
|
|
45039
|
+
await waitForPidExit(holder.pid, 15e3);
|
|
45040
|
+
appendUpgradeLog(`Terminated foreign native-addon holder pid ${holder.pid}`);
|
|
45041
|
+
} else {
|
|
45042
|
+
appendUpgradeLog(`Failed to terminate foreign native-addon holder pid ${holder.pid}`);
|
|
45043
|
+
}
|
|
45044
|
+
results.push({ ...holder, killed });
|
|
45045
|
+
}
|
|
45046
|
+
return results;
|
|
45047
|
+
}
|
|
45048
|
+
function getUpgradeFailureNoticePath() {
|
|
45049
|
+
const home = os27.homedir();
|
|
45050
|
+
const dir = path36.join(home, ".adhdev");
|
|
45051
|
+
try {
|
|
45052
|
+
fs25.mkdirSync(dir, { recursive: true });
|
|
45053
|
+
} catch {
|
|
45054
|
+
}
|
|
45055
|
+
return path36.join(dir, "daemon-upgrade-last-error.txt");
|
|
45056
|
+
}
|
|
45057
|
+
function buildManualRecoveryCommand(installCommand) {
|
|
45058
|
+
return [installCommand.command, ...installCommand.args].map((part) => /\s/.test(part) ? `"${part}"` : part).join(" ");
|
|
45059
|
+
}
|
|
45060
|
+
function emitUpgradeFailureNotice(lines) {
|
|
45061
|
+
const body = lines.join("\n");
|
|
45062
|
+
appendUpgradeLog(`Upgrade blocked \u2014 user action required:
|
|
45063
|
+
${body}`);
|
|
45064
|
+
try {
|
|
45065
|
+
fs25.writeFileSync(getUpgradeFailureNoticePath(), `[${(/* @__PURE__ */ new Date()).toISOString()}]
|
|
45066
|
+
${body}
|
|
45067
|
+
`, "utf8");
|
|
45068
|
+
} catch {
|
|
45069
|
+
}
|
|
45070
|
+
}
|
|
44984
45071
|
function isRetriableInstallLockError(error) {
|
|
44985
45072
|
const code = error?.code;
|
|
44986
45073
|
if (code === "EBUSY" || code === "EPERM") return true;
|
|
@@ -45067,6 +45154,7 @@ async function runDaemonUpgradeHelper(payload) {
|
|
|
45067
45154
|
}
|
|
45068
45155
|
await stopSessionHostProcesses(sessionHostAppName);
|
|
45069
45156
|
removeDaemonPidFile();
|
|
45157
|
+
await stopForeignNativeAddonHolders(installCommand.surface.packageRoot, { parentPid: payload.parentPid });
|
|
45070
45158
|
cleanupStaleGlobalInstallDirs(payload.packageName, installCommand.surface);
|
|
45071
45159
|
const spec = `${payload.packageName}@${payload.targetVersion || "latest"}`;
|
|
45072
45160
|
appendUpgradeLog(`Installing ${spec}`);
|
|
@@ -45088,11 +45176,30 @@ async function runDaemonUpgradeHelper(payload) {
|
|
|
45088
45176
|
break;
|
|
45089
45177
|
} catch (error) {
|
|
45090
45178
|
if (attempt < maxInstallAttempts && isRetriableInstallLockError(error)) {
|
|
45091
|
-
appendUpgradeLog(`Install attempt ${attempt} hit a file lock (${error?.code || "lock"});
|
|
45179
|
+
appendUpgradeLog(`Install attempt ${attempt} hit a file lock (${error?.code || "lock"}); clearing holders + staging and retrying after backoff`);
|
|
45180
|
+
await stopForeignNativeAddonHolders(installCommand.surface.packageRoot, { parentPid: payload.parentPid });
|
|
45092
45181
|
cleanupStaleGlobalInstallDirs(payload.packageName, installCommand.surface);
|
|
45093
45182
|
await new Promise((resolve24) => setTimeout(resolve24, attempt * 1500));
|
|
45094
45183
|
continue;
|
|
45095
45184
|
}
|
|
45185
|
+
if (isRetriableInstallLockError(error)) {
|
|
45186
|
+
const blockers = listForeignNativeAddonHolders(installCommand.surface.packageRoot);
|
|
45187
|
+
const notice = [
|
|
45188
|
+
`adhdev ${spec} could not be installed: a file lock (${error?.code || "EBUSY/EPERM"}) is blocking the native addon.`
|
|
45189
|
+
];
|
|
45190
|
+
if (blockers.length > 0) {
|
|
45191
|
+
notice.push("Processes still holding the lock:");
|
|
45192
|
+
for (const b of blockers) {
|
|
45193
|
+
notice.push(` pid ${b.pid}${b.commandLine ? ` \u2014 ${b.commandLine}` : ""}`);
|
|
45194
|
+
}
|
|
45195
|
+
notice.push("To recover, stop them and reinstall:");
|
|
45196
|
+
notice.push(` Stop-Process -Id ${blockers.map((b) => b.pid).join(",")} -Force`);
|
|
45197
|
+
} else {
|
|
45198
|
+
notice.push("To recover, reinstall manually:");
|
|
45199
|
+
}
|
|
45200
|
+
notice.push(` ${buildManualRecoveryCommand(installCommand)}`);
|
|
45201
|
+
emitUpgradeFailureNotice(notice);
|
|
45202
|
+
}
|
|
45096
45203
|
throw error;
|
|
45097
45204
|
}
|
|
45098
45205
|
}
|
|
@@ -47759,7 +47866,7 @@ var DaemonCommandRouter = class {
|
|
|
47759
47866
|
return sanitizedInlineMesh;
|
|
47760
47867
|
}
|
|
47761
47868
|
async getMeshForCommand(meshId, inlineMesh, options) {
|
|
47762
|
-
const preferInline = options?.preferInline
|
|
47869
|
+
const preferInline = options?.preferInline !== false;
|
|
47763
47870
|
if (preferInline) {
|
|
47764
47871
|
const cached4 = this.getCachedInlineMesh(meshId);
|
|
47765
47872
|
if (cached4) {
|
|
@@ -52118,7 +52225,7 @@ ${ptyResult.output.slice(-2e3)}`);
|
|
|
52118
52225
|
workspace
|
|
52119
52226
|
};
|
|
52120
52227
|
}
|
|
52121
|
-
const { existsSync: existsSync46, readFileSync: readFileSync37, writeFileSync:
|
|
52228
|
+
const { existsSync: existsSync46, readFileSync: readFileSync37, writeFileSync: writeFileSync24, copyFileSync: copyFileSync4, mkdirSync: mkdirSync21 } = await import("fs");
|
|
52122
52229
|
const { dirname: dirname17 } = await import("path");
|
|
52123
52230
|
const mcpConfigPath = coordinatorSetup.configPath;
|
|
52124
52231
|
const hermesManualFallback = cliType === "hermes-cli" && configFormat === "hermes_config_yaml" ? createHermesManualMeshCoordinatorSetup(meshId, workspace) : null;
|
|
@@ -52191,7 +52298,7 @@ ${ptyResult.output.slice(-2e3)}`);
|
|
|
52191
52298
|
}
|
|
52192
52299
|
};
|
|
52193
52300
|
try {
|
|
52194
|
-
|
|
52301
|
+
writeFileSync24(mcpConfigPath, serializeMeshCoordinatorMcpConfig(mcpConfig, configFormat), "utf-8");
|
|
52195
52302
|
} catch (error) {
|
|
52196
52303
|
const message = `Could not write MCP config for automatic setup: ${error?.message || error}`;
|
|
52197
52304
|
LOG.error("MeshCoordinator", message);
|