/** * Resolve an `npm` executable when it is not on PATH. * * OpenCode and Pi are frequently launched from a GUI / dock / Desktop app, * which gives the process a stripped PATH that does NOT include a Node version * manager's bin directory (nvm, mise, volta, fnm, asdf) or even Homebrew. When * that happens, `spawn("npm", ...)` fails with "Executable not found in $PATH", * so the auto-updater and LSP auto-install silently break. See issue: a user's * auto-update churned every launch (rewrite package.json -> delete package -> * npm install fails -> restore) and they stayed pinned to the old version. * * `npm` is itself a Node script (`#!/usr/bin/env node` shebang on Unix), so once * we find npm's absolute path we must also make its sibling `node` reachable, or * the shebang fails the same way. `resolveNpm()` returns both the command and * its bin directory; `npmSpawnEnv()` prepends that directory to PATH for the * spawn so npm can find its own node. */ import { type ChildProcess } from "node:child_process"; export interface ResolvedNpm { /** Absolute path to npm (or a bare name). Pass it through `npmInvocation()`. */ command: string; /** Directory containing npm, prepended to PATH at spawn time so npm's * `#!/usr/bin/env node` shebang can find its sibling node. Null when the * command was found via the OS PATH resolver and no augmentation is needed. */ binDir: string | null; } /** Executable and arguments suitable for Node's child-process APIs. */ export interface NpmInvocation { command: string; args: string[]; /** Environment additions required by the invocation. */ env?: Readonly>; /** Required when passing a fully quoted command line to cmd.exe. */ windowsVerbatimArguments?: boolean; /** True when cmd.exe is wrapping an npm.cmd/npm.bat script. */ windowsCmdShim?: boolean; } interface ResolveNpmDeps { platform: NodeJS.Platform; env: NodeJS.ProcessEnv; home: string; execPath: string; /** * Absolute system bin directories scanned last (e.g. /usr/local/bin). Defaults * to the platform's well-known list. Injectable so tests can pass `[]` to stay * hermetic — otherwise a real system npm (present on CI runners) leaks in and * breaks the "returns null" cases. */ systemNpmDirs?: string[]; } /** * Resolve npm, preferring PATH, then node-adjacent, then well-known version * manager / system locations. Returns null only when npm genuinely cannot be * found anywhere we know to look. */ export declare function resolveNpm(deps?: ResolveNpmDeps): ResolvedNpm | null; /** * Build a cross-platform child-process invocation for a resolved npm command. * * Windows `.cmd`/`.bat` shims are scripts, not native executables, and direct * `spawn()`/`execFileSync()` calls fail with EINVAL. Route only those shims * through cmd.exe; native executables and Unix npm scripts remain direct. */ export declare function npmInvocation(resolved: ResolvedNpm, npmArgs: readonly string[], platform?: NodeJS.Platform, env?: NodeJS.ProcessEnv): NpmInvocation; /** * Terminate an npm child safely. Windows cmd shims create a cmd.exe -> node.exe * tree, so killing only the immediate child can leave npm writing in the * background after a rollback or install-lock release. Resolves after the * immediate child exits, or once Windows tree termination is confirmed at the * grace deadline. Direct children escalate to SIGKILL after a grace period. * Windows tree-kill failures reject as unknown outcomes instead of falling back * to killing cmd.exe alone. */ export declare class NpmTerminationUnknownError extends Error { readonly code = "npm_termination_unknown"; constructor(detail: string); } export declare function terminateNpmProcessTree(child: ChildProcess, invocation: NpmInvocation, env?: NodeJS.ProcessEnv, gracePeriodMs?: number): Promise; /** * Build a spawn env that makes a resolved npm runnable: prepend its bin dir to * PATH so npm's `#!/usr/bin/env node` shebang finds its sibling node, even when * the inherited PATH was stripped by a GUI launch. */ export declare function npmSpawnEnv(resolved: ResolvedNpm, baseEnv?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): NodeJS.ProcessEnv; /** * Quick boolean check: can we run npm at all? Used by pre-flight gating before * destructive auto-update steps. */ export declare function isNpmAvailable(deps?: ResolveNpmDeps): boolean; /** Test seam: verify a resolved npm actually executes (used by diagnostics). */ export declare function probeNpmVersion(resolved: ResolvedNpm): string | null; export {}; //# sourceMappingURL=npm-resolver.d.ts.map