hivemind/adapters/
codex.rs1use super::opencode::{OpenCodeAdapter, OpenCodeConfig};
4use super::runtime::{
5 AdapterConfig, ExecutionInput, ExecutionReport, InteractiveAdapterEvent,
6 InteractiveExecutionResult, RuntimeAdapter, RuntimeError,
7};
8use std::path::PathBuf;
9use std::time::Duration;
10use uuid::Uuid;
11
12#[derive(Debug, Clone)]
14pub struct CodexConfig {
15 pub base: AdapterConfig,
17 pub model: Option<String>,
19}
20
21impl CodexConfig {
22 pub fn new(binary_path: PathBuf) -> Self {
24 Self {
25 base: AdapterConfig::new("codex", binary_path).with_timeout(Duration::from_secs(600)),
26 model: None,
27 }
28 }
29
30 #[must_use]
32 pub fn with_model(mut self, model: impl Into<String>) -> Self {
33 self.model = Some(model.into());
34 self
35 }
36}
37
38impl Default for CodexConfig {
39 fn default() -> Self {
40 let mut cfg = Self::new(PathBuf::from("codex"));
41 cfg.base.args = vec![
42 "exec".to_string(),
43 "--skip-git-repo-check".to_string(),
44 "-".to_string(),
45 ];
46 cfg
47 }
48}
49
50pub struct CodexAdapter {
52 inner: OpenCodeAdapter,
53}
54
55impl CodexAdapter {
56 pub fn new(config: CodexConfig) -> Self {
58 let mut inner = OpenCodeConfig::new(config.base.binary_path.clone());
59 inner.base.name = "codex".to_string();
60 inner.base.args = config.base.args;
61 inner.base.env = config.base.env;
62 inner.base.timeout = config.base.timeout;
63 inner.model.clone_from(&config.model);
64
65 if let Some(model) = config.model {
67 let has_model_flag = inner
68 .base
69 .args
70 .iter()
71 .any(|a| a == "--model" || a.starts_with("--model="));
72 if !has_model_flag {
73 inner.base.args.extend(["--model".to_string(), model]);
74 }
75 }
76
77 Self {
78 inner: OpenCodeAdapter::new(inner),
79 }
80 }
81
82 pub fn with_defaults() -> Self {
84 Self::new(CodexConfig::default())
85 }
86
87 pub fn execute_interactive<F>(
88 &mut self,
89 input: &ExecutionInput,
90 on_event: F,
91 ) -> Result<InteractiveExecutionResult, RuntimeError>
92 where
93 F: FnMut(InteractiveAdapterEvent) -> std::result::Result<(), String>,
94 {
95 self.inner.execute_interactive(input, on_event)
96 }
97}
98
99impl RuntimeAdapter for CodexAdapter {
100 fn name(&self) -> &str {
101 self.inner.name()
102 }
103
104 fn initialize(&mut self) -> Result<(), RuntimeError> {
105 self.inner.initialize()
106 }
107
108 fn prepare(&mut self, task_id: Uuid, worktree: &std::path::Path) -> Result<(), RuntimeError> {
109 self.inner.prepare(task_id, worktree)
110 }
111
112 fn execute(&mut self, input: ExecutionInput) -> Result<ExecutionReport, RuntimeError> {
113 self.inner.execute(input)
114 }
115
116 fn terminate(&mut self) -> Result<(), RuntimeError> {
117 self.inner.terminate()
118 }
119
120 fn config(&self) -> &AdapterConfig {
121 self.inner.config()
122 }
123}