// FFI bindings for Codex CLI integration. // // These bindings provide direct access to the native Rust library. import 'dart:ffi'; import 'dart:io'; import 'package:ffi/ffi.dart'; // ============================================================================ // FFI Structures // ============================================================================ /// FFI-compatible result type. final class CodexResultFFI extends Struct { @Bool() external bool success; @Int32() external int errorCode; external Pointer errorMessage; } /// FFI-compatible message type. final class CodexMessageFFI extends Struct { external Pointer messageType; external Pointer content; external Pointer threadId; external Pointer turnId; } /// FFI-compatible event type. final class CodexEventFFI extends Struct { external Pointer eventType; external Pointer threadId; external Pointer turnId; external Pointer data; @Int64() external int timestamp; } /// FFI-compatible configuration. final class CodexConfigFFI extends Struct { external Pointer codexPath; external Pointer workingDirectory; @Int32() external int sandboxMode; @Int32() external int approvalPolicy; external Pointer model; external Pointer apiKey; external Pointer gatewayUrl; @Bool() external bool debug; } /// Opaque thread handle. final class ThreadHandleFFI extends Struct { @Uint64() external int id; } // ============================================================================ // Native Functions // ============================================================================ typedef _CodexInitNative = Int32 Function(); typedef _CodexInitDart = int Function(); typedef _CodexRuntimeCreateNative = Pointer Function(Pointer config); typedef _CodexRuntimeCreateDart = Pointer Function(Pointer config); typedef _CodexRuntimeDestroyNative = Void Function(Pointer runtime); typedef _CodexRuntimeDestroyDart = void Function(Pointer runtime); typedef _CodexStartThreadNative = ThreadHandleFFI Function(Pointer runtime, Pointer cwd); typedef _CodexStartThreadDart = ThreadHandleFFI Function(Pointer runtime, Pointer cwd); typedef _CodexSendMessageNative = Int32 Function( Pointer runtime, ThreadHandleFFI thread, Pointer message, ); typedef _CodexSendMessageDart = int Function( Pointer runtime, ThreadHandleFFI thread, Pointer message, ); typedef _CodexPollEventsNative = UintPtr Function( Pointer runtime, Pointer events, UintPtr maxEvents, ); typedef _CodexPollEventsDart = int Function( Pointer runtime, Pointer events, int maxEvents, ); typedef _CodexShutdownNative = Int32 Function(Pointer runtime); typedef _CodexShutdownDart = int Function(Pointer runtime); typedef _CodexLastErrorNative = Pointer Function(Pointer runtime); typedef _CodexLastErrorDart = Pointer Function(Pointer runtime); // Opaque runtime type final class CodexRuntime extends Opaque {} // ============================================================================ // Dart Wrapper Class // ============================================================================ /// Dart wrapper for Codex FFI. class CodexFFIBindings { final DynamicLibrary _lib; late final _CodexInitDart _init; late final _CodexRuntimeCreateDart _runtimeCreate; late final _CodexRuntimeDestroyDart _runtimeDestroy; late final _CodexStartThreadDart _startThread; late final _CodexSendMessageDart _sendMessage; late final _CodexPollEventsDart _pollEvents; late final _CodexShutdownDart _shutdown; late final _CodexLastErrorDart _lastError; Pointer? _runtime; CodexFFIBindings() : _lib = _loadLibrary() { _init = _lib.lookupFunction<_CodexInitNative, _CodexInitDart>('codex_init'); _runtimeCreate = _lib .lookupFunction<_CodexRuntimeCreateNative, _CodexRuntimeCreateDart>( 'codex_runtime_create', ); _runtimeDestroy = _lib .lookupFunction<_CodexRuntimeDestroyNative, _CodexRuntimeDestroyDart>( 'codex_runtime_destroy', ); _startThread = _lib .lookupFunction<_CodexStartThreadNative, _CodexStartThreadDart>( 'codex_start_thread', ); _sendMessage = _lib .lookupFunction<_CodexSendMessageNative, _CodexSendMessageDart>( 'codex_send_message', ); _pollEvents = _lib .lookupFunction<_CodexPollEventsNative, _CodexPollEventsDart>( 'codex_poll_events', ); _shutdown = _lib.lookupFunction<_CodexShutdownNative, _CodexShutdownDart>( 'codex_shutdown', ); _lastError = _lib .lookupFunction<_CodexLastErrorNative, _CodexLastErrorDart>( 'codex_last_error', ); } static DynamicLibrary _loadLibrary() { if (Platform.isMacOS) { return DynamicLibrary.open('libcodex_ffi.dylib'); } else if (Platform.isLinux) { return DynamicLibrary.open('libcodex_ffi.so'); } else if (Platform.isWindows) { return DynamicLibrary.open('codex_ffi.dll'); } throw UnsupportedError('Unsupported platform'); } /// Initialize the library. void initialize() { final result = _init(); if (result != 0) { throw StateError('Failed to initialize Codex FFI'); } } /// Create a runtime with configuration. void createRuntime(CodexConfig config) { if (_runtime != null) { throw StateError('Runtime already created'); } final configPtr = _createConfigFFI(config); try { _runtime = _runtimeCreate(configPtr); if (_runtime == nullptr) { throw StateError('Failed to create runtime'); } } finally { _freeConfigFFI(configPtr); } } /// Destroy the runtime. void destroyRuntime() { if (_runtime != null) { _runtimeDestroy(_runtime!); _runtime = nullptr; } } /// Start a new thread. int startThread(String cwd) { _ensureRuntime(); final cwdPtr = cwd.toNativeUtf8(); try { final handle = _startThread(_runtime!, cwdPtr); return handle.id; } finally { calloc.free(cwdPtr); } } /// Send a message to the thread. int sendMessage(int threadId, String message) { _ensureRuntime(); final messagePtr = message.toNativeUtf8(); final handlePtr = calloc(); try { handlePtr.ref.id = threadId; return _sendMessage(_runtime!, handlePtr.ref, messagePtr); } finally { calloc.free(messagePtr); calloc.free(handlePtr); } } /// Poll for events. List> pollEvents(int maxEvents) { _ensureRuntime(); final eventsPtr = calloc(maxEvents); try { final count = _pollEvents(_runtime!, eventsPtr, maxEvents); final events = >[]; for (var i = 0; i < count; i++) { final event = eventsPtr[i]; events.add({ 'eventType': event.eventType.toDartString(), 'threadId': event.threadId.toDartString(), 'turnId': event.turnId.toDartString(), 'data': event.data.toDartString(), 'timestamp': event.timestamp, }); } return events; } finally { calloc.free(eventsPtr); } } /// Shutdown the runtime. void shutdown() { _ensureRuntime(); _shutdown(_runtime!); } /// Get last error message. String? lastError() { if (_runtime == null) return null; final ptr = _lastError(_runtime!); if (ptr == nullptr) return null; return ptr.toDartString(); } void _ensureRuntime() { if (_runtime == null) { throw StateError('Runtime not initialized'); } } Pointer _createConfigFFI(CodexConfig config) { final ptr = calloc(); ptr.ref.codexPath = config.codexPath?.toNativeUtf8() ?? nullptr; ptr.ref.workingDirectory = config.workingDirectory?.toNativeUtf8() ?? nullptr; ptr.ref.sandboxMode = config.sandboxMode; ptr.ref.approvalPolicy = config.approvalPolicy; ptr.ref.model = config.model?.toNativeUtf8() ?? nullptr; ptr.ref.apiKey = config.apiKey?.toNativeUtf8() ?? nullptr; ptr.ref.gatewayUrl = config.gatewayUrl?.toNativeUtf8() ?? nullptr; ptr.ref.debug = config.debug; return ptr; } void _freeConfigFFI(Pointer ptr) { if (ptr.ref.codexPath != nullptr) { calloc.free(ptr.ref.codexPath); } if (ptr.ref.workingDirectory != nullptr) { calloc.free(ptr.ref.workingDirectory); } if (ptr.ref.model != nullptr) { calloc.free(ptr.ref.model); } if (ptr.ref.apiKey != nullptr) { calloc.free(ptr.ref.apiKey); } if (ptr.ref.gatewayUrl != nullptr) { calloc.free(ptr.ref.gatewayUrl); } calloc.free(ptr); } } /// Configuration for Codex FFI. class CodexConfig { final String? codexPath; final String? workingDirectory; final int sandboxMode; final int approvalPolicy; final String? model; final String? apiKey; final String? gatewayUrl; final bool debug; const CodexConfig({ this.codexPath, this.workingDirectory, this.sandboxMode = 1, // workspace-write this.approvalPolicy = 0, // suggest this.model, this.apiKey, this.gatewayUrl, this.debug = false, }); }