|
|
@@ -11,11 +11,7 @@ import { lazy } from "../util/lazy"
|
|
|
import { NamedError } from "../util/error"
|
|
|
import { Flag } from "../flag/flag"
|
|
|
import { Auth } from "../auth"
|
|
|
-import {
|
|
|
- type ParseError as JsoncParseError,
|
|
|
- parse as parseJsonc,
|
|
|
- printParseErrorCode,
|
|
|
-} from "jsonc-parser"
|
|
|
+import { type ParseError as JsoncParseError, parse as parseJsonc, printParseErrorCode } from "jsonc-parser"
|
|
|
import { Instance } from "../project/instance"
|
|
|
import { LSPServer } from "../lsp/server"
|
|
|
import { BunProc } from "@/bun"
|
|
|
@@ -50,10 +46,7 @@ export namespace Config {
|
|
|
if (value.type === "wellknown") {
|
|
|
process.env[value.key] = value.token
|
|
|
const wellknown = (await fetch(`${key}/.well-known/opencode`).then((x) => x.json())) as any
|
|
|
- result = mergeDeep(
|
|
|
- result,
|
|
|
- await load(JSON.stringify(wellknown.config ?? {}), process.cwd()),
|
|
|
- )
|
|
|
+ result = mergeDeep(result, await load(JSON.stringify(wellknown.config ?? {}), process.cwd()))
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -159,18 +152,10 @@ export namespace Config {
|
|
|
|
|
|
const gitignore = path.join(dir, ".gitignore")
|
|
|
const hasGitIgnore = await Bun.file(gitignore).exists()
|
|
|
- if (!hasGitIgnore)
|
|
|
- await Bun.write(
|
|
|
- gitignore,
|
|
|
- ["node_modules", "package.json", "bun.lock", ".gitignore"].join("\n"),
|
|
|
- )
|
|
|
+ if (!hasGitIgnore) await Bun.write(gitignore, ["node_modules", "package.json", "bun.lock", ".gitignore"].join("\n"))
|
|
|
|
|
|
await BunProc.run(
|
|
|
- [
|
|
|
- "add",
|
|
|
- "@opencode-ai/plugin@" + (Installation.isLocal() ? "latest" : Installation.VERSION),
|
|
|
- "--exact",
|
|
|
- ],
|
|
|
+ ["add", "@opencode-ai/plugin@" + (Installation.isLocal() ? "latest" : Installation.VERSION), "--exact"],
|
|
|
{
|
|
|
cwd: dir,
|
|
|
},
|
|
|
@@ -330,10 +315,7 @@ export namespace Config {
|
|
|
type: z.literal("remote").describe("Type of MCP server connection"),
|
|
|
url: z.string().describe("URL of the remote MCP server"),
|
|
|
enabled: z.boolean().optional().describe("Enable or disable the MCP server on startup"),
|
|
|
- headers: z
|
|
|
- .record(z.string(), z.string())
|
|
|
- .optional()
|
|
|
- .describe("Headers to send with the request"),
|
|
|
+ headers: z.record(z.string(), z.string()).optional().describe("Headers to send with the request"),
|
|
|
timeout: z
|
|
|
.number()
|
|
|
.int()
|
|
|
@@ -389,70 +371,30 @@ export namespace Config {
|
|
|
|
|
|
export const Keybinds = z
|
|
|
.object({
|
|
|
- leader: z
|
|
|
- .string()
|
|
|
- .optional()
|
|
|
- .default("ctrl+x")
|
|
|
- .describe("Leader key for keybind combinations"),
|
|
|
- app_exit: z
|
|
|
- .string()
|
|
|
- .optional()
|
|
|
- .default("ctrl+c,ctrl+d,<leader>q")
|
|
|
- .describe("Exit the application"),
|
|
|
+ leader: z.string().optional().default("ctrl+x").describe("Leader key for keybind combinations"),
|
|
|
+ app_exit: z.string().optional().default("ctrl+c,ctrl+d,<leader>q").describe("Exit the application"),
|
|
|
editor_open: z.string().optional().default("<leader>e").describe("Open external editor"),
|
|
|
theme_list: z.string().optional().default("<leader>t").describe("List available themes"),
|
|
|
sidebar_toggle: z.string().optional().default("<leader>b").describe("Toggle sidebar"),
|
|
|
status_view: z.string().optional().default("<leader>s").describe("View status"),
|
|
|
- session_export: z
|
|
|
- .string()
|
|
|
- .optional()
|
|
|
- .default("<leader>x")
|
|
|
- .describe("Export session to editor"),
|
|
|
+ session_export: z.string().optional().default("<leader>x").describe("Export session to editor"),
|
|
|
session_new: z.string().optional().default("<leader>n").describe("Create a new session"),
|
|
|
session_list: z.string().optional().default("<leader>l").describe("List all sessions"),
|
|
|
- session_timeline: z
|
|
|
- .string()
|
|
|
- .optional()
|
|
|
- .default("<leader>g")
|
|
|
- .describe("Show session timeline"),
|
|
|
+ session_timeline: z.string().optional().default("<leader>g").describe("Show session timeline"),
|
|
|
session_share: z.string().optional().default("none").describe("Share current session"),
|
|
|
session_unshare: z.string().optional().default("none").describe("Unshare current session"),
|
|
|
- session_interrupt: z
|
|
|
- .string()
|
|
|
- .optional()
|
|
|
- .default("escape")
|
|
|
- .describe("Interrupt current session"),
|
|
|
+ session_interrupt: z.string().optional().default("escape").describe("Interrupt current session"),
|
|
|
session_compact: z.string().optional().default("<leader>c").describe("Compact the session"),
|
|
|
- messages_page_up: z
|
|
|
- .string()
|
|
|
- .optional()
|
|
|
- .default("pageup")
|
|
|
- .describe("Scroll messages up by one page"),
|
|
|
- messages_page_down: z
|
|
|
- .string()
|
|
|
- .optional()
|
|
|
- .default("pagedown")
|
|
|
- .describe("Scroll messages down by one page"),
|
|
|
- messages_half_page_up: z
|
|
|
- .string()
|
|
|
- .optional()
|
|
|
- .default("ctrl+alt+u")
|
|
|
- .describe("Scroll messages up by half page"),
|
|
|
+ messages_page_up: z.string().optional().default("pageup").describe("Scroll messages up by one page"),
|
|
|
+ messages_page_down: z.string().optional().default("pagedown").describe("Scroll messages down by one page"),
|
|
|
+ messages_half_page_up: z.string().optional().default("ctrl+alt+u").describe("Scroll messages up by half page"),
|
|
|
messages_half_page_down: z
|
|
|
.string()
|
|
|
.optional()
|
|
|
.default("ctrl+alt+d")
|
|
|
.describe("Scroll messages down by half page"),
|
|
|
- messages_first: z
|
|
|
- .string()
|
|
|
- .optional()
|
|
|
- .default("ctrl+g,home")
|
|
|
- .describe("Navigate to first message"),
|
|
|
- messages_last: z
|
|
|
- .string()
|
|
|
- .optional()
|
|
|
- .default("ctrl+alt+g,end")
|
|
|
- .describe("Navigate to last message"),
|
|
|
+ messages_first: z.string().optional().default("ctrl+g,home").describe("Navigate to first message"),
|
|
|
+ messages_last: z.string().optional().default("ctrl+alt+g,end").describe("Navigate to last message"),
|
|
|
messages_copy: z.string().optional().default("<leader>y").describe("Copy message"),
|
|
|
messages_undo: z.string().optional().default("<leader>u").describe("Undo message"),
|
|
|
messages_redo: z.string().optional().default("<leader>r").describe("Redo message"),
|
|
|
@@ -463,11 +405,7 @@ export namespace Config {
|
|
|
.describe("Toggle code block concealment in messages"),
|
|
|
model_list: z.string().optional().default("<leader>m").describe("List available models"),
|
|
|
model_cycle_recent: z.string().optional().default("f2").describe("Next recently used model"),
|
|
|
- model_cycle_recent_reverse: z
|
|
|
- .string()
|
|
|
- .optional()
|
|
|
- .default("shift+f2")
|
|
|
- .describe("Previous recently used model"),
|
|
|
+ model_cycle_recent_reverse: z.string().optional().default("shift+f2").describe("Previous recently used model"),
|
|
|
command_list: z.string().optional().default("ctrl+p").describe("List available commands"),
|
|
|
agent_list: z.string().optional().default("<leader>a").describe("List agents"),
|
|
|
agent_cycle: z.string().optional().default("tab").describe("Next agent"),
|
|
|
@@ -476,23 +414,11 @@ export namespace Config {
|
|
|
input_forward_delete: z.string().optional().default("ctrl+d").describe("Forward delete"),
|
|
|
input_paste: z.string().optional().default("ctrl+v").describe("Paste from clipboard"),
|
|
|
input_submit: z.string().optional().default("return").describe("Submit input"),
|
|
|
- input_newline: z
|
|
|
- .string()
|
|
|
- .optional()
|
|
|
- .default("shift+return,ctrl+j")
|
|
|
- .describe("Insert newline in input"),
|
|
|
+ input_newline: z.string().optional().default("shift+return,ctrl+j").describe("Insert newline in input"),
|
|
|
history_previous: z.string().optional().default("up").describe("Previous history item"),
|
|
|
history_next: z.string().optional().default("down").describe("Next history item"),
|
|
|
- session_child_cycle: z
|
|
|
- .string()
|
|
|
- .optional()
|
|
|
- .default("ctrl+right")
|
|
|
- .describe("Next child session"),
|
|
|
- session_child_cycle_reverse: z
|
|
|
- .string()
|
|
|
- .optional()
|
|
|
- .default("ctrl+left")
|
|
|
- .describe("Previous child session"),
|
|
|
+ session_child_cycle: z.string().optional().default("ctrl+right").describe("Next child session"),
|
|
|
+ session_child_cycle_reverse: z.string().optional().default("ctrl+left").describe("Previous child session"),
|
|
|
})
|
|
|
.strict()
|
|
|
.meta({
|
|
|
@@ -534,23 +460,13 @@ export namespace Config {
|
|
|
autoshare: z
|
|
|
.boolean()
|
|
|
.optional()
|
|
|
- .describe(
|
|
|
- "@deprecated Use 'share' field instead. Share newly created sessions automatically",
|
|
|
- ),
|
|
|
+ .describe("@deprecated Use 'share' field instead. Share newly created sessions automatically"),
|
|
|
autoupdate: z.boolean().optional().describe("Automatically update to the latest version"),
|
|
|
- disabled_providers: z
|
|
|
- .array(z.string())
|
|
|
- .optional()
|
|
|
- .describe("Disable providers that are loaded automatically"),
|
|
|
- model: z
|
|
|
- .string()
|
|
|
- .describe("Model to use in the format of provider/model, eg anthropic/claude-2")
|
|
|
- .optional(),
|
|
|
+ disabled_providers: z.array(z.string()).optional().describe("Disable providers that are loaded automatically"),
|
|
|
+ model: z.string().describe("Model to use in the format of provider/model, eg anthropic/claude-2").optional(),
|
|
|
small_model: z
|
|
|
.string()
|
|
|
- .describe(
|
|
|
- "Small model to use for tasks like title generation in the format of provider/model",
|
|
|
- )
|
|
|
+ .describe("Small model to use for tasks like title generation in the format of provider/model")
|
|
|
.optional(),
|
|
|
username: z
|
|
|
.string()
|
|
|
@@ -583,10 +499,7 @@ export namespace Config {
|
|
|
.object({
|
|
|
apiKey: z.string().optional(),
|
|
|
baseURL: z.string().optional(),
|
|
|
- enterpriseUrl: z
|
|
|
- .string()
|
|
|
- .optional()
|
|
|
- .describe("GitHub Enterprise URL for copilot authentication"),
|
|
|
+ enterpriseUrl: z.string().optional().describe("GitHub Enterprise URL for copilot authentication"),
|
|
|
timeout: z
|
|
|
.union([
|
|
|
z
|
|
|
@@ -610,10 +523,7 @@ export namespace Config {
|
|
|
)
|
|
|
.optional()
|
|
|
.describe("Custom provider configurations and model overrides"),
|
|
|
- mcp: z
|
|
|
- .record(z.string(), Mcp)
|
|
|
- .optional()
|
|
|
- .describe("MCP (Model Context Protocol) server configurations"),
|
|
|
+ mcp: z.record(z.string(), Mcp).optional().describe("MCP (Model Context Protocol) server configurations"),
|
|
|
formatter: z
|
|
|
.record(
|
|
|
z.string(),
|
|
|
@@ -657,10 +567,7 @@ export namespace Config {
|
|
|
error: "For custom LSP servers, 'extensions' array is required.",
|
|
|
},
|
|
|
),
|
|
|
- instructions: z
|
|
|
- .array(z.string())
|
|
|
- .optional()
|
|
|
- .describe("Additional instruction files or patterns to include"),
|
|
|
+ instructions: z.array(z.string()).optional().describe("Additional instruction files or patterns to include"),
|
|
|
layout: Layout.optional().describe("@deprecated Always uses stretch layout."),
|
|
|
permission: z
|
|
|
.object({
|
|
|
@@ -694,10 +601,7 @@ export namespace Config {
|
|
|
.optional(),
|
|
|
})
|
|
|
.optional(),
|
|
|
- chatMaxRetries: z
|
|
|
- .number()
|
|
|
- .optional()
|
|
|
- .describe("Number of retries for chat completions on failure"),
|
|
|
+ chatMaxRetries: z.number().optional().describe("Number of retries for chat completions on failure"),
|
|
|
disable_paste_summary: z.boolean().optional(),
|
|
|
})
|
|
|
.optional(),
|
|
|
@@ -727,10 +631,7 @@ export namespace Config {
|
|
|
if (provider && model) result.model = `${provider}/${model}`
|
|
|
result["$schema"] = "https://opencode.ai/config.json"
|
|
|
result = mergeDeep(result, rest)
|
|
|
- await Bun.write(
|
|
|
- path.join(Global.Path.config, "config.json"),
|
|
|
- JSON.stringify(result, null, 2),
|
|
|
- )
|
|
|
+ await Bun.write(path.join(Global.Path.config, "config.json"), JSON.stringify(result, null, 2))
|
|
|
await fs.unlink(path.join(Global.Path.config, "config"))
|
|
|
})
|
|
|
.catch(() => {})
|
|
|
@@ -769,9 +670,7 @@ export namespace Config {
|
|
|
if (filePath.startsWith("~/")) {
|
|
|
filePath = path.join(os.homedir(), filePath.slice(2))
|
|
|
}
|
|
|
- const resolvedPath = path.isAbsolute(filePath)
|
|
|
- ? filePath
|
|
|
- : path.resolve(configDir, filePath)
|
|
|
+ const resolvedPath = path.isAbsolute(filePath) ? filePath : path.resolve(configDir, filePath)
|
|
|
const fileContent = (
|
|
|
await Bun.file(resolvedPath)
|
|
|
.text()
|