@adhdev/daemon-core 0.9.2 → 0.9.4

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/session-host-core",
3
- "version": "0.9.2",
3
+ "version": "0.9.4",
4
4
  "description": "ADHDev local session host core \u2014 session registry, protocol, buffers",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-core",
3
- "version": "0.9.2",
3
+ "version": "0.9.4",
4
4
  "description": "ADHDev daemon core \u2014 CDP, IDE detection, providers, command execution",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1962,11 +1962,6 @@ export class ProviderCliAdapter implements CliAdapter {
1962
1962
  throw new Error(`${this.cliName} is still processing the previous prompt`);
1963
1963
  }
1964
1964
  }
1965
- const blockingModal = this.activeModal || this.getStartupConfirmationModal(this.terminalScreen.getText() || '');
1966
- if (blockingModal || this.currentStatus === 'waiting_approval') {
1967
- throw new Error(`${this.cliName} is awaiting confirmation before it can accept a prompt`);
1968
- }
1969
-
1970
1965
  this.isWaitingForResponse = true;
1971
1966
  this.responseBuffer = '';
1972
1967
  this.finishRetryCount = 0;
@@ -15,6 +15,18 @@ export interface DaemonUpgradeHelperPayload {
15
15
  sessionHostAppName?: string;
16
16
  }
17
17
 
18
+ export interface CurrentGlobalInstallSurface {
19
+ npmExecutable: string;
20
+ packageRoot: string | null;
21
+ installPrefix: string | null;
22
+ }
23
+
24
+ export interface PinnedGlobalInstallCommand {
25
+ command: string;
26
+ args: string[];
27
+ surface: CurrentGlobalInstallSurface;
28
+ }
29
+
18
30
  function getUpgradeLogPath(): string {
19
31
  const home = os.homedir();
20
32
  const dir = path.join(home, '.adhdev');
@@ -31,10 +43,107 @@ function appendUpgradeLog(message: string): void {
31
43
  }
32
44
  }
33
45
 
34
- function getNpmExecutable(): string {
46
+ function resolveSiblingNpmExecutable(nodeExecutable: string): string {
47
+ const binDir = path.dirname(nodeExecutable);
48
+ const candidates = process.platform === 'win32'
49
+ ? ['npm.cmd', 'npm.exe', 'npm']
50
+ : ['npm'];
51
+ for (const candidate of candidates) {
52
+ const candidatePath = path.join(binDir, candidate);
53
+ if (fs.existsSync(candidatePath)) {
54
+ return candidatePath;
55
+ }
56
+ }
35
57
  return 'npm';
36
58
  }
37
59
 
60
+ function findCurrentPackageRoot(currentCliPath: string | undefined, packageName: string): string | null {
61
+ if (!currentCliPath) return null;
62
+
63
+ let resolvedPath = currentCliPath;
64
+ try {
65
+ resolvedPath = fs.realpathSync.native(currentCliPath);
66
+ } catch {
67
+ // keep the original path when realpath is unavailable
68
+ }
69
+
70
+ let currentDir = resolvedPath;
71
+ try {
72
+ if (fs.statSync(resolvedPath).isFile()) {
73
+ currentDir = path.dirname(resolvedPath);
74
+ }
75
+ } catch {
76
+ currentDir = path.dirname(resolvedPath);
77
+ }
78
+
79
+ while (true) {
80
+ const packageJsonPath = path.join(currentDir, 'package.json');
81
+ try {
82
+ if (fs.existsSync(packageJsonPath)) {
83
+ const parsed = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
84
+ if (parsed?.name === packageName) {
85
+ const normalized = currentDir.replace(/\\/g, '/');
86
+ return normalized.includes('/node_modules/') ? currentDir : null;
87
+ }
88
+ }
89
+ } catch {
90
+ // ignore malformed package metadata while scanning upward
91
+ }
92
+
93
+ const parentDir = path.dirname(currentDir);
94
+ if (parentDir === currentDir) {
95
+ return null;
96
+ }
97
+ currentDir = parentDir;
98
+ }
99
+ }
100
+
101
+ function resolveInstallPrefixFromPackageRoot(packageRoot: string, packageName: string): string | null {
102
+ const nodeModulesDir = packageName.startsWith('@')
103
+ ? path.dirname(path.dirname(packageRoot))
104
+ : path.dirname(packageRoot);
105
+ if (path.basename(nodeModulesDir) !== 'node_modules') {
106
+ return null;
107
+ }
108
+
109
+ const maybeLibDir = path.dirname(nodeModulesDir);
110
+ if (path.basename(maybeLibDir) === 'lib') {
111
+ return path.dirname(maybeLibDir);
112
+ }
113
+ return maybeLibDir;
114
+ }
115
+
116
+ export function resolveCurrentGlobalInstallSurface(options: {
117
+ packageName: string;
118
+ currentCliPath?: string;
119
+ nodeExecutable?: string;
120
+ }): CurrentGlobalInstallSurface {
121
+ const packageRoot = findCurrentPackageRoot(options.currentCliPath || process.argv[1], options.packageName);
122
+ return {
123
+ npmExecutable: resolveSiblingNpmExecutable(options.nodeExecutable || process.execPath),
124
+ packageRoot,
125
+ installPrefix: packageRoot ? resolveInstallPrefixFromPackageRoot(packageRoot, options.packageName) : null,
126
+ };
127
+ }
128
+
129
+ export function buildPinnedGlobalInstallCommand(options: {
130
+ packageName: string;
131
+ targetVersion: string;
132
+ currentCliPath?: string;
133
+ nodeExecutable?: string;
134
+ }): PinnedGlobalInstallCommand {
135
+ const surface = resolveCurrentGlobalInstallSurface(options);
136
+ const args = ['install', '-g', `${options.packageName}@${options.targetVersion || 'latest'}`, '--force'];
137
+ if (surface.installPrefix) {
138
+ args.push('--prefix', surface.installPrefix);
139
+ }
140
+ return {
141
+ command: surface.npmExecutable,
142
+ args,
143
+ surface,
144
+ };
145
+ }
146
+
38
147
  function getNpmExecOptions(): { shell: boolean } {
39
148
  return { shell: process.platform === 'win32' };
40
149
  }
@@ -107,11 +216,13 @@ function removeDaemonPidFile(): void {
107
216
  }
108
217
  }
