diff --git a/packages/opencode/src/cli/cmd/tui/app.tsx b/packages/opencode/src/cli/cmd/tui/app.tsx index 4b177e292cf..0d553d935ba 100644 --- a/packages/opencode/src/cli/cmd/tui/app.tsx +++ b/packages/opencode/src/cli/cmd/tui/app.tsx @@ -105,6 +105,7 @@ export function tui(input: { directory?: string fetch?: typeof fetch events?: EventSource + startServer?: () => Promise onExit?: () => Promise }) { // promise to prevent immediate exit @@ -131,6 +132,7 @@ export function tui(input: { directory={input.directory} fetch={input.fetch} events={input.events} + startServer={input.startServer} > @@ -491,8 +493,36 @@ function App() { { title: "Open WebUI", value: "webui.open", - onSelect: () => { - open(sdk.url).catch(() => {}) + onSelect: async () => { + let url = sdk.url + if (sdk.url.includes("opencode.internal")) { + if (!sdk.startServer) { + toast.show({ + variant: "warning", + message: "WebUI requires server mode. Restart with: opencode --port 4096", + duration: 5000, + }) + dialog.clear() + return + } + toast.show({ + variant: "info", + message: "Starting server...", + duration: 2000, + }) + try { + url = await sdk.startServer() + } catch (e) { + toast.show({ + variant: "error", + message: "Failed to start server", + duration: 3000, + }) + dialog.clear() + return + } + } + open(url).catch(() => {}) dialog.clear() }, category: "System", diff --git a/packages/opencode/src/cli/cmd/tui/context/sdk.tsx b/packages/opencode/src/cli/cmd/tui/context/sdk.tsx index 3339e7b00d2..549fd336678 100644 --- a/packages/opencode/src/cli/cmd/tui/context/sdk.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/sdk.tsx @@ -9,7 +9,13 @@ export type EventSource = { export const { use: useSDK, provider: SDKProvider } = createSimpleContext({ name: "SDK", - init: (props: { url: string; directory?: string; fetch?: typeof fetch; events?: EventSource }) => { + init: (props: { + url: string + directory?: string + fetch?: typeof fetch + events?: EventSource + startServer?: () => Promise + }) => { const abort = new AbortController() const sdk = createOpencodeClient({ baseUrl: props.url, @@ -89,6 +95,6 @@ export const { use: useSDK, provider: SDKProvider } = createSimpleContext({ if (timer) clearTimeout(timer) }) - return { client: sdk, event: emitter, url: props.url } + return { client: sdk, event: emitter, url: props.url, startServer: props.startServer } }, }) diff --git a/packages/opencode/src/cli/cmd/tui/thread.ts b/packages/opencode/src/cli/cmd/tui/thread.ts index 05714268545..3e51f391f28 100644 --- a/packages/opencode/src/cli/cmd/tui/thread.ts +++ b/packages/opencode/src/cli/cmd/tui/thread.ts @@ -151,6 +151,12 @@ export const TuiThreadCommand = cmd({ model: args.model, prompt, }, + startServer: shouldStartServer + ? undefined + : async () => { + const server = await client.call("server", networkOpts) + return server.url + }, onExit: async () => { await client.call("shutdown", undefined) },