diff --git a/sdks/vscode/README.md b/sdks/vscode/README.md index 1ca5078ce5c..63fa335db5c 100644 --- a/sdks/vscode/README.md +++ b/sdks/vscode/README.md @@ -13,6 +13,21 @@ This extension requires the [opencode CLI](https://opencode.ai) to be installed - **Context Awareness**: Automatically share your current selection or tab with opencode. - **File Reference Shortcuts**: Use `Cmd+Option+K` (Mac) or `Alt+Ctrl+K` (Linux/Windows) to insert file references. For example, `@File#L37-42`. +## Configuration + +| Setting | Default | Description | +| ------------------------- | ----------- | ------------------------------------------------------------------------ | +| `opencode.attach.enabled` | `false` | Attach to an existing OpenCode server instead of spawning a new instance | +| `opencode.attach.host` | `localhost` | Host of the OpenCode server (supports `http://` or `https://` prefix) | +| `opencode.attach.port` | `4096` | Port of the OpenCode server | + +**Attach Mode**: Useful when OpenCode is already running (e.g., in cloud workspaces like Coder). Enable attach mode to connect to the existing server instead of starting a new one. + +The host setting supports full URLs with protocols. For example: + +- `localhost` - Uses `http://localhost` (default) +- `https://myserver.com` - Uses HTTPS for secure connections + ## Support This is an early release. If you encounter issues or have feedback, please create an issue at https://github.com/sst/opencode/issues. diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index 9a3bc2af533..77d8da08c8b 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -78,7 +78,27 @@ "win": "ctrl+alt+K", "linux": "ctrl+alt+K" } - ] + ], + "configuration": { + "title": "OpenCode", + "properties": { + "opencode.attach.enabled": { + "type": "boolean", + "default": false, + "description": "Attach to an existing OpenCode server instead of spawning a new instance" + }, + "opencode.attach.host": { + "type": "string", + "default": "localhost", + "description": "Host of the OpenCode server to attach to" + }, + "opencode.attach.port": { + "type": "number", + "default": 4096, + "description": "Port of the OpenCode server to attach to" + } + } + } }, "scripts": { "vscode:prepublish": "bun run package", diff --git a/sdks/vscode/src/extension.ts b/sdks/vscode/src/extension.ts index 63d8d332e40..cb898c8ace9 100644 --- a/sdks/vscode/src/extension.ts +++ b/sdks/vscode/src/extension.ts @@ -33,18 +33,32 @@ export function activate(context: vscode.ExtensionContext) { } if (terminal.name === TERMINAL_NAME) { - // @ts-ignore - const port = terminal.creationOptions.env?.["_EXTENSION_OPENCODE_PORT"] - port ? await appendPrompt(parseInt(port), fileRef) : terminal.sendText(fileRef) + const env = (terminal.creationOptions as vscode.TerminalOptions)?.env || {} + const port = env?.["_EXTENSION_OPENCODE_PORT"] + const host = env?.["_EXTENSION_OPENCODE_HOST"] ?? "localhost" + port ? await appendPrompt(parseInt(port), fileRef, host) : terminal.sendText(fileRef) terminal.show() } }) context.subscriptions.push(openTerminalDisposable, addFilepathDisposable) + function buildUrl(host: string, port: number, path = "") { + if (host.startsWith("http://") || host.startsWith("https://")) { + return `${host}:${port}${path}` + } + return `http://${host}:${port}${path}` + } + async function openTerminal() { - // Create a new terminal in split screen - const port = Math.floor(Math.random() * (65535 - 16384 + 1)) + 16384 + const config = vscode.workspace.getConfiguration("opencode") + const attachEnabled = config.get("attach.enabled", false) + const attachHost = config.get("attach.host", "localhost") + const attachPort = config.get("attach.port", 4096) + + const host = attachEnabled ? attachHost : "localhost" + const port = attachEnabled ? attachPort : Math.floor(Math.random() * (65535 - 16384 + 1)) + 16384 + const terminal = vscode.window.createTerminal({ name: TERMINAL_NAME, iconPath: { @@ -57,12 +71,14 @@ export function activate(context: vscode.ExtensionContext) { }, env: { _EXTENSION_OPENCODE_PORT: port.toString(), + _EXTENSION_OPENCODE_HOST: host, OPENCODE_CALLER: "vscode", }, }) terminal.show() - terminal.sendText(`opencode --port ${port}`) + const command = attachEnabled ? `opencode attach ${buildUrl(host, port)}` : `opencode --port ${port}` + terminal.sendText(command) const fileRef = getActiveFile() if (!fileRef) { @@ -75,7 +91,7 @@ export function activate(context: vscode.ExtensionContext) { do { await new Promise((resolve) => setTimeout(resolve, 200)) try { - await fetch(`http://localhost:${port}/app`) + await fetch(buildUrl(host, port, "/app")) connected = true break } catch (e) {} @@ -85,13 +101,13 @@ export function activate(context: vscode.ExtensionContext) { // If connected, append the prompt to the terminal if (connected) { - await appendPrompt(port, `In ${fileRef}`) + await appendPrompt(port, `In ${fileRef}`, host) terminal.show() } } - async function appendPrompt(port: number, text: string) { - await fetch(`http://localhost:${port}/tui/append-prompt`, { + async function appendPrompt(port: number, text: string, host = "localhost") { + await fetch(buildUrl(host, port, "/tui/append-prompt"), { method: "POST", headers: { "Content-Type": "application/json",