109
218
 
110
- function cleanupStaleGlobalInstallDirs(pkgName: string): void {
219
+ function cleanupStaleGlobalInstallDirs(pkgName: string, surface: CurrentGlobalInstallSurface): void {
111
220
  const npmExecOpts = getNpmExecOptions();
112
- const npmRoot = execFileSync(getNpmExecutable(), ['root', '-g'], { encoding: 'utf8', ...npmExecOpts }).trim();
221
+ const prefixArgs = surface.installPrefix ? ['--prefix', surface.installPrefix] : [];
222
+ const npmRoot = execFileSync(surface.npmExecutable, ['root', '-g', ...prefixArgs], { encoding: 'utf8', ...npmExecOpts }).trim();
113
223
  if (!npmRoot) return;
114
- const npmPrefix = execFileSync(getNpmExecutable(), ['prefix', '-g'], { encoding: 'utf8', ...npmExecOpts }).trim();
224
+ const npmPrefix = surface.installPrefix
225
+ || execFileSync(surface.npmExecutable, ['prefix', '-g', ...prefixArgs], { encoding: 'utf8', ...npmExecOpts }).trim();
115
226
  const binDir = process.platform === 'win32' ? npmPrefix : path.join(npmPrefix, 'bin');
116
227
  const packageBaseName = pkgName.startsWith('@') ? pkgName.split('/')[1] : pkgName;
117
228
  const binNames = new Set<string>([packageBaseName]);
@@ -138,7 +249,7 @@ function cleanupStaleGlobalInstallDirs(pkgName: string): void {
138
249
 
139
250
  if (fs.existsSync(binDir)) {
140
251
  for (const entry of fs.readdirSync(binDir)) {
141
- if (![...binNames].some((name) => entry.startsWith(`.${name}-`))) continue;
252
+ if (!Array.from(binNames).some((name) => entry.startsWith(`.${name}-`))) continue;
142
253
  fs.rmSync(path.join(binDir, entry), { recursive: true, force: true });
143
254
  appendUpgradeLog(`Removed stale bin staging entry: ${path.join(binDir, entry)}`);
144
255
  }
@@ -160,7 +271,15 @@ export function spawnDetachedDaemonUpgradeHelper(payload: DaemonUpgradeHelperPay
160
271
  async function runDaemonUpgradeHelper(payload: DaemonUpgradeHelperPayload): Promise<void> {
161
272
  const restartArgv = Array.isArray(payload.restartArgv) ? payload.restartArgv : [];
162
273
  const sessionHostAppName = payload.sessionHostAppName || process.env.ADHDEV_SESSION_HOST_NAME || 'adhdev';
274
+ const installCommand = buildPinnedGlobalInstallCommand({
275
+ packageName: payload.packageName,
276
+ targetVersion: payload.targetVersion,
277
+ });
163
278
  appendUpgradeLog(`Upgrade helper started for ${payload.packageName}@${payload.targetVersion}`);
279
+ appendUpgradeLog(`Using npm executable: ${installCommand.command}`);
280
+ if (installCommand.surface.installPrefix) {
281
+ appendUpgradeLog(`Pinned install prefix: ${installCommand.surface.installPrefix}`);
282
+ }
164
283
 
165
284
  if (Number.isFinite(payload.parentPid) && payload.parentPid > 0) {
166
285
  appendUpgradeLog(`Waiting for parent pid ${payload.parentPid} to exit`);
@@ -169,13 +288,13 @@ async function runDaemonUpgradeHelper(payload: DaemonUpgradeHelperPayload): Prom
169
288
 
170
289
  stopSessionHostProcesses(sessionHostAppName);
171
290
  removeDaemonPidFile();
172
- cleanupStaleGlobalInstallDirs(payload.packageName);
291
+ cleanupStaleGlobalInstallDirs(payload.packageName, installCommand.surface);
173
292
 
174
293
  const spec = `${payload.packageName}@${payload.targetVersion || 'latest'}`;
175
294
  appendUpgradeLog(`Installing ${spec}`);
176
295
  const installOutput = execFileSync(
177
- getNpmExecutable(),
178
- ['install', '-g', spec, '--force'],
296
+ installCommand.command,
297
+ installCommand.args,
179
298
  {
180
299
  encoding: 'utf8',
181
300
  stdio: 'pipe',
@@ -192,7 +311,7 @@ async function runDaemonUpgradeHelper(payload: DaemonUpgradeHelperPayload): Prom
192
311
  // processes have exited.
193
312
  if (process.platform === 'win32') {
194
313
  await new Promise((resolve) => setTimeout(resolve, 500));
195
- cleanupStaleGlobalInstallDirs(payload.packageName);
314
+ cleanupStaleGlobalInstallDirs(payload.packageName, installCommand.surface);
196
315
  appendUpgradeLog('Post-install staging cleanup complete');
197
316
  }
198
317
 
package/src/index.d.ts CHANGED
@@ -41,8 +41,8 @@ export { DaemonCommandHandler } from './commands/handler.js';
41
41
  export type { CommandResult, CommandContext } from './commands/handler.js';
42
42
  export { DaemonCommandRouter } from './commands/router.js';
43
43
  export type { CommandRouterDeps, CommandRouterResult } from './commands/router.js';
44
- export { maybeRunDaemonUpgradeHelperFromEnv, spawnDetachedDaemonUpgradeHelper } from './commands/upgrade-helper.js';
45
- export type { DaemonUpgradeHelperPayload } from './commands/upgrade-helper.js';
44
+ export { maybeRunDaemonUpgradeHelperFromEnv, spawnDetachedDaemonUpgradeHelper, resolveCurrentGlobalInstallSurface, buildPinnedGlobalInstallCommand } from './commands/upgrade-helper.js';
45
+ export type { DaemonUpgradeHelperPayload, CurrentGlobalInstallSurface, PinnedGlobalInstallCommand } from './commands/upgrade-helper.js';
46
46
  export { DaemonStatusReporter } from './status/reporter.js';
47
47
  export { buildSessionEntries, findCdpManager, hasCdpManager, isCdpConnected } from './status/builders.js';
48
48
  export { buildStatusSnapshot, buildMachineInfo } from './status/snapshot.js';
package/src/index.ts CHANGED
@@ -137,8 +137,17 @@ export { DaemonCommandHandler } from './commands/handler.js';
137
137
  export type { CommandResult, CommandContext } from './commands/handler.js';
138
138
  export { DaemonCommandRouter } from './commands/router.js';
139
139
  export type { CommandRouterDeps, CommandRouterResult } from './commands/router.js';
140
- export { maybeRunDaemonUpgradeHelperFromEnv, spawnDetachedDaemonUpgradeHelper } from './commands/upgrade-helper.js';
141
- export type { DaemonUpgradeHelperPayload } from './commands/upgrade-helper.js';
140
+ export {
141
+ maybeRunDaemonUpgradeHelperFromEnv,
142
+ spawnDetachedDaemonUpgradeHelper,
143
+ resolveCurrentGlobalInstallSurface,
144
+ buildPinnedGlobalInstallCommand,
145
+ } from './commands/upgrade-helper.js';
146
+ export type {
147
+ DaemonUpgradeHelperPayload,
148
+ CurrentGlobalInstallSurface,
149
+ PinnedGlobalInstallCommand,
150
+ } from './commands/upgrade-helper.js';
142
151
 
143
152
  // ── Status ──
144
153
  export { DaemonStatusReporter } from './status/reporter.js';