opencode-rs-sdk
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chineseopencode-sdk
opencode-sdk
Rust SDK for OpenCode HTTP API with SSE streaming support. Provides ergonomic async client, 15 REST API modules, 40+ event types, and managed server lifecycle.
Use this file to understand the SDK structure, feature flags, and correct API usage patterns. All examples assume async context with tokio runtime.
支持SSE流的OpenCode HTTP API Rust SDK,提供符合人体工程学的异步客户端、15个REST API模块、40+种事件类型和托管服务生命周期管理。
你可以通过本文档了解SDK结构、功能开关和正确的API使用模式,所有示例都默认在tokio运行时的异步环境下运行。
Agent Operating Rules
Agent操作规则
- Always check feature flags before using APIs - and
httpare enabled by default,sseandserverrequire explicit enabling.cli - Unix platforms only - Windows will fail at compile time with .
compile_error! - Subscribe before sending - For streaming workflows, create SSE subscription before sending async prompts to avoid missing early events.
- Use convenience methods - Prefer and
Client::run_simple_text()for common workflows.wait_for_idle_text() - Handle all error variants - Match on variants, using helper methods like
OpencodeErrorandis_not_found().is_validation_error() - Drop subscriptions to cancel - and
SseSubscriptioncancel on drop; explicitly callRawSseSubscriptionfor early termination..close() - Server processes auto-kill - kills child process on drop; call
ManagedServerfor graceful shutdown..stop() - Directory header required - Most operations require header set via
x-opencode-directoryorClientBuilder::directory().ManagedRuntimeBuilder::directory() - URL encode path parameters - All path parameters must be URL-encoded using to handle special characters.
urlencoding::encode()
- 使用API前必须检查功能开关 - 和
http默认开启,sse和server需要显式启用。cli - 仅支持Unix平台 - Windows下编译时会触发报错。
compile_error! - 发送请求前先订阅 - 针对流处理工作流,发送异步提示前请先创建SSE订阅,避免丢失早期事件。
- 优先使用便捷方法 - 通用工作流推荐使用和
Client::run_simple_text()。wait_for_idle_text() - 处理所有错误变体 - 匹配变体,可使用
OpencodeError、is_not_found()等辅助方法。is_validation_error() - 销毁订阅即可取消 - 和
SseSubscription在销毁时自动取消;如需提前终止可显式调用RawSseSubscription。.close() - 服务进程自动终止 - 在销毁时会自动杀死子进程;如需优雅关闭可调用
ManagedServer。.stop() - 必须指定目录请求头 - 大部分操作需要通过或
ClientBuilder::directory()设置ManagedRuntimeBuilder::directory()请求头。x-opencode-directory - 路径参数需要URL编码 - 所有路径参数必须使用进行URL编码以处理特殊字符。
urlencoding::encode()
Environment and Version Constraints
环境与版本约束
| Constraint | Value | Impact |
|---|---|---|
| Platform | Unix only (Linux/macOS) | Windows compilation fails |
| Rust Edition | 2024 | Requires Rust 1.85+ |
| Default Features | | Always available unless disabled |
| Optional Features | | Requires explicit |
| Full Feature Set | | Use for complete functionality |
| Default Server URL | | ClientBuilder default |
| Timeout Default | 300 seconds | Suitable for long AI requests |
| 约束 | 值 | 影响 |
|---|---|---|
| 运行平台 | 仅支持Unix(Linux/macOS) | Windows下编译失败 |
| Rust版本 | 2024 | 需要Rust 1.85+ |
| 默认功能 | | 除非手动禁用否则始终可用 |
| 可选功能 | | 需要显式指定 |
| 完整功能集 | | 用于获取完整能力 |
| 默认服务URL | | ClientBuilder默认值 |
| 默认超时时间 | 300秒 | 适合长时间AI请求 |
Quick Task Playbooks
快速任务指南
Send a Simple Text Prompt and Wait for Response
发送简单文本提示并等待响应
rust
let client = Client::builder().build()?;
let session = client.run_simple_text("Hello, AI!").await?;
let response = client.wait_for_idle_text(&session.id, Duration::from_secs(60)).await?;rust
let client = Client::builder().build()?;
let session = client.run_simple_text("Hello, AI!").await?;
let response = client.wait_for_idle_text(&session.id, Duration::from_secs(60)).await?;Create Session with Custom Title
创建自定义标题的会话
rust
let session = client.create_session_with_title("My Coding Task").await?;rust
let session = client.create_session_with_title("My Coding Task").await?;Stream Events for a Session
订阅会话的事件流
rust
let mut subscription = client.subscribe_session(&session.id).await?;
while let Some(event) = subscription.recv().await {
match event {
Event::MessagePartUpdated { properties } => println!("{}", properties.delta.unwrap_or_default()),
Event::SessionIdle { .. } => break,
_ => {}
}
}rust
let mut subscription = client.subscribe_session(&session.id).await?;
while let Some(event) = subscription.recv().await {
match event {
Event::MessagePartUpdated { properties } => println!("{}", properties.delta.unwrap_or_default()),
Event::SessionIdle { .. } => break,
_ => {}
}
}Start Managed Server
启动托管服务
rust
let server = ManagedServer::start(ServerOptions::new().port(8080)).await?;
let client = Client::builder().base_url(server.url()).build()?;
// Server auto-stops when `server` is droppedrust
let server = ManagedServer::start(ServerOptions::new().port(8080)).await?;
let client = Client::builder().base_url(server.url()).build()?;
// 当`server`被销毁时服务会自动停止Getting Started
快速开始
Add to :
Cargo.tomltoml
[dependencies]
opencode-sdk = "0.1"在中添加依赖:
Cargo.tomltoml
[dependencies]
opencode-sdk = "0.1"Or with all features:
如需启用所有功能:
opencode-sdk = { version = "0.1", features = ["full"] }
Basic client setup:
```rust
use opencode_sdk::{Client, ClientBuilder};
let client = Client::builder()
.base_url("http://127.0.0.1:4096")
.directory("/path/to/project")
.timeout_secs(300)
.build()?;opencode-sdk = { version = "0.1", features = ["full"] }
基础客户端配置:
```rust
use opencode_sdk::{Client, ClientBuilder};
let client = Client::builder()
.base_url("http://127.0.0.1:4096")
.directory("/path/to/project")
.timeout_secs(300)
.build()?;Workspace Overview
工作空间结构
src/
├── lib.rs # Crate root, re-exports, feature gates
├── client.rs # Client, ClientBuilder - ergonomic API entrypoint
├── error.rs # OpencodeError, Result<T> - error handling
├── sse.rs # SseSubscriber, SseSubscription, SessionEventRouter
├── server.rs # ManagedServer, ServerOptions - server feature
├── cli.rs # CliRunner, RunOptions, CliEvent - cli feature
├── runtime.rs # ManagedRuntime - server+http features
├── http/ # HTTP API modules (requires http feature)
│ ├── mod.rs # HttpClient, HttpConfig
│ ├── sessions.rs # SessionsApi - 18 endpoints
│ ├── messages.rs # MessagesApi - 6 endpoints
│ ├── files.rs # FilesApi
│ ├── tools.rs # ToolsApi
│ └── ... # 11 more API modules
└── types/ # Data models
├── mod.rs # Type re-exports
├── session.rs # Session, SessionCreateOptions
├── message.rs # Message, Part, PromptRequest
├── event.rs # Event enum (40 variants)
└── ... # 11 more type modulessrc/
├── lib.rs # crate根文件,重导出、功能开关定义
├── client.rs # Client、ClientBuilder - 易用API入口
├── error.rs # OpencodeError、Result<T> - 错误处理
├── sse.rs # SseSubscriber、SseSubscription、SessionEventRouter
├── server.rs # ManagedServer、ServerOptions - server功能模块
├── cli.rs # CliRunner、RunOptions、CliEvent - cli功能模块
├── runtime.rs # ManagedRuntime - server+http功能模块
├── http/ # HTTP API模块(需要http功能开启)
│ ├── mod.rs # HttpClient、HttpConfig
│ ├── sessions.rs # SessionsApi - 18个接口
│ ├── messages.rs # MessagesApi - 6个接口
│ ├── files.rs # FilesApi
│ ├── tools.rs # ToolsApi
│ └── ... # 其余11个API模块
└── types/ # 数据模型
├── mod.rs # 类型重导出
├── session.rs # Session、SessionCreateOptions
├── message.rs # Message、Part、PromptRequest
├── event.rs # Event枚举(40个变体)
└── ... # 其余11个类型模块Core Client API
核心客户端API
The struct () is the primary ergonomic API.
Clientsrc/client.rsClientsrc/client.rsClientBuilder
ClientBuilder
Chain configuration before building:
- - Server URL (default:
base_url(url))http://127.0.0.1:4096 - - Set
directory(dir)headerx-opencode-directory - - Request timeout (default: 300)
timeout_secs(secs) - - Create the client (requires
build()feature)http
构建客户端前可链式配置参数:
- - 服务URL(默认:
base_url(url))http://127.0.0.1:4096 - - 设置
directory(dir)请求头x-opencode-directory - - 请求超时时间(默认:300)
timeout_secs(secs) - - 创建客户端(需要
build()功能开启)http
HTTP API Accessor Methods
HTTP API访问方法
Each returns an API client for the corresponding endpoint group:
rust
let sessions = client.sessions(); // SessionsApi
let messages = client.messages(); // MessagesApi
let parts = client.parts(); // PartsApi
let permissions = client.permissions(); // PermissionsApi
let questions = client.questions(); // QuestionsApi
let files = client.files(); // FilesApi
let find = client.find(); // FindApi
let providers = client.providers(); // ProvidersApi
let mcp = client.mcp(); // McpApi
let pty = client.pty(); // PtyApi
let config = client.config(); // ConfigApi
let tools = client.tools(); // ToolsApi
let project = client.project(); // ProjectApi
let worktree = client.worktree(); // WorktreeApi
let misc = client.misc(); // MiscApi每个方法返回对应接口组的API客户端:
rust
let sessions = client.sessions(); // SessionsApi
let messages = client.messages(); // MessagesApi
let parts = client.parts(); // PartsApi
let permissions = client.permissions(); // PermissionsApi
let questions = client.questions(); // QuestionsApi
let files = client.files(); // FilesApi
let find = client.find(); // FindApi
let providers = client.providers(); // ProvidersApi
let mcp = client.mcp(); // McpApi
let pty = client.pty(); // PtyApi
let config = client.config(); // ConfigApi
let tools = client.tools(); // ToolsApi
let project = client.project(); // ProjectApi
let worktree = client.worktree(); // WorktreeApi
let misc = client.misc(); // MiscApiSSE Subscription Methods (requires sse feature)
SSE订阅方法(需要sse功能开启)
rust
let sub = client.subscribe().await?; // All events for directory
let sub = client.subscribe_session(id).await?; // Filtered to session
let sub = client.subscribe_global().await?; // Global events (all dirs)
let raw = client.subscribe_raw().await?; // Raw JSON frames
let router = client.session_event_router().await?; // Get cached routerrust
let sub = client.subscribe().await?; // 目录下的所有事件
let sub = client.subscribe_session(id).await?; // 过滤指定会话的事件
let sub = client.subscribe_global().await?; // 全局事件(所有目录)
let raw = client.subscribe_raw().await?; // 原始JSON帧
let router = client.session_event_router().await?; // 获取缓存的路由Convenience Methods
便捷方法
rust
// Create session and send text (returns immediately, use SSE for response)
let session = client.run_simple_text("Hello").await?;
// Create session with title
let session = client.create_session_with_title("Task").await?;
// Send text asynchronously (empty response, use SSE)
client.send_text_async(&session.id, "Hello", None).await?;
// Subscribe, send, and wait for idle with text collection
let text = client.send_text_async_and_wait_for_idle(&session.id, "Hello", None, Duration::from_secs(60)).await?;
// Wait for idle on existing subscription
let text = client.wait_for_idle_text(&session.id, Duration::from_secs(60)).await?;rust
// 创建会话并发送文本(立即返回,响应需通过SSE获取)
let session = client.run_simple_text("Hello").await?;
// 创建带标题的会话
let session = client.create_session_with_title("Task").await?;
// 异步发送文本(空响应,需通过SSE获取结果)
client.send_text_async(&session.id, "Hello", None).await?;
// 订阅、发送、等待空闲并收集文本结果
let text = client.send_text_async_and_wait_for_idle(&session.id, "Hello", None, Duration::from_secs(60)).await?;
// 在已有订阅上等待会话空闲
let text = client.wait_for_idle_text(&session.id, Duration::from_secs(60)).await?;HTTP Endpoints
HTTP接口
All API modules require the feature (enabled by default).
http所有API模块需要功能开启(默认已启用)。
httpSessionsApi (src/http/sessions.rs
)
src/http/sessions.rsSessionsApi(src/http/sessions.rs
)
src/http/sessions.rs| Method | Endpoint | Description |
|---|---|---|
| POST /session | Create new session |
| POST /session | Create with convenience options |
| GET /session/{id} | Get session by ID |
| GET /session | List all sessions |
| DELETE /session/{id} | Delete session |
| POST /session/{id}/fork | Fork session |
| POST /session/{id}/abort | Abort active session |
| PATCH /session/{id} | Update session |
| POST /session/{id}/init | Initialize session |
| POST /session/{id}/share | Share session |
| DELETE /session/{id}/share | Unshare session |
| POST /session/{id}/revert | Revert to message |
| POST /session/{id}/unrevert | Undo revert |
| POST /session/{id}/summarize | Summarize session |
| GET /session/{id}/diff | Get session diff |
| GET /session/{id}/diff?messageId={msg_id} | Get diff since message |
| GET /session/status | Get server status |
| GET /session/{id}/children | Get forked sessions |
| GET /session/{id}/todo | Get todo items |
Important: Session creation requires as a query parameter, not in the JSON body:
directoryrust
let path = if let Some(directory) = &req.directory {
format!("/session?directory={}", urlencoding::encode(directory))
} else {
"/session".to_string()
};| 方法 | 接口路径 | 描述 |
|---|---|---|
| POST /session | 创建新会话 |
| POST /session | 使用便捷配置创建会话 |
| GET /session/{id} | 根据ID获取会话 |
| GET /session | 列出所有会话 |
| DELETE /session/{id} | 删除会话 |
| POST /session/{id}/fork | 复刻会话 |
| POST /session/{id}/abort | 终止活跃会话 |
| PATCH /session/{id} | 更新会话 |
| POST /session/{id}/init | 初始化会话 |
| POST /session/{id}/share | 共享会话 |
| DELETE /session/{id}/share | 取消共享会话 |
| POST /session/{id}/revert | 回退到指定消息 |
| POST /session/{id}/unrevert | 撤销回退 |
| POST /session/{id}/summarize | 生成会话摘要 |
| GET /session/{id}/diff | 获取会话差异 |
| GET /session/{id}/diff?messageId={msg_id} | 获取指定消息之后的差异 |
| GET /session/status | 获取服务状态 |
| GET /session/{id}/children | 获取复刻的子会话 |
| GET /session/{id}/todo | 获取待办事项 |
重要提示: 会话创建时是查询参数,不是JSON请求体中的字段:
directoryrust
let path = if let Some(directory) = &req.directory {
format!("/session?directory={}", urlencoding::encode(directory))
} else {
"/session".to_string()
};MessagesApi (src/http/messages.rs
)
src/http/messages.rsMessagesApi(src/http/messages.rs
)
src/http/messages.rs| Method | Endpoint | Description |
|---|---|---|
| POST /session/{id}/message | Send prompt |
| POST /session/{id}/prompt_async | Async prompt (empty response) |
| POST /session/{id}/prompt_async | Convenience text sender |
| GET /session/{id}/message | List messages |
| GET /session/{id}/message/{mid} | Get message |
| DELETE /session/{id}/message/{mid} | Remove message |
Important: and return empty body on success - use not .
prompt()prompt_async()request_empty()request_json()| 方法 | 接口路径 | 描述 |
|---|---|---|
| POST /session/{id}/message | 发送提示 |
| POST /session/{id}/prompt_async | 异步发送提示(空响应) |
| POST /session/{id}/prompt_async | 便捷文本发送方法 |
| GET /session/{id}/message | 列出消息列表 |
| GET /session/{id}/message/{mid} | 获取指定消息 |
| DELETE /session/{id}/message/{mid} | 删除消息 |
重要提示: 和成功时返回空响应体,请使用而非。
prompt()prompt_async()request_empty()request_json()Other API Modules
其他API模块
- PartsApi () - Message part CRUD operations
src/http/parts.rs - PermissionsApi () - Permission management
src/http/permissions.rs - QuestionsApi () - Question operations
src/http/questions.rs - FilesApi () - File operations (requires URL encoding for paths)
src/http/files.rs - FindApi () - Search operations
src/http/find.rs - ProvidersApi () - Provider management
src/http/providers.rs - McpApi () - MCP operations
src/http/mcp.rs - PtyApi () - PTY operations
src/http/pty.rs - ConfigApi () - Configuration
src/http/config.rs - ToolsApi () - Tool operations
src/http/tools.rs - ProjectApi () - Project operations
src/http/project.rs - WorktreeApi () - Worktree operations
src/http/worktree.rs - MiscApi () - Miscellaneous endpoints
src/http/misc.rs
- PartsApi () - 消息部分CRUD操作
src/http/parts.rs - PermissionsApi () - 权限管理
src/http/permissions.rs - QuestionsApi () - 问题操作
src/http/questions.rs - FilesApi () - 文件操作(路径需要URL编码)
src/http/files.rs - FindApi () - 搜索操作
src/http/find.rs - ProvidersApi () - 提供商管理
src/http/providers.rs - McpApi () - MCP操作
src/http/mcp.rs - PtyApi () - PTY操作
src/http/pty.rs - ConfigApi () - 配置管理
src/http/config.rs - ToolsApi () - 工具操作
src/http/tools.rs - ProjectApi () - 项目操作
src/http/project.rs - WorktreeApi () - 工作树操作
src/http/worktree.rs - MiscApi () - 其他杂项接口
src/http/misc.rs
Types and Models
类型和模型
Session Types (src/types/session.rs
)
src/types/session.rs会话类型(src/types/session.rs
)
src/types/session.rsrust
pub struct Session {
pub id: String,
pub project_id: Option<String>,
pub directory: Option<String>,
pub parent_id: Option<String>,
pub summary: Option<SessionSummary>,
pub share: Option<ShareInfo>,
pub title: String,
pub version: String,
pub time: Option<SessionTime>,
pub permission: Option<Ruleset>,
pub revert: Option<RevertInfo>,
}
pub struct SessionCreateOptions { /* builder pattern */ }
pub struct CreateSessionRequest { parent_id, title, permission, directory } // directory is query param!
pub struct UpdateSessionRequest { title }
pub struct SummarizeRequest { provider_id, model_id, auto }
pub struct RevertRequest { message_id, part_id }
pub struct SessionStatus { active_session_id, busy }
pub struct SessionDiff { diff, files }
pub struct TodoItem { id, content, completed, priority }rust
pub struct Session {
pub id: String,
pub project_id: Option<String>,
pub directory: Option<String>,
pub parent_id: Option<String>,
pub summary: Option<SessionSummary>,
pub share: Option<ShareInfo>,
pub title: String,
pub version: String,
pub time: Option<SessionTime>,
pub permission: Option<Ruleset>,
pub revert: Option<RevertInfo>,
}
pub struct SessionCreateOptions { /* builder模式 */ }
pub struct CreateSessionRequest { parent_id, title, permission, directory } // directory是查询参数!
pub struct UpdateSessionRequest { title }
pub struct SummarizeRequest { provider_id, model_id, auto }
pub struct RevertRequest { message_id, part_id }
pub struct SessionStatus { active_session_id, busy }
pub struct SessionDiff { diff, files }
pub struct TodoItem { id, content, completed, priority }Message Types (src/types/message.rs
)
src/types/message.rs消息类型(src/types/message.rs
)
src/types/message.rsrust
pub struct Message {
pub info: MessageInfo,
pub parts: Vec<Part>,
}
pub struct MessageInfo {
pub id: String,
pub session_id: Option<String>,
pub role: String, // "user", "assistant", "system"
pub time: MessageTime,
pub agent: Option<String>,
pub variant: Option<String>,
}
pub enum Part { // 12 variants
Text { id, text, synthetic, ignored, metadata },
File { id, mime, url, filename, source },
Tool { id, call_id, tool, input, state, metadata },
Reasoning { id, text, metadata },
StepStart { id, snapshot },
StepFinish { id, reason, snapshot, cost, tokens },
Snapshot { id, snapshot },
Patch { id, hash, files },
Agent { id, name, source },
Retry { id, attempt, error },
Compaction { id, auto },
Subtask { id, prompt, description, agent, command },
Unknown, // For forward compatibility
}
pub enum PromptPart {
Text { text, synthetic, ignored, metadata },
File { mime, url, filename },
Agent { name },
Subtask { prompt, description, agent, command },
}
pub struct PromptRequest {
pub parts: Vec<PromptPart>,
pub message_id: Option<String>,
pub model: Option<ModelRef>,
pub agent: Option<String>,
pub no_reply: Option<bool>,
pub system: Option<String>,
pub variant: Option<String>,
}
pub enum ToolState { // 5 variants - ORDER MATTERS for untagged deserialization
Completed(ToolStateCompleted), // Must come before more specific variants
Error(ToolStateError),
Running(ToolStateRunning),
Pending(ToolStatePending),
Unknown(serde_json::Value),
}Important: uses . More specific variants (, ) with more required fields must come before less specific ones (, ) to avoid incorrect deserialization.
ToolState#[serde(untagged)]CompletedErrorPendingRunningrust
pub struct Message {
pub info: MessageInfo,
pub parts: Vec<Part>,
}
pub struct MessageInfo {
pub id: String,
pub session_id: Option<String>,
pub role: String, // "user", "assistant", "system"
pub time: MessageTime,
pub agent: Option<String>,
pub variant: Option<String>,
}
pub enum Part { // 12个变体
Text { id, text, synthetic, ignored, metadata },
File { id, mime, url, filename, source },
Tool { id, call_id, tool, input, state, metadata },
Reasoning { id, text, metadata },
StepStart { id, snapshot },
StepFinish { id, reason, snapshot, cost, tokens },
Snapshot { id, snapshot },
Patch { id, hash, files },
Agent { id, name, source },
Retry { id, attempt, error },
Compaction { id, auto },
Subtask { id, prompt, description, agent, command },
Unknown, // 用于前向兼容
}
pub enum PromptPart {
Text { text, synthetic, ignored, metadata },
File { mime, url, filename },
Agent { name },
Subtask { prompt, description, agent, command },
}
pub struct PromptRequest {
pub parts: Vec<PromptPart>,
pub message_id: Option<String>,
pub model: Option<ModelRef>,
pub agent: Option<String>,
pub no_reply: Option<bool>,
pub system: Option<String>,
pub variant: Option<String>,
}
pub enum ToolState { // 5个变体 - 无标签反序列化时顺序非常重要
Completed(ToolStateCompleted), // 必须放在更通用的变体之前
Error(ToolStateError),
Running(ToolStateRunning),
Pending(ToolStatePending),
Unknown(serde_json::Value),
}重要提示: 使用注解,必填字段更多的特定变体(、)必须放在更通用的变体(、)之前,避免反序列化错误。
ToolState#[serde(untagged)]CompletedErrorPendingRunningEvent Types (src/types/event.rs
)
src/types/event.rs事件类型(src/types/event.rs
)
src/types/event.rs40 SSE event variants organized by category:
Server/Instance (4): , , ,
ServerConnectedServerHeartbeatServerInstanceDisposedGlobalDisposedSession (8): , , , , , , ,
SessionCreatedSessionUpdatedSessionDeletedSessionDiffSessionErrorSessionCompactedSessionStatusSessionIdleMessages (4): , , ,
MessageUpdatedMessageRemovedMessagePartUpdatedMessagePartRemovedPTY (4): , , ,
PtyCreatedPtyUpdatedPtyExitedPtyDeletedPermissions (4): , , ,
PermissionUpdatedPermissionRepliedPermissionAskedPermissionRepliedNextProject/Files (4): , , ,
ProjectUpdatedFileEditedFileWatcherUpdatedVcsBranchUpdatedLSP/Tools (4): , , ,
LspUpdatedLspClientDiagnosticsCommandExecutedMcpToolsChangedInstallation (3): , ,
InstallationUpdatedInstallationUpdateAvailableIdeInstalledTUI (4): , , ,
TuiPromptAppendTuiCommandExecuteTuiToastShowTuiSessionSelectTodo (1):
TodoUpdatedEvent deserialization uses - the field determines the variant. Session ID aliases are supported via . Unknown events deserialize to for forward compatibility.
#[serde(tag = "type")]"type"#[serde(alias = "sessionID")]Event::Unknown40个SSE事件变体按类别分组:
服务/实例(4个): 、、、
ServerConnectedServerHeartbeatServerInstanceDisposedGlobalDisposed会话(8个): 、、、、、、、
SessionCreatedSessionUpdatedSessionDeletedSessionDiffSessionErrorSessionCompactedSessionStatusSessionIdle消息(4个): 、、、
MessageUpdatedMessageRemovedMessagePartUpdatedMessagePartRemovedPTY(4个): 、、、
PtyCreatedPtyUpdatedPtyExitedPtyDeleted权限(4个): 、、、
PermissionUpdatedPermissionRepliedPermissionAskedPermissionRepliedNext项目/文件(4个): 、、、
ProjectUpdatedFileEditedFileWatcherUpdatedVcsBranchUpdatedLSP/工具(4个): 、、、
LspUpdatedLspClientDiagnosticsCommandExecutedMcpToolsChanged安装(3个): 、、
InstallationUpdatedInstallationUpdateAvailableIdeInstalledTUI(4个): 、、、
TuiPromptAppendTuiCommandExecuteTuiToastShowTuiSessionSelect待办(1个):
TodoUpdated事件反序列化使用注解,字段决定使用哪个变体。会话ID支持别名。未知事件会反序列化为以保证前向兼容。
#[serde(tag = "type")]"type"#[serde(alias = "sessionID")]Event::UnknownSSE Streaming
SSE流
The SSE module () provides robust event streaming with automatic reconnection.
src/sse.rsSSE模块()提供稳定的事件流能力,支持自动重连。
src/sse.rsKey Types
核心类型
rust
pub struct SseSubscriber { /* creates subscriptions */ }
pub struct SseSubscription { /* typed event receiver */ }
pub struct RawSseSubscription { /* raw JSON receiver */ }
pub struct SessionEventRouter { /* multiplexes to per-session channels */ }
pub struct SseOptions {
pub capacity: usize, // default: 256
pub initial_interval: Duration, // default: 250ms
pub max_interval: Duration, // default: 30s
}
pub struct SessionEventRouterOptions {
pub upstream: SseOptions,
pub session_capacity: usize, // default: 256
pub subscriber_capacity: usize, // default: 256
}
pub struct SseStreamStats {
pub events_in: u64, // server frames received
pub events_out: u64, // delivered to caller
pub dropped: u64, // filtered or channel full
pub parse_errors: u64, // bad JSON
pub reconnects: u64, // retry count
pub last_event_id: Option<String>, // resumption token
}rust
pub struct SseSubscriber { /* 创建订阅 */ }
pub struct SseSubscription { /* 类型化事件接收器 */ }
pub struct RawSseSubscription { /* 原始JSON接收器 */ }
pub struct SessionEventRouter { /* 多路复用到每个会话的通道 */ }
pub struct SseOptions {
pub capacity: usize, // 默认:256
pub initial_interval: Duration, // 默认:250ms
pub max_interval: Duration, // 默认:30s
}
pub struct SessionEventRouterOptions {
pub upstream: SseOptions,
pub session_capacity: usize, // 默认:256
pub subscriber_capacity: usize, // 默认:256
}
pub struct SseStreamStats {
pub events_in: u64, // 收到的服务端帧数量
pub events_out: u64, // 递交给调用方的数量
pub dropped: u64, // 过滤或通道已满丢弃的数量
pub parse_errors: u64, // JSON解析错误数量
pub reconnects: u64, // 重试次数
pub last_event_id: Option<String>, // 恢复令牌
}SseSubscriber Methods
SseSubscriber方法
rust
pub async fn subscribe(&self, opts: SseOptions) -> Result<SseSubscription>;
pub async fn subscribe_typed(&self, opts: SseOptions) -> Result<SseSubscription>;
pub async fn subscribe_global(&self, opts: SseOptions) -> Result<SseSubscription>;
pub async fn subscribe_typed_global(&self, opts: SseOptions) -> Result<SseSubscription>;
pub async fn subscribe_raw(&self, opts: SseOptions) -> Result<RawSseSubscription>;
pub async fn subscribe_session(&self, session_id: &str, opts: SseOptions) -> Result<SseSubscription>;
pub async fn session_event_router(&self, opts: SessionEventRouterOptions) -> Result<SessionEventRouter>;rust
pub async fn subscribe(&self, opts: SseOptions) -> Result<SseSubscription>;
pub async fn subscribe_typed(&self, opts: SseOptions) -> Result<SseSubscription>;
pub async fn subscribe_global(&self, opts: SseOptions) -> Result<SseSubscription>;
pub async fn subscribe_typed_global(&self, opts: SseOptions) -> Result<SseSubscription>;
pub async fn subscribe_raw(&self, opts: SseOptions) -> Result<RawSseSubscription>;
pub async fn subscribe_session(&self, session_id: &str, opts: SseOptions) -> Result<SseSubscription>;
pub async fn session_event_router(&self, opts: SessionEventRouterOptions) -> Result<SessionEventRouter>;Subscription Methods
订阅方法
rust
impl SseSubscription {
pub async fn recv(&mut self) -> Option<Event>; // None = stream closed
pub fn stats(&self) -> SseStreamStats;
pub fn close(&self);
}
impl RawSseSubscription {
pub async fn recv(&mut self) -> Option<RawSseEvent>;
pub fn stats(&self) -> SseStreamStats;
pub fn close(&self);
}
impl SessionEventRouter {
pub async fn subscribe(&self, session_id: &str) -> SseSubscription;
pub fn stats(&self) -> SseStreamStats;
pub fn close(&self);
}rust
impl SseSubscription {
pub async fn recv(&mut self) -> Option<Event>; // None表示流已关闭
pub fn stats(&self) -> SseStreamStats;
pub fn close(&self);
}
impl RawSseSubscription {
pub async fn recv(&mut self) -> Option<RawSseEvent>;
pub fn stats(&self) -> SseStreamStats;
pub fn close(&self);
}
impl SessionEventRouter {
pub async fn subscribe(&self, session_id: &str) -> SseSubscription;
pub fn stats(&self) -> SseStreamStats;
pub fn close(&self);
}Reconnection Behavior
重连行为
- Exponential backoff starting at 250ms, max 30s
- Jitter applied to prevent thundering herd
- Last-Event-ID header sent for resumption
- No max retry limit (infinite reconnection)
- Backoff resets on successful connection ()
EsEvent::Open
- 指数退避,初始间隔250ms,最大30s
- 添加抖动避免惊群效应
- 发送Last-Event-ID头用于断点恢复
- 无最大重试限制(无限重连)
- 连接成功()后退避间隔重置
EsEvent::Open
Session Filtering
会话过滤
- Client-side session filtering - filters events after parsing; server still sends all events
subscribe_session() - Session ID extraction has fallbacks - for , extracts from
message.part.updated; forproperties.part.sessionID|sessionId, fromsession.idle/errorproperties.sessionID|sessionId - Events without session ID are dropped when filtered (counts toward stat)
dropped
- 客户端侧会话过滤 - 在解析后过滤事件,服务端仍会发送所有事件
subscribe_session() - 会话ID提取有降级策略 - 对于,从
message.part.updated提取;对于properties.part.sessionID|sessionId,从session.idle/error提取properties.sessionID|sessionId - 过滤时无会话ID的事件会被丢弃(计入统计)
dropped
Server and CLI Features
服务和CLI功能
ManagedServer (requires server feature)
ManagedServer(需要server功能开启)
Spawn and manage processes:
opencode serverust
pub struct ServerOptions {
pub port: Option<u16>, // None = random port
pub hostname: String, // default: "127.0.0.1"
pub directory: Option<PathBuf>,
pub config_json: Option<String>, // via OPENCODE_CONFIG_CONTENT
pub startup_timeout_ms: u64, // default: 5000
pub binary: String, // default: "opencode"
}
pub struct ManagedServer {
pub async fn start(opts: ServerOptions) -> Result<Self>;
pub fn url(&self) -> &Url;
pub fn port(&self) -> u16;
pub async fn stop(mut self) -> Result<()>;
pub fn is_running(&mut self) -> bool;
}Server automatically stops on drop via kill signal. Waits for "opencode server listening on" in stdout or falls back to probe.
/doc启动并管理进程:
opencode serverust
pub struct ServerOptions {
pub port: Option<u16>, // None表示随机端口
pub hostname: String, // 默认:"127.0.0.1"
pub directory: Option<PathBuf>,
pub config_json: Option<String>, // 通过OPENCODE_CONFIG_CONTENT环境变量传递
pub startup_timeout_ms: u64, // 默认:5000
pub binary: String, // 默认:"opencode"
}
pub struct ManagedServer {
pub async fn start(opts: ServerOptions) -> Result<Self>;
pub fn url(&self) -> &Url;
pub fn port(&self) -> u16;
pub async fn stop(mut self) -> Result<()>;
pub fn is_running(&mut self) -> bool;
}服务在销毁时会自动通过kill信号停止,启动时会等待stdout输出"opencode server listening on"或通过探针检测启动成功。
/docCliRunner (requires cli feature)
CliRunner(需要cli功能开启)
Wrap :
opencode run --format jsonrust
pub struct RunOptions {
pub format: Option<String>, // default: "json"
pub attach: Option<String>,
pub continue_session: bool,
pub session: Option<String>,
pub file: Vec<String>,
pub share: bool,
pub model: Option<String>,
pub agent: Option<String>,
pub title: Option<String>,
pub port: Option<u16>,
pub command: Option<String>,
pub directory: Option<PathBuf>,
pub binary: String, // default: "opencode"
}
pub struct CliEvent {
pub r#type: String,
pub timestamp: Option<i64>,
pub session_id: Option<String>,
pub data: serde_json::Value,
}
pub struct CliRunner {
pub async fn start(prompt: &str, opts: RunOptions) -> Result<Self>;
pub async fn recv(&mut self) -> Option<CliEvent>;
pub async fn collect_text(&mut self) -> String;
}CliEvent helper methods: , , , , , .
is_text()is_step_start()is_step_finish()is_error()is_tool_use()text()Important: CLI stderr is inherited () to prevent buffer deadlock when CLI writes >64KB to stdout.
Stdio::inherit()封装命令:
opencode run --format jsonrust
pub struct RunOptions {
pub format: Option<String>, // 默认:"json"
pub attach: Option<String>,
pub continue_session: bool,
pub session: Option<String>,
pub file: Vec<String>,
pub share: bool,
pub model: Option<String>,
pub agent: Option<String>,
pub title: Option<String>,
pub port: Option<u16>,
pub command: Option<String>,
pub directory: Option<PathBuf>,
pub binary: String, // 默认:"opencode"
}
pub struct CliEvent {
pub r#type: String,
pub timestamp: Option<i64>,
pub session_id: Option<String>,
pub data: serde_json::Value,
}
pub struct CliRunner {
pub async fn start(prompt: &str, opts: RunOptions) -> Result<Self>;
pub async fn recv(&mut self) -> Option<CliEvent>;
pub async fn collect_text(&mut self) -> String;
}CliEvent辅助方法:、、、、、。
is_text()is_step_start()is_step_finish()is_error()is_tool_use()text()重要提示: CLI stderr会被继承(),避免CLI向stdout写入超过64KB数据时出现缓冲区死锁。
Stdio::inherit()ManagedRuntime (requires server and http features)
ManagedRuntime(需要server和http功能开启)
Combined server process + client for integration testing:
rust
let runtime = ManagedRuntime::builder()
.hostname("127.0.0.1")
.port(4096)
.directory("/test/project")
.startup_timeout_ms(10_000)
.start()
.await?;
let client = runtime.client();
// Use client...
runtime.stop().await?;
// Or just drop runtime to stop serverQuick start with current directory:
rust
let runtime = ManagedRuntime::start_for_cwd().await?;
let session = runtime.client().run_simple_text("test").await?;组合服务进程和客户端,用于集成测试:
rust
let runtime = ManagedRuntime::builder()
.hostname("127.0.0.1")
.port(4096)
.directory("/test/project")
.startup_timeout_ms(10_000)
.start()
.await?;
let client = runtime.client();
// 使用client...
runtime.stop().await?;
// 或者直接销毁runtime即可停止服务使用当前目录快速启动:
rust
let runtime = ManagedRuntime::start_for_cwd().await?;
let session = runtime.client().run_simple_text("test").await?;Usage Cards
使用指南
Client Usage Card
客户端使用指南
Use when: Building applications that interact with OpenCode HTTP API
Enable/Install: Default features (, )
httpsseImport/Invoke:
rust
use opencode_sdk::{Client, ClientBuilder};
let client = Client::builder().build()?;Minimal flow:
- Build client with
Client::builder().base_url(url).directory(dir).build() - Use API accessors like
client.sessions().create(&req).await - For streaming, create SSE subscription before sending async requests
- Handle errors using and
Result<T>variantsOpencodeError
Key APIs:
- - Create builder
Client::builder() - - Build client
ClientBuilder::build() - ,
client.sessions()- API accessorsclient.messages() - - Per-session SSE
client.subscribe_session(id).await - - Quick prompt
client.run_simple_text(text).await
Pitfalls:
- Forgetting to subscribe before - events will be lost
prompt_async - Not handling - may miss structured error data
OpencodeError::Http - Missing feature causes
httpto returnbuild()OpencodeError::InvalidConfig
Source:
src/client.rs适用场景: 构建与OpenCode HTTP API交互的应用
启用/安装: 默认功能(、)
httpsse导入/调用:
rust
use opencode_sdk::{Client, ClientBuilder};
let client = Client::builder().build()?;最小流程:
- 使用构建客户端
Client::builder().base_url(url).directory(dir).build() - 使用API访问器如调用接口
client.sessions().create(&req).await - 对于流处理场景,发送异步请求前先创建SSE订阅
- 使用和
Result<T>变体处理错误OpencodeError
核心API:
- - 创建构建器
Client::builder() - - 构建客户端
ClientBuilder::build() - 、
client.sessions()- API访问器client.messages() - - 单会话SSE订阅
client.subscribe_session(id).await - - 快速发送提示
client.run_simple_text(text).await
常见陷阱:
- 在前忘记订阅 - 会丢失事件
prompt_async - 没有处理- 可能错过结构化错误数据
OpencodeError::Http - 未开启功能会导致
http返回build()OpencodeError::InvalidConfig
源码位置:
src/client.rsSessionsApi Usage Card
SessionsApi使用指南
Use when: Managing sessions (CRUD, forking, sharing, reverting)
Enable/Install: feature (default)
httpImport/Invoke:
rust
let sessions = client.sessions();Minimal flow:
- Create:
sessions.create(&CreateSessionRequest::default()).await - Or with title:
sessions.create_with(SessionCreateOptions::new().with_title("Task")).await - List:
sessions.list().await - Delete:
sessions.delete(&id).await
Key APIs:
- ,
create(req)- Create sessionscreate_with(opts) - ,
get(id)- Retrieve sessionslist() - - Remove session
delete(id) - - Fork session
fork(id) - ,
share(id)- Sharingunshare(id) - - Revert to message
revert(id, req)
Pitfalls:
- Session IDs are strings - don't assume UUID format
- is more ergonomic than manual
create_withCreateSessionRequest - Critical: is a query parameter, NOT in the JSON body
directory
Source:
src/http/sessions.rs适用场景: 管理会话(CRUD、复刻、共享、回退)
启用/安装: 功能(默认已开启)
http导入/调用:
rust
let sessions = client.sessions();最小流程:
- 创建:
sessions.create(&CreateSessionRequest::default()).await - 带标题创建:
sessions.create_with(SessionCreateOptions::new().with_title("Task")).await - 列表:
sessions.list().await - 删除:
sessions.delete(&id).await
核心API:
- 、
create(req)- 创建会话create_with(opts) - 、
get(id)- 查询会话list() - - 删除会话
delete(id) - - 复刻会话
fork(id) - 、
share(id)- 共享管理unshare(id) - - 回退到指定消息
revert(id, req)
常见陷阱:
- 会话ID是字符串 - 不要假设是UUID格式
- 比手动构造
create_with更易用CreateSessionRequest - 关键: 是查询参数,不是JSON请求体的字段
directory
源码位置:
src/http/sessions.rsMessagesApi Usage Card
MessagesApi使用指南
Use when: Sending prompts or managing messages
Enable/Install: feature (default)
httpImport/Invoke:
rust
let messages = client.messages();Minimal flow:
- Send prompt:
messages.prompt(&session_id, &PromptRequest::text("Hello")).await - Or async (no response body):
messages.prompt_async(&session_id, &req).await - List:
messages.list(&session_id).await - Get:
messages.get(&session_id, &message_id).await
Key APIs:
- - Send prompt (sync-like)
prompt(session_id, req) - - Async send (use with SSE)
prompt_async(session_id, req) - - Convenience method
send_text_async(session_id, text, model) - ,
list(session_id)- Retrieve messagesget(session_id, message_id) - - Delete message
remove(session_id, message_id)
Pitfalls:
- returns empty body - must use SSE for response
prompt_async - for model selection
PromptRequest::text("...").with_model("provider", "model")
Source:
src/http/messages.rs适用场景: 发送提示或管理消息
启用/安装: 功能(默认已开启)
http导入/调用:
rust
let messages = client.messages();最小流程:
- 发送提示:
messages.prompt(&session_id, &PromptRequest::text("Hello")).await - 异步发送(无响应体):
messages.prompt_async(&session_id, &req).await - 列表:
messages.list(&session_id).await - 查询:
messages.get(&session_id, &message_id).await
核心API:
- - 发送提示(类同步)
prompt(session_id, req) - - 异步发送(配合SSE使用)
prompt_async(session_id, req) - - 便捷方法
send_text_async(session_id, text, model) - 、
list(session_id)- 查询消息get(session_id, message_id) - - 删除消息
remove(session_id, message_id)
常见陷阱:
- 返回空响应体 - 必须通过SSE获取响应
prompt_async - 模型选择使用
PromptRequest::text("...").with_model("provider", "model")
源码位置:
src/http/messages.rsSseSubscriber Usage Card
SseSubscriber使用指南
Use when: Consuming real-time OpenCode events (session updates, message streaming, permission requests)
Enable/Install: feature (default)
sseImport/Invoke:
rust
use opencode_sdk::sse::{SseSubscriber, SseOptions};
let subscriber = SseSubscriber::new(
"http://127.0.0.1:4096".into(),
Some("/my/project".into()),
None, // optional ReqClient
);
let mut sub = subscriber.subscribe(SseOptions::default()).await?;
while let Some(event) = sub.recv().await {
// handle event
}Minimal flow:
- Create subscriber:
SseSubscriber::new(base_url, directory, client) - Subscribe:
subscriber.subscribe(SseOptions::default()).await? - Receive:
while let Some(event) = sub.recv().await { /* process */ } - Drop or to cancel
sub.close()
Key APIs:
- - Subscribe to typed events
subscribe(opts) - - Filter by session
subscribe_session(session_id, opts) - - Subscribe to global events
subscribe_global(opts) - - Raw JSON frames for debugging
subscribe_raw(opts)
Pitfalls:
- Drop cancels stream - both and
SseSubscriptioncancel on dropRawSseSubscription - can return
recv()(stream closed) - handle this caseNone - Monitor to detect backpressure or filtering issues
stats().dropped
Source:
src/sse.rs适用场景: 消费OpenCode实时事件(会话更新、消息流、权限请求)
启用/安装: 功能(默认已开启)
sse导入/调用:
rust
use opencode_sdk::sse::{SseSubscriber, SseOptions};
let subscriber = SseSubscriber::new(
"http://127.0.0.1:4096".into(),
Some("/my/project".into()),
None, // 可选ReqClient
);
let mut sub = subscriber.subscribe(SseOptions::default()).await?;
while let Some(event) = sub.recv().await {
// 处理事件
}最小流程:
- 创建订阅者:
SseSubscriber::new(base_url, directory, client) - 订阅:
subscriber.subscribe(SseOptions::default()).await? - 接收:
while let Some(event) = sub.recv().await { /* 处理 */ } - 销毁或调用取消订阅
sub.close()
核心API:
- - 订阅类型化事件
subscribe(opts) - - 按会话过滤
subscribe_session(session_id, opts) - - 订阅全局事件
subscribe_global(opts) - - 原始JSON帧(用于调试)
subscribe_raw(opts)
常见陷阱:
- 销毁会取消流 - 和
SseSubscription在销毁时都会取消订阅RawSseSubscription - 可能返回
recv()(流已关闭) - 请处理该情况None - 监控检测背压或过滤问题
stats().dropped
源码位置:
src/sse.rsEvent Enum Usage Card
Event枚举使用指南
Use when: Handling specific OpenCode event types (40 variants)
Enable/Install: Part of module
opencode_sdk::types::eventImport/Invoke:
rust
use opencode_sdk::types::event::Event;Minimal flow:
rust
let event: Event = serde_json::from_str(&json)?;
match event {
Event::MessagePartUpdated { properties } => {
// Streaming text delta
if let Some(delta) = &properties.delta {
print!("{}", delta);
}
}
Event::SessionIdle { properties } => println!("Session idle: {}", properties.info.id),
Event::PermissionAsked { properties } => {
let request = &properties.request;
println!("Permission: {} for {:?}", request.permission, request.patterns);
}
Event::Unknown => println!("Unknown event type"),
_ => {}
}Key APIs:
- - Extract session ID if present (returns
event.session_id())Option<&str> - - Check for keep-alive
event.is_heartbeat() - - Check for connection event
event.is_connected()
Pitfalls:
- Only 10 of 40 event variants contain session_id - use method which handles this correctly
session_id() - Unknown types deserialize to - always handle this case
Event::Unknown
Source:
src/types/event.rs适用场景: 处理特定OpenCode事件类型(40个变体)
启用/安装: 属于模块
opencode_sdk::types::event导入/调用:
rust
use opencode_sdk::types::event::Event;最小流程:
rust
let event: Event = serde_json::from_str(&json)?;
match event {
Event::MessagePartUpdated { properties } => {
// 流文本增量
if let Some(delta) = &properties.delta {
print!("{}", delta);
}
}
Event::SessionIdle { properties } => println!("会话空闲: {}", properties.info.id),
Event::PermissionAsked { properties } => {
let request = &properties.request;
println!("权限请求: {} 对应 {:?}", request.permission, request.patterns);
}
Event::Unknown => println!("未知事件类型"),
_ => {}
}核心API:
- - 提取会话ID(存在则返回
event.session_id())Option<&str> - - 检查是否是心跳事件
event.is_heartbeat() - - 检查是否是连接事件
event.is_connected()
常见陷阱:
- 40个事件变体中只有10个包含session_id - 请使用方法正确处理
session_id() - 未知类型会反序列化为- 请始终处理该情况
Event::Unknown
源码位置:
src/types/event.rsManagedServer Usage Card
ManagedServer使用指南
Use when: Programmatically starting OpenCode server for tests or automation
Enable/Install: feature (NOT default)
serverImport/Invoke:
rust
use opencode_sdk::server::{ManagedServer, ServerOptions};
let server = ManagedServer::start(ServerOptions::new().port(8080)).await?;Minimal flow:
- Configure:
ServerOptions::new().port(8080).directory("/project") - Start:
let server = ManagedServer::start(opts).await? - Get URL:
let url = server.url() - Create client:
let client = Client::builder().base_url(url).build()? - Stop (or drop):
server.stop().await?
Key APIs:
- - Create options
ServerOptions::new() - ,
ServerOptions::port(),::hostname(),::directory()- Configure::config_json() - - Spawn server
ManagedServer::start(opts).await - ,
ManagedServer::url()- Get connection info::port() - - Graceful shutdown
ManagedServer::stop().await
Pitfalls:
- Server kills on drop - hold reference while using
ManagedServer - sets env var
config_json- server must support thisOPENCODE_CONFIG_CONTENT - Startup timeout defaults to 5s - increase for slow systems
Source:
src/server.rs适用场景: 编程式启动OpenCode服务用于测试或自动化场景
启用/安装: 功能(默认未开启)
server导入/调用:
rust
use opencode_sdk::server::{ManagedServer, ServerOptions};
let server = ManagedServer::start(ServerOptions::new().port(8080)).await?;最小流程:
- 配置:
ServerOptions::new().port(8080).directory("/project") - 启动:
let server = ManagedServer::start(opts).await? - 获取URL:
let url = server.url() - 创建客户端:
let client = Client::builder().base_url(url).build()? - 停止(或销毁):
server.stop().await?
核心API:
- - 创建配置
ServerOptions::new() - 、
ServerOptions::port()、::hostname()、::directory()- 配置参数::config_json() - - 启动服务
ManagedServer::start(opts).await - 、
ManagedServer::url()- 获取连接信息::port() - - 优雅关闭
ManagedServer::stop().await
常见陷阱:
- 销毁时会杀死服务 - 使用期间请持有引用
ManagedServer - 会设置环境变量
config_json- 服务需要支持该配置OPENCODE_CONFIG_CONTENT - 启动超时默认5秒 - 慢系统请适当增大
源码位置:
src/server.rsCliRunner Usage Card
CliRunner使用指南
Use when: Falling back to CLI when HTTP API unavailable
Enable/Install: feature (NOT default)
cliImport/Invoke:
rust
use opencode_sdk::cli::{CliRunner, RunOptions};
let mut runner = CliRunner::start("Hello", RunOptions::new()).await?;Minimal flow:
- Create options:
RunOptions::new().model("provider/model").agent("code") - Start:
let mut runner = CliRunner::start("prompt", opts).await? - Stream:
while let Some(event) = runner.recv().await { /* process */ } - Or collect:
let text = runner.collect_text().await
Key APIs:
- - Create options (format defaults to "json")
RunOptions::new() - ,
RunOptions::model(),::agent(),::title()- Configure::attach() - - Start CLI
CliRunner::start(prompt, opts).await - - Get next event
CliRunner::recv().await - - Aggregate text events
CliRunner::collect_text().await - ,
CliEvent::is_text()- Event inspection::text()
Pitfalls:
- CLI outputs NDJSON to stdout - must be "json"
format - stderr inherited - CLI errors visible but not captured
- Session sharing requires in options
share: true
Source:
src/cli.rs适用场景: HTTP API不可用时降级使用CLI
启用/安装: 功能(默认未开启)
cli导入/调用:
rust
use opencode_sdk::cli::{CliRunner, RunOptions};
let mut runner = CliRunner::start("Hello", RunOptions::new()).await?;最小流程:
- 创建配置:
RunOptions::new().model("provider/model").agent("code") - 启动:
let mut runner = CliRunner::start("prompt", opts).await? - 流处理:
while let Some(event) = runner.recv().await { /* 处理 */ } - 或收集结果:
let text = runner.collect_text().await
核心API:
- - 创建配置(格式默认是"json")
RunOptions::new() - 、
RunOptions::model()、::agent()、::title()- 配置参数::attach() - - 启动CLI
CliRunner::start(prompt, opts).await - - 获取下一个事件
CliRunner::recv().await - - 聚合文本事件
CliRunner::collect_text().await - 、
CliEvent::is_text()- 事件检查::text()
常见陷阱:
- CLI向stdout输出NDJSON - 必须设置为"json"
format - stderr被继承 - CLI错误可见但不会被捕获
- 会话共享需要在配置中设置
share: true
源码位置:
src/cli.rsOpencodeError Usage Card
OpencodeError使用指南
Use when: Handling errors from SDK operations
Enable/Install: Part of crate (re-exported from )
opencode_sdklib.rsImport/Invoke:
rust
use opencode_sdk::{OpencodeError, Result};
fn handle_error(err: OpencodeError) {
match err {
OpencodeError::Http { status, name, message, .. } => {
eprintln!("HTTP {}: {}", status, message);
}
OpencodeError::Network(msg) => {
eprintln!("Network error: {}", msg);
}
OpencodeError::StreamClosed => {
eprintln!("SSE stream closed unexpectedly");
}
_ => eprintln!("Other error: {}", err),
}
}Minimal flow:
rust
let result = client.run_simple_text("test").await;
if let Err(e) = result {
if e.is_not_found() {
// Handle 404
} else if e.is_server_error() {
// Handle 5xx
}
}Key APIs:
- - Parse HTTP error with NamedError body
OpencodeError::http(status, body) - - Check for 404 errors
is_not_found() - - Check for 400 validation errors
is_validation_error() - - Check for 5xx errors
is_server_error() - - Get NamedError name (e.g., "ValidationError")
error_name()
Pitfalls:
- HTTP errors may contain structured field with additional context
data - Plain text HTTP responses fall back to generic "HTTP {status}" message
- SSE error requires re-subscription to recover
StreamClosed
Source:
src/error.rs适用场景: 处理SDK操作返回的错误
启用/安装: 属于 crate(从重导出)
opencode_sdklib.rs导入/调用:
rust
use opencode_sdk::{OpencodeError, Result};
fn handle_error(err: OpencodeError) {
match err {
OpencodeError::Http { status, name, message, .. } => {
eprintln!("HTTP {}: {}", status, message);
}
OpencodeError::Network(msg) => {
eprintln!("网络错误: {}", msg);
}
OpencodeError::StreamClosed => {
eprintln!("SSE流意外关闭");
}
_ => eprintln!("其他错误: {}", err),
}
}最小流程:
rust
let result = client.run_simple_text("test").await;
if let Err(e) = result {
if e.is_not_found() {
// 处理404
} else if e.is_server_error() {
// 处理5xx
}
}核心API:
- - 解析带NamedError body的HTTP错误
OpencodeError::http(status, body) - - 检查是否是404错误
is_not_found() - - 检查是否是400校验错误
is_validation_error() - - 检查是否是5xx错误
is_server_error() - - 获取NamedError名称(如"ValidationError")
error_name()
常见陷阱:
- HTTP错误可能包含结构化字段提供额外上下文
data - 纯文本HTTP响应会降级为通用"HTTP {status}"消息
- SSE 错误需要重新订阅才能恢复
StreamClosed
源码位置:
src/error.rsAPI Reference
API参考
Core Types
核心类型
| Type | Location | Description |
|---|---|---|
| | Main ergonomic API client |
| | Builder for Client |
| | Error enum (13 variants) |
| | Type alias for Result |
| 类型 | 位置 | 描述 |
|---|---|---|
| | 主易用API客户端 |
| | Client构建器 |
| | 错误枚举(13个变体) |
| | Result类型别名 |
SSE Types
SSE类型
| Type | Location | Description |
|---|---|---|
| | Creates SSE subscriptions |
| | Typed event subscription |
| | Raw JSON subscription |
| | Multiplexes to sessions |
| | Subscription options |
| | Router options |
| | Diagnostics snapshot |
| | Raw SSE frame |
| 类型 | 位置 | 描述 |
|---|---|---|
| | 创建SSE订阅 |
| | 类型化事件订阅 |
| | 原始JSON订阅 |
| | 多路复用到会话 |
| | 订阅配置 |
| | 路由配置 |
| | 诊断快照 |
| | 原始SSE帧 |
Server/CLI Types
服务/CLI类型
| Type | Location | Description |
|---|---|---|
| | Managed server process |
| | Server configuration |
| | Server + Client combo |
| | CLI wrapper |
| | CLI options |
| | CLI output event |
| 类型 | 位置 | 描述 |
|---|---|---|
| | 托管服务进程 |
| | 服务配置 |
| | 服务+客户端组合 |
| | CLI封装 |
| | CLI配置 |
| | CLI输出事件 |
HTTP Types
HTTP类型
| Type | Location | Description |
|---|---|---|
| | Low-level HTTP client |
| | HTTP configuration |
| | Sessions API client |
| | Messages API client |
| 类型 | 位置 | 描述 |
|---|---|---|
| | 底层HTTP客户端 |
| | HTTP配置 |
| | 会话API客户端 |
| | 消息API客户端 |
Model Types
模型类型
| Type | Location | Description |
|---|---|---|
| | Session model |
| | Builder for create |
| | Message with parts |
| | Message metadata |
| | Content part enum (12 variants) |
| | Prompt request |
| | Prompt part enum |
| | SSE event enum (40 variants) |
| | Global event wrapper |
| | Tool execution state |
| 类型 | 位置 | 描述 |
|---|---|---|
| | 会话模型 |
| | 创建构建器 |
| | 带部分的消息 |
| | 消息元数据 |
| | 内容部分枚举(12个变体) |
| | 提示请求 |
| | 提示部分枚举 |
| | SSE事件枚举(40个变体) |
| | 全局事件包装 |
| | 工具执行状态 |
Common Pitfalls
常见陷阱
Feature Flag Mismatches
功能开关不匹配
rust
// ❌ Won't compile without http feature
let client = Client::builder().build()?; // Returns OpencodeError::InvalidConfig
// ✅ Enable http feature in Cargo.toml
opencode-sdk = { version = "0.1", features = ["http"] }rust
// ❌ 未开启http功能时无法编译
let client = Client::builder().build()?; // 返回OpencodeError::InvalidConfig
// ✅ 在Cargo.toml中开启http功能
opencode-sdk = { version = "0.1", features = ["http"] }Missing SSE Subscription
缺失SSE订阅
rust
// ❌ Response events lost
client.send_text_async(&session_id, "Hello", None).await?;
let sub = client.subscribe_session(&session_id).await?; // Too late!
// ✅ Subscribe first
let sub = client.subscribe_session(&session_id).await?;
client.send_text_async(&session_id, "Hello", None).await?;
// Now events are capturedrust
// ❌ 响应事件丢失
client.send_text_async(&session_id, "Hello", None).await?;
let sub = client.subscribe_session(&session_id).await?; // 太晚了!
// ✅ 先订阅
let sub = client.subscribe_session(&session_id).await?;
client.send_text_async(&session_id, "Hello", None).await?;
// 现在事件会被捕获Platform Incompatibility
平台不兼容
rust
// ❌ Compiling on Windows will fail with:
// error: opencode_sdk only supports Unix-like platforms (Linux/macOS). Windows is not supported.
// ✅ Use WSL, Docker, or macOS/Linuxrust
// ❌ Windows下编译会失败,报错:
// error: opencode_sdk only supports Unix-like platforms (Linux/macOS). Windows is not supported.
// ✅ 使用WSL、Docker或macOS/Linux系统Server Lifecycle Management
服务生命周期管理
rust
// ❌ Server dropped too early
{
let server = ManagedServer::start(ServerOptions::new()).await?;
let client = Client::builder().base_url(server.url()).build()?;
} // Server killed here!
// ❌ Client requests will fail
// ✅ Keep server alive
let server = ManagedServer::start(ServerOptions::new()).await?;
let client = Client::builder().base_url(server.url()).build()?;
// Use client while server in scope
server.stop().await?; // Or let it droprust
// ❌ 服务被提前销毁
{
let server = ManagedServer::start(ServerOptions::new()).await?;
let client = Client::builder().base_url(server.url()).build()?;
} // 服务在这里被杀死!
// ❌ 客户端请求会失败
// ✅ 保持服务存活
let server = ManagedServer::start(ServerOptions::new()).await?;
let client = Client::builder().base_url(server.url()).build()?;
// 服务在作用域内时使用客户端
server.stop().await?; // 或者等它自动销毁Error Handling
错误处理
rust
// ❌ Generic error handling misses context
if let Err(e) = result {
eprintln!("Error: {}", e);
}
// ✅ Use helper methods for specific handling
match result {
Err(e) if e.is_not_found() => println!("Not found"),
Err(e) if e.is_validation_error() => println!("Validation: {:?}", e.error_name()),
Err(e) => eprintln!("Other: {}", e),
Ok(v) => v,
}rust
// ❌ 通用错误处理会丢失上下文
if let Err(e) = result {
eprintln!("Error: {}", e);
}
// ✅ 使用辅助方法做针对性处理
match result {
Err(e) if e.is_not_found() => println!("资源不存在"),
Err(e) if e.is_validation_error() => println!("校验错误: {:?}", e.error_name()),
Err(e) => eprintln!("其他错误: {}", e),
Ok(v) => v,
}Channel Saturation
通道饱和
rust
// ❌ Not checking for dropped events
let sub = client.subscribe_session(&id).await?;
// Slow processing...
while let Some(event) = sub.recv().await {
tokio::time::sleep(Duration::from_secs(1)).await; // Too slow!
}
// ✅ Monitor stats
if sub.stats().dropped > 0 {
tracing::warn!("Dropped {} events", sub.stats().dropped);
}rust
// ❌ 未检查丢弃的事件
let sub = client.subscribe_session(&id).await?;
// 处理过慢...
while let Some(event) = sub.recv().await {
tokio::time::sleep(Duration::from_secs(1)).await; // 处理太慢!
}
// ✅ 监控统计信息
if sub.stats().dropped > 0 {
tracing::warn!("丢弃了{}个事件", sub.stats().dropped);
}Session Directory Not Applied
会话目录未生效
rust
// ❌ Treating directory as body field
let request = CreateSessionRequest {
directory: Some("/my/project".to_string()),
..Default::default()
};
// Session won't have directory context!
// ✅ Directory is a query parameter - use builder
let request = SessionCreateOptions::new()
.with_directory("/my/project")
.into();rust
// ❌ 把directory当作请求体字段
let request = CreateSessionRequest {
directory: Some("/my/project".to_string()),
..Default::default()
};
// 会话不会获取到目录上下文!
// ✅ directory是查询参数 - 使用构建器
let request = SessionCreateOptions::new()
.with_directory("/my/project")
.into();URL Encoding Missing
缺失URL编码
rust
// ❌ Path with special characters causes 404
let content = files.read("path/with spaces/file.txt").await;
// ✅ URL encode path parameters
let content = files.read(&urlencoding::encode("path/with spaces/file.txt")).await;rust
// ❌ 带特殊字符的路径会导致404
let content = files.read("path/with spaces/file.txt").await;
// ✅ 路径参数需要URL编码
let content = files.read(&urlencoding::encode("path/with spaces/file.txt")).await;Blocking on recv() Without Timeout
无超时阻塞recv()
rust
// ❌ Can hang indefinitely if stream closes
let event = subscription.recv().await;
// ✅ Use timeout
let event = tokio::time::timeout(Duration::from_secs(30), subscription.recv()).await?;rust
// ❌ 如果流关闭可能无限挂起
let event = subscription.recv().await;
// ✅ 使用超时
let event = tokio::time::timeout(Duration::from_secs(30), subscription.recv()).await?;Optional
可选内容
Additional Resources
额外资源
- API Documentation: Full rustdocs
- Repository: Source code
- OpenCode Documentation: Platform docs
- API文档: 完整rustdocs
- 代码仓库: 源代码
- OpenCode文档: 平台文档
Version History
版本历史
| Version | Notes |
|---|---|
| 0.1.x | Initial release with HTTP API, SSE streaming, managed server |
| 版本 | 说明 |
|---|---|
| 0.1.x | 初始版本,包含HTTP API、SSE流、托管服务能力 |
Feature Flag Matrix
功能开关矩阵
| Feature | Dependencies | APIs Enabled |
|---|---|---|
| reqwest, serde_json | All HTTP API modules |
| reqwest-eventsource, backon | SSE streaming, subscriptions |
| tokio/process, portpicker | ManagedServer |
| tokio/process | CliRunner |
| All above | Everything |
| 功能 | 依赖 | 启用的API |
|---|---|---|
| reqwest、serde_json | 所有HTTP API模块 |
| reqwest-eventsource、backon | SSE流、订阅能力 |
| tokio/process、portpicker | ManagedServer |
| tokio/process | CliRunner |
| 以上所有 | 全部能力 |
Default Dependencies
默认依赖
- (rt-multi-thread, macros, sync, time)
tokio - (derive)
serde thiserrorurlhttptokio-utilfuturesurlencoding- (v4, serde)
uuid - (serde)
chrono tracing
- (rt-multi-thread, macros, sync, time)
tokio - (derive)
serde thiserrorurlhttptokio-utilfuturesurlencoding- (v4, serde)
uuid - (serde)
chrono tracing
License
许可证
Apache-2.0
Generated for opencode-sdk v0.1.7
Apache-2.0
生成于opencode-sdk v0.1.7