mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-08-28 05:27:41 +00:00
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:
parent
834b33a7f8
commit
563a9eeb49
78 changed files with 228 additions and 379 deletions
|
|
@ -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" }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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::*;
|
||||
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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]",
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
|
|
|
|||
|
|
@ -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() {
|
|||
}
|
||||
}
|
||||
]
|
||||
"###
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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]"}
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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]",
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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!();
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
"#);
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -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 -----
|
||||
");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use fabro_test::{fabro_snapshot, test_context};
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "server")]
|
||||
fn start_status_stop_lifecycle() {
|
||||
let context = test_context!();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue