@adhdev/daemon-core 0.9.82-rc.317 → 0.9.82-rc.318
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/index.js +538 -510
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +549 -521
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/cli-adapters/resolve-executable.ts +46 -1
- package/src/detection/cli-detector.ts +28 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adhdev/daemon-core",
|
|
3
|
-
"version": "0.9.82-rc.
|
|
3
|
+
"version": "0.9.82-rc.318",
|
|
4
4
|
"description": "ADHDev daemon core — CDP, IDE detection, providers, command execution",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"author": "vilmire",
|
|
47
47
|
"license": "AGPL-3.0-or-later",
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@adhdev/mesh-shared": "0.9.82-rc.
|
|
49
|
+
"@adhdev/mesh-shared": "0.9.82-rc.318",
|
|
50
50
|
"@adhdev/session-host-core": "*",
|
|
51
51
|
"@agentclientprotocol/sdk": "^0.16.1",
|
|
52
52
|
"ajv": "^8.20.0",
|
|
@@ -6,6 +6,42 @@ import * as path from 'path';
|
|
|
6
6
|
// need a cmd.exe wrapper).
|
|
7
7
|
const DIRECT_EXEC_EXT = new Set(['.exe', '.com']);
|
|
8
8
|
|
|
9
|
+
// Executable extensions to probe when scanning a directory ourselves, ordered
|
|
10
|
+
// most-directly-launchable first. node-pty's ConPTY backend launches an
|
|
11
|
+
// absolute `.cmd`/`.bat` shim fine (verified) — it only fails to *resolve* a
|
|
12
|
+
// bare command against an incomplete PATH — so once we hand it an absolute
|
|
13
|
+
// path, a `.cmd` shim works just as well as a real `.exe`.
|
|
14
|
+
const WIN_EXEC_EXT = ['.exe', '.com', '.cmd', '.bat'];
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Resolve a bare command against well-known global-bin directories that are
|
|
18
|
+
* frequently NOT on the daemon's inherited PATH, so `where` (which only
|
|
19
|
+
* searches PATH) misses them. A daemon running under one Node install (e.g.
|
|
20
|
+
* nvm) never sees another npm prefix's bin dir — notably npm's Windows default
|
|
21
|
+
* prefix at %APPDATA%\npm, where `npm i -g @openai/codex` lands. Returns an
|
|
22
|
+
* absolute path on the first hit, or null. Mirrors findBinary()'s extraDirs in
|
|
23
|
+
* provider-cli-shared.ts; kept inline here so this lightweight module (loaded
|
|
24
|
+
* by pty-transport) need not pull in the heavier shared module.
|
|
25
|
+
*/
|
|
26
|
+
function resolveWin32GlobalBin(trimmed: string): string | null {
|
|
27
|
+
// Only resolve a bare command name — anything with a path separator is the
|
|
28
|
+
// caller's explicit location and must not be re-pointed at a global bin dir.
|
|
29
|
+
if (path.isAbsolute(trimmed) || trimmed.includes('/') || trimmed.includes('\\')) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
const extraDirs: string[] = [];
|
|
33
|
+
if (process.env.APPDATA) extraDirs.push(path.join(process.env.APPDATA, 'npm'));
|
|
34
|
+
try { extraDirs.push(path.dirname(process.execPath)); } catch { /* best-effort */ }
|
|
35
|
+
for (const dir of extraDirs) {
|
|
36
|
+
if (!dir) continue;
|
|
37
|
+
for (const ext of WIN_EXEC_EXT) {
|
|
38
|
+
const full = path.join(dir, trimmed + ext);
|
|
39
|
+
if (existsSync(full)) return full;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
9
45
|
/**
|
|
10
46
|
* Resolve a launch command to an absolute executable path on Windows.
|
|
11
47
|
*
|
|
@@ -39,7 +75,16 @@ export function resolveWin32Executable(command: string): string {
|
|
|
39
75
|
return direct || matches[0] || command;
|
|
40
76
|
}
|
|
41
77
|
} catch {
|
|
42
|
-
// `where` not found / non-zero exit — fall through to
|
|
78
|
+
// `where` not found / non-zero exit — fall through to the global-bin scan.
|
|
43
79
|
}
|
|
80
|
+
|
|
81
|
+
// `where` found nothing on PATH. Before giving up (and letting node-pty crash
|
|
82
|
+
// with "File not found:" on the bare command), search npm's off-PATH global
|
|
83
|
+
// bin dir(s). This is the codex case: `npm i -g @openai/codex` installs to
|
|
84
|
+
// %APPDATA%\npm, which is absent from a nvm-launched daemon's PATH, so a spec
|
|
85
|
+
// binary of "codex" never resolved and the spawn ENOENT'd.
|
|
86
|
+
const globalBin = resolveWin32GlobalBin(trimmed);
|
|
87
|
+
if (globalBin) return globalBin;
|
|
88
|
+
|
|
44
89
|
return command;
|
|
45
90
|
}
|
|
@@ -12,6 +12,7 @@ import * as os from 'os';
|
|
|
12
12
|
import * as path from 'path';
|
|
13
13
|
import { existsSync } from 'fs';
|
|
14
14
|
import type { ProviderLoader } from '../providers/provider-loader.js';
|
|
15
|
+
import { findBinary } from '../cli-adapters/provider-cli-shared.js';
|
|
15
16
|
|
|
16
17
|
export interface CLIInfo {
|
|
17
18
|
id: string;
|
|
@@ -57,6 +58,29 @@ function resolveCommandPath(command: string): string | null {
|
|
|
57
58
|
return null;
|
|
58
59
|
}
|
|
59
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Resolve a CLI command to an absolute path for install detection.
|
|
63
|
+
* Order: explicit path → PATH (`where`/`which`) → well-known global-bin dirs
|
|
64
|
+
* (e.g. %APPDATA%\npm) via the shared spawn-layer findBinary.
|
|
65
|
+
*
|
|
66
|
+
* The final step keeps detection consistent with the spawn layer: findBinary
|
|
67
|
+
* already searches npm's default Windows prefix at %APPDATA%\npm (where
|
|
68
|
+
* `npm i -g @openai/codex` lands), so a CLI installed under an npm prefix that
|
|
69
|
+
* is NOT on the daemon's inherited PATH is detected as installed instead of
|
|
70
|
+
* being blocked at the launch gate. findBinary returns a bare "<name>.cmd"
|
|
71
|
+
* (non-absolute) when nothing is found, so we only accept an absolute path
|
|
72
|
+
* that actually exists.
|
|
73
|
+
*/
|
|
74
|
+
async function resolveDetectionPath(command: string, whichCmd: string): Promise<string | null> {
|
|
75
|
+
const explicitPath = resolveCommandPath(command);
|
|
76
|
+
if (explicitPath) return explicitPath;
|
|
77
|
+
const whichResult = await execAsync(`${whichCmd} ${shellQuote(command)}`);
|
|
78
|
+
if (whichResult) return whichResult.split('\n')[0];
|
|
79
|
+
const resolved = findBinary(command);
|
|
80
|
+
if (path.isAbsolute(resolved) && existsSync(resolved)) return resolved;
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
|
|
60
84
|
/** Run a shell command with timeout, returning stdout or null on failure */
|
|
61
85
|
function execAsync(cmd: string, timeoutMs = 5000): Promise<string | null> {
|
|
62
86
|
return new Promise((resolve) => {
|
|
@@ -97,11 +121,8 @@ export async function detectCLIs(
|
|
|
97
121
|
const results = await Promise.all(
|
|
98
122
|
cliList.map(async (cli): Promise<CLIInfo> => {
|
|
99
123
|
try {
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
if (!pathResult) return { ...cli, installed: false };
|
|
103
|
-
|
|
104
|
-
const firstPath = explicitPath || pathResult.split('\n')[0];
|
|
124
|
+
const firstPath = await resolveDetectionPath(cli.command, whichCmd);
|
|
125
|
+
if (!firstPath) return { ...cli, installed: false };
|
|
105
126
|
|
|
106
127
|
// Get version (parallel with other checks)
|
|
107
128
|
let version: string | undefined;
|
|
@@ -148,10 +169,8 @@ export async function detectCLI(
|
|
|
148
169
|
const platform = os.platform();
|
|
149
170
|
const whichCmd = platform === 'win32' ? 'where' : 'which';
|
|
150
171
|
try {
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
if (!pathResult) return null;
|
|
154
|
-
const firstPath = explicitPath || pathResult.split('\n')[0];
|
|
172
|
+
const firstPath = await resolveDetectionPath(target.command, whichCmd);
|
|
173
|
+
if (!firstPath) return null;
|
|
155
174
|
let version: string | undefined;
|
|
156
175
|
if (options?.includeVersion !== false) {
|
|
157
176
|
const versionCommands = [
|