diff --git a/packages/desktop-electron/src/main/index.ts b/packages/desktop-electron/src/main/index.ts index 1799146bda..2d4b2acb13 100644 --- a/packages/desktop-electron/src/main/index.ts +++ b/packages/desktop-electron/src/main/index.ts @@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto" import { EventEmitter } from "node:events" import { existsSync } from "node:fs" import * as nodeHttp from "node:http" +import * as nodeHttps from "node:https" import { homedir } from "node:os" import { join } from "node:path" import type { Event } from "electron" @@ -344,7 +345,7 @@ function wireMenu() { } registerIpcHandlers({ - httpFetch: (input) => bridgedHttpFetch(input, readyWslUrls()), + httpFetch: (input) => bridgedHttpFetch(input), killSidecar: () => killSidecar(), relaunch: () => relaunchApp(), awaitInitialization: async (sendStep) => { @@ -393,12 +394,6 @@ registerIpcHandlers({ setBackgroundColor: (color) => setBackgroundColor(color), }) -function readyWslUrls() { - return wslServers - .getState() - .servers.flatMap((item) => (item.runtime.kind === "ready" ? [item.runtime.url] : [])) -} - function killSidecar() { if (!server) return server.stop() @@ -414,7 +409,7 @@ function relaunchApp() { app.exit(0) } -// Uses node:http directly rather than global fetch (undici). On Windows, +// Uses node http clients directly rather than global fetch (undici). On Windows, // undici pools keep-alive sockets across requests; the WSL2 port proxy // silently drops idle loopback sockets, so reusing one hangs until timeout. // `agent: false` + `Connection: close` forces a fresh TCP connection per @@ -430,7 +425,6 @@ function bridgedHttpFetch( body?: string timeoutMs?: number }, - allowedUrls: string[], ): Promise<{ status: number statusText: string @@ -445,12 +439,8 @@ function bridgedHttpFetch( reject(new Error(`httpFetch: invalid url ${input.url}: ${String(error)}`)) return } - if (parsed.protocol !== "http:") { - reject(new Error(`httpFetch: only http: is supported (got ${parsed.protocol})`)) - return - } - if (!allowedUrls.some((url) => sameOrigin(parsed, url))) { - reject(new Error("httpFetch: url is not an active WSL sidecar")) + if (parsed.protocol !== "http:" && parsed.protocol !== "https:") { + reject(new Error(`httpFetch: only http: and https: are supported (got ${parsed.protocol})`)) return } const method = input.method.toUpperCase() @@ -463,9 +453,9 @@ function bridgedHttpFetch( return } - const req = nodeHttp.request({ + const req = (parsed.protocol === "https:" ? nodeHttps : nodeHttp).request({ host: parsed.hostname, - port: parsed.port ? Number(parsed.port) : 80, + port: parsed.port ? Number(parsed.port) : parsed.protocol === "https:" ? 443 : 80, path: `${parsed.pathname}${parsed.search}`, method, headers: { ...input.headers, connection: "close" }, @@ -517,15 +507,6 @@ function bridgedHttpFetch( }) } -function sameOrigin(input: URL, allowed: string) { - try { - const url = new URL(allowed) - return input.protocol === url.protocol && input.hostname === url.hostname && input.port === url.port - } catch { - return false - } -} - function ensureLoopbackNoProxy() { const loopback = ["127.0.0.1", "localhost", "::1"] const upsert = (key: string) => { diff --git a/packages/desktop-electron/src/main/wsl.ts b/packages/desktop-electron/src/main/wsl.ts index b10084e632..8974e4f78a 100644 --- a/packages/desktop-electron/src/main/wsl.ts +++ b/packages/desktop-electron/src/main/wsl.ts @@ -306,10 +306,11 @@ export async function installWslDistro(name: string, opts?: RunWslOptions) { } export async function installWslOpencode(version: string, distro: string, opts?: RunWslOptions) { - return runWslBash( - `curl -fsSL https://opencode.ai/install | bash -s -- --version ${shellEscape(version)}`, - distro, + return runInteractiveCommand( + resolveSystem32Command("wsl.exe"), + wslArgs(["bash", "-lc", `curl -fsSL https://opencode.ai/install | bash -s -- --version ${shellEscape(version)}`], distro), withTimeout(opts, DEFAULT_WSL_INSTALL_TIMEOUT_MS), + DEFAULT_WSL_INSTALL_TIMEOUT_MS, ) } @@ -421,10 +422,11 @@ export async function readWslCommandVersion(command: string, distro: string, opt } export async function upgradeWslOpencode(target: string, command: string, distro: string, opts?: RunWslOptions) { - return runWslBash( - `${shellEscape(command)} upgrade ${shellEscape(target)}`, - distro, + return runInteractiveCommand( + resolveSystem32Command("wsl.exe"), + wslArgs(["bash", "-lc", `${shellEscape(command)} upgrade ${shellEscape(target)}`], distro), withTimeout(opts, DEFAULT_WSL_INSTALL_TIMEOUT_MS), + DEFAULT_WSL_INSTALL_TIMEOUT_MS, ) }