diff --git a/.changeset/fix-vscode-server-auth.md b/.changeset/fix-vscode-server-auth.md new file mode 100644 index 0000000000..0b3d11a994 --- /dev/null +++ b/.changeset/fix-vscode-server-auth.md @@ -0,0 +1,7 @@ +--- +"cline": patch +--- + +fix: use vscode.env.openExternal for auth in remote environments + +Fixes OAuth authentication in VS Code Server and remote environments by routing browser URL opening through VS Code's native openExternal API instead of the npm 'open' package. diff --git a/cli/src/acp/ACPHostBridgeClientProvider.ts b/cli/src/acp/ACPHostBridgeClientProvider.ts index e487d62aa8..5ed3acc284 100644 --- a/cli/src/acp/ACPHostBridgeClientProvider.ts +++ b/cli/src/acp/ACPHostBridgeClientProvider.ts @@ -172,6 +172,16 @@ class ACPEnvServiceClient implements EnvServiceClientInterface { Logger.debug("[ACPEnvServiceClient] shutdown called (stub)") return proto.cline.Empty.create() } + + async openExternal(request: proto.cline.StringRequest): Promise { + const url = request.value || "" + if (url) { + Logger.debug(`[ACPEnvServiceClient] openExternal: ${url}`) + const { openUrlInBrowser } = await import("../utils/browser") + await openUrlInBrowser(url) + } + return proto.cline.Empty.create() + } } /** diff --git a/cli/src/controllers/index.ts b/cli/src/controllers/index.ts index 2ea5ebc277..cae16efe13 100644 --- a/cli/src/controllers/index.ts +++ b/cli/src/controllers/index.ts @@ -142,6 +142,17 @@ export class CliEnvServiceClient implements EnvServiceClientInterface { printInfo("Shutting down...") return proto.cline.Empty.create() } + + async openExternal(request: proto.cline.StringRequest): Promise { + const url = request.value || "" + if (url) { + printInfo(`🌐 Opening: ${url}`) + // Dynamically import 'open' to open URL in default browser + const { default: open } = await import("open") + await open(url) + } + return proto.cline.Empty.create() + } } /** diff --git a/cli/src/utils/browser.ts b/cli/src/utils/browser.ts new file mode 100644 index 0000000000..3e10aee231 --- /dev/null +++ b/cli/src/utils/browser.ts @@ -0,0 +1,10 @@ +/** + * Opens a URL in the user's default browser. + * Uses dynamic import of the 'open' package to open URLs. + * + * @param url - The URL to open in the browser + */ +export async function openUrlInBrowser(url: string): Promise { + const { default: open } = await import("open") + await open(url) +} diff --git a/proto/host/env.proto b/proto/host/env.proto index a8e234964f..2c5a229448 100644 --- a/proto/host/env.proto +++ b/proto/host/env.proto @@ -36,6 +36,11 @@ service EnvService { // Logs a debug message to the host environment's log/output console. rpc debugLog(cline.StringRequest) returns (cline.Empty); + + // Opens an external URL in the default browser. + // In remote environments (VS Code Server, SSH, etc.), this routes the URL + // to the user's local machine to open in their local browser. + rpc openExternal(cline.StringRequest) returns (cline.Empty); } message GetHostVersionResponse { diff --git a/src/hosts/vscode/hostbridge/env/openExternal.ts b/src/hosts/vscode/hostbridge/env/openExternal.ts new file mode 100644 index 0000000000..d3c6841a3f --- /dev/null +++ b/src/hosts/vscode/hostbridge/env/openExternal.ts @@ -0,0 +1,8 @@ +import { Empty, StringRequest } from "@shared/proto/cline/common" +import * as vscode from "vscode" + +export async function openExternal(request: StringRequest): Promise { + const uri = vscode.Uri.parse(request.value) + await vscode.env.openExternal(uri) // ← Routes to local browser in remote setups! + return Empty.create({}) +} diff --git a/src/utils/env.ts b/src/utils/env.ts index d6cc50a180..682b77de48 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -1,5 +1,4 @@ import { EmptyRequest, StringRequest } from "@shared/proto/cline/common" -import open from "open" import { HostProvider } from "@/hosts/host-provider" import { Logger } from "@/shared/services/Logger" @@ -41,5 +40,6 @@ export async function readTextFromClipboard(): Promise { */ export async function openExternal(url: string): Promise { Logger.log("Opening browser:", url) - await open(url) + // Use VS Code's openExternal which handles remote environments + await HostProvider.env.openExternal(StringRequest.create({ value: url })) }