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/go_task_service_client.dart`
|
||||||
- `lib/runtime/codex_runtime.dart`
|
- `lib/runtime/codex_runtime.dart`
|
||||||
- `lib/app/app_controller_desktop_settings_runtime.dart`
|
- `lib/app/app_controller_desktop_settings_runtime.dart`
|
||||||
- `rust/src/lib.rs`
|
- `rust/src/lib.rs`(历史记录)
|
||||||
|
|
||||||
## Maintenance Rule
|
## Maintenance Rule
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
`rust/src` 当前是一组相对独立、边界清晰的 Codex FFI 草图实现。这里的重点是:
|
`rust/` 目录已退役,下面这份文档仅作为历史记录保留。它描述的是曾经存在的 Codex FFI 草图实现,不代表当前可用能力。
|
||||||
|
|
||||||
|
历史上这里的重点是:
|
||||||
|
|
||||||
- Rust 公开结构体与状态模型
|
- Rust 公开结构体与状态模型
|
||||||
- C ABI 函数签名
|
- C ABI 函数签名
|
||||||
@ -170,7 +172,7 @@
|
|||||||
|
|
||||||
## Exported FFI Functions
|
## Exported FFI Functions
|
||||||
|
|
||||||
下面这些函数定义在 `rust/src/lib.rs`,是 Flutter / Dart 侧真正可调用的 C ABI 面。
|
下面这些函数曾定义在 `rust/src/lib.rs`,是 Flutter / Dart 侧的 C ABI 面。
|
||||||
|
|
||||||
## `codex_init`
|
## `codex_init`
|
||||||
|
|
||||||
@ -280,4 +282,4 @@
|
|||||||
3. `rust/src/lib.rs`
|
3. `rust/src/lib.rs`
|
||||||
4. `rust/src/runtime.rs`
|
4. `rust/src/runtime.rs`
|
||||||
5. `rust/src/types.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`
|
- 通过 `CodeAgentNodeOrchestrator` 把 XWorkmate 固定为 `app-mediated cooperative node`
|
||||||
- 通过 `RuntimeCoordinator` 保留多外部 Code Agent CLI 的统一 registry surface
|
- 通过 `RuntimeCoordinator` 保留多外部 Code Agent CLI 的统一 registry surface
|
||||||
|
|
||||||
Rust FFI / built-in Codex 仍是 future placeholder,不应宣传为已完成。
|
Rust FFI / built-in Codex 已退役,不应再作为当前实现宣传。
|
||||||
|
|
||||||
## 能力补全清单(按需求项)
|
## 能力补全清单(按需求项)
|
||||||
|
|
||||||
1. 内置 Code Agent(built-in):
|
1. 内置 Code Agent(built-in):
|
||||||
- 已提供运行时模式接入位与桥接流程编排(AI Gateway / OpenClaw 协同元数据)
|
- 已提供运行时模式接入位与桥接流程编排(AI Gateway / OpenClaw 协同元数据)
|
||||||
- 当前仍属于 experimental,受 Rust FFI TODO 约束
|
- 当前仍属于 experimental,且 Rust FFI 路径已退役
|
||||||
2. 外部依赖 Codex CLI:
|
2. 外部依赖 Codex CLI:
|
||||||
- 已作为稳定主路径接入
|
- 已作为稳定主路径接入
|
||||||
- 保持与内置模式相同的桥接能力和模式切换语义
|
- 保持与内置模式相同的桥接能力和模式切换语义
|
||||||
@ -72,16 +72,15 @@ Rust FFI / built-in Codex 仍是 future placeholder,不应宣传为已完成
|
|||||||
- 不做第二个 provider 之前的通用 UI
|
- 不做第二个 provider 之前的通用 UI
|
||||||
- 不做复杂调度策略
|
- 不做复杂调度策略
|
||||||
|
|
||||||
### Phase 3: 内置 Codex / Rust FFI
|
### Phase 3: 内置 Codex / Rust FFI(历史)
|
||||||
|
|
||||||
目标:
|
目标:
|
||||||
|
|
||||||
- 仅在 Rust FFI 具备真实可用能力后,再开放 built-in 交付承诺
|
- 这一路径仅作历史记录保留,不再推进为当前交付面
|
||||||
|
|
||||||
前置条件:
|
前置条件:
|
||||||
|
|
||||||
- `rust/src/lib.rs` 的消息发送 / 轮询 TODO 完成
|
- 历史文档中的 Rust FFI TODO 不再作为当前实现目标
|
||||||
- `rust/src/runtime.rs` 的进程启动 / 停止 TODO 完成
|
|
||||||
- 能复用与 external CLI 相同的 coordinator / registry 契约
|
- 能复用与 external CLI 相同的 coordinator / registry 契约
|
||||||
|
|
||||||
## truth 收口
|
## truth 收口
|
||||||
|
|||||||
@ -45,7 +45,7 @@
|
|||||||
- 多 provider 调度策略
|
- 多 provider 调度策略
|
||||||
- 第二个 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 契约
|
- 能复用当前 coordinator / registry 契约
|
||||||
|
|
||||||
状态:
|
状态:
|
||||||
|
|||||||
@ -66,8 +66,7 @@ XWorkmate 当前唯一可交付的 Codex 集成路径是 **External Codex CLI**
|
|||||||
|
|
||||||
现状:
|
现状:
|
||||||
|
|
||||||
- `rust/src/lib.rs` 仍保留消息发送和事件轮询 TODO
|
- `rust/` 目录已退役,曾经的消息发送、事件轮询、进程启动和停止 TODO 仅保留在历史文档里
|
||||||
- `rust/src/runtime.rs` 仍保留进程启动和停止 TODO
|
|
||||||
- Flutter 侧的 `builtIn` 只是保留枚举位,不会实际走可用 FFI 路径
|
- Flutter 侧的 `builtIn` 只是保留枚举位,不会实际走可用 FFI 路径
|
||||||
|
|
||||||
### 2. 其他外部 Provider 的通用选择与调度
|
### 2. 其他外部 Provider 的通用选择与调度
|
||||||
@ -117,6 +116,6 @@ flowchart LR
|
|||||||
不能宣称的能力:
|
不能宣称的能力:
|
||||||
|
|
||||||
- Built-in Codex 已可用
|
- Built-in Codex 已可用
|
||||||
- Rust FFI 已完成
|
- Rust FFI 已退役,不作为当前交付能力
|
||||||
- Scheduled Tasks 已支持增删改
|
- Scheduled Tasks 已支持增删改
|
||||||
- Memory 已支持完整 CRUD
|
- 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