Skip to content

OpenCode Server 模式接口

OpenDesk 当前实现了一个 opencode 兼容的 headless HTTP server 模式,用于让 opencode TUI 或兼容客户端连接 OpenDesk 的任务、模型、权限、MCP 和事件系统。

本文只描述 OpenDesk 当前真实实现的接口。opencode 原项目中存在但 OpenDesk 尚未实现的接口,会明确标注为未实现或占位。

CLI 子命令:

Terminal window
opendesk serve --mode opencode --port 4096 --hostname 127.0.0.1

可用参数:

参数默认值说明
--mode <mode>opencode当前真正实现 opencodeacp 只打印“暂未实现”并退出。
--port <port>4096HTTP 监听端口。
--hostname <hostname>127.0.0.1HTTP 监听地址。
--cors <origins>未设置允许的跨域源,逗号分隔。不设置时允许 *
--password <password>环境变量启用 HTTP Basic Auth。也可用 OPENCODE_SERVER_PASSWORD
--username <username>环境变量或 opencodeBasic Auth 用户名。也可用 OPENCODE_SERVER_USERNAME

启动时会初始化 CLI 平台、全局状态、内置 Agent、应用注册表和应用 Worker,然后启动 Hono HTTP server。

健康检查:

Terminal window
curl http://127.0.0.1:4096/global/health

响应:

{
"healthy": true,
"version": "0.2.0"
}

如果未设置 password,所有请求直接放行,启动日志会打印警告。

如果设置了 password,所有非 OPTIONS 请求都需要 Basic Auth:

Terminal window
curl -u opencode:your-password http://127.0.0.1:4096/global/health

也支持用 query 参数传递 Basic Auth token:

?auth_token=<base64(username:password)>

认证失败时返回:

{
"error": "Unauthorized"
}

HTTP 状态码为 401,并带 WWW-Authenticate: Basic realm="opendesk"

server 允许的方法:

GET, POST, PUT, PATCH, DELETE, OPTIONS

允许的请求头:

Content-Type, Authorization, x-opencode-directory, x-opencode-sync

暴露的响应头:

x-opencode-sync, x-next-cursor, link

大多数实例级接口会解析当前工作目录。

优先级:

  1. query 参数 directory
  2. 请求头 x-opencode-directory
  3. 平台当前有效工作空间
  4. process.cwd() 兜底

directory 会先 URL decode,再 path.resolve()

opencode workspace ID 和文件系统目录是分开的。OpenDesk 会读取:

  1. query 参数 workspace
  2. 请求头 x-opencode-workspace

该值只作为 opencode workspace ID 透传到 session 信息中,不参与路径解析。

示例:

Terminal window
curl 'http://127.0.0.1:4096/path?directory=/repo/demo&workspace=wrk_123'

OpenDesk 内部任务 ID 使用 task_ 前缀,opencode session ID 使用 ses_ 前缀。server 路由会自动互转:

外部 ID内部 ID
ses_abctask_abc
task_abctask_abc

其他 ID 前缀:

前缀用途
msg_message
prt_message part
evt_event
per_permission request
que_question request

通用错误通常返回:

{
"error": "message"
}

部分 opencode 兼容接口返回 opencode 风格错误对象,例如:

{
"_tag": "PermissionNotFoundError",
"requestID": "per_missing",
"message": "Permission request not found: per_missing"
}

OpenDesk 当前实现两个 SSE 事件流:

路径说明
GET /event实例事件流。
GET /global/event全局事件流,opencode TUI 主要连接这个端点。

两个端点都使用 SSE event name message

连接成功后立即发送 server.connected,之后每 10 秒发送 server.heartbeat

/event 的 data 是 BusEvent

{
"id": "evt_connected_lx...",
"type": "server.connected",
"properties": {}
}

/global/event 的 data 是 GlobalBusEvent

{
"directory": "/repo/demo",
"project": "proj_demo",
"payload": {
"id": "evt_connected_lx...",
"type": "server.connected",
"properties": {}
}
}

常见事件类型包括:

