fabro/lib/crates/fabro-cli/src/cli_config.rs
Bryan Helmkamp 28884ae093 rename Arc to Fabro in all Rust crates, symbols, env vars, and supporting files
- Rename 20 crate directories lib/crates/arc-* → fabro-*
- Update all Cargo.toml: crate names, dep paths, feature flags, bin name
- Rename arc_server module → fabro_server in fabro-llm
- ArcError → FabroError across 30+ files
- ARC_VERSION/ARC_GIT_SHA/ARC_BUILD_DATE → FABRO_* constants
- All use/qualified paths: arc_agent:: → fabro_agent::, etc. (~1500 occurrences)
- Env vars ARC_* → FABRO_* in string literals and shell scripts
- String literals: X-Arc-Demo, arc-bot, arc@local, arc-web, arc-mcp, etc.
- Path strings: .arc/ → .fabro/, arc.toml → fabro.toml, refs/arc/ → refs/fabro/
- arc-api.yaml → fabro-api.yaml (OpenAPI spec)
- skills/arc-create-workflow → fabro-create-workflow
- trycmd fixtures: $ arc → $ fabro
- Inline snapshots (insta) updated
- CI, Docker, install.sh, scripts, CLAUDE.md, AGENTS.md
- TypeScript app: env vars, headers, JWT issuer
- Docs: page slugs, git refs, config paths, sandbox names, repo URLs
- Repo references: brynary/arc → fabro-sh/fabro

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 12:25:58 -04:00

154 lines
4.6 KiB
Rust

pub use fabro_config::cli::*;
#[cfg(feature = "server")]
use tracing::debug;
#[cfg(feature = "server")]
#[derive(Debug, PartialEq)]
pub struct ResolvedMode {
pub mode: ExecutionMode,
pub server_base_url: String,
pub tls: Option<ClientTlsConfig>,
}
#[cfg(feature = "server")]
const DEFAULT_SERVER_URL: &str = "http://localhost:3000";
#[cfg(feature = "server")]
pub fn resolve_mode(
cli_mode: Option<ExecutionMode>,
cli_server_url: Option<&str>,
config: &CliConfig,
) -> ResolvedMode {
let mode = cli_mode.or_else(|| config.mode.clone()).unwrap_or_default();
let server_defaults = config.server.as_ref();
let server_base_url = cli_server_url
.map(String::from)
.or_else(|| server_defaults.and_then(|s| s.base_url.clone()))
.unwrap_or_else(|| DEFAULT_SERVER_URL.to_string());
let tls = server_defaults.and_then(|s| s.tls.clone());
debug!(mode = ?mode, base_url = %server_base_url, tls = tls.is_some(), "CLI mode resolved");
ResolvedMode {
mode,
server_base_url,
tls,
}
}
#[cfg(feature = "server")]
pub fn build_server_client(tls: Option<&ClientTlsConfig>) -> anyhow::Result<reqwest::Client> {
let Some(tls) = tls else {
return Ok(reqwest::Client::new());
};
let cert_path = fabro_config::expand_tilde(&tls.cert);
let key_path = fabro_config::expand_tilde(&tls.key);
let ca_path = fabro_config::expand_tilde(&tls.ca);
let cert_pem = std::fs::read(&cert_path)?;
let key_pem = std::fs::read(&key_path)?;
let ca_pem = std::fs::read(&ca_path)?;
let mut identity_pem = cert_pem;
identity_pem.push(b'\n');
identity_pem.extend_from_slice(&key_pem);
let identity = reqwest::Identity::from_pem(&identity_pem)?;
let ca_cert = reqwest::Certificate::from_pem(&ca_pem)?;
let client = reqwest::Client::builder()
.use_rustls_tls()
.identity(identity)
.add_root_certificate(ca_cert)
.build()?;
Ok(client)
}
#[cfg(all(test, feature = "server"))]
mod tests {
use std::path::PathBuf;
use super::*;
// --- resolve_mode precedence ---
#[test]
fn resolve_mode_defaults_to_standalone() {
let config = CliConfig::default();
let resolved = resolve_mode(None, None, &config);
assert_eq!(resolved.mode, ExecutionMode::Standalone);
assert_eq!(resolved.server_base_url, DEFAULT_SERVER_URL);
assert_eq!(resolved.tls, None);
}
#[test]
fn resolve_mode_config_overrides_default() {
let config = CliConfig {
mode: Some(ExecutionMode::Server),
server: Some(ServerDefaults {
base_url: Some("https://config.example.com".to_string()),
tls: None,
}),
..CliConfig::default()
};
let resolved = resolve_mode(None, None, &config);
assert_eq!(resolved.mode, ExecutionMode::Server);
assert_eq!(resolved.server_base_url, "https://config.example.com");
}
#[test]
fn resolve_mode_cli_overrides_config() {
let config = CliConfig {
mode: Some(ExecutionMode::Standalone),
server: Some(ServerDefaults {
base_url: Some("https://config.example.com".to_string()),
tls: None,
}),
..CliConfig::default()
};
let resolved = resolve_mode(
Some(ExecutionMode::Server),
Some("https://cli.example.com"),
&config,
);
assert_eq!(resolved.mode, ExecutionMode::Server);
assert_eq!(resolved.server_base_url, "https://cli.example.com");
}
#[test]
fn resolve_mode_cli_url_overrides_config_url() {
let config = CliConfig {
server: Some(ServerDefaults {
base_url: Some("https://config.example.com".to_string()),
tls: None,
}),
..CliConfig::default()
};
let resolved = resolve_mode(None, Some("https://cli.example.com"), &config);
assert_eq!(resolved.server_base_url, "https://cli.example.com");
}
#[test]
fn resolve_mode_tls_from_config() {
let tls = ClientTlsConfig {
cert: PathBuf::from("cert.pem"),
key: PathBuf::from("key.pem"),
ca: PathBuf::from("ca.pem"),
};
let config = CliConfig {
server: Some(ServerDefaults {
base_url: None,
tls: Some(tls.clone()),
}),
..CliConfig::default()
};
let resolved = resolve_mode(None, None, &config);
assert_eq!(resolved.tls, Some(tls));
}
}