Rename fabro-api crate to fabro-server

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Bryan Helmkamp 2026-03-29 13:04:06 -04:00
parent 01dd50752e
commit 1c0dcf58b1
21 changed files with 101 additions and 92 deletions

View file

@ -13,7 +13,7 @@ medium = { max-threads = 4 } # 18-30 MB binaries
[[profile.default.overrides]]
filter = """
package(fabro-api)
package(fabro-server)
| package(fabro-workflows)
| package(fabro-agent)
| package(fabro-cli)

View file

@ -7,7 +7,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
### Rust
- `cargo build --workspace` — build all crates
- `cargo nextest run --workspace` — run all unit tests
- `cargo nextest run -p fabro-api` — test a single crate
- `cargo nextest run -p fabro-server` — test a single crate
- `cargo nextest run -p fabro-workflows -- test_name` — run a single test
- `set -a && source .env && set +a && cargo nextest run --workspace --profile e2e --run-ignored only` — run all E2E live tests (requires credentials in `.env`, see `.env.example`)
- `set -a && source .env && set +a && cargo nextest run -p fabro-llm --profile e2e --run-ignored only` — run E2E tests for a single crate
@ -41,8 +41,8 @@ The OpenAPI spec at `docs/api-reference/fabro-api.yaml` is the source of truth f
1. Edit `docs/api-reference/fabro-api.yaml`
2. `cargo build -p fabro-api-types` — build.rs regenerates Rust types via typify
3. Write/update handler in `lib/crates/fabro-api/src/server.rs`, add route to `build_router()`
4. `cargo nextest run -p fabro-api` — conformance test catches spec/router drift
3. Write/update handler in `lib/crates/fabro-server/src/server.rs`, add route to `build_router()`
4. `cargo nextest run -p fabro-server` — conformance test catches spec/router drift
5. `cd lib/packages/fabro-api-client && bun run generate` — regenerates TypeScript Axios client
## Architecture
@ -53,7 +53,7 @@ Fabro is an AI-powered workflow orchestration platform. Workflows are defined as
- **fabro-cli** — CLI entry point. Commands: `run`, `exec`, `serve`, `validate`, `parse`, `cp`, `model`, `doctor`, `init`, `install`, `ps`, `system prune`, `llm`
- **fabro-workflows** — Core workflow engine. Parses Graphviz graphs, runs stages, manages checkpoints/resume, hooks, retros, and human-in-the-loop interactions
- **fabro-agent** — AI coding agent with tool use (Bash, Read, Write, Edit, Glob, Grep, WebFetch). `Sandbox` trait abstracts execution environments
- **fabro-api** — Axum HTTP server. Routes for runs, sessions, models, completions, usage. SSE event streaming. Demo mode via header
- **fabro-server** — Axum HTTP server. Routes for runs, sessions, models, completions, usage. SSE event streaming. Demo mode via header
- **fabro-exe** — SSH-based sandbox implementation (`ExeSandbox`)
- **fabro-sprites** — Sprites VM sandbox implementation via `sprite` CLI
- **fabro-llm** — Unified LLM client with providers: Anthropic, OpenAI, Gemini, OpenAI-compatible, plus retry/middleware/streaming

118
Cargo.lock generated
View file

@ -1442,64 +1442,6 @@ dependencies = [
"uuid",
]
[[package]]
name = "fabro-api"
version = "0.176.2"
dependencies = [
"anyhow",
"axum",
"base64",
"bytes",
"chrono",
"clap",
"dirs",
"fabro-agent",
"fabro-api-types",
"fabro-config",
"fabro-db",
"fabro-github",
"fabro-graphviz",
"fabro-hooks",
"fabro-interview",
"fabro-llm",
"fabro-model",
"fabro-retro",
"fabro-sandbox",
"fabro-store",
"fabro-types",
"fabro-util",
"fabro-workflows",
"futures-util",
"hex",
"hmac",
"http-body-util",
"hyper",
"hyper-util",
"jsonwebtoken",
"object_store",
"openapiv3",
"reqwest",
"rustls",
"rustls-pemfile",
"rustls-pki-types",
"serde",
"serde_json",
"serde_yaml",
"sha2",
"sqlx",
"tempfile",
"tokio",
"tokio-rustls",
"tokio-stream",
"toml 0.8.23",
"tower",
"tower-service",
"tracing",
"ulid",
"uuid",
"x509-parser",
]
[[package]]
name = "fabro-api-types"
version = "0.176.2"
@ -1534,7 +1476,6 @@ dependencies = [
"dirs",
"dotenvy",
"fabro-agent",
"fabro-api",
"fabro-config",
"fabro-devcontainer",
"fabro-git-storage",
@ -1549,6 +1490,7 @@ dependencies = [
"fabro-proctitle",
"fabro-retro",
"fabro-sandbox",
"fabro-server",
"fabro-store",
"fabro-telemetry",
"fabro-types",
@ -1863,6 +1805,64 @@ dependencies = [
"uuid",
]
[[package]]
name = "fabro-server"
version = "0.176.2"
dependencies = [
"anyhow",
"axum",
"base64",
"bytes",
"chrono",
"clap",
"dirs",
"fabro-agent",
"fabro-api-types",
"fabro-config",
"fabro-db",
"fabro-github",
"fabro-graphviz",
"fabro-hooks",
"fabro-interview",
"fabro-llm",
"fabro-model",
"fabro-retro",
"fabro-sandbox",
"fabro-store",
"fabro-types",
"fabro-util",
"fabro-workflows",
"futures-util",
"hex",
"hmac",
"http-body-util",
"hyper",
"hyper-util",
"jsonwebtoken",
"object_store",
"openapiv3",
"reqwest",
"rustls",
"rustls-pemfile",
"rustls-pki-types",
"serde",
"serde_json",
"serde_yaml",
"sha2",
"sqlx",
"tempfile",
"tokio",
"tokio-rustls",
"tokio-stream",
"toml 0.8.23",
"tower",
"tower-service",
"tracing",
"ulid",
"uuid",
"x509-parser",
]
[[package]]
name = "fabro-slack"
version = "0.176.2"

View file

@ -11,7 +11,7 @@ path = "src/main.rs"
[features]
default = []
server = ["dep:fabro-api"]
server = ["dep:fabro-server"]
exedev = ["fabro-sandbox/exe", "fabro-config/exedev", "fabro-workflows/exedev", "fabro-types/exedev"]
sleep_inhibitor = ["dep:core-foundation"]
@ -36,7 +36,7 @@ fabro-git-storage = { path = "../fabro-git-storage" }
fabro-graphviz = { path = "../fabro-graphviz" }
fabro-validate = { path = "../fabro-validate" }
fabro-workflows = { path = "../fabro-workflows" }
fabro-api = { path = "../fabro-api", optional = true }
fabro-server = { path = "../fabro-server", optional = true }
fabro-telemetry = { path = "../fabro-telemetry" }
fabro-store = { path = "../fabro-store" }
fabro-types = { path = "../fabro-types" }

View file

@ -772,7 +772,7 @@ pub(crate) enum Commands {
},
/// Start the HTTP API server
#[cfg(feature = "server")]
Serve(fabro_api::serve::ServeArgs),
Serve(fabro_server::serve::ServeArgs),
/// Check environment and integration health
Doctor {
/// Show detailed information for each check

View file

@ -185,7 +185,7 @@ async fn main_inner() -> (String, Result<()>) {
#[cfg(feature = "server")]
Commands::Serve(args) => {
let styles: &'static Styles = Box::leak(Box::new(Styles::detect_stderr()));
fabro_api::serve::serve_command(args, styles).await?;
fabro_server::serve::serve_command(args, styles).await?;
}
Commands::Doctor { verbose, dry_run } => {
let cli_settings = cli_config::load_cli_settings(None)?;

View file

@ -1,9 +1,9 @@
[package]
name = "fabro-api"
name = "fabro-server"
edition.workspace = true
version.workspace = true
license.workspace = true
description = "HTTP API server for Fabro pipelines"
description = "HTTP server for Fabro pipelines"
[lib]
doctest = false

View file

@ -8,10 +8,10 @@ mod mtls_e2e {
use std::path::Path;
use std::process::{Command, Stdio};
use fabro_api::jwt_auth::{AuthMode, AuthStrategy};
use fabro_api::server::{build_router, create_app_state};
use fabro_api::server_config::TlsSettings;
use fabro_api::tls::{ClientAuth, build_rustls_config};
use fabro_server::jwt_auth::{AuthMode, AuthStrategy};
use fabro_server::server::{build_router, create_app_state};
use fabro_server::server_config::TlsSettings;
use fabro_server::tls::{ClientAuth, build_rustls_config};
use fabro_workflows::pipeline::LlmSpec;
use tokio::net::TcpListener;
@ -195,7 +195,7 @@ mod mtls_e2e {
let router = build_router(state, auth_mode);
tokio::spawn(async move {
let _ = fabro_api::tls::serve_tls(listener, tls_acceptor, router).await;
let _ = fabro_server::tls::serve_tls(listener, tls_acceptor, router).await;
});
addr
@ -391,7 +391,7 @@ mod mtls_e2e {
AuthStrategy::Mtls,
AuthStrategy::Jwt {
key: Arc::new(decoding_key),
validation: Arc::new(fabro_api::jwt_auth::jwt_validation()),
validation: Arc::new(fabro_server::jwt_auth::jwt_validation()),
allowed_usernames: vec!["brynary".to_string()],
},
]);
@ -428,8 +428,8 @@ mod server_lifecycle {
use axum::body::Body;
use axum::http::{Request, StatusCode};
use fabro_api::server::{build_router, create_app_state_with_registry_factory};
use fabro_interview::Interviewer;
use fabro_server::server::{build_router, create_app_state_with_registry_factory};
use fabro_workflows::handler::HandlerRegistry;
use fabro_workflows::handler::agent::AgentHandler;
use fabro_workflows::handler::exit::ExitHandler;
@ -471,8 +471,11 @@ mod server_lifecycle {
async fn full_http_lifecycle_approve_and_complete() {
let state =
create_app_state_with_registry_factory(test_db().await, test_llm_spec, gate_registry);
fabro_api::server::spawn_scheduler(Arc::clone(&state));
let app = build_router(Arc::clone(&state), fabro_api::jwt_auth::AuthMode::Disabled);
fabro_server::server::spawn_scheduler(Arc::clone(&state));
let app = build_router(
Arc::clone(&state),
fabro_server::jwt_auth::AuthMode::Disabled,
);
// 1. Start run
let req = Request::builder()
@ -568,8 +571,11 @@ mod server_lifecycle {
async fn full_http_lifecycle_cancel() {
let state =
create_app_state_with_registry_factory(test_db().await, test_llm_spec, gate_registry);
fabro_api::server::spawn_scheduler(Arc::clone(&state));
let app = build_router(Arc::clone(&state), fabro_api::jwt_auth::AuthMode::Disabled);
fabro_server::server::spawn_scheduler(Arc::clone(&state));
let app = build_router(
Arc::clone(&state),
fabro_server::jwt_auth::AuthMode::Disabled,
);
// Start a run that will block at the human gate
let req = Request::builder()
@ -621,7 +627,7 @@ mod sse_events {
use axum::body::Body;
use axum::http::{Request, StatusCode};
use fabro_api::server::{build_router, create_app_state};
use fabro_server::server::{build_router, create_app_state};
use http_body_util::BodyExt;
use tower::ServiceExt;
@ -636,8 +642,11 @@ mod sse_events {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn sse_stream_contains_expected_event_types() {
let state = create_app_state(test_db().await, test_llm_spec);
fabro_api::server::spawn_scheduler(Arc::clone(&state));
let app = build_router(Arc::clone(&state), fabro_api::jwt_auth::AuthMode::Disabled);
fabro_server::server::spawn_scheduler(Arc::clone(&state));
let app = build_router(
Arc::clone(&state),
fabro_server::jwt_auth::AuthMode::Disabled,
);
// Start run
let req = Request::builder()
@ -765,7 +774,7 @@ mod serve_dry_run {
use axum::body::Body;
use axum::http::{Request, StatusCode};
use fabro_api::server::{build_router, create_app_state};
use fabro_server::server::{build_router, create_app_state};
use tower::ServiceExt;
const MINIMAL_DOT: &str = r#"digraph Test {
@ -778,8 +787,8 @@ mod serve_dry_run {
/// Build the router exactly as `serve_command` does in dry-run mode.
async fn dry_run_app() -> axum::Router {
let state = create_app_state(test_db().await, test_llm_spec);
fabro_api::server::spawn_scheduler(Arc::clone(&state));
build_router(state, fabro_api::jwt_auth::AuthMode::Disabled)
fabro_server::server::spawn_scheduler(Arc::clone(&state));
build_router(state, fabro_server::jwt_auth::AuthMode::Disabled)
}
async fn body_json(body: Body) -> serde_json::Value {

View file

@ -5,13 +5,13 @@ use std::collections::BTreeSet;
use axum::body::Body;
use axum::http::{Method, Request, StatusCode};
use fabro_api::jwt_auth::AuthMode;
use fabro_api::server::{build_router, create_app_state};
use fabro_api::server_config::*;
use fabro_config::run::*;
use fabro_config::sandbox::SandboxSettings;
use fabro_hooks::*;
use fabro_sandbox::daytona::*;
use fabro_server::jwt_auth::AuthMode;
use fabro_server::server::{build_router, create_app_state};
use fabro_server::server_config::*;
use tower::ServiceExt;
fn load_spec() -> openapiv3::OpenAPI {

View file

@ -3,8 +3,8 @@
use super::helpers::{test_db, test_llm_spec};
use axum::body::Body;
use axum::http::{Request, StatusCode};
use fabro_api::jwt_auth::AuthMode;
use fabro_api::server::{build_router, create_app_state};
use fabro_server::jwt_auth::AuthMode;
use fabro_server::server::{build_router, create_app_state};
use tower::ServiceExt;
async fn get_json(app: axum::Router, uri: &str) -> serde_json::Value {