refactor(cli): remove global execution mode

This commit is contained in:
Bryan Helmkamp 2026-04-05 15:20:00 -04:00
parent b25613271f
commit cdddcdeadf
84 changed files with 520 additions and 338 deletions

View file

@ -110,11 +110,9 @@ Send the `X-Fabro-Demo: 1` header on any API request to get static mock data wit
## Pointing the CLI at a server
The CLI can delegate commands to a running Fabro server instead of executing locally. Set `mode = "server"` in `~/.fabro/user.toml`:
The CLI can target a running Fabro server for commands that support a remote API. Configure `~/.fabro/user.toml`:
```toml title="user.toml"
mode = "server"
[server]
base_url = "https://fabro.example.com:3000/api/v1"
```
@ -125,7 +123,9 @@ Or use the `--server-url` flag:
fabro --server-url https://fabro.example.com:3000/api/v1 model list
```
This applies to commands like `fabro model list`, `fabro model test`, and `fabro exec`. See [User Configuration](/reference/user-configuration#mode) for the full options including mTLS setup.
`fabro model list` and `fabro model test` honor `[server].base_url` by default unless you explicitly pass `--storage-dir`. `fabro exec` remains a local agent session and only uses the server when you pass `--server-url`.
See [User Configuration](/reference/user-configuration#server-section) for the full connection options, including mTLS setup.
## Next steps

View file

@ -11,8 +11,8 @@ These flags apply to all subcommands:
|---|---|
| `--debug` | Enable DEBUG-level logging (default is INFO) |
| `--no-upgrade-check` | Skip the automatic background upgrade check |
| `--storage-dir <DIR>` | Storage directory for local run data (default: `~/.fabro`). Implies standalone mode. |
| `--server-url <URL>` | Fabro API server URL (overrides `server.base_url` from `user.toml`). Implies server mode. |
| `--storage-dir <DIR>` | Local storage directory for Fabro data (default: `~/.fabro`). |
| `--server-url <URL>` | Fabro API server URL for commands that support a remote target (overrides `server.base_url` from `user.toml`). |
| `-h, --help` | Print help |
| `-V, --version` | Print version |
@ -31,9 +31,14 @@ output_format = "text"
[llm]
model = "claude-sonnet-4-5"
[server]
base_url = "https://fabro.example.com:3000/api/v1"
```
`[exec]` config applies to `fabro exec`. `[llm]` sets the default workflow model/provider for commands like `fabro run` and `fabro preflight`.
`[exec]` config applies to `fabro exec`. `[llm]` sets the default workflow model/provider for commands like `fabro run` and `fabro preflight`. `[server]` stores connection info for commands that can target a remote Fabro API.
`fabro model` uses `[server].base_url` by default when no explicit `--storage-dir` is passed. `fabro exec` remains a local session unless you pass `--server-url`, even if `[server].base_url` is configured.
CLI flags always override `user.toml` values, which override hardcoded defaults.

View file

