use clap::Args;
use colored::Colorize;
use std::process::Command as StdCommand;
use tokio::signal;
#[derive(Args)]
pub struct ServerArgs {
#[arg(long, short, default_value = "8080")]
pub port: u16,
#[arg(long, env = "AGENTOVEN_SERVER_BIN")]
pub bin: Option<String>,
#[arg(long, env = "DATABASE_URL")]
pub database_url: Option<String>,
#[arg(long, env = "AGENTOVEN_PGVECTOR_URL")]
pub pgvector_url: Option<String>,
#[arg(long, env = "OPENAI_API_KEY")]
pub openai_api_key: Option<String>,
#[arg(long, env = "OLLAMA_URL")]
pub ollama_url: Option<String>,
#[arg(long, default_value = "*")]
pub cors_origins: String,
}
pub async fn execute(args: ServerArgs) -> anyhow::Result<()> {
println!();
println!(" 🏺 {}", "AgentOven Server".bold());
println!();
let port = args.port;
let server_bin = match args.bin {
Some(bin) => bin,
None => find_server_binary()?,
};
let mut cmd = StdCommand::new(&server_bin);
cmd.env("AGENTOVEN_PORT", port.to_string());
cmd.env("AGENTOVEN_CORS_ORIGINS", &args.cors_origins);
let store_mode;
if let Some(ref db_url) = args.database_url {
cmd.env("DATABASE_URL", db_url);
store_mode = "postgresql";
if let Some(ref pgv_url) = args.pgvector_url {
cmd.env("AGENTOVEN_PGVECTOR_URL", pgv_url);
}
} else {
store_mode = "in-memory";
if let Some(ref pgv_url) = args.pgvector_url {
cmd.env("AGENTOVEN_PGVECTOR_URL", pgv_url);
}
}
if let Some(ref key) = args.openai_api_key {
cmd.env("OPENAI_API_KEY", key);
}
if let Some(ref url) = args.ollama_url {
cmd.env("OLLAMA_URL", url);
}
println!(" Port: {}", port.to_string().cyan());
println!(" Store: {}", store_mode.cyan());
if args.pgvector_url.is_some() || args.database_url.is_some() {
println!(" pgvector: {}", "enabled".green());
} else {
println!(
" pgvector: {} (set --database-url or --pgvector-url to enable)",
"disabled".yellow()
);
}
println!(" Binary: {}", server_bin.dimmed());
println!();
cmd.stdout(std::process::Stdio::inherit());
cmd.stderr(std::process::Stdio::inherit());
let mut child = cmd.spawn().map_err(|e| {
anyhow::anyhow!(
"Failed to start server: {e}\n Binary: {server_bin}\n\n \
If the binary is not installed, run:\n \
cd control-plane && go build -o agentoven-server ./cmd/server/\n\n \
Or use `agentoven local up` to download and run via Docker."
)
})?;
let local_url = format!("http://localhost:{port}");
let mut config = agentoven_core::AgentOvenConfig::load();
config.url = local_url.clone();
config.edition = Some("community".into());
config.save()?;
println!(" {} CLI configured → {}", "✓".green().bold(), local_url.cyan());
println!(" Press {} to stop.", "Ctrl+C".bold());
println!();
tokio::select! {
status = tokio::task::spawn_blocking(move || child.wait()) => {
match status {
Ok(Ok(exit)) => {
if !exit.success() {
anyhow::bail!("Server exited with status: {}", exit);
}
}
Ok(Err(e)) => anyhow::bail!("Failed to wait for server: {e}"),
Err(e) => anyhow::bail!("Task join error: {e}"),
}
}
_ = signal::ctrl_c() => {
println!();
println!(" 🛑 Shutting down server...");
tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
println!(" 👋 AgentOven stopped.");
}
}
println!();
Ok(())
}
fn find_server_binary() -> anyhow::Result<String> {
if let Ok(output) = StdCommand::new("which")
.arg("agentoven-server")
.output()
{
if output.status.success() {
let path = String::from_utf8_lossy(&output.stdout).trim().to_string();
if !path.is_empty() {
return Ok(path);
}
}
}
if let Ok(exe) = std::env::current_exe() {
let dir = exe.parent().unwrap_or(std::path::Path::new("."));
let candidate = dir.join("agentoven-server");
if candidate.exists() {
return Ok(candidate.to_string_lossy().to_string());
}
}
if let Some(home) = dirs::home_dir() {
let candidate = home
.join(".agentoven")
.join("local")
.join("bin")
.join("agentoven-server");
if candidate.exists() {
return Ok(candidate.to_string_lossy().to_string());
}
}
let cwd = std::env::current_dir().unwrap_or_default();
for candidate_path in &[
cwd.join("control-plane/agentoven-server"),
cwd.join("server"),
cwd.join("agentoven-server"),
] {
if candidate_path.exists() {
return Ok(candidate_path.to_string_lossy().to_string());
}
}
Err(anyhow::anyhow!(
"Could not find the AgentOven server binary.\n\n \
Options:\n \
1. Build it: cd control-plane && go build -o agentoven-server ./cmd/server/\n \
2. Specify: agentoven server --bin /path/to/agentoven-server\n \
3. Use Docker: agentoven local up"
))
}