opencode-rs-sdk

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

opencode-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操作规则

  1. Always check feature flags before using APIs -
    http
    and
    sse
    are enabled by default,
    server
    and
    cli
    require explicit enabling.
  2. Unix platforms only - Windows will fail at compile time with
    compile_error!
    .
  3. Subscribe before sending - For streaming workflows, create SSE subscription before sending async prompts to avoid missing early events.
  4. Use convenience methods - Prefer
    Client::run_simple_text()
    and
    wait_for_idle_text()
    for common workflows.
  5. Handle all error variants - Match on
    OpencodeError
    variants, using helper methods like
    is_not_found()
    and
    is_validation_error()
    .
  6. Drop subscriptions to cancel -
    SseSubscription
    and
    RawSseSubscription
    cancel on drop; explicitly call
    .close()
    for early termination.
  7. Server processes auto-kill -
    ManagedServer
    kills child process on drop; call
    .stop()
    for graceful shutdown.
  8. Directory header required - Most operations require
    x-opencode-directory
    header set via
    ClientBuilder::directory()
    or
    ManagedRuntimeBuilder::directory()
    .
  9. URL encode path parameters - All path parameters must be URL-encoded using
    urlencoding::encode()
    to handle special characters.
  1. 使用API前必须检查功能开关 -
    http
    sse
    默认开启,
    server
    cli
    需要显式启用。
  2. 仅支持Unix平台 - Windows下编译时会触发
    compile_error!
    报错。
  3. 发送请求前先订阅 - 针对流处理工作流,发送异步提示前请先创建SSE订阅,避免丢失早期事件。
  4. 优先使用便捷方法 - 通用工作流推荐使用
    Client::run_simple_text()
    wait_for_idle_text()
  5. 处理所有错误变体 - 匹配
    OpencodeError
    变体,可使用
    is_not_found()
    is_validation_error()
    等辅助方法。
  6. 销毁订阅即可取消 -
    SseSubscription
    RawSseSubscription
    在销毁时自动取消;如需提前终止可显式调用
    .close()
  7. 服务进程自动终止 -
    ManagedServer
    在销毁时会自动杀死子进程;如需优雅关闭可调用
    .stop()
  8. 必须指定目录请求头 - 大部分操作需要通过
    ClientBuilder::directory()
    ManagedRuntimeBuilder::directory()
    设置
    x-opencode-directory
    请求头。
  9. 路径参数需要URL编码 - 所有路径参数必须使用
    urlencoding::encode()
    进行URL编码以处理特殊字符。

Environment and Version Constraints

环境与版本约束

ConstraintValueImpact
PlatformUnix only (Linux/macOS)Windows compilation fails
Rust Edition2024Requires Rust 1.85+
Default Features
http
,
sse
Always available unless disabled
Optional Features
server
,
cli
Requires explicit
--features
Full Feature Set
full
= all features
Use for complete functionality
Default Server URL
http://127.0.0.1:4096
ClientBuilder default
Timeout Default300 secondsSuitable for long AI requests
约束影响
运行平台仅支持Unix(Linux/macOS)Windows下编译失败
Rust版本2024需要Rust 1.85+
默认功能
http
,
sse
除非手动禁用否则始终可用
可选功能
server
,
cli
需要显式指定
--features
启用
完整功能集
full
= 所有功能
用于获取完整能力
默认服务URL
http://127.0.0.1:4096
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 dropped
rust
let server = ManagedServer::start(ServerOptions::new().port(8080)).await?;
let client = Client::builder().base_url(server.url()).build()?;
// 当`server`被销毁时服务会自动停止

Getting Started

快速开始

