chore: retire rust ffi scaffold
This commit is contained in:
parent
b0389e9b2c
commit
05fc574f8b
@ -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
|
||||
|
||||
|
||||
@ -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 都未完成
|
||||
|
||||
@ -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 收口
|
||||
|
||||
@ -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 契约
|
||||
|
||||
状态:
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
@ -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<std::io::Error> for CodexError {
|
||||
fn from(err: std::io::Error) -> Self {
|
||||
CodexError::Io(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<serde_json::Error> for CodexError {
|
||||
fn from(err: serde_json::Error) -> Self {
|
||||
CodexError::Config(err.to_string())
|
||||
}
|
||||
}
|
||||
139
rust/src/lib.rs
139
rust/src/lib.rs
@ -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()
|
||||
}
|
||||
@ -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<CodexConfigRust, CodexError> {
|
||||
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<String>,
|
||||
pub working_directory: Option<String>,
|
||||
pub sandbox_mode: i32,
|
||||
pub approval_policy: i32,
|
||||
pub model: Option<String>,
|
||||
pub api_key: Option<String>,
|
||||
pub gateway_url: Option<String>,
|
||||
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<PathBuf> {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user