类型触发场景
server.connectedSSE 建连。
server.heartbeat心跳。
session.created创建 session。
session.deleted删除 session。
session.statussession busy/idle 状态变化。
session.aborted中止 session。
message.updatedmessage 元信息变化。
message.part.updated流式 part 更新。
permission.asked工具调用需要权限确认。
permission.replied权限请求已回复。
question.askedAgent 需要用户回答问题。
question.replied问题已回答。
question.rejected问题被拒绝。
server.instance.disposed调用 dispose。

返回 server 健康状态和版本。

{
"healthy": true,
"version": "0.2.0"
}

全局 SSE 事件流。格式见“SSE 事件”。

返回 opencode 兼容配置,等价于 GET /config

响应示例:

{
"model": "openai/gpt4o",
"provider": {
"openai": {
"api": "openai",
"options": {
"apiKey": "sk-...",
"baseURL": "https://api.openai.com/v1",
"noProxy": false
}
}
},
"permission": {
"read": "allow"
}
}

更新配置,逻辑等价于 PATCH /config

支持两类 payload:

{
"model": "openai/gpt4o",
"provider": {
"openai": {
"options": {
"apiKey": "sk-...",
"baseURL": "https://api.openai.com/v1",
"noProxy": false
},
"enabled": false
}
}
}

或 OpenDesk 原生字段:

{
"default_model": "gpt4o",
"applications": {
"search": {
"defaultWebSearch": "bing"
}
}
}

发送 {}{ "config": {} } 会触发从磁盘重新加载 setting.json,刷新 skills 和当前任务上下文。

provider 的启用状态通过 OpenDesk 原生 providers[].enabled 保存。enabled: false 的 provider 不会出现在 /config/config/providers/provider/provider/auth 中;未设置或 true 表示启用。

发布 server.instance.disposed 事件,返回:

true

opencode 的 POST /global/upgrade 当前没有在 OpenDesk server 中注册。

返回 opencode 兼容配置。

生成规则:

OpenDesk 字段opencode 响应
providers[].nameprovider.<name>
providers[].api_keyprovider.<name>.options.apiKey
providers[].base_urlprovider.<name>.options.baseURL
providers[].no_proxyprovider.<name>.options.noProxy
providers[].enabledfalse 时该 provider 不输出;未设置或 true 时输出。
default_modelmodel,格式为 <provider_name>/<alias>
permission原样输出

如果需要禁用某个 provider,请在 setting.json 的对应 providers[] 项里设置 "enabled": false。如果默认模型属于已禁用 provider,model 和默认 provider 映射都不会输出。

更新配置。行为与 PATCH /global/config 一致。

opencode 风格 provider patch 会更新或新增 provider:

{
"provider": {
"openai": {
"options": {
"apiKey": "new-key",
"baseURL": "https://new.example/v1",
"noProxy": true
},
"enabled": false
}
}
}

model 可以是字符串:

{
"model": "openai/gpt-4o"
}

也可以是对象:

{
"model": {
"providerID": "openai",
"modelID": "gpt-4o"
}
}

OpenDesk 会把它解析为本地 models[].alias。如果能根据 provider 和 model 名称匹配到本地模型,使用对应 alias;否则退回使用 model id。

返回配置中的 provider 列表和默认模型映射。

{
"providers": [
{
"id": "openai",
"name": "openai",
"source": "config",
"env": [],
"options": {
"baseURL": "https://api.openai.com/v1"
},
"models": {
"gpt4o": {
"id": "gpt4o",
"providerID": "openai",
"name": "gpt-4o",
"status": "active"
}
}
}
],
"default": {
"openai": "gpt4o"
}
}

model 对象还包含 capabilitiescostlimitheaders 等字段。context_length 未配置时,limit.context 默认为 128000,limit.output 固定为 16384。

providers[].enabled === false 的 provider 会从该接口返回中过滤掉。可以先修改磁盘上的 setting.json,再调用 PATCH /configPATCH /global/config 并发送 {}{ "config": {} } 触发 reload,随后请求 /config/providers 验证过滤结果。

列出可用 provider。

{
"all": [],
"default": {},
"connected": []
}
字段说明
allprovider 列表,格式同 /config/providers 中的 provider 对象。
defaultprovider 到默认模型 alias 的映射。
connectedapi_key 非空的 provider 名称列表。

返回每个 provider 支持的认证方式。当前全部是 API Key:

