diff --git a/src/cli/config-manager/add-provider-config.ts b/src/cli/config-manager/add-provider-config.ts index 833c9882f..b2a400a43 100644 --- a/src/cli/config-manager/add-provider-config.ts +++ b/src/cli/config-manager/add-provider-config.ts @@ -1,4 +1,4 @@ -import { writeFileSync } from "node:fs" +import { readFileSync, writeFileSync } from "node:fs" import type { ConfigMergeResult, InstallConfig } from "../types" import { getConfigDir } from "./config-context" import { ensureConfigDirectoryExists } from "./ensure-config-directory-exists" @@ -45,7 +45,23 @@ export function addProviderConfig(config: InstallConfig): ConfigMergeResult { newConfig.provider = providers } - writeFileSync(path, JSON.stringify(newConfig, null, 2) + "\n") + if (format === "jsonc") { + const content = readFileSync(path, "utf-8") + const providerRegex = /"provider"\s*:\s*\{[\s\S]*?\n \}/ + const providerJson = JSON.stringify(newConfig.provider, null, 2) + .split("\n") + .map((line, i) => (i === 0 ? line : ` ${line}`)) + .join("\n") + if (providerRegex.test(content)) { + const newContent = content.replace(providerRegex, `"provider": ${providerJson}`) + writeFileSync(path, newContent) + } else { + const newContent = content.replace(/(\{)/, `$1\n "provider": ${providerJson},`) + writeFileSync(path, newContent) + } + } else { + writeFileSync(path, JSON.stringify(newConfig, null, 2) + "\n") + } return { success: true, configPath: path } } catch (err) { return { diff --git a/src/cli/config-manager/auth-plugins.ts b/src/cli/config-manager/auth-plugins.ts index 577d80a20..4e4d5718f 100644 --- a/src/cli/config-manager/auth-plugins.ts +++ b/src/cli/config-manager/auth-plugins.ts @@ -8,7 +8,7 @@ import { parseOpenCodeConfigFileWithError, type OpenCodeConfig } from "./parse-o export async function fetchLatestVersion(packageName: string): Promise { try { - const res = await fetch(`https://registry.npmjs.org/${packageName}/latest`) + const res = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`) if (!res.ok) return null const data = (await res.json()) as { version: string } return data.version diff --git a/src/cli/config-manager/bun-install.ts b/src/cli/config-manager/bun-install.ts index 052617e49..f24e77fa2 100644 --- a/src/cli/config-manager/bun-install.ts +++ b/src/cli/config-manager/bun-install.ts @@ -39,7 +39,7 @@ export async function runBunInstallWithDetails(): Promise { return { success: false, timedOut: true, - error: `bun install timed out after ${BUN_INSTALL_TIMEOUT_SECONDS} seconds. Try running manually: cd ~/.config/opencode && bun i`, + error: `bun install timed out after ${BUN_INSTALL_TIMEOUT_SECONDS} seconds. Try running manually: cd ${getConfigDir()} && bun i`, } } diff --git a/src/cli/config-manager/config-context.ts b/src/cli/config-manager/config-context.ts index 78eb88d77..67448f29a 100644 --- a/src/cli/config-manager/config-context.ts +++ b/src/cli/config-manager/config-context.ts @@ -19,6 +19,9 @@ export function initConfigContext(binary: OpenCodeBinaryType, version: string | export function getConfigContext(): ConfigContext { if (!configContext) { + if (process.env.NODE_ENV !== "production") { + console.warn("[config-context] getConfigContext() called before initConfigContext(); defaulting to CLI paths.") + } const paths = getOpenCodeConfigPaths({ binary: "opencode", version: null }) configContext = { binary: "opencode", version: null, paths } } diff --git a/src/cli/config-manager/deep-merge-record.ts b/src/cli/config-manager/deep-merge-record.ts index 54c0daa57..b38c26633 100644 --- a/src/cli/config-manager/deep-merge-record.ts +++ b/src/cli/config-manager/deep-merge-record.ts @@ -5,6 +5,7 @@ export function deepMergeRecord>( const result: TTarget = { ...target } for (const key of Object.keys(source) as Array) { + if (key === "__proto__" || key === "constructor" || key === "prototype") continue const sourceValue = source[key] const targetValue = result[key]