From 4c4566f7bf453ec31bb602192d8909a927287f1d Mon Sep 17 00:00:00 2001 From: Ishaan Gupta Date: Wed, 3 Jun 2026 17:35:21 +0530 Subject: [PATCH] Fix Windows auth URL opener --- src/cli.ts | 3 ++- src/config.ts | 3 ++- src/services/auth.ts | 25 ++++++++----------------- src/services/client.ts | 2 +- src/services/openUrl.ts | 34 ++++++++++++++++++++++++++++++++++ 5 files changed, 47 insertions(+), 20 deletions(-) create mode 100644 src/services/openUrl.ts diff --git a/src/cli.ts b/src/cli.ts index 9114fae..1e047cd 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -11,6 +11,7 @@ const OPENCODE_CONFIG_DIR = join(homedir(), ".config", "opencode"); const OPENCODE_COMMAND_DIR = join(OPENCODE_CONFIG_DIR, "command"); const OH_MY_OPENCODE_CONFIG = join(OPENCODE_CONFIG_DIR, "oh-my-opencode.json"); const PLUGIN_NAME = "opencode-supermemory@latest"; +const DEFAULT_CONFIG_FILE = CONFIG_FILE ?? join(OPENCODE_CONFIG_DIR, "supermemory.json"); const SUPERMEMORY_INIT_COMMAND = `--- description: Initialize Supermemory with comprehensive codebase knowledge @@ -377,7 +378,7 @@ interface InstallOptions { async function install(options: InstallOptions): Promise { console.log("\n🧠 opencode-supermemory installer\n"); - writeInstallDefaults(existsSync(CONFIG_FILE)); + writeInstallDefaults(existsSync(DEFAULT_CONFIG_FILE)); const rl = options.tui ? createReadline() : null; diff --git a/src/config.ts b/src/config.ts index de238f9..ec34ad8 100644 --- a/src/config.ts +++ b/src/config.ts @@ -102,6 +102,7 @@ function getApiKey(): string | undefined { export const SUPERMEMORY_API_KEY = getApiKey(); export const CONFIG_FILE = CONFIG_FILES[1]; +const DEFAULT_CONFIG_FILE = CONFIG_FILE ?? join(CONFIG_DIR, "supermemory.json"); export const CONFIG = { similarityThreshold: fileConfig.similarityThreshold ?? DEFAULTS.similarityThreshold, @@ -140,5 +141,5 @@ export function writeInstallDefaults(isExistingInstall: boolean): void { next.autoRecallEveryPrompt = false; next.captureEveryNTurns = 0; } - writeFileSync(CONFIG_FILE, JSON.stringify(next, null, 2)); + writeFileSync(DEFAULT_CONFIG_FILE, JSON.stringify(next, null, 2)); } diff --git a/src/services/auth.ts b/src/services/auth.ts index d642a72..d8fc521 100644 --- a/src/services/auth.ts +++ b/src/services/auth.ts @@ -1,8 +1,8 @@ import { createServer, type IncomingMessage, type ServerResponse } from "node:http"; import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"; -import { exec } from "node:child_process"; import { join } from "node:path"; import { homedir } from "node:os"; +import { openUrl } from "./openUrl.js"; const CREDENTIALS_DIR = join(homedir(), ".supermemory-opencode"); const CREDENTIALS_FILE = join(CREDENTIALS_DIR, "credentials.json"); @@ -40,21 +40,6 @@ export function clearCredentials(): boolean { return true; } -function openBrowser(url: string): void { - const platform = process.platform; - - const commands: Record = { - darwin: `open "${url}"`, - win32: `start "" "${url}"`, - linux: `xdg-open "${url}"`, - }; - - const cmd = commands[platform] ?? `xdg-open "${url}"`; - exec(cmd, (err) => { - if (err) console.error("Failed to open browser:", err.message); - }); -} - export interface AuthResult { success: boolean; apiKey?: string; @@ -129,7 +114,13 @@ export function startAuthFlow(timeoutMs = 120000): Promise { console.log("Opening browser for authentication..."); console.log(`If it doesn't open, visit: ${authUrl}`); - openBrowser(authUrl); + openUrl(authUrl).catch((error) => { + if (!resolved) { + resolved = true; + server.close(); + resolve({ success: false, error: `Failed to open browser: ${error.message}` }); + } + }); }); setTimeout(() => { diff --git a/src/services/client.ts b/src/services/client.ts index 67c2cb2..06c2dae 100644 --- a/src/services/client.ts +++ b/src/services/client.ts @@ -121,7 +121,7 @@ export class SupermemoryClient { sm_source: "opencode", sm_capture_mode: metadata?.sm_capture_mode ?? "tool", ...(metadata ?? {}), - } as Record; + } as unknown as Record; const result = await withTimeout( this.getClient().memories.add({ diff --git a/src/services/openUrl.ts b/src/services/openUrl.ts new file mode 100644 index 0000000..dae8053 --- /dev/null +++ b/src/services/openUrl.ts @@ -0,0 +1,34 @@ +import { execFile } from "node:child_process"; + +function run(command: string, args: string[]): Promise { + return new Promise((resolve, reject) => { + execFile(command, args, { windowsHide: true }, (error) => { + if (error) reject(error); + else resolve(); + }); + }); +} + +export async function openUrl(url: string | URL): Promise { + const href = url.toString(); + if (!/^https?:\/\//i.test(href)) { + throw new Error("Refusing to open non-http URL"); + } + + if (process.platform === "win32") { + try { + await run("rundll32.exe", ["url.dll,FileProtocolHandler", href]); + return; + } catch {} + + await run("cmd.exe", ["/c", "start", '""', href]); + return; + } + + if (process.platform === "darwin") { + await run("open", [href]); + return; + } + + await run("xdg-open", [href]); +}