fix(cli): harden no-daemon teardown

This commit is contained in:
2026-04-29 16:18:27 -04:00
parent d073f2272f
commit a516d80c71
2 changed files with 18 additions and 11 deletions

8
cli.ts
View File

@@ -79,10 +79,10 @@ async function runInProcess(
const result = await runCommand(cmdArg, client, uri, params); const result = await runCommand(cmdArg, client, uri, params);
process.stdout.write(JSON.stringify(result, null, 2) + "\n"); process.stdout.write(JSON.stringify(result, null, 2) + "\n");
} finally { } finally {
// Fire-And-Forget Shutdown - With `gopls -remote=auto` (and similar) // Hard Teardown - The no-daemon path is short-lived/debug-only. Avoid
// the spawned process is a thin client to a background daemon; a // graceful JSON-RPC shutdown because the server stdio stream may already
// graceful shutdown can hang the parent. Kick it off but don't wait. // be closed, which can surface ERR_STREAM_DESTROYED during process exit.
void client.dispose(); void client.dispose({ graceful: false });
} }
} }

View File

@@ -252,15 +252,22 @@ export class LspClient {
} }
// Dispose - Best-effort shutdown; kills the process if it doesn't exit. // Dispose - Best-effort shutdown; kills the process if it doesn't exit.
async dispose(): Promise<void> { async dispose(options: { graceful?: boolean } = {}): Promise<void> {
const graceful = options.graceful ?? true;
if (this.conn) { if (this.conn) {
if (graceful) {
try { try {
await this.conn.sendRequest("shutdown", undefined); await this.conn.sendRequest("shutdown", undefined);
this.conn.sendNotification("exit"); this.conn.sendNotification("exit");
} catch { } catch {
// Ignore - we're tearing down anyway. // Ignore - we're tearing down anyway.
} }
}
try {
this.conn.dispose(); this.conn.dispose();
} catch {
// Ignore - connection may already be closed.
}
} }
if (this.proc && !this.proc.killed) { if (this.proc && !this.proc.killed) {
this.proc.kill(); this.proc.kill();