fix: use vscode.env.openExternal for auth in remote environments (#9111)

* fix: use vscode.env.openExternal for auth in remote environments

Fixes #5109

The OAuth authentication flow was broken in VS Code Server and remote
environments because the code used the npm 'open' package directly, which
tries to launch a browser on the server itself (which has no display).

This change routes browser URL opening through VS Code's native
vscode.env.openExternal() API via the HostBridge pattern, which properly
forwards URLs to the user's local machine in remote environments.

Changes:
- Added openExternal RPC to proto/host/env.proto
- Created VS Code handler using vscode.env.openExternal()
- Updated src/utils/env.ts to use HostProvider.env.openExternal()
- Added openExternal to CLI CliEnvServiceClient (uses npm 'open')
- Added openExternal to CLI ACPEnvServiceClient (uses npm 'open')

Related issues: #5394, #2152, #7971

* chore: add changeset for vscode server auth fix

* refactor: extract shared openUrlInBrowser utility for CLI
This commit is contained in:
ClineXDiego
2026-02-04 19:31:20 -08:00
committed by GitHub
parent 3ce1ad3504
commit f440f3a5dd
7 changed files with 53 additions and 2 deletions
+7
View File
@@ -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.
@@ -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<proto.cline.Empty> {
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()
}
}
/**
+11
View File
@@ -142,6 +142,17 @@ export class CliEnvServiceClient implements EnvServiceClientInterface {
printInfo("Shutting down...")
return proto.cline.Empty.create()
}
async openExternal(request: proto.cline.StringRequest): Promise<proto.cline.Empty> {
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()
}
}
/**
+10
View File
@@ -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<void> {
const { default: open } = await import("open")
await open(url)
}
+5
View File
@@ -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 {
+8
View File
@@ -0,0 +1,8 @@
import { Empty, StringRequest } from "@shared/proto/cline/common"
import * as vscode from "vscode"
export async function openExternal(request: StringRequest): Promise<Empty> {
const uri = vscode.Uri.parse(request.value)
await vscode.env.openExternal(uri) // ← Routes to local browser in remote setups!
return Empty.create({})
}
+2 -2
View File
@@ -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<string> {
*/
export async function openExternal(url: string): Promise<void> {
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 }))
}