2025-12-10 03:32:04 +08:00
import { BusEvent } from "@/bus/bus-event"
import { Bus } from "@/bus"
import { GlobalBus } from "@/bus/global"
2025-06-01 02:41:00 +08:00
import { Log } from "../util/log"
2025-11-08 09:59:02 +08:00
import { describeRoute , generateSpecs , validator , resolver , openAPIRouteHandler } from "hono-openapi"
2025-06-01 02:41:00 +08:00
import { Hono } from "hono"
2025-09-15 15:28:08 +08:00
import { cors } from "hono/cors"
2025-10-24 21:46:28 +08:00
import { stream , streamSSE } from "hono/streaming"
2025-11-05 01:33:03 +08:00
import { proxy } from "hono/proxy"
2025-06-05 05:38:15 +08:00
import { Session } from "../session"
2025-10-27 03:50:41 +08:00
import z from "zod"
2025-06-01 02:41:00 +08:00
import { Provider } from "../provider/provider"
2025-12-10 03:32:04 +08:00
import { mapValues } from "remeda"
2025-11-25 01:56:00 +08:00
import { NamedError } from "@opencode-ai/util/error"
2025-06-13 11:10:03 +08:00
import { ModelsDev } from "../provider/models"
2025-07-01 02:46:18 +08:00
import { Ripgrep } from "../file/ripgrep"
2025-06-19 10:20:03 +08:00
import { Config } from "../config/config"
2025-07-03 05:08:06 +08:00
import { File } from "../file"
import { LSP } from "../lsp"
2025-11-01 23:14:39 +08:00
import { Format } from "../format"
2025-07-08 03:53:43 +08:00
import { MessageV2 } from "../session/message-v2"
2025-11-01 03:07:36 +08:00
import { TuiRoute } from "./tui"
2025-07-31 22:34:43 +08:00
import { Permission } from "../permission"
2025-09-02 05:15:49 +08:00
import { Instance } from "../project/instance"
2025-11-26 11:39:20 +08:00
import { Vcs } from "../project/vcs"
2025-08-08 04:32:12 +08:00
import { Agent } from "../agent/agent"
2025-08-15 04:24:46 +08:00
import { Auth } from "../auth"
2025-08-23 05:04:28 +08:00
import { Command } from "../command"
2025-11-21 13:21:06 +08:00
import { ProviderAuth } from "../provider/auth"
2025-09-02 05:15:49 +08:00
import { Global } from "../global"
import { ProjectRoute } from "./project"
2025-09-09 04:25:04 +08:00
import { ToolRegistry } from "../tool/registry"
import { zodToJsonSchema } from "zod-to-json-schema"
2025-09-13 17:46:14 +08:00
import { SessionPrompt } from "../session/prompt"
import { SessionCompaction } from "../session/compaction"
import { SessionRevert } from "../session/revert"
2025-09-17 13:16:49 +08:00
import { lazy } from "../util/lazy"
2025-10-07 06:51:57 +08:00
import { Todo } from "../session/todo"
2025-09-19 17:11:29 +08:00
import { InstanceBootstrap } from "../project/bootstrap"
2025-10-07 16:04:19 +08:00
import { MCP } from "../mcp"
2025-10-15 23:53:09 +08:00
import { Storage } from "../storage/storage"
import type { ContentfulStatusCode } from "hono/utils/http-status"
2025-11-01 03:07:36 +08:00
import { TuiEvent } from "@/cli/cmd/tui/event"
2025-10-21 05:58:48 +08:00
import { Snapshot } from "@/snapshot"
2025-10-24 04:28:20 +08:00
import { SessionSummary } from "@/session/summary"
2025-11-17 23:57:18 +08:00
import { SessionStatus } from "@/session/status"
2025-12-05 10:32:08 +08:00
import { upgradeWebSocket , websocket } from "hono/bun"
import { errors } from "./error"
import { Pty } from "@/pty"
2025-06-10 02:01:11 +08:00
2025-11-20 22:42:24 +08:00
// @ts-ignore This global is needed to prevent ai-sdk from logging warnings to stdout https://github.com/vercel/ai/blob/2dc67e0ef538307f21368db32d5a12345d98831b/packages/ai/src/logger/log-warnings.ts#L85
globalThis . AI_SDK_LOG_WARNINGS = false
2025-05-19 02:13:04 +08:00
export namespace Server {
2025-06-01 02:41:00 +08:00
const log = Log . create ( { service : "server" } )
2025-05-19 02:13:04 +08:00
2025-07-29 10:58:12 +08:00
export const Event = {
2025-12-10 03:32:04 +08:00
Connected : BusEvent.define ( "server.connected" , z . object ( { } ) ) ,
2025-07-29 10:58:12 +08:00
}
2025-09-02 05:15:49 +08:00
const app = new Hono ( )
2025-09-17 13:16:49 +08:00
export const App = lazy ( ( ) = >
app
. onError ( ( err , c ) = > {
log . error ( "failed" , {
error : err ,
} )
if ( err instanceof NamedError ) {
2025-10-15 23:53:09 +08:00
let status : ContentfulStatusCode
2025-10-19 00:49:29 +08:00
if ( err instanceof Storage . NotFoundError ) status = 404
else if ( err instanceof Provider . ModelNotFoundError ) status = 400
else status = 500
2025-10-15 23:53:09 +08:00
return c . json ( err . toObject ( ) , { status } )
2025-09-17 13:16:49 +08:00
}
2025-10-24 04:04:58 +08:00
const message = err instanceof Error && err . stack ? err.stack : err.toString ( )
2025-10-14 03:10:35 +08:00
return c . json ( new NamedError . Unknown ( { message } ) . toObject ( ) , {
2025-10-15 23:53:09 +08:00
status : 500 ,
2025-07-08 03:53:43 +08:00
} )
2025-06-05 08:49:28 +08:00
} )
2025-09-17 13:16:49 +08:00
. use ( async ( c , next ) = > {
const skipLogging = c . req . path === "/log"
if ( ! skipLogging ) {
log . info ( "request" , {
method : c.req.method ,
path : c.req.path ,
} )
}
2025-11-09 09:17:46 +08:00
const timer = log . time ( "request" , {
method : c.req.method ,
path : c.req.path ,
} )
2025-09-17 13:16:49 +08:00
await next ( )
if ( ! skipLogging ) {
2025-11-09 09:17:46 +08:00
timer . stop ( )
2025-09-17 13:16:49 +08:00
}
} )
2025-11-19 01:00:27 +08:00
. use ( cors ( ) )
2025-11-15 01:32:43 +08:00
. get (
"/global/event" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Get global events" ,
description : "Subscribe to global events from the OpenCode system using server-sent events." ,
2025-11-15 01:42:32 +08:00
operationId : "global.event" ,
2025-11-15 01:32:43 +08:00
responses : {
200 : {
description : "Event stream" ,
content : {
"text/event-stream" : {
schema : resolver (
2025-11-15 01:35:44 +08:00
z
. object ( {
directory : z.string ( ) ,
2025-12-10 03:32:04 +08:00
payload : BusEvent.payloads ( ) ,
2025-11-15 01:35:44 +08:00
} )
. meta ( {
ref : "GlobalEvent" ,
} ) ,
2025-11-15 01:32:43 +08:00
) ,
} ,
} ,
} ,
} ,
} ) ,
async ( c ) = > {
log . info ( "global event connected" )
return streamSSE ( c , async ( stream ) = > {
async function handler ( event : any ) {
await stream . writeSSE ( {
data : JSON.stringify ( event ) ,
} )
}
GlobalBus . on ( "event" , handler )
await new Promise < void > ( ( resolve ) = > {
stream . onAbort ( ( ) = > {
GlobalBus . off ( "event" , handler )
resolve ( )
log . info ( "global event disconnected" )
} )
} )
} )
} ,
)
2025-09-17 13:16:49 +08:00
. use ( async ( c , next ) = > {
2025-11-19 01:00:27 +08:00
const directory = c . req . query ( "directory" ) ? ? c . req . header ( "x-opencode-directory" ) ? ? process . cwd ( )
2025-09-19 17:11:29 +08:00
return Instance . provide ( {
directory ,
init : InstanceBootstrap ,
async fn() {
return next ( )
} ,
2025-09-02 05:15:49 +08:00
} )
2025-06-04 01:00:27 +08:00
} )
2025-09-17 13:16:49 +08:00
. get (
"/doc" ,
openAPIRouteHandler ( app , {
documentation : {
info : {
title : "opencode" ,
version : "0.0.3" ,
description : "opencode api" ,
2025-05-29 23:32:55 +08:00
} ,
2025-09-17 13:16:49 +08:00
openapi : "3.1.1" ,
2025-05-29 23:32:55 +08:00
} ,
2025-09-17 13:16:49 +08:00
} ) ,
)
. use ( validator ( "query" , z . object ( { directory : z.string ( ) . optional ( ) } ) ) )
2025-12-05 10:32:08 +08:00
2025-09-17 13:16:49 +08:00
. route ( "/project" , ProjectRoute )
2025-12-05 10:32:08 +08:00
. get (
"/pty" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "List PTY sessions" ,
description : "Get a list of all active pseudo-terminal (PTY) sessions managed by OpenCode." ,
2025-12-05 10:32:08 +08:00
operationId : "pty.list" ,
responses : {
200 : {
description : "List of sessions" ,
content : {
"application/json" : {
schema : resolver ( Pty . Info . array ( ) ) ,
} ,
} ,
} ,
} ,
} ) ,
async ( c ) = > {
return c . json ( Pty . list ( ) )
} ,
)
. post (
"/pty" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Create PTY session" ,
description : "Create a new pseudo-terminal (PTY) session for running shell commands and processes." ,
2025-12-05 10:32:08 +08:00
operationId : "pty.create" ,
responses : {
200 : {
description : "Created session" ,
content : {
"application/json" : {
schema : resolver ( Pty . Info ) ,
} ,
} ,
} ,
. . . errors ( 400 ) ,
} ,
} ) ,
validator ( "json" , Pty . CreateInput ) ,
async ( c ) = > {
const info = await Pty . create ( c . req . valid ( "json" ) )
return c . json ( info )
} ,
)
2025-12-06 00:30:44 +08:00
. get (
2025-12-08 08:04:14 +08:00
"/pty/:ptyID" ,
2025-12-05 10:32:08 +08:00
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Get PTY session" ,
description : "Retrieve detailed information about a specific pseudo-terminal (PTY) session." ,
2025-12-06 00:30:44 +08:00
operationId : "pty.get" ,
2025-12-05 10:32:08 +08:00
responses : {
200 : {
2025-12-06 00:30:44 +08:00
description : "Session info" ,
2025-12-05 10:32:08 +08:00
content : {
"application/json" : {
schema : resolver ( Pty . Info ) ,
} ,
} ,
} ,
2025-12-06 00:30:44 +08:00
. . . errors ( 404 ) ,
2025-12-05 10:32:08 +08:00
} ,
} ) ,
2025-12-08 08:04:14 +08:00
validator ( "param" , z . object ( { ptyID : z.string ( ) } ) ) ,
2025-12-05 10:32:08 +08:00
async ( c ) = > {
2025-12-08 08:04:14 +08:00
const info = Pty . get ( c . req . valid ( "param" ) . ptyID )
2025-12-06 00:30:44 +08:00
if ( ! info ) {
throw new Storage . NotFoundError ( { message : "Session not found" } )
}
2025-12-05 10:32:08 +08:00
return c . json ( info )
} ,
)
2025-12-06 00:30:44 +08:00
. put (
2025-12-08 08:04:14 +08:00
"/pty/:ptyID" ,
2025-12-05 10:32:08 +08:00
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Update PTY session" ,
description : "Update properties of an existing pseudo-terminal (PTY) session." ,
2025-12-06 00:30:44 +08:00
operationId : "pty.update" ,
2025-12-05 10:32:08 +08:00
responses : {
200 : {
2025-12-06 00:30:44 +08:00
description : "Updated session" ,
2025-12-05 10:32:08 +08:00
content : {
"application/json" : {
schema : resolver ( Pty . Info ) ,
} ,
} ,
} ,
2025-12-06 00:30:44 +08:00
. . . errors ( 400 ) ,
2025-12-05 10:32:08 +08:00
} ,
} ) ,
2025-12-08 08:04:14 +08:00
validator ( "param" , z . object ( { ptyID : z.string ( ) } ) ) ,
2025-12-06 00:30:44 +08:00
validator ( "json" , Pty . UpdateInput ) ,
2025-12-05 10:32:08 +08:00
async ( c ) = > {
2025-12-08 08:04:14 +08:00
const info = await Pty . update ( c . req . valid ( "param" ) . ptyID , c . req . valid ( "json" ) )
2025-12-05 10:32:08 +08:00
return c . json ( info )
} ,
)
. delete (
2025-12-08 08:04:14 +08:00
"/pty/:ptyID" ,
2025-12-05 10:32:08 +08:00
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Remove PTY session" ,
description : "Remove and terminate a specific pseudo-terminal (PTY) session." ,
2025-12-05 10:32:08 +08:00
operationId : "pty.remove" ,
responses : {
200 : {
description : "Session removed" ,
content : {
"application/json" : {
schema : resolver ( z . boolean ( ) ) ,
} ,
} ,
} ,
. . . errors ( 404 ) ,
} ,
} ) ,
2025-12-08 08:04:14 +08:00
validator ( "param" , z . object ( { ptyID : z.string ( ) } ) ) ,
2025-12-05 10:32:08 +08:00
async ( c ) = > {
2025-12-08 08:04:14 +08:00
await Pty . remove ( c . req . valid ( "param" ) . ptyID )
2025-12-05 10:32:08 +08:00
return c . json ( true )
} ,
)
. get (
2025-12-08 08:04:14 +08:00
"/pty/:ptyID/connect" ,
2025-12-05 10:32:08 +08:00
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Connect to PTY session" ,
description :
"Establish a WebSocket connection to interact with a pseudo-terminal (PTY) session in real-time." ,
2025-12-05 10:32:08 +08:00
operationId : "pty.connect" ,
responses : {
200 : {
description : "Connected session" ,
content : {
"application/json" : {
schema : resolver ( z . boolean ( ) ) ,
} ,
} ,
} ,
2025-12-06 00:30:44 +08:00
. . . errors ( 404 ) ,
2025-12-05 10:32:08 +08:00
} ,
} ) ,
2025-12-08 08:04:14 +08:00
validator ( "param" , z . object ( { ptyID : z.string ( ) } ) ) ,
2025-12-05 10:32:08 +08:00
upgradeWebSocket ( ( c ) = > {
2025-12-08 08:04:14 +08:00
const id = c . req . param ( "ptyID" )
2025-12-05 10:32:08 +08:00
let handler : ReturnType < typeof Pty.connect >
2025-12-06 00:30:44 +08:00
if ( ! Pty . get ( id ) ) throw new Error ( "Session not found" )
2025-12-05 10:32:08 +08:00
return {
onOpen ( _event , ws ) {
handler = Pty . connect ( id , ws )
} ,
onMessage ( event ) {
handler ? . onMessage ( String ( event . data ) )
} ,
onClose() {
handler ? . onClose ( )
} ,
}
} ) ,
)
2025-09-17 13:16:49 +08:00
. get (
"/config" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Get configuration" ,
description : "Retrieve the current OpenCode configuration settings and preferences." ,
2025-09-17 13:16:49 +08:00
operationId : "config.get" ,
responses : {
200 : {
description : "Get config info" ,
content : {
"application/json" : {
schema : resolver ( Config . Info ) ,
} ,
2025-06-03 23:59:03 +08:00
} ,
} ,
} ,
2025-09-17 13:16:49 +08:00
} ) ,
async ( c ) = > {
return c . json ( await Config . get ( ) )
2025-06-03 23:59:03 +08:00
} ,
2025-09-17 13:16:49 +08:00
)
2025-11-12 10:30:38 +08:00
2025-09-26 14:34:51 +08:00
. patch (
"/config" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Update configuration" ,
description : "Update OpenCode configuration settings and preferences." ,
2025-09-26 14:34:51 +08:00
operationId : "config.update" ,
responses : {
200 : {
description : "Successfully updated config" ,
content : {
"application/json" : {
schema : resolver ( Config . Info ) ,
} ,
} ,
} ,
2025-10-15 23:53:09 +08:00
. . . errors ( 400 ) ,
2025-09-26 14:34:51 +08:00
} ,
} ) ,
validator ( "json" , Config . Info ) ,
async ( c ) = > {
const config = c . req . valid ( "json" )
await Config . update ( config )
return c . json ( config )
} ,
)
2025-09-17 13:16:49 +08:00
. get (
"/experimental/tool/ids" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "List tool IDs" ,
description :
"Get a list of all available tool IDs, including both built-in tools and dynamically registered tools." ,
2025-09-17 13:16:49 +08:00
operationId : "tool.ids" ,
responses : {
200 : {
description : "Tool IDs" ,
content : {
"application/json" : {
2025-10-24 04:04:58 +08:00
schema : resolver ( z . array ( z . string ( ) ) . meta ( { ref : "ToolIDs" } ) ) ,
2025-09-17 13:16:49 +08:00
} ,
2025-09-09 04:25:04 +08:00
} ,
} ,
2025-10-15 23:53:09 +08:00
. . . errors ( 400 ) ,
2025-09-09 04:25:04 +08:00
} ,
2025-09-17 13:16:49 +08:00
} ) ,
async ( c ) = > {
2025-09-18 15:58:21 +08:00
return c . json ( await ToolRegistry . ids ( ) )
2025-09-09 04:25:04 +08:00
} ,
2025-09-17 13:16:49 +08:00
)
. get (
"/experimental/tool" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "List tools" ,
description :
"Get a list of available tools with their JSON schema parameters for a specific provider and model combination." ,
2025-09-17 13:16:49 +08:00
operationId : "tool.list" ,
responses : {
200 : {
description : "Tools" ,
content : {
"application/json" : {
schema : resolver (
z
. array (
z
. object ( {
id : z.string ( ) ,
description : z.string ( ) ,
parameters : z.any ( ) ,
} )
. meta ( { ref : "ToolListItem" } ) ,
)
. meta ( { ref : "ToolList" } ) ,
) ,
} ,
2025-09-09 04:25:04 +08:00
} ,
} ,
2025-10-15 23:53:09 +08:00
. . . errors ( 400 ) ,
2025-09-09 04:25:04 +08:00
} ,
} ) ,
2025-09-17 13:16:49 +08:00
validator (
"query" ,
z . object ( {
provider : z.string ( ) ,
model : z.string ( ) ,
} ) ,
) ,
async ( c ) = > {
2025-12-04 10:09:03 +08:00
const { provider } = c . req . valid ( "query" )
const tools = await ToolRegistry . tools ( provider )
2025-09-17 13:16:49 +08:00
return c . json (
tools . map ( ( t ) = > ( {
id : t.id ,
description : t.description ,
// Handle both Zod schemas and plain JSON schemas
2025-11-08 09:59:02 +08:00
parameters : ( t . parameters as any ) ? . _def ? zodToJsonSchema ( t . parameters as any ) : t . parameters ,
2025-09-17 13:16:49 +08:00
} ) ) ,
)
} ,
)
2025-11-21 13:21:06 +08:00
. post (
"/instance/dispose" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Dispose instance" ,
description : "Clean up and dispose the current OpenCode instance, releasing all resources." ,
2025-11-21 13:21:06 +08:00
operationId : "instance.dispose" ,
responses : {
200 : {
description : "Instance disposed" ,
content : {
"application/json" : {
schema : resolver ( z . boolean ( ) ) ,
} ,
} ,
} ,
} ,
} ) ,
async ( c ) = > {
await Instance . dispose ( )
return c . json ( true )
} ,
)
2025-09-17 13:16:49 +08:00
. get (
"/path" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Get paths" ,
description : "Retrieve the current working directory and related path information for the OpenCode instance." ,
2025-09-17 13:16:49 +08:00
operationId : "path.get" ,
responses : {
200 : {
description : "Path" ,
content : {
"application/json" : {
schema : resolver (
z
. object ( {
state : z.string ( ) ,
config : z.string ( ) ,
worktree : z.string ( ) ,
directory : z.string ( ) ,
} )
. meta ( {
ref : "Path" ,
} ) ,
) ,
} ,
2025-06-04 02:24:45 +08:00
} ,
} ,
} ,
2025-09-17 13:16:49 +08:00
} ) ,
async ( c ) = > {
return c . json ( {
state : Global.Path.state ,
config : Global.Path.config ,
worktree : Instance.worktree ,
directory : Instance.directory ,
} )
2025-06-04 02:24:45 +08:00
} ,
2025-09-17 13:16:49 +08:00
)
2025-11-26 11:39:20 +08:00
. get (
"/vcs" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Get VCS info" ,
description : "Retrieve version control system (VCS) information for the current project, such as git branch." ,
2025-11-26 11:39:20 +08:00
operationId : "vcs.get" ,
responses : {
200 : {
description : "VCS info" ,
content : {
"application/json" : {
2025-11-27 01:34:48 +08:00
schema : resolver ( Vcs . Info ) ,
2025-11-26 11:39:20 +08:00
} ,
} ,
} ,
} ,
} ) ,
async ( c ) = > {
const branch = await Vcs . branch ( )
return c . json ( {
2025-11-27 01:34:48 +08:00
branch ,
2025-11-26 11:39:20 +08:00
} )
} ,
)
2025-09-17 13:16:49 +08:00
. get (
"/session" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "List sessions" ,
description : "Get a list of all OpenCode sessions, sorted by most recently updated." ,
2025-09-17 13:16:49 +08:00
operationId : "session.list" ,
responses : {
200 : {
description : "List of sessions" ,
content : {
"application/json" : {
schema : resolver ( Session . Info . array ( ) ) ,
} ,
2025-06-04 00:38:48 +08:00
} ,
} ,
} ,
2025-09-17 13:16:49 +08:00
} ) ,
async ( c ) = > {
const sessions = await Array . fromAsync ( Session . list ( ) )
sessions . sort ( ( a , b ) = > b . time . updated - a . time . updated )
return c . json ( sessions )
2025-06-04 00:38:48 +08:00
} ,
2025-09-17 13:16:49 +08:00
)
2025-11-17 23:57:18 +08:00
. get (
"/session/status" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Get session status" ,
description : "Retrieve the current status of all sessions, including active, idle, and completed states." ,
2025-11-17 23:57:18 +08:00
operationId : "session.status" ,
responses : {
200 : {
description : "Get session status" ,
content : {
"application/json" : {
schema : resolver ( z . record ( z . string ( ) , SessionStatus . Info ) ) ,
} ,
} ,
} ,
. . . errors ( 400 ) ,
} ,
} ) ,
async ( c ) = > {
const result = SessionStatus . list ( )
return c . json ( result )
} ,
)
2025-09-17 13:16:49 +08:00
. get (
2025-12-08 08:04:14 +08:00
"/session/:sessionID" ,
2025-09-17 13:16:49 +08:00
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Get session" ,
description : "Retrieve detailed information about a specific OpenCode session." ,
2025-12-08 10:55:36 +08:00
tags : [ "Session" ] ,
2025-09-17 13:16:49 +08:00
operationId : "session.get" ,
responses : {
200 : {
description : "Get session" ,
content : {
"application/json" : {
schema : resolver ( Session . Info ) ,
} ,
2025-06-02 03:01:57 +08:00
} ,
} ,
2025-10-15 23:53:09 +08:00
. . . errors ( 400 , 404 ) ,
2025-06-02 03:01:57 +08:00
} ,
2025-08-07 08:24:36 +08:00
} ) ,
2025-09-17 13:16:49 +08:00
validator (
"param" ,
z . object ( {
2025-12-08 08:04:14 +08:00
sessionID : Session.get.schema ,
2025-09-17 13:16:49 +08:00
} ) ,
) ,
async ( c ) = > {
2025-12-08 08:04:14 +08:00
const sessionID = c . req . valid ( "param" ) . sessionID
log . info ( "SEARCH" , { url : c.req.url } )
2025-09-17 13:16:49 +08:00
const session = await Session . get ( sessionID )
return c . json ( session )
} ,
)
. get (
2025-12-08 08:04:14 +08:00
"/session/:sessionID/children" ,
2025-09-17 13:16:49 +08:00
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Get session children" ,
2025-12-08 10:55:36 +08:00
tags : [ "Session" ] ,
2025-12-08 08:58:04 +08:00
description : "Retrieve all child sessions that were forked from the specified parent session." ,
2025-09-17 13:16:49 +08:00
operationId : "session.children" ,
responses : {
200 : {
description : "List of children" ,
content : {
"application/json" : {
schema : resolver ( Session . Info . array ( ) ) ,
} ,
2025-08-15 21:49:19 +08:00
} ,
} ,
2025-10-15 23:53:09 +08:00
. . . errors ( 400 , 404 ) ,
2025-08-15 21:49:19 +08:00
} ,
2025-09-02 05:15:49 +08:00
} ) ,
2025-09-17 13:16:49 +08:00
validator (
"param" ,
z . object ( {
2025-12-08 08:04:14 +08:00
sessionID : Session.children.schema ,
2025-09-17 13:16:49 +08:00
} ) ,
) ,
async ( c ) = > {
2025-12-08 08:04:14 +08:00
const sessionID = c . req . valid ( "param" ) . sessionID
2025-09-17 13:16:49 +08:00
const session = await Session . children ( sessionID )
return c . json ( session )
} ,
)
2025-10-07 06:51:57 +08:00
. get (
2025-12-08 08:04:14 +08:00
"/session/:sessionID/todo" ,
2025-10-07 06:51:57 +08:00
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Get session todos" ,
description : "Retrieve the todo list associated with a specific session, showing tasks and action items." ,
2025-10-07 06:51:57 +08:00
operationId : "session.todo" ,
responses : {
200 : {
description : "Todo list" ,
content : {
"application/json" : {
schema : resolver ( Todo . Info . array ( ) ) ,
} ,
} ,
} ,
2025-10-15 23:53:09 +08:00
. . . errors ( 400 , 404 ) ,
2025-10-07 06:51:57 +08:00
} ,
} ) ,
validator (
"param" ,
z . object ( {
2025-12-08 08:04:14 +08:00
sessionID : z.string ( ) . meta ( { description : "Session ID" } ) ,
2025-10-07 06:51:57 +08:00
} ) ,
) ,
async ( c ) = > {
2025-12-08 08:04:14 +08:00
const sessionID = c . req . valid ( "param" ) . sessionID
2025-10-07 06:51:57 +08:00
const todos = await Todo . get ( sessionID )
return c . json ( todos )
} ,
)
2025-09-17 13:16:49 +08:00
. post (
"/session" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Create session" ,
description : "Create a new OpenCode session for interacting with AI assistants and managing conversations." ,
2025-09-17 13:16:49 +08:00
operationId : "session.create" ,
responses : {
2025-10-15 23:53:09 +08:00
. . . errors ( 400 ) ,
2025-09-17 13:16:49 +08:00
200 : {
description : "Successfully created session" ,
content : {
"application/json" : {
schema : resolver ( Session . Info ) ,
} ,
2025-05-20 23:11:06 +08:00
} ,
} ,
} ,
2025-09-17 13:16:49 +08:00
} ) ,
2025-10-07 07:37:30 +08:00
validator ( "json" , Session . create . schema . optional ( ) ) ,
2025-09-17 13:16:49 +08:00
async ( c ) = > {
const body = c . req . valid ( "json" ) ? ? { }
2025-10-07 07:37:30 +08:00
const session = await Session . create ( body )
2025-09-17 13:16:49 +08:00
return c . json ( session )
2025-05-20 23:11:06 +08:00
} ,
2025-09-17 13:16:49 +08:00
)
. delete (
2025-12-08 08:04:14 +08:00
"/session/:sessionID" ,
2025-09-17 13:16:49 +08:00
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Delete session" ,
description : "Delete a session and permanently remove all associated data, including messages and history." ,
2025-09-17 13:16:49 +08:00
operationId : "session.delete" ,
responses : {
200 : {
description : "Successfully deleted session" ,
content : {
"application/json" : {
schema : resolver ( z . boolean ( ) ) ,
} ,
2025-05-27 06:06:41 +08:00
} ,
} ,
2025-10-15 23:53:09 +08:00
. . . errors ( 400 , 404 ) ,
2025-05-27 06:06:41 +08:00
} ,
2025-09-02 05:15:49 +08:00
} ) ,
2025-09-17 13:16:49 +08:00
validator (
"param" ,
z . object ( {
2025-12-08 08:04:14 +08:00
sessionID : Session.remove.schema ,
2025-09-17 13:16:49 +08:00
} ) ,
) ,
async ( c ) = > {
2025-12-08 08:04:14 +08:00
const sessionID = c . req . valid ( "param" ) . sessionID
2025-11-01 03:07:36 +08:00
await Session . remove ( sessionID )
await Bus . publish ( TuiEvent . CommandExecute , {
command : "session.list" ,
} )
2025-09-17 13:16:49 +08:00
return c . json ( true )
} ,
)
. patch (
2025-12-08 08:04:14 +08:00
"/session/:sessionID" ,
2025-09-17 13:16:49 +08:00
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Update session" ,
description : "Update properties of an existing session, such as title or other metadata." ,
2025-09-17 13:16:49 +08:00
operationId : "session.update" ,
responses : {
200 : {
description : "Successfully updated session" ,
content : {
"application/json" : {
schema : resolver ( Session . Info ) ,
} ,
2025-08-13 04:22:03 +08:00
} ,
} ,
2025-10-15 23:53:09 +08:00
. . . errors ( 400 , 404 ) ,
2025-08-13 04:22:03 +08:00
} ,
} ) ,
2025-09-17 13:16:49 +08:00
validator (
"param" ,
z . object ( {
2025-12-08 08:04:14 +08:00
sessionID : z.string ( ) ,
2025-09-17 13:16:49 +08:00
} ) ,
) ,
validator (
"json" ,
z . object ( {
title : z.string ( ) . optional ( ) ,
} ) ,
) ,
async ( c ) = > {
2025-12-08 08:04:14 +08:00
const sessionID = c . req . valid ( "param" ) . sessionID
2025-09-17 13:16:49 +08:00
const updates = c . req . valid ( "json" )
2025-08-13 04:22:03 +08:00
2025-09-17 13:16:49 +08:00
const updatedSession = await Session . update ( sessionID , ( session ) = > {
if ( updates . title !== undefined ) {
session . title = updates . title
}
} )
2025-08-13 04:22:03 +08:00
2025-09-17 13:16:49 +08:00
return c . json ( updatedSession )
} ,
)
. post (
2025-12-08 08:04:14 +08:00
"/session/:sessionID/init" ,
2025-09-17 13:16:49 +08:00
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Initialize session" ,
description :
"Analyze the current application and create an AGENTS.md file with project-specific agent configurations." ,
2025-09-17 13:16:49 +08:00
operationId : "session.init" ,
responses : {
200 : {
description : "200" ,
content : {
"application/json" : {
schema : resolver ( z . boolean ( ) ) ,
} ,
2025-06-21 03:22:41 +08:00
} ,
} ,
2025-10-15 23:53:09 +08:00
. . . errors ( 400 , 404 ) ,
2025-06-21 03:22:41 +08:00
} ,
2025-09-02 05:15:49 +08:00
} ) ,
2025-09-17 13:16:49 +08:00
validator (
"param" ,
z . object ( {
2025-12-08 08:04:14 +08:00
sessionID : z.string ( ) . meta ( { description : "Session ID" } ) ,
2025-09-17 13:16:49 +08:00
} ) ,
) ,
2025-10-07 07:37:30 +08:00
validator ( "json" , Session . initialize . schema . omit ( { sessionID : true } ) ) ,
2025-09-17 13:16:49 +08:00
async ( c ) = > {
2025-12-08 08:04:14 +08:00
const sessionID = c . req . valid ( "param" ) . sessionID
2025-09-17 13:16:49 +08:00
const body = c . req . valid ( "json" )
await Session . initialize ( { . . . body , sessionID } )
return c . json ( true )
} ,
)
2025-10-07 06:51:57 +08:00
. post (
2025-12-08 08:04:14 +08:00
"/session/:sessionID/fork" ,
2025-10-07 06:51:57 +08:00
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Fork session" ,
description : "Create a new session by forking an existing session at a specific message point." ,
2025-10-07 06:51:57 +08:00
operationId : "session.fork" ,
responses : {
200 : {
description : "200" ,
content : {
"application/json" : {
schema : resolver ( Session . Info ) ,
} ,
} ,
} ,
} ,
} ) ,
validator (
"param" ,
z . object ( {
2025-12-08 08:04:14 +08:00
sessionID : Session.fork.schema.shape.sessionID ,
2025-10-07 06:51:57 +08:00
} ) ,
) ,
validator ( "json" , Session . fork . schema . omit ( { sessionID : true } ) ) ,
async ( c ) = > {
2025-12-08 08:04:14 +08:00
const sessionID = c . req . valid ( "param" ) . sessionID
2025-10-07 06:51:57 +08:00
const body = c . req . valid ( "json" )
const result = await Session . fork ( { . . . body , sessionID } )
return c . json ( result )
} ,
)
2025-09-17 13:16:49 +08:00
. post (
2025-12-08 08:04:14 +08:00
"/session/:sessionID/abort" ,
2025-09-17 13:16:49 +08:00
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Abort session" ,
description : "Abort an active session and stop any ongoing AI processing or command execution." ,
2025-09-17 13:16:49 +08:00
operationId : "session.abort" ,
responses : {
200 : {
description : "Aborted session" ,
content : {
"application/json" : {
schema : resolver ( z . boolean ( ) ) ,
} ,
2025-05-28 03:34:46 +08:00
} ,
} ,
2025-10-15 23:53:09 +08:00
. . . errors ( 400 , 404 ) ,
2025-05-28 03:34:46 +08:00
} ,
2025-09-02 05:15:49 +08:00
} ) ,
2025-09-17 13:16:49 +08:00
validator (
"param" ,
z . object ( {
2025-12-08 08:04:14 +08:00
sessionID : z.string ( ) ,
2025-09-17 13:16:49 +08:00
} ) ,
) ,
async ( c ) = > {
2025-12-08 08:04:14 +08:00
SessionPrompt . cancel ( c . req . valid ( "param" ) . sessionID )
2025-11-17 23:57:18 +08:00
return c . json ( true )
2025-09-17 13:16:49 +08:00
} ,
)
. post (
2025-12-08 08:04:14 +08:00
"/session/:sessionID/share" ,
2025-09-17 13:16:49 +08:00
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Share session" ,
description : "Create a shareable link for a session, allowing others to view the conversation." ,
2025-09-17 13:16:49 +08:00
operationId : "session.share" ,
responses : {
200 : {
description : "Successfully shared session" ,
content : {
"application/json" : {
schema : resolver ( Session . Info ) ,
} ,
2025-05-29 03:07:51 +08:00
} ,
} ,
2025-10-15 23:53:09 +08:00
. . . errors ( 400 , 404 ) ,
2025-05-29 03:07:51 +08:00
} ,
2025-09-02 05:15:49 +08:00
} ) ,
2025-09-17 13:16:49 +08:00
validator (
"param" ,
z . object ( {
2025-12-08 08:04:14 +08:00
sessionID : z.string ( ) ,
2025-09-17 13:16:49 +08:00
} ) ,
) ,
async ( c ) = > {
2025-12-08 08:04:14 +08:00
const sessionID = c . req . valid ( "param" ) . sessionID
await Session . share ( sessionID )
const session = await Session . get ( sessionID )
2025-09-17 13:16:49 +08:00
return c . json ( session )
} ,
)
2025-10-21 05:58:48 +08:00
. get (
2025-12-08 08:04:14 +08:00
"/session/:sessionID/diff" ,
2025-10-21 05:58:48 +08:00
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Get message diff" ,
description : "Get the file changes (diff) that resulted from a specific user message in the session." ,
2025-10-21 05:58:48 +08:00
operationId : "session.diff" ,
responses : {
200 : {
description : "Successfully retrieved diff" ,
content : {
"application/json" : {
schema : resolver ( Snapshot . FileDiff . array ( ) ) ,
} ,
} ,
} ,
} ,
} ) ,
validator (
"param" ,
z . object ( {
2025-12-08 08:04:14 +08:00
sessionID : SessionSummary.diff.schema.shape.sessionID ,
2025-10-21 05:58:48 +08:00
} ) ,
) ,
validator (
"query" ,
z . object ( {
2025-10-24 04:28:20 +08:00
messageID : SessionSummary.diff.schema.shape.messageID ,
2025-10-21 05:58:48 +08:00
} ) ,
) ,
async ( c ) = > {
const query = c . req . valid ( "query" )
const params = c . req . valid ( "param" )
2025-10-24 04:28:20 +08:00
const result = await SessionSummary . diff ( {
2025-12-08 08:04:14 +08:00
sessionID : params.sessionID ,
2025-10-21 05:58:48 +08:00
messageID : query.messageID ,
} )
return c . json ( result )
} ,
)
2025-09-17 13:16:49 +08:00
. delete (
2025-12-08 08:04:14 +08:00
"/session/:sessionID/share" ,
2025-09-17 13:16:49 +08:00
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Unshare session" ,
description : "Remove the shareable link for a session, making it private again." ,
2025-09-17 13:16:49 +08:00
operationId : "session.unshare" ,
responses : {
200 : {
description : "Successfully unshared session" ,
content : {
"application/json" : {
schema : resolver ( Session . Info ) ,
} ,
2025-06-25 00:07:41 +08:00
} ,
} ,
2025-10-15 23:53:09 +08:00
. . . errors ( 400 , 404 ) ,
2025-06-25 00:07:41 +08:00
} ,
2025-09-02 05:15:49 +08:00
} ) ,
2025-09-17 13:16:49 +08:00
validator (
"param" ,
z . object ( {
2025-12-08 08:04:14 +08:00
sessionID : Session.unshare.schema ,
2025-09-17 13:16:49 +08:00
} ) ,
) ,
async ( c ) = > {
2025-12-08 08:04:14 +08:00
const sessionID = c . req . valid ( "param" ) . sessionID
await Session . unshare ( sessionID )
const session = await Session . get ( sessionID )
2025-09-17 13:16:49 +08:00
return c . json ( session )
} ,
)
. post (
2025-12-08 08:04:14 +08:00
"/session/:sessionID/summarize" ,
2025-09-17 13:16:49 +08:00
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Summarize session" ,
description : "Generate a concise summary of the session using AI compaction to preserve key information." ,
2025-09-17 13:16:49 +08:00
operationId : "session.summarize" ,
responses : {
200 : {
description : "Summarized session" ,
content : {
"application/json" : {
schema : resolver ( z . boolean ( ) ) ,
} ,
2025-05-30 01:17:56 +08:00
} ,
} ,
2025-10-15 23:53:09 +08:00
. . . errors ( 400 , 404 ) ,
2025-05-30 01:17:56 +08:00
} ,
2025-09-02 05:15:49 +08:00
} ) ,
2025-09-17 13:16:49 +08:00
validator (
"param" ,
z . object ( {
2025-12-08 08:04:14 +08:00
sessionID : z.string ( ) . meta ( { description : "Session ID" } ) ,
2025-09-17 13:16:49 +08:00
} ) ,
) ,
validator (
"json" ,
z . object ( {
providerID : z.string ( ) ,
modelID : z.string ( ) ,
} ) ,
) ,
async ( c ) = > {
2025-12-08 08:04:14 +08:00
const sessionID = c . req . valid ( "param" ) . sessionID
2025-09-17 13:16:49 +08:00
const body = c . req . valid ( "json" )
2025-12-08 08:04:14 +08:00
const msgs = await Session . messages ( { sessionID } )
2025-11-21 16:13:10 +08:00
let currentAgent = "build"
for ( let i = msgs . length - 1 ; i >= 0 ; i -- ) {
const info = msgs [ i ] . info
if ( info . role === "user" ) {
currentAgent = info . agent || "build"
break
}
}
2025-11-17 23:57:18 +08:00
await SessionCompaction . create ( {
2025-12-08 08:04:14 +08:00
sessionID ,
2025-11-21 16:13:10 +08:00
agent : currentAgent ,
2025-11-17 23:57:18 +08:00
model : {
providerID : body.providerID ,
modelID : body.modelID ,
} ,
2025-11-26 02:10:56 +08:00
auto : false ,
2025-11-17 23:57:18 +08:00
} )
2025-12-08 08:04:14 +08:00
await SessionPrompt . loop ( sessionID )
2025-09-17 13:16:49 +08:00
return c . json ( true )
} ,
)
. get (
2025-12-08 08:04:14 +08:00
"/session/:sessionID/message" ,
2025-09-17 13:16:49 +08:00
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Get session messages" ,
description : "Retrieve all messages in a session, including user prompts and AI responses." ,
2025-09-17 13:16:49 +08:00
operationId : "session.messages" ,
responses : {
200 : {
description : "List of messages" ,
content : {
"application/json" : {
schema : resolver ( MessageV2 . WithParts . array ( ) ) ,
} ,
2025-07-31 22:34:43 +08:00
} ,
} ,
2025-10-15 23:53:09 +08:00
. . . errors ( 400 , 404 ) ,
2025-07-31 22:34:43 +08:00
} ,
2025-09-02 05:15:49 +08:00
} ) ,
2025-09-17 13:16:49 +08:00
validator (
"param" ,
z . object ( {
2025-12-08 08:04:14 +08:00
sessionID : z.string ( ) . meta ( { description : "Session ID" } ) ,
2025-09-17 13:16:49 +08:00
} ) ,
) ,
2025-11-07 02:20:13 +08:00
validator (
"query" ,
z . object ( {
2025-11-08 02:03:54 +08:00
limit : z.coerce.number ( ) . optional ( ) ,
2025-11-07 02:20:13 +08:00
} ) ,
) ,
2025-09-17 13:16:49 +08:00
async ( c ) = > {
2025-11-07 02:20:13 +08:00
const query = c . req . valid ( "query" )
const messages = await Session . messages ( {
2025-12-08 08:04:14 +08:00
sessionID : c.req.valid ( "param" ) . sessionID ,
2025-11-07 02:20:13 +08:00
limit : query.limit ,
} )
return c . json ( messages )
2025-09-17 13:16:49 +08:00
} ,
)
2025-11-05 02:32:56 +08:00
. get (
2025-12-08 08:04:14 +08:00
"/session/:sessionID/diff" ,
2025-11-05 02:32:56 +08:00
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Get session diff" ,
description : "Get all file changes (diffs) made during this session." ,
2025-11-05 02:32:56 +08:00
operationId : "session.diff" ,
responses : {
200 : {
description : "List of diffs" ,
content : {
"application/json" : {
schema : resolver ( Snapshot . FileDiff . array ( ) ) ,
} ,
} ,
} ,
. . . errors ( 400 , 404 ) ,
} ,
} ) ,
validator (
"param" ,
z . object ( {
2025-12-08 08:04:14 +08:00
sessionID : z.string ( ) . meta ( { description : "Session ID" } ) ,
2025-11-05 02:32:56 +08:00
} ) ,
) ,
async ( c ) = > {
2025-12-08 08:04:14 +08:00
const diff = await Session . diff ( c . req . valid ( "param" ) . sessionID )
2025-11-05 02:32:56 +08:00
return c . json ( diff )
} ,
)
2025-09-17 13:16:49 +08:00
. get (
2025-12-08 08:04:14 +08:00
"/session/:sessionID/message/:messageID" ,
2025-09-17 13:16:49 +08:00
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Get message" ,
description : "Retrieve a specific message from a session by its message ID." ,
2025-09-17 13:16:49 +08:00
operationId : "session.message" ,
responses : {
200 : {
description : "Message" ,
content : {
"application/json" : {
schema : resolver (
z . object ( {
info : MessageV2.Info ,
parts : MessageV2.Part.array ( ) ,
} ) ,
) ,
} ,
2025-05-29 03:07:51 +08:00
} ,
} ,
2025-10-15 23:53:09 +08:00
. . . errors ( 400 , 404 ) ,
2025-05-29 03:07:51 +08:00
} ,
2025-09-02 05:15:49 +08:00
} ) ,
2025-09-17 13:16:49 +08:00
validator (
"param" ,
z . object ( {
2025-12-08 08:04:14 +08:00
sessionID : z.string ( ) . meta ( { description : "Session ID" } ) ,
2025-09-17 13:16:49 +08:00
messageID : z.string ( ) . meta ( { description : "Message ID" } ) ,
} ) ,
) ,
async ( c ) = > {
const params = c . req . valid ( "param" )
2025-11-08 03:46:58 +08:00
const message = await MessageV2 . get ( {
2025-12-08 08:04:14 +08:00
sessionID : params.sessionID ,
2025-10-21 05:58:48 +08:00
messageID : params.messageID ,
} )
2025-09-17 13:16:49 +08:00
return c . json ( message )
} ,
)
. post (
2025-12-08 08:04:14 +08:00
"/session/:sessionID/message" ,
2025-09-17 13:16:49 +08:00
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Send message" ,
description : "Create and send a new message to a session, streaming the AI response." ,
2025-09-17 13:16:49 +08:00
operationId : "session.prompt" ,
responses : {
200 : {
description : "Created message" ,
content : {
"application/json" : {
schema : resolver (
z . object ( {
info : MessageV2.Assistant ,
parts : MessageV2.Part.array ( ) ,
} ) ,
) ,
} ,
2025-08-23 05:04:28 +08:00
} ,
} ,
2025-10-15 23:53:09 +08:00
. . . errors ( 400 , 404 ) ,
2025-08-23 05:04:28 +08:00
} ,
2025-09-02 05:15:49 +08:00
} ) ,
2025-09-17 13:16:49 +08:00
validator (
"param" ,
z . object ( {
2025-12-08 08:04:14 +08:00
sessionID : z.string ( ) . meta ( { description : "Session ID" } ) ,
2025-09-17 13:16:49 +08:00
} ) ,
) ,
validator ( "json" , SessionPrompt . PromptInput . omit ( { sessionID : true } ) ) ,
async ( c ) = > {
2025-10-24 21:46:28 +08:00
c . status ( 200 )
c . header ( "Content-Type" , "application/json" )
return stream ( c , async ( stream ) = > {
2025-12-08 08:04:14 +08:00
const sessionID = c . req . valid ( "param" ) . sessionID
2025-10-24 21:46:28 +08:00
const body = c . req . valid ( "json" )
const msg = await SessionPrompt . prompt ( { . . . body , sessionID } )
stream . write ( JSON . stringify ( msg ) )
} )
2025-09-17 13:16:49 +08:00
} ,
)
2025-11-25 13:52:57 +08:00
. post (
2025-12-08 08:04:14 +08:00
"/session/:sessionID/prompt_async" ,
2025-11-25 13:52:57 +08:00
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Send async message" ,
description :
"Create and send a new message to a session asynchronously, starting the session if needed and returning immediately." ,
2025-11-25 13:52:57 +08:00
operationId : "session.prompt_async" ,
responses : {
204 : {
description : "Prompt accepted" ,
} ,
. . . errors ( 400 , 404 ) ,
} ,
} ) ,
validator (
"param" ,
z . object ( {
2025-12-08 08:04:14 +08:00
sessionID : z.string ( ) . meta ( { description : "Session ID" } ) ,
2025-11-25 13:52:57 +08:00
} ) ,
) ,
validator ( "json" , SessionPrompt . PromptInput . omit ( { sessionID : true } ) ) ,
async ( c ) = > {
c . status ( 204 )
c . header ( "Content-Type" , "application/json" )
2025-12-04 10:09:03 +08:00
return stream ( c , async ( ) = > {
2025-12-08 08:04:14 +08:00
const sessionID = c . req . valid ( "param" ) . sessionID
2025-11-25 13:52:57 +08:00
const body = c . req . valid ( "json" )
SessionPrompt . prompt ( { . . . body , sessionID } )
} )
} ,
)
2025-09-17 13:16:49 +08:00
. post (
2025-12-08 08:04:14 +08:00
"/session/:sessionID/command" ,
2025-09-17 13:16:49 +08:00
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Send command" ,
description : "Send a new command to a session for execution by the AI assistant." ,
2025-09-17 13:16:49 +08:00
operationId : "session.command" ,
responses : {
200 : {
description : "Created message" ,
content : {
"application/json" : {
schema : resolver (
z . object ( {
info : MessageV2.Assistant ,
parts : MessageV2.Part.array ( ) ,
} ) ,
) ,
} ,
2025-08-14 01:28:51 +08:00
} ,
} ,
2025-10-15 23:53:09 +08:00
. . . errors ( 400 , 404 ) ,
2025-08-14 01:28:51 +08:00
} ,
2025-09-02 05:15:49 +08:00
} ) ,
2025-09-17 13:16:49 +08:00
validator (
"param" ,
z . object ( {
2025-12-08 08:04:14 +08:00
sessionID : z.string ( ) . meta ( { description : "Session ID" } ) ,
2025-09-17 13:16:49 +08:00
} ) ,
) ,
validator ( "json" , SessionPrompt . CommandInput . omit ( { sessionID : true } ) ) ,
async ( c ) = > {
2025-12-08 08:04:14 +08:00
const sessionID = c . req . valid ( "param" ) . sessionID
2025-09-17 13:16:49 +08:00
const body = c . req . valid ( "json" )
const msg = await SessionPrompt . command ( { . . . body , sessionID } )
return c . json ( msg )
} ,
)
. post (
2025-12-08 08:04:14 +08:00
"/session/:sessionID/shell" ,
2025-09-17 13:16:49 +08:00
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Run shell command" ,
description : "Execute a shell command within the session context and return the AI's response." ,
2025-09-17 13:16:49 +08:00
operationId : "session.shell" ,
responses : {
200 : {
description : "Created message" ,
content : {
"application/json" : {
schema : resolver ( MessageV2 . Assistant ) ,
} ,
2025-07-24 08:30:46 +08:00
} ,
} ,
2025-10-15 23:53:09 +08:00
. . . errors ( 400 , 404 ) ,
2025-07-24 08:30:46 +08:00
} ,
2025-09-02 05:15:49 +08:00
} ) ,
2025-09-17 13:16:49 +08:00
validator (
"param" ,
z . object ( {
2025-12-08 08:04:14 +08:00
sessionID : z.string ( ) . meta ( { description : "Session ID" } ) ,
2025-09-17 13:16:49 +08:00
} ) ,
) ,
validator ( "json" , SessionPrompt . ShellInput . omit ( { sessionID : true } ) ) ,
async ( c ) = > {
2025-12-08 08:04:14 +08:00
const sessionID = c . req . valid ( "param" ) . sessionID
2025-09-17 13:16:49 +08:00
const body = c . req . valid ( "json" )
const msg = await SessionPrompt . shell ( { . . . body , sessionID } )
return c . json ( msg )
} ,
)
. post (
2025-12-08 08:04:14 +08:00
"/session/:sessionID/revert" ,
2025-09-17 13:16:49 +08:00
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Revert message" ,
description : "Revert a specific message in a session, undoing its effects and restoring the previous state." ,
2025-09-17 13:16:49 +08:00
operationId : "session.revert" ,
responses : {
200 : {
description : "Updated session" ,
content : {
"application/json" : {
schema : resolver ( Session . Info ) ,
} ,
2025-07-24 08:30:46 +08:00
} ,
} ,
2025-10-15 23:53:09 +08:00
. . . errors ( 400 , 404 ) ,
2025-07-24 08:30:46 +08:00
} ,
2025-09-02 05:15:49 +08:00
} ) ,
2025-09-17 13:16:49 +08:00
validator (
"param" ,
z . object ( {
2025-12-08 08:04:14 +08:00
sessionID : z.string ( ) ,
2025-09-17 13:16:49 +08:00
} ) ,
) ,
validator ( "json" , SessionRevert . RevertInput . omit ( { sessionID : true } ) ) ,
async ( c ) = > {
2025-12-08 08:04:14 +08:00
const sessionID = c . req . valid ( "param" ) . sessionID
2025-09-17 13:16:49 +08:00
log . info ( "revert" , c . req . valid ( "json" ) )
2025-10-21 05:58:48 +08:00
const session = await SessionRevert . revert ( {
2025-12-08 08:04:14 +08:00
sessionID ,
2025-10-21 05:58:48 +08:00
. . . c . req . valid ( "json" ) ,
} )
2025-09-17 13:16:49 +08:00
return c . json ( session )
} ,
)
. post (
2025-12-08 08:04:14 +08:00
"/session/:sessionID/unrevert" ,
2025-09-17 13:16:49 +08:00
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Restore reverted messages" ,
description : "Restore all previously reverted messages in a session." ,
2025-09-17 13:16:49 +08:00
operationId : "session.unrevert" ,
responses : {
200 : {
description : "Updated session" ,
content : {
"application/json" : {
schema : resolver ( Session . Info ) ,
} ,
2025-07-31 22:34:43 +08:00
} ,
} ,
2025-10-15 23:53:09 +08:00
. . . errors ( 400 , 404 ) ,
2025-07-31 22:34:43 +08:00
} ,
2025-09-02 05:15:49 +08:00
} ) ,
2025-09-17 13:16:49 +08:00
validator (
"param" ,
z . object ( {
2025-12-08 08:04:14 +08:00
sessionID : z.string ( ) ,
2025-09-17 13:16:49 +08:00
} ) ,
) ,
async ( c ) = > {
2025-12-08 08:04:14 +08:00
const sessionID = c . req . valid ( "param" ) . sessionID
const session = await SessionRevert . unrevert ( { sessionID } )
2025-09-17 13:16:49 +08:00
return c . json ( session )
} ,
)
. post (
2025-12-08 08:04:14 +08:00
"/session/:sessionID/permissions/:permissionID" ,
2025-09-17 13:16:49 +08:00
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Respond to permission" ,
description : "Approve or deny a permission request from the AI assistant." ,
2025-12-08 08:04:14 +08:00
operationId : "permission.respond" ,
2025-09-17 13:16:49 +08:00
responses : {
200 : {
description : "Permission processed successfully" ,
content : {
"application/json" : {
schema : resolver ( z . boolean ( ) ) ,
} ,
2025-08-23 05:04:28 +08:00
} ,
} ,
2025-10-15 23:53:09 +08:00
. . . errors ( 400 , 404 ) ,
2025-08-23 05:04:28 +08:00
} ,
2025-09-02 05:15:49 +08:00
} ) ,
2025-09-17 13:16:49 +08:00
validator (
"param" ,
z . object ( {
2025-12-08 08:04:14 +08:00
sessionID : z.string ( ) ,
2025-09-17 13:16:49 +08:00
permissionID : z.string ( ) ,
} ) ,
) ,
validator ( "json" , z . object ( { response : Permission.Response } ) ) ,
async ( c ) = > {
const params = c . req . valid ( "param" )
2025-12-08 08:04:14 +08:00
const sessionID = params . sessionID
2025-09-17 13:16:49 +08:00
const permissionID = params . permissionID
2025-10-21 05:58:48 +08:00
Permission . respond ( {
2025-12-08 08:04:14 +08:00
sessionID ,
2025-10-21 05:58:48 +08:00
permissionID ,
response : c.req.valid ( "json" ) . response ,
} )
2025-09-17 13:16:49 +08:00
return c . json ( true )
} ,
)
. get (
"/command" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "List commands" ,
description : "Get a list of all available commands in the OpenCode system." ,
2025-09-17 13:16:49 +08:00
operationId : "command.list" ,
responses : {
200 : {
description : "List of commands" ,
content : {
"application/json" : {
schema : resolver ( Command . Info . array ( ) ) ,
} ,
2025-05-29 00:53:22 +08:00
} ,
} ,
} ,
2025-09-17 13:16:49 +08:00
} ) ,
async ( c ) = > {
const commands = await Command . list ( )
return c . json ( commands )
2025-05-29 00:53:22 +08:00
} ,
2025-09-17 13:16:49 +08:00
)
. get (
"/config/providers" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "List config providers" ,
description : "Get a list of all configured AI providers and their default models." ,
2025-09-17 13:16:49 +08:00
operationId : "config.providers" ,
responses : {
200 : {
description : "List of providers" ,
content : {
"application/json" : {
schema : resolver (
z . object ( {
2025-12-04 10:09:03 +08:00
providers : Provider.Info.array ( ) ,
2025-09-17 13:16:49 +08:00
default : z . record ( z . string ( ) , z . string ( ) ) ,
} ) ,
) ,
} ,
2025-07-03 05:08:06 +08:00
} ,
} ,
} ,
2025-09-17 13:16:49 +08:00
} ) ,
async ( c ) = > {
2025-11-09 09:17:46 +08:00
using _ = log . time ( "providers" )
2025-12-04 10:09:03 +08:00
const providers = await Provider . list ( ) . then ( ( x ) = > mapValues ( x , ( item ) = > item ) )
2025-09-17 13:16:49 +08:00
return c . json ( {
providers : Object.values ( providers ) ,
2025-11-08 09:59:02 +08:00
default : mapValues ( providers , ( item ) = > Provider . sort ( Object . values ( item . models ) ) [ 0 ] . id ) ,
2025-09-17 13:16:49 +08:00
} )
2025-07-03 05:08:06 +08:00
} ,
2025-09-17 13:16:49 +08:00
)
2025-11-21 13:21:06 +08:00
. get (
"/provider" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "List providers" ,
description : "Get a list of all available AI providers, including both available and connected ones." ,
2025-11-21 13:21:06 +08:00
operationId : "provider.list" ,
responses : {
200 : {
description : "List of providers" ,
content : {
"application/json" : {
schema : resolver (
z . object ( {
all : ModelsDev.Provider.array ( ) ,
default : z . record ( z . string ( ) , z . string ( ) ) ,
connected : z.array ( z . string ( ) ) ,
} ) ,
) ,
} ,
} ,
} ,
} ,
} ) ,
async ( c ) = > {
2025-12-09 05:28:32 +08:00
const config = await Config . get ( )
const disabled = new Set ( config . disabled_providers ? ? [ ] )
const enabled = config . enabled_providers ? new Set ( config . enabled_providers ) : undefined
const allProviders = await ModelsDev . get ( )
const filteredProviders : Record < string , ( typeof allProviders ) [ string ] > = { }
for ( const [ key , value ] of Object . entries ( allProviders ) ) {
if ( ( enabled ? enabled . has ( key ) : true ) && ! disabled . has ( key ) ) {
filteredProviders [ key ] = value
}
}
2025-12-11 02:49:45 +08:00
const connected = await Provider . list ( )
const providers = Object . assign (
mapValues ( filteredProviders , ( x ) = > Provider . fromModelsDevProvider ( x ) ) ,
connected ,
)
2025-11-21 13:21:06 +08:00
return c . json ( {
all : Object.values ( providers ) ,
default : mapValues ( providers , ( item ) = > Provider . sort ( Object . values ( item . models ) ) [ 0 ] . id ) ,
2025-12-11 02:49:45 +08:00
connected : Object.keys ( connected ) ,
2025-11-21 13:21:06 +08:00
} )
} ,
)
. get (
"/provider/auth" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Get provider auth methods" ,
description : "Retrieve available authentication methods for all AI providers." ,
2025-11-21 13:21:06 +08:00
operationId : "provider.auth" ,
responses : {
200 : {
description : "Provider auth methods" ,
content : {
"application/json" : {
schema : resolver ( z . record ( z . string ( ) , z . array ( ProviderAuth . Method ) ) ) ,
} ,
} ,
} ,
} ,
} ) ,
async ( c ) = > {
return c . json ( await ProviderAuth . methods ( ) )
} ,
)
. post (
2025-12-08 08:04:14 +08:00
"/provider/:providerID/oauth/authorize" ,
2025-11-21 13:21:06 +08:00
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "OAuth authorize" ,
description : "Initiate OAuth authorization for a specific AI provider to get an authorization URL." ,
2025-11-21 13:21:06 +08:00
operationId : "provider.oauth.authorize" ,
responses : {
200 : {
description : "Authorization URL and method" ,
content : {
"application/json" : {
schema : resolver ( ProviderAuth . Authorization . optional ( ) ) ,
} ,
} ,
} ,
. . . errors ( 400 ) ,
} ,
} ) ,
validator (
"param" ,
z . object ( {
2025-12-08 08:04:14 +08:00
providerID : z.string ( ) . meta ( { description : "Provider ID" } ) ,
2025-11-21 13:21:06 +08:00
} ) ,
) ,
validator (
"json" ,
z . object ( {
method : z.number ( ) . meta ( { description : "Auth method index" } ) ,
} ) ,
) ,
async ( c ) = > {
2025-12-08 08:04:14 +08:00
const providerID = c . req . valid ( "param" ) . providerID
2025-11-21 13:21:06 +08:00
const { method } = c . req . valid ( "json" )
const result = await ProviderAuth . authorize ( {
2025-12-08 08:04:14 +08:00
providerID ,
2025-11-21 13:21:06 +08:00
method ,
} )
return c . json ( result )
} ,
)
. post (
2025-12-08 08:04:14 +08:00
"/provider/:providerID/oauth/callback" ,
2025-11-21 13:21:06 +08:00
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "OAuth callback" ,
description : "Handle the OAuth callback from a provider after user authorization." ,
2025-11-21 13:21:06 +08:00
operationId : "provider.oauth.callback" ,
responses : {
200 : {
description : "OAuth callback processed successfully" ,
content : {
"application/json" : {
schema : resolver ( z . boolean ( ) ) ,
} ,
} ,
} ,
. . . errors ( 400 ) ,
} ,
} ) ,
validator (
"param" ,
z . object ( {
2025-12-08 08:04:14 +08:00
providerID : z.string ( ) . meta ( { description : "Provider ID" } ) ,
2025-11-21 13:21:06 +08:00
} ) ,
) ,
validator (
"json" ,
z . object ( {
method : z.number ( ) . meta ( { description : "Auth method index" } ) ,
code : z.string ( ) . optional ( ) . meta ( { description : "OAuth authorization code" } ) ,
} ) ,
) ,
async ( c ) = > {
2025-12-08 08:04:14 +08:00
const providerID = c . req . valid ( "param" ) . providerID
2025-11-21 13:21:06 +08:00
const { method , code } = c . req . valid ( "json" )
await ProviderAuth . callback ( {
2025-12-08 08:04:14 +08:00
providerID ,
2025-11-21 13:21:06 +08:00
method ,
code ,
} )
return c . json ( true )
} ,
)
2025-09-17 13:16:49 +08:00
. get (
"/find" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Find text" ,
description : "Search for text patterns across files in the project using ripgrep." ,
2025-09-17 13:16:49 +08:00
operationId : "find.text" ,
responses : {
200 : {
description : "Matches" ,
content : {
"application/json" : {
schema : resolver ( Ripgrep . Match . shape . data . array ( ) ) ,
} ,
2025-06-12 11:59:51 +08:00
} ,
} ,
} ,
2025-09-02 05:15:49 +08:00
} ) ,
2025-09-17 13:16:49 +08:00
validator (
"query" ,
z . object ( {
pattern : z.string ( ) ,
} ) ,
) ,
async ( c ) = > {
const pattern = c . req . valid ( "query" ) . pattern
const result = await Ripgrep . search ( {
cwd : Instance.directory ,
pattern ,
limit : 10 ,
} )
return c . json ( result )
} ,
)
. get (
"/find/file" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Find files" ,
description : "Search for files by name or pattern in the project directory." ,
2025-09-17 13:16:49 +08:00
operationId : "find.files" ,
responses : {
200 : {
description : "File paths" ,
content : {
"application/json" : {
schema : resolver ( z . string ( ) . array ( ) ) ,
} ,
2025-07-03 05:08:06 +08:00
} ,
} ,
} ,
2025-09-02 05:15:49 +08:00
} ) ,
2025-09-17 13:16:49 +08:00
validator (
"query" ,
z . object ( {
query : z.string ( ) ,
2025-11-15 07:48:23 +08:00
dirs : z.enum ( [ "true" , "false" ] ) . optional ( ) ,
2025-09-17 13:16:49 +08:00
} ) ,
) ,
async ( c ) = > {
const query = c . req . valid ( "query" ) . query
2025-11-04 00:53:38 +08:00
const dirs = c . req . valid ( "query" ) . dirs
2025-10-01 15:37:01 +08:00
const results = await File . search ( {
2025-09-17 13:16:49 +08:00
query ,
limit : 10 ,
2025-11-04 05:35:46 +08:00
dirs : dirs !== "false" ,
2025-09-17 13:16:49 +08:00
} )
2025-10-01 15:37:01 +08:00
return c . json ( results )
2025-09-17 13:16:49 +08:00
} ,
)
. get (
"/find/symbol" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Find symbols" ,
description : "Search for workspace symbols like functions, classes, and variables using LSP." ,
2025-09-17 13:16:49 +08:00
operationId : "find.symbols" ,
responses : {
200 : {
description : "Symbols" ,
content : {
"application/json" : {
schema : resolver ( LSP . Symbol . array ( ) ) ,
} ,
2025-08-28 04:27:49 +08:00
} ,
} ,
} ,
2025-09-02 05:15:49 +08:00
} ) ,
2025-09-17 13:16:49 +08:00
validator (
"query" ,
z . object ( {
query : z.string ( ) ,
} ) ,
) ,
async ( c ) = > {
2025-10-14 02:05:54 +08:00
/ *
2025-09-17 13:16:49 +08:00
const query = c . req . valid ( "query" ) . query
const result = await LSP . workspaceSymbol ( query )
return c . json ( result )
2025-10-14 02:05:54 +08:00
* /
return c . json ( [ ] )
2025-09-17 13:16:49 +08:00
} ,
)
. get (
"/file" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "List files" ,
description : "List files and directories in a specified path." ,
2025-09-17 13:16:49 +08:00
operationId : "file.list" ,
responses : {
200 : {
description : "Files and directories" ,
content : {
"application/json" : {
schema : resolver ( File . Node . array ( ) ) ,
} ,
2025-07-03 05:08:06 +08:00
} ,
} ,
} ,
2025-09-02 05:15:49 +08:00
} ) ,
2025-09-17 13:16:49 +08:00
validator (
"query" ,
z . object ( {
path : z.string ( ) ,
} ) ,
) ,
async ( c ) = > {
const path = c . req . valid ( "query" ) . path
const content = await File . list ( path )
return c . json ( content )
} ,
)
. get (
"/file/content" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Read file" ,
description : "Read the content of a specified file." ,
2025-09-17 13:16:49 +08:00
operationId : "file.read" ,
responses : {
200 : {
description : "File content" ,
content : {
"application/json" : {
schema : resolver ( File . Content ) ,
} ,
2025-07-03 05:08:06 +08:00
} ,
} ,
} ,
} ) ,
2025-09-17 13:16:49 +08:00
validator (
"query" ,
z . object ( {
path : z.string ( ) ,
} ) ,
) ,
async ( c ) = > {
const path = c . req . valid ( "query" ) . path
const content = await File . read ( path )
return c . json ( content )
} ,
)
. get (
"/file/status" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Get file status" ,
description : "Get the git status of all files in the project." ,
2025-09-17 13:16:49 +08:00
operationId : "file.status" ,
responses : {
200 : {
description : "File status" ,
content : {
"application/json" : {
schema : resolver ( File . Info . array ( ) ) ,
} ,
2025-09-02 05:15:49 +08:00
} ,
} ,
} ,
2025-09-17 13:16:49 +08:00
} ) ,
async ( c ) = > {
const content = await File . status ( )
return c . json ( content )
2025-07-03 05:08:06 +08:00
} ,
2025-09-17 13:16:49 +08:00
)
. post (
"/log" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Write log" ,
description : "Write a log entry to the server logs with specified level and metadata." ,
2025-09-17 13:16:49 +08:00
operationId : "app.log" ,
responses : {
200 : {
description : "Log entry written successfully" ,
content : {
"application/json" : {
schema : resolver ( z . boolean ( ) ) ,
} ,
2025-07-09 21:16:10 +08:00
} ,
} ,
2025-10-15 23:53:09 +08:00
. . . errors ( 400 ) ,
2025-07-09 21:16:10 +08:00
} ,
} ) ,
2025-09-17 13:16:49 +08:00
validator (
"json" ,
z . object ( {
2025-10-24 04:04:58 +08:00
service : z.string ( ) . meta ( { description : "Service name for the log entry" } ) ,
level : z.enum ( [ "debug" , "info" , "error" , "warn" ] ) . meta ( { description : "Log level" } ) ,
2025-09-17 13:16:49 +08:00
message : z.string ( ) . meta ( { description : "Log message" } ) ,
extra : z
. record ( z . string ( ) , z . any ( ) )
. optional ( )
. meta ( { description : "Additional metadata for the log entry" } ) ,
} ) ,
) ,
async ( c ) = > {
const { service , level , message , extra } = c . req . valid ( "json" )
const logger = Log . create ( { service } )
2025-07-09 21:16:10 +08:00
2025-09-17 13:16:49 +08:00
switch ( level ) {
case "debug" :
logger . debug ( message , extra )
break
case "info" :
logger . info ( message , extra )
break
case "error" :
logger . error ( message , extra )
break
case "warn" :
logger . warn ( message , extra )
break
}
2025-07-09 21:16:10 +08:00
2025-09-17 13:16:49 +08:00
return c . json ( true )
} ,
)
. get (
"/agent" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "List agents" ,
description : "Get a list of all available AI agents in the OpenCode system." ,
2025-09-17 13:16:49 +08:00
operationId : "app.agents" ,
responses : {
200 : {
description : "List of agents" ,
content : {
"application/json" : {
schema : resolver ( Agent . Info . array ( ) ) ,
} ,
2025-07-10 03:44:59 +08:00
} ,
} ,
} ,
2025-09-17 13:16:49 +08:00
} ) ,
async ( c ) = > {
const modes = await Agent . list ( )
return c . json ( modes )
2025-07-10 03:44:59 +08:00
} ,
2025-09-17 13:16:49 +08:00
)
2025-10-07 16:04:19 +08:00
. get (
"/mcp" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Get MCP status" ,
description : "Get the status of all Model Context Protocol (MCP) servers." ,
2025-10-07 16:04:19 +08:00
operationId : "mcp.status" ,
responses : {
200 : {
description : "MCP server status" ,
content : {
"application/json" : {
2025-11-01 03:07:36 +08:00
schema : resolver ( z . record ( z . string ( ) , MCP . Status ) ) ,
2025-10-07 16:04:19 +08:00
} ,
} ,
} ,
} ,
} ) ,
async ( c ) = > {
return c . json ( await MCP . status ( ) )
} ,
)
2025-11-08 03:57:12 +08:00
. post (
"/mcp" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Add MCP server" ,
description : "Dynamically add a new Model Context Protocol (MCP) server to the system." ,
2025-11-08 03:57:12 +08:00
operationId : "mcp.add" ,
responses : {
200 : {
description : "MCP server added successfully" ,
content : {
"application/json" : {
schema : resolver ( z . record ( z . string ( ) , MCP . Status ) ) ,
} ,
} ,
} ,
. . . errors ( 400 ) ,
} ,
} ) ,
validator (
"json" ,
z . object ( {
name : z.string ( ) ,
config : Config.Mcp ,
} ) ,
) ,
async ( c ) = > {
const { name , config } = c . req . valid ( "json" )
const result = await MCP . add ( name , config )
return c . json ( result . status )
} ,
)
2025-12-08 04:47:27 +08:00
. post (
"/mcp/:name/auth" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Start MCP OAuth" ,
description : "Start OAuth authentication flow for a Model Context Protocol (MCP) server." ,
2025-12-08 04:47:27 +08:00
operationId : "mcp.auth.start" ,
responses : {
200 : {
description : "OAuth flow started" ,
content : {
"application/json" : {
schema : resolver (
z . object ( {
authorizationUrl : z.string ( ) . describe ( "URL to open in browser for authorization" ) ,
} ) ,
) ,
} ,
} ,
} ,
. . . errors ( 400 , 404 ) ,
} ,
} ) ,
async ( c ) = > {
const name = c . req . param ( "name" )
const supportsOAuth = await MCP . supportsOAuth ( name )
if ( ! supportsOAuth ) {
return c . json ( { error : ` MCP server ${ name } does not support OAuth ` } , 400 )
}
const result = await MCP . startAuth ( name )
return c . json ( result )
} ,
)
. post (
"/mcp/:name/auth/callback" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Complete MCP OAuth" ,
description :
"Complete OAuth authentication for a Model Context Protocol (MCP) server using the authorization code." ,
2025-12-08 04:47:27 +08:00
operationId : "mcp.auth.callback" ,
responses : {
200 : {
description : "OAuth authentication completed" ,
content : {
"application/json" : {
schema : resolver ( MCP . Status ) ,
} ,
} ,
} ,
. . . errors ( 400 , 404 ) ,
} ,
} ) ,
validator (
"json" ,
z . object ( {
code : z.string ( ) . describe ( "Authorization code from OAuth callback" ) ,
} ) ,
) ,
async ( c ) = > {
const name = c . req . param ( "name" )
const { code } = c . req . valid ( "json" )
const status = await MCP . finishAuth ( name , code )
return c . json ( status )
} ,
)
. post (
"/mcp/:name/auth/authenticate" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Authenticate MCP OAuth" ,
2025-12-08 04:47:27 +08:00
description : "Start OAuth flow and wait for callback (opens browser)" ,
operationId : "mcp.auth.authenticate" ,
responses : {
200 : {
description : "OAuth authentication completed" ,
content : {
"application/json" : {
schema : resolver ( MCP . Status ) ,
} ,
} ,
} ,
. . . errors ( 400 , 404 ) ,
} ,
} ) ,
async ( c ) = > {
const name = c . req . param ( "name" )
const supportsOAuth = await MCP . supportsOAuth ( name )
if ( ! supportsOAuth ) {
return c . json ( { error : ` MCP server ${ name } does not support OAuth ` } , 400 )
}
const status = await MCP . authenticate ( name )
return c . json ( status )
} ,
)
. delete (
"/mcp/:name/auth" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Remove MCP OAuth" ,
2025-12-08 04:47:27 +08:00
description : "Remove OAuth credentials for an MCP server" ,
operationId : "mcp.auth.remove" ,
responses : {
200 : {
description : "OAuth credentials removed" ,
content : {
"application/json" : {
schema : resolver ( z . object ( { success : z.literal ( true ) } ) ) ,
} ,
} ,
} ,
. . . errors ( 404 ) ,
} ,
} ) ,
async ( c ) = > {
const name = c . req . param ( "name" )
await MCP . removeAuth ( name )
return c . json ( { success : true as const } )
} ,
)
2025-12-09 01:31:22 +08:00
. post (
"/mcp/:name/connect" ,
describeRoute ( {
description : "Connect an MCP server" ,
operationId : "mcp.connect" ,
responses : {
200 : {
description : "MCP server connected successfully" ,
content : {
"application/json" : {
schema : resolver ( z . boolean ( ) ) ,
} ,
} ,
} ,
} ,
} ) ,
validator ( "param" , z . object ( { name : z.string ( ) } ) ) ,
async ( c ) = > {
const { name } = c . req . valid ( "param" )
await MCP . connect ( name )
return c . json ( true )
} ,
)
. post (
"/mcp/:name/disconnect" ,
describeRoute ( {
description : "Disconnect an MCP server" ,
operationId : "mcp.disconnect" ,
responses : {
200 : {
description : "MCP server disconnected successfully" ,
content : {
"application/json" : {
schema : resolver ( z . boolean ( ) ) ,
} ,
} ,
} ,
} ,
} ) ,
validator ( "param" , z . object ( { name : z.string ( ) } ) ) ,
async ( c ) = > {
const { name } = c . req . valid ( "param" )
await MCP . disconnect ( name )
return c . json ( true )
} ,
)
2025-11-01 03:07:36 +08:00
. get (
"/lsp" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Get LSP status" ,
2025-11-01 03:07:36 +08:00
description : "Get LSP server status" ,
operationId : "lsp.status" ,
responses : {
200 : {
description : "LSP server status" ,
content : {
"application/json" : {
schema : resolver ( LSP . Status . array ( ) ) ,
} ,
} ,
} ,
} ,
} ) ,
async ( c ) = > {
return c . json ( await LSP . status ( ) )
} ,
)
2025-11-01 23:14:39 +08:00
. get (
"/formatter" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Get formatter status" ,
2025-11-01 23:14:39 +08:00
description : "Get formatter status" ,
operationId : "formatter.status" ,
responses : {
200 : {
description : "Formatter status" ,
content : {
"application/json" : {
schema : resolver ( Format . Status . array ( ) ) ,
} ,
} ,
} ,
} ,
} ) ,
async ( c ) = > {
return c . json ( await Format . status ( ) )
} ,
)
2025-09-17 13:16:49 +08:00
. post (
"/tui/append-prompt" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Append TUI prompt" ,
2025-09-17 13:16:49 +08:00
description : "Append prompt to the TUI" ,
operationId : "tui.appendPrompt" ,
responses : {
200 : {
description : "Prompt processed successfully" ,
content : {
"application/json" : {
schema : resolver ( z . boolean ( ) ) ,
} ,
2025-07-22 07:53:22 +08:00
} ,
} ,
2025-10-15 23:53:09 +08:00
. . . errors ( 400 ) ,
2025-07-22 07:53:22 +08:00
} ,
} ) ,
2025-11-01 03:07:36 +08:00
validator ( "json" , TuiEvent . PromptAppend . properties ) ,
async ( c ) = > {
await Bus . publish ( TuiEvent . PromptAppend , c . req . valid ( "json" ) )
return c . json ( true )
} ,
2025-09-17 13:16:49 +08:00
)
. post (
"/tui/open-help" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Open help dialog" ,
description : "Open the help dialog in the TUI to display user assistance information." ,
2025-09-17 13:16:49 +08:00
operationId : "tui.openHelp" ,
responses : {
200 : {
description : "Help dialog opened successfully" ,
content : {
"application/json" : {
schema : resolver ( z . boolean ( ) ) ,
} ,
2025-07-22 07:53:22 +08:00
} ,
} ,
} ,
2025-09-17 13:16:49 +08:00
} ) ,
2025-11-01 03:07:36 +08:00
async ( c ) = > {
// TODO: open dialog
return c . json ( true )
} ,
2025-09-17 13:16:49 +08:00
)
. post (
"/tui/open-sessions" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Open sessions dialog" ,
2025-09-17 13:16:49 +08:00
description : "Open the session dialog" ,
operationId : "tui.openSessions" ,
responses : {
200 : {
description : "Session dialog opened successfully" ,
content : {
"application/json" : {
schema : resolver ( z . boolean ( ) ) ,
} ,
2025-08-01 00:24:23 +08:00
} ,
} ,
} ,
2025-09-17 13:16:49 +08:00
} ) ,
2025-11-01 03:07:36 +08:00
async ( c ) = > {
await Bus . publish ( TuiEvent . CommandExecute , {
command : "session.list" ,
} )
return c . json ( true )
} ,
2025-09-17 13:16:49 +08:00
)
. post (
"/tui/open-themes" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Open themes dialog" ,
2025-09-17 13:16:49 +08:00
description : "Open the theme dialog" ,
operationId : "tui.openThemes" ,
responses : {
200 : {
description : "Theme dialog opened successfully" ,
content : {
"application/json" : {
schema : resolver ( z . boolean ( ) ) ,
} ,
2025-08-01 00:24:23 +08:00
} ,
} ,
} ,
2025-09-17 13:16:49 +08:00
} ) ,
2025-11-01 03:07:36 +08:00
async ( c ) = > {
await Bus . publish ( TuiEvent . CommandExecute , {
command : "session.list" ,
} )
return c . json ( true )
} ,
2025-09-17 13:16:49 +08:00
)
. post (
"/tui/open-models" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Open models dialog" ,
2025-09-17 13:16:49 +08:00
description : "Open the model dialog" ,
operationId : "tui.openModels" ,
responses : {
200 : {
description : "Model dialog opened successfully" ,
content : {
"application/json" : {
schema : resolver ( z . boolean ( ) ) ,
} ,
2025-08-01 00:24:23 +08:00
} ,
} ,
} ,
2025-09-17 13:16:49 +08:00
} ) ,
2025-11-01 03:07:36 +08:00
async ( c ) = > {
await Bus . publish ( TuiEvent . CommandExecute , {
command : "model.list" ,
} )
return c . json ( true )
} ,
2025-09-17 13:16:49 +08:00
)
. post (
"/tui/submit-prompt" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Submit TUI prompt" ,
2025-09-17 13:16:49 +08:00
description : "Submit the prompt" ,
operationId : "tui.submitPrompt" ,
responses : {
200 : {
description : "Prompt submitted successfully" ,
content : {
"application/json" : {
schema : resolver ( z . boolean ( ) ) ,
} ,
2025-08-01 00:24:23 +08:00
} ,
} ,
} ,
2025-09-17 13:16:49 +08:00
} ) ,
2025-11-01 03:07:36 +08:00
async ( c ) = > {
await Bus . publish ( TuiEvent . CommandExecute , {
command : "prompt.submit" ,
} )
return c . json ( true )
} ,
2025-09-17 13:16:49 +08:00
)
. post (
"/tui/clear-prompt" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Clear TUI prompt" ,
2025-09-17 13:16:49 +08:00
description : "Clear the prompt" ,
operationId : "tui.clearPrompt" ,
responses : {
200 : {
description : "Prompt cleared successfully" ,
content : {
"application/json" : {
schema : resolver ( z . boolean ( ) ) ,
} ,
2025-08-01 00:24:23 +08:00
} ,
} ,
} ,
2025-09-17 13:16:49 +08:00
} ) ,
2025-11-01 03:07:36 +08:00
async ( c ) = > {
await Bus . publish ( TuiEvent . CommandExecute , {
command : "prompt.clear" ,
} )
return c . json ( true )
} ,
2025-09-17 13:16:49 +08:00
)
. post (
"/tui/execute-command" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Execute TUI command" ,
2025-09-17 13:16:49 +08:00
description : "Execute a TUI command (e.g. agent_cycle)" ,
operationId : "tui.executeCommand" ,
responses : {
200 : {
description : "Command executed successfully" ,
content : {
"application/json" : {
schema : resolver ( z . boolean ( ) ) ,
} ,
2025-08-01 00:24:23 +08:00
} ,
} ,
2025-10-15 23:53:09 +08:00
. . . errors ( 400 ) ,
2025-08-01 00:24:23 +08:00
} ,
} ) ,
2025-11-01 03:07:36 +08:00
validator ( "json" , z . object ( { command : z.string ( ) } ) ) ,
async ( c ) = > {
const command = c . req . valid ( "json" ) . command
await Bus . publish ( TuiEvent . CommandExecute , {
// @ts-expect-error
command : {
session_new : "session.new" ,
session_share : "session.share" ,
session_interrupt : "session.interrupt" ,
session_compact : "session.compact" ,
messages_page_up : "session.page.up" ,
messages_page_down : "session.page.down" ,
messages_half_page_up : "session.half.page.up" ,
messages_half_page_down : "session.half.page.down" ,
messages_first : "session.first" ,
messages_last : "session.last" ,
agent_cycle : "agent.cycle" ,
} [ command ] ,
} )
return c . json ( true )
} ,
2025-09-17 13:16:49 +08:00
)
. post (
"/tui/show-toast" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Show TUI toast" ,
2025-09-17 13:16:49 +08:00
description : "Show a toast notification in the TUI" ,
operationId : "tui.showToast" ,
responses : {
200 : {
description : "Toast notification shown successfully" ,
content : {
"application/json" : {
schema : resolver ( z . boolean ( ) ) ,
} ,
2025-08-15 21:39:58 +08:00
} ,
} ,
} ,
} ) ,
2025-11-01 03:07:36 +08:00
validator ( "json" , TuiEvent . ToastShow . properties ) ,
async ( c ) = > {
await Bus . publish ( TuiEvent . ToastShow , c . req . valid ( "json" ) )
return c . json ( true )
} ,
)
. post (
"/tui/publish" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Publish TUI event" ,
2025-11-01 03:07:36 +08:00
description : "Publish a TUI event" ,
operationId : "tui.publish" ,
responses : {
200 : {
description : "Event published successfully" ,
content : {
"application/json" : {
schema : resolver ( z . boolean ( ) ) ,
} ,
} ,
} ,
. . . errors ( 400 ) ,
} ,
} ) ,
2025-09-17 13:16:49 +08:00
validator (
"json" ,
2025-11-01 03:07:36 +08:00
z . union (
Object . values ( TuiEvent ) . map ( ( def ) = > {
return z
. object ( {
type : z . literal ( def . type ) ,
properties : def.properties ,
} )
. meta ( {
ref : "Event" + "." + def . type ,
} )
} ) ,
) ,
2025-09-17 13:16:49 +08:00
) ,
2025-11-01 03:07:36 +08:00
async ( c ) = > {
const evt = c . req . valid ( "json" )
2025-11-08 09:59:02 +08:00
await Bus . publish ( Object . values ( TuiEvent ) . find ( ( def ) = > def . type === evt . type ) ! , evt . properties )
2025-11-01 03:07:36 +08:00
return c . json ( true )
} ,
2025-09-17 13:16:49 +08:00
)
. route ( "/tui/control" , TuiRoute )
. put (
2025-12-08 08:04:14 +08:00
"/auth/:providerID" ,
2025-09-17 13:16:49 +08:00
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Set auth credentials" ,
2025-09-17 13:16:49 +08:00
description : "Set authentication credentials" ,
operationId : "auth.set" ,
responses : {
200 : {
description : "Successfully set authentication credentials" ,
content : {
"application/json" : {
schema : resolver ( z . boolean ( ) ) ,
} ,
2025-08-15 04:24:46 +08:00
} ,
} ,
2025-10-15 23:53:09 +08:00
. . . errors ( 400 ) ,
2025-08-15 04:24:46 +08:00
} ,
2025-09-17 13:16:49 +08:00
} ) ,
validator (
"param" ,
z . object ( {
2025-12-08 08:04:14 +08:00
providerID : z.string ( ) ,
2025-09-17 13:16:49 +08:00
} ) ,
) ,
validator ( "json" , Auth . Info ) ,
async ( c ) = > {
2025-12-08 08:04:14 +08:00
const providerID = c . req . valid ( "param" ) . providerID
2025-09-17 13:16:49 +08:00
const info = c . req . valid ( "json" )
2025-12-08 08:04:14 +08:00
await Auth . set ( providerID , info )
2025-09-17 13:16:49 +08:00
return c . json ( true )
2025-08-15 04:24:46 +08:00
} ,
2025-09-17 13:16:49 +08:00
)
. get (
"/event" ,
describeRoute ( {
2025-12-08 08:58:04 +08:00
summary : "Subscribe to events" ,
2025-09-17 13:16:49 +08:00
description : "Get events" ,
operationId : "event.subscribe" ,
responses : {
200 : {
description : "Event stream" ,
content : {
"text/event-stream" : {
2025-12-10 03:32:04 +08:00
schema : resolver ( BusEvent . payloads ( ) ) ,
2025-09-17 13:16:49 +08:00
} ,
} ,
} ,
} ,
2025-09-02 05:15:49 +08:00
} ) ,
2025-09-17 13:16:49 +08:00
async ( c ) = > {
log . info ( "event connected" )
return streamSSE ( c , async ( stream ) = > {
stream . writeSSE ( {
data : JSON.stringify ( {
type : "server.connected" ,
properties : { } ,
} ) ,
} )
const unsub = Bus . subscribeAll ( async ( event ) = > {
await stream . writeSSE ( {
data : JSON.stringify ( event ) ,
} )
2025-12-01 22:13:53 +08:00
if ( event . type === Bus . InstanceDisposed . type ) {
stream . close ( )
}
2025-09-17 13:16:49 +08:00
} )
await new Promise < void > ( ( resolve ) = > {
stream . onAbort ( ( ) = > {
unsub ( )
resolve ( )
log . info ( "event disconnected" )
} )
} )
} )
} ,
2025-11-05 01:33:03 +08:00
)
2025-12-08 23:35:05 +08:00
. all ( "/*" , async ( c ) = > {
2025-12-08 23:48:21 +08:00
return proxy ( ` https://desktop.opencode.ai ${ c . req . path } ` , {
2025-11-05 01:33:03 +08:00
. . . c . req ,
headers : {
2025-12-08 23:48:21 +08:00
host : "desktop.opencode.ai" ,
2025-11-05 01:33:03 +08:00
} ,
} )
} ) ,
2025-09-17 13:16:49 +08:00
)
2025-05-19 10:30:41 +08:00
export async function openapi() {
2025-09-17 13:16:49 +08:00
const result = await generateSpecs ( App ( ) , {
2025-05-19 10:30:41 +08:00
documentation : {
info : {
title : "opencode" ,
version : "1.0.0" ,
description : "opencode api" ,
} ,
2025-08-15 00:10:32 +08:00
openapi : "3.1.1" ,
2025-05-19 10:30:41 +08:00
} ,
2025-06-01 02:41:00 +08:00
} )
return result
2025-05-19 02:28:08 +08:00
}
2025-05-19 02:13:04 +08:00
2025-06-25 08:52:09 +08:00
export function listen ( opts : { port : number ; hostname : string } ) {
2025-05-19 02:28:08 +08:00
const server = Bun . serve ( {
2025-06-25 08:52:09 +08:00
port : opts.port ,
hostname : opts.hostname ,
2025-05-19 02:13:04 +08:00
idleTimeout : 0 ,
2025-09-17 13:16:49 +08:00
fetch : App ( ) . fetch ,
2025-12-05 10:32:08 +08:00
websocket : websocket ,
2025-06-01 02:41:00 +08:00
} )
return server
2025-05-18 09:31:42 +08:00
}
}