{
"openai": [
{
"type": "api_key",
"description": "API Key authentication"
}
]
}

以下 opencode OAuth 接口当前没有在 OpenDesk server 中注册:

接口状态
POST /provider/:providerID/oauth/authorize未实现
POST /provider/:providerID/oauth/callback未实现

返回路径信息。

{
"directory": "/repo/demo",
"home": "/home/user",
"state": "",
"config": "",
"worktree": "/repo/demo"
}

列出 Agent 支持的模式,返回数组:

[
{
"id": "main:standard",
"name": "standard",
"description": "..."
}
]

列出所有 Agent:

[
{
"name": "main",
"type": "main",
"modes": [
{
"id": "standard",
"description": "..."
}
]
}
]

列出用户可调用的 skills:

[
{
"name": "skill-name",
"description": "description",
"location": "bundle-or-directory",
"content": "markdown body"
}
]

返回内容包含已安装 skills、skillmgr.external_paths 扫描到的外部 skills,以及其它应用注册的用户可调用内置 skills。external_paths 会递归扫描包含 SKILL.md 的目录;外部 skill 的 location 是实际 skill 目录。修改磁盘配置后,可以通过 PATCH /configPATCH /global/config 发送 {} / { "config": {} } 触发 reload,再请求该接口验证新增或移除的外部 skill。

当前只返回:

true

返回当前项目信息:

{
"id": "proj_demo",
"name": "demo",
"path": "/repo/demo",
"directory": "/repo/demo"
}

这些接口主要用于让 opencode TUI 启动过程不崩溃,当前返回占位数据:

接口当前响应
GET /vcs{ "sha": "", "branch": "", "dirty": false, "root": "<directory>" }
GET /lsp[]
GET /formatter[]
GET /find[]
GET /find/file[]
GET /find/symbol[]
GET /experimental/console{ "consoleManagedProviders": [], "switchableOrgCount": 0 }
GET /experimental/resource{}
GET /experimental/worktree[]
GET /experimental/workspace[]
GET /experimental/workspace/status[]
POST /experimental/workspace/sync-list[]

返回全局 session 列表,结构接近 GET /session。支持 query:

参数说明
roots=true只返回根 session。
start=<timestamp>只返回创建时间大于等于该毫秒时间戳的 session。
search=<text>按标题模糊过滤。
limit=<n>返回最后 n 条。
scope=project不按当前 directory 过滤。

OpenDesk 返回的 session 基本结构:

{
"id": "ses_abc",
"slug": "abc",
"projectID": "proj_demo",
"workspaceID": "wrk_123",
"directory": "/repo/demo",
"path": "",
"title": "Task title",
"version": "1",
"agent": "explore",
"time": {
"created": 1710000000000,
"updated": 1710000000000
},
"tokens": {
"input": 0,
"output": 0,
"reasoning": 0,
"cache": {
"read": 0,
"write": 0
}
},
"cost": 0
}

如果是 fork 出来的子任务,会额外包含 parentID

列出 session。

支持 query:

参数说明
search=<text>按标题过滤。
limit=<n>只返回最后 n 条。
roots=true只返回没有父任务的 session。
scope=project不按当前 directory 过滤。
directory=<path>指定当前目录。
workspace=<id>透传 workspace ID。

默认会按当前 directory 过滤。

返回所有 session 状态映射:

{
"ses_abc": {
"type": "idle"
},
"ses_def": {
"type": "busy"
}
}

OpenDesk 内部 runningwaiting 都映射为 busy,其他状态映射为 idle

创建 session。

请求示例:

{
"title": "Fix tests",
"workspaceID": "wrk_123",
"model": {
"providerID": "openai",
"modelID": "gpt-4o"
}
}

也支持顶层 providerIDmodelID,或字符串 model: "openai/gpt-4o"

创建时会初始化 OpenDesk task harness、权限提示器、ask-user 工具集、MCP workspace 和 system prompt,并发布 session.created 事件。

获取 session 详情。不存在返回 404:

{
"error": "Session not found"
}

更新 session。当前支持:

字段说明
title更新任务标题。
model / providerID / modelID更新该 task 使用的模型别名。

删除 session,返回 true,并发布 session.deleted

返回子 session 列表。

返回消息列表。

支持 query:

