mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-11 03:40:05 +00:00
Some checks are pending
Rust / Clippy (push) Waiting to run
Rust / Format (push) Waiting to run
TypeScript / Build (push) Waiting to run
Rust / Generated Docs (push) Waiting to run
Rust / Test (Linux) (push) Waiting to run
Rust / Test (macOS) (push) Waiting to run
TypeScript / Typecheck (push) Waiting to run
TypeScript / Test (push) Waiting to run
## What Adds a CRUD interface for **server-managed Environments** at `/settings/environments`, driven by the `/api/v1/environments` REST API (list / create / retrieve / replace / delete), and reshapes how built-in environments are provisioned and protected. The page lives in the **Workflows** settings nav section (also introduced in this branch), positioned before Variables. ## Why The Environments REST API shipped (#453) but had no UI — environments could only be managed via the API/CLI. This gives operators a web UI alongside Variables and Secrets, and along the way tightens the model: environments are seeded at install time (not silently re-created on every boot), and the `default` fallback is an ordinary, deletable environment. ## Web UI **Pages & component** - `settings-environments.tsx` — list view: provider badge, image/resource summary, row actions (Edit/Delete). **"New environment" is a dropdown** of the enabled sandbox providers; the chosen provider is fixed for the environment's lifetime. - `settings-environments-new.tsx` / `settings-environments-edit.tsx` — create/edit flows; create reads the provider from a query param. - `environment-form.tsx` — shared form, reorganized: - **General** panel (merged identity + image): id, and an **image-source selector** (Image reference *vs* inline Dockerfile) that shows, requires, and sends only the selected, mutually-exclusive source. - **Resources**: CPU / memory / disk as **range sliders** (CPU 1–8, memory 1–16 GB, disk 1–20 GB), each always writing a concrete value. - **Environment variables** key/value editor. - **Advanced** progressive-disclosure section holding **Network** (a single "Block all network access" toggle — allow-all vs block) and **Lifecycle** (preserve / stop-on-terminal / auto-stop). Opens by default when any advanced value is non-default. - The in-form **provider control and the Labels editor were removed** — labels remain API-managed and are round-tripped untouched so UI edits never clear them. **Data layer**: `environmentsApi` client, `queryKeys.environments`, `useEnvironments` / `useEnvironment` SWR hooks. **Nav & routing**: "Environments" item in the Workflows section before Variables; routes registered in `router.tsx`. ## Backend: seed at install, deletable `default` - **Seeding moved to install time.** The server no longer seeds built-ins on startup; `EnvironmentStore::load_or_seed` → `load` (load-only). A new public `seed_environments(dir)` (idempotent, preserves operator edits) is called by both the web installer and the CLI installer. An uninstalled instance therefore has no managed environments, and a run selecting an absent environment fails explicitly (`unknown environment: default`) rather than resurrecting a built-in. - **`default` is no longer protected.** The delete guard and the `Protected` error variant are gone; deleting `default` succeeds (204) and removes the run fallback on purpose — forcing an explicit choice. `local` is unchanged (reserved, in-memory). - **`volumes` removed** from environment settings across the OpenAPI spec, generated Rust + TS clients, config layers, sandbox/server/workflow plumbing, docs, and tests. ## API contract details honored - Edit sends the environment `revision` as `If-Match`; 409 conflicts surface a "changed since you opened it" message. - The REST API accepts inline Dockerfiles only — the form never sends a Dockerfile path. ## Verification - Rust: `cargo build` (touched crates) ✅, `cargo nextest -p fabro-environment` 21/21 ✅, server env unit + `tests/it` integration 2/2 + 15/15 ✅, `clippy` (nightly, touched crates, all targets) clean ✅, `fmt --check` clean ✅. Full `--workspace` suite not run here — worth a CI pass. - Web: `bun run typecheck` ✅, `bun run build` ✅, `environment-form.test.ts` 5/5 ✅. Web suite: 512 pass / 1 unrelated pre-existing `RunDetail` failure. - **Not visually verified in-browser** — the local app is login-gated and automated loads redirect to `/login`; rendering of the form, the New-environment dropdown, and `default` delete should be confirmed in a logged-in session. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: fabro-sh-0530[bot] <281434857+fabro-sh-0530[bot]@users.noreply.github.com> Co-authored-by: Fabro <noreply@fabro.sh> Co-authored-by: Release Repro <release-repro@example.com>
134 lines
4.2 KiB
TOML
134 lines
4.2 KiB
TOML
[package]
|
|
name = "fabro-cli"
|
|
edition.workspace = true
|
|
version.workspace = true
|
|
publish = false
|
|
license.workspace = true
|
|
description = "Unified CLI for the Fabro AI framework"
|
|
|
|
[[bin]]
|
|
name = "fabro"
|
|
path = "src/main.rs"
|
|
|
|
[features]
|
|
default = []
|
|
sleep_inhibitor = ["dep:core-foundation"]
|
|
|
|
[lints]
|
|
workspace = true
|
|
|
|
[dependencies]
|
|
fabro-auth = { path = "../fabro-auth" }
|
|
fabro-config = { path = "../fabro-config" }
|
|
fabro-environment = { path = "../fabro-environment" }
|
|
fabro-llm = { path = "../fabro-llm" }
|
|
fabro-model = { path = "../fabro-model" }
|
|
fabro-oauth = { path = "../fabro-oauth" }
|
|
fabro-github = { path = "../fabro-github" }
|
|
fabro-agent = { path = "../fabro-agent" }
|
|
fabro-dump = { path = "../fabro-dump" }
|
|
fabro-hooks = { path = "../fabro-hooks" }
|
|
fabro-install = { path = "../fabro-install" }
|
|
fabro-interview = { path = "../fabro-interview" }
|
|
fabro-mcp = { path = "../fabro-mcp" }
|
|
fabro-mcp-server = { path = "../fabro-mcp-server" }
|
|
fabro-manifest = { path = "../fabro-manifest" }
|
|
fabro-proc = { path = "../fabro-proc" }
|
|
fabro-sandbox = { path = "../fabro-sandbox", features = ["daytona"] }
|
|
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" }
|
|
fabro-client = { path = "../fabro-client" }
|
|
fabro-api = { path = "../fabro-api" }
|
|
fabro-telemetry = { path = "../fabro-telemetry" }
|
|
fabro-store = { path = "../fabro-store" }
|
|
fabro-vault = { path = "../fabro-vault" }
|
|
fabro-types = { path = "../fabro-types", features = ["clap"] }
|
|
fabro-redact.workspace = true
|
|
fabro-util = { path = "../fabro-util" }
|
|
fabro-http.workspace = true
|
|
fabro-static.workspace = true
|
|
fabro-template = { path = "../fabro-template" }
|
|
fabro-tool = { path = "../fabro-tool" }
|
|
clap.workspace = true
|
|
clap_complete.workspace = true
|
|
cli-table.workspace = true
|
|
console.workspace = true
|
|
indicatif.workspace = true
|
|
daytona-sdk.workspace = true
|
|
anyhow.workspace = true
|
|
miette.workspace = true
|
|
dotenvy.workspace = true
|
|
tokio.workspace = true
|
|
tokio-tungstenite.workspace = true
|
|
tracing.workspace = true
|
|
tracing-subscriber.workspace = true
|
|
tracing-appender.workspace = true
|
|
chrono = { workspace = true, features = ["serde"] }
|
|
dirs.workspace = true
|
|
fs2.workspace = true
|
|
serde.workspace = true
|
|
thiserror.workspace = true
|
|
toml.workspace = true
|
|
toml_edit.workspace = true
|
|
futures.workspace = true
|
|
regex.workspace = true
|
|
semver.workspace = true
|
|
progenitor-client = "0.13"
|
|
async-trait.workspace = true
|
|
jsonwebtoken.workspace = true
|
|
base64.workspace = true
|
|
ulid.workspace = true
|
|
scopeguard = "1"
|
|
rustls = { version = "0.23", default-features = false, features = ["std", "ring"] }
|
|
ring = "0.17"
|
|
x509-parser = "0.16"
|
|
rand.workspace = true
|
|
dialoguer.workspace = true
|
|
git2.workspace = true
|
|
axum.workspace = true
|
|
serde_json.workspace = true
|
|
serde_yaml = "0.9"
|
|
tempfile = "3"
|
|
sha2.workspace = true
|
|
shlex = "1"
|
|
walkdir.workspace = true
|
|
object_store.workspace = true
|
|
bytes.workspace = true
|
|
tokio-util.workspace = true
|
|
libc = "0.2"
|
|
nix = { version = "0.30", features = ["fs"] }
|
|
|
|
[target.'cfg(target_os = "macos")'.dependencies]
|
|
core-foundation = { version = "0.9", optional = true }
|
|
|
|
# Vendor openssl only for musl targets. daytona-sdk transitively pulls
|
|
# native-tls via reqwest, which needs libssl. On glibc runners the system
|
|
# libssl is used; on musl runners we compile openssl from source.
|
|
[target.'cfg(target_env = "musl")'.dependencies]
|
|
openssl = { version = "0.10", features = ["vendored"] }
|
|
|
|
[build-dependencies]
|
|
fabro-build-support = { path = "../build-support" }
|
|
chrono = { workspace = true }
|
|
|
|
[dev-dependencies]
|
|
assert_cmd = "2"
|
|
fabro-acp = { path = "../fabro-acp", features = ["test-support"] }
|
|
fabro-build-support = { path = "../build-support" }
|
|
fabro-server = { path = "../fabro-server", features = ["test-support"] }
|
|
fabro-types = { path = "../fabro-types", features = ["clap", "test-support"] }
|
|
insta = { workspace = true, features = ["filters"] }
|
|
paste = "1"
|
|
predicates = "3"
|
|
serde_json.workspace = true
|
|
tempfile = "3"
|
|
temp-env = "0.3"
|
|
httpmock = "0.8"
|
|
fabro-test = { workspace = true }
|
|
fabro-macros = { path = "../fabro-macros" }
|
|
hkdf.workspace = true
|
|
reqwest = { workspace = true, features = ["cookies"] }
|
|
tokio = { workspace = true, features = ["test-util", "macros"] }
|