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支持 opencode 与 acp 两种模式。acp 模式详见 通过 ACP 协议使用 OpenDesk。
--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 对象还包含 capabilities、cost、limit、headers 等字段。context_length 未配置时,limit.context 默认为 128000,limit.output 固定为 16384。

providers[].enabled === false 的 provider 会从该接口返回中过滤掉。可以先修改磁盘上的 setting.json,再调用 PATCH /config 或 PATCH /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 /config 或 PATCH /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 内部 running 和 waiting 都映射为 busy,其他状态映射为 idle。

创建 session。

请求示例:

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

也支持顶层 providerID、modelID,或字符串 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.updated 和 message.part.updated。
  6. 调用 task.submitUserMessage(text)。
  7. 通过 waitUntilFinished 将流式 chunk 转换为 message.part.updated。
  8. 完成后发布 assistant message.updated 和 session.status idle。
  9. 返回最终 assistant message。

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

{
"error": "No message content"
}

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

后台仍会发布同样的 session.status、message.updated 和 message.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
success、resultcompleted
error、failederror
其他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 name 或 id 查找 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 name 或 id 查找 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
writeFile、editFile、searchReplace、applyPatch 或文件 writeedit
globFilesglob
grepFilesgrep
executePowerShell、executeBash、executeZsh 或 execute actionbash
webFetch 或 networkwebfetch
名称包含 websearch 或以 search 结尾websearch
loadSkill、callSkill 或 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(已独立实现,见 ACP 协议,不属于 opencode 兼容范围)
认证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 的完整替代实现。