2026-02-08 15:01:42 +09:00
|
|
|
import { readFileSync, writeFileSync } from "node:fs"
|
|
|
|
|
import type { ConfigMergeResult } from "../types"
|
2026-03-17 15:16:54 +09:00
|
|
|
import { PLUGIN_NAME, LEGACY_PLUGIN_NAME } from "../../shared"
|
2026-02-08 15:01:42 +09:00
|
|
|
import { getConfigDir } from "./config-context"
|
|
|
|
|
import { ensureConfigDirectoryExists } from "./ensure-config-directory-exists"
|
|
|
|
|
import { formatErrorWithSuggestion } from "./format-error-with-suggestion"
|
|
|
|
|
import { detectConfigFormat } from "./opencode-config-format"
|
|
|
|
|
import { parseOpenCodeConfigFileWithError, type OpenCodeConfig } from "./parse-opencode-config-file"
|
|
|
|
|
import { getPluginNameWithVersion } from "./plugin-name-with-version"
|
|
|
|
|
|
|
|
|
|
export async function addPluginToOpenCodeConfig(currentVersion: string): Promise<ConfigMergeResult> {
|
|
|
|
|
try {
|
|
|
|
|
ensureConfigDirectoryExists()
|
|
|
|
|
} catch (err) {
|
|
|
|
|
return {
|
|
|
|
|
success: false,
|
|
|
|
|
configPath: getConfigDir(),
|
|
|
|
|
error: formatErrorWithSuggestion(err, "create config directory"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { format, path } = detectConfigFormat()
|
2026-03-17 15:16:54 +09:00
|
|
|
const pluginEntry = await getPluginNameWithVersion(currentVersion, PLUGIN_NAME)
|
2026-02-08 15:01:42 +09:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
if (format === "none") {
|
|
|
|
|
const config: OpenCodeConfig = { plugin: [pluginEntry] }
|
|
|
|
|
writeFileSync(path, JSON.stringify(config, null, 2) + "\n")
|
|
|
|
|
return { success: true, configPath: path }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const parseResult = parseOpenCodeConfigFileWithError(path)
|
|
|
|
|
if (!parseResult.config) {
|
|
|
|
|
return {
|
|
|
|
|
success: false,
|
|
|
|
|
configPath: path,
|
|
|
|
|
error: parseResult.error ?? "Failed to parse config file",
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const config = parseResult.config
|
|
|
|
|
const plugins = config.plugin ?? []
|
|
|
|
|
|
2026-03-26 18:04:31 +09:00
|
|
|
const canonicalEntries = plugins.filter(
|
2026-03-17 15:16:54 +09:00
|
|
|
(plugin) => plugin === PLUGIN_NAME || plugin.startsWith(`${PLUGIN_NAME}@`)
|
|
|
|
|
)
|
2026-03-26 18:04:31 +09:00
|
|
|
const legacyEntries = plugins.filter(
|
2026-03-17 15:16:54 +09:00
|
|
|
(plugin) => plugin === LEGACY_PLUGIN_NAME || plugin.startsWith(`${LEGACY_PLUGIN_NAME}@`)
|
|
|
|
|
)
|
2026-03-26 18:04:31 +09:00
|
|
|
const otherPlugins = plugins.filter(
|
|
|
|
|
(plugin) => !(plugin === PLUGIN_NAME || plugin.startsWith(`${PLUGIN_NAME}@`))
|
|
|
|
|
&& !(plugin === LEGACY_PLUGIN_NAME || plugin.startsWith(`${LEGACY_PLUGIN_NAME}@`))
|
|
|
|
|
)
|
2026-03-17 15:16:54 +09:00
|
|
|
|
2026-03-26 18:04:31 +09:00
|
|
|
const normalizedPlugins = [...otherPlugins]
|
|
|
|
|
|
|
|
|
|
if (canonicalEntries.length > 0) {
|
|
|
|
|
normalizedPlugins.push(canonicalEntries[0])
|
|
|
|
|
} else if (legacyEntries.length > 0) {
|
|
|
|
|
const versionMatch = legacyEntries[0].match(/@(.+)$/)
|
|
|
|
|
const preservedVersion = versionMatch ? versionMatch[1] : null
|
|
|
|
|
normalizedPlugins.push(preservedVersion ? `${PLUGIN_NAME}@${preservedVersion}` : pluginEntry)
|
2026-02-08 15:01:42 +09:00
|
|
|
} else {
|
2026-03-26 18:04:31 +09:00
|
|
|
normalizedPlugins.push(pluginEntry)
|
2026-02-08 15:01:42 +09:00
|
|
|
}
|
|
|
|
|
|
2026-03-26 18:04:31 +09:00
|
|
|
config.plugin = normalizedPlugins
|
2026-02-08 15:01:42 +09:00
|
|
|
|
|
|
|
|
if (format === "jsonc") {
|
|
|
|
|
const content = readFileSync(path, "utf-8")
|
2026-03-26 18:04:31 +09:00
|
|
|
const pluginArrayRegex = /((?:"plugin"|plugin)\s*:\s*)\[([\s\S]*?)\]/
|
2026-02-08 15:01:42 +09:00
|
|
|
const match = content.match(pluginArrayRegex)
|
|
|
|
|
|
|
|
|
|
if (match) {
|
2026-03-26 18:04:31 +09:00
|
|
|
const formattedPlugins = normalizedPlugins.map((p) => `"${p}"`).join(",\n ")
|
|
|
|
|
const newContent = content.replace(pluginArrayRegex, `$1[\n ${formattedPlugins}\n ]`)
|
2026-02-08 15:01:42 +09:00
|
|
|
writeFileSync(path, newContent)
|
|
|
|
|
} else {
|
2026-02-08 15:31:32 +09:00
|
|
|
const newContent = content.replace(/(\{)/, `$1\n "plugin": ["${pluginEntry}"],`)
|
2026-02-08 15:01:42 +09:00
|
|
|
writeFileSync(path, newContent)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
writeFileSync(path, JSON.stringify(config, null, 2) + "\n")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return { success: true, configPath: path }
|
|
|
|
|
} catch (err) {
|
|
|
|
|
return {
|
|
|
|
|
success: false,
|
|
|
|
|
configPath: path,
|
|
|
|
|
error: formatErrorWithSuggestion(err, "update opencode config"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|