Skip to content

Commit 98910ff

Browse files
committed
feat: update config api
1 parent c21161b commit 98910ff

File tree

4 files changed

+101
-2
lines changed

4 files changed

+101
-2
lines changed

packages/opencode/src/config/config.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import { Flag } from "../flag/flag"
1414
import { Auth } from "../auth"
1515
import { type ParseError as JsoncParseError, parse as parseJsonc, printParseErrorCode } from "jsonc-parser"
1616
import { Instance } from "../project/instance"
17+
import { Bus } from "../bus"
18+
import { State } from "../project/state"
1719

1820
export namespace Config {
1921
const log = Log.create({ service: "config" })
@@ -673,4 +675,59 @@ export namespace Config {
673675
export async function directories() {
674676
return state().then((x) => x.directories)
675677
}
678+
679+
export type ConfigScope = "global" | "project"
680+
681+
export async function update(scope: ConfigScope, updates: Partial<Info>) {
682+
const validated = Info.partial().parse(updates)
683+
684+
const targetPath =
685+
scope === "global"
686+
? path.join(Global.Path.config, "opencode.json")
687+
: path.join(Instance.directory, "opencode.json")
688+
689+
log.info("updating config", { scope, path: targetPath })
690+
691+
let existing: Info = {}
692+
try {
693+
const content = await Bun.file(targetPath).text()
694+
const errors: JsoncParseError[] = []
695+
existing = parseJsonc(content, errors, { allowTrailingComma: true }) || {}
696+
} catch (err) {
697+
if ((err as any).code !== "ENOENT") {
698+
throw new JsonError({ path: targetPath, message: "Failed to read existing config" }, { cause: err })
699+
}
700+
}
701+
702+
const merged = mergeDeep(existing, validated)
703+
704+
if (!merged.$schema) {
705+
merged.$schema = "https://opencode.ai/config.json"
706+
}
707+
708+
await Bun.write(targetPath, JSON.stringify(merged, null, 2))
709+
710+
const reloaded = await reload()
711+
712+
log.info("config updated", { scope })
713+
return reloaded
714+
}
715+
716+
export async function reload() {
717+
log.info("reloading config")
718+
719+
global.clear()
720+
721+
await State.dispose(Instance.directory)
722+
723+
const reloaded = await get()
724+
725+
Bus.publish(Event.Reloaded, { config: reloaded })
726+
727+
return reloaded
728+
}
729+
730+
export const Event = {
731+
Reloaded: Bus.event("config.reloaded", z.object({ config: Info })),
732+
}
676733
}

packages/opencode/src/project/state.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,14 @@ export namespace State {
2626
}
2727

2828
export async function dispose(key: string) {
29-
for (const [_, entry] of entries.get(key)?.entries() ?? []) {
29+
const collection = entries.get(key)
30+
if (!collection) return
31+
32+
for (const [_, entry] of collection.entries()) {
3033
if (!entry.dispose) continue
3134
await entry.dispose(await entry.state)
3235
}
36+
37+
entries.delete(key)
3338
}
3439
}

packages/opencode/src/server/server.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,36 @@ export namespace Server {
135135
return c.json(await Config.get())
136136
},
137137
)
138+
.patch(
139+
"/config",
140+
describeRoute({
141+
description: "Update configuration",
142+
operationId: "config.update",
143+
responses: {
144+
200: {
145+
description: "Updated config",
146+
content: {
147+
"application/json": {
148+
schema: resolver(Config.Info),
149+
},
150+
},
151+
},
152+
...ERRORS,
153+
},
154+
}),
155+
validator(
156+
"json",
157+
z.object({
158+
scope: z.enum(["global", "project"]).describe("Scope of config to update"),
159+
updates: Config.Info.partial().describe("Partial config updates to apply"),
160+
}),
161+
),
162+
async (c) => {
163+
const { scope, updates } = c.req.valid("json")
164+
const updated = await Config.update(scope, updates)
165+
return c.json(updated)
166+
},
167+
)
138168
.get(
139169
"/experimental/tool/ids",
140170
describeRoute({

packages/opencode/src/util/lazy.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,17 @@ export function lazy<T>(fn: () => T) {
22
let value: T | undefined
33
let loaded = false
44

5-
return (): T => {
5+
const result = (): T => {
66
if (loaded) return value as T
77
loaded = true
88
value = fn()
99
return value as T
1010
}
11+
12+
result.clear = () => {
13+
loaded = false
14+
value = undefined
15+
}
16+
17+
return result
1118
}

0 commit comments

Comments
 (0)