diff --git a/bun.lock b/bun.lock index 1e431773f..17fef2836 100644 --- a/bun.lock +++ b/bun.lock @@ -55,7 +55,7 @@ "cordova": "13.0.0", "core-js": "^3.47.0", "dayjs": "^1.11.19", - "dompurify": "^3.3.2", + "dompurify": "^3.4.0", "escape-string-regexp": "^5.0.0", "esprima": "^4.0.1", "filesize": "^11.0.13", @@ -1177,7 +1177,7 @@ "domhandler": ["domhandler@2.3.0", "", { "dependencies": { "domelementtype": "1" } }, "sha512-q9bUwjfp7Eif8jWxxxPSykdRZAb6GkguBGSgvvCrhI9wB71W2K/Kvv4E61CF/mcCfnVJDeDWx/Vb/uAqbDj6UQ=="], - "dompurify": ["dompurify@3.3.3", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA=="], + "dompurify": ["dompurify@3.4.1", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-JahakDAIg1gyOm7dlgWSDjV4n7Ip2PKR55NIT6jrMfIgLFgWo81vdr1/QGqWtFNRqXP9UV71oVePtjqS2ebnPw=="], "domutils": ["domutils@1.5.1", "", { "dependencies": { "dom-serializer": "0", "domelementtype": "1" } }, "sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw=="], @@ -2171,6 +2171,8 @@ "make-fetch-happen/negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], + "mermaid/dompurify": ["dompurify@3.3.3", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA=="], + "mermaid/marked": ["marked@16.4.2", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA=="], "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], diff --git a/src/cm/lsp/serverLauncher.ts b/src/cm/lsp/serverLauncher.ts index 5b00c077a..1350a1ff3 100644 --- a/src/cm/lsp/serverLauncher.ts +++ b/src/cm/lsp/serverLauncher.ts @@ -77,12 +77,13 @@ export { formatCommand } from "./installRuntime"; let cachedFilesDir: string | null = null; /** - * Get the terminal home directory from system.getFilesDir(). - * This is where axs stores port files. + * Get candidate Terminal data directories from system.getFilesDir(). + * Newer Terminal builds keep shared runtime state in public. Older builds used + * alpine/home, and some installs keep it as a symlink for shell compatibility. */ -async function getTerminalHomeDir(): Promise { +async function getTerminalDataDirs(): Promise { if (cachedFilesDir) { - return `${cachedFilesDir}/alpine/home`; + return [`${cachedFilesDir}/public`, `${cachedFilesDir}/alpine/home`]; } const system = ( @@ -104,7 +105,7 @@ async function getTerminalHomeDir(): Promise { system.getFilesDir( (filesDir: string) => { cachedFilesDir = filesDir; - resolve(`${filesDir}/alpine/home`); + resolve([`${filesDir}/public`, `${filesDir}/alpine/home`]); }, (error: string) => reject(new Error(error)), ); @@ -115,14 +116,16 @@ async function getTerminalHomeDir(): Promise { * Get the port file path for a given server and session. * Port file format: ~/.axs/lsp_ports/{serverName}_{session} */ -async function getPortFilePath( +async function getPortFilePaths( serverName: string, session: string, -): Promise { - const homeDir = await getTerminalHomeDir(); +): Promise { + const dataDirs = await getTerminalDataDirs(); // Use just the binary name (not full path), mirroring axs behavior const baseName = serverName.split("/").pop() || serverName; - return `file://${homeDir}/.axs/lsp_ports/${baseName}_${session}`; + return dataDirs.map( + (dataDir) => `file://${dataDir}/.axs/lsp_ports/${baseName}_${session}`, + ); } /** @@ -166,14 +169,16 @@ export async function getLspPort( session: string, ): Promise { try { - const filePath = await getPortFilePath(serverName, session); - const port = await readPortFromFile(filePath); + const filePaths = await getPortFilePaths(serverName, session); - if (port === null) { - return null; + for (const filePath of filePaths) { + const port = await readPortFromFile(filePath); + if (port !== null) { + return { port, filePath, session }; + } } - return { port, filePath, session }; + return null; } catch { return null; } @@ -1094,6 +1099,18 @@ export async function ensureServerRunning( const key = server.id; if (managedServers.has(key)) { const existing = managedServers.get(key); + if (bridge && !bridge.port) { + if (existing?.port) { + return { uuid: existing.uuid, discoveredPort: existing.port }; + } + const portInfo = await getLspPort(serverName, effectiveSession); + if (portInfo) { + if (existing) { + existing.port = portInfo.port; + } + return { uuid: existing?.uuid ?? null, discoveredPort: portInfo.port }; + } + } return { uuid: existing?.uuid ?? null }; } @@ -1126,6 +1143,11 @@ export async function ensureServerRunning( entry.port = discoveredPort; } } + if (!discoveredPort) { + throw new Error( + `Could not discover websocket bridge port for ${server.id}`, + ); + } } else if ( server.transport?.url && (server.transport.kind === "websocket" ||