Remove server feature flag from fabro-cli, always compile server in

The server subcommand and related code were gated behind
cfg(feature = "server"). This removes the feature flag entirely,
making fabro-server a required dependency so the server command
is always available.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Bryan Helmkamp 2026-04-03 10:09:56 -07:00
parent 834b33a7f8
commit 563a9eeb49
78 changed files with 228 additions and 379 deletions

View file

@ -12,7 +12,6 @@ path = "src/main.rs"
[features]
default = []
server = ["dep:fabro-server"]
sleep_inhibitor = ["dep:core-foundation"]
[lints]
@ -36,7 +35,7 @@ fabro-checkpoint = { path = "../fabro-checkpoint" }
fabro-graphviz = { path = "../fabro-graphviz" }
fabro-validate = { path = "../fabro-validate" }
fabro-workflow = { path = "../fabro-workflow" }
fabro-server = { path = "../fabro-server", optional = true }
fabro-server = { path = "../fabro-server" }
fabro-telemetry = { path = "../fabro-telemetry" }
fabro-store = { path = "../fabro-store" }
fabro-types = { path = "../fabro-types" }

View file

@ -41,7 +41,6 @@ pub(crate) struct GlobalArgs {
#[arg(long, global = true, env = "FABRO_STORAGE_DIR")]
pub storage_dir: Option<PathBuf>,
#[cfg(feature = "server")]
/// Server URL (overrides server.base_url from user.toml)
#[arg(
long,
@ -767,7 +766,6 @@ pub(crate) enum Commands {
command: Option<ModelsCommand>,
},
/// Server operations
#[cfg(feature = "server")]
Server(ServerNamespace),
/// Check environment and integration health
Doctor {
@ -855,7 +853,6 @@ impl Commands {
Some(ModelsCommand::Test { .. }) => "model test",
None => "model",
},
#[cfg(feature = "server")]
Self::Server(ns) => match &ns.command {
ServerCommand::Start { .. } => "server start",
ServerCommand::Stop { .. } => "server stop",
@ -972,17 +969,14 @@ pub(crate) enum SecretCommand {
Set(SecretSetArgs),
}
#[cfg(feature = "server")]
#[derive(Args)]
pub(crate) struct ServerNamespace {
#[command(subcommand)]
pub(crate) command: ServerCommand,
}
#[cfg(feature = "server")]
use fabro_server::serve::ServeArgs;
#[cfg(feature = "server")]
#[derive(Subcommand)]
pub(crate) enum ServerCommand {
/// Start the HTTP API server

View file

@ -1,12 +1,9 @@
use std::path::PathBuf;
#[cfg(feature = "server")]
use std::process::Command;
#[cfg(feature = "server")]
use std::sync::LazyLock;
use base64::Engine as _;
use base64::engine::general_purpose::STANDARD as BASE64_STANDARD;
#[cfg(feature = "server")]
use fabro_config::server::{ApiAuthStrategy, AuthProvider};
use fabro_config::user::{default_user_config_path, legacy_user_config_path};
use fabro_llm::client::Client as LlmClient;
@ -17,9 +14,7 @@ pub(crate) use fabro_util::check_report::{
};
use fabro_util::terminal::Styles;
use futures::future::join_all;
#[cfg(feature = "server")]
use regex::Regex;
#[cfg(feature = "server")]
use semver::Version;
use crate::args::GlobalArgs;
@ -30,7 +25,6 @@ use crate::user_config::load_user_settings;
// System dependency types and parsers (server mode only)
// ---------------------------------------------------------------------------
#[cfg(feature = "server")]
pub struct DepSpec {
pub name: &'static str,
command: &'static [&'static str],
@ -39,7 +33,6 @@ pub struct DepSpec {
pattern: &'static LazyLock<Regex>,
}
#[cfg(feature = "server")]
#[derive(Debug, Clone, PartialEq)]
pub enum ProbeOutcome {
NotFound,
@ -47,16 +40,12 @@ pub enum ProbeOutcome {
Ok { version: Option<Version> },
}
#[cfg(feature = "server")]
static OPENSSL_RE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"(?:OpenSSL|LibreSSL)\s+(\d+)\.(\d+)\.(\d+)").unwrap());
#[cfg(feature = "server")]
static NODE_RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"v(\d+)\.(\d+)\.(\d+)").unwrap());
#[cfg(feature = "server")]
static DOT_RE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"graphviz version (\d+)\.(\d+)\.(\d+)").unwrap());
#[cfg(feature = "server")]
fn parse_version(re: &Regex, output: &str) -> Option<Version> {
let caps = re.captures(output)?;
Some(Version::new(
@ -66,7 +55,6 @@ fn parse_version(re: &Regex, output: &str) -> Option<Version> {
))
}
#[cfg(feature = "server")]
pub const DEP_SPECS: &[DepSpec] = &[
DepSpec {
name: "openssl",
@ -91,7 +79,6 @@ pub const DEP_SPECS: &[DepSpec] = &[
},
];
#[cfg(feature = "server")]
pub fn probe_system_deps() -> Vec<ProbeOutcome> {
DEP_SPECS
.iter()
@ -116,7 +103,6 @@ pub fn probe_system_deps() -> Vec<ProbeOutcome> {
.collect()
}
#[cfg(feature = "server")]
fn dep_issue(name: &str, issue: &str, required: bool) -> (CheckStatus, String) {
let severity = if required { "required" } else { "optional" };
let status = if required {
@ -127,7 +113,6 @@ fn dep_issue(name: &str, issue: &str, required: bool) -> (CheckStatus, String) {
(status, format!("{name}: {issue} ({severity})"))
}
#[cfg(feature = "server")]
pub fn check_system_deps(specs: &[DepSpec], outcomes: &[ProbeOutcome]) -> CheckResult {
let mut details = Vec::new();
let mut worst_status = CheckStatus::Pass;
@ -414,11 +399,8 @@ pub(crate) struct GithubAppStatus {
/// Result of attempting to sign a JWT with the configured credentials.
/// `None` if app_id or private key is missing.
pub sign_result: Option<Result<(), String>>,
#[cfg(feature = "server")]
pub client_id: bool,
#[cfg(feature = "server")]
pub client_secret: bool,
#[cfg(feature = "server")]
pub webhook_secret: bool,
}
@ -429,17 +411,9 @@ impl GithubAppStatus {
fn none_set(&self) -> bool {
let core_none = self.app_id.is_none() && !self.private_key_set;
#[cfg(feature = "server")]
{
core_none && !self.client_id && !self.client_secret && !self.webhook_secret
}
#[cfg(not(feature = "server"))]
{
core_none
}
core_none && !self.client_id && !self.client_secret && !self.webhook_secret
}
#[cfg(feature = "server")]
fn all_set(&self) -> bool {
self.core_set() && self.client_id && self.client_secret && self.webhook_secret
}
@ -463,13 +437,11 @@ pub(crate) fn check_github_app(status: &GithubAppStatus) -> CheckResult {
}
)));
#[allow(unused_mut)]
let mut fields: Vec<(&str, bool)> = vec![
("git.app_id", status.app_id.is_some()),
("GITHUB_APP_PRIVATE_KEY", status.private_key_set),
];
#[cfg(feature = "server")]
{
let server_fields: Vec<(&str, bool)> = vec![
("git.client_id", status.client_id),
@ -530,7 +502,6 @@ pub(crate) fn check_github_app(status: &GithubAppStatus) -> CheckResult {
};
}
#[cfg(feature = "server")]
if status.all_set() {
return CheckResult {
name: "GitHub App".to_string(),
@ -541,17 +512,6 @@ pub(crate) fn check_github_app(status: &GithubAppStatus) -> CheckResult {
};
}
#[cfg(not(feature = "server"))]
if status.core_set() {
return CheckResult {
name: "GitHub App".to_string(),
status: CheckStatus::Pass,
summary: "configured".to_string(),
details,
remediation: None,
};
}
let missing: Vec<_> = fields
.iter()
.filter(|(_, set)| !set)
@ -566,13 +526,11 @@ pub(crate) fn check_github_app(status: &GithubAppStatus) -> CheckResult {
}
}
#[cfg(feature = "server")]
pub struct ApiStatus {
pub base_url: String,
pub authentication_strategies: Vec<ApiAuthStrategy>,
}
#[cfg(feature = "server")]
fn format_auth_strategies(strategies: &[ApiAuthStrategy]) -> String {
strategies
.iter()
@ -584,7 +542,6 @@ fn format_auth_strategies(strategies: &[ApiAuthStrategy]) -> String {
.join(", ")
}
#[cfg(feature = "server")]
pub fn check_api(status: &ApiStatus, live_result: Option<&Result<(), String>>) -> CheckResult {
let mut details = vec![
CheckDetail::new(format!("Base URL: {}", status.base_url)),
@ -609,14 +566,12 @@ pub fn check_api(status: &ApiStatus, live_result: Option<&Result<(), String>>) -
}
}
#[cfg(feature = "server")]
pub struct WebStatus {
pub url: String,
pub auth_provider: AuthProvider,
pub allowed_usernames_count: usize,
}
#[cfg(feature = "server")]
fn format_auth_provider(provider: &AuthProvider) -> &'static str {
match provider {
AuthProvider::Github => "github",
@ -624,7 +579,6 @@ fn format_auth_provider(provider: &AuthProvider) -> &'static str {
}
}
#[cfg(feature = "server")]
pub fn check_web(status: &WebStatus, live_result: Option<&Result<(), String>>) -> CheckResult {
let mut details = vec![
CheckDetail::new(format!("URL: {}", status.url)),
@ -657,14 +611,12 @@ pub fn check_web(status: &WebStatus, live_result: Option<&Result<(), String>>) -
// Cryptographic key validation
// ---------------------------------------------------------------------------
#[cfg(feature = "server")]
pub struct TlsCheckInput {
pub cert_pem: String,
pub key_pem: String,
pub ca_pem: String,
}
#[cfg(feature = "server")]
pub struct CryptoInput {
pub auth_strategies: Vec<ApiAuthStrategy>,
pub tls_files: Option<Result<TlsCheckInput, String>>,
@ -674,7 +626,6 @@ pub struct CryptoInput {
pub now_epoch: i64,
}
#[cfg(feature = "server")]
fn decode_pem_value(name: &str, value: &str) -> Result<String, String> {
if value.starts_with("-----") {
return Ok(value.to_string());
@ -684,7 +635,6 @@ fn decode_pem_value(name: &str, value: &str) -> Result<String, String> {
String::from_utf8(bytes).map_err(|e| format!("{name} base64 decoded to invalid UTF-8: {e}"))
}
#[cfg(feature = "server")]
fn validate_tls_cert(pem: &str, now_epoch: i64) -> Result<String, String> {
let mut reader = std::io::Cursor::new(pem.as_bytes());
let certs: Vec<_> = rustls_pemfile::certs(&mut reader)
@ -708,7 +658,6 @@ fn validate_tls_cert(pem: &str, now_epoch: i64) -> Result<String, String> {
Ok(format!("CN={cn}, valid"))
}
#[cfg(feature = "server")]
fn validate_tls_private_key(pem: &str) -> Result<(), String> {
let mut reader = std::io::Cursor::new(pem.as_bytes());
rustls_pemfile::private_key(&mut reader)
@ -717,7 +666,6 @@ fn validate_tls_private_key(pem: &str) -> Result<(), String> {
Ok(())
}
#[cfg(feature = "server")]
fn validate_tls_ca(pem: &str) -> Result<(), String> {
let mut reader = std::io::Cursor::new(pem.as_bytes());
let certs: Vec<_> = rustls_pemfile::certs(&mut reader)
@ -729,7 +677,6 @@ fn validate_tls_ca(pem: &str) -> Result<(), String> {
Ok(())
}
#[cfg(feature = "server")]
fn validate_session_secret(value: &str) -> Result<(), String> {
if value.len() < 64 {
return Err(format!(
@ -743,14 +690,12 @@ fn validate_session_secret(value: &str) -> Result<(), String> {
Ok(())
}
#[cfg(feature = "server")]
struct CryptoCheckState {
details: Vec<CheckDetail>,
errors: Vec<String>,
worst: CheckStatus,
}
#[cfg(feature = "server")]
impl CryptoCheckState {
fn new() -> Self {
Self {
@ -787,7 +732,6 @@ impl CryptoCheckState {
}
}
#[cfg(feature = "server")]
pub fn check_crypto(input: &CryptoInput) -> CheckResult {
let has_jwt = input.auth_strategies.contains(&ApiAuthStrategy::Jwt);
let has_mtls = input.auth_strategies.contains(&ApiAuthStrategy::Mtls);
@ -942,7 +886,6 @@ async fn probe_brave_search(http: &reqwest::Client) -> Result<(), String> {
}
}
#[cfg(feature = "server")]
async fn probe_url(http: &reqwest::Client, url: &str) -> Result<(), String> {
http.get(url)
.send()
@ -988,10 +931,8 @@ pub(crate) async fn run_doctor(
let daytona_configured = std::env::var("DAYTONA_API_KEY").is_ok();
#[cfg(feature = "server")]
let server_settings = fabro_config::server::load_server_settings(None).unwrap_or_default();
#[cfg(feature = "server")]
let api_status = {
let api = server_settings.api.clone().unwrap_or_default();
ApiStatus {
@ -1000,7 +941,6 @@ pub(crate) async fn run_doctor(
}
};
#[cfg(feature = "server")]
let web_status = {
let web = server_settings.web.clone().unwrap_or_default();
WebStatus {
@ -1010,13 +950,10 @@ pub(crate) async fn run_doctor(
}
};
#[cfg(feature = "server")]
let server_git = server_settings.git.clone().unwrap_or_default();
#[cfg(feature = "server")]
let server_api = server_settings.api.clone().unwrap_or_default();
#[cfg(feature = "server")]
let server_web = server_settings.web.clone().unwrap_or_default();
let git_app_id = cli_settings.app_id().map(str::to_owned);
@ -1050,15 +987,11 @@ pub(crate) async fn run_doctor(
slug: cli_settings.slug().map(str::to_owned),
private_key_set: private_key_raw.is_some(),
sign_result,
#[cfg(feature = "server")]
client_id: server_git.client_id.is_some(),
#[cfg(feature = "server")]
client_secret: std::env::var("GITHUB_APP_CLIENT_SECRET").is_ok(),
#[cfg(feature = "server")]
webhook_secret: std::env::var("GITHUB_APP_WEBHOOK_SECRET").is_ok(),
};
#[cfg(feature = "server")]
let crypto_input = {
let has_mtls = server_api
.authentication_strategies
@ -1089,16 +1022,13 @@ pub(crate) async fn run_doctor(
}
};
#[cfg(feature = "server")]
let dep_results = probe_system_deps();
// Live probes (only when --live is set)
let sandbox_status;
let llm_live_results: Option<Vec<(Provider, Result<(), String>)>>;
let brave_live_result: Option<Result<(), String>>;
#[cfg(feature = "server")]
let api_live_result: Option<Result<(), String>>;
#[cfg(feature = "server")]
let web_live_result: Option<Result<(), String>>;
if live {
@ -1134,30 +1064,18 @@ pub(crate) async fn run_doctor(
};
let brave_fut = probe_brave_search(&http);
#[cfg(feature = "server")]
{
let api_url = format!("{}/runs", server_api.base_url);
let api_fut = probe_url(&http, &api_url);
let web_fut = probe_url(&http, &server_web.url);
let api_url = format!("{}/runs", server_api.base_url);
let api_fut = probe_url(&http, &api_url);
let web_fut = probe_url(&http, &server_web.url);
let (sandbox, llm, brave, api, web) =
tokio::join!(sandbox_fut, llm_fut, brave_fut, api_fut, web_fut);
let (sandbox, llm, brave, api, web) =
tokio::join!(sandbox_fut, llm_fut, brave_fut, api_fut, web_fut);
sandbox_status = sandbox;
llm_live_results = llm;
brave_live_result = Some(brave);
api_live_result = Some(api);
web_live_result = Some(web);
}
#[cfg(not(feature = "server"))]
{
let (sandbox, llm, brave) = tokio::join!(sandbox_fut, llm_fut, brave_fut);
sandbox_status = sandbox;
llm_live_results = llm;
brave_live_result = Some(brave);
}
sandbox_status = sandbox;
llm_live_results = llm;
brave_live_result = Some(brave);
api_live_result = Some(api);
web_live_result = Some(web);
} else {
sandbox_status = SandboxStatus {
daytona_configured,
@ -1165,15 +1083,11 @@ pub(crate) async fn run_doctor(
};
llm_live_results = None;
brave_live_result = None;
#[cfg(feature = "server")]
{
api_live_result = None;
web_live_result = None;
}
api_live_result = None;
web_live_result = None;
}
// Run pure checks
#[allow(unused_mut)]
let mut sections = vec![
CheckSection {
title: "Required".into(),
@ -1203,7 +1117,6 @@ pub(crate) async fn run_doctor(
},
];
#[cfg(feature = "server")]
sections.push(CheckSection {
title: "Server".into(),
checks: vec![
@ -1421,11 +1334,8 @@ mod tests {
sign_result: Some(Err(
"Signing failed: signature error: UnexpectedError".to_string()
)),
#[cfg(feature = "server")]
client_id: true,
#[cfg(feature = "server")]
client_secret: true,
#[cfg(feature = "server")]
webhook_secret: true,
};
let result = check_github_app(&status);
@ -1446,11 +1356,8 @@ mod tests {
slug: None,
private_key_set: false,
sign_result: None,
#[cfg(feature = "server")]
client_id: false,
#[cfg(feature = "server")]
client_secret: false,
#[cfg(feature = "server")]
webhook_secret: false,
};
let result = check_github_app(&status);
@ -1459,7 +1366,6 @@ mod tests {
// -- Server-only checks (check_api, check_web, check_crypto) --
#[cfg(feature = "server")]
mod server_tests {
use super::*;
@ -1652,7 +1558,6 @@ mod tests {
// -- parse_version (server only) --
#[test]
#[cfg(feature = "server")]
fn parse_version_openssl() {
assert_eq!(
parse_version(
@ -1664,7 +1569,6 @@ mod tests {
}
#[test]
#[cfg(feature = "server")]
fn parse_version_libressl() {
assert_eq!(
parse_version(&OPENSSL_RE, "LibreSSL 3.3.6"),
@ -1673,7 +1577,6 @@ mod tests {
}
#[test]
#[cfg(feature = "server")]
fn parse_version_node() {
assert_eq!(
parse_version(&NODE_RE, "v22.14.0"),
@ -1682,7 +1585,6 @@ mod tests {
}
#[test]
#[cfg(feature = "server")]
fn parse_version_dot() {
assert_eq!(
parse_version(&DOT_RE, "dot - graphviz version 12.2.1 (20241206.2024)"),
@ -1691,7 +1593,6 @@ mod tests {
}
#[test]
#[cfg(feature = "server")]
fn parse_version_garbage_returns_none() {
assert_eq!(parse_version(&OPENSSL_RE, "not a version"), None);
assert_eq!(parse_version(&NODE_RE, "node not found"), None);
@ -1700,10 +1601,8 @@ mod tests {
// -- check_system_deps (server only) --
#[cfg(feature = "server")]
static TEST_RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"unused").unwrap());
#[cfg(feature = "server")]
fn spec(name: &'static str, required: bool, min_version: Version) -> DepSpec {
DepSpec {
name,
@ -1715,7 +1614,6 @@ mod tests {
}
#[test]
#[cfg(feature = "server")]
fn check_system_deps_all_present() {
let specs = [
spec("openssl", true, Version::new(3, 0, 0)),
@ -1743,7 +1641,6 @@ mod tests {
}
#[test]
#[cfg(feature = "server")]
fn check_system_deps_required_missing_is_error() {
let specs = [spec("openssl", true, Version::new(3, 0, 0))];
let outcomes = [ProbeOutcome::NotFound];
@ -1753,7 +1650,6 @@ mod tests {
}
#[test]
#[cfg(feature = "server")]
fn check_system_deps_optional_missing_is_warning() {
let specs = [spec("gh", false, Version::new(2, 0, 0))];
let outcomes = [ProbeOutcome::NotFound];
@ -1763,7 +1659,6 @@ mod tests {
}
#[test]
#[cfg(feature = "server")]
fn check_system_deps_outdated_is_warning() {
let specs = [spec("openssl", true, Version::new(3, 0, 0))];
let outcomes = [ProbeOutcome::Ok {
@ -1776,7 +1671,6 @@ mod tests {
}
#[test]
#[cfg(feature = "server")]
fn check_system_deps_unparseable_success_is_pass() {
let specs = [spec("openssl", true, Version::new(3, 0, 0))];
let outcomes = [ProbeOutcome::Ok { version: None }];
@ -1786,7 +1680,6 @@ mod tests {
}
#[test]
#[cfg(feature = "server")]
fn check_system_deps_required_command_failed_is_error() {
let specs = [spec("node", true, Version::new(20, 0, 0))];
let outcomes = [ProbeOutcome::Failed];
@ -1796,7 +1689,6 @@ mod tests {
}
#[test]
#[cfg(feature = "server")]
fn check_system_deps_optional_command_failed_is_warning() {
let specs = [spec("gh", false, Version::new(2, 0, 0))];
let outcomes = [ProbeOutcome::Failed];
@ -1806,7 +1698,6 @@ mod tests {
}
#[test]
#[cfg(feature = "server")]
fn check_system_deps_error_beats_warning() {
let specs = [
spec("openssl", true, Version::new(3, 0, 0)),
@ -1819,7 +1710,6 @@ mod tests {
// -- check_crypto --
#[cfg(feature = "server")]
mod server_crypto_tests {
use super::*;

View file

@ -1,7 +1,5 @@
use anyhow::Result;
#[cfg(feature = "server")]
use fabro_agent::cli::run_with_args_and_client;
use fabro_agent::cli::{AgentArgs, OutputFormat, run_with_args};
use fabro_agent::cli::{AgentArgs, OutputFormat, run_with_args, run_with_args_and_client};
use fabro_config::mcp::McpServerEntry;
use fabro_mcp::config::McpServerSettings;
@ -22,7 +20,6 @@ pub(crate) async fn execute(mut args: AgentArgs, globals: &GlobalArgs) -> Result
if globals.json {
args.output_format = Some(OutputFormat::Json);
}
#[cfg(feature = "server")]
let resolved = user_config::resolve_mode(
globals.storage_dir.as_deref(),
globals.server_url.as_deref(),
@ -33,40 +30,31 @@ pub(crate) async fn execute(mut args: AgentArgs, globals: &GlobalArgs) -> Result
.into_iter()
.map(|(name, entry): (String, McpServerEntry)| entry.into_config(name))
.collect();
#[cfg(feature = "server")]
{
match resolved.mode {
user_config::ExecutionMode::Server => {
tracing::info!(mode = "server", "Agent session starting");
let http_client = user_config::build_server_client(resolved.tls.as_ref())?;
let provider_name = args
.provider
.clone()
.unwrap_or_else(|| "anthropic".to_string());
let adapter = std::sync::Arc::new(fabro_llm::providers::FabroServerAdapter::new(
http_client,
&resolved.server_base_url,
&provider_name,
));
let mut client =
fabro_llm::client::Client::new(std::collections::HashMap::new(), None, vec![]);
client
.register_provider(adapter)
.await
.map_err(|e| anyhow::anyhow!("Failed to register fabro server adapter: {e}"))?;
run_with_args_and_client(args, Some(client), mcp_servers).await?
}
user_config::ExecutionMode::Standalone => {
tracing::info!(mode = "standalone", "Agent session starting");
run_with_args(args, mcp_servers).await?
}
match resolved.mode {
user_config::ExecutionMode::Server => {
tracing::info!(mode = "server", "Agent session starting");
let http_client = user_config::build_server_client(resolved.tls.as_ref())?;
let provider_name = args
.provider
.clone()
.unwrap_or_else(|| "anthropic".to_string());
let adapter = std::sync::Arc::new(fabro_llm::providers::FabroServerAdapter::new(
http_client,
&resolved.server_base_url,
&provider_name,
));
let mut client =
fabro_llm::client::Client::new(std::collections::HashMap::new(), None, vec![]);
client
.register_provider(adapter)
.await
.map_err(|e| anyhow::anyhow!("Failed to register fabro server adapter: {e}"))?;
run_with_args_and_client(args, Some(client), mcp_servers).await?
}
user_config::ExecutionMode::Standalone => {
tracing::info!(mode = "standalone", "Agent session starting");
run_with_args(args, mcp_servers).await?
}
}
#[cfg(not(feature = "server"))]
{
let _ = globals;
tracing::info!(mode = "standalone", "Agent session starting");
run_with_args(args, mcp_servers).await?;
}
Ok(())

View file

@ -1,4 +1,3 @@
#[cfg(feature = "server")]
use std::io::Write as _;
use std::net::SocketAddr;
use std::path::Path;
@ -29,11 +28,10 @@ use crate::shared::provider_auth::{
};
// ---------------------------------------------------------------------------
// OpenSSL helpers (server mode only)
// OpenSSL helpers
// ---------------------------------------------------------------------------
/// Run an openssl subcommand and return stdout on success.
#[cfg(feature = "server")]
fn run_openssl(args: &[&str], description: &str) -> Result<Vec<u8>> {
let output = Command::new("openssl")
.args(args)
@ -49,7 +47,6 @@ fn run_openssl(args: &[&str], description: &str) -> Result<Vec<u8>> {
}
/// Run an openssl subcommand that reads key material from stdin.
#[cfg(feature = "server")]
fn run_openssl_with_stdin(args: &[&str], stdin_data: &[u8], description: &str) -> Result<Vec<u8>> {
let mut child = Command::new("openssl")
.args(args)
@ -77,10 +74,9 @@ fn run_openssl_with_stdin(args: &[&str], stdin_data: &[u8], description: &str) -
}
// ---------------------------------------------------------------------------
// Session secret (server mode only)
// Session secret
// ---------------------------------------------------------------------------
#[cfg(feature = "server")]
fn generate_session_secret() -> String {
let mut rng = rand::thread_rng();
let bytes: [u8; 32] = rng.gen();
@ -88,10 +84,9 @@ fn generate_session_secret() -> String {
}
// ---------------------------------------------------------------------------
// JWT keypair generation (server mode only)
// JWT keypair generation
// ---------------------------------------------------------------------------
#[cfg(feature = "server")]
fn generate_jwt_keypair() -> Result<(String, String)> {
let private_pem = run_openssl(&["genpkey", "-algorithm", "Ed25519"], "generate keypair")?;
let public_pem =
@ -103,10 +98,9 @@ fn generate_jwt_keypair() -> Result<(String, String)> {
}
// ---------------------------------------------------------------------------
// mTLS certificate generation (server mode only)
// mTLS certificate generation
// ---------------------------------------------------------------------------
#[cfg(feature = "server")]
fn generate_mtls_certs(dir: &Path) -> Result<()> {
std::fs::create_dir_all(dir).context("failed to create certs directory")?;
@ -185,10 +179,9 @@ fn generate_mtls_certs(dir: &Path) -> Result<()> {
}
// ---------------------------------------------------------------------------
// Config TOML generation (server mode only)
// Config TOML generation
// ---------------------------------------------------------------------------
#[cfg(feature = "server")]
fn format_config_toml(username: &str) -> String {
format!(
r#"[web]
@ -229,7 +222,6 @@ fn detect_binary_on_path(binary: &str) -> bool {
// Interactive setup
// ---------------------------------------------------------------------------
#[cfg(feature = "server")]
fn prompt_input(prompt: &str) -> Result<String> {
Ok(dialoguer::Input::with_theme(&ColorfulTheme::default())
.with_prompt(prompt)
@ -508,8 +500,7 @@ pub(crate) async fn run_install(web_url: &str, globals: &GlobalArgs) -> Result<(
.join(".fabro");
std::fs::create_dir_all(&arc_dir)?;
// Pre-flight checks (server mode only — standalone doesn't need openssl/node/dot)
#[cfg(feature = "server")]
// Pre-flight checks
{
eprintln!(
" {}",
@ -680,8 +671,7 @@ pub(crate) async fn run_install(web_url: &str, globals: &GlobalArgs) -> Result<(
}
eprintln!();
// Server configuration (server mode only)
#[cfg(feature = "server")]
// Server configuration
{
eprintln!(" {}", s.bold.apply_to("Server · Configuration"));
eprintln!(" {}", s.dim.apply_to("─────────────────────"));
@ -713,8 +703,7 @@ pub(crate) async fn run_install(web_url: &str, globals: &GlobalArgs) -> Result<(
eprintln!();
}
// Secrets and certificates (server mode only)
#[cfg(feature = "server")]
// Secrets and certificates
{
eprintln!(
" {}",
@ -772,10 +761,9 @@ pub(crate) async fn run_install(web_url: &str, globals: &GlobalArgs) -> Result<(
}
// ---------------------------------------------------------------------------
// Hex encoding (server mode only — used by generate_session_secret)
// Hex encoding (used by generate_session_secret)
// ---------------------------------------------------------------------------
#[cfg(feature = "server")]
mod hex {
pub fn encode(bytes: &[u8]) -> String {
bytes.iter().map(|b| format!("{b:02x}")).collect()
@ -802,33 +790,29 @@ mod tests {
assert!(!detect_binary_on_path("arc_nonexistent_xyz"));
}
// -- Session secret (server only) --
// -- Session secret --
#[test]
#[cfg(feature = "server")]
fn session_secret_length() {
let secret = generate_session_secret();
assert_eq!(secret.len(), 64);
}
#[test]
#[cfg(feature = "server")]
fn session_secret_is_hex() {
let secret = generate_session_secret();
assert!(secret.chars().all(|c| c.is_ascii_hexdigit()));
}
#[test]
#[cfg(feature = "server")]
fn session_secret_is_lowercase() {
let secret = generate_session_secret();
assert!(secret.chars().all(|c| !c.is_ascii_uppercase()));
}
// -- JWT keypair (server only) --
// -- JWT keypair --
#[test]
#[cfg(feature = "server")]
fn jwt_keypair_private_pem_header() {
let (private, _) = generate_jwt_keypair().unwrap();
assert!(
@ -838,7 +822,6 @@ mod tests {
}
#[test]
#[cfg(feature = "server")]
fn jwt_keypair_public_pem_header() {
let (_, public) = generate_jwt_keypair().unwrap();
assert!(
@ -848,16 +831,14 @@ mod tests {
}
#[test]
#[cfg(feature = "server")]
fn jwt_keypair_public_parses() {
let (_, public) = generate_jwt_keypair().unwrap();
jsonwebtoken::DecodingKey::from_ed_pem(public.as_bytes()).expect("public key should parse");
}
// -- mTLS cert generation (server only) --
// -- mTLS cert generation --
#[test]
#[cfg(feature = "server")]
fn mtls_certs_creates_files() {
let dir = tempfile::tempdir().unwrap();
let certs_dir = dir.path().join("certs");
@ -870,7 +851,6 @@ mod tests {
}
#[test]
#[cfg(feature = "server")]
fn mtls_ca_cert_is_pem() {
let dir = tempfile::tempdir().unwrap();
let certs_dir = dir.path().join("certs");
@ -884,7 +864,6 @@ mod tests {
}
#[test]
#[cfg(feature = "server")]
fn mtls_server_cert_is_pem() {
let dir = tempfile::tempdir().unwrap();
let certs_dir = dir.path().join("certs");
@ -898,7 +877,6 @@ mod tests {
}
#[test]
#[cfg(feature = "server")]
fn mtls_certs_parse_via_rustls() {
let dir = tempfile::tempdir().unwrap();
let certs_dir = dir.path().join("certs");
@ -919,10 +897,9 @@ mod tests {
assert_eq!(server_certs.len(), 1);
}
// -- Config TOML generation (server only) --
// -- Config TOML generation --
#[test]
#[cfg(feature = "server")]
fn config_toml_roundtrips() {
let toml_str = format_config_toml("brynary");
let settings: fabro_types::Settings =
@ -934,7 +911,6 @@ mod tests {
}
#[test]
#[cfg(feature = "server")]
fn config_toml_has_auth_strategies() {
let toml_str = format_config_toml("alice");
let settings: fabro_types::Settings = toml::from_str(&toml_str).unwrap();
@ -948,7 +924,6 @@ mod tests {
}
#[test]
#[cfg(feature = "server")]
fn config_toml_has_tls_paths() {
use std::path::PathBuf;
let toml_str = format_config_toml("bob");

View file

@ -1,7 +1,5 @@
use anyhow::Result;
use fabro_llm::cli::{ChatArgs, run_chat};
#[cfg(feature = "server")]
use fabro_llm::cli::{ServerConnection, run_chat_via_server};
use fabro_llm::cli::{ChatArgs, ServerConnection, run_chat, run_chat_via_server};
use fabro_types::Settings;
use crate::args::GlobalArgs;
@ -17,32 +15,23 @@ pub(super) async fn execute(
args.model = llm_defaults.and_then(|l| l.model.clone());
}
#[cfg(feature = "server")]
{
let resolved = crate::user_config::resolve_mode(
globals.storage_dir.as_deref(),
globals.server_url.as_deref(),
cli_settings,
);
match resolved.mode {
crate::user_config::ExecutionMode::Server => {
let client = crate::user_config::build_server_client(resolved.tls.as_ref())?;
let server = ServerConnection {
client,
base_url: resolved.server_base_url,
};
run_chat_via_server(args, &server).await?;
}
crate::user_config::ExecutionMode::Standalone => {
run_chat(args).await?;
}
let resolved = crate::user_config::resolve_mode(
globals.storage_dir.as_deref(),
globals.server_url.as_deref(),
cli_settings,
);
match resolved.mode {
crate::user_config::ExecutionMode::Server => {
let client = crate::user_config::build_server_client(resolved.tls.as_ref())?;
let server = ServerConnection {
client,
base_url: resolved.server_base_url,
};
run_chat_via_server(args, &server).await?;
}
crate::user_config::ExecutionMode::Standalone => {
run_chat(args).await?;
}
}
#[cfg(not(feature = "server"))]
{
let _ = globals;
run_chat(args).await?;
}
Ok(())

View file

@ -1,7 +1,5 @@
use anyhow::Result;
use fabro_llm::cli::{PromptArgs, run_prompt};
#[cfg(feature = "server")]
use fabro_llm::cli::{ServerConnection, run_prompt_via_server};
use fabro_llm::cli::{PromptArgs, ServerConnection, run_prompt, run_prompt_via_server};
use fabro_types::Settings;
use crate::args::GlobalArgs;
@ -16,32 +14,23 @@ pub(super) async fn execute(
args.model = llm_defaults.and_then(|l| l.model.clone());
}
#[cfg(feature = "server")]
{
let resolved = crate::user_config::resolve_mode(
globals.storage_dir.as_deref(),
globals.server_url.as_deref(),
cli_settings,
);
match resolved.mode {
crate::user_config::ExecutionMode::Server => {
let client = crate::user_config::build_server_client(resolved.tls.as_ref())?;
let server = ServerConnection {
client,
base_url: resolved.server_base_url,
};
run_prompt_via_server(args, &server, globals.json).await?;
}
crate::user_config::ExecutionMode::Standalone => {
run_prompt(args, globals.json).await?;
}
let resolved = crate::user_config::resolve_mode(
globals.storage_dir.as_deref(),
globals.server_url.as_deref(),
cli_settings,
);
match resolved.mode {
crate::user_config::ExecutionMode::Server => {
let client = crate::user_config::build_server_client(resolved.tls.as_ref())?;
let server = ServerConnection {
client,
base_url: resolved.server_base_url,
};
run_prompt_via_server(args, &server, globals.json).await?;
}
crate::user_config::ExecutionMode::Standalone => {
run_prompt(args, globals.json).await?;
}
}
#[cfg(not(feature = "server"))]
{
let _ = globals;
run_prompt(args, globals.json).await?;
}
Ok(())

View file

@ -15,7 +15,6 @@ pub(crate) mod run;
pub(crate) mod runs;
pub(crate) mod sandbox;
pub(crate) mod secret;
#[cfg(feature = "server")]
pub(crate) mod server;
pub(crate) mod skill;
pub(crate) mod store;

View file

@ -1,38 +1,25 @@
use anyhow::Result;
#[cfg(feature = "server")]
use fabro_llm::cli::ServerConnection;
use fabro_llm::cli::{ModelsCommand, run_models};
use fabro_llm::cli::{ModelsCommand, ServerConnection, run_models};
use crate::args::GlobalArgs;
#[cfg(feature = "server")]
use crate::user_config;
pub(crate) async fn execute(command: Option<ModelsCommand>, globals: &GlobalArgs) -> Result<()> {
let server = {
#[cfg(feature = "server")]
{
let cli_settings = user_config::load_user_settings_with_globals(globals)?;
let resolved = user_config::resolve_mode(
globals.storage_dir.as_deref(),
globals.server_url.as_deref(),
&cli_settings,
);
match resolved.mode {
user_config::ExecutionMode::Server => {
let client = user_config::build_server_client(resolved.tls.as_ref())?;
Some(ServerConnection {
client,
base_url: resolved.server_base_url,
})
}
user_config::ExecutionMode::Standalone => None,
}
}
#[cfg(not(feature = "server"))]
{
let _ = globals;
None
let cli_settings = user_config::load_user_settings_with_globals(globals)?;
let resolved = user_config::resolve_mode(
globals.storage_dir.as_deref(),
globals.server_url.as_deref(),
&cli_settings,
);
let server = match resolved.mode {
user_config::ExecutionMode::Server => {
let client = user_config::build_server_client(resolved.tls.as_ref())?;
Some(ServerConnection {
client,
base_url: resolved.server_base_url,
})
}
user_config::ExecutionMode::Standalone => None,
};
run_models(command, server, globals.json).await

View file

@ -10,9 +10,7 @@ mod store;
mod user_config;
use anyhow::Result;
use args::{Commands, GlobalArgs, LONG_VERSION, RunCommands};
#[cfg(feature = "server")]
use args::{ServerCommand, ServerNamespace};
use args::{Commands, GlobalArgs, LONG_VERSION, RunCommands, ServerCommand, ServerNamespace};
use clap::{CommandFactory, Parser};
use fabro_telemetry::{git, panic as tel_panic, sanitize, sender};
use fabro_util::printer::Printer;
@ -106,37 +104,24 @@ async fn main_inner() -> (String, Result<()>) {
let command_name = command.name().to_string();
let (config_log_level, upgrade_check_enabled) = {
#[cfg(feature = "server")]
if let Commands::Server(ServerNamespace {
command:
ServerCommand::Start {
serve_args: args, ..
}
| ServerCommand::Serve {
serve_args: args, ..
},
}) = command.as_ref()
{
if let Commands::Server(ServerNamespace {
command:
ServerCommand::Start {
serve_args: args, ..
}
| ServerCommand::Serve {
serve_args: args, ..
},
}) = command.as_ref()
{
match fabro_config::server::load_server_settings(args.config.as_deref()) {
Ok(server_settings) => (
server_settings.log.as_ref().and_then(|l| l.level.clone()),
false,
),
Err(err) => return (command_name, Err(err)),
}
} else {
match user_config::load_user_settings() {
Ok(cli_settings) => (
cli_settings.log.as_ref().and_then(|l| l.level.clone()),
cli_settings.upgrade_check_enabled(),
),
Err(err) => return (command_name, Err(err)),
}
match fabro_config::server::load_server_settings(args.config.as_deref()) {
Ok(server_settings) => (
server_settings.log.as_ref().and_then(|l| l.level.clone()),
false,
),
Err(err) => return (command_name, Err(err)),
}
}
#[cfg(not(feature = "server"))]
{
} else {
match user_config::load_user_settings() {
Ok(cli_settings) => (
cli_settings.log.as_ref().and_then(|l| l.level.clone()),
@ -192,7 +177,6 @@ async fn main_inner() -> (String, Result<()>) {
Commands::Store(ns) => commands::store::dispatch(ns, &globals).await?,
Commands::RunsCmd(cmd) => commands::runs::dispatch(cmd, &globals).await?,
Commands::Model { command } => commands::model::execute(command, &globals).await?,
#[cfg(feature = "server")]
Commands::Server(ns) => {
commands::server::dispatch(ns.command, &globals).await?;
}
@ -369,7 +353,6 @@ mod tests {
}
#[test]
#[cfg(feature = "server")]
fn parse_server_url_conflicts_with_storage_dir() {
let result = Cli::try_parse_from([
"fabro",

View file

@ -1,17 +1,13 @@
#[cfg(feature = "server")]
use std::path::Path;
#[allow(unused_imports)]
pub(crate) use fabro_config::user::*;
use fabro_config::ConfigLayer;
use fabro_types::Settings;
use tracing::debug;
use crate::args::GlobalArgs;
#[cfg(feature = "server")]
use tracing::debug;
pub(crate) fn load_user_settings() -> anyhow::Result<Settings> {
ConfigLayer::user()?.resolve()
}
@ -31,7 +27,6 @@ pub(crate) fn apply_global_overrides(mut layer: ConfigLayer, globals: &GlobalArg
layer.mode = Some(ExecutionMode::Standalone);
}
#[cfg(feature = "server")]
if let Some(url) = &globals.server_url {
layer.server.get_or_insert_with(Default::default).base_url = Some(url.clone());
layer.mode = Some(ExecutionMode::Server);
@ -40,7 +35,6 @@ pub(crate) fn apply_global_overrides(mut layer: ConfigLayer, globals: &GlobalArg
layer
}
#[cfg(feature = "server")]
#[derive(Debug, PartialEq)]
pub(crate) struct ResolvedMode {
pub mode: ExecutionMode,
@ -48,10 +42,8 @@ pub(crate) struct ResolvedMode {
pub tls: Option<ClientTlsSettings>,
}
#[cfg(feature = "server")]
const DEFAULT_SERVER_URL: &str = "http://localhost:3000/api/v1";
#[cfg(feature = "server")]
pub(crate) fn resolve_mode(
cli_storage_dir: Option<&Path>,
cli_server_url: Option<&str>,
@ -83,7 +75,6 @@ pub(crate) fn resolve_mode(
}
}
#[cfg(feature = "server")]
pub(crate) fn build_server_client(
tls: Option<&ClientTlsSettings>,
) -> anyhow::Result<reqwest::Client> {
@ -115,7 +106,7 @@ pub(crate) fn build_server_client(
Ok(client)
}
#[cfg(all(test, feature = "server"))]
#[cfg(test)]
mod tests {
use std::path::{Path, PathBuf};

View file

@ -25,6 +25,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -29,6 +29,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -27,6 +27,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -30,6 +30,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");
@ -472,6 +473,13 @@ fn attach_json_errors_without_prompting_for_human_input() {
"run_id": "[ULID]",
"ts": "[TIMESTAMP]"
},
{
"event": "run.running",
"id": "[EVENT_ID]",
"properties": {},
"run_id": "[ULID]",
"ts": "[TIMESTAMP]"
},
{
"event": "run.started",
"id": "[EVENT_ID]",
@ -482,13 +490,6 @@ fn attach_json_errors_without_prompting_for_human_input() {
"run_id": "[ULID]",
"ts": "[TIMESTAMP]"
},
{
"event": "run.running",
"id": "[EVENT_ID]",
"properties": {},
"run_id": "[ULID]",
"ts": "[TIMESTAMP]"
},
{
"event": "stage.started",
"id": "[EVENT_ID]",

View file

@ -23,6 +23,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -1,7 +1,6 @@
use std::path::PathBuf;
use fabro_config::mcp::McpTransport;
#[cfg(feature = "server")]
use fabro_config::user::ExecutionMode;
use fabro_test::{fabro_snapshot, test_context};
use fabro_types::Settings;
@ -32,6 +31,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");
@ -570,7 +570,6 @@ shared = "legacy"
}
#[test]
#[cfg(feature = "server")]
fn settings_server_url_overrides_cli_defaults() {
let context = test_context!();
let project = setup_settings_fixture(&context);
@ -585,6 +584,7 @@ fn settings_server_url_overrides_cli_defaults() {
let output = context
.command()
.env_remove("FABRO_STORAGE_DIR")
.current_dir(project.path())
.args(["--server-url", "https://cli.example.com", "settings"])
.assert()

View file

@ -35,6 +35,7 @@ fn help() {
--model <MODEL> Override default LLM model
--provider <PROVIDER> Override default LLM provider
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-v, --verbose Enable verbose output
--sandbox <SANDBOX> Sandbox for agent tools [possible values: local, docker, daytona]
--label <KEY=VALUE> Attach a label to this run (repeatable, format: KEY=VALUE)

View file

@ -27,6 +27,7 @@ fn help() {
--resume Resume from checkpoint instead of fresh start
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -28,6 +28,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -20,6 +20,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -20,6 +20,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -30,6 +30,7 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");
@ -60,13 +61,21 @@ fn dry_run_flag() {
[!] Cloud sandbox (no sandbox configured)
[!] Brave Search (not configured)
Found issues in 4 categories.
Server
[!] System dependencies (some issues)
[] Fabro API (http://localhost:3000/api/v1)
[] Fabro Web (http://localhost:3000)
[!] Cryptographic keys (no authentication configured)
Found issues in 6 categories.
Warnings:
Configuration Create ~/.fabro/user.toml
GitHub App Configure GitHub App in server.toml and set env vars to enable GitHub integration
Cloud sandbox Set DAYTONA_API_KEY to enable cloud sandbox execution
Brave Search Set BRAVE_SEARCH_API_KEY to enable web search
System dependencies Install missing system dependencies
Cryptographic keys Configure authentication_strategies in [api] section of server.toml
----- stderr -----
");
}

View file

@ -35,6 +35,7 @@ fn help() {
--debug Print LLM request/response debug info to stderr
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--verbose Print full LLM request/response JSON to stderr
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
--skills-dir <SKILLS_DIR> Directory containing skill files (overrides default discovery)
--output-format <OUTPUT_FORMAT> Output format (text for human-readable, json for NDJSON event stream) [possible values: text, json]
-h, --help Print help

View file

@ -29,6 +29,7 @@ fn help() {
rm Remove one or more workflow runs
inspect Show detailed information about a workflow run
model List and test LLM models
server Server operations
doctor Check environment and integration health
install Set up the Fabro environment (LLMs, certs, GitHub)
pr Pull request operations
@ -52,6 +53,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
-V, --version Print version
----- stderr -----

View file

@ -33,6 +33,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -66,6 +66,11 @@ fn help() {
[env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL>
Server URL (overrides server.base_url from user.toml)
[env: FABRO_SERVER_URL=]
-h, --help
Print help (see a summary with '-h')
----- stderr -----

View file

@ -30,6 +30,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");
@ -68,7 +69,7 @@ fn inspect_completed_run_shows_run_start_conclusion_checkpoint() {
let run = setup_completed_dry_run(&context);
let output = run_success(&context, &["inspect", &run.run_id]);
assert_snapshot!(serde_json::to_string_pretty(&compact_inspect(&output)).unwrap(), @r###"
assert_snapshot!(serde_json::to_string_pretty(&compact_inspect(&output)).unwrap(), @r#"
[
{
"run_id": "[ULID]",
@ -86,7 +87,7 @@ fn inspect_completed_run_shows_run_start_conclusion_checkpoint() {
"conclusion": {
"status": "success",
"duration_ms": "[DURATION_MS]",
"stage_count": 3
"stage_count": null
},
"checkpoint": {
"current_node": "report",
@ -102,7 +103,7 @@ fn inspect_completed_run_shows_run_start_conclusion_checkpoint() {
}
}
]
"###);
"#);
}
#[test]
@ -120,7 +121,7 @@ fn inspect_completed_run_reads_store_without_disk_metadata_files() {
}
let output = run_success(&context, &["inspect", &run.run_id]);
assert_snapshot!(serde_json::to_string_pretty(&compact_inspect(&output)).unwrap(), @r###"
assert_snapshot!(serde_json::to_string_pretty(&compact_inspect(&output)).unwrap(), @r#"
[
{
"run_id": "[ULID]",
@ -138,7 +139,7 @@ fn inspect_completed_run_reads_store_without_disk_metadata_files() {
"conclusion": {
"status": "success",
"duration_ms": "[DURATION_MS]",
"stage_count": 3
"stage_count": null
},
"checkpoint": {
"current_node": "report",
@ -154,7 +155,7 @@ fn inspect_completed_run_reads_store_without_disk_metadata_files() {
}
}
]
"###);
"#);
}
#[test]
@ -165,7 +166,7 @@ fn inspect_git_backed_run_exposes_checkpoint_and_sandbox_state() {
assert_snapshot!(
serde_json::to_string_pretty(&compact_git_inspect(&output)).unwrap(),
@r###"
@r#"
[
{
"run_id": "[ULID]",
@ -186,7 +187,7 @@ fn inspect_git_backed_run_exposes_checkpoint_and_sandbox_state() {
"status": "success",
"duration_ms": "[DURATION_MS]",
"final_git_commit_sha": "[SHA]",
"stage_count": 3
"stage_count": null
},
"checkpoint": {
"current_node": "step_two",
@ -204,6 +205,6 @@ fn inspect_git_backed_run_exposes_checkpoint_and_sandbox_state() {
}
}
]
"###
"#
);
}

View file

@ -21,6 +21,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -1,7 +1,5 @@
use fabro_test::{fabro_snapshot, test_context};
#[cfg(feature = "server")]
use httpmock::prelude::*;
#[cfg(feature = "server")]
use serde_json::Value;
#[test]
@ -33,13 +31,13 @@ fn help() {
--verbose Enable verbose output [env: FABRO_VERBOSE=]
-o, --option <OPTION> key=value options (temperature, `max_tokens`, `top_p`)
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");
}
#[test]
#[cfg(feature = "server")]
fn prompt_json_streaming_server_reports_resolved_model() {
let context = test_context!();
let server = MockServer::start();

View file

@ -29,6 +29,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
"#);
@ -66,10 +67,10 @@ fn logs_completed_run_outputs_raw_ndjson() {
{"event":"run.submitted","id":"[EVENT_ID]","properties":{},"run_id":"[ULID]","ts":"[TIMESTAMP]"}
{"event":"run.starting","id":"[EVENT_ID]","properties":{"reason":"sandbox_initializing"},"run_id":"[ULID]","ts":"[TIMESTAMP]"}
{"event":"sandbox.initializing","id":"[EVENT_ID]","properties":{"provider":"local"},"run_id":"[ULID]","ts":"[TIMESTAMP]"}
{"event":"run.running","id":"[EVENT_ID]","properties":{},"run_id":"[ULID]","ts":"[TIMESTAMP]"}
{"event":"sandbox.ready","id":"[EVENT_ID]","properties":{"cpu":null,"duration_ms": [DURATION_MS],"memory":null,"name":null,"provider":"local","url":null},"run_id":"[ULID]","ts":"[TIMESTAMP]"}
{"event":"sandbox.initialized","id":"[EVENT_ID]","properties":{"provider":"local","working_directory":"[TEMP_DIR]"},"run_id":"[ULID]","ts":"[TIMESTAMP]"}
{"event":"run.started","id":"[EVENT_ID]","properties":{"goal":"Run tests and report results","name":"Simple"},"run_id":"[ULID]","ts":"[TIMESTAMP]"}
{"event":"run.running","id":"[EVENT_ID]","properties":{},"run_id":"[ULID]","ts":"[TIMESTAMP]"}
{"event":"stage.started","id":"[EVENT_ID]","node_id":"start","node_label":"Start","properties":{"attempt":1,"handler_type":"start","index":0,"max_attempts":1},"run_id":"[ULID]","ts":"[TIMESTAMP]"}
{"event":"stage.completed","id":"[EVENT_ID]","node_id":"start","node_label":"Start","properties":{"attempt":1,"context_values":{"current.preamble":"Goal: Run tests and report results/n","current_node":"start","graph.goal":"Run tests and report results","graph.rankdir":"LR","internal.fidelity":"compact","internal.node_visit_count":1,"internal.run_id":"[ULID]","internal.thread_id":null},"duration_ms": [DURATION_MS],"files_touched":[],"index":0,"max_attempts":1,"node_visits":{"start":1},"notes":"[Simulated] start","preferred_label":null,"status":"success","suggested_next_ids":[],"usage":null},"run_id":"[ULID]","ts":"[TIMESTAMP]"}
{"event":"edge.selected","id":"[EVENT_ID]","properties":{"condition":null,"from_node":"start","is_jump":false,"label":null,"reason":"unconditional","stage_status":"success","to_node":"run_tests"},"run_id":"[ULID]","ts":"[TIMESTAMP]"}
@ -231,10 +232,10 @@ fn logs_follow_detached_run_streams_until_completion() {
{"event":"run.submitted","id":"[EVENT_ID]","properties":{},"run_id":"[ULID]","ts":"[TIMESTAMP]"}
{"event":"run.starting","id":"[EVENT_ID]","properties":{"reason":"sandbox_initializing"},"run_id":"[ULID]","ts":"[TIMESTAMP]"}
{"event":"sandbox.initializing","id":"[EVENT_ID]","properties":{"provider":"local"},"run_id":"[ULID]","ts":"[TIMESTAMP]"}
{"event":"run.running","id":"[EVENT_ID]","properties":{},"run_id":"[ULID]","ts":"[TIMESTAMP]"}
{"event":"sandbox.ready","id":"[EVENT_ID]","properties":{"cpu":null,"duration_ms": [DURATION_MS],"memory":null,"name":null,"provider":"local","url":null},"run_id":"[ULID]","ts":"[TIMESTAMP]"}
{"event":"sandbox.initialized","id":"[EVENT_ID]","properties":{"provider":"local","working_directory":"[TEMP_DIR]"},"run_id":"[ULID]","ts":"[TIMESTAMP]"}
{"event":"run.started","id":"[EVENT_ID]","properties":{"goal":"Run tests and report results","name":"Simple"},"run_id":"[ULID]","ts":"[TIMESTAMP]"}
{"event":"run.running","id":"[EVENT_ID]","properties":{},"run_id":"[ULID]","ts":"[TIMESTAMP]"}
{"event":"stage.started","id":"[EVENT_ID]","node_id":"start","node_label":"Start","properties":{"attempt":1,"handler_type":"start","index":0,"max_attempts":1},"run_id":"[ULID]","ts":"[TIMESTAMP]"}
{"event":"stage.completed","id":"[EVENT_ID]","node_id":"start","node_label":"Start","properties":{"attempt":1,"context_values":{"current.preamble":"Goal: Run tests and report results/n","current_node":"start","graph.goal":"Run tests and report results","graph.rankdir":"LR","internal.fidelity":"compact","internal.node_visit_count":1,"internal.run_id":"[ULID]","internal.thread_id":null},"duration_ms": [DURATION_MS],"files_touched":[],"index":0,"max_attempts":1,"node_visits":{"start":1},"notes":"[Simulated] start","preferred_label":null,"status":"success","suggested_next_ids":[],"usage":null},"run_id":"[ULID]","ts":"[TIMESTAMP]"}
{"event":"edge.selected","id":"[EVENT_ID]","properties":{"condition":null,"from_node":"start","is_jump":false,"label":null,"reason":"unconditional","stage_status":"success","to_node":"run_tests"},"run_id":"[ULID]","ts":"[TIMESTAMP]"}

View file

@ -25,6 +25,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -22,6 +22,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -23,6 +23,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -23,6 +23,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -28,6 +28,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -23,6 +23,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -26,6 +26,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -21,6 +21,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -24,6 +24,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -46,6 +46,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -31,6 +31,7 @@ fn help() {
-v, --verbose Enable verbose output
--sandbox <SANDBOX> Sandbox for agent tools [possible values: local, docker, daytona]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -24,6 +24,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -21,6 +21,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -27,6 +27,7 @@ fn help() {
--verbose Enable verbose output [env: FABRO_VERBOSE=]
-q, --quiet Only display run IDs
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -32,6 +32,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");
@ -112,6 +113,7 @@ fn test_repo_init_help_does_not_show_skill() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -20,6 +20,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -22,6 +22,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -26,6 +26,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -33,6 +33,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");
@ -68,8 +69,7 @@ fn rewind_list_prints_timeline_for_completed_git_run() {
----- stdout -----
----- stderr -----
@ Node Details
@1 step_one
@2 step_two
@1 step_one
");
}
@ -94,8 +94,8 @@ fn rewind_target_updates_metadata_and_resume_hint() {
exit_code: 0
----- stdout -----
----- stderr -----
Rewound metadata branch to @1 (step_one)
Rewound run branch fabro/run/[ULID] to [SHA]
Rewound metadata branch to @1 (start)
Warning: checkpoint @1 has no git_commit_sha; run branch not moved
To resume: fabro resume [RUN_PREFIX]
");

View file

@ -28,6 +28,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -31,6 +31,7 @@ fn help() {
--model <MODEL> Override default LLM model
--provider <PROVIDER> Override default LLM provider
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-v, --verbose Enable verbose output
--sandbox <SANDBOX> Sandbox for agent tools [possible values: local, docker, daytona]
--label <KEY=VALUE> Attach a label to this run (repeatable, format: KEY=VALUE)
@ -395,6 +396,13 @@ fn json_run_implies_auto_approve_for_human_gates() {
"run_id": "[ULID]",
"ts": "[TIMESTAMP]"
},
{
"event": "run.running",
"id": "[EVENT_ID]",
"properties": {},
"run_id": "[ULID]",
"ts": "[TIMESTAMP]"
},
{
"event": "sandbox.ready",
"id": "[EVENT_ID]",
@ -429,13 +437,6 @@ fn json_run_implies_auto_approve_for_human_gates() {
"run_id": "[ULID]",
"ts": "[TIMESTAMP]"
},
{
"event": "run.running",
"id": "[EVENT_ID]",
"properties": {},
"run_id": "[ULID]",
"ts": "[TIMESTAMP]"
},
{
"event": "stage.started",
"id": "[EVENT_ID]",

View file

@ -27,6 +27,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -29,6 +29,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -27,6 +27,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -28,6 +28,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -23,6 +23,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -22,6 +22,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -23,6 +23,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -24,6 +24,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -23,6 +23,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -23,6 +23,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -1,7 +1,6 @@
use fabro_test::{fabro_snapshot, test_context};
#[test]
#[cfg(feature = "server")]
fn help() {
let context = test_context!();
let mut cmd = context.command();
@ -52,7 +51,6 @@ fn help() {
}
#[test]
#[cfg(feature = "server")]
fn start_already_running_exits_with_error() {
let context = test_context!();

View file

@ -1,7 +1,6 @@
use fabro_test::{fabro_snapshot, test_context};
#[test]
#[cfg(feature = "server")]
fn help() {
let context = test_context!();
let mut cmd = context.command();
@ -28,7 +27,6 @@ fn help() {
}
#[test]
#[cfg(feature = "server")]
fn status_when_not_running() {
let context = test_context!();
let mut cmd = context.command();

View file

@ -1,7 +1,6 @@
use fabro_test::{fabro_snapshot, test_context};
#[test]
#[cfg(feature = "server")]
fn help() {
let context = test_context!();
let mut cmd = context.command();
@ -29,7 +28,6 @@ fn help() {
}
#[test]
#[cfg(feature = "server")]
fn stop_when_not_running() {
let context = test_context!();
let mut cmd = context.command();

View file

@ -27,6 +27,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -24,6 +24,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -28,6 +28,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");
@ -51,7 +52,7 @@ fn store_dump_exports_completed_run_snapshot() {
success: true
exit_code: 0
----- stdout -----
Exported 14 files for run [ULID] to [TEMP_DIR]/export
Exported 17 files for run [ULID] to [TEMP_DIR]/export
----- stderr -----
");

View file

@ -25,6 +25,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -23,6 +23,7 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -28,6 +28,7 @@ fn help() {
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--yes Actually delete (default is dry-run)
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -23,6 +23,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
"#);

View file

@ -30,6 +30,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -27,6 +27,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -25,6 +25,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -31,6 +31,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -22,6 +22,7 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -1,7 +1,6 @@
use fabro_test::{fabro_snapshot, test_context};
#[test]
#[cfg(feature = "server")]
fn start_status_stop_lifecycle() {
let context = test_context!();