Add to
Cargo.toml
:
toml
[dependencies]
opencode-sdk = "0.1"
Cargo.toml
中添加依赖:
toml
[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 modules
src/
├── 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
Client
struct (
src/client.rs
) is the primary ergonomic API.
Client
结构体(
src/client.rs
)是主要的易用API入口。

ClientBuilder

ClientBuilder

Chain configuration before building:
  • base_url(url)
    - Server URL (default:
    http://127.0.0.1:4096
    )
  • directory(dir)
    - Set
    x-opencode-directory
    header
  • timeout_secs(secs)
    - Request timeout (default: 300)
  • build()
    - Create the client (requires
    http
    feature)
构建客户端前可链式配置参数:
  • base_url(url)
    - 服务URL(默认:
    http://127.0.0.1:4096
  • directory(dir)
    - 设置
    x-opencode-directory
    请求头
  • timeout_secs(secs)
    - 请求超时时间(默认:300)
  • 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();             // MiscApi

SSE 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 router
rust
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
http
feature (enabled by default).
所有API模块需要
http
功能开启(默认已启用)。

SessionsApi (
src/http/sessions.rs
)

SessionsApi(
src/http/sessions.rs

MethodEndpointDescription
create(req)
POST /sessionCreate new session
create_with(opts)
POST /sessionCreate with convenience options
get(id)
GET /session/{id}Get session by ID
list()
GET /sessionList all sessions
delete(id)
DELETE /session/{id}Delete session
fork(id)
POST /session/{id}/forkFork session
abort(id)
POST /session/{id}/abortAbort active session
update(id, req)
PATCH /session/{id}Update session
init(id)
POST /session/{id}/initInitialize session
share(id)
POST /session/{id}/shareShare session
unshare(id)
DELETE /session/{id}/shareUnshare session
revert(id, req)
POST /session/{id}/revertRevert to message
unrevert(id)
POST /session/{id}/unrevertUndo revert
summarize(id, req)
POST /session/{id}/summarizeSummarize session
diff(id)
GET /session/{id}/diffGet session diff
diff_since_message(id, msg_id)
GET /session/{id}/diff?messageId={msg_id}Get diff since message
status()
GET /session/statusGet server status
children(id)
GET /session/{id}/childrenGet forked sessions
todo(id)
GET /session/{id}/todoGet todo items
Important: Session creation requires
directory
as a query parameter, not in the JSON body:
rust
let path = if let Some(directory) = &req.directory {
    format!("/session?directory={}", urlencoding::encode(directory))
} else {
    "/session".to_string()
};
方法接口路径描述
create(req)
POST /session创建新会话
create_with(opts)
POST /session使用便捷配置创建会话
get(id)
GET /session/{id}根据ID获取会话
list()
GET /session列出所有会话
delete(id)
DELETE /session/{id}删除会话
fork(id)
POST /session/{id}/fork复刻会话
abort(id)
POST /session/{id}/abort终止活跃会话
update(id, req)
PATCH /session/{id}更新会话
init(id)
POST /session/{id}/init初始化会话
share(id)
POST /session/{id}/share共享会话
unshare(id)
DELETE /session/{id}/share取消共享会话
revert(id, req)
POST /session/{id}/revert回退到指定消息
unrevert(id)
POST /session/{id}/unrevert撤销回退
summarize(id, req)
POST /session/{id}/summarize生成会话摘要
diff(id)
GET /session/{id}/diff获取会话差异
diff_since_message(id, msg_id)
GET /session/{id}/diff?messageId={msg_id}获取指定消息之后的差异
status()
GET /session/status获取服务状态
children(id)
GET /session/{id}/children获取复刻的子会话
todo(id)
GET /session/{id}/todo获取待办事项
重要提示: 会话创建时
directory
是查询参数,不是JSON请求体中的字段:
rust
let path = if let Some(directory) = &req.directory {
    format!("/session?directory={}", urlencoding::encode(directory))
} else {
    "/session".to_string()
};

MessagesApi (
src/http/messages.rs
)

MessagesApi(
src/http/messages.rs

MethodEndpointDescription
prompt(session_id, req)
POST /session/{id}/messageSend prompt
prompt_async(session_id, req)
POST /session/{id}/prompt_asyncAsync prompt (empty response)
send_text_async(session_id, text, model)
POST /session/{id}/prompt_asyncConvenience text sender
list(session_id)
GET /session/{id}/messageList messages
get(session_id, message_id)
GET /session/{id}/message/{mid}Get message
remove(session_id, message_id)
DELETE /session/{id}/message/{mid}Remove message
Important:
prompt()
and
prompt_async()
return empty body on success - use
request_empty()
not
request_json()
.
方法接口路径描述
prompt(session_id, req)
POST /session/{id}/message发送提示
prompt_async(session_id, req)
POST /session/{id}/prompt_async异步发送提示(空响应)
send_text_async(session_id, text, model)
POST /session/{id}/prompt_async便捷文本发送方法
list(session_id)
GET /session/{id}/message列出消息列表
get(session_id, message_id)
GET /session/{id}/message/{mid}获取指定消息
remove(session_id, message_id)
DELETE /session/{id}/message/{mid}删除消息
重要提示:
prompt()
prompt_async()
成功时返回空响应体,请使用
request_empty()
而非
request_json()

Other API Modules

其他API模块

  • PartsApi (
    src/http/parts.rs
    ) - Message part CRUD operations
  • PermissionsApi (
    src/http/permissions.rs
    ) - Permission management
  • QuestionsApi (
    src/http/questions.rs
    ) - Question operations
  • FilesApi (
    src/http/files.rs
    ) - File operations (requires URL encoding for paths)
  • FindApi (
    src/http/find.rs
    ) - Search operations
  • ProvidersApi (
    src/http/providers.rs
    ) - Provider management
  • McpApi (
    src/http/mcp.rs
    ) - MCP operations
  • PtyApi (
    src/http/pty.rs
    ) - PTY operations
  • ConfigApi (
    src/http/config.rs
    ) - Configuration
  • ToolsApi (
    src/http/tools.rs
    ) - Tool operations
  • ProjectApi (
    src/http/project.rs
    ) - Project operations
  • WorktreeApi (
    src/http/worktree.rs
    ) - Worktree operations
  • MiscApi (
    src/http/misc.rs
    ) - Miscellaneous endpoints
  • PartsApi (
    src/http/parts.rs
    ) - 消息部分CRUD操作
  • PermissionsApi (
    src/http/permissions.rs
    ) - 权限管理
  • QuestionsApi (
    src/http/questions.rs
    ) - 问题操作
  • FilesApi (
    src/http/files.rs
    ) - 文件操作(路径需要URL编码)
  • FindApi (
    src/http/find.rs
    ) - 搜索操作
  • ProvidersApi (
    src/http/providers.rs
    ) - 提供商管理
  • McpApi (
    src/http/mcp.rs
    ) - MCP操作
  • PtyApi (
    src/http/pty.rs
    ) - PTY操作
  • 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

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 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

rust
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:
ToolState
uses
#[serde(untagged)]
. More specific variants (
Completed
,
Error
) with more required fields must come before less specific ones (
Pending
,
Running
) to avoid incorrect deserialization.
rust
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)]
注解,必填字段更多的特定变体(
Completed
Error
)必须放在更通用的变体(
Pending
Running
)之前,避免反序列化错误。

Event Types (
src/types/event.rs
)

事件类型(
src/types/event.rs

40 SSE event variants organized by category:
Server/Instance (4):
ServerConnected
,
ServerHeartbeat
,
ServerInstanceDisposed
,
GlobalDisposed
Session (8):
SessionCreated
,
SessionUpdated
,
SessionDeleted
,
SessionDiff
,
SessionError
,
SessionCompacted
,
SessionStatus
,
SessionIdle
Messages (4):
MessageUpdated
,
MessageRemoved
,
MessagePartUpdated
,
MessagePartRemoved
PTY (4):
PtyCreated
,
PtyUpdated
,
PtyExited
,
PtyDeleted
Permissions (4):
PermissionUpdated
,
PermissionReplied
,
PermissionAsked
,
PermissionRepliedNext
Project/Files (4):
ProjectUpdated
,
FileEdited
,
FileWatcherUpdated
,
VcsBranchUpdated
LSP/Tools (4):
LspUpdated
,
LspClientDiagnostics
,
CommandExecuted
,
McpToolsChanged
Installation (3):
InstallationUpdated
,
InstallationUpdateAvailable
,
IdeInstalled
TUI (4):
TuiPromptAppend
,
TuiCommandExecute
,
TuiToastShow
,
TuiSessionSelect
Todo (1):
TodoUpdated
Event deserialization uses
#[serde(tag = "type")]
- the
"type"
field determines the variant. Session ID aliases are supported via
#[serde(alias = "sessionID")]
. Unknown events deserialize to
Event::Unknown
for forward compatibility.
40个SSE事件变体按类别分组:
服务/实例(4个):
ServerConnected
ServerHeartbeat
ServerInstanceDisposed
GlobalDisposed
会话(8个):
SessionCreated
SessionUpdated
SessionDeleted
SessionDiff
SessionError
SessionCompacted
SessionStatus
SessionIdle
消息(4个):
MessageUpdated
MessageRemoved
MessagePartUpdated
MessagePartRemoved
PTY(4个):
PtyCreated
PtyUpdated
PtyExited
PtyDeleted
权限(4个):
PermissionUpdated
PermissionReplied
PermissionAsked
PermissionRepliedNext
项目/文件(4个):
ProjectUpdated
FileEdited
FileWatcherUpdated
VcsBranchUpdated
LSP/工具(4个):
LspUpdated
LspClientDiagnostics
CommandExecuted
McpToolsChanged
安装(3个):
InstallationUpdated
InstallationUpdateAvailable
IdeInstalled
TUI(4个):
TuiPromptAppend
TuiCommandExecute
TuiToastShow
TuiSessionSelect
待办(1个):
TodoUpdated
事件反序列化使用
#[serde(tag = "type")]
注解,
"type"
字段决定使用哪个变体。会话ID支持
#[serde(alias = "sessionID")]
别名。未知事件会反序列化为
Event::Unknown
以保证前向兼容。

SSE Streaming

SSE流

The SSE module (
src/sse.rs
) provides robust event streaming with automatic reconnection.
SSE模块(
src/sse.rs
)提供稳定的事件流能力,支持自动重连。

Key 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 -
    subscribe_session()
    filters events after parsing; server still sends all events
  • Session ID extraction has fallbacks - for
    message.part.updated
    , extracts from
    properties.part.sessionID|sessionId
    ; for
    session.idle/error
    , from
    properties.sessionID|sessionId
  • Events without session ID are dropped when filtered (counts toward
    dropped
    stat)
  • 客户端侧会话过滤 -
    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
opencode serve
processes:
rust
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
/doc
probe.
启动并管理
opencode serve
进程:
rust
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"或通过
/doc
探针检测启动成功。

CliRunner (requires cli feature)

CliRunner(需要cli功能开启)

Wrap
opencode run --format json
:
rust
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 (
Stdio::inherit()
) to prevent buffer deadlock when CLI writes >64KB to stdout.
封装
opencode run --format json
命令:
rust
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会被继承(
Stdio::inherit()
),避免CLI向stdout写入超过64KB数据时出现缓冲区死锁。

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 server
Quick 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 (
http
,
sse
)
Import/Invoke:
rust
use opencode_sdk::{Client, ClientBuilder};
let client = Client::builder().build()?;
Minimal flow:
  1. Build client with
    Client::builder().base_url(url).directory(dir).build()
  2. Use API accessors like
    client.sessions().create(&req).await
  3. For streaming, create SSE subscription before sending async requests
  4. Handle errors using
    Result<T>
    and
    OpencodeError
    variants
Key APIs:
  • Client::builder()
    - Create builder
  • ClientBuilder::build()
    - Build client
  • client.sessions()
    ,
    client.messages()
    - API accessors
  • client.subscribe_session(id).await
    - Per-session SSE
  • client.run_simple_text(text).await
    - Quick prompt
Pitfalls:
  • Forgetting to subscribe before
    prompt_async
    - events will be lost
  • Not handling
    OpencodeError::Http
    - may miss structured error data
  • Missing
    http
    feature causes
    build()
    to return
    OpencodeError::InvalidConfig
Source:
src/client.rs

适用场景: 构建与OpenCode HTTP API交互的应用
启用/安装: 默认功能(
http
sse
导入/调用:
rust
use opencode_sdk::{Client, ClientBuilder};
let client = Client::builder().build()?;
最小流程:
  1. 使用
    Client::builder().base_url(url).directory(dir).build()
    构建客户端
  2. 使用API访问器如
    client.sessions().create(&req).await
    调用接口
  3. 对于流处理场景,发送异步请求前先创建SSE订阅
  4. 使用
    Result<T>
    OpencodeError
    变体处理错误
核心API:
  • Client::builder()
    - 创建构建器
  • ClientBuilder::build()
    - 构建客户端
  • client.sessions()
    client.messages()
    - API访问器
  • client.subscribe_session(id).await
    - 单会话SSE订阅
  • client.run_simple_text(text).await
    - 快速发送提示
常见陷阱:
  • prompt_async
    前忘记订阅 - 会丢失事件
  • 没有处理
    OpencodeError::Http
    - 可能错过结构化错误数据
  • 未开启
    http
    功能会导致
    build()
    返回
    OpencodeError::InvalidConfig
源码位置:
src/client.rs

SessionsApi Usage Card

SessionsApi使用指南

Use when: Managing sessions (CRUD, forking, sharing, reverting)
Enable/Install:
http
feature (default)
Import/Invoke:
rust
let sessions = client.sessions();
Minimal flow:
  1. Create:
    sessions.create(&CreateSessionRequest::default()).await
  2. Or with title:
    sessions.create_with(SessionCreateOptions::new().with_title("Task")).await
  3. List:
    sessions.list().await
  4. Delete:
    sessions.delete(&id).await
Key APIs:
  • create(req)
    ,
    create_with(opts)
    - Create sessions
  • get(id)
    ,
    list()
    - Retrieve sessions
  • delete(id)
    - Remove session
  • fork(id)
    - Fork session
  • share(id)
    ,
    unshare(id)
    - Sharing
  • revert(id, req)
    - Revert to message
Pitfalls:
  • Session IDs are strings - don't assume UUID format
  • create_with
    is more ergonomic than manual
    CreateSessionRequest
  • Critical:
    directory
    is a query parameter, NOT in the JSON body
Source:
src/http/sessions.rs

适用场景: 管理会话(CRUD、复刻、共享、回退)
启用/安装:
http
功能(默认已开启)
导入/调用:
rust
let sessions = client.sessions();
最小流程:
  1. 创建:
    sessions.create(&CreateSessionRequest::default()).await
  2. 带标题创建:
    sessions.create_with(SessionCreateOptions::new().with_title("Task")).await
  3. 列表:
    sessions.list().await
  4. 删除:
    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
    更易用
  • 关键:
    directory
    是查询参数,不是JSON请求体的字段
源码位置:
src/http/sessions.rs

MessagesApi Usage Card

MessagesApi使用指南

Use when: Sending prompts or managing messages
Enable/Install:
http
feature (default)
Import/Invoke:
rust
let messages = client.messages();
Minimal flow:
  1. Send prompt:
    messages.prompt(&session_id, &PromptRequest::text("Hello")).await
  2. Or async (no response body):
    messages.prompt_async(&session_id, &req).await
  3. List:
    messages.list(&session_id).await
  4. Get:
    messages.get(&session_id, &message_id).await
Key APIs:
  • prompt(session_id, req)
    - Send prompt (sync-like)
  • prompt_async(session_id, req)
    - Async send (use with SSE)
  • send_text_async(session_id, text, model)
    - Convenience method
  • list(session_id)
    ,
    get(session_id, message_id)
    - Retrieve messages
  • remove(session_id, message_id)
    - Delete message
Pitfalls:
  • prompt_async
    returns empty body - must use SSE for response
  • PromptRequest::text("...").with_model("provider", "model")
    for model selection
Source:
src/http/messages.rs

适用场景: 发送提示或管理消息
启用/安装:
http
功能(默认已开启)
导入/调用:
rust
let messages = client.messages();
最小流程:
  1. 发送提示:
    messages.prompt(&session_id, &PromptRequest::text("Hello")).await
  2. 异步发送(无响应体):
    messages.prompt_async(&session_id, &req).await
  3. 列表:
    messages.list(&session_id).await
  4. 查询:
    messages.get(&session_id, &message_id).await
核心API:
  • prompt(session_id, req)
    - 发送提示(类同步)
  • prompt_async(session_id, req)
    - 异步发送(配合SSE使用)
  • send_text_async(session_id, text, model)
    - 便捷方法
  • list(session_id)
    get(session_id, message_id)
    - 查询消息
  • remove(session_id, message_id)
    - 删除消息
常见陷阱:
  • prompt_async
    返回空响应体 - 必须通过SSE获取响应
  • 模型选择使用
    PromptRequest::text("...").with_model("provider", "model")
源码位置:
src/http/messages.rs

SseSubscriber Usage Card

SseSubscriber使用指南

Use when: Consuming real-time OpenCode events (session updates, message streaming, permission requests)
Enable/Install:
sse
feature (default)
Import/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:
  1. Create subscriber:
    SseSubscriber::new(base_url, directory, client)
  2. Subscribe:
    subscriber.subscribe(SseOptions::default()).await?
  3. Receive:
    while let Some(event) = sub.recv().await { /* process */ }
  4. Drop or
    sub.close()
    to cancel
Key APIs:
  • subscribe(opts)
    - Subscribe to typed events
  • subscribe_session(session_id, opts)
    - Filter by session
  • subscribe_global(opts)
    - Subscribe to global events
  • subscribe_raw(opts)
    - Raw JSON frames for debugging
Pitfalls:
  • Drop cancels stream - both
    SseSubscription
    and
    RawSseSubscription
    cancel on drop
  • recv()
    can return
    None
    (stream closed) - handle this case
  • Monitor
    stats().dropped
    to detect backpressure or filtering issues
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 {
    // 处理事件
}
最小流程:
  1. 创建订阅者:
    SseSubscriber::new(base_url, directory, client)
  2. 订阅:
    subscriber.subscribe(SseOptions::default()).await?
  3. 接收:
    while let Some(event) = sub.recv().await { /* 处理 */ }
  4. 销毁或调用
    sub.close()
    取消订阅
核心API:
  • subscribe(opts)
    - 订阅类型化事件
  • subscribe_session(session_id, opts)
    - 按会话过滤
  • subscribe_global(opts)
    - 订阅全局事件
  • subscribe_raw(opts)
    - 原始JSON帧(用于调试)
常见陷阱:
  • 销毁会取消流 -
    SseSubscription
    RawSseSubscription
    在销毁时都会取消订阅
  • recv()
    可能返回
    None
    (流已关闭) - 请处理该情况
  • 监控
    stats().dropped
    检测背压或过滤问题
源码位置:
src/sse.rs

Event Enum Usage Card

Event枚举使用指南

Use when: Handling specific OpenCode event types (40 variants)
Enable/Install: Part of
opencode_sdk::types::event
module
Import/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:
  • event.session_id()
    - Extract session ID if present (returns
    Option<&str>
    )
  • event.is_heartbeat()
    - Check for keep-alive
  • event.is_connected()
    - Check for connection event
Pitfalls:
  • Only 10 of 40 event variants contain session_id - use
    session_id()
    method which handles this correctly
  • Unknown types deserialize to
    Event::Unknown
    - always handle this case
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:
  • event.session_id()
    - 提取会话ID(存在则返回
    Option<&str>
  • event.is_heartbeat()
    - 检查是否是心跳事件
  • event.is_connected()
    - 检查是否是连接事件
常见陷阱:
  • 40个事件变体中只有10个包含session_id - 请使用
    session_id()
    方法正确处理
  • 未知类型会反序列化为
    Event::Unknown
    - 请始终处理该情况
源码位置:
src/types/event.rs

ManagedServer Usage Card

ManagedServer使用指南

Use when: Programmatically starting OpenCode server for tests or automation
Enable/Install:
server
feature (NOT default)
Import/Invoke:
rust
use opencode_sdk::server::{ManagedServer, ServerOptions};
let server = ManagedServer::start(ServerOptions::new().port(8080)).await?;
Minimal flow:
  1. Configure:
    ServerOptions::new().port(8080).directory("/project")
  2. Start:
    let server = ManagedServer::start(opts).await?
  3. Get URL:
    let url = server.url()
  4. Create client:
    let client = Client::builder().base_url(url).build()?
  5. Stop (or drop):
    server.stop().await?
Key APIs:
  • ServerOptions::new()
    - Create options
  • ServerOptions::port()
    ,
    ::hostname()
    ,
    ::directory()
    ,
    ::config_json()
    - Configure
  • ManagedServer::start(opts).await
    - Spawn server
  • ManagedServer::url()
    ,
    ::port()
    - Get connection info
  • ManagedServer::stop().await
    - Graceful shutdown
Pitfalls:
  • Server kills on drop - hold
    ManagedServer
    reference while using
  • config_json
    sets env var
    OPENCODE_CONFIG_CONTENT
    - server must support this
  • 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?;
最小流程:
  1. 配置:
    ServerOptions::new().port(8080).directory("/project")
  2. 启动:
    let server = ManagedServer::start(opts).await?
  3. 获取URL:
    let url = server.url()
  4. 创建客户端:
    let client = Client::builder().base_url(url).build()?
  5. 停止(或销毁):
    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.rs

CliRunner Usage Card

CliRunner使用指南

Use when: Falling back to CLI when HTTP API unavailable
Enable/Install:
cli
feature (NOT default)
Import/Invoke:
rust
use opencode_sdk::cli::{CliRunner, RunOptions};
let mut runner = CliRunner::start("Hello", RunOptions::new()).await?;
Minimal flow:
  1. Create options:
    RunOptions::new().model("provider/model").agent("code")
  2. Start:
    let mut runner = CliRunner::start("prompt", opts).await?
  3. Stream:
    while let Some(event) = runner.recv().await { /* process */ }
  4. Or collect:
    let text = runner.collect_text().await
Key APIs:
  • RunOptions::new()
    - Create options (format defaults to "json")
  • RunOptions::model()
    ,
    ::agent()
    ,
    ::title()
    ,
    ::attach()
    - Configure
  • CliRunner::start(prompt, opts).await
    - Start CLI
  • CliRunner::recv().await
    - Get next event
  • CliRunner::collect_text().await
    - Aggregate text events
  • CliEvent::is_text()
    ,
    ::text()
    - Event inspection
Pitfalls:
  • CLI outputs NDJSON to stdout -
    format
    must be "json"
  • stderr inherited - CLI errors visible but not captured
  • Session sharing requires
    share: true
    in options
Source:
src/cli.rs

适用场景: HTTP API不可用时降级使用CLI
启用/安装:
cli
功能(默认未开启)
导入/调用:
rust
use opencode_sdk::cli::{CliRunner, RunOptions};
let mut runner = CliRunner::start("Hello", RunOptions::new()).await?;
最小流程:
  1. 创建配置:
    RunOptions::new().model("provider/model").agent("code")
  2. 启动:
    let mut runner = CliRunner::start("prompt", opts).await?
  3. 流处理:
    while let Some(event) = runner.recv().await { /* 处理 */ }
  4. 或收集结果:
    let text = runner.collect_text().await
核心API:
  • RunOptions::new()
    - 创建配置(格式默认是"json")
  • RunOptions::model()
    ::agent()
    ::title()
    ::attach()
    - 配置参数
  • CliRunner::start(prompt, opts).await
    - 启动CLI
  • CliRunner::recv().await
    - 获取下一个事件
  • CliRunner::collect_text().await
    - 聚合文本事件
  • CliEvent::is_text()
    ::text()
    - 事件检查
常见陷阱:
  • CLI向stdout输出NDJSON -
    format
    必须设置为"json"
  • stderr被继承 - CLI错误可见但不会被捕获
  • 会话共享需要在配置中设置
    share: true
源码位置:
src/cli.rs

OpencodeError Usage Card

OpencodeError使用指南

Use when: Handling errors from SDK operations
Enable/Install: Part of
opencode_sdk
crate (re-exported from
lib.rs
)
Import/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:
  • OpencodeError::http(status, body)
    - Parse HTTP error with NamedError body
  • is_not_found()
    - Check for 404 errors
  • is_validation_error()
    - Check for 400 validation errors
  • is_server_error()
    - Check for 5xx errors
  • error_name()
    - Get NamedError name (e.g., "ValidationError")
Pitfalls:
  • HTTP errors may contain structured
    data
    field with additional context
  • Plain text HTTP responses fall back to generic "HTTP {status}" message
  • SSE
    StreamClosed
    error requires re-subscription to recover
Source:
src/error.rs

适用场景: 处理SDK操作返回的错误
启用/安装: 属于
opencode_sdk
crate(从
lib.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:
  • OpencodeError::http(status, body)
    - 解析带NamedError body的HTTP错误
  • is_not_found()
    - 检查是否是404错误
  • is_validation_error()
    - 检查是否是400校验错误
  • is_server_error()
    - 检查是否是5xx错误
  • error_name()
    - 获取NamedError名称(如"ValidationError")
常见陷阱:
  • HTTP错误可能包含结构化
    data
    字段提供额外上下文
  • 纯文本HTTP响应会降级为通用"HTTP {status}"消息
  • SSE
    StreamClosed
    错误需要重新订阅才能恢复
源码位置:
src/error.rs

API Reference

API参考

Core Types

核心类型

TypeLocationDescription
Client
src/client.rs:12
Main ergonomic API client
ClientBuilder
src/client.rs:24
Builder for Client
OpencodeError
src/error.rs:7
Error enum (13 variants)
Result<T>
src/error.rs:6
Type alias for Result
类型位置描述
Client
src/client.rs:12
主易用API客户端
ClientBuilder
src/client.rs:24
Client构建器
OpencodeError
src/error.rs:7
错误枚举(13个变体)
Result<T>
src/error.rs:6
Result类型别名

SSE Types

SSE类型

TypeLocationDescription
SseSubscriber
src/sse.rs:331
Creates SSE subscriptions
SseSubscription
src/sse.rs:134
Typed event subscription
RawSseSubscription
src/sse.rs:155
Raw JSON subscription
SessionEventRouter
src/sse.rs:195
Multiplexes to sessions
SseOptions
src/sse.rs:62
Subscription options
SessionEventRouterOptions
src/sse.rs:163
Router options
SseStreamStats
src/sse.rs:83
Diagnostics snapshot
RawSseEvent
src/sse.rs:143
Raw SSE frame
类型位置描述
SseSubscriber
src/sse.rs:331
创建SSE订阅
SseSubscription
src/sse.rs:134
类型化事件订阅
RawSseSubscription
src/sse.rs:155
原始JSON订阅
SessionEventRouter
src/sse.rs:195
多路复用到会话
SseOptions
src/sse.rs:62
订阅配置
SessionEventRouterOptions
src/sse.rs:163
路由配置
SseStreamStats
src/sse.rs:83
诊断快照
RawSseEvent
src/sse.rs:143
原始SSE帧

Server/CLI Types

服务/CLI类型

TypeLocationDescription
ManagedServer
src/server.rs:88
Managed server process
ServerOptions
src/server.rs:14
Server configuration
ManagedRuntime
src/runtime.rs
Server + Client combo
CliRunner
src/cli.rs:186
CLI wrapper
RunOptions
src/cli.rs:13
CLI options
CliEvent
src/cli.rs:133
CLI output event
类型位置描述
ManagedServer
src/server.rs:88
托管服务进程
ServerOptions
src/server.rs:14
服务配置
ManagedRuntime
src/runtime.rs
服务+客户端组合
CliRunner
src/cli.rs:186
CLI封装
RunOptions
src/cli.rs:13
CLI配置
CliEvent
src/cli.rs:133
CLI输出事件

HTTP Types

HTTP类型

TypeLocationDescription
HttpClient
src/http/mod.rs:43
Low-level HTTP client
HttpConfig
src/http/mod.rs:32
HTTP configuration
SessionsApi
src/http/sessions.rs:15
Sessions API client
MessagesApi
src/http/messages.rs:14
Messages API client
类型位置描述
HttpClient
src/http/mod.rs:43
底层HTTP客户端
HttpConfig
src/http/mod.rs:32
HTTP配置
SessionsApi
src/http/sessions.rs:15
会话API客户端
MessagesApi
src/http/messages.rs:14
消息API客户端

Model Types

模型类型

TypeLocationDescription
Session
src/types/session.rs:9
Session model
SessionCreateOptions
src/types/session.rs:153
Builder for create
Message
src/types/message.rs:45
Message with parts
MessageInfo
src/types/message.rs:11
Message metadata
Part
src/types/message.rs:75
Content part enum (12 variants)
PromptRequest
src/types/message.rs:513
Prompt request
PromptPart
src/types/message.rs:584
Prompt part enum
Event
src/types/event.rs:22
SSE event enum (40 variants)
GlobalEventEnvelope
src/types/event.rs:12
Global event wrapper
ToolState
src/types/message.rs:423
Tool execution state
类型位置描述
Session
src/types/session.rs:9
会话模型
SessionCreateOptions
src/types/session.rs:153
创建构建器
Message
src/types/message.rs:45
带部分的消息
MessageInfo
src/types/message.rs:11
消息元数据
Part
src/types/message.rs:75
内容部分枚举(12个变体)
PromptRequest
src/types/message.rs:513
提示请求
PromptPart
src/types/message.rs:584
提示部分枚举
Event
src/types/event.rs:22
SSE事件枚举(40个变体)
GlobalEventEnvelope
src/types/event.rs:12
全局事件包装
ToolState
src/types/message.rs:423
工具执行状态

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 captured
rust
// ❌ 响应事件丢失
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/Linux
rust
// ❌ 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 drop
rust
// ❌ 服务被提前销毁
{
    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

额外资源

Version History

版本历史

VersionNotes
0.1.xInitial release with HTTP API, SSE streaming, managed server
版本说明
0.1.x初始版本,包含HTTP API、SSE流、托管服务能力

Feature Flag Matrix

功能开关矩阵

FeatureDependenciesAPIs Enabled
http
reqwest, serde_jsonAll HTTP API modules
sse
reqwest-eventsource, backonSSE streaming, subscriptions
server
tokio/process, portpickerManagedServer
cli
tokio/processCliRunner
full
All aboveEverything
功能依赖启用的API
http
reqwest、serde_json所有HTTP API模块
sse
reqwest-eventsource、backonSSE流、订阅能力
server
tokio/process、portpickerManagedServer
cli
tokio/processCliRunner
full
以上所有全部能力

Default Dependencies

默认依赖

  • tokio
    (rt-multi-thread, macros, sync, time)
  • serde
    (derive)
  • thiserror
  • url
  • http
  • tokio-util
  • futures
  • urlencoding
  • uuid
    (v4, serde)
  • chrono
    (serde)
  • tracing
  • tokio
    (rt-multi-thread, macros, sync, time)
  • serde
    (derive)
  • thiserror
  • url
  • http
  • tokio-util
  • futures
  • urlencoding
  • uuid
    (v4, serde)
  • chrono
    (serde)
  • tracing

License

许可证

Apache-2.0

Generated for opencode-sdk v0.1.7
Apache-2.0

生成于opencode-sdk v0.1.7