@ -22,7 +22,6 @@ CLI flags always take the highest priority:
```toml title="user.toml"
verbose = true
upgrade_check = true
mode = "server"
[server]
base_url = "https://fabro.example.com:3000/api/v1"
@ -148,31 +147,22 @@ Customize the git author identity used for checkpoint commits. Overrides the ser
| `name` | Git author name | `"fabro"` |
| `email` | Git author email | `"fabro@local"` |
## `mode`
Controls the default execution mode when neither `--storage-dir` nor `--server-url` is passed.
| Value | Description |
|---|---|
| `"standalone"` | Execute locally (default) |
| `"server"` | Delegate to an Fabro API server |
For a single invocation, `--storage-dir` implies standalone mode and `--server-url` implies server mode.
## `[server]` section
Configuration for server mode.
Connection info for commands that can target a remote Fabro API server.
| Key | Description | Default |
|---|---|---|
| `base_url` | Server URL | `"http://localhost:3000/api/v1"` |
Passing `--server-url` implies server mode and overrides `server.base_url`:
`fabro model` uses `[server].base_url` by default when no explicit `--storage-dir` is passed. `--server-url` overrides `server.base_url`:
```bash
fabro --server-url https://fabro.example.com:3000/api/v1 model list
```
`fabro exec` does not automatically use `[server].base_url`. It only routes model traffic through a Fabro server when you pass `--server-url` for that invocation.
### `[server.tls]` section
Optional mTLS configuration for authenticating with the server. When present, the CLI presents a client certificate during the TLS handshake.

View file

@ -36,11 +36,11 @@ pub(crate) struct GlobalArgs {
#[arg(long, global = true, env = "FABRO_VERBOSE", value_parser = clap::builder::BoolishValueParser::new(), conflicts_with = "quiet")]
pub verbose: bool,
/// Storage directory (default: ~/.fabro)
/// Local storage directory (default: ~/.fabro)
#[arg(long, global = true, env = "FABRO_STORAGE_DIR")]
pub storage_dir: Option<PathBuf>,
/// Server URL (overrides server.base_url from user.toml)
/// Fabro API server URL (overrides server.base_url from user.toml when supported)
#[arg(
long,
global = true,

View file

@ -24,40 +24,33 @@ pub(crate) async fn execute(mut args: AgentArgs, globals: &GlobalArgs) -> Result
if globals.json {
args.output_format = Some(OutputFormat::Json);
}
let resolved = user_config::resolve_mode(
globals.storage_dir.as_deref(),
globals.server_url.as_deref(),
&cli_settings,
);
let server_target = user_config::exec_server_target(globals, &cli_settings);
let mcp_servers: Vec<McpServerSettings> = cli_settings
.mcp_servers
.into_iter()
.map(|(name, entry): (String, McpServerEntry)| entry.into_config(name))
.collect();
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 = Arc::new(FabroServerAdapter::new(
http_client,
&resolved.server_base_url,
&provider_name,
));
let mut client = Client::new(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?;
}
if let Some(target) = server_target {
tracing::info!(transport = "server", "Agent session starting");
let http_client = user_config::build_server_client(target.tls.as_ref())?;
let provider_name = args
.provider
.clone()
.unwrap_or_else(|| "anthropic".to_string());
let adapter = Arc::new(FabroServerAdapter::new(
http_client,
&target.server_base_url,
&provider_name,
));
let mut client = Client::new(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?;
} else {
tracing::info!(transport = "direct", "Agent session starting");
run_with_args(args, mcp_servers).await?;
}
Ok(())

View file

@ -39,13 +39,9 @@ struct ModelTestOutput {
pub(crate) async fn execute(command: Option<ModelsCommand>, globals: &GlobalArgs) -> Result<()> {
let cli_settings = user_config::load_user_settings_with_globals(globals)?;
let client = match globals.server_url.as_deref() {
Some(base_url) => {
let tls = cli_settings
.server
.as_ref()
.and_then(|server| server.tls.as_ref());
server_client::connect_remote_api_client(base_url, tls)?
let client = match user_config::model_server_target(globals, &cli_settings) {
Some(target) => {
server_client::connect_remote_api_client(&target.server_base_url, target.tls.as_ref())?
}
None => server_client::connect_api_client(&cli_settings.storage_dir()).await?,
};

View file

@ -1,5 +1,3 @@
use std::path::Path;
pub(crate) use fabro_config::user::*;
use fabro_config::ConfigLayer;
@ -24,55 +22,70 @@ pub(crate) fn load_user_settings_with_globals(globals: &GlobalArgs) -> anyhow::R
pub(crate) fn apply_global_overrides(mut layer: ConfigLayer, globals: &GlobalArgs) -> ConfigLayer {
if let Some(dir) = &globals.storage_dir {
layer.storage_dir = Some(dir.clone());
layer.mode = Some(ExecutionMode::Standalone);
}
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);
}
layer
}
#[derive(Debug, PartialEq)]
pub(crate) struct ResolvedMode {
pub mode: ExecutionMode,
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct ServerTarget {
pub server_base_url: String,
pub tls: Option<ClientTlsSettings>,
}
const DEFAULT_SERVER_URL: &str = "http://localhost:3000/api/v1";
fn configured_server_target(settings: &Settings) -> Option<ServerTarget> {
settings.server.as_ref().and_then(|server| {
server.base_url.clone().map(|server_base_url| ServerTarget {
server_base_url,
tls: server.tls.clone(),
})
})
}
pub(crate) fn resolve_mode(
cli_storage_dir: Option<&Path>,
cli_server_url: Option<&str>,
pub(crate) fn exec_server_target(
globals: &GlobalArgs,
settings: &Settings,
) -> ResolvedMode {
let mode = if cli_server_url.is_some() {
ExecutionMode::Server
} else if cli_storage_dir.is_some() {
ExecutionMode::Standalone
) -> Option<ServerTarget> {
let target = globals
.server_url
.as_ref()
.map(|server_base_url| ServerTarget {
server_base_url: server_base_url.clone(),
tls: settings
.server
.as_ref()
.and_then(|server| server.tls.clone()),
});
debug!(has_target = target.is_some(), "Resolved exec server target");
target
}
pub(crate) fn model_server_target(
globals: &GlobalArgs,
settings: &Settings,
) -> Option<ServerTarget> {
let target = if let Some(server_base_url) = globals.server_url.as_ref() {
Some(ServerTarget {
server_base_url: server_base_url.clone(),
tls: settings
.server
.as_ref()
.and_then(|server| server.tls.clone()),
})
} else if globals.storage_dir.is_some() {
None
} else {
settings.mode.clone().unwrap_or_default()
configured_server_target(settings)
};
let server_defaults = settings.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,
}
debug!(
has_target = target.is_some(),
"Resolved model server target"
);
target
}
pub(crate) fn build_server_client(
@ -108,63 +121,44 @@ pub(crate) fn build_server_client(
#[cfg(test)]
mod tests {
use std::path::{Path, PathBuf};
use std::path::PathBuf;
use super::*;
// --- resolve_mode precedence ---
fn globals() -> GlobalArgs {
GlobalArgs {
json: false,
debug: false,
no_upgrade_check: false,
quiet: false,
verbose: false,
storage_dir: None,
server_url: None,
}
}
#[test]
fn resolve_mode_defaults_to_standalone() {
fn exec_has_no_server_target_by_default() {
let settings = Settings::default();
let resolved = resolve_mode(None, None, &settings);
assert_eq!(resolved.mode, ExecutionMode::Standalone);
assert_eq!(resolved.server_base_url, DEFAULT_SERVER_URL);
assert_eq!(resolved.tls, None);
assert_eq!(exec_server_target(&globals(), &settings), None);
}
#[test]
fn resolve_mode_storage_dir_forces_standalone() {
let settings = Settings {
mode: Some(ExecutionMode::Server),
..Settings::default()
};
let resolved = resolve_mode(Some(Path::new("/tmp/fabro")), None, &settings);
assert_eq!(resolved.mode, ExecutionMode::Standalone);
}
#[test]
fn resolve_mode_server_url_forces_server() {
let settings = Settings {
mode: Some(ExecutionMode::Standalone),
server: Some(ServerSettings {
base_url: Some("https://config.example.com".to_string()),
fn exec_uses_cli_server_url() {
let settings = Settings::default();
let mut globals = globals();
globals.server_url = Some("https://cli.example.com".to_string());
assert_eq!(
exec_server_target(&globals, &settings),
Some(ServerTarget {
server_base_url: "https://cli.example.com".to_string(),
tls: None,
}),
..Settings::default()
};
let resolved = resolve_mode(None, Some("https://cli.example.com"), &settings);
assert_eq!(resolved.mode, ExecutionMode::Server);
assert_eq!(resolved.server_base_url, "https://cli.example.com");
})
);
}
#[test]
fn resolve_mode_config_overrides_default() {
let settings = Settings {
mode: Some(ExecutionMode::Server),
server: Some(ServerSettings {
base_url: Some("https://config.example.com".to_string()),
tls: None,
}),
..Settings::default()
};
let resolved = resolve_mode(None, None, &settings);
assert_eq!(resolved.mode, ExecutionMode::Server);
assert_eq!(resolved.server_base_url, "https://config.example.com");
}
#[test]
fn resolve_mode_cli_url_overrides_config_url() {
fn exec_ignores_configured_server_base_url_without_cli_server_url() {
let settings = Settings {
server: Some(ServerSettings {
base_url: Some("https://config.example.com".to_string()),
@ -172,12 +166,63 @@ mod tests {
}),
..Settings::default()
};
let resolved = resolve_mode(None, Some("https://cli.example.com"), &settings);
assert_eq!(resolved.server_base_url, "https://cli.example.com");
assert_eq!(exec_server_target(&globals(), &settings), None);
}
#[test]
fn resolve_mode_tls_from_config() {
fn model_uses_configured_server_base_url() {
let settings = Settings {
server: Some(ServerSettings {
base_url: Some("https://config.example.com".to_string()),
tls: None,
}),
..Settings::default()
};
assert_eq!(
model_server_target(&globals(), &settings),
Some(ServerTarget {
server_base_url: "https://config.example.com".to_string(),
tls: None,
})
);
}
#[test]
fn model_cli_server_url_overrides_config_url() {
let settings = Settings {
server: Some(ServerSettings {
base_url: Some("https://config.example.com".to_string()),
tls: None,
}),
..Settings::default()
};
let mut globals = globals();
globals.server_url = Some("https://cli.example.com".to_string());
assert_eq!(
model_server_target(&globals, &settings),
Some(ServerTarget {
server_base_url: "https://cli.example.com".to_string(),
tls: None,
})
);
}
#[test]
fn model_storage_dir_suppresses_configured_remote_target() {
let settings = Settings {
server: Some(ServerSettings {
base_url: Some("https://config.example.com".to_string()),
tls: None,
}),
..Settings::default()
};
let mut globals = globals();
globals.storage_dir = Some(PathBuf::from("/tmp/fabro"));
assert_eq!(model_server_target(&globals, &settings), None);
}
#[test]
fn remote_target_uses_tls_from_config() {
let tls = ClientTlsSettings {
cert: PathBuf::from("cert.pem"),
key: PathBuf::from("key.pem"),
@ -190,7 +235,14 @@ mod tests {
}),
..Settings::default()
};
let resolved = resolve_mode(None, None, &settings);
assert_eq!(resolved.tls, Some(tls));
let mut globals = globals();
globals.server_url = Some("https://cli.example.com".to_string());
assert_eq!(
exec_server_target(&globals, &settings),
Some(ServerTarget {
server_base_url: "https://cli.example.com".to_string(),
tls: Some(tls),
})
);
}
}

View file

@ -24,8 +24,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -28,8 +28,8 @@ fn help() {
--tree Preserve {node_slug}/retry_{N}/ directory structure
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -26,8 +26,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -31,8 +31,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");
@ -444,7 +444,6 @@ fn attach_json_errors_without_prompting_for_human_input() {
"model": "gpt-5.4",
"provider": "openai"
},
"mode": "standalone",
"no_retro": true,
"sandbox": {
"daytona": null,

View file

@ -22,8 +22,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -1,7 +1,6 @@
use std::path::PathBuf;
use fabro_config::mcp::McpTransport;
use fabro_config::user::ExecutionMode;
use fabro_test::{fabro_snapshot, test_context};
use fabro_types::Settings;
use predicates::prelude::*;
@ -31,8 +30,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");
@ -585,9 +584,7 @@ fn settings_server_url_overrides_cli_defaults() {
let existing = std::fs::read_to_string(&user_toml_path).unwrap();
context.write_home(
".fabro/user.toml",
format!(
"{existing}\nmode = \"standalone\"\n[server]\nbase_url = \"https://config.example.com\"\n"
),
format!("{existing}\n[server]\nbase_url = \"https://config.example.com\"\n"),
);
let output = context
@ -602,7 +599,6 @@ fn settings_server_url_overrides_cli_defaults() {
.clone();
let cfg = parse_settings(&output);
assert_eq!(cfg.mode, Some(ExecutionMode::Server));
assert_eq!(
cfg.server
.as_ref()

View file

@ -34,8 +34,8 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [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,8 +27,8 @@ fn help() {
--shortstat Show only files-changed/insertions/deletions summary (live diffs only)
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -19,8 +19,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -19,8 +19,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -31,8 +31,8 @@ fn help() {
--dry-run Skip live service probes (LLM, sandbox, API, web, Brave Search)
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -3,6 +3,7 @@
use std::process::Output;
use fabro_test::{fabro_snapshot, test_context};
use httpmock::MockServer;
async fn run_success_output(mut cmd: assert_cmd::Command) -> Output {
tokio::task::spawn_blocking(move || cmd.assert().success().get_output().clone())
@ -35,9 +36,9 @@ fn help() {
--auto-approve Skip interactive prompts; deny tools outside permission level
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--debug Print LLM request/response debug info to stderr
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--storage-dir <STORAGE_DIR> Local 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=]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [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
@ -119,6 +120,125 @@ fn exec_uses_user_config_defaults() {
");
}
#[test]
fn exec_server_url_uses_remote_transport_instead_of_local_api_key_resolution() {
let context = test_context!();
let server = MockServer::start();
server.mock(|when, then| {
when.method("POST").path("/api/v1/completions");
then.status(500).body("server-routed-marker");
});
let mut cmd = context.exec_cmd();
cmd.env_clear();
cmd.env("HOME", &context.home_dir);
cmd.env("FABRO_NO_UPGRADE_CHECK", "true");
cmd.args([
"--server-url",
&format!("{}/api/v1", server.base_url()),
"--provider",
"openai",
"--model",
"gpt-5.4-mini",
"test prompt",
]);
let output = cmd.assert().failure().get_output().clone();
let stderr = String::from_utf8(output.stderr).expect("valid utf8");
assert!(
stderr.contains("server-routed-marker"),
"expected remote server failure marker, got: {stderr}"
);
assert!(
!stderr.contains("API key not set"),
"exec should not fail local API key validation when --server-url is set: {stderr}"
);
}
#[test]
fn exec_configured_server_base_url_alone_does_not_reroute_exec() {
let context = test_context!();
let server = MockServer::start();
server.mock(|when, then| {
when.method("POST").path("/api/v1/completions");
then.status(500).body("config-should-not-be-used");
});
context.write_home(
".fabro/user.toml",
format!("[server]\nbase_url = \"{}/api/v1\"\n", server.base_url()),
);
let mut cmd = context.exec_cmd();
cmd.env_clear();
cmd.env("HOME", &context.home_dir);
cmd.env("FABRO_NO_UPGRADE_CHECK", "true");
cmd.args([
"--provider",
"openai",
"--model",
"gpt-5.4-mini",
"test prompt",
]);
let output = cmd.assert().failure().get_output().clone();
let stderr = String::from_utf8(output.stderr).expect("valid utf8");
assert!(
stderr.contains("API key not set for provider 'openai'"),
"expected local API key validation failure, got: {stderr}"
);
assert!(
!stderr.contains("config-should-not-be-used"),
"exec should ignore configured server.base_url without --server-url: {stderr}"
);
}
#[test]
fn exec_cli_server_url_overrides_configured_server_base_url() {
let context = test_context!();
let config_server = MockServer::start();
config_server.mock(|when, then| {
when.method("POST").path("/api/v1/completions");
then.status(500).body("config-should-not-be-used");
});
let cli_server = MockServer::start();
cli_server.mock(|when, then| {
when.method("POST").path("/api/v1/completions");
then.status(500).body("cli-override-marker");
});
context.write_home(
".fabro/user.toml",
format!(
"[server]\nbase_url = \"{}/api/v1\"\n",
config_server.base_url()
),
);
let mut cmd = context.exec_cmd();
cmd.env_clear();
cmd.env("HOME", &context.home_dir);
cmd.env("FABRO_NO_UPGRADE_CHECK", "true");
cmd.args([
"--server-url",
&format!("{}/api/v1", cli_server.base_url()),
"--provider",
"openai",
"--model",
"gpt-5.4-mini",
"test prompt",
]);
let output = cmd.assert().failure().get_output().clone();
let stderr = String::from_utf8(output.stderr).expect("valid utf8");
assert!(
stderr.contains("cli-override-marker"),
"expected CLI server URL to win, got: {stderr}"
);
assert!(
!stderr.contains("config-should-not-be-used"),
"configured server.base_url should not be used when --server-url is passed: {stderr}"
);
}
#[fabro_macros::e2e_test(live("ANTHROPIC_API_KEY"))]
fn exec_creates_file() {
let context = test_context!();

View file

@ -52,8 +52,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
-V, --version Print version
----- stderr -----

View file

@ -32,8 +32,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -62,12 +62,12 @@ fn help() {
[env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR>
Storage directory (default: ~/.fabro)
Local storage directory (default: ~/.fabro)
[env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL>
Server URL (overrides server.base_url from user.toml)
Fabro API server URL (overrides server.base_url from user.toml when supported)
[env: FABRO_SERVER_URL=]

View file

@ -29,8 +29,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -20,8 +20,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -69,8 +69,8 @@ fn help() {
-p, --pretty Formatted colored output with rendered assistant text
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
"#);

View file

@ -1,4 +1,5 @@
use fabro_test::{fabro_snapshot, test_context};
use httpmock::MockServer;
#[test]
fn help() {
@ -24,8 +25,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");
@ -175,3 +176,61 @@ fn list_invalid_provider_errors() {
error: unknown provider: not-a-provider
");
}
#[test]
fn list_uses_configured_server_base_url_without_server_url_flag() {
let context = test_context!();
let server = MockServer::start();
let mock = server.mock(|when, then| {
when.method("GET");
then.status(200)
.header("Content-Type", "application/json")
.body(
serde_json::json!({
"data": [{
"id": "remote-model",
"display_name": "Remote Model",
"provider": "openai",
"family": "test",
"aliases": ["remote"],
"limits": {
"context_window": 131_072,
"max_output": 4096
},
"training": null,
"knowledge_cutoff": null,
"features": {
"tools": true,
"vision": false,
"reasoning": false,
"effort": false
},
"costs": {
"input_cost_per_mtok": 1.0,
"output_cost_per_mtok": 2.0,
"cache_input_cost_per_mtok": null
},
"estimated_output_tps": 42.0,
"default": false
}],
"meta": { "has_more": false }
})
.to_string(),
);
});
context.write_home(
".fabro/user.toml",
format!("[server]\nbase_url = \"{}/api/v1\"\n", server.base_url()),
);
let mut cmd = context.model();
cmd.env_remove("FABRO_STORAGE_DIR");
cmd.args(["list", "--json"]);
let output = cmd.assert().success().get_output().stdout.clone();
let models: serde_json::Value =
serde_json::from_slice(&output).expect("model list json should parse");
mock.assert();
assert_eq!(models.as_array().map(Vec::len), Some(1));
assert_eq!(models[0]["id"].as_str(), Some("remote-model"));
}

View file

@ -21,8 +21,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -22,8 +22,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -22,8 +22,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -27,8 +27,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -22,8 +22,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -25,8 +25,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -20,8 +20,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -23,8 +23,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -28,8 +28,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -30,8 +30,8 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
-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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -23,8 +23,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -20,8 +20,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -27,8 +27,8 @@ fn help() {
-a, --all Show all runs, not just running (like docker ps -a)
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -31,8 +31,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");
@ -112,8 +112,8 @@ fn test_repo_init_help_does_not_show_skill() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -19,8 +19,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -21,8 +21,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -27,8 +27,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -32,8 +32,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -28,8 +28,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -37,8 +37,8 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [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)
@ -374,7 +374,6 @@ fn json_run_implies_auto_approve_for_human_gates() {
"model": "[LLM_MODEL]",
"provider": "[LLM_PROVIDER]"
},
"mode": "standalone",
"no_retro": true,
"sandbox": {
"daytona": null,

View file

@ -26,8 +26,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -26,8 +26,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -28,8 +28,8 @@ fn help() {
--open Open URL in browser (implies --signed)
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -26,8 +26,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -28,8 +28,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -22,8 +22,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -21,8 +21,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -22,8 +22,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -23,8 +23,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -22,8 +22,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -22,8 +22,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -46,11 +46,11 @@ fn help() {
--sandbox <SANDBOX>
Sandbox for agent tools
--storage-dir <STORAGE_DIR>
Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--max-concurrent-runs <MAX_CONCURRENT_RUNS>
Maximum number of concurrent run executions
--server-url <SERVER_URL>
Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
--config <CONFIG>
Path to server config file (default: ~/.fabro/server.toml)
-h, --help

View file

@ -25,8 +25,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -26,8 +26,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -28,8 +28,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -23,8 +23,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -27,8 +27,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -24,8 +24,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -22,8 +22,8 @@ fn help() {
--debug Enable DEBUG-level logging (default is INFO) [env: FABRO_DEBUG=]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -26,9 +26,9 @@ fn help() {
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--older-than <DURATION> Only prune runs older than this duration (e.g. 24h, 7d). Default: 24h when no explicit filters are set
--verbose Enable verbose output [env: FABRO_VERBOSE=]
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--storage-dir <STORAGE_DIR> Local 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=]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -22,8 +22,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
"#);

View file

@ -29,8 +29,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -26,8 +26,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -24,8 +24,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -30,8 +30,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -21,8 +21,8 @@ fn help() {
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--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=]
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
--server-url <SERVER_URL> Fabro API server URL (overrides server.base_url from user.toml when supported) [env: FABRO_SERVER_URL=]
-h, --help Print help
----- stderr -----
");

View file

@ -12,7 +12,7 @@ use crate::run::{
};
use crate::sandbox::SandboxConfig;
use crate::server::{self, ApiConfig, FeaturesConfig, GitConfig, LogConfig, WebConfig};
use crate::user::{self, ExecConfig, ExecutionMode, ServerConfig};
use crate::user::{self, ExecConfig, ServerConfig};
use fabro_types::Settings;
fn is_default_checkpoint(c: &CheckpointConfig) -> bool {
@ -77,9 +77,6 @@ pub struct ConfigLayer {
pub github: Option<GitHubConfig>,
// --- User config fields ---
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mode: Option<ExecutionMode>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub server: Option<ServerConfig>,
@ -161,7 +158,6 @@ impl Combine for ConfigLayer {
hooks,
mcp_servers: self.mcp_servers.combine(other.mcp_servers),
github: self.github.combine(other.github),
mode: self.mode.combine(other.mode),
server: self.server.combine(other.server),
exec: self.exec.combine(other.exec),
prevent_idle_sleep: self.prevent_idle_sleep.combine(other.prevent_idle_sleep),

View file

@ -23,7 +23,6 @@ impl TryFrom<ConfigLayer> for Settings {
hooks: value.hooks,
mcp_servers: value.mcp_servers,
github: value.github.map(Into::into),
mode: value.mode,
server: value.server.map(TryInto::try_into).transpose()?,
exec: value.exec.map(Into::into),
prevent_idle_sleep: value.prevent_idle_sleep,

View file

@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
use crate::config::ConfigLayer;
pub use fabro_types::settings::user::{
ClientTlsSettings, ExecSettings, ExecutionMode, OutputFormat, PermissionLevel, ServerSettings,
ClientTlsSettings, ExecSettings, OutputFormat, PermissionLevel, ServerSettings,
};
pub const USER_CONFIG_FILENAME: &str = "user.toml";

View file

@ -1,5 +1,6 @@
use axum::body::Body;
use axum::body::{Body, to_bytes};
use axum::http::{Method, Request, StatusCode};
use fabro_server::jwt_auth::AuthMode;
use fabro_server::server::{build_router, create_app_state};
use tower::ServiceExt;
@ -7,10 +8,7 @@ use crate::helpers::body_json;
#[tokio::test]
async fn old_unversioned_routes_return_404() {
let app = build_router(
create_app_state(),
fabro_server::jwt_auth::AuthMode::Disabled,
);
let app = build_router(create_app_state(), AuthMode::Disabled);
let cases = [(Method::POST, "/completions")];
@ -27,10 +25,7 @@ async fn old_unversioned_routes_return_404() {
#[tokio::test]
async fn root_and_health_stay_at_root() {
let app = build_router(
create_app_state(),
fabro_server::jwt_auth::AuthMode::Disabled,
);
let app = build_router(create_app_state(), AuthMode::Disabled);
let root_req = Request::builder()
.method("GET")
@ -39,7 +34,7 @@ async fn root_and_health_stay_at_root() {
.unwrap();
let root_response = app.clone().oneshot(root_req).await.unwrap();
assert_eq!(root_response.status(), StatusCode::OK);
let root_body = axum::body::to_bytes(root_response.into_body(), usize::MAX)
let root_body = to_bytes(root_response.into_body(), usize::MAX)
.await
.unwrap();
let root_html = String::from_utf8(root_body.to_vec()).unwrap();
@ -58,10 +53,7 @@ async fn root_and_health_stay_at_root() {
#[tokio::test]
async fn moved_routes_not_at_root_of_api_prefix() {
let app = build_router(
create_app_state(),
fabro_server::jwt_auth::AuthMode::Disabled,
);
let app = build_router(create_app_state(), AuthMode::Disabled);
for path in ["/api/v1/health", "/api/v1/"] {
let req = Request::builder()

View file

@ -1,13 +1,14 @@
use std::sync::Arc;
use std::time::Duration;
use axum::body::Body;
use axum::body::{Body, to_bytes};
use axum::http::{Request, StatusCode};
use fabro_server::jwt_auth::AuthMode;
use fabro_server::server::{
AppState, build_router, create_app_state, create_app_state_with_options, spawn_scheduler,
};
use fabro_types::Settings;
use tokio::time::sleep;
use tower::ServiceExt;
pub(crate) const MINIMAL_DOT: &str = r#"digraph Test {
@ -47,7 +48,7 @@ pub(crate) fn api(path: &str) -> String {
}
pub(crate) async fn body_json(body: Body) -> serde_json::Value {
let bytes = axum::body::to_bytes(body, usize::MAX).await.unwrap();
let bytes = to_bytes(body, usize::MAX).await.unwrap();
serde_json::from_slice(&bytes).unwrap()
}
@ -98,7 +99,7 @@ pub(crate) async fn wait_for_run_status(
if expected.iter().any(|candidate| *candidate == status) {
return status;
}
tokio::time::sleep(POLL_INTERVAL).await;
sleep(POLL_INTERVAL).await;
}
panic!("run {run_id} did not reach any of {expected:?}");
}
@ -114,7 +115,7 @@ pub(crate) async fn wait_for_run_status_not_in(
if unexpected.iter().all(|candidate| *candidate != status) {
return status;
}
tokio::time::sleep(POLL_INTERVAL).await;
sleep(POLL_INTERVAL).await;
}
panic!("run {run_id} stayed in {unexpected:?}");
}

View file

@ -3,12 +3,14 @@ use std::sync::Arc;
use axum::body::Body;
use axum::http::{Request, StatusCode};
use fabro_interview::Interviewer;
use fabro_server::server::{build_router, create_app_state_with_registry_factory};
use fabro_server::jwt_auth::AuthMode;
use fabro_server::server::{build_router, create_app_state_with_registry_factory, spawn_scheduler};
use fabro_workflow::handler::HandlerRegistry;
use fabro_workflow::handler::agent::AgentHandler;
use fabro_workflow::handler::exit::ExitHandler;
use fabro_workflow::handler::human::HumanHandler;
use fabro_workflow::handler::start::StartHandler;
use tokio::time::sleep;
use tower::ServiceExt;
use crate::helpers::{
@ -42,7 +44,7 @@ async fn wait_for_question_id(app: &axum::Router, run_id: &str) -> String {
{
return question_id;
}
tokio::time::sleep(POLL_INTERVAL).await;
sleep(POLL_INTERVAL).await;
}
panic!("question should have appeared");
}
@ -66,11 +68,8 @@ const GATE_DOT: &str = r#"digraph GateTest {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn full_http_lifecycle_approve_and_complete() {
let state = create_app_state_with_registry_factory(gate_registry);
fabro_server::server::spawn_scheduler(Arc::clone(&state));
let app = build_router(
Arc::clone(&state),
fabro_server::jwt_auth::AuthMode::Disabled,
);
spawn_scheduler(Arc::clone(&state));
let app = build_router(Arc::clone(&state), AuthMode::Disabled);
// 1. Create run
let req = Request::builder()
@ -134,11 +133,8 @@ async fn full_http_lifecycle_approve_and_complete() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn full_http_lifecycle_cancel() {
let state = create_app_state_with_registry_factory(gate_registry);
fabro_server::server::spawn_scheduler(Arc::clone(&state));
let app = build_router(
Arc::clone(&state),
fabro_server::jwt_auth::AuthMode::Disabled,
);
spawn_scheduler(Arc::clone(&state));
let app = build_router(Arc::clone(&state), AuthMode::Disabled);
// Create and start a run that will block at the human gate
let req = Request::builder()

View file

@ -1,6 +1,7 @@
use axum::body::Body;
use axum::http::{Request, StatusCode};
use fabro_server::server::create_app_state_with_options;
use tokio::time::sleep;
use tower::ServiceExt;
use crate::helpers::{
@ -27,7 +28,7 @@ async fn attach_run_events_returns_sse_stream() {
let run_id = create_and_start_run(&app, MINIMAL_DOT).await;
// Wait for scheduler to promote run.
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
sleep(std::time::Duration::from_millis(100)).await;
let req = Request::builder()
.method("GET")

View file

@ -4,6 +4,7 @@ use axum::body::Body;
use axum::http::{Request, StatusCode};
use fabro_server::server::create_app_state_with_options;
use http_body_util::BodyExt;
use tokio::time::{sleep, timeout};
use tower::ServiceExt;
use crate::helpers::{
@ -30,7 +31,7 @@ async fn wait_for_checkpoint(app: &axum::Router, run_id: &str) -> serde_json::Va
if response.status() == StatusCode::OK {
return body_json(response.into_body()).await;
}
tokio::time::sleep(POLL_INTERVAL).await;
sleep(POLL_INTERVAL).await;
}
panic!("checkpoint did not become available for {run_id}");
}
@ -72,8 +73,7 @@ async fn sse_stream_contains_expected_event_types() {
// Collect SSE frames with a timeout
let mut body = response.into_body();
let mut sse_data = String::new();
while let Ok(Some(Ok(frame))) = tokio::time::timeout(Duration::from_secs(2), body.frame()).await
{
while let Ok(Some(Ok(frame))) = timeout(Duration::from_secs(2), body.frame()).await {
if let Some(data) = frame.data_ref() {
sse_data.push_str(&String::from_utf8_lossy(data));
}

View file

@ -1,6 +1,7 @@
use axum::body::Body;
use axum::http::{Request, StatusCode};
use fabro_server::server::create_app_state_with_options;
use tokio::time::sleep;
use tower::ServiceExt;
use crate::helpers::{
@ -35,7 +36,7 @@ async fn aggregate_usage_increments_after_run_completes() {
if total_runs == 1 {
break;
}
tokio::time::sleep(POLL_INTERVAL).await;
sleep(POLL_INTERVAL).await;
}
assert_eq!(total_runs, 1);
}

View file

@ -305,7 +305,7 @@ fn stop_session_server(storage_dir: &Path) {
let Ok(record) = serde_json::from_str::<serde_json::Value>(&content) else {
return;
};
let Some(pid) = record["pid"].as_u64().map(|p| p as u32) else {
let Some(pid) = record["pid"].as_u64().and_then(|p| u32::try_from(p).ok()) else {
return;
};

View file

@ -30,9 +30,7 @@ pub use server::{
GitProvider, GitSettings, LogSettings, TlsSettings, WebSettings, WebhookSettings,
WebhookStrategy,
};
pub use user::{
ClientTlsSettings, ExecSettings, ExecutionMode, OutputFormat, PermissionLevel, ServerSettings,
};
pub use user::{ClientTlsSettings, ExecSettings, OutputFormat, PermissionLevel, ServerSettings};
fn is_default_checkpoint(c: &CheckpointSettings) -> bool {
c.exclude_globs.is_empty()
@ -73,8 +71,6 @@ pub struct Settings {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub github: Option<GitHubSettings>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mode: Option<ExecutionMode>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub server: Option<ServerSettings>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub exec: Option<ExecSettings>,

View file

@ -19,15 +19,6 @@ pub enum PermissionLevel {
Full,
}
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize, crate::Combine)]
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
#[serde(rename_all = "lowercase")]
pub enum ExecutionMode {
#[default]
Standalone,
Server,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct ClientTlsSettings {
pub cert: PathBuf,