diff --git a/packages/opencode/src/cli/cmd/tui/thread.ts b/packages/opencode/src/cli/cmd/tui/thread.ts index 1d8821110b..8c4ff52e93 100644 --- a/packages/opencode/src/cli/cmd/tui/thread.ts +++ b/packages/opencode/src/cli/cmd/tui/thread.ts @@ -45,14 +45,14 @@ function createEventSource(client: RpcClient): EventSource { } } -async function workerURL() { +async function target() { if (typeof OPENCODE_WORKER_PATH !== "undefined") return OPENCODE_WORKER_PATH const dist = new URL("./cli/cmd/tui/worker.js", import.meta.url) if (await Filesystem.exists(fileURLToPath(dist))) return dist return new URL("./worker.ts", import.meta.url) } -async function promptText(value?: string) { +async function input(value?: string) { const piped = process.stdin.isTTY ? undefined : await Bun.stdin.text() if (!value) return piped if (!piped) return value @@ -111,9 +111,9 @@ export const TuiThreadCommand = cmd({ } // Resolve relative paths against PWD to preserve behavior when using --cwd flag - const baseCwd = process.env.PWD ?? process.cwd() - const cwd = args.project ? path.resolve(baseCwd, args.project) : process.cwd() - const workerPath = await workerURL() + const root = process.env.PWD ?? process.cwd() + const cwd = args.project ? path.resolve(root, args.project) : process.cwd() + const file = await target() try { process.chdir(cwd) } catch { @@ -121,7 +121,7 @@ export const TuiThreadCommand = cmd({ return } - const worker = new Worker(workerPath, { + const worker = new Worker(file, { env: Object.fromEntries( Object.entries(process.env).filter((entry): entry is [string, string] => entry[1] !== undefined), ), @@ -131,27 +131,27 @@ export const TuiThreadCommand = cmd({ } const client = Rpc.client(worker) - const onError = (e: unknown) => { + const error = (e: unknown) => { Log.Default.error(e) } - const onReload = () => { - client.call("reload", undefined).catch((error) => { + const reload = () => { + client.call("reload", undefined).catch((err) => { Log.Default.warn("worker reload failed", { - error: error instanceof Error ? error.message : String(error), + error: err instanceof Error ? err.message : String(err), }) }) } - process.on("uncaughtException", onError) - process.on("unhandledRejection", onError) - process.on("SIGUSR2", onReload) + process.on("uncaughtException", error) + process.on("unhandledRejection", error) + process.on("SIGUSR2", reload) let stopped = false const stop = async () => { if (stopped) return stopped = true - process.off("uncaughtException", onError) - process.off("unhandledRejection", onError) - process.off("SIGUSR2", onReload) + process.off("uncaughtException", error) + process.off("unhandledRejection", error) + process.off("SIGUSR2", reload) await withTimeout(client.call("shutdown", undefined), 6000).catch((error) => { Log.Default.warn("worker shutdown failed", { error: error instanceof Error ? error.message : String(error), @@ -160,7 +160,7 @@ export const TuiThreadCommand = cmd({ worker.terminate() } - const prompt = await promptText(args.prompt) + const prompt = await input(args.prompt) const config = await Instance.provide({ directory: cwd, fn: () => TuiConfig.get(), diff --git a/packages/opencode/src/mcp/index.ts b/packages/opencode/src/mcp/index.ts index 26e16beb84..54133fa07d 100644 --- a/packages/opencode/src/mcp/index.ts +++ b/packages/opencode/src/mcp/index.ts @@ -27,7 +27,7 @@ import open from "open" export namespace MCP { const log = Log.create({ service: "mcp" }) const DEFAULT_TIMEOUT = 30_000 - const CLOSE_TIMEOUT = 5_000 + const CLOSE = 5_000 export const Resource = z .object({ @@ -183,7 +183,7 @@ export namespace MCP { return pids } - function transportPID(client: MCPClient) { + function proc(client: MCPClient) { const pid = (client.transport as { pid?: number } | undefined)?.pid if (typeof pid === "number") return pid return undefined @@ -198,20 +198,20 @@ export namespace MCP { } } - async function signalDescendants(pid: number, kind: NodeJS.Signals) { + async function tree(pid: number, kind: NodeJS.Signals) { const pids = await descendants(pid) pids.forEach((child) => { signal(child, kind) }) } - async function closeClient(client: MCPClient, key: string, inputPID?: number) { - const pid = inputPID ?? transportPID(client) + async function close(client: MCPClient, key: string, raw?: number) { + const pid = raw ?? proc(client) if (pid !== undefined) { - await signalDescendants(pid, "SIGTERM") + await tree(pid, "SIGTERM") } - const closed = await withTimeout(client.close(), CLOSE_TIMEOUT) + const closed = await withTimeout(client.close(), CLOSE) .then(() => true) .catch((error) => { log.error("Failed to close MCP client", { key, error }) @@ -221,12 +221,12 @@ export namespace MCP { if (closed || pid === undefined) return signal(pid, "SIGTERM") - await signalDescendants(pid, "SIGTERM") + await tree(pid, "SIGTERM") if (process.platform === "win32") return await Bun.sleep(200) signal(pid, "SIGKILL") - await signalDescendants(pid, "SIGKILL") + await tree(pid, "SIGKILL") } const state = Instance.state( @@ -265,7 +265,7 @@ export namespace MCP { } }, async (state) => { - await Promise.all(Object.entries(state.clients).map(([key, client]) => closeClient(client, key))) + await Promise.all(Object.entries(state.clients).map(([key, client]) => close(client, key))) pendingOAuthTransports.clear() }, ) @@ -335,9 +335,9 @@ export namespace MCP { } } // Close existing client if present to prevent memory leaks - const existingClient = s.clients[name] - if (existingClient) { - await closeClient(existingClient, name) + const prev = s.clients[name] + if (prev) { + await close(prev, name) } s.clients[name] = result.mcpClient s.status[name] = result.status @@ -402,14 +402,14 @@ export namespace MCP { ] let lastError: Error | undefined - const connectTimeout = mcp.timeout ?? DEFAULT_TIMEOUT + const timeout = mcp.timeout ?? DEFAULT_TIMEOUT for (const { name, transport } of transports) { try { const client = new Client({ name: "opencode", version: Installation.VERSION, }) - await withTimeout(client.connect(transport), connectTimeout) + await withTimeout(client.connect(transport), timeout) registerNotificationHandlers(client, key) mcpClient = client log.info("connected", { key, transport: name }) @@ -482,20 +482,20 @@ export namespace MCP { log.info(`mcp stderr: ${chunk.toString()}`, { key }) }) - const connectTimeout = mcp.timeout ?? DEFAULT_TIMEOUT + const timeout = mcp.timeout ?? DEFAULT_TIMEOUT const client = new Client({ name: "opencode", version: Installation.VERSION, }) try { - await withTimeout(client.connect(transport), connectTimeout) + await withTimeout(client.connect(transport), timeout) registerNotificationHandlers(client, key) mcpClient = client status = { status: "connected", } } catch (error) { - await closeClient(client, key, (transport as { pid?: number } | undefined)?.pid) + await close(client, key, (transport as { pid?: number } | undefined)?.pid) log.error("local mcp startup failed", { key, command: mcp.command, @@ -528,7 +528,7 @@ export namespace MCP { return undefined }) if (!result) { - await closeClient(mcpClient, key) + await close(mcpClient, key) status = { status: "failed", error: "Failed to get tools", @@ -597,9 +597,9 @@ export namespace MCP { s.status[name] = result.status if (result.mcpClient) { // Close existing client if present to prevent memory leaks - const existingClient = s.clients[name] - if (existingClient) { - await closeClient(existingClient, name) + const prev = s.clients[name] + if (prev) { + await close(prev, name) } s.clients[name] = result.mcpClient } @@ -609,7 +609,7 @@ export namespace MCP { const s = await state() const client = s.clients[name] if (client) { - await closeClient(client, name) + await close(client, name) delete s.clients[name] } s.status[name] = { status: "disabled" }