mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-04 02:33:56 +00:00
## Summary
Exposes the existing variables API through a new `fabro variable` CLI
namespace (`list`, `get`, `set`, `rm`), following the same patterns as
`fabro secret`. Variables are intentionally readable — `list` and `get`
show stored values — while `fabro secret` remains write-only. This PR
also ships a significant set of accompanying changes: a refactored
sandbox lifecycle model in the web UI, removal of the
`fabro-devcontainer` crate, and a new `RunSandbox` OpenAPI schema that
models the full planned → initializing → ready/failed lifecycle.
## What Changed
### CLI (`fabro variable`)
- New `fabro variable` namespace with `list` (aliased `ls`), `get`,
`set`, and `rm` subcommands, dispatched through the same
`ServerTargetArgs` pattern as `fabro secret`.
- `fabro-client` gains five new wrapper methods (`list_variables`,
`get_variable`, `create_variable`, `update_variable`, `delete_variable`)
over the generated OpenAPI client.
- `set` is an upsert; `--value-stdin` accepts empty input after
newline-trimming (unlike the secrets equivalent).
- CLI reference docs (`docs/public/reference/cli.mdx`) regenerated;
`docs/public/workflows/variables.mdx` gains a short section explaining
`{{ vars.NAME }}` interpolation and the variables-vs-secrets security
boundary.
### Sandbox lifecycle model (web)
- New `RunSandbox` OpenAPI shape splits the old flat object into `kind`
(planned/initializing/ready/failed) + `plan` + optional `instance` +
optional `failure`.
- `apps/fabro-web/app/lib/run-sandbox-lifecycle.ts` centralises
lifecycle helpers (`sandboxLifecycleKind`, `sandboxInstance`,
`sandboxRuntime`, `sandboxIsReady`, `sandboxTabVisible`,
`SANDBOX_LIFECYCLE_DISPLAY`).
- Run summary panel and sandbox route now show lifecycle state
(Initializing / Failed with causes / Not created) before or instead of
the fully-loaded `SandboxDetails`.
- The sandbox details query is skipped entirely until `sandboxIsReady`
returns true, preventing unnecessary 404 fetches for planned/failed
sandboxes.
- `runHasSandbox` in `tabs-shell.tsx` delegates to `sandboxTabVisible`,
hiding the Sandbox tab for `planned` state and showing it for
`initializing`/`ready`/`failed`.
- Legacy flat sandbox shape (no `kind`) is handled via
backwards-compatible shims in the new helpers.
### `fabro-devcontainer` removal
- The `fabro-devcontainer` crate has been removed from `Cargo.lock` and
all dependent crates.
- References to devcontainer in internal plans, docs, changelog entries,
and event schemas have been cleaned up or reworded to reflect that the
feature is no longer present.
### Plan summary
- **Unit 1:** `fabro-client` variable wrappers
- **Unit 2:** CLI args, dispatch, and `commands/variable/mod.rs`
- **Unit 3:** `list`, `get`, `set`, `rm` behavior modules
- **Unit 4:** Test harness helpers and integration tests
- **Unit 5:** Regenerated CLI docs + `variables.mdx` update
### Fabro Details
<details>
<summary>Ran 8 stages in 58m 50s for $23.81</summary>
| Stage | Duration | Cost | Retries |
|---|---|---|---|
| start | 0s | – | 0 |
| toolchain | 1s | – | 0 |
| preflight_compile | 2m 9s | – | 0 |
| preflight_lint | 2m 21s | – | 0 |
| implement | 31m 34s | $18.44 | 0 |
| simplify_opus | 8m 48s | $2.60 | 0 |
| simplify_gpt | 4m 1s | $2.77 | 0 |
| verify | 9m 17s | – | 0 |
| **Total** | **58m 50s** | **$23.81** | **0** |
</details>
<details>
<summary>Ran <code>ImplementPlan.fabro</code> (11 nodes and 14
edges)</summary>
```dot
digraph ImplementPlan {
graph [
goal="Implement and simplify",
model_stylesheet="
* { model: claude-opus-4-7; }
"
]
rankdir=LR
start [shape=Mdiamond, label="Start"]
exit [shape=Msquare, label="Exit"]
toolchain [label="Toolchain", shape=parallelogram, script="command -v cargo >/dev/null || { curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && sudo ln -sf $HOME/.cargo/bin/* /usr/local/bin/; }; cargo --version 2>&1", max_retries=0]
preflight_compile [label="Preflight Compile", shape=parallelogram, script="cargo check -q --workspace 2>&1", max_retries=0]
preflight_lint [label="Preflight Lint", shape=parallelogram, script="cargo +nightly-2026-04-14 clippy -q --workspace --all-targets -- -D warnings 2>&1", max_retries=0]
fix_lints [label="Fix Lints", prompt="The preflight lint step failed. Read the build output from context and fix all clippy lint warnings.", max_visits=3]
implement [label="Implement", prompt="Read the plan file referenced in the goal and implement every step. Make all the code changes described in the plan. Use red/green TDD.", model="gpt-55", reasoning_effort="xhigh"]
simplify_opus [label="Simplify (Opus)", prompt="@prompts/simplify.md"]
simplify_gpt [label="Simplify (GPT-55)", prompt="@prompts/simplify.md", model="gpt-55"]
verify [label="Verify", shape=parallelogram, script="git fetch origin main 2>&1 && git merge --no-edit --no-stat origin/main 2>&1 && cargo +nightly-2026-04-14 fmt --all 2>&1 && cargo dev docs refresh 2>&1 && cargo +nightly-2026-04-14 fmt --check --all 2>&1 && { command -v rg >/dev/null 2>&1 || { echo 'rg is required for verify'; exit 127; }; } && ! rg -n 'AuthMode::Disabled|RunAuthMethod|RunSubjectProvenance|\bActorRef\b|\bActorKind\b|AuthenticatedSubject|AuthenticatedService|AuthorizeRunScoped|AuthorizeRunBlob|AuthorizeStageArtifact|AuthorizeCommandLog|auth_method\s*==\s*\"disabled\"' lib/crates apps lib/packages docs/public/api-reference/fabro-api.yaml 2>&1 && cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D warnings 2>&1 && cargo nextest run --workspace --status-level slow --profile ci 2>&1 && cargo dev docs check 2>&1 && bun install --frozen-lockfile 2>&1 && (cd apps/fabro-web && bun run typecheck) 2>&1 && (cd apps/fabro-web && bun run test) 2>&1 && (cd lib/packages/fabro-api-client && bun run typecheck) 2>&1 && cargo dev build -- -p fabro-cli --release 2>&1", goal_gate=true, retry_target="fixup"]
fixup [label="Fixup", prompt="The verify step failed. Read the build output from context and fix all format, clippy, Rust test, docs, TypeScript typecheck/test, and build failures.", max_visits=3]
start -> toolchain
toolchain -> preflight_compile [condition="outcome=succeeded"]
toolchain -> exit
preflight_compile -> preflight_lint [condition="outcome=succeeded"]
preflight_compile -> exit
preflight_lint -> implement [condition="outcome=succeeded"]
preflight_lint -> fix_lints
fix_lints -> preflight_lint
implement -> simplify_opus -> simplify_gpt -> verify
verify -> exit [condition="outcome=succeeded"]
verify -> fixup
fixup -> verify
}
```
</details>
⚒️ Generated with [Fabro](https://fabro.sh)
---------
Co-authored-by: Fabro <noreply@fabro.sh>
133 lines
5.1 KiB
Rust
133 lines
5.1 KiB
Rust
use fabro_test::{fabro_snapshot, test_context};
|
|
|
|
#[test]
|
|
fn help() {
|
|
let context = test_context!();
|
|
let mut cmd = context.command();
|
|
cmd.arg("--help");
|
|
fabro_snapshot!(context.filters(), cmd, @"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
Usage: fabro [OPTIONS] [COMMAND]
|
|
|
|
Commands:
|
|
run Launch a workflow run
|
|
create Create a workflow run (allocate run dir, persist spec)
|
|
start Start a created workflow run on the server
|
|
attach Attach to a running or finished workflow run
|
|
events View the event log of a workflow run
|
|
logs View the raw worker tracing log of a workflow run
|
|
resume Resume an interrupted workflow run
|
|
rewind Rewind a workflow run to an earlier checkpoint
|
|
fork Fork a workflow run from an earlier checkpoint into a new run
|
|
wait Block until a workflow run completes
|
|
steer Steer a running agent mid-execution
|
|
ask Ask Fabro a read-only question about a run
|
|
preflight Validate run configuration without executing
|
|
validate Validate a workflow
|
|
graph Render a workflow graph as SVG
|
|
artifact Inspect and copy run artifacts (screenshots, reports, traces)
|
|
dump Export a run's durable state to a directory
|
|
rm Remove one or more workflow runs
|
|
inspect Show detailed information about a workflow run
|
|
approve Approve pending workflow runs
|
|
deny Deny pending workflow runs
|
|
archive Mark terminal runs as archived (reviewed, no further action needed). Archived runs are hidden from default listings
|
|
unarchive Restore archived runs to their prior terminal status
|
|
model List and test LLM models
|
|
mcp Model Context Protocol server
|
|
server Server operations
|
|
doctor Check environment and integration health
|
|
version Show client and server version information
|
|
install Set up the Fabro environment (LLMs, certs, GitHub)
|
|
uninstall Uninstall Fabro from this machine
|
|
auth Manage CLI authentication state
|
|
pr Pull request operations
|
|
parent Manage run parent links
|
|
secret Manage server-owned secrets
|
|
variable Manage server-owned variables
|
|
settings Inspect effective settings
|
|
workflow Workflow operations
|
|
discord Open the Discord community in the browser
|
|
docs Open the docs website in the browser
|
|
upgrade Upgrade fabro to the latest version
|
|
repo Repository commands
|
|
provider Provider operations
|
|
sandbox Sandbox operations (cp, ssh, preview)
|
|
completion Generate shell completions
|
|
system System maintenance commands
|
|
help Print this message or the help of the given subcommand(s)
|
|
|
|
Options:
|
|
--json Output as JSON [env: FABRO_JSON=]
|
|
--debug Enable DEBUG-level logging (default is INFO) [env: FABRO_DEBUG=]
|
|
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
|
|
--quiet Suppress non-essential output [env: FABRO_QUIET=]
|
|
--verbose Enable verbose output [env: FABRO_VERBOSE=]
|
|
-h, --help Print help
|
|
-V, --version Print version
|
|
----- stderr -----
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn no_args_prints_curated_landing() {
|
|
let context = test_context!();
|
|
let cmd = context.command();
|
|
fabro_snapshot!(context.filters(), cmd, @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
fabro — AI-powered workflow orchestration.
|
|
|
|
Usage: fabro <command>
|
|
|
|
Set up
|
|
|
|
fabro install Set up the Fabro environment (LLMs, certs, GitHub)
|
|
fabro doctor Check environment and integration health
|
|
fabro repo init Initialize Fabro in a repository
|
|
fabro server start Start the Fabro API server
|
|
fabro secret set Store a server-owned secret
|
|
fabro docs Open the docs website in the browser
|
|
|
|
Run workflows
|
|
|
|
fabro validate Validate a workflow
|
|
fabro preflight Validate run configuration without executing
|
|
fabro run Launch a workflow run
|
|
|
|
Inspect runs
|
|
|
|
fabro events View the event log of a workflow run
|
|
fabro logs View the raw worker tracing log of a workflow run
|
|
fabro sandbox ssh SSH into a run's sandbox
|
|
|
|
If you need help along the way:
|
|
|
|
Run fabro <command> --help for more information about a command.
|
|
Join our Discord at https://fabro.sh/discord to get help from the Fabro community.
|
|
|
|
For a full list of commands, run `fabro help`.
|
|
----- stderr -----
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn llm_namespace_is_not_available() {
|
|
let context = test_context!();
|
|
let mut cmd = context.command();
|
|
cmd.arg("llm");
|
|
fabro_snapshot!(context.filters(), cmd, @"
|
|
success: false
|
|
exit_code: 2
|
|
----- stdout -----
|
|
----- stderr -----
|
|
error: unrecognized subcommand 'llm'
|
|
|
|
Usage: fabro [OPTIONS] [COMMAND]
|
|
|
|
For more information, try '--help'.
|
|
");
|
|
}
|