From 05fc574f8b341815bf421a79c2becc44231e8a1c Mon Sep 17 00:00:00 2001 From: Haitao Pan Date: Sat, 6 Jun 2026 12:00:13 +0800 Subject: [PATCH] chore: retire rust ffi scaffold --- docs/architecture/public-api/README.md | 2 +- docs/architecture/public-api/ffi-and-rust.md | 8 +- docs/codex-integration/tasks.md | 11 +- .../xworkmate-unfinished-features-roadmap.md | 5 +- .../codex-integration-status-actual.md | 5 +- rust/Cargo.toml | 30 -- rust/src/error.rs | 59 ---- rust/src/lib.rs | 139 --------- rust/src/runtime.rs | 291 ------------------ rust/src/types.rs | 68 ---- 10 files changed, 15 insertions(+), 603 deletions(-) delete mode 100644 rust/Cargo.toml delete mode 100644 rust/src/error.rs delete mode 100644 rust/src/lib.rs delete mode 100644 rust/src/runtime.rs delete mode 100644 rust/src/types.rs diff --git a/docs/architecture/public-api/README.md b/docs/architecture/public-api/README.md index 70ba0e94..ddbeb06f 100644 --- a/docs/architecture/public-api/README.md +++ b/docs/architecture/public-api/README.md @@ -126,7 +126,7 @@ rg -n '`_' docs/architecture/public-api/*.md - `lib/runtime/go_task_service_client.dart` - `lib/runtime/codex_runtime.dart` - `lib/app/app_controller_desktop_settings_runtime.dart` -- `rust/src/lib.rs` +- `rust/src/lib.rs`(历史记录) ## Maintenance Rule diff --git a/docs/architecture/public-api/ffi-and-rust.md b/docs/architecture/public-api/ffi-and-rust.md index f65ffd4c..f17696ad 100644 --- a/docs/architecture/public-api/ffi-and-rust.md +++ b/docs/architecture/public-api/ffi-and-rust.md @@ -2,7 +2,9 @@ ## Purpose -`rust/src` 当前是一组相对独立、边界清晰的 Codex FFI 草图实现。这里的重点是: +`rust/` 目录已退役,下面这份文档仅作为历史记录保留。它描述的是曾经存在的 Codex FFI 草图实现,不代表当前可用能力。 + +历史上这里的重点是: - Rust 公开结构体与状态模型 - C ABI 函数签名 @@ -170,7 +172,7 @@ ## Exported FFI Functions -下面这些函数定义在 `rust/src/lib.rs`,是 Flutter / Dart 侧真正可调用的 C ABI 面。 +下面这些函数曾定义在 `rust/src/lib.rs`,是 Flutter / Dart 侧的 C ABI 面。 ## `codex_init` @@ -280,4 +282,4 @@ 3. `rust/src/lib.rs` 4. `rust/src/runtime.rs` 5. `rust/src/types.rs` -- 当前 FFI 面已经具备“结构体/函数签名骨架”,但消息收发、event polling、thread lifecycle 仍未完整实现 +- 当前 FFI 面仅停留在结构体/函数签名骨架,消息收发、event polling、thread lifecycle 都未完成 diff --git a/docs/codex-integration/tasks.md b/docs/codex-integration/tasks.md index 346e4adb..89afd0f2 100644 --- a/docs/codex-integration/tasks.md +++ b/docs/codex-integration/tasks.md @@ -9,13 +9,13 @@ XWorkmate 当前唯一可交付的 Codex 集成路径是 **external CLI**: - 通过 `CodeAgentNodeOrchestrator` 把 XWorkmate 固定为 `app-mediated cooperative node` - 通过 `RuntimeCoordinator` 保留多外部 Code Agent CLI 的统一 registry surface -Rust FFI / built-in Codex 仍是 future placeholder,不应宣传为已完成。 +Rust FFI / built-in Codex 已退役,不应再作为当前实现宣传。 ## 能力补全清单(按需求项) 1. 内置 Code Agent(built-in): - 已提供运行时模式接入位与桥接流程编排(AI Gateway / OpenClaw 协同元数据) - - 当前仍属于 experimental,受 Rust FFI TODO 约束 + - 当前仍属于 experimental,且 Rust FFI 路径已退役 2. 外部依赖 Codex CLI: - 已作为稳定主路径接入 - 保持与内置模式相同的桥接能力和模式切换语义 @@ -72,16 +72,15 @@ Rust FFI / built-in Codex 仍是 future placeholder,不应宣传为已完成 - 不做第二个 provider 之前的通用 UI - 不做复杂调度策略 -### Phase 3: 内置 Codex / Rust FFI +### Phase 3: 内置 Codex / Rust FFI(历史) 目标: -- 仅在 Rust FFI 具备真实可用能力后,再开放 built-in 交付承诺 +- 这一路径仅作历史记录保留,不再推进为当前交付面 前置条件: -- `rust/src/lib.rs` 的消息发送 / 轮询 TODO 完成 -- `rust/src/runtime.rs` 的进程启动 / 停止 TODO 完成 +- 历史文档中的 Rust FFI TODO 不再作为当前实现目标 - 能复用与 external CLI 相同的 coordinator / registry 契约 ## truth 收口 diff --git a/docs/plans/xworkmate-unfinished-features-roadmap.md b/docs/plans/xworkmate-unfinished-features-roadmap.md index 85a9cf09..b495a0ee 100644 --- a/docs/plans/xworkmate-unfinished-features-roadmap.md +++ b/docs/plans/xworkmate-unfinished-features-roadmap.md @@ -45,7 +45,7 @@ - 多 provider 调度策略 - 第二个 provider 之前的泛化产品设计 -## Phase 3: Built-in Codex / Rust FFI +## Phase 3: Built-in Codex / Rust FFI(历史) 目标: @@ -53,8 +53,7 @@ 前置条件: -- `rust/src/lib.rs` 补完消息发送与事件轮询 -- `rust/src/runtime.rs` 补完进程启动与停止 +- 这一路径仅保留历史记录,不再作为当前实施目标 - 能复用当前 coordinator / registry 契约 状态: diff --git a/docs/reports/codex-integration-status-actual.md b/docs/reports/codex-integration-status-actual.md index 3fa76cd9..9bd55baf 100644 --- a/docs/reports/codex-integration-status-actual.md +++ b/docs/reports/codex-integration-status-actual.md @@ -66,8 +66,7 @@ XWorkmate 当前唯一可交付的 Codex 集成路径是 **External Codex CLI** 现状: -- `rust/src/lib.rs` 仍保留消息发送和事件轮询 TODO -- `rust/src/runtime.rs` 仍保留进程启动和停止 TODO +- `rust/` 目录已退役,曾经的消息发送、事件轮询、进程启动和停止 TODO 仅保留在历史文档里 - Flutter 侧的 `builtIn` 只是保留枚举位,不会实际走可用 FFI 路径 ### 2. 其他外部 Provider 的通用选择与调度 @@ -117,6 +116,6 @@ flowchart LR 不能宣称的能力: - Built-in Codex 已可用 -- Rust FFI 已完成 +- Rust FFI 已退役,不作为当前交付能力 - Scheduled Tasks 已支持增删改 - Memory 已支持完整 CRUD diff --git a/rust/Cargo.toml b/rust/Cargo.toml deleted file mode 100644 index cc943a8f..00000000 --- a/rust/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -[package] -name = "codex-ffi" -version = "0.1.0" -edition = "2021" -description = "FFI bindings for Codex CLI integration" -license = "Apache-2.0" - -[lib] -name = "codex_ffi" -crate-type = ["cdylib", "staticlib"] - -[dependencies] -# Minimal dependencies for FFI -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -thiserror = "1.0" - -[features] -default = [] -flutter = [] - -[profile.release] -opt-level = 3 -lto = true -codegen-units = 1 -strip = true - -[profile.dev] -opt-level = 0 -debug = true diff --git a/rust/src/error.rs b/rust/src/error.rs deleted file mode 100644 index 4d27db3b..00000000 --- a/rust/src/error.rs +++ /dev/null @@ -1,59 +0,0 @@ -//! Error types for Codex FFI. - -use std::fmt; - -/// Error type for Codex operations. -#[derive(Debug, Clone)] -pub enum CodexError { - /// Invalid argument. - InvalidArgument(String), - /// Runtime not initialized. - NotInitialized, - /// Runtime already initialized. - AlreadyInitialized, - /// IO error. - Io(String), - /// JSON-RPC error. - Rpc { code: i32, message: String }, - /// Timeout error. - Timeout(String), - /// Process error. - Process(String), - /// Thread error. - Thread(String), - /// Configuration error. - Config(String), - /// Unknown error. - Unknown(String), -} - -impl fmt::Display for CodexError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - CodexError::InvalidArgument(msg) => write!(f, "Invalid argument: {}", msg), - CodexError::NotInitialized => write!(f, "Runtime not initialized"), - CodexError::AlreadyInitialized => write!(f, "Runtime already initialized"), - CodexError::Io(msg) => write!(f, "IO error: {}", msg), - CodexError::Rpc { code, message } => write!(f, "RPC error ({}): {}", code, message), - CodexError::Timeout(msg) => write!(f, "Timeout: {}", msg), - CodexError::Process(msg) => write!(f, "Process error: {}", msg), - CodexError::Thread(msg) => write!(f, "Thread error: {}", msg), - CodexError::Config(msg) => write!(f, "Configuration error: {}", msg), - CodexError::Unknown(msg) => write!(f, "Unknown error: {}", msg), - } - } -} - -impl std::error::Error for CodexError {} - -impl From for CodexError { - fn from(err: std::io::Error) -> Self { - CodexError::Io(err.to_string()) - } -} - -impl From for CodexError { - fn from(err: serde_json::Error) -> Self { - CodexError::Config(err.to_string()) - } -} diff --git a/rust/src/lib.rs b/rust/src/lib.rs deleted file mode 100644 index ebe438db..00000000 --- a/rust/src/lib.rs +++ /dev/null @@ -1,139 +0,0 @@ -//! FFI bindings for Codex CLI integration. -//! -//! This crate provides C-compatible FFI bindings for embedding Codex CLI -//! into Flutter applications. - -mod runtime; -mod error; -mod types; - -pub use error::CodexError; -pub use runtime::{CodexRuntime, CodexConfig, CodexConfigRust, ThreadHandle, RuntimeState}; -pub use types::{CodexResult, CodexEvent}; - -use std::ffi::CStr; -use std::os::raw::c_char; - -/// FFI-exported initialization function. -/// -/// # Safety -/// Must be called before any other FFI functions. -#[no_mangle] -pub unsafe extern "C" fn codex_init() -> i32 { - 0 // Success -} - -/// FFI-exported runtime creation. -/// -/// # Safety -/// Returns a pointer to the runtime. Caller must ensure thread safety. -#[no_mangle] -pub unsafe extern "C" fn codex_runtime_create(config: *const CodexConfig) -> *mut CodexRuntime { - if config.is_null() { - return std::ptr::null_mut(); - } - - let config = &*config; - let runtime = Box::new(CodexRuntime::new(config.clone())); - Box::into_raw(runtime) -} - -/// FFI-exported runtime destruction. -/// -/// # Safety -/// Must be called with a valid pointer from `codex_runtime_create`. -#[no_mangle] -pub unsafe extern "C" fn codex_runtime_destroy(runtime: *mut CodexRuntime) { - if !runtime.is_null() { - drop(Box::from_raw(runtime)); - } -} - -/// FFI-exported start thread function. -/// -/// # Safety -/// Must be called with valid pointers. -#[no_mangle] -pub unsafe extern "C" fn codex_start_thread( - _runtime: *mut CodexRuntime, - cwd: *const c_char, -) -> ThreadHandle { - if cwd.is_null() { - return ThreadHandle::null(); - } - - let _cwd = CStr::from_ptr(cwd); - - ThreadHandle::new(0) -} - -/// FFI-exported send message function. -/// -/// # Safety -/// Must be called with valid pointers. -#[no_mangle] -pub unsafe extern "C" fn codex_send_message( - runtime: *mut CodexRuntime, - _thread: ThreadHandle, - message: *const c_char, -) -> i32 { - if runtime.is_null() || message.is_null() { - return -1; - } - - let _runtime = &mut *runtime; - let _message = CStr::from_ptr(message); - - // TODO: Implement async message sending - 0 -} - -/// FFI-exported poll events function. -/// -/// # Safety -/// Must be called with valid pointers. -#[no_mangle] -pub unsafe extern "C" fn codex_poll_events( - runtime: *mut CodexRuntime, - events: *mut CodexEvent, - max_events: usize, -) -> usize { - if runtime.is_null() || events.is_null() { - return 0; - } - - let _runtime = &mut *runtime; - let _events = std::slice::from_raw_parts_mut(events, max_events); - - // TODO: Implement event polling - 0 -} - -/// FFI-exported shutdown function. -/// -/// # Safety -/// Must be called with a valid runtime pointer. -#[no_mangle] -pub unsafe extern "C" fn codex_shutdown(runtime: *mut CodexRuntime) -> i32 { - if runtime.is_null() { - return -1; - } - - let _runtime = &mut *runtime; - // TODO: Implement graceful shutdown - 0 -} - -/// Get the last error message. -/// -/// # Safety -/// Returns a pointer to static memory that is valid until the next FFI call. -#[no_mangle] -pub unsafe extern "C" fn codex_last_error(runtime: *mut CodexRuntime) -> *const c_char { - if runtime.is_null() { - return std::ptr::null(); - } - - let runtime = &mut *runtime; - runtime.last_error.as_ptr() -} diff --git a/rust/src/runtime.rs b/rust/src/runtime.rs deleted file mode 100644 index 60cdca75..00000000 --- a/rust/src/runtime.rs +++ /dev/null @@ -1,291 +0,0 @@ -//! Core runtime for Codex FFI. - -use std::ffi::CString; -use std::os::raw::c_char; -use std::path::PathBuf; - -use crate::error::CodexError; - -/// Configuration for Codex runtime. -#[derive(Debug, Clone)] -#[repr(C)] -pub struct CodexConfig { - /// Path to Codex binary. - pub codex_path: *const c_char, - /// Working directory. - pub working_directory: *const c_char, - /// Sandbox mode: 0=read-only, 1=workspace-write, 2=danger-full-access. - pub sandbox_mode: i32, - /// Approval policy: 0=suggest, 1=auto-edit, 2=full-auto. - pub approval_policy: i32, - /// Model to use. - pub model: *const c_char, - /// API key for gateway. - pub api_key: *const c_char, - /// Gateway URL. - pub gateway_url: *const c_char, - /// Enable debug logging. - pub debug: bool, -} - -impl Default for CodexConfig { - fn default() -> Self { - CodexConfig { - codex_path: std::ptr::null(), - working_directory: std::ptr::null(), - sandbox_mode: 1, // workspace-write - approval_policy: 0, // suggest - model: std::ptr::null(), - api_key: std::ptr::null(), - gateway_url: std::ptr::null(), - debug: false, - } - } -} - -impl CodexConfig { - /// Convert FFI config to Rust types. - pub unsafe fn to_rust(&self) -> Result { - let codex_path = if self.codex_path.is_null() { - None - } else { - Some(std::ffi::CStr::from_ptr(self.codex_path) - .to_string_lossy() - .into_owned()) - }; - - let working_directory = if self.working_directory.is_null() { - None - } else { - Some(std::ffi::CStr::from_ptr(self.working_directory) - .to_string_lossy() - .into_owned()) - }; - - let model = if self.model.is_null() { - None - } else { - Some(std::ffi::CStr::from_ptr(self.model) - .to_string_lossy() - .into_owned()) - }; - - let api_key = if self.api_key.is_null() { - None - } else { - Some(std::ffi::CStr::from_ptr(self.api_key) - .to_string_lossy() - .into_owned()) - }; - - let gateway_url = if self.gateway_url.is_null() { - None - } else { - Some(std::ffi::CStr::from_ptr(self.gateway_url) - .to_string_lossy() - .into_owned()) - }; - - Ok(CodexConfigRust { - codex_path, - working_directory, - sandbox_mode: self.sandbox_mode, - approval_policy: self.approval_policy, - model, - api_key, - gateway_url, - debug: self.debug, - }) - } -} - -/// Rust-native config type. -#[derive(Debug, Clone, Default)] -pub struct CodexConfigRust { - pub codex_path: Option, - pub working_directory: Option, - pub sandbox_mode: i32, - pub approval_policy: i32, - pub model: Option, - pub api_key: Option, - pub gateway_url: Option, - pub debug: bool, -} - -/// Opaque handle to a thread. -#[repr(C)] -#[derive(Debug, Clone, Copy)] -pub struct ThreadHandle { - pub id: u64, -} - -impl ThreadHandle { - pub fn new(id: u64) -> Self { - ThreadHandle { id } - } - - pub fn null() -> Self { - ThreadHandle { id: 0 } - } - - pub fn is_null(&self) -> bool { - self.id == 0 - } -} - -/// Codex runtime state. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum RuntimeState { - Disconnected, - Connecting, - Connected, - Ready, - Error, -} - -/// Core runtime for managing Codex process. -pub struct CodexRuntime { - config: CodexConfigRust, - state: RuntimeState, - pub last_error: CString, -} - -impl CodexRuntime { - /// Create a new runtime with the given configuration. - pub fn new(config: CodexConfig) -> Self { - let rust_config = unsafe { config.to_rust().unwrap_or_default() }; - CodexRuntime { - config: rust_config, - state: RuntimeState::Disconnected, - last_error: CString::new("").unwrap_or_default(), - } - } - - /// Create from Rust config. - pub fn with_config(config: CodexConfigRust) -> Self { - CodexRuntime { - config, - state: RuntimeState::Disconnected, - last_error: CString::new("").unwrap_or_default(), - } - } - - /// Get the current state. - pub fn state(&self) -> RuntimeState { - self.state - } - - /// Set error message. - pub fn set_error(&mut self, message: &str) { - self.last_error = CString::new(message).unwrap_or_default(); - self.state = RuntimeState::Error; - } - - /// Find the Codex binary. - pub fn find_codex_binary(&self) -> Option { - // Check config path - if let Some(ref path) = self.config.codex_path { - let path = PathBuf::from(path); - if path.exists() { - return Some(path); - } - } - - // Check environment - if let Ok(path) = std::env::var("CODEX_PATH") { - let path = PathBuf::from(path); - if path.exists() { - return Some(path); - } - } - - // Check common locations - let home = std::env::var("HOME").unwrap_or_default(); - let cargo_path = format!("{}/.cargo/bin/codex", home); - let local_path = format!("{}/.local/bin/codex", home); - let paths = [ - "/usr/local/bin/codex", - "/opt/homebrew/bin/codex", - cargo_path.as_str(), - local_path.as_str(), - ]; - - for path in paths { - let path = PathBuf::from(path); - if path.exists() { - return Some(path); - } - } - - None - } - - /// Start the runtime. - pub async fn start(&mut self) -> Result<(), CodexError> { - if self.state == RuntimeState::Ready { - return Err(CodexError::AlreadyInitialized); - } - - self.state = RuntimeState::Connecting; - - // Find binary - let _binary = self.find_codex_binary() - .ok_or_else(|| CodexError::Process("Codex binary not found".into()))?; - - // TODO: Start process - self.state = RuntimeState::Ready; - - Ok(()) - } - - /// Stop the runtime. - pub async fn stop(&mut self) -> Result<(), CodexError> { - if self.state == RuntimeState::Disconnected { - return Ok(()); - } - - // TODO: Stop process - self.state = RuntimeState::Disconnected; - - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_config_default() { - let config = CodexConfig::default(); - assert!(config.codex_path.is_null()); - assert_eq!(config.sandbox_mode, 1); - } - - #[test] - fn test_thread_handle() { - let handle = ThreadHandle::new(42); - assert_eq!(handle.id, 42); - assert!(!handle.is_null()); - - let null_handle = ThreadHandle::null(); - assert!(null_handle.is_null()); - } - - #[test] - fn test_runtime_state() { - let config = CodexConfigRust { - codex_path: None, - working_directory: None, - sandbox_mode: 1, - approval_policy: 0, - model: None, - api_key: None, - gateway_url: None, - debug: false, - }; - - let runtime = CodexRuntime::with_config(config); - assert_eq!(runtime.state(), RuntimeState::Disconnected); - } -} diff --git a/rust/src/types.rs b/rust/src/types.rs deleted file mode 100644 index bcec69b0..00000000 --- a/rust/src/types.rs +++ /dev/null @@ -1,68 +0,0 @@ -//! FFI-safe types for Codex integration. - -use std::ffi::CString; -use std::os::raw::c_char; - -/// FFI-safe result type. -#[repr(C)] -pub struct CodexResult { - /// Whether the operation was successful. - pub success: bool, - /// Error code if failed. - pub error_code: i32, - /// Error message if failed. - pub error_message: *const c_char, -} - -impl CodexResult { - pub fn ok() -> Self { - CodexResult { - success: true, - error_code: 0, - error_message: std::ptr::null(), - } - } - - pub fn err(code: i32, message: &str) -> Self { - let c_message = CString::new(message).unwrap_or_default(); - CodexResult { - success: false, - error_code: code, - error_message: c_message.as_ptr(), - } - } -} - -/// FFI-safe event type. -#[repr(C)] -pub struct CodexEvent { - /// Event type (started, delta, completed, error). - pub event_type: *const c_char, - /// Thread ID. - pub thread_id: *const c_char, - /// Turn ID. - pub turn_id: *const c_char, - /// Event data as JSON. - pub data: *const c_char, - /// Timestamp (Unix millis). - pub timestamp: i64, -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_result_ok() { - let result = CodexResult::ok(); - assert!(result.success); - assert_eq!(result.error_code, 0); - } - - #[test] - fn test_result_err() { - let result = CodexResult::err(1, "test error"); - assert!(!result.success); - assert_eq!(result.error_code, 1); - } -}