edsger 0.72.4 → 0.72.5

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,5 +1,37 @@
1
1
  import { loadConfig } from '../config.js';
2
2
  import { type CliOptions } from '../types/index.js';
3
+ /**
4
+ * Determine the correct npm install target for optional dependencies.
5
+ *
6
+ * `Function('return import("pkg")')()` resolves from the edsger module's
7
+ * own location, walking up the node_modules chain. We need to install
8
+ * packages into a directory that sits on that resolution path.
9
+ *
10
+ * - Source checkout / monorepo → `npm install` in the edsger package dir
11
+ * - Project dependency → `npm install` in the project root (has package.json)
12
+ * - Global-style install → `npm install -g --prefix <prefix>`
13
+ *
14
+ * The global-style case covers both a plain `npm install -g edsger` and the
15
+ * desktop app's managed install (`npm install -g edsger --prefix <userData>/cli`).
16
+ * In both, edsger lives at `<prefix>/lib/node_modules/edsger` (or
17
+ * `<prefix>/node_modules/edsger` on Windows) and there is NO package.json at
18
+ * the directory that owns that node_modules. Running a *project-local*
19
+ * `npm install` there would treat every already-installed package — including
20
+ * edsger and all its dependencies — as extraneous and PRUNE them, destroying
21
+ * the install. We must install with `--prefix` into the same prefix instead.
22
+ */
23
+ export interface NpmInstallTarget {
24
+ global: boolean;
25
+ cwd?: string;
26
+ prefix?: string;
27
+ }
28
+ /**
29
+ * Pure resolution of the npm install target, given edsger's own directory and
30
+ * a predicate for testing whether a path exists. Separated from
31
+ * {@link getNpmInstallTarget} so it can be unit-tested without touching the
32
+ * real filesystem or `import.meta.url`.
33
+ */
34
+ export declare function resolveNpmInstallTarget(edsgerDir: string, fileExists: (p: string) => boolean): NpmInstallTarget;
3
35
  export interface ValidationResult {
4
36
  config: ReturnType<typeof loadConfig>;
5
37
  mcpServerUrl: string;
@@ -1,9 +1,37 @@
1
1
  import { execSync, spawnSync } from 'child_process';
2
- import { dirname, resolve, sep } from 'path';
2
+ import { existsSync } from 'fs';
3
+ import { basename, dirname, join, resolve, sep } from 'path';
3
4
  import { fileURLToPath } from 'url';
4
5
  import { getMcpServerUrl, getMcpToken } from '../auth/auth-store.js';
5
6
  import { loadConfig, validateConfig } from '../config.js';
6
7
  import { logInfo, logWarning } from './logger.js';
8
+ /**
9
+ * Pure resolution of the npm install target, given edsger's own directory and
10
+ * a predicate for testing whether a path exists. Separated from
11
+ * {@link getNpmInstallTarget} so it can be unit-tested without touching the
12
+ * real filesystem or `import.meta.url`.
13
+ */
14
+ export function resolveNpmInstallTarget(edsgerDir, fileExists) {
15
+ const nodeModulesSegment = `${sep}node_modules${sep}`;
16
+ if (!edsgerDir.includes(nodeModulesSegment)) {
17
+ // Source checkout / monorepo — install in edsger's own package dir
18
+ return { global: false, cwd: edsgerDir };
19
+ }
20
+ // edsger is inside some node_modules tree. The directory that owns the
21
+ // outermost node_modules containing edsger is the candidate install root.
22
+ const installRoot = edsgerDir.split(nodeModulesSegment)[0];
23
+ // A real project dependency has a package.json at that root, so a
24
+ // project-local install safely adds to its dependencies without pruning.
25
+ if (fileExists(join(installRoot, 'package.json'))) {
26
+ return { global: false, cwd: installRoot };
27
+ }
28
+ // No package.json → global-style install. Derive the npm prefix so the
29
+ // package lands on edsger's resolution path. Unix global installs nest
30
+ // under `<prefix>/lib`; Windows installs put node_modules directly under
31
+ // `<prefix>`.
32
+ const prefix = basename(installRoot) === 'lib' ? dirname(installRoot) : installRoot;
33
+ return { global: true, prefix };
34
+ }
7
35
  /**
8
36
  * Determine the correct npm install target for optional dependencies.
9
37
  *
@@ -11,33 +39,22 @@ import { logInfo, logWarning } from './logger.js';
11
39
  * own location, walking up the node_modules chain. We need to install
12
40
  * packages into a directory that sits on that resolution path.
13
41
  *
14
- * - Global install → `npm install -g`
15
- * - Project dependency → `npm install` in the project root
16
- * - Monorepo / development → `npm install` in the edsger package dir
42
+ * - Source checkout / monorepo → `npm install` in the edsger package dir
43
+ * - Project dependency → `npm install` in the project root (has package.json)
44
+ * - Global-style install → `npm install -g --prefix <prefix>`
45
+ *
46
+ * The global-style case covers both a plain `npm install -g edsger` and the
47
+ * desktop app's managed install (`npm install -g edsger --prefix <userData>/cli`).
48
+ * In both, edsger lives at `<prefix>/lib/node_modules/edsger` (or
49
+ * `<prefix>/node_modules/edsger` on Windows) and there is NO package.json at
50
+ * the directory that owns that node_modules. Running a *project-local*
51
+ * `npm install` there would treat every already-installed package — including
52
+ * edsger and all its dependencies — as extraneous and PRUNE them, destroying
53
+ * the install. We must install with `--prefix` into the same prefix instead.
17
54
  */
18
55
  function getNpmInstallTarget() {
19
56
  const edsgerDir = resolve(dirname(fileURLToPath(import.meta.url)), '../..');
20
- const nodeModulesSegment = `${sep}node_modules${sep}`;
21
- if (!edsgerDir.includes(nodeModulesSegment)) {
22
- // Source checkout / monorepo — install in edsger's own package dir
23
- return { global: false, cwd: edsgerDir };
24
- }
25
- // edsger is inside some node_modules tree
26
- try {
27
- const globalPrefix = execSync('npm config get prefix', {
28
- encoding: 'utf-8',
29
- stdio: ['pipe', 'pipe', 'ignore'],
30
- }).trim();
31
- if (edsgerDir.startsWith(globalPrefix)) {
32
- return { global: true };
33
- }
34
- }
35
- catch {
36
- // ignore — fall through to project-local
37
- }
38
- // Project dependency — install at the project root (parent of node_modules)
39
- const projectRoot = edsgerDir.split(nodeModulesSegment)[0];
40
- return { global: false, cwd: projectRoot };
57
+ return resolveNpmInstallTarget(edsgerDir, existsSync);
41
58
  }
42
59
  /**
43
60
  * Common configuration validation for all CLI commands
@@ -106,7 +123,8 @@ const restartProcess = (envFlag) => {
106
123
  function npmInstall(packageName) {
107
124
  const target = getNpmInstallTarget();
108
125
  const globalFlag = target.global ? ' -g' : '';
109
- const cmd = `npm install${globalFlag} ${packageName}`;
126
+ const prefixFlag = target.prefix ? ` --prefix "${target.prefix}"` : '';
127
+ const cmd = `npm install${globalFlag}${prefixFlag} ${packageName}`;
110
128
  logInfo(` $ ${cmd}${target.cwd ? ` (in ${target.cwd})` : ''}`);
111
129
  execSync(cmd, { cwd: target.cwd, stdio: 'inherit' });
112
130
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "edsger",
3
- "version": "0.72.4",
3
+ "version": "0.72.5",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "edsger": "dist/index.js"