sqlite_graphrag/spawn/
mod.rs1pub mod claude_adapter;
8pub mod codex_adapter;
9pub mod compat_matrix;
10pub mod env_whitelist;
11pub mod error_propagator;
12pub mod executor_version;
13pub mod opencode_adapter;
14
15use crate::errors::AppError;
16use async_trait::async_trait;
17use executor_version::ExecutorVersion;
18use std::collections::BTreeMap;
19use std::process::Stdio;
20
21#[derive(Debug, Clone)]
23pub struct ParsedOutput {
24 pub items: Vec<serde_json::Value>,
25 pub raw_stdout: String,
26 pub raw_stderr: String,
27 pub exit_code: i32,
28}
29
30#[derive(Debug, Clone, PartialEq, Eq)]
32pub struct ExecutorCapabilities {
33 pub supports_mcp_map: bool,
34 pub supports_ask_for_approval_flag: bool,
35 pub supports_strict_schema: bool,
36 pub default_flags: Vec<String>,
37 pub removed_flags: Vec<String>,
38}
39
40impl ExecutorCapabilities {
41 pub fn empty() -> Self {
42 Self {
43 supports_mcp_map: false,
44 supports_ask_for_approval_flag: false,
45 supports_strict_schema: false,
46 default_flags: Vec::new(),
47 removed_flags: Vec::new(),
48 }
49 }
50}
51
52#[async_trait]
54pub trait VersionAdapter: Send + Sync {
55 fn name(&self) -> &'static str;
57
58 async fn detect(&self) -> Result<ExecutorVersion, AppError>;
60
61 fn capabilities_for(&self, version: &ExecutorVersion) -> ExecutorCapabilities;
63
64 fn build_args(
66 &self,
67 prompt: &str,
68 caps: &ExecutorCapabilities,
69 compat_mode: CompatMode,
70 ) -> Vec<String>;
71
72 fn parse_output(&self, raw_stdout: &str, raw_stderr: &str, exit_code: i32) -> ParsedOutput;
74}
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub enum CompatMode {
79 Strict,
81 Lenient,
83 Auto,
85}
86
87impl CompatMode {
88 pub fn parse(s: &str) -> Self {
89 match s.to_ascii_lowercase().as_str() {
90 "strict" => Self::Strict,
91 "lenient" => Self::Lenient,
92 _ => Self::Auto,
93 }
94 }
95}
96
97#[derive(Debug, Default)]
100pub struct VersionCache {
101 inner: std::sync::Mutex<BTreeMap<String, ExecutorVersion>>,
102}
103
104impl VersionCache {
105 pub fn new() -> Self {
106 Self::default()
107 }
108
109 pub fn get(&self, name: &str) -> Option<ExecutorVersion> {
110 self.inner.lock().ok().and_then(|m| m.get(name).cloned())
111 }
112
113 pub fn put(&self, name: &str, version: ExecutorVersion) {
114 if let Ok(mut m) = self.inner.lock() {
115 m.insert(name.to_string(), version);
116 }
117 }
118
119 pub fn clear(&self) {
120 if let Ok(mut m) = self.inner.lock() {
121 m.clear();
122 }
123 }
124}
125
126static VERSION_CACHE: std::sync::OnceLock<VersionCache> = std::sync::OnceLock::new();
127
128pub fn global_version_cache() -> &'static VersionCache {
129 VERSION_CACHE.get_or_init(VersionCache::new)
130}
131
132pub fn base_command(binary: &str) -> std::process::Command {
134 let mut cmd = std::process::Command::new(binary);
135 cmd.stdin(Stdio::null())
136 .stdout(Stdio::piped())
137 .stderr(Stdio::piped());
138 cmd
139}