import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; const BUNDLE_CONFIG_PATH = fileURLToPath(new URL("../config/lsp.json", import.meta.url)); const TYPESCRIPT_SERVER_ID = "typescript-language-server"; interface LspConfigFile { version?: number; servers?: Array<{ id?: string; bin?: string; args?: string[]; [key: string]: unknown; }>; } export interface TypeScriptLspRuntime { nodeBinary: string; serverCliPath: string; } export function materializeBundleLspConfig(source: string, runtime: TypeScriptLspRuntime): string { const config = JSON.parse(source) as LspConfigFile; const server = config.servers?.find((entry) => entry.id === TYPESCRIPT_SERVER_ID); if (server === undefined) { throw new Error(`Missing ${TYPESCRIPT_SERVER_ID} entry in bundled LSP config`); } server.bin = runtime.nodeBinary; server.args = [runtime.serverCliPath, "--stdio"]; return `${JSON.stringify(config, null, 2)}\n`; } export function deployBundleLspConfig(agentDir: string, runtime: TypeScriptLspRuntime): void { const targetPath = join(agentDir, "lsp.json"); const bundledConfig = materializeBundleLspConfig(readFileSync(BUNDLE_CONFIG_PATH, "utf8"), runtime); try { if (readFileSync(targetPath, "utf8") === bundledConfig) return; } catch { // Missing or unreadable target: replace it with the bundle-owned baseline. } mkdirSync(dirname(targetPath), { recursive: true }); const temporaryPath = `${targetPath}.my-pi.tmp`; writeFileSync(temporaryPath, bundledConfig, "utf8"); renameSync(temporaryPath, targetPath); }