参数说明
limit=<n>最多返回 n 条。
before=<messageID>返回指定 message 之前的消息。

limit 截断结果时,响应头会设置:

X-Next-Cursor: <messageID>
Link: <current-url>&before=<messageID>; rel="next"

消息格式:

{
"info": {
"id": "msg_abc",
"sessionID": "ses_abc",
"role": "assistant",
"time": {
"created": 1710000000000,
"completed": 1710000000000
},
"modelID": "",
"providerID": "",
"mode": "default",
"agent": "explore",
"path": {
"cwd": "/repo/demo",
"root": "/repo/demo"
},
"cost": 0
},
"parts": []
}

GET /session/:sessionID/message/:messageID

Section titled “GET /session/:sessionID/message/:messageID”

返回单条消息。session 或 message 不存在时分别返回 404。

同步发送消息,等待 OpenDesk task 完成后返回 assistant message。

请求体文本提取规则:

字段支持格式
parts字符串数组,或 { "type": "text", "text": "..." } 数组
promptstring 或 text parts
messagestring 或 text parts

示例:

{
"messageID": "msg_user_1",
"model": {
"providerID": "openai",
"modelID": "gpt-4o"
},
"parts": [
{
"type": "text",
"text": "帮我分析这个目录"
}
]
}

行为:

  1. 确保 task harness 已初始化。
  2. 如果 task 正在运行,先等待上一轮完成。
  3. 根据请求体更新 task model。
  4. 发布 session.status busy。
  5. 发布用户 message.updatedmessage.part.updated
  6. 调用 task.submitUserMessage(text)
  7. 通过 waitUntilFinished 将流式 chunk 转换为 message.part.updated
  8. 完成后发布 assistant message.updatedsession.status idle。
  9. 返回最终 assistant message。

如果没有文本内容,返回 400:

{
"error": "No message content"
}

异步发送消息,立即返回 204。请求体文本规则同 POST /session/:sessionID/message

后台仍会发布同样的 session.statusmessage.updatedmessage.part.updated 事件。

调用 task.stop("user") 中止 session,返回 true,并发布 session.aborted

调用内部 task.fork() 创建子 session,并返回新 session 信息。

当前返回空数组:

[]

当前返回空数组:

[]

DELETE /session/:sessionID/message/:messageID

Section titled “DELETE /session/:sessionID/message/:messageID”

删除一条消息,返回 true。session 或 message 不存在时返回 404。

接口当前行为
POST /session/:sessionID/command返回 400 { "error": "Commands not supported" }
POST /session/:sessionID/shell返回 400 { "error": "Shell not supported" }

opencode 原生的 summarize、revert、permission response 等更多 session 接口当前没有在 OpenDesk server 中注册。

OpenDesk 会把内部消息 part 转换为 opencode part:

OpenDesk partopencode part
text{ type: "text", text }
tool_call{ type: "tool", callID, tool, state, input, output }
reasoning{ type: "reasoning", text }
image{ type: "file", mediaType: "image", url }
file{ type: "file", file }
error{ type: "text", text: "Error: ..." }

工具状态映射:

OpenDesk 状态opencode 状态
pendingpending
runningrunning
successresultcompleted
errorfailederror
其他completed

流式输出期间,同一 assistant message 中同一类型的 part 会使用稳定 part ID,便于 opencode TUI 做增量更新。

返回 MCP server 状态映射:

{
"filesystem": {
"status": "connected"
},
"github": {
"status": "failed",
"error": "boom"
},
"fetch": {
"status": "disabled"
}
}

状态来源优先是 mcpmgr worker 的 getServers,失败时回退到 applications.mcpmgr.servers 配置。

映射规则:

OpenDesk 状态opencode 状态
connected{ "status": "connected" }
error{ "status": "failed", "error": "..." }
其他{ "status": "disabled" }

按 MCP nameid 查找 server,调用 worker 的 connectServer。请求带 directory 时,会作为 stdio workspace 传入。

id 是 OpenDesk 创建 MCP server 时生成的内部唯一标识,格式为 mcp_<毫秒时间戳>_<6位base36随机后缀>,例如 mcp_1765200000000_ab12cd。它不会根据 server 名称或配置内容稳定复用;删除后重新添加会生成新的 id。

