mod command;
mod mcp;
mod provider;
mod response;
#[cfg(test)]
mod tests;
use serde::Deserialize;
use std::path::PathBuf;
use std::time::Duration;
use tokio::process::Command;
pub use mcp::mcp_tool_patterns;
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(3600);
pub struct ClaudeCodeProvider {
max_turns: u32,
allowed_tools: Vec<String>,
timeout: Duration,
working_dir: Option<PathBuf>,
max_resume_attempts: u32,
model: String,
oauth_token: Option<String>,
sandbox_profile: kernex_sandbox::SandboxProfile,
}
#[derive(Debug, Deserialize)]
struct ClaudeCliResponse {
#[serde(default, rename = "type")]
response_type: Option<String>,
#[serde(default)]
subtype: Option<String>,
#[serde(default)]
result: Option<String>,
#[serde(default)]
is_error: bool,
#[serde(default)]
session_id: Option<String>,
#[serde(default)]
model: Option<String>,
#[serde(default)]
num_turns: Option<u32>,
}
impl ClaudeCodeProvider {
pub fn new() -> Self {
Self {
max_turns: 25,
allowed_tools: vec![],
timeout: DEFAULT_TIMEOUT,
working_dir: None,
max_resume_attempts: 5,
model: String::new(),
oauth_token: None,
sandbox_profile: Default::default(),
}
}
pub fn from_config(
max_turns: u32,
allowed_tools: Vec<String>,
timeout_secs: u64,
working_dir: Option<PathBuf>,
max_resume_attempts: u32,
model: String,
oauth_token: Option<String>,
) -> Self {
Self {
max_turns,
allowed_tools,
timeout: Duration::from_secs(timeout_secs),
working_dir,
max_resume_attempts,
model,
oauth_token,
sandbox_profile: Default::default(),
}
}
pub fn with_sandbox_profile(mut self, profile: kernex_sandbox::SandboxProfile) -> Self {
self.sandbox_profile = profile;
self
}
pub async fn check_cli() -> bool {
Command::new("claude")
.arg("--version")
.output()
.await
.map(|o| o.status.success())
.unwrap_or(false)
}
}
impl Default for ClaudeCodeProvider {
fn default() -> Self {
Self::new()
}
}