/** * Orchestrates AFT's npm-based LSP auto-install. * * Flow at plugin startup: * * 1. Resolve already-cached LSP binary directories. Pass these to Rust as * `lsp_paths_extra` so the layered resolver finds them before PATH. * * 2. For each Pattern B/D server whose project is "relevant" (root marker * OR matching extension exists in the bounded project walk) AND not yet cached * AND not in `lsp.disabled`: * * a. If a user pinned a version via `lsp.versions: {"": "X"}`, * use that version directly (skip 7-day grace). * b. Else if cache says we checked recently (< grace_days ago) and * we have a known eligible version, use it. * c. Else probe the npm registry, apply 7-day grace filter. * d. If grace blocks all candidates AND the package is already * installed, log a warning and keep the existing version. * Otherwise skip + warn. * * 3. Spawn `npm install --no-save @ --ignore-scripts` * in the background. Drop a lockfile while running. Log progress. * * 4. The newly-installed binary will be picked up on the user's NEXT * plugin session — first session with auto-install just kicks off * the install. This matches OpenCode's "may need restart" UX and * avoids mid-session bridge restarts. */ import { type AftTransportPool } from "@cortexkit/aft-bridge"; /** Per-call configuration drawn from `lsp.*` plugin config. */ export interface AutoInstallConfig { /** Master enable. Default: true. */ autoInstall: boolean; /** Supply-chain grace window. Default: 7. */ graceDays: number; /** User-pinned versions (bypasses grace). E.g. `{ "pyright": "1.1.300" }`. */ versions: Readonly>; /** Server IDs the user explicitly disabled. Lowercase string match against `NpmServerSpec.id`. */ disabled: ReadonlySet; } /** Result returned to the caller. */ export interface AutoInstallResult { /** Bin directories of every cached install — pass to Rust as `lsp_paths_extra`. */ cachedBinDirs: string[]; /** Number of background installs kicked off. */ installsStarted: number; /** Binary names whose installs are actively in flight at return time. */ installingBinaries: string[]; /** * Servers that were disabled or skipped at decision time (synchronous). * * Note: this only includes synchronous reasons (disabled, irrelevant, * `auto_install: false`). Async reasons (grace blocked, registry probe * failed, install crashed) populate via the `installsComplete` callback * because they're known only after the background work runs. */ skipped: Array<{ id: string; reason: string; }>; /** * Promise that resolves when EVERY backgrounded install settles. Each * install holds its per-package install lock for the entire duration; * concurrent sessions racing into the same package will see the lock * held and back off honestly. * * Plugin startup ignores this; tests await it to assert install outcomes. * Each completed install pushes its skip-reason into `skipped` (mutates * the array shared with the synchronous return value). */ installsComplete: Promise; /** Re-scan the cache after background installs settle. */ getCachedBinDirs: () => string[]; } export declare function abortInFlightAutoInstalls(): Promise; /** * Spawn `npm install --no-save @` in the cache dir. * * Uses `--ignore-scripts` to neutralize lifecycle hooks (the v0.16 audit * hardening). Output goes to plugin log. * * Previously this used `bun add`, but OpenCode runs under whichever runtime * the user has installed (Node 22 is common), and bun is not guaranteed to * be on PATH. Every install would fail silently with ENOENT, so users * without bun saw recurring `lsp_binary_missing` warnings for newer servers * like `@vue/language-server` even though `lsp.auto_install` was true. * GitHub #46 reported this exact symptom against v0.27.0. * * npm is guaranteed to be present whenever the plugin runs through OpenCode's * normal CLI distribution path, matches Pi's auto-install behavior, and is * what OpenCode itself uses for its built-in LSP auto-install. */ /** * Anchor an `npm install --no-save` to `cwd` by writing a minimal package.json. * * Without a package.json in `cwd`, npm walks UP the directory tree; if any * ancestor (e.g. ~/package.json) has one, npm installs into THAT package's * node_modules instead, leaving our cache dir's node_modules/ missing * while still exiting 0. The result is a silent install failure and a recurring * lsp_binary_missing warning. The old `bun add` flow created this package.json * implicitly; npm needs it written explicitly. GitHub #92. * * Idempotent: only writes when absent. Failures are non-fatal (logged). */ export declare function ensureInstallAnchor(cwd: string): void; /** * Top-level entry point. Returns the list of bin directories that already * have an installed binary AND kicks off background installs for missing * packages relevant to this project. * * Caller passes `cachedBinDirs` to Rust as `lsp_paths_extra`. When a background * install settles, the plugin calls `getCachedBinDirs()` to refresh the list and * reconfigures live bridges; future bridges use the pool override. */ export declare function runAutoInstall(projectRoot: string, config: AutoInstallConfig, fetchImpl?: typeof fetch): AutoInstallResult; /** Apply newly discovered cache paths to both future and live bridge config. */ export declare function pushLspPathsAfterAutoInstall(pool: Pick, projectRoot: string, cachedBinDirs: readonly string[]): Promise; //# sourceMappingURL=lsp-auto-install.d.ts.map