Skip to content

Commit 6e2375b

Browse files
committed
fix(embed): install @huggingface/transformers into codegraph's host node_modules
`embed`'s lazy install calls `execFileSync('npm', ['install', ...])` with no `cwd`. Default cwd = `process.cwd()`, which is whatever directory the user invoked codegraph from — typically NOT the directory where codegraph itself is installed. The resulting install lands in the user's project node_modules, but the subsequent `await import('@huggingface/transformers')` resolves from codegraph's own location, where the dep is missing. The user sees: [ENGINE_UNAVAILABLE]: @huggingface/transformers was installed but failed to load. Please check your environment. …while their project's node_modules silently gains an unwanted dep. Pin `cwd` to the host directory that contains @optave/codegraph (resolved via `_require.resolve('@optave/codegraph/package.json')` then four `path.dirname` hops). The install now lands in the same node_modules that contains codegraph, so the dynamic import resolves correctly without polluting the user's working directory. Falls back to `cwd: undefined` (= process.cwd()) when @optave/codegraph can't be resolved — typically a source-of-truth dev checkout — so the existing test suite (which mocks the import to fail) continues to pass. Closes #1175
1 parent 4d8df7b commit 6e2375b

1 file changed

Lines changed: 33 additions & 0 deletions

File tree

src/domain/search/models.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,38 @@
11
import { execFileSync } from 'node:child_process';
2+
import { createRequire } from 'node:module';
3+
import path from 'node:path';
24
import { createInterface } from 'node:readline';
35
import { info } from '../../infrastructure/logger.js';
46
import { ConfigError, EngineError } from '../../shared/errors.js';
57

8+
const _require = createRequire(import.meta.url);
9+
10+
/**
11+
* Resolve the directory where `npm install` should run so the installed
12+
* package ends up reachable by `await import(pkg)` from inside this module.
13+
*
14+
* Without a `cwd`, `execFileSync('npm', ['install', ...])` operates on
15+
* `process.cwd()` — when the user runs codegraph against a repo that is *not*
16+
* the directory where codegraph itself is installed, npm installs into the
17+
* wrong `node_modules`, the dynamic import still fails, and the user gets
18+
* `ENGINE_UNAVAILABLE: ... installed but failed to load`.
19+
*
20+
* Pin cwd to the directory that contains @optave/codegraph's `node_modules`
21+
* so the install lands where Node's resolution algorithm will find it.
22+
*/
23+
function resolveNpmInstallCwd(): string | undefined {
24+
try {
25+
const pkgJsonPath = _require.resolve('@optave/codegraph/package.json');
26+
// pkgJsonPath = <host>/node_modules/@optave/codegraph/package.json
27+
// dirname x4: package.json → codegraph → @optave → node_modules → <host>
28+
return path.dirname(path.dirname(path.dirname(path.dirname(pkgJsonPath))));
29+
} catch {
30+
// Source-of-truth checkout (no @optave/codegraph in node_modules) — fall back
31+
// to process.cwd() so legacy behavior survives in tests.
32+
return undefined;
33+
}
34+
}
35+
636
export interface ModelConfig {
737
name: string;
838
dim: number;
@@ -104,12 +134,14 @@ export function getModelConfig(modelKey?: string): ModelConfig {
104134
* @internal Not part of the public barrel.
105135
*/
106136
export function promptInstall(packageName: string): Promise<boolean> {
137+
const installCwd = resolveNpmInstallCwd();
107138
if (!process.stdin.isTTY) {
108139
info(`Installing ${packageName} (optional dependency for semantic search)…`);
109140
try {
110141
execFileSync(NPM_BIN, ['install', '--no-save', packageName], {
111142
stdio: 'inherit',
112143
timeout: 300_000,
144+
cwd: installCwd,
113145
});
114146
return Promise.resolve(true);
115147
} catch (err) {
@@ -131,6 +163,7 @@ export function promptInstall(packageName: string): Promise<boolean> {
131163
execFileSync(NPM_BIN, ['install', packageName], {
132164
stdio: 'inherit',
133165
timeout: 300_000,
166+
cwd: installCwd,
134167
});
135168
resolve(true);
136169
} catch (err) {

0 commit comments

Comments
 (0)