成功返回:

true

找不到 server 返回 404:

{
"_tag": "McpServerNotFoundError",
"name": "missing",
"message": "MCP server not found: missing"
}

按 MCP nameid 查找 server,调用 worker 的 disconnectServer。成功返回 true

opencode 的 MCP add 和 OAuth 接口当前没有实现:

接口状态
POST /mcp未实现
POST /mcp/:name/auth未实现
POST /mcp/:name/auth/callback未实现
POST /mcp/:name/auth/authenticate未实现
DELETE /mcp/:name/auth未实现

OpenDesk server 把内部权限请求桥接成 opencode permission request。

返回待处理权限请求数组:

[
{
"id": "per_lx...",
"sessionID": "ses_abc",
"permission": "read",
"patterns": ["D:/repo/README.md"],
"metadata": {
"toolName": "readFile",
"input": {
"file_path": "D:/repo/README.md"
},
"requests": [
{
"resourceType": "file",
"action": "read",
"resourcePath": "D:/repo/README.md"
}
],
"prefixOptions": []
},
"always": ["D:/repo/README.md"],
"tool": {
"messageID": "msg_...",
"callID": "call_..."
}
}
]

tool 只有在当前权限请求能关联到 assistant message 和 tool call 时才出现。

权限名推断规则:

工具或资源permission
readFile 或文件 readread
writeFileeditFilesearchReplaceapplyPatch 或文件 writeedit
globFilesglob
grepFilesgrep
executePowerShellexecuteBashexecuteZsh 或 execute actionbash
webFetch 或 networkwebfetch
名称包含 websearch 或以 search 结尾websearch
loadSkillcallSkill 或 skill resourceskill
createSubTasktask

回复权限请求。请求体支持两种格式:

{
"reply": "once"
}

或:

{
"body": {
"reply": "always"
}
}

reply 可选值:

OpenDesk 行为
once本次允许,不记忆。
always允许并按首个 always pattern 记忆为 prefix 规则。
reject拒绝。并会拒绝同 session 的其他待处理权限请求。

成功返回:

true

非法请求体返回 400:

{
"error": "Invalid permission reply"
}

不存在的 request 返回 404:

{
"_tag": "PermissionNotFoundError",
"requestID": "per_missing",
"message": "Permission request not found: per_missing"
}

权限请求默认超时 300 秒。超时会按 reject 处理并发布 permission.replied

OpenDesk 的 ask-user 工具会通过 question broker 暴露给 opencode 客户端。

返回待处理问题数组:

[
{
"id": "que_lx...",
"sessionID": "ses_abc",
"questions": [
{
"question": "Which mode should be used?",
"header": "Mode",
"options": [
{
"label": "Fast",
"description": "Use the faster path"
}
],
"multiple": false,
"custom": true
}
]
}
]

回复问题。

{
"answers": [["Fast"]]
}

answers 是二维字符串数组,第一层顺序对应 questions 顺序,第二层是选择的 label 列表。

成功返回 true。非法请求体返回:

{
"error": "Invalid question reply"
}

不存在的 request 返回:

{
"_tag": "QuestionNotFoundError",
"requestID": "que_missing",
"message": "Question request not found: que_missing"
}

拒绝问题,OpenDesk 内部会把 ask-user 结果解析为 cancelled。

成功返回:

true
类别已实现占位或未实现
启动serve --mode opencode--mode acp
认证Basic Auth、auth_token queryOAuth
事件/event/global/event SSEWebSocket 事件
配置/config/global/config/config/providersopencode 全量配置语义
provider/provider/provider/authprovider OAuth
sessionCRUD、消息同步/异步、abort、fork、children、message list/deletesummarize、revert、session permission response 等
MCPstatus、connect、disconnectadd、OAuth
权限list、reply
问题list、reply、reject
bootstrappath、project、agent、command、skillVCS/LSP/find/formatter 仅 stub
experimentalconsole/resource/session/worktree/workspace 部分 stubtool、pty、sync、file、v2、tui 等未注册

因此,当前 server 模式已经能支撑 opencode TUI 的基础连接、配置读取、session 对话、流式渲染、权限确认、用户问题确认和 MCP 连接状态,但还不是 opencode server 全量 API 的完整替代实现。