summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDax Raad <[email protected]>2025-06-18 22:20:03 -0400
committerDax Raad <[email protected]>2025-06-18 23:01:19 -0400
commite5e9b3e3c04df00db57d573d3cc0a029736184b1 (patch)
tree0d86520f7b243a08168f27a2f1302a5ee1438560
parent1e8a681de923518e3828306c4897e72ac803b6dd (diff)
downloadopencode-e5e9b3e3c04df00db57d573d3cc0a029736184b1.tar.gz
opencode-e5e9b3e3c04df00db57d573d3cc0a029736184b1.zip
rework config
-rw-r--r--packages/opencode/config.schema.json113
-rw-r--r--packages/opencode/src/cli/cmd/run.ts4
-rw-r--r--packages/opencode/src/config/config.ts97
-rw-r--r--packages/opencode/src/global/config.ts26
-rw-r--r--packages/opencode/src/index.ts4
-rw-r--r--packages/opencode/src/provider/provider.ts5
-rw-r--r--packages/opencode/src/server/server.ts20
-rw-r--r--packages/opencode/src/session/index.ts4
-rw-r--r--packages/tui/internal/app/app.go38
-rw-r--r--packages/tui/internal/commands/command.go12
-rw-r--r--packages/tui/internal/config/config.go49
-rw-r--r--packages/tui/internal/tui/tui.go8
-rw-r--r--packages/tui/pkg/client/gen/openapi.json292
-rw-r--r--packages/tui/pkg/client/generated-client.go314
14 files changed, 784 insertions, 202 deletions
diff --git a/packages/opencode/config.schema.json b/packages/opencode/config.schema.json
index c9d7e7f59..48978fcb6 100644
--- a/packages/opencode/config.schema.json
+++ b/packages/opencode/config.schema.json
@@ -4,11 +4,116 @@
"$schema": {
"type": "string"
},
+ "theme": {
+ "type": "string"
+ },
+ "keybinds": {
+ "type": "object",
+ "properties": {
+ "leader": {
+ "type": "string"
+ },
+ "help": {
+ "type": "string"
+ },
+ "editor_open": {
+ "type": "string"
+ },
+ "session_new": {
+ "type": "string"
+ },
+ "session_list": {
+ "type": "string"
+ },
+ "session_share": {
+ "type": "string"
+ },
+ "session_interrupt": {
+ "type": "string"
+ },
+ "session_compact": {
+ "type": "string"
+ },
+ "tool_details": {
+ "type": "string"
+ },
+ "model_list": {
+ "type": "string"
+ },
+ "theme_list": {
+ "type": "string"
+ },
+ "project_init": {
+ "type": "string"
+ },
+ "input_clear": {
+ "type": "string"
+ },
+ "input_paste": {
+ "type": "string"
+ },
+ "input_submit": {
+ "type": "string"
+ },
+ "input_newline": {
+ "type": "string"
+ },
+ "history_previous": {
+ "type": "string"
+ },
+ "history_next": {
+ "type": "string"
+ },
+ "messages_page_up": {
+ "type": "string"
+ },
+ "messages_page_down": {
+ "type": "string"
+ },
+ "messages_half_page_up": {
+ "type": "string"
+ },
+ "messages_half_page_down": {
+ "type": "string"
+ },
+ "messages_previous": {
+ "type": "string"
+ },
+ "messages_next": {
+ "type": "string"
+ },
+ "messages_first": {
+ "type": "string"
+ },
+ "messages_last": {
+ "type": "string"
+ },
+ "app_exit": {
+ "type": "string"
+ }
+ },
+ "additionalProperties": false
+ },
+ "autoshare": {
+ "type": "boolean"
+ },
+ "autoupdate": {
+ "type": "boolean"
+ },
+ "disabled_providers": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
"provider": {
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
+ "api": {
+ "type": "string"
+ },
"name": {
"type": "string"
},
@@ -50,18 +155,16 @@
"output": {
"type": "number"
},
- "inputCached": {
+ "cache_read": {
"type": "number"
},
- "outputCached": {
+ "cache_write": {
"type": "number"
}
},
"required": [
"input",
- "output",
- "inputCached",
- "outputCached"
+ "output"
],
"additionalProperties": false
},
diff --git a/packages/opencode/src/cli/cmd/run.ts b/packages/opencode/src/cli/cmd/run.ts
index 77e2a6873..01dcd9bf8 100644
--- a/packages/opencode/src/cli/cmd/run.ts
+++ b/packages/opencode/src/cli/cmd/run.ts
@@ -7,8 +7,8 @@ import { Share } from "../../share/share"
import { Message } from "../../session/message"
import { UI } from "../ui"
import { cmd } from "./cmd"
-import { GlobalConfig } from "../../global/config"
import { Flag } from "../../flag/flag"
+import { Config } from "../../config/config"
const TOOL: Record<string, [string, string]> = {
opencode_todowrite: ["Todo", UI.Style.TEXT_WARNING_BOLD],
@@ -60,7 +60,7 @@ export const RunCommand = cmd({
UI.println(UI.Style.TEXT_NORMAL_BOLD + "> ", message)
UI.empty()
- const cfg = await GlobalConfig.get()
+ const cfg = await Config.get()
if (cfg.autoshare || Flag.OPENCODE_AUTO_SHARE || args.share) {
await Session.share(session.id)
UI.println(
diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts
index 74c21c7ea..565e84735 100644
--- a/packages/opencode/src/config/config.ts
+++ b/packages/opencode/src/config/config.ts
@@ -6,15 +6,14 @@ import { Filesystem } from "../util/filesystem"
import { ModelsDev } from "../provider/models"
import { mergeDeep } from "remeda"
import { Global } from "../global"
+import fs from "fs/promises"
+import { lazy } from "../util/lazy"
export namespace Config {
const log = Log.create({ service: "config" })
export const state = App.state("config", async (app) => {
- let result = await Bun.file(path.join(Global.Path.config, "config.json"))
- .json()
- .then((mod) => Info.parse(mod))
- .catch(() => ({}) as Info)
+ let result = await global()
for (const file of ["opencode.jsonc", "opencode.json"]) {
const [resolved] = await Filesystem.findUp(
file,
@@ -43,16 +42,24 @@ export namespace Config {
return result
})
- export const McpLocal = z.object({
- type: z.literal("local"),
- command: z.string().array(),
- environment: z.record(z.string(), z.string()).optional(),
- })
+ export const McpLocal = z
+ .object({
+ type: z.literal("local"),
+ command: z.string().array(),
+ environment: z.record(z.string(), z.string()).optional(),
+ })
+ .openapi({
+ ref: "Config.McpLocal",
+ })
- export const McpRemote = z.object({
- type: z.literal("remote"),
- url: z.string(),
- })
+ export const McpRemote = z
+ .object({
+ type: z.literal("remote"),
+ url: z.string(),
+ })
+ .openapi({
+ ref: "Config.McpRemote",
+ })
export const Mcp = z.discriminatedUnion("type", [McpLocal, McpRemote])
export type Mcp = z.infer<typeof Mcp>
@@ -60,6 +67,41 @@ export namespace Config {
export const Info = z
.object({
$schema: z.string().optional(),
+ theme: z.string().optional(),
+ keybinds: z
+ .object({
+ leader: z.string().optional(),
+ help: z.string().optional(),
+ editor_open: z.string().optional(),
+ session_new: z.string().optional(),
+ session_list: z.string().optional(),
+ session_share: z.string().optional(),
+ session_interrupt: z.string().optional(),
+ session_compact: z.string().optional(),
+ tool_details: z.string().optional(),
+ model_list: z.string().optional(),
+ theme_list: z.string().optional(),
+ project_init: z.string().optional(),
+ input_clear: z.string().optional(),
+ input_paste: z.string().optional(),
+ input_submit: z.string().optional(),
+ input_newline: z.string().optional(),
+ history_previous: z.string().optional(),
+ history_next: z.string().optional(),
+ messages_page_up: z.string().optional(),
+ messages_page_down: z.string().optional(),
+ messages_half_page_up: z.string().optional(),
+ messages_half_page_down: z.string().optional(),
+ messages_previous: z.string().optional(),
+ messages_next: z.string().optional(),
+ messages_first: z.string().optional(),
+ messages_last: z.string().optional(),
+ app_exit: z.string().optional(),
+ })
+ .optional(),
+ autoshare: z.boolean().optional(),
+ autoupdate: z.boolean().optional(),
+ disabled_providers: z.array(z.string()).optional(),
provider: z
.record(
ModelsDev.Provider.partial().extend({
@@ -70,10 +112,37 @@ export namespace Config {
.optional(),
mcp: z.record(z.string(), Mcp).optional(),
})
- .strict()
+ .openapi({
+ ref: "Config.Info",
+ })
export type Info = z.output<typeof Info>
+ export const global = lazy(async () => {
+ let result = await Bun.file(path.join(Global.Path.config, "config.json"))
+ .json()
+ .then((mod) => Info.parse(mod))
+ .catch(() => ({}) as Info)
+
+ await import(path.join(Global.Path.config, "config"), {
+ with: {
+ type: "toml",
+ },
+ })
+ .then(async (mod) => {
+ delete mod.default.provider
+ delete mod.default.model
+ result = mergeDeep(result, mod.default)
+ 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(() => {})
+ return Info.parse(result)
+ })
+
export function get() {
return state()
}
diff --git a/packages/opencode/src/global/config.ts b/packages/opencode/src/global/config.ts
deleted file mode 100644
index 0bad721c9..000000000
--- a/packages/opencode/src/global/config.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { z } from "zod"
-import { Global } from "."
-import { lazy } from "../util/lazy"
-import path from "path"
-
-export namespace GlobalConfig {
- export const Info = z.object({
- provider: z.string().optional(),
- model: z.string().optional(),
- autoupdate: z.boolean().optional(),
- autoshare: z.boolean().optional(),
- disabled_providers: z.array(z.string()).optional(),
- })
- export type Info = z.infer<typeof Info>
-
- export const get = lazy(async () => {
- const toml = await import(path.join(Global.Path.config, "config"), {
- with: {
- type: "toml",
- },
- })
- .then((mod) => mod.default)
- .catch(() => ({}))
- return Info.parse(toml)
- })
-}
diff --git a/packages/opencode/src/index.ts b/packages/opencode/src/index.ts
index 10de7fa57..20b8b8f6a 100644
--- a/packages/opencode/src/index.ts
+++ b/packages/opencode/src/index.ts
@@ -15,9 +15,9 @@ import { AuthCommand, AuthLoginCommand } from "./cli/cmd/auth"
import { UpgradeCommand } from "./cli/cmd/upgrade"
import { Provider } from "./provider/provider"
import { UI } from "./cli/ui"
-import { GlobalConfig } from "./global/config"
import { Installation } from "./installation"
import { Bus } from "./bus"
+import { Config } from "./config/config"
const cli = yargs(hideBin(process.argv))
.scriptName("opencode")
@@ -87,7 +87,7 @@ const cli = yargs(hideBin(process.argv))
;(async () => {
if (Installation.VERSION === "dev") return
if (Installation.isSnapshot()) return
- const config = await GlobalConfig.get()
+ const config = await Config.global()
if (config.autoupdate === false) return
const latest = await Installation.latest()
if (Installation.VERSION === latest) return
diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts
index 17090233a..36f0600f1 100644
--- a/packages/opencode/src/provider/provider.ts
+++ b/packages/opencode/src/provider/provider.ts
@@ -24,7 +24,6 @@ import { ModelsDev } from "./models"
import { NamedError } from "../util/error"
import { Auth } from "../auth"
import { TaskTool } from "../tool/task"
-import { GlobalConfig } from "../global/config"
import { Global } from "../global"
export namespace Provider {
@@ -179,7 +178,7 @@ export namespace Provider {
database[providerID] = parsed
}
- const disabled = await GlobalConfig.get().then(
+ const disabled = await Config.get().then(
(cfg) => new Set(cfg.disabled_providers ?? []),
)
// load env
@@ -300,7 +299,7 @@ export namespace Provider {
}
export async function defaultModel() {
- const cfg = await GlobalConfig.get()
+ const cfg = await Config.get()
const provider = await list()
.then((val) => Object.values(val))
.then((x) => x.find((p) => !cfg.provider || cfg.provider === p.info.id))
diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts
index e28ae1d19..c7fd13a38 100644
--- a/packages/opencode/src/server/server.ts
+++ b/packages/opencode/src/server/server.ts
@@ -15,6 +15,7 @@ import { NamedError } from "../util/error"
import { ModelsDev } from "../provider/models"
import { Ripgrep } from "../external/ripgrep"
import { Installation } from "../installation"
+import { Config } from "../config/config"
const ERRORS = {
400: {
@@ -141,6 +142,25 @@ export namespace Server {
},
)
.post(
+ "/config_get",
+ describeRoute({
+ description: "Get config info",
+ responses: {
+ 200: {
+ description: "Get config info",
+ content: {
+ "application/json": {
+ schema: resolver(Config.Info),
+ },
+ },
+ },
+ },
+ }),
+ async (c) => {
+ return c.json(await Config.get())
+ },
+ )
+ .post(
"/app_initialize",
describeRoute({
description: "Initialize the app",
diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts
index 962b1e574..568a649e5 100644
--- a/packages/opencode/src/session/index.ts
+++ b/packages/opencode/src/session/index.ts
@@ -30,8 +30,8 @@ import type { Tool } from "../tool/tool"
import { SystemPrompt } from "./system"
import { Flag } from "../flag/flag"
import type { ModelsDev } from "../provider/models"
-import { GlobalConfig } from "../global/config"
import { Installation } from "../installation"
+import { Config } from "../config/config"
export namespace Session {
const log = Log.create({ service: "session" })
@@ -114,7 +114,7 @@ export namespace Session {
log.info("created", result)
state().sessions.set(result.id, result)
await Storage.writeJSON("session/info/" + result.id, result)
- const cfg = await GlobalConfig.get()
+ const cfg = await Config.get()
if (!result.parentID && (Flag.OPENCODE_AUTO_SHARE || cfg.autoshare))
share(result.id).then((share) => {
update(result.id, (draft) => {
diff --git a/packages/tui/internal/app/app.go b/packages/tui/internal/app/app.go
index 6f0733aa3..eb4bd2dad 100644
--- a/packages/tui/internal/app/app.go
+++ b/packages/tui/internal/app/app.go
@@ -22,8 +22,9 @@ type App struct {
Info client.AppInfo
Version string
StatePath string
- Config *config.Config
+ Configg *client.ConfigInfo
Client *client.ClientWithResponses
+ State *config.State
Provider *client.ProviderInfo
Model *client.ModelInfo
Session *client.SessionInfo
@@ -54,14 +55,15 @@ func New(
) (*App, error) {
RootPath = appInfo.Path.Root
- appConfigPath := filepath.Join(appInfo.Path.Config, "config")
- appConfig, err := config.LoadConfig(appConfigPath)
+ configResponse, err := httpClient.PostConfigGetWithResponse(ctx)
if err != nil {
- appConfig = config.NewConfig()
+ return nil, err
}
- if len(appConfig.Keybinds) == 0 {
- appConfig.Keybinds = make(map[string]string)
- appConfig.Keybinds["leader"] = "ctrl+x"
+ configInfo := configResponse.JSON200
+ if configInfo.Keybinds == nil {
+ keybinds := make(map[string]string)
+ keybinds["leader"] = "ctrl+x"
+ configInfo.Keybinds = &keybinds
}
appStatePath := filepath.Join(appInfo.Path.State, "tui")
@@ -71,20 +73,25 @@ func New(
config.SaveState(appStatePath, appState)
}
- mergedConfig := config.MergeState(appState, appConfig)
- theme.SetTheme(mergedConfig.Theme)
+ if configInfo.Theme != nil {
+ appState.Theme = *configInfo.Theme
+ }
+ if appState.Theme != "" {
+ theme.SetTheme(appState.Theme)
+ }
- slog.Debug("Loaded config", "config", mergedConfig)
+ slog.Debug("Loaded config", "config", configInfo)
app := &App{
Info: appInfo,
Version: version,
StatePath: appStatePath,
- Config: mergedConfig,
+ Configg: configInfo,
+ State: appState,
Client: httpClient,
Session: &client.SessionInfo{},
Messages: []client.MessageInfo{},
- Commands: commands.LoadFromConfig(mergedConfig),
+ Commands: commands.LoadFromConfig(configInfo),
}
return app, nil
@@ -130,11 +137,11 @@ func (a *App) InitializeProvider() tea.Cmd {
var currentProvider *client.ProviderInfo
var currentModel *client.ModelInfo
for _, provider := range providers {
- if provider.Id == a.Config.Provider {
+ if provider.Id == a.State.Provider {
currentProvider = &provider
for _, model := range provider.Models {
- if model.Id == a.Config.Model {
+ if model.Id == a.State.Model {
currentModel = &model
}
}
@@ -182,8 +189,7 @@ func (a *App) IsBusy() bool {
}
func (a *App) SaveState() {
- state := config.ConfigToState(a.Config)
- err := config.SaveState(a.StatePath, state)
+ err := config.SaveState(a.StatePath, a.State)
if err != nil {
slog.Error("Failed to save state", "error", err)
}
diff --git a/packages/tui/internal/commands/command.go b/packages/tui/internal/commands/command.go
index ad7ccf30c..6f628ae31 100644
--- a/packages/tui/internal/commands/command.go
+++ b/packages/tui/internal/commands/command.go
@@ -5,7 +5,7 @@ import (
"strings"
tea "github.com/charmbracelet/bubbletea/v2"
- "github.com/sst/opencode/internal/config"
+ "github.com/sst/opencode/pkg/client"
)
type ExecuteCommandMsg Command
@@ -106,8 +106,12 @@ func (k Command) Matches(msg tea.KeyPressMsg, leader bool) bool {
return false
}
-func (k Command) FromConfig(config *config.Config) Command {
- if keybind, ok := config.Keybinds[string(k.Name)]; ok {
+func (k Command) FromConfig(config *client.ConfigInfo) Command {
+ if config.Keybinds == nil {
+ return k
+ }
+ keybinds := *config.Keybinds
+ if keybind, ok := keybinds[string(k.Name)]; ok {
k.Keybindings = parseBindings(keybind)
}
return k
@@ -129,7 +133,7 @@ func parseBindings(bindings ...string) []Keybinding {
return parsedBindings
}
-func LoadFromConfig(config *config.Config) CommandRegistry {
+func LoadFromConfig(config *client.ConfigInfo) CommandRegistry {
defaults := []Command{
{
Name: AppHelpCommand,
diff --git a/packages/tui/internal/config/config.go b/packages/tui/internal/config/config.go
index 6474f75eb..29db8657e 100644
--- a/packages/tui/internal/config/config.go
+++ b/packages/tui/internal/config/config.go
@@ -7,6 +7,7 @@ import (
"os"
"github.com/BurntSushi/toml"
+ "github.com/sst/opencode/pkg/client"
)
type State struct {
@@ -15,44 +16,15 @@ type State struct {
Model string `toml:"model"`
}
-type Config struct {
- Theme string `toml:"theme"`
- Provider string `toml:"provider"`
- Model string `toml:"model"`
- Keybinds map[string]string `toml:"keybinds"`
-}
-
func NewState() *State {
return &State{
Theme: "opencode",
}
}
-func NewConfig() *Config {
- keybinds := make(map[string]string)
- keybinds["leader"] = "ctrl+x"
- return &Config{
- Keybinds: keybinds,
- }
-}
-
-func ConfigToState(config *Config) *State {
- return &State{
- Theme: config.Theme,
- Provider: config.Provider,
- Model: config.Model,
- }
-}
-
-func MergeState(state *State, config *Config) *Config {
- if config.Theme == "" {
- config.Theme = state.Theme
- }
- if config.Provider == "" {
- config.Provider = state.Provider
- }
- if config.Model == "" {
- config.Model = state.Model
+func MergeState(state *State, config *client.ConfigInfo) *client.ConfigInfo {
+ if config.Theme == nil {
+ config.Theme = &state.Theme
}
return config
}
@@ -79,19 +51,6 @@ func SaveState(filePath string, state *State) error {
return nil
}
-// LoadConfig reads a Config struct from the specified TOML file.
-// It returns a pointer to the Config struct and an error if any issues occur.
-func LoadConfig(filePath string) (*Config, error) {
- var config Config
- if _, err := toml.DecodeFile(filePath, &config); err != nil {
- if _, statErr := os.Stat(filePath); os.IsNotExist(statErr) {
- return nil, fmt.Errorf("config file not found at %s: %w", filePath, statErr)
- }
- return nil, fmt.Errorf("failed to decode TOML from file %s: %w", filePath, err)
- }
- return &config, nil
-}
-
// LoadState loads the state from the specified TOML file.
// It returns a pointer to the State struct and an error if any issues occur.
func LoadState(filePath string) (*State, error) {
diff --git a/packages/tui/internal/tui/tui.go b/packages/tui/internal/tui/tui.go
index 0b38bc3bc..5d63a161a 100644
--- a/packages/tui/internal/tui/tui.go
+++ b/packages/tui/internal/tui/tui.go
@@ -249,11 +249,11 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case app.ModelSelectedMsg:
a.app.Provider = &msg.Provider
a.app.Model = &msg.Model
- a.app.Config.Provider = msg.Provider.Id
- a.app.Config.Model = msg.Model.Id
+ a.app.State.Provider = msg.Provider.Id
+ a.app.State.Model = msg.Model.Id
a.app.SaveState()
case dialog.ThemeSelectedMsg:
- a.app.Config.Theme = msg.ThemeName
+ a.app.State.Theme = msg.ThemeName
a.app.SaveState()
}
@@ -509,7 +509,7 @@ func NewModel(app *app.App) tea.Model {
messagesContainer := layout.NewContainer(messages)
var leaderBinding *key.Binding
- if leader, ok := app.Config.Keybinds["leader"]; ok {
+ if leader, ok := (*app.Configg.Keybinds)["leader"]; ok {
binding := key.NewBinding(key.WithKeys(leader))
leaderBinding = &binding
}
diff --git a/packages/tui/pkg/client/gen/openapi.json b/packages/tui/pkg/client/gen/openapi.json
index 0afe49373..ea055a64f 100644
--- a/packages/tui/pkg/client/gen/openapi.json
+++ b/packages/tui/pkg/client/gen/openapi.json
@@ -44,6 +44,25 @@
"description": "Get app info"
}
},
+ "/config_get": {
+ "post": {
+ "responses": {
+ "200": {
+ "description": "Get config info",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Config.Info"
+ }
+ }
+ }
+ }
+ },
+ "operationId": "postConfig_get",
+ "parameters": [],
+ "description": "Get config info"
+ }
+ },
"/app_initialize": {
"post": {
"responses": {
@@ -519,13 +538,13 @@
"$ref": "#/components/schemas/Event.message.part.updated"
},
{
- "$ref": "#/components/schemas/Event.session.updated"
+ "$ref": "#/components/schemas/Event.installation.updated"
},
{
- "$ref": "#/components/schemas/Event.session.error"
+ "$ref": "#/components/schemas/Event.session.updated"
},
{
- "$ref": "#/components/schemas/Event.installation.updated"
+ "$ref": "#/components/schemas/Event.session.error"
}
],
"discriminator": {
@@ -536,9 +555,9 @@
"permission.updated": "#/components/schemas/Event.permission.updated",
"message.updated": "#/components/schemas/Event.message.updated",
"message.part.updated": "#/components/schemas/Event.message.part.updated",
+ "installation.updated": "#/components/schemas/Event.installation.updated",
"session.updated": "#/components/schemas/Event.session.updated",
- "session.error": "#/components/schemas/Event.session.error",
- "installation.updated": "#/components/schemas/Event.installation.updated"
+ "session.error": "#/components/schemas/Event.session.error"
}
}
},
@@ -1182,6 +1201,30 @@
"properties"
]
},
+ "Event.installation.updated": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "const": "installation.updated"
+ },
+ "properties": {
+ "type": "object",
+ "properties": {
+ "version": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "version"
+ ]
+ }
+ },
+ "required": [
+ "type",
+ "properties"
+ ]
+ },
"Event.session.updated": {
"type": "object",
"properties": {
@@ -1220,21 +1263,20 @@
"share": {
"type": "object",
"properties": {
- "secret": {
- "type": "string"
- },
"url": {
"type": "string"
}
},
"required": [
- "secret",
"url"
]
},
"title": {
"type": "string"
},
+ "version": {
+ "type": "string"
+ },
"time": {
"type": "object",
"properties": {
@@ -1254,6 +1296,7 @@
"required": [
"id",
"title",
+ "version",
"time"
]
},
@@ -1292,30 +1335,6 @@
"properties"
]
},
- "Event.installation.updated": {
- "type": "object",
- "properties": {
- "type": {
- "type": "string",
- "const": "installation.updated"
- },
- "properties": {
- "type": "object",
- "properties": {
- "version": {
- "type": "string"
- }
- },
- "required": [
- "version"
- ]
- }
- },
- "required": [
- "type",
- "properties"
- ]
- },
"App.Info": {
"type": "object",
"properties": {
@@ -1368,21 +1387,153 @@
"time"
]
},
- "Error": {
+ "Config.Info": {
"type": "object",
"properties": {
- "data": {
+ "$schema": {
+ "type": "string"
+ },
+ "theme": {
+ "type": "string"
+ },
+ "keybinds": {
"type": "object",
- "additionalProperties": {}
+ "additionalProperties": {
+ "type": "string"
+ }
+ },
+ "autoshare": {
+ "type": "boolean"
+ },
+ "autoupdate": {
+ "type": "boolean"
+ },
+ "disabled_providers": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "provider": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "object",
+ "properties": {
+ "api": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "env": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "id": {
+ "type": "string"
+ },
+ "npm": {
+ "type": "string"
+ },
+ "models": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "attachment": {
+ "type": "boolean"
+ },
+ "reasoning": {
+ "type": "boolean"
+ },
+ "temperature": {
+ "type": "boolean"
+ },
+ "cost": {
+ "type": "object",
+ "properties": {
+ "input": {
+ "type": "number"
+ },
+ "output": {
+ "type": "number"
+ },
+ "cache_read": {
+ "type": "number"
+ },
+ "cache_write": {
+ "type": "number"
+ }
+ },
+ "required": [
+ "input",
+ "output"
+ ]
+ },
+ "limit": {
+ "type": "object",
+ "properties": {
+ "context": {
+ "type": "number"
+ },
+ "output": {
+ "type": "number"
+ }
+ },
+ "required": [
+ "context",
+ "output"
+ ]
+ },
+ "id": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "options": {
+ "type": "object",
+ "additionalProperties": {}
+ }
+ },
+ "required": [
+ "models"
+ ]
+ }
+ },
+ "mcp": {
+ "type": "object",
+ "additionalProperties": {
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/Config.McpLocal"
+ },
+ {
+ "$ref": "#/components/schemas/Config.McpRemote"
+ }
+ ],
+ "discriminator": {
+ "propertyName": "type",
+ "mapping": {
+ "local": "#/components/schemas/Config.McpLocal",
+ "remote": "#/components/schemas/Config.McpRemote"
+ }
+ }
+ }
}
- },
- "required": [
- "data"
- ]
+ }
},
"Provider.Info": {
"type": "object",
"properties": {
+ "api": {
+ "type": "string"
+ },
"name": {
"type": "string"
},
@@ -1436,18 +1587,16 @@
"output": {
"type": "number"
},
- "inputCached": {
+ "cache_read": {
"type": "number"
},
- "outputCached": {
+ "cache_write": {
"type": "number"
}
},
"required": [
"input",
- "output",
- "inputCached",
- "outputCached"
+ "output"
]
},
"limit": {
@@ -1479,6 +1628,59 @@
"id"
]
},
+ "Config.McpLocal": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "const": "local"
+ },
+ "command": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "environment": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ },
+ "required": [
+ "type",
+ "command"
+ ]
+ },
+ "Config.McpRemote": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "const": "remote"
+ },
+ "url": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "url"
+ ]
+ },
+ "Error": {
+ "type": "object",
+ "properties": {
+ "data": {
+ "type": "object",
+ "additionalProperties": {}
+ }
+ },
+ "required": [
+ "data"
+ ]
+ },
"InstallationInfo": {
"type": "object",
"properties": {
diff --git a/packages/tui/pkg/client/generated-client.go b/packages/tui/pkg/client/generated-client.go
index 4f85f858e..c2ecffe94 100644
--- a/packages/tui/pkg/client/generated-client.go
+++ b/packages/tui/pkg/client/generated-client.go
@@ -39,6 +39,60 @@ type AppInfo struct {
User string `json:"user"`
}
+// ConfigInfo defines model for Config.Info.
+type ConfigInfo struct {
+ Schema *string `json:"$schema,omitempty"`
+ Autoshare *bool `json:"autoshare,omitempty"`
+ Autoupdate *bool `json:"autoupdate,omitempty"`
+ DisabledProviders *[]string `json:"disabled_providers,omitempty"`
+ Keybinds *map[string]string `json:"keybinds,omitempty"`
+ Mcp *map[string]ConfigInfo_Mcp_AdditionalProperties `json:"mcp,omitempty"`
+ Provider *map[string]struct {
+ Api *string `json:"api,omitempty"`
+ Env *[]string `json:"env,omitempty"`
+ Id *string `json:"id,omitempty"`
+ Models map[string]struct {
+ Attachment *bool `json:"attachment,omitempty"`
+ Cost *struct {
+ CacheRead *float32 `json:"cache_read,omitempty"`
+ CacheWrite *float32 `json:"cache_write,omitempty"`
+ Input float32 `json:"input"`
+ Output float32 `json:"output"`
+ } `json:"cost,omitempty"`
+ Id *string `json:"id,omitempty"`
+ Limit *struct {
+ Context float32 `json:"context"`
+ Output float32 `json:"output"`
+ } `json:"limit,omitempty"`
+ Name *string `json:"name,omitempty"`
+ Reasoning *bool `json:"reasoning,omitempty"`
+ Temperature *bool `json:"temperature,omitempty"`
+ } `json:"models"`
+ Name *string `json:"name,omitempty"`
+ Npm *string `json:"npm,omitempty"`
+ Options *map[string]interface{} `json:"options,omitempty"`
+ } `json:"provider,omitempty"`
+ Theme *string `json:"theme,omitempty"`
+}
+
+// ConfigInfo_Mcp_AdditionalProperties defines model for Config.Info.mcp.AdditionalProperties.
+type ConfigInfo_Mcp_AdditionalProperties struct {
+ union json.RawMessage
+}
+
+// ConfigMcpLocal defines model for Config.McpLocal.
+type ConfigMcpLocal struct {
+ Command []string `json:"command"`
+ Environment *map[string]string `json:"environment,omitempty"`
+ Type string `json:"type"`
+}
+
+// ConfigMcpRemote defines model for Config.McpRemote.
+type ConfigMcpRemote struct {
+ Type string `json:"type"`
+ Url string `json:"url"`
+}
+
// Error defines model for Error.
type Error struct {
Data map[string]interface{} `json:"data"`
@@ -263,10 +317,10 @@ type MessageToolInvocationToolResult struct {
type ModelInfo struct {
Attachment bool `json:"attachment"`
Cost struct {
- Input float32 `json:"input"`
- InputCached float32 `json:"inputCached"`
- Output float32 `json:"output"`
- OutputCached float32 `json:"outputCached"`
+ CacheRead *float32 `json:"cache_read,omitempty"`
+ CacheWrite *float32 `json:"cache_write,omitempty"`
+ Input float32 `json:"input"`
+ Output float32 `json:"output"`
} `json:"cost"`
Id string `json:"id"`
Limit struct {
@@ -280,6 +334,7 @@ type ModelInfo struct {
// ProviderInfo defines model for Provider.Info.
type ProviderInfo struct {
+ Api *string `json:"api,omitempty"`
Env []string `json:"env"`
Id string `json:"id"`
Models map[string]ModelInfo `json:"models"`
@@ -320,14 +375,14 @@ type SessionInfo struct {
Id string `json:"id"`
ParentID *string `json:"parentID,omitempty"`
Share *struct {
- Secret string `json:"secret"`
- Url string `json:"url"`
+ Url string `json:"url"`
} `json:"share,omitempty"`
Time struct {
Created float32 `json:"created"`
Updated float32 `json:"updated"`
} `json:"time"`
- Title string `json:"title"`
+ Title string `json:"title"`
+ Version string `json:"version"`
}
// PostFileSearchJSONBody defines parameters for PostFileSearch.
@@ -472,6 +527,95 @@ func (a MessageInfo_Metadata_Tool_AdditionalProperties) MarshalJSON() ([]byte, e
return json.Marshal(object)
}
+// AsConfigMcpLocal returns the union data inside the ConfigInfo_Mcp_AdditionalProperties as a ConfigMcpLocal
+func (t ConfigInfo_Mcp_AdditionalProperties) AsConfigMcpLocal() (ConfigMcpLocal, error) {
+ var body ConfigMcpLocal
+ err := json.Unmarshal(t.union, &body)
+ return body, err
+}
+
+// FromConfigMcpLocal overwrites any union data inside the ConfigInfo_Mcp_AdditionalProperties as the provided ConfigMcpLocal
+func (t *ConfigInfo_Mcp_AdditionalProperties) FromConfigMcpLocal(v ConfigMcpLocal) error {
+ v.Type = "local"
+ b, err := json.Marshal(v)
+ t.union = b
+ return err
+}
+
+// MergeConfigMcpLocal performs a merge with any union data inside the ConfigInfo_Mcp_AdditionalProperties, using the provided ConfigMcpLocal
+func (t *ConfigInfo_Mcp_AdditionalProperties) MergeConfigMcpLocal(v ConfigMcpLocal) error {
+ v.Type = "local"
+ b, err := json.Marshal(v)
+ if err != nil {
+ return err
+ }
+
+ merged, err := runtime.JSONMerge(t.union, b)
+ t.union = merged
+ return err
+}
+
+// AsConfigMcpRemote returns the union data inside the ConfigInfo_Mcp_AdditionalProperties as a ConfigMcpRemote
+func (t ConfigInfo_Mcp_AdditionalProperties) AsConfigMcpRemote() (ConfigMcpRemote, error) {
+ var body ConfigMcpRemote
+ err := json.Unmarshal(t.union, &body)
+ return body, err
+}
+
+// FromConfigMcpRemote overwrites any union data inside the ConfigInfo_Mcp_AdditionalProperties as the provided ConfigMcpRemote
+func (t *ConfigInfo_Mcp_AdditionalProperties) FromConfigMcpRemote(v ConfigMcpRemote) error {
+ v.Type = "remote"
+ b, err := json.Marshal(v)
+ t.union = b
+ return err
+}
+
+// MergeConfigMcpRemote performs a merge with any union data inside the ConfigInfo_Mcp_AdditionalProperties, using the provided ConfigMcpRemote
+func (t *ConfigInfo_Mcp_AdditionalProperties) MergeConfigMcpRemote(v ConfigMcpRemote) error {
+ v.Type = "remote"
+ b, err := json.Marshal(v)
+ if err != nil {
+ return err
+ }
+
+ merged, err := runtime.JSONMerge(t.union, b)
+ t.union = merged
+ return err
+}
+
+func (t ConfigInfo_Mcp_AdditionalProperties) Discriminator() (string, error) {
+ var discriminator struct {
+ Discriminator string `json:"type"`
+ }
+ err := json.Unmarshal(t.union, &discriminator)
+ return discriminator.Discriminator, err
+}
+
+func (t ConfigInfo_Mcp_AdditionalProperties) ValueByDiscriminator() (interface{}, error) {
+ discriminator, err := t.Discriminator()
+ if err != nil {
+ return nil, err
+ }
+ switch discriminator {
+ case "local":
+ return t.AsConfigMcpLocal()
+ case "remote":
+ return t.AsConfigMcpRemote()
+ default:
+ return nil, errors.New("unknown discriminator value: " + discriminator)
+ }
+}
+
+func (t ConfigInfo_Mcp_AdditionalProperties) MarshalJSON() ([]byte, error) {
+ b, err := t.union.MarshalJSON()
+ return b, err
+}
+
+func (t *ConfigInfo_Mcp_AdditionalProperties) UnmarshalJSON(b []byte) error {
+ err := t.union.UnmarshalJSON(b)
+ return err
+}
+
// AsEventStorageWrite returns the union data inside the Event as a EventStorageWrite
func (t Event) AsEventStorageWrite() (EventStorageWrite, error) {
var body EventStorageWrite
@@ -612,24 +756,24 @@ func (t *Event) MergeEventMessagePartUpdated(v EventMessagePartUpdated) error {
return err
}
-// AsEventSessionUpdated returns the union data inside the Event as a EventSessionUpdated
-func (t Event) AsEventSessionUpdated() (EventSessionUpdated, error) {
- var body EventSessionUpdated
+// AsEventInstallationUpdated returns the union data inside the Event as a EventInstallationUpdated
+func (t Event) AsEventInstallationUpdated() (EventInstallationUpdated, error) {
+ var body EventInstallationUpdated
err := json.Unmarshal(t.union, &body)
return body, err
}
-// FromEventSessionUpdated overwrites any union data inside the Event as the provided EventSessionUpdated
-func (t *Event) FromEventSessionUpdated(v EventSessionUpdated) error {
- v.Type = "session.updated"
+// FromEventInstallationUpdated overwrites any union data inside the Event as the provided EventInstallationUpdated
+func (t *Event) FromEventInstallationUpdated(v EventInstallationUpdated) error {
+ v.Type = "installation.updated"
b, err := json.Marshal(v)
t.union = b
return err
}
-// MergeEventSessionUpdated performs a merge with any union data inside the Event, using the provided EventSessionUpdated
-func (t *Event) MergeEventSessionUpdated(v EventSessionUpdated) error {
- v.Type = "session.updated"
+// MergeEventInstallationUpdated performs a merge with any union data inside the Event, using the provided EventInstallationUpdated
+func (t *Event) MergeEventInstallationUpdated(v EventInstallationUpdated) error {
+ v.Type = "installation.updated"
b, err := json.Marshal(v)
if err != nil {
return err
@@ -640,24 +784,24 @@ func (t *Event) MergeEventSessionUpdated(v EventSessionUpdated) error {
return err
}
-// AsEventSessionError returns the union data inside the Event as a EventSessionError
-func (t Event) AsEventSessionError() (EventSessionError, error) {
- var body EventSessionError
+// AsEventSessionUpdated returns the union data inside the Event as a EventSessionUpdated
+func (t Event) AsEventSessionUpdated() (EventSessionUpdated, error) {
+ var body EventSessionUpdated
err := json.Unmarshal(t.union, &body)
return body, err
}
-// FromEventSessionError overwrites any union data inside the Event as the provided EventSessionError
-func (t *Event) FromEventSessionError(v EventSessionError) error {
- v.Type = "session.error"
+// FromEventSessionUpdated overwrites any union data inside the Event as the provided EventSessionUpdated
+func (t *Event) FromEventSessionUpdated(v EventSessionUpdated) error {
+ v.Type = "session.updated"
b, err := json.Marshal(v)
t.union = b
return err
}
-// MergeEventSessionError performs a merge with any union data inside the Event, using the provided EventSessionError
-func (t *Event) MergeEventSessionError(v EventSessionError) error {
- v.Type = "session.error"
+// MergeEventSessionUpdated performs a merge with any union data inside the Event, using the provided EventSessionUpdated
+func (t *Event) MergeEventSessionUpdated(v EventSessionUpdated) error {
+ v.Type = "session.updated"
b, err := json.Marshal(v)
if err != nil {
return err
@@ -668,24 +812,24 @@ func (t *Event) MergeEventSessionError(v EventSessionError) error {
return err
}
-// AsEventInstallationUpdated returns the union data inside the Event as a EventInstallationUpdated
-func (t Event) AsEventInstallationUpdated() (EventInstallationUpdated, error) {
- var body EventInstallationUpdated
+// AsEventSessionError returns the union data inside the Event as a EventSessionError
+func (t Event) AsEventSessionError() (EventSessionError, error) {
+ var body EventSessionError
err := json.Unmarshal(t.union, &body)
return body, err
}
-// FromEventInstallationUpdated overwrites any union data inside the Event as the provided EventInstallationUpdated
-func (t *Event) FromEventInstallationUpdated(v EventInstallationUpdated) error {
- v.Type = "installation.updated"
+// FromEventSessionError overwrites any union data inside the Event as the provided EventSessionError
+func (t *Event) FromEventSessionError(v EventSessionError) error {
+ v.Type = "session.error"
b, err := json.Marshal(v)
t.union = b
return err
}
-// MergeEventInstallationUpdated performs a merge with any union data inside the Event, using the provided EventInstallationUpdated
-func (t *Event) MergeEventInstallationUpdated(v EventInstallationUpdated) error {
- v.Type = "installation.updated"
+// MergeEventSessionError performs a merge with any union data inside the Event, using the provided EventSessionError
+func (t *Event) MergeEventSessionError(v EventSessionError) error {
+ v.Type = "session.error"
b, err := json.Marshal(v)
if err != nil {
return err
@@ -1326,6 +1470,9 @@ type ClientInterface interface {
// PostAppInitialize request
PostAppInitialize(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
+ // PostConfigGet request
+ PostConfigGet(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
+
// GetEvent request
GetEvent(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
@@ -1404,6 +1551,18 @@ func (c *Client) PostAppInitialize(ctx context.Context, reqEditors ...RequestEdi
return c.Client.Do(req)
}
+func (c *Client) PostConfigGet(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) {
+ req, err := NewPostConfigGetRequest(c.Server)
+ if err != nil {
+ return nil, err
+ }
+ req = req.WithContext(ctx)
+ if err := c.applyEditors(ctx, req, reqEditors); err != nil {
+ return nil, err
+ }
+ return c.Client.Do(req)
+}
+
func (c *Client) GetEvent(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewGetEventRequest(c.Server)
if err != nil {
@@ -1698,6 +1857,33 @@ func NewPostAppInitializeRequest(server string) (*http.Request, error) {
return req, nil
}
+// NewPostConfigGetRequest generates requests for PostConfigGet
+func NewPostConfigGetRequest(server string) (*http.Request, error) {
+ var err error
+
+ serverURL, err := url.Parse(server)
+ if err != nil {
+ return nil, err
+ }
+
+ operationPath := fmt.Sprintf("/config_get")
+ if operationPath[0] == '/' {
+ operationPath = "." + operationPath
+ }
+
+ queryURL, err := serverURL.Parse(operationPath)
+ if err != nil {
+ return nil, err
+ }
+
+ req, err := http.NewRequest("POST", queryURL.String(), nil)
+ if err != nil {
+ return nil, err
+ }
+
+ return req, nil
+}
+
// NewGetEventRequest generates requests for GetEvent
func NewGetEventRequest(server string) (*http.Request, error) {
var err error
@@ -2189,6 +2375,9 @@ type ClientWithResponsesInterface interface {
// PostAppInitializeWithResponse request
PostAppInitializeWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*PostAppInitializeResponse, error)
+ // PostConfigGetWithResponse request
+ PostConfigGetWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*PostConfigGetResponse, error)
+
// GetEventWithResponse request
GetEventWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetEventResponse, error)
@@ -2287,6 +2476,28 @@ func (r PostAppInitializeResponse) StatusCode() int {
return 0
}
+type PostConfigGetResponse struct {
+ Body []byte
+ HTTPResponse *http.Response
+ JSON200 *ConfigInfo
+}
+
+// Status returns HTTPResponse.Status
+func (r PostConfigGetResponse) Status() string {
+ if r.HTTPResponse != nil {
+ return r.HTTPResponse.Status
+ }
+ return http.StatusText(0)
+}
+
+// StatusCode returns HTTPResponse.StatusCode
+func (r PostConfigGetResponse) StatusCode() int {
+ if r.HTTPResponse != nil {
+ return r.HTTPResponse.StatusCode
+ }
+ return 0
+}
+
type GetEventResponse struct {
Body []byte
HTTPResponse *http.Response
@@ -2600,6 +2811,15 @@ func (c *ClientWithResponses) PostAppInitializeWithResponse(ctx context.Context,
return ParsePostAppInitializeResponse(rsp)
}
+// PostConfigGetWithResponse request returning *PostConfigGetResponse
+func (c *ClientWithResponses) PostConfigGetWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*PostConfigGetResponse, error) {
+ rsp, err := c.PostConfigGet(ctx, reqEditors...)
+ if err != nil {
+ return nil, err
+ }
+ return ParsePostConfigGetResponse(rsp)
+}
+
// GetEventWithResponse request returning *GetEventResponse
func (c *ClientWithResponses) GetEventWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetEventResponse, error) {
rsp, err := c.GetEvent(ctx, reqEditors...)
@@ -2825,6 +3045,32 @@ func ParsePostAppInitializeResponse(rsp *http.Response) (*PostAppInitializeRespo
return response, nil
}
+// ParsePostConfigGetResponse parses an HTTP response from a PostConfigGetWithResponse call
+func ParsePostConfigGetResponse(rsp *http.Response) (*PostConfigGetResponse, error) {
+ bodyBytes, err := io.ReadAll(rsp.Body)
+ defer func() { _ = rsp.Body.Close() }()
+ if err != nil {
+ return nil, err
+ }
+
+ response := &PostConfigGetResponse{
+ Body: bodyBytes,
+ HTTPResponse: rsp,
+ }
+
+ switch {
+ case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
+ var dest ConfigInfo
+ if err := json.Unmarshal(bodyBytes, &dest); err != nil {
+ return nil, err
+ }
+ response.JSON200 = &dest
+
+ }
+
+ return response, nil
+}
+
// ParseGetEventResponse parses an HTTP response from a GetEventWithResponse call
func ParseGetEventResponse(rsp *http.Response) (*GetEventResponse, error) {
bodyBytes, err := io.ReadAll(rsp.Body)