mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-06 02:48:25 +00:00
Infer CLI mode from storage and server flags
This commit is contained in:
parent
aefb6e01c5
commit
79a366022f
51 changed files with 395 additions and 316 deletions
|
|
@ -119,13 +119,13 @@ mode = "server"
|
|||
base_url = "https://fabro.example.com:3000"
|
||||
```
|
||||
|
||||
Or use the `--mode` flag:
|
||||
Or use the `--server-url` flag:
|
||||
|
||||
```bash
|
||||
fabro --mode server --server-url https://fabro.example.com:3000 models list
|
||||
fabro --server-url https://fabro.example.com:3000 model list
|
||||
```
|
||||
|
||||
This applies to commands like `fabro models list`, `fabro llm chat`, and `fabro exec`. See [CLI Configuration](/reference/cli-configuration#mode) for the full options including mTLS setup.
|
||||
This applies to commands like `fabro model list`, `fabro llm chat`, and `fabro exec`. See [CLI Configuration](/reference/cli-configuration#mode) for the full options including mTLS setup.
|
||||
|
||||
## Next steps
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ Fabro now exposes a `POST /completions` endpoint for single-turn LLM completions
|
|||
The CLI's `fabro llm` commands can now target the server instead of calling providers directly:
|
||||
|
||||
```bash
|
||||
fabro llm prompt "Summarize this file" --mode server
|
||||
fabro llm chat --mode server
|
||||
fabro llm prompt "Summarize this file" --server-url http://localhost:3000
|
||||
fabro llm chat --server-url http://localhost:3000
|
||||
```
|
||||
|
||||
## Two new sandbox providers: exe.dev and Sprites
|
||||
|
|
@ -63,14 +63,14 @@ To migrate, regenerate your TypeScript client and update any direct API calls.
|
|||
- New `POST /completions` endpoint for single-turn LLM completions with SSE streaming and structured output via JSON Schema
|
||||
- New `GET /models` endpoint exposes the full LLM model catalog with pagination
|
||||
- New `POST /models/{id}/test` endpoint for testing model connectivity in server mode
|
||||
- Session endpoints for interactive LLM chat via `fabro llm chat --mode server`
|
||||
- Session endpoints for interactive LLM chat via `fabro llm chat --server-url http://localhost:3000`
|
||||
- Verification API reorganized: `/verifications` split into `/verification/criteria` and `/verification/controls`
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="CLI">
|
||||
- `fabro llm prompt --mode server` routes prompts through the Fabro server
|
||||
- `fabro llm chat --mode server` enables interactive chat sessions through the server
|
||||
- `fabro model list --mode standalone|server` controls whether model list comes from local config or server
|
||||
- `fabro llm prompt --server-url <url>` routes prompts through the Fabro server
|
||||
- `fabro llm chat --server-url <url>` enables interactive chat sessions through the server
|
||||
- `fabro model list --server-url <url>` fetches the model list from the Fabro server
|
||||
- Added `--goal` arg to `fabro run start` to override the workflow goal from the command line
|
||||
- Turn and tool-call counts now display correctly in non-TTY mode
|
||||
</Accordion>
|
||||
|
|
|
|||
|
|
@ -149,18 +149,14 @@ Customize the git author identity used for checkpoint commits. Overrides the ser
|
|||
|
||||
## `mode`
|
||||
|
||||
Controls whether commands run in-process or delegate to a running Fabro API server.
|
||||
Controls the default execution mode when neither `--storage-dir` nor `--server-url` is passed.
|
||||
|
||||
| Value | Description |
|
||||
|---|---|
|
||||
| `"standalone"` | Execute locally (default) |
|
||||
| `"server"` | Delegate to an Fabro API server |
|
||||
|
||||
Override with the `--mode` CLI flag:
|
||||
|
||||
```bash
|
||||
fabro --mode server models list
|
||||
```
|
||||
For a single invocation, `--storage-dir` implies standalone mode and `--server-url` implies server mode.
|
||||
|
||||
## `[server]` section
|
||||
|
||||
|
|
@ -170,10 +166,10 @@ Configuration for server mode.
|
|||
|---|---|---|
|
||||
| `base_url` | Server URL | `"http://localhost:3000"` |
|
||||
|
||||
Override the URL with the `--server-url` CLI flag:
|
||||
Passing `--server-url` implies server mode and overrides `server.base_url`:
|
||||
|
||||
```bash
|
||||
fabro --mode server --server-url https://fabro.example.com:3000 models list
|
||||
fabro --server-url https://fabro.example.com:3000 model list
|
||||
```
|
||||
|
||||
### `[server.tls]` section
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ These flags apply to all subcommands:
|
|||
|---|---|
|
||||
| `--debug` | Enable DEBUG-level logging (default is INFO) |
|
||||
| `--no-upgrade-check` | Skip the automatic background upgrade check |
|
||||
| `--storage-dir <DIR>` | Storage directory for local run data (default: `~/.fabro`). Implies standalone mode. |
|
||||
| `--server-url <URL>` | Fabro API server URL (overrides `server.base_url` from `cli.toml`). Implies server mode. |
|
||||
| `-h, --help` | Print help |
|
||||
| `-V, --version` | Print version |
|
||||
|
||||
|
|
@ -70,7 +72,6 @@ fabro run run.toml
|
|||
| Argument / Flag | Description |
|
||||
|---|---|
|
||||
| `<WORKFLOW>` | Path to a `.fabro` workflow file, `.toml` task config, or workflow name (resolved from `fabro/workflows/` in the project, then `~/.fabro/workflows/`). |
|
||||
| `--storage-dir <DIR>` | Storage directory (default: `~/.fabro`) |
|
||||
| `--dry-run` | Execute with a simulated LLM backend |
|
||||
| `--auto-approve` | Auto-approve all human gates |
|
||||
| `--model <MODEL>` | Override default LLM model |
|
||||
|
|
@ -249,8 +250,6 @@ fabro exec "Refactor the auth module" --permissions full --auto-approve
|
|||
| `--verbose` | Print full LLM request/response JSON to stderr | — |
|
||||
| `--skills-dir <DIR>` | Directory containing skill files (overrides default discovery) | — |
|
||||
| `--output-format <FORMAT>` | Output format: `text` (human-readable) or `json` (NDJSON event stream) | `text` |
|
||||
| `--mode <MODE>` | `standalone` (default) or `server` — in server mode, routes through the Fabro API's `/completions` endpoint | `standalone` |
|
||||
| `--server-url <URL>` | Fabro API server URL (overrides `server.base_url` from `cli.toml`) | — |
|
||||
|
||||
Permission levels control which tools are auto-approved: `read-only` allows read tools (`read_file`, `grep`, `glob`, `list_dir`) and subagent tools; `read-write` adds write tools (`write_file`, `edit_file`, `apply_patch`); `full` allows all tools including shell commands. Tools outside the permission level are either interactively prompted (if a TTY is present) or denied (with `--auto-approve`). See [default models by provider](/core-concepts/models#default-models).
|
||||
|
||||
|
|
@ -284,14 +283,13 @@ Start an interactive multi-turn chat session. In server mode, the session is bac
|
|||
```bash
|
||||
fabro llm chat
|
||||
fabro llm chat -m claude-opus-4-6 -s "You are a helpful coding assistant"
|
||||
fabro llm chat --mode server
|
||||
fabro llm chat --server-url http://localhost:3000
|
||||
```
|
||||
|
||||
| Flag | Description |
|
||||
|---|---|
|
||||
| `-m, --model <MODEL>` | Model to use |
|
||||
| `-s, --system <SYSTEM>` | System prompt |
|
||||
| `--mode <MODE>` | `standalone` (default) or `server` — in server mode, routes through the Fabro API |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -303,14 +301,13 @@ List available LLM models from the built-in catalog. Running `fabro model` with
|
|||
fabro model list
|
||||
fabro model list -p anthropic
|
||||
fabro model list -q sonnet
|
||||
fabro model list --mode server
|
||||
fabro model list --server-url http://localhost:3000
|
||||
```
|
||||
|
||||
| Flag | Description |
|
||||
|---|---|
|
||||
| `-p, --provider <PROVIDER>` | Filter by provider |
|
||||
| `-q, --query <QUERY>` | Search for models matching this string (checks ID, display name, and aliases) |
|
||||
| `--mode <MODE>` | `standalone` (default) or `server` — in server mode, fetches the catalog from the Fabro API |
|
||||
|
||||
## `fabro model test`
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ Each `fabro run` invocation creates a timestamped directory under `~/.fabro/runs
|
|||
~/.fabro/runs/20260307-01JQXYZ123ABC456DEF789/
|
||||
```
|
||||
|
||||
The naming format is `YYYYMMDD-{run_id}`, where `run_id` is the ULID assigned to the run. You can override the base storage directory with `--storage-dir` (the runs directory will be `<storage-dir>/runs/`).
|
||||
The naming format is `YYYYMMDD-{run_id}`, where `run_id` is the ULID assigned to the run. You can override the base storage directory with the global `--storage-dir` flag (the runs directory will be `<storage-dir>/runs/`).
|
||||
|
||||
## Root-level files
|
||||
|
||||
|
|
|
|||
|
|
@ -6,9 +6,6 @@ use fabro_agent::cli::AgentArgs;
|
|||
use fabro_graphviz::render::GraphFormat;
|
||||
use fabro_llm::cli::{ChatArgs, ModelsCommand, PromptArgs};
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
use crate::cli_config;
|
||||
|
||||
pub(crate) const LONG_VERSION: &str = concat!(
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
" (",
|
||||
|
|
@ -36,26 +33,14 @@ pub(crate) struct GlobalArgs {
|
|||
#[arg(long, global = true, conflicts_with = "quiet")]
|
||||
pub verbose: bool,
|
||||
|
||||
/// Execution mode: standalone (in-process) or server (delegate to API)
|
||||
#[cfg(feature = "server")]
|
||||
#[arg(long, global = true, value_parser = parse_execution_mode)]
|
||||
pub mode: Option<cli_config::ExecutionMode>,
|
||||
|
||||
/// Server URL (overrides server.base_url from cli.toml)
|
||||
#[cfg(feature = "server")]
|
||||
/// Storage directory (default: ~/.fabro)
|
||||
#[arg(long, global = true)]
|
||||
pub server_url: Option<String>,
|
||||
}
|
||||
pub storage_dir: Option<PathBuf>,
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
pub(crate) fn parse_execution_mode(s: &str) -> Result<cli_config::ExecutionMode, String> {
|
||||
match s {
|
||||
"standalone" => Ok(cli_config::ExecutionMode::Standalone),
|
||||
"server" => Ok(cli_config::ExecutionMode::Server),
|
||||
_ => Err(format!(
|
||||
"invalid mode '{s}', expected 'standalone' or 'server'"
|
||||
)),
|
||||
}
|
||||
#[cfg(feature = "server")]
|
||||
/// Server URL (overrides server.base_url from cli.toml)
|
||||
#[arg(long, global = true, conflicts_with = "storage_dir")]
|
||||
pub server_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, ValueEnum)]
|
||||
|
|
@ -102,10 +87,6 @@ pub(crate) struct RunArgs {
|
|||
#[arg(required = true)]
|
||||
pub(crate) workflow: Option<PathBuf>,
|
||||
|
||||
/// Storage directory (default: ~/.fabro)
|
||||
#[arg(long)]
|
||||
pub(crate) storage_dir: Option<PathBuf>,
|
||||
|
||||
/// Execute with simulated LLM backend
|
||||
#[arg(long)]
|
||||
pub(crate) dry_run: bool,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,14 @@
|
|||
#[cfg(feature = "server")]
|
||||
use std::path::Path;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use fabro_config::cli::*;
|
||||
|
||||
use fabro_config::ConfigLayer;
|
||||
use fabro_config::FabroSettings;
|
||||
|
||||
use crate::args::GlobalArgs;
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
use tracing::debug;
|
||||
|
||||
|
|
@ -11,9 +16,19 @@ pub(crate) fn load_cli_settings() -> anyhow::Result<FabroSettings> {
|
|||
ConfigLayer::cli()?.resolve()
|
||||
}
|
||||
|
||||
pub(crate) fn load_cli_settings_with_globals(
|
||||
globals: &GlobalArgs,
|
||||
) -> anyhow::Result<FabroSettings> {
|
||||
let mut layer = ConfigLayer::cli()?;
|
||||
if let Some(dir) = &globals.storage_dir {
|
||||
layer.storage_dir = Some(dir.clone());
|
||||
}
|
||||
layer.resolve()
|
||||
}
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct ResolvedMode {
|
||||
pub(crate) struct ResolvedMode {
|
||||
pub mode: ExecutionMode,
|
||||
pub server_base_url: String,
|
||||
pub tls: Option<ClientTlsSettings>,
|
||||
|
|
@ -23,14 +38,18 @@ pub struct ResolvedMode {
|
|||
const DEFAULT_SERVER_URL: &str = "http://localhost:3000";
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
pub fn resolve_mode(
|
||||
cli_mode: Option<ExecutionMode>,
|
||||
pub(crate) fn resolve_mode(
|
||||
cli_storage_dir: Option<&Path>,
|
||||
cli_server_url: Option<&str>,
|
||||
settings: &FabroSettings,
|
||||
) -> ResolvedMode {
|
||||
let mode = cli_mode
|
||||
.or_else(|| settings.mode.clone())
|
||||
.unwrap_or_default();
|
||||
let mode = if cli_server_url.is_some() {
|
||||
ExecutionMode::Server
|
||||
} else if cli_storage_dir.is_some() {
|
||||
ExecutionMode::Standalone
|
||||
} else {
|
||||
settings.mode.clone().unwrap_or_default()
|
||||
};
|
||||
|
||||
let server_defaults = settings.server.as_ref();
|
||||
|
||||
|
|
@ -51,7 +70,9 @@ pub fn resolve_mode(
|
|||
}
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
pub fn build_server_client(tls: Option<&ClientTlsSettings>) -> anyhow::Result<reqwest::Client> {
|
||||
pub(crate) fn build_server_client(
|
||||
tls: Option<&ClientTlsSettings>,
|
||||
) -> anyhow::Result<reqwest::Client> {
|
||||
let Some(tls) = tls else {
|
||||
return Ok(reqwest::Client::new());
|
||||
};
|
||||
|
|
@ -82,7 +103,7 @@ pub fn build_server_client(tls: Option<&ClientTlsSettings>) -> anyhow::Result<re
|
|||
|
||||
#[cfg(all(test, feature = "server"))]
|
||||
mod tests {
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use super::*;
|
||||
|
||||
|
|
@ -97,6 +118,31 @@ mod tests {
|
|||
assert_eq!(resolved.tls, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_mode_storage_dir_forces_standalone() {
|
||||
let settings = FabroSettings {
|
||||
mode: Some(ExecutionMode::Server),
|
||||
..FabroSettings::default()
|
||||
};
|
||||
let resolved = resolve_mode(Some(Path::new("/tmp/fabro")), None, &settings);
|
||||
assert_eq!(resolved.mode, ExecutionMode::Standalone);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_mode_server_url_forces_server() {
|
||||
let settings = FabroSettings {
|
||||
mode: Some(ExecutionMode::Standalone),
|
||||
server: Some(ServerSettings {
|
||||
base_url: Some("https://config.example.com".to_string()),
|
||||
tls: None,
|
||||
}),
|
||||
..FabroSettings::default()
|
||||
};
|
||||
let resolved = resolve_mode(None, Some("https://cli.example.com"), &settings);
|
||||
assert_eq!(resolved.mode, ExecutionMode::Server);
|
||||
assert_eq!(resolved.server_base_url, "https://cli.example.com");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_mode_config_overrides_default() {
|
||||
let settings = FabroSettings {
|
||||
|
|
@ -112,25 +158,6 @@ mod tests {
|
|||
assert_eq!(resolved.server_base_url, "https://config.example.com");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_mode_cli_overrides_config() {
|
||||
let settings = FabroSettings {
|
||||
mode: Some(ExecutionMode::Standalone),
|
||||
server: Some(ServerSettings {
|
||||
base_url: Some("https://config.example.com".to_string()),
|
||||
tls: None,
|
||||
}),
|
||||
..FabroSettings::default()
|
||||
};
|
||||
let resolved = resolve_mode(
|
||||
Some(ExecutionMode::Server),
|
||||
Some("https://cli.example.com"),
|
||||
&settings,
|
||||
);
|
||||
assert_eq!(resolved.mode, ExecutionMode::Server);
|
||||
assert_eq!(resolved.server_base_url, "https://cli.example.com");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_mode_cli_url_overrides_config_url() {
|
||||
let settings = FabroSettings {
|
||||
|
|
|
|||
|
|
@ -6,12 +6,12 @@ use fabro_store::RuntimeState;
|
|||
use fabro_workflows::assets::{AssetEntry, scan_assets};
|
||||
use fabro_workflows::run_lookup::{resolve_run, runs_base};
|
||||
|
||||
use crate::args::AssetCpArgs;
|
||||
use crate::cli_config::load_cli_settings;
|
||||
use crate::args::{AssetCpArgs, GlobalArgs};
|
||||
use crate::cli_config::load_cli_settings_with_globals;
|
||||
use crate::shared::split_run_path;
|
||||
|
||||
pub(super) fn cp_command(args: &AssetCpArgs) -> Result<()> {
|
||||
let cli_settings = load_cli_settings()?;
|
||||
pub(super) fn cp_command(args: &AssetCpArgs, globals: &GlobalArgs) -> Result<()> {
|
||||
let cli_settings = load_cli_settings_with_globals(globals)?;
|
||||
let base = runs_base(&cli_settings.storage_dir());
|
||||
let (run_id, asset_path) = parse_source(&args.source);
|
||||
let run = resolve_run(&base, run_id)?;
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@ use fabro_store::RuntimeState;
|
|||
use fabro_workflows::assets::scan_assets;
|
||||
use fabro_workflows::run_lookup::{resolve_run, runs_base};
|
||||
|
||||
use crate::args::AssetListArgs;
|
||||
use crate::cli_config::load_cli_settings;
|
||||
use crate::args::{AssetListArgs, GlobalArgs};
|
||||
use crate::cli_config::load_cli_settings_with_globals;
|
||||
use crate::shared::format_size;
|
||||
|
||||
pub(super) fn list_command(args: &AssetListArgs) -> Result<()> {
|
||||
let cli_settings = load_cli_settings()?;
|
||||
pub(super) fn list_command(args: &AssetListArgs, globals: &GlobalArgs) -> Result<()> {
|
||||
let cli_settings = load_cli_settings_with_globals(globals)?;
|
||||
let base = runs_base(&cli_settings.storage_dir());
|
||||
let run = resolve_run(&base, &args.run_id)?;
|
||||
let runtime_state = RuntimeState::new(&run.path);
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ mod list;
|
|||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::args::{AssetCommand, AssetNamespace};
|
||||
use crate::args::{AssetCommand, AssetNamespace, GlobalArgs};
|
||||
|
||||
pub(crate) fn dispatch(ns: AssetNamespace) -> Result<()> {
|
||||
pub(crate) fn dispatch(ns: AssetNamespace, globals: &GlobalArgs) -> Result<()> {
|
||||
match ns.command {
|
||||
AssetCommand::List(args) => list::list_command(&args),
|
||||
AssetCommand::Cp(args) => cp::cp_command(&args),
|
||||
AssetCommand::List(args) => list::list_command(&args, globals),
|
||||
AssetCommand::Cp(args) => cp::cp_command(&args, globals),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,31 @@
|
|||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::args::{ConfigCommand, ConfigNamespace, ConfigShowArgs};
|
||||
use crate::args::{ConfigCommand, ConfigNamespace, ConfigShowArgs, GlobalArgs};
|
||||
use fabro_config::{ConfigLayer, FabroSettings};
|
||||
|
||||
pub(crate) fn dispatch(ns: ConfigNamespace) -> anyhow::Result<()> {
|
||||
pub(crate) fn dispatch(ns: ConfigNamespace, globals: &GlobalArgs) -> anyhow::Result<()> {
|
||||
match ns.command {
|
||||
ConfigCommand::Show(args) => show_command(&args),
|
||||
ConfigCommand::Show(args) => show_command(&args, globals),
|
||||
}
|
||||
}
|
||||
|
||||
fn merged_config(workflow: Option<&Path>) -> anyhow::Result<FabroSettings> {
|
||||
fn merged_config(workflow: Option<&Path>, globals: &GlobalArgs) -> anyhow::Result<FabroSettings> {
|
||||
let cwd = std::env::current_dir()?;
|
||||
let base = match workflow {
|
||||
Some(path) => ConfigLayer::for_workflow(path, &cwd)?,
|
||||
None => ConfigLayer::project(&cwd)?,
|
||||
};
|
||||
let mut cli = ConfigLayer::cli()?;
|
||||
if let Some(dir) = &globals.storage_dir {
|
||||
cli.storage_dir = Some(dir.clone());
|
||||
}
|
||||
|
||||
base.combine(ConfigLayer::cli()?).resolve()
|
||||
base.combine(cli).resolve()
|
||||
}
|
||||
|
||||
pub(crate) fn show_command(args: &ConfigShowArgs) -> anyhow::Result<()> {
|
||||
let config = merged_config(args.workflow.as_deref())?;
|
||||
pub(crate) fn show_command(args: &ConfigShowArgs, globals: &GlobalArgs) -> anyhow::Result<()> {
|
||||
let config = merged_config(args.workflow.as_deref(), globals)?;
|
||||
let mut yaml = serde_yaml::to_string(&config)?;
|
||||
if !yaml.ends_with('\n') {
|
||||
yaml.push('\n');
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use crate::args::GlobalArgs;
|
|||
use crate::cli_config;
|
||||
|
||||
pub(crate) async fn execute(mut args: AgentArgs, globals: &GlobalArgs) -> Result<()> {
|
||||
let cli_settings = cli_config::load_cli_settings()?;
|
||||
let cli_settings = cli_config::load_cli_settings_with_globals(globals)?;
|
||||
#[cfg(feature = "sleep_inhibitor")]
|
||||
let _sleep_guard = crate::sleep_inhibitor::guard(cli_settings.prevent_idle_sleep_enabled());
|
||||
let exec_defaults = cli_settings.exec.as_ref();
|
||||
|
|
@ -21,7 +21,7 @@ pub(crate) async fn execute(mut args: AgentArgs, globals: &GlobalArgs) -> Result
|
|||
);
|
||||
#[cfg(feature = "server")]
|
||||
let resolved = cli_config::resolve_mode(
|
||||
globals.mode.clone(),
|
||||
globals.storage_dir.as_deref(),
|
||||
globals.server_url.as_deref(),
|
||||
&cli_settings,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ pub(super) async fn execute(
|
|||
#[cfg(feature = "server")]
|
||||
{
|
||||
let resolved = crate::cli_config::resolve_mode(
|
||||
globals.mode.clone(),
|
||||
globals.storage_dir.as_deref(),
|
||||
globals.server_url.as_deref(),
|
||||
cli_settings,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ mod prompt;
|
|||
use anyhow::Result;
|
||||
|
||||
use crate::args::{GlobalArgs, LlmCommand, LlmNamespace};
|
||||
use crate::cli_config::load_cli_settings;
|
||||
use crate::cli_config::load_cli_settings_with_globals;
|
||||
|
||||
pub(crate) async fn dispatch(ns: LlmNamespace, globals: &GlobalArgs) -> Result<()> {
|
||||
let cli_settings = load_cli_settings()?;
|
||||
let cli_settings = load_cli_settings_with_globals(globals)?;
|
||||
|
||||
match ns.command {
|
||||
LlmCommand::Prompt(args) => prompt::execute(args, &cli_settings, globals).await,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ pub(super) async fn execute(
|
|||
#[cfg(feature = "server")]
|
||||
{
|
||||
let resolved = crate::cli_config::resolve_mode(
|
||||
globals.mode.clone(),
|
||||
globals.storage_dir.as_deref(),
|
||||
globals.server_url.as_deref(),
|
||||
cli_settings,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pub(crate) async fn execute(command: Option<ModelsCommand>, globals: &GlobalArgs
|
|||
let server = {
|
||||
#[cfg(feature = "server")]
|
||||
{
|
||||
let cli_settings = cli_config::load_cli_settings()?;
|
||||
let cli_settings = cli_config::load_cli_settings_with_globals(globals)?;
|
||||
let resolved = cli_config::resolve_mode(
|
||||
globals.mode.clone(),
|
||||
globals.storage_dir.as_deref(),
|
||||
globals.server_url.as_deref(),
|
||||
&cli_settings,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -5,14 +5,15 @@ use fabro_config::FabroSettingsExt;
|
|||
use fabro_workflows::run_lookup::runs_base;
|
||||
use tracing::info;
|
||||
|
||||
use crate::args::PrCloseArgs;
|
||||
use crate::cli_config::load_cli_settings;
|
||||
use crate::args::{GlobalArgs, PrCloseArgs};
|
||||
use crate::cli_config::load_cli_settings_with_globals;
|
||||
|
||||
pub(super) async fn close_command(
|
||||
args: PrCloseArgs,
|
||||
github_app: Option<fabro_github::GitHubAppCredentials>,
|
||||
globals: &GlobalArgs,
|
||||
) -> Result<()> {
|
||||
let cli_settings = load_cli_settings()?;
|
||||
let cli_settings = load_cli_settings_with_globals(globals)?;
|
||||
let base = runs_base(&cli_settings.storage_dir());
|
||||
close_from(&base, args, github_app).await
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,14 +12,16 @@ use fabro_workflows::records::{
|
|||
use fabro_workflows::run_lookup::{resolve_run_combined, runs_base};
|
||||
use tracing::info;
|
||||
|
||||
use crate::args::PrCreateArgs;
|
||||
use crate::cli_config::load_cli_settings;
|
||||
use crate::args::{GlobalArgs, PrCreateArgs};
|
||||
use crate::cli_config::load_cli_settings_with_globals;
|
||||
use crate::store;
|
||||
|
||||
pub(super) async fn create_command(
|
||||
args: PrCreateArgs,
|
||||
github_app: Option<fabro_github::GitHubAppCredentials>,
|
||||
globals: &GlobalArgs,
|
||||
) -> Result<()> {
|
||||
let cli_settings = load_cli_settings()?;
|
||||
let cli_settings = load_cli_settings_with_globals(globals)?;
|
||||
let base = runs_base(&cli_settings.storage_dir());
|
||||
create_from(&base, args, github_app).await
|
||||
}
|
||||
|
|
@ -30,10 +32,10 @@ async fn create_from(
|
|||
github_app: Option<fabro_github::GitHubAppCredentials>,
|
||||
) -> Result<()> {
|
||||
let storage_dir = base.parent().unwrap_or(base);
|
||||
let store = crate::store::build_store(storage_dir)?;
|
||||
let store = store::build_store(storage_dir)?;
|
||||
let run = resolve_run_combined(store.as_ref(), base, &args.run_id).await?;
|
||||
let run_dir = run.path.clone();
|
||||
let run_store = crate::store::open_run_reader(storage_dir, &run.run_id).await?;
|
||||
let run_store = store::open_run_reader(storage_dir, &run.run_id).await?;
|
||||
|
||||
let record = match run_store.as_ref() {
|
||||
Some(run_store) => run_store
|
||||
|
|
|
|||
|
|
@ -7,16 +7,18 @@ use fabro_workflows::run_lookup::{runs_base, scan_runs_combined};
|
|||
use futures::future::join_all;
|
||||
use tracing::info;
|
||||
|
||||
use crate::args::PrListArgs;
|
||||
use crate::cli_config::load_cli_settings;
|
||||
use crate::args::{GlobalArgs, PrListArgs};
|
||||
use crate::cli_config::load_cli_settings_with_globals;
|
||||
use crate::store;
|
||||
|
||||
pub(super) async fn list_command(
|
||||
args: PrListArgs,
|
||||
github_app: Option<fabro_github::GitHubAppCredentials>,
|
||||
globals: &GlobalArgs,
|
||||
) -> Result<()> {
|
||||
let cli_settings = load_cli_settings()?;
|
||||
let cli_settings = load_cli_settings_with_globals(globals)?;
|
||||
let base = runs_base(&cli_settings.storage_dir());
|
||||
let store = crate::store::build_store(&cli_settings.storage_dir())?;
|
||||
let store = store::build_store(&cli_settings.storage_dir())?;
|
||||
list_from(store.as_ref(), &base, args, github_app).await
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,14 +6,15 @@ use tracing::info;
|
|||
|
||||
use fabro_workflows::run_lookup::runs_base;
|
||||
|
||||
use crate::args::PrMergeArgs;
|
||||
use crate::cli_config::load_cli_settings;
|
||||
use crate::args::{GlobalArgs, PrMergeArgs};
|
||||
use crate::cli_config::load_cli_settings_with_globals;
|
||||
|
||||
pub(super) async fn merge_command(
|
||||
args: PrMergeArgs,
|
||||
github_app: Option<fabro_github::GitHubAppCredentials>,
|
||||
globals: &GlobalArgs,
|
||||
) -> Result<()> {
|
||||
let cli_settings = load_cli_settings()?;
|
||||
let cli_settings = load_cli_settings_with_globals(globals)?;
|
||||
let base = runs_base(&cli_settings.storage_dir());
|
||||
merge_from(&base, args, github_app).await
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,20 +11,21 @@ use anyhow::{Context, Result};
|
|||
use fabro_workflows::pull_request::PullRequestRecord;
|
||||
use fabro_workflows::run_lookup::resolve_run_combined;
|
||||
|
||||
use crate::args::{PrCommand, PrNamespace};
|
||||
use crate::cli_config::load_cli_settings;
|
||||
use crate::args::{GlobalArgs, PrCommand, PrNamespace};
|
||||
use crate::cli_config::load_cli_settings_with_globals;
|
||||
use crate::shared::github::build_github_app_credentials;
|
||||
use crate::store;
|
||||
|
||||
pub(crate) async fn dispatch(ns: PrNamespace) -> Result<()> {
|
||||
let cli_settings = load_cli_settings()?;
|
||||
pub(crate) async fn dispatch(ns: PrNamespace, globals: &GlobalArgs) -> Result<()> {
|
||||
let cli_settings = load_cli_settings_with_globals(globals)?;
|
||||
let github_app = build_github_app_credentials(cli_settings.app_id());
|
||||
|
||||
match ns.command {
|
||||
PrCommand::Create(args) => create::create_command(args, github_app).await,
|
||||
PrCommand::List(args) => list::list_command(args, github_app).await,
|
||||
PrCommand::View(args) => view::view_command(args, github_app).await,
|
||||
PrCommand::Merge(args) => merge::merge_command(args, github_app).await,
|
||||
PrCommand::Close(args) => close::close_command(args, github_app).await,
|
||||
PrCommand::Create(args) => create::create_command(args, github_app, globals).await,
|
||||
PrCommand::List(args) => list::list_command(args, github_app, globals).await,
|
||||
PrCommand::View(args) => view::view_command(args, github_app, globals).await,
|
||||
PrCommand::Merge(args) => merge::merge_command(args, github_app, globals).await,
|
||||
PrCommand::Close(args) => close::close_command(args, github_app, globals).await,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -33,7 +34,7 @@ pub(crate) async fn load_pr_record(
|
|||
run_id: &str,
|
||||
) -> Result<(PullRequestRecord, PathBuf)> {
|
||||
let storage_dir = base.parent().unwrap_or(base);
|
||||
let store = crate::store::build_store(storage_dir)?;
|
||||
let store = store::build_store(storage_dir)?;
|
||||
let run_dir = resolve_run_combined(store.as_ref(), base, run_id)
|
||||
.await?
|
||||
.path;
|
||||
|
|
|
|||
|
|
@ -6,14 +6,15 @@ use tracing::info;
|
|||
|
||||
use fabro_workflows::run_lookup::runs_base;
|
||||
|
||||
use crate::args::PrViewArgs;
|
||||
use crate::cli_config::load_cli_settings;
|
||||
use crate::args::{GlobalArgs, PrViewArgs};
|
||||
use crate::cli_config::load_cli_settings_with_globals;
|
||||
|
||||
pub(super) async fn view_command(
|
||||
args: PrViewArgs,
|
||||
github_app: Option<fabro_github::GitHubAppCredentials>,
|
||||
globals: &GlobalArgs,
|
||||
) -> Result<()> {
|
||||
let cli_settings = load_cli_settings()?;
|
||||
let cli_settings = load_cli_settings_with_globals(globals)?;
|
||||
let base = runs_base(&cli_settings.storage_dir());
|
||||
view_from(&base, args, github_app).await
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,13 +16,17 @@ use fabro_util::terminal::Styles;
|
|||
use fabro_workflows::git::{GitSyncStatus, sync_status};
|
||||
use fabro_workflows::operations::{ValidateInput, WorkflowInput, validate};
|
||||
|
||||
use crate::args::PreflightArgs;
|
||||
use crate::args::{GlobalArgs, PreflightArgs};
|
||||
use crate::cli_config::load_cli_settings_with_globals;
|
||||
use crate::shared::github::build_github_app_credentials;
|
||||
|
||||
pub(crate) async fn execute(mut args: PreflightArgs) -> anyhow::Result<()> {
|
||||
pub(crate) async fn execute(mut args: PreflightArgs, globals: &GlobalArgs) -> anyhow::Result<()> {
|
||||
let styles: &'static Styles = Box::leak(Box::new(Styles::detect_stderr()));
|
||||
let cli = ConfigLayer::cli()?;
|
||||
let cli_settings: FabroSettings = cli.clone().resolve()?;
|
||||
let mut cli = ConfigLayer::cli()?;
|
||||
if let Some(dir) = &globals.storage_dir {
|
||||
cli.storage_dir = Some(dir.clone());
|
||||
}
|
||||
let cli_settings: FabroSettings = load_cli_settings_with_globals(globals)?;
|
||||
args.verbose = args.verbose || cli_settings.verbose_enabled();
|
||||
|
||||
let github_app = build_github_app_credentials(cli_settings.app_id());
|
||||
|
|
|
|||
|
|
@ -16,9 +16,10 @@ use fabro_workflows::outcome::StageStatus;
|
|||
use fabro_workflows::records::{Conclusion, ConclusionExt, RunRecord, RunRecordExt};
|
||||
use fabro_workflows::run_status::{RunStatus, RunStatusRecord, RunStatusRecordExt};
|
||||
use tokio::signal::ctrl_c;
|
||||
use tokio::time::sleep;
|
||||
use tokio::time::{self, sleep};
|
||||
|
||||
use super::run_progress;
|
||||
use crate::store;
|
||||
|
||||
#[cfg(test)]
|
||||
const ATTACH_STARTUP_GRACE: Duration = Duration::from_millis(200);
|
||||
|
|
@ -44,7 +45,7 @@ pub(crate) async fn attach_run(
|
|||
.map(|record| record.settings.storage_dir()),
|
||||
run_id.or_else(|| run_record.as_ref().map(|record| record.run_id.as_str())),
|
||||
) {
|
||||
match crate::store::open_run_reader(&storage_dir, run_id).await {
|
||||
match store::open_run_reader(&storage_dir, run_id).await {
|
||||
Ok(Some(run_store)) => match run_store.list_events().await {
|
||||
Ok(events) => {
|
||||
let event_lines = events
|
||||
|
|
@ -55,7 +56,7 @@ pub(crate) async fn attach_run(
|
|||
run_dir,
|
||||
run_store.as_ref(),
|
||||
event_lines,
|
||||
events.last().map(|event| event.seq).unwrap_or(0),
|
||||
events.last().map_or(0, |event| event.seq),
|
||||
kill_on_detach,
|
||||
styles,
|
||||
engine_child,
|
||||
|
|
@ -158,7 +159,7 @@ async fn attach_run_store(
|
|||
}
|
||||
|
||||
let mut saw_event = false;
|
||||
match tokio::time::timeout(Duration::from_millis(100), stream.next()).await {
|
||||
match time::timeout(Duration::from_millis(100), stream.next()).await {
|
||||
Ok(Some(Ok(event))) => {
|
||||
let line = event_payload_line(&event)?;
|
||||
progress_ui.handle_json_line(&line);
|
||||
|
|
@ -642,27 +643,24 @@ fn determine_exit_code(conclusion_path: &Path, status_record: Option<RunStatusRe
|
|||
}
|
||||
|
||||
async fn determine_exit_code_with_store(run_store: &dyn RunStore, run_dir: &Path) -> ExitCode {
|
||||
match run_store.get_conclusion().await {
|
||||
Ok(Some(conclusion)) => {
|
||||
let success = matches!(
|
||||
conclusion.status,
|
||||
StageStatus::Success | StageStatus::PartialSuccess
|
||||
);
|
||||
if success {
|
||||
ExitCode::from(0)
|
||||
} else {
|
||||
ExitCode::from(1)
|
||||
}
|
||||
}
|
||||
Ok(None) | Err(_) => {
|
||||
let status_path = run_dir.join("status.json");
|
||||
let conclusion_path = run_dir.join("conclusion.json");
|
||||
let status_record = match run_store.get_status().await {
|
||||
Ok(record) => record.or_else(|| read_status_record(&status_path)),
|
||||
Err(_) => read_status_record(&status_path),
|
||||
};
|
||||
determine_exit_code(&conclusion_path, status_record)
|
||||
if let Ok(Some(conclusion)) = run_store.get_conclusion().await {
|
||||
let success = matches!(
|
||||
conclusion.status,
|
||||
StageStatus::Success | StageStatus::PartialSuccess
|
||||
);
|
||||
if success {
|
||||
ExitCode::from(0)
|
||||
} else {
|
||||
ExitCode::from(1)
|
||||
}
|
||||
} else {
|
||||
let status_path = run_dir.join("status.json");
|
||||
let conclusion_path = run_dir.join("conclusion.json");
|
||||
let status_record = match run_store.get_status().await {
|
||||
Ok(record) => record.or_else(|| read_status_record(&status_path)),
|
||||
Err(_) => read_status_record(&status_path),
|
||||
};
|
||||
determine_exit_code(&conclusion_path, status_record)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,16 +3,18 @@ use fabro_config::ConfigLayer;
|
|||
use fabro_util::terminal::Styles;
|
||||
|
||||
use crate::args::{GlobalArgs, RunArgs};
|
||||
use crate::cli_config;
|
||||
|
||||
pub(crate) async fn execute(mut args: RunArgs, _globals: &GlobalArgs) -> Result<()> {
|
||||
pub(crate) async fn execute(mut args: RunArgs, globals: &GlobalArgs) -> Result<()> {
|
||||
let styles: &'static Styles = Box::leak(Box::new(Styles::detect_stderr()));
|
||||
let cli_settings = cli_config::load_cli_settings_with_globals(globals)?;
|
||||
let cli = ConfigLayer::cli()?;
|
||||
let cli_settings = cli.clone().resolve()?;
|
||||
args.verbose = args.verbose || cli_settings.verbose_enabled();
|
||||
|
||||
let quiet = args.detach;
|
||||
let prevent_idle_sleep = cli_settings.prevent_idle_sleep_enabled();
|
||||
let (run_id, run_dir) = super::create::create_run(&args, cli, styles, quiet)?;
|
||||
let (run_id, run_dir) =
|
||||
super::create::create_run(&args, cli, styles, quiet, globals.storage_dir.clone())?;
|
||||
|
||||
#[cfg(feature = "sleep_inhibitor")]
|
||||
let _sleep_guard = crate::sleep_inhibitor::guard(prevent_idle_sleep);
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ use fabro_workflows::run_lookup::{resolve_run, runs_base};
|
|||
use tokio::fs;
|
||||
use tracing::{debug, info};
|
||||
|
||||
use crate::args::CpArgs;
|
||||
use crate::cli_config::load_cli_settings;
|
||||
use crate::args::{CpArgs, GlobalArgs};
|
||||
use crate::cli_config::load_cli_settings_with_globals;
|
||||
use crate::shared::split_run_path;
|
||||
|
||||
enum CopyDirection {
|
||||
|
|
@ -26,9 +26,9 @@ enum CopyDirection {
|
|||
},
|
||||
}
|
||||
|
||||
pub(crate) async fn cp_command(args: CpArgs) -> Result<()> {
|
||||
pub(crate) async fn cp_command(args: CpArgs, globals: &GlobalArgs) -> Result<()> {
|
||||
let direction = parse_direction(&args.src, &args.dst)?;
|
||||
let cli_settings = load_cli_settings()?;
|
||||
let cli_settings = load_cli_settings_with_globals(globals)?;
|
||||
let base = runs_base(&cli_settings.storage_dir());
|
||||
|
||||
match direction {
|
||||
|
|
|
|||
|
|
@ -16,12 +16,16 @@ pub(crate) fn create_run(
|
|||
cli_defaults: ConfigLayer,
|
||||
styles: &Styles,
|
||||
quiet: bool,
|
||||
storage_dir_override: Option<PathBuf>,
|
||||
) -> anyhow::Result<(String, PathBuf)> {
|
||||
let workflow_path = args
|
||||
.workflow
|
||||
.as_ref()
|
||||
.ok_or_else(|| anyhow::anyhow!("--workflow is required"))?;
|
||||
let cli_args_config = ConfigLayer::try_from(args)?;
|
||||
let mut cli_args_config = ConfigLayer::try_from(args)?;
|
||||
if let Some(dir) = storage_dir_override {
|
||||
cli_args_config.storage_dir = Some(dir);
|
||||
}
|
||||
let cwd = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
|
||||
let settings: FabroSettings = cli_args_config
|
||||
.combine(ConfigLayer::for_workflow(workflow_path, &cwd)?)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ use fabro_workflows::records::{RunRecord, RunRecordExt};
|
|||
|
||||
use crate::cli_config;
|
||||
use crate::shared;
|
||||
use crate::store;
|
||||
|
||||
pub(crate) async fn execute(run_dir: PathBuf, launcher_path: PathBuf, resume: bool) -> Result<()> {
|
||||
let _ = fabro_proctitle::init();
|
||||
|
|
@ -31,7 +32,7 @@ pub(crate) async fn execute(run_dir: PathBuf, launcher_path: PathBuf, resume: bo
|
|||
fabro_proctitle::set(&format!("fabro: {short_id} {node_id}"));
|
||||
}) as Arc<dyn Fn(&str) + Send + Sync>
|
||||
});
|
||||
let store = crate::store::build_store(&run_record.settings.storage_dir())?;
|
||||
let store = store::build_store(&run_record.settings.storage_dir())?;
|
||||
let run_store = open_or_hydrate_run(store.as_ref(), &run_dir).await?;
|
||||
|
||||
let github_app = shared::github::build_github_app_credentials(cli_settings.app_id());
|
||||
|
|
|
|||
|
|
@ -10,16 +10,17 @@ use fabro_workflows::run_lookup::{resolve_run_combined, runs_base};
|
|||
use fabro_workflows::sandbox_git::GIT_REMOTE;
|
||||
use tracing::{debug, info};
|
||||
|
||||
use crate::args::DiffArgs;
|
||||
use crate::cli_config::load_cli_settings;
|
||||
use crate::args::{DiffArgs, GlobalArgs};
|
||||
use crate::cli_config::load_cli_settings_with_globals;
|
||||
use crate::store;
|
||||
|
||||
pub(crate) async fn run(args: DiffArgs) -> Result<()> {
|
||||
pub(crate) async fn run(args: DiffArgs, globals: &GlobalArgs) -> Result<()> {
|
||||
info!(run_id = %args.run, "Showing diff");
|
||||
let cli_settings = load_cli_settings()?;
|
||||
let cli_settings = load_cli_settings_with_globals(globals)?;
|
||||
let base = runs_base(&cli_settings.storage_dir());
|
||||
let store = crate::store::build_store(&cli_settings.storage_dir())?;
|
||||
let store = store::build_store(&cli_settings.storage_dir())?;
|
||||
let run = resolve_run_combined(store.as_ref(), &base, &args.run).await?;
|
||||
let run_store = crate::store::open_run_reader(&cli_settings.storage_dir(), &run.run_id).await?;
|
||||
let run_store = store::open_run_reader(&cli_settings.storage_dir(), &run.run_id).await?;
|
||||
|
||||
let patch = resolve_diff(&run.path, run_store.as_deref(), &args).await?;
|
||||
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ use fabro_workflows::operations::{
|
|||
};
|
||||
use git2::Repository;
|
||||
|
||||
use crate::args::ForkArgs;
|
||||
use crate::cli_config::load_cli_settings;
|
||||
use crate::args::{ForkArgs, GlobalArgs};
|
||||
use crate::cli_config::load_cli_settings_with_globals;
|
||||
use crate::store::{build_store, open_run_reader};
|
||||
|
||||
pub(crate) async fn run(args: &ForkArgs, styles: &Styles) -> Result<()> {
|
||||
pub(crate) async fn run(args: &ForkArgs, styles: &Styles, globals: &GlobalArgs) -> Result<()> {
|
||||
let repo = Repository::discover(".").context("not in a git repository")?;
|
||||
let cli_settings = load_cli_settings()?;
|
||||
let cli_settings = load_cli_settings_with_globals(globals)?;
|
||||
let durable_store = build_store(&cli_settings.storage_dir())?;
|
||||
let run_id =
|
||||
find_run_id_by_prefix_or_store(&repo, durable_store.as_ref(), &args.run_id).await?;
|
||||
|
|
|
|||
|
|
@ -9,15 +9,17 @@ use fabro_store::RunStore;
|
|||
use fabro_util::terminal::Styles;
|
||||
use fabro_workflows::run_lookup::{resolve_run_combined, runs_base};
|
||||
use futures::StreamExt;
|
||||
use tokio::time;
|
||||
use tracing::{debug, info, warn};
|
||||
|
||||
use crate::args::LogsArgs;
|
||||
use crate::cli_config::load_cli_settings;
|
||||
use crate::args::{GlobalArgs, LogsArgs};
|
||||
use crate::cli_config::load_cli_settings_with_globals;
|
||||
use crate::store;
|
||||
|
||||
pub(crate) async fn run(args: &LogsArgs, styles: &Styles) -> Result<()> {
|
||||
let cli_settings = load_cli_settings()?;
|
||||
pub(crate) async fn run(args: &LogsArgs, styles: &Styles, globals: &GlobalArgs) -> Result<()> {
|
||||
let cli_settings = load_cli_settings_with_globals(globals)?;
|
||||
let base = runs_base(&cli_settings.storage_dir());
|
||||
let store = crate::store::build_store(&cli_settings.storage_dir())?;
|
||||
let store = store::build_store(&cli_settings.storage_dir())?;
|
||||
let run = resolve_run_combined(store.as_ref(), &base, &args.run).await?;
|
||||
|
||||
info!(run_id = %run.run_id, "Showing logs");
|
||||
|
|
@ -27,12 +29,12 @@ pub(crate) async fn run(args: &LogsArgs, styles: &Styles) -> Result<()> {
|
|||
None => None,
|
||||
};
|
||||
|
||||
let run_store = crate::store::open_run_reader(&cli_settings.storage_dir(), &run.run_id).await?;
|
||||
let run_store = store::open_run_reader(&cli_settings.storage_dir(), &run.run_id).await?;
|
||||
let progress_path = run.path.join("progress.jsonl");
|
||||
let (all_lines, last_seq, use_store_follow) = match run_store.as_ref() {
|
||||
Some(run_store) => match run_store.list_events().await {
|
||||
let (all_lines, last_seq, use_store_follow) = if let Some(run_store) = run_store.as_ref() {
|
||||
match run_store.list_events().await {
|
||||
Ok(events) => {
|
||||
let last_seq = events.last().map(|event| event.seq).unwrap_or(0);
|
||||
let last_seq = events.last().map_or(0, |event| event.seq);
|
||||
let lines = events
|
||||
.iter()
|
||||
.map(event_payload_line)
|
||||
|
|
@ -50,13 +52,12 @@ pub(crate) async fn run(args: &LogsArgs, styles: &Styles) -> Result<()> {
|
|||
);
|
||||
(read_lines(&progress_path)?, 0, false)
|
||||
}
|
||||
},
|
||||
None => {
|
||||
if !progress_path.exists() {
|
||||
bail!("No progress.jsonl found for run '{}'", run.run_id);
|
||||
}
|
||||
(read_lines(&progress_path)?, 0, false)
|
||||
}
|
||||
} else {
|
||||
if !progress_path.exists() {
|
||||
bail!("No progress.jsonl found for run '{}'", run.run_id);
|
||||
}
|
||||
(read_lines(&progress_path)?, 0, false)
|
||||
};
|
||||
let filtered = apply_filters(&all_lines, since_cutoff.as_ref(), args.tail);
|
||||
|
||||
|
|
@ -252,7 +253,7 @@ async fn follow_store_logs(
|
|||
let mut out = stdout.lock();
|
||||
|
||||
loop {
|
||||
match tokio::time::timeout(Duration::from_millis(200), stream.next()).await {
|
||||
match time::timeout(Duration::from_millis(200), stream.next()).await {
|
||||
Ok(Some(Ok(event))) => {
|
||||
let line = event_payload_line(&event)?;
|
||||
if pretty {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ use fabro_util::terminal::Styles;
|
|||
use fabro_workflows::run_lookup::{resolve_run_combined, runs_base};
|
||||
|
||||
use crate::args::{GlobalArgs, RunCommands};
|
||||
use crate::cli_config::load_cli_settings;
|
||||
use crate::cli_config::load_cli_settings_with_globals;
|
||||
use crate::store;
|
||||
|
||||
pub(crate) mod attach;
|
||||
pub(crate) mod command;
|
||||
|
|
@ -36,14 +37,15 @@ pub(crate) async fn dispatch(cmd: RunCommands, globals: &GlobalArgs) -> Result<(
|
|||
RunCommands::Create(args) => {
|
||||
let styles: &'static Styles = Box::leak(Box::new(Styles::detect_stderr()));
|
||||
let cli = ConfigLayer::cli()?;
|
||||
let (run_id, _run_dir) = create::create_run(&args, cli, styles, true)?;
|
||||
let (run_id, _run_dir) =
|
||||
create::create_run(&args, cli, styles, true, globals.storage_dir.clone())?;
|
||||
println!("{run_id}");
|
||||
Ok(())
|
||||
}
|
||||
RunCommands::Start { run } => {
|
||||
let cli_settings = load_cli_settings()?;
|
||||
let cli_settings = load_cli_settings_with_globals(globals)?;
|
||||
let base = runs_base(&cli_settings.storage_dir());
|
||||
let store = crate::store::build_store(&cli_settings.storage_dir())?;
|
||||
let store = store::build_store(&cli_settings.storage_dir())?;
|
||||
let run_info = resolve_run_combined(store.as_ref(), &base, &run).await?;
|
||||
let child = start::start_run(&run_info.path, false)?;
|
||||
eprintln!("Started engine process (PID {})", child.id());
|
||||
|
|
@ -51,9 +53,9 @@ pub(crate) async fn dispatch(cmd: RunCommands, globals: &GlobalArgs) -> Result<(
|
|||
}
|
||||
RunCommands::Attach { run } => {
|
||||
let styles: &'static Styles = Box::leak(Box::new(Styles::detect_stderr()));
|
||||
let cli_settings = load_cli_settings()?;
|
||||
let cli_settings = load_cli_settings_with_globals(globals)?;
|
||||
let base = runs_base(&cli_settings.storage_dir());
|
||||
let store = crate::store::build_store(&cli_settings.storage_dir())?;
|
||||
let store = store::build_store(&cli_settings.storage_dir())?;
|
||||
let run_info = resolve_run_combined(store.as_ref(), &base, &run).await?;
|
||||
let exit_code =
|
||||
attach::attach_run(&run_info.path, Some(&run_info.run_id), false, styles, None)
|
||||
|
|
@ -68,34 +70,34 @@ pub(crate) async fn dispatch(cmd: RunCommands, globals: &GlobalArgs) -> Result<(
|
|||
launcher_path,
|
||||
resume,
|
||||
} => detached::execute(run_dir, launcher_path, resume).await,
|
||||
RunCommands::Cp(args) => cp::cp_command(args).await,
|
||||
RunCommands::Preview(args) => preview::run(args).await,
|
||||
RunCommands::Ssh(args) => ssh::run(args).await,
|
||||
RunCommands::Diff(args) => diff::run(args).await,
|
||||
RunCommands::Cp(args) => cp::cp_command(args, globals).await,
|
||||
RunCommands::Preview(args) => preview::run(args, globals).await,
|
||||
RunCommands::Ssh(args) => ssh::run(args, globals).await,
|
||||
RunCommands::Diff(args) => diff::run(args, globals).await,
|
||||
RunCommands::Logs(args) => {
|
||||
let styles = Styles::detect_stdout();
|
||||
logs::run(&args, &styles).await
|
||||
logs::run(&args, &styles, globals).await
|
||||
}
|
||||
RunCommands::Resume(args) => {
|
||||
let styles: &'static Styles = Box::leak(Box::new(Styles::detect_stderr()));
|
||||
#[cfg(feature = "sleep_inhibitor")]
|
||||
let _sleep_guard = {
|
||||
let cli_settings = load_cli_settings()?;
|
||||
let cli_settings = load_cli_settings_with_globals(globals)?;
|
||||
crate::sleep_inhibitor::guard(cli_settings.prevent_idle_sleep_enabled())
|
||||
};
|
||||
resume::resume_command(args, styles).await
|
||||
resume::resume_command(args, styles, globals).await
|
||||
}
|
||||
RunCommands::Rewind(args) => {
|
||||
let styles = Styles::detect_stderr();
|
||||
rewind::run(&args, &styles).await
|
||||
rewind::run(&args, &styles, globals).await
|
||||
}
|
||||
RunCommands::Fork(args) => {
|
||||
let styles = Styles::detect_stderr();
|
||||
fork::run(&args, &styles).await
|
||||
fork::run(&args, &styles, globals).await
|
||||
}
|
||||
RunCommands::Wait(args) => {
|
||||
let styles = Styles::detect_stderr();
|
||||
wait::run(&args, &styles).await
|
||||
wait::run(&args, &styles, globals).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,6 @@ impl TryFrom<&RunArgs> for ConfigLayer {
|
|||
dry_run: sparse_flag(args.dry_run),
|
||||
auto_approve: sparse_flag(args.auto_approve),
|
||||
no_retro: sparse_flag(args.no_retro),
|
||||
storage_dir: args.storage_dir.clone(),
|
||||
labels: parse_labels(&args.label),
|
||||
..Default::default()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -5,19 +5,18 @@ use fabro_sandbox::daytona::DaytonaSandbox;
|
|||
use fabro_workflows::run_lookup::{resolve_run_combined, runs_base};
|
||||
use tracing::info;
|
||||
|
||||
use crate::args::PreviewArgs;
|
||||
use crate::cli_config::load_cli_settings;
|
||||
use crate::args::{GlobalArgs, PreviewArgs};
|
||||
use crate::cli_config::load_cli_settings_with_globals;
|
||||
use crate::shared::validate_daytona_provider;
|
||||
use crate::store;
|
||||
|
||||
pub(crate) async fn run(args: PreviewArgs) -> Result<()> {
|
||||
let cli_settings = load_cli_settings()?;
|
||||
pub(crate) async fn run(args: PreviewArgs, globals: &GlobalArgs) -> Result<()> {
|
||||
let cli_settings = load_cli_settings_with_globals(globals)?;
|
||||
let base = runs_base(&cli_settings.storage_dir());
|
||||
let store = crate::store::build_store(&cli_settings.storage_dir())?;
|
||||
let store = store::build_store(&cli_settings.storage_dir())?;
|
||||
let run = resolve_run_combined(store.as_ref(), &base, &args.run).await?;
|
||||
let sandbox_json = run.path.join("sandbox.json");
|
||||
let record = match crate::store::open_run_reader(&cli_settings.storage_dir(), &run.run_id)
|
||||
.await?
|
||||
{
|
||||
let record = match store::open_run_reader(&cli_settings.storage_dir(), &run.run_id).await? {
|
||||
Some(run_store) => run_store
|
||||
.get_sandbox()
|
||||
.await
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@ use fabro_util::terminal::Styles;
|
|||
use fabro_workflows::records::{RunRecord, RunRecordExt};
|
||||
use fabro_workflows::run_lookup::{resolve_run_combined, runs_base};
|
||||
|
||||
use crate::args::ResumeArgs;
|
||||
use crate::cli_config::load_cli_settings;
|
||||
use crate::args::{GlobalArgs, ResumeArgs};
|
||||
use crate::cli_config::load_cli_settings_with_globals;
|
||||
use crate::store;
|
||||
|
||||
/// Resume an interrupted workflow run.
|
||||
///
|
||||
|
|
@ -15,10 +16,11 @@ use crate::cli_config::load_cli_settings;
|
|||
pub(crate) async fn resume_command(
|
||||
args: ResumeArgs,
|
||||
styles: &'static Styles,
|
||||
globals: &GlobalArgs,
|
||||
) -> anyhow::Result<()> {
|
||||
let cli_settings = load_cli_settings()?;
|
||||
let cli_settings = load_cli_settings_with_globals(globals)?;
|
||||
let base = runs_base(&cli_settings.storage_dir());
|
||||
let store = crate::store::build_store(&cli_settings.storage_dir())?;
|
||||
let store = store::build_store(&cli_settings.storage_dir())?;
|
||||
let run = resolve_run_combined(store.as_ref(), &base, &args.run).await?;
|
||||
let run_dir = run.path;
|
||||
|
||||
|
|
|
|||
|
|
@ -11,14 +11,14 @@ use fabro_workflows::operations::{
|
|||
};
|
||||
use git2::Repository;
|
||||
|
||||
use crate::args::RewindArgs;
|
||||
use crate::cli_config::load_cli_settings;
|
||||
use crate::args::{GlobalArgs, RewindArgs};
|
||||
use crate::cli_config::load_cli_settings_with_globals;
|
||||
use crate::shared::color_if;
|
||||
use crate::store::{build_store, open_run_reader};
|
||||
|
||||
pub(crate) async fn run(args: &RewindArgs, styles: &Styles) -> Result<()> {
|
||||
pub(crate) async fn run(args: &RewindArgs, styles: &Styles, globals: &GlobalArgs) -> Result<()> {
|
||||
let repo = Repository::discover(".").context("not in a git repository")?;
|
||||
let cli_settings = load_cli_settings()?;
|
||||
let cli_settings = load_cli_settings_with_globals(globals)?;
|
||||
let durable_store = build_store(&cli_settings.storage_dir())?;
|
||||
let run_id =
|
||||
find_run_id_by_prefix_or_store(&repo, durable_store.as_ref(), &args.run_id).await?;
|
||||
|
|
|
|||
|
|
@ -5,19 +5,18 @@ use fabro_sandbox::daytona::DaytonaSandbox;
|
|||
use fabro_workflows::run_lookup::{resolve_run_combined, runs_base};
|
||||
use tracing::info;
|
||||
|
||||
use crate::args::SshArgs;
|
||||
use crate::cli_config::load_cli_settings;
|
||||
use crate::args::{GlobalArgs, SshArgs};
|
||||
use crate::cli_config::load_cli_settings_with_globals;
|
||||
use crate::shared::validate_daytona_provider;
|
||||
use crate::store;
|
||||
|
||||
pub(crate) async fn run(args: SshArgs) -> Result<()> {
|
||||
let cli_settings = load_cli_settings()?;
|
||||
pub(crate) async fn run(args: SshArgs, globals: &GlobalArgs) -> Result<()> {
|
||||
let cli_settings = load_cli_settings_with_globals(globals)?;
|
||||
let base = runs_base(&cli_settings.storage_dir());
|
||||
let store = crate::store::build_store(&cli_settings.storage_dir())?;
|
||||
let store = store::build_store(&cli_settings.storage_dir())?;
|
||||
let run = resolve_run_combined(store.as_ref(), &base, &args.run).await?;
|
||||
let sandbox_json = run.path.join("sandbox.json");
|
||||
let record = match crate::store::open_run_reader(&cli_settings.storage_dir(), &run.run_id)
|
||||
.await?
|
||||
{
|
||||
let record = match store::open_run_reader(&cli_settings.storage_dir(), &run.run_id).await? {
|
||||
Some(run_store) => run_store
|
||||
.get_sandbox()
|
||||
.await
|
||||
|
|
|
|||
|
|
@ -8,20 +8,20 @@ use fabro_workflows::run_lookup::{resolve_run_combined, runs_base};
|
|||
use fabro_workflows::run_status::{RunStatus, RunStatusRecord, RunStatusRecordExt};
|
||||
use tracing::info;
|
||||
|
||||
use crate::args::WaitArgs;
|
||||
use crate::cli_config::load_cli_settings;
|
||||
use crate::args::{GlobalArgs, WaitArgs};
|
||||
use crate::cli_config::load_cli_settings_with_globals;
|
||||
use crate::shared::format_duration_ms;
|
||||
use crate::store;
|
||||
|
||||
pub(crate) async fn run(args: &WaitArgs, styles: &Styles) -> Result<()> {
|
||||
let cli_settings = load_cli_settings()?;
|
||||
pub(crate) async fn run(args: &WaitArgs, styles: &Styles, globals: &GlobalArgs) -> Result<()> {
|
||||
let cli_settings = load_cli_settings_with_globals(globals)?;
|
||||
let base = runs_base(&cli_settings.storage_dir());
|
||||
let store = crate::store::build_store(&cli_settings.storage_dir())?;
|
||||
let store = store::build_store(&cli_settings.storage_dir())?;
|
||||
let run_info = resolve_run_combined(store.as_ref(), &base, &args.run).await?;
|
||||
|
||||
info!(run_id = %run_info.run_id, "Waiting for run to complete");
|
||||
|
||||
let run_store =
|
||||
crate::store::open_run_reader(&cli_settings.storage_dir(), &run_info.run_id).await?;
|
||||
let run_store = store::open_run_reader(&cli_settings.storage_dir(), &run_info.run_id).await?;
|
||||
let status_path = run_info.path.join("status.json");
|
||||
let deadline = args
|
||||
.timeout
|
||||
|
|
|
|||
|
|
@ -10,8 +10,9 @@ use fabro_workflows::records::{Checkpoint, Conclusion, RunRecord, StartRecord};
|
|||
use fabro_workflows::run_lookup::{resolve_run_combined, runs_base};
|
||||
use fabro_workflows::run_status::RunStatus;
|
||||
|
||||
use crate::args::InspectArgs;
|
||||
use crate::cli_config::load_cli_settings;
|
||||
use crate::args::{GlobalArgs, InspectArgs};
|
||||
use crate::cli_config::load_cli_settings_with_globals;
|
||||
use crate::store;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub(crate) struct InspectOutput {
|
||||
|
|
@ -25,18 +26,17 @@ pub(crate) struct InspectOutput {
|
|||
pub sandbox: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
pub(crate) async fn run(args: &InspectArgs) -> Result<()> {
|
||||
let cli_settings = load_cli_settings()?;
|
||||
pub(crate) async fn run(args: &InspectArgs, globals: &GlobalArgs) -> Result<()> {
|
||||
let cli_settings = load_cli_settings_with_globals(globals)?;
|
||||
let base = runs_base(&cli_settings.storage_dir());
|
||||
let store = crate::store::build_store(&cli_settings.storage_dir())?;
|
||||
let store = store::build_store(&cli_settings.storage_dir())?;
|
||||
let run = resolve_run_combined(store.as_ref(), &base, &args.run).await?;
|
||||
let output =
|
||||
match crate::store::open_run_reader(&cli_settings.storage_dir(), &run.run_id).await? {
|
||||
Some(run_store) => {
|
||||
inspect_run_store(&run.run_id, &run.path, run.status, run_store.as_ref()).await
|
||||
}
|
||||
None => inspect_run_dir(&run.run_id, &run.path, run.status),
|
||||
};
|
||||
let output = match store::open_run_reader(&cli_settings.storage_dir(), &run.run_id).await? {
|
||||
Some(run_store) => {
|
||||
inspect_run_store(&run.run_id, &run.path, run.status, run_store.as_ref()).await
|
||||
}
|
||||
None => inspect_run_dir(&run.run_id, &run.path, run.status),
|
||||
};
|
||||
let json = serde_json::to_string_pretty(&[output])?;
|
||||
println!("{json}");
|
||||
Ok(())
|
||||
|
|
@ -55,8 +55,7 @@ async fn inspect_run_store(
|
|||
status: snapshot
|
||||
.status
|
||||
.as_ref()
|
||||
.map(|record| record.status)
|
||||
.unwrap_or(status),
|
||||
.map_or(status, |record| record.status),
|
||||
run_record: serde_json::to_value(snapshot.run).ok(),
|
||||
start_record: snapshot
|
||||
.start
|
||||
|
|
|
|||
|
|
@ -11,16 +11,21 @@ use fabro_util::text::strip_goal_decoration;
|
|||
use fabro_workflows::run_lookup::{StatusFilter, filter_runs, runs_base, scan_runs_combined};
|
||||
use fabro_workflows::run_status::RunStatus;
|
||||
|
||||
use crate::args::RunsListArgs;
|
||||
use crate::cli_config::load_cli_settings;
|
||||
use crate::args::{GlobalArgs, RunsListArgs};
|
||||
use crate::cli_config::load_cli_settings_with_globals;
|
||||
use crate::shared::{color_if, format_duration_ms, tilde_path};
|
||||
use crate::store;
|
||||
|
||||
use super::short_run_id;
|
||||
|
||||
pub(crate) async fn list_command(args: &RunsListArgs, styles: &Styles) -> Result<()> {
|
||||
let cli_settings = load_cli_settings()?;
|
||||
pub(crate) async fn list_command(
|
||||
args: &RunsListArgs,
|
||||
styles: &Styles,
|
||||
globals: &GlobalArgs,
|
||||
) -> Result<()> {
|
||||
let cli_settings = load_cli_settings_with_globals(globals)?;
|
||||
let base = runs_base(&cli_settings.storage_dir());
|
||||
let store = crate::store::build_store(&cli_settings.storage_dir())?;
|
||||
let store = store::build_store(&cli_settings.storage_dir())?;
|
||||
let runs = scan_runs_combined(store.as_ref(), &base).await?;
|
||||
let label_filters = parse_label_filters(&args.filter.label);
|
||||
let filtered = filter_runs(
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
use anyhow::Result;
|
||||
use fabro_util::terminal::Styles;
|
||||
|
||||
use crate::args::RunsCommands;
|
||||
use crate::args::{GlobalArgs, RunsCommands};
|
||||
|
||||
pub(crate) mod inspect;
|
||||
pub(crate) mod list;
|
||||
pub(crate) mod rm;
|
||||
|
||||
pub(crate) async fn dispatch(cmd: RunsCommands) -> Result<()> {
|
||||
pub(crate) async fn dispatch(cmd: RunsCommands, globals: &GlobalArgs) -> Result<()> {
|
||||
match cmd {
|
||||
RunsCommands::Ps(args) => {
|
||||
let styles = Styles::detect_stdout();
|
||||
list::list_command(&args, &styles).await
|
||||
list::list_command(&args, &styles, globals).await
|
||||
}
|
||||
RunsCommands::Rm(args) => rm::remove_command(&args).await,
|
||||
RunsCommands::Inspect(args) => inspect::run(&args).await,
|
||||
RunsCommands::Rm(args) => rm::remove_command(&args, globals).await,
|
||||
RunsCommands::Inspect(args) => inspect::run(&args, globals).await,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,17 +8,18 @@ use tracing::warn;
|
|||
|
||||
use fabro_sandbox::reconnect::reconnect as reconnect_sandbox;
|
||||
use fabro_workflows::run_lookup::{resolve_run_combined, runs_base};
|
||||
use fabro_workflows::run_status::{RunStatus, write_run_status};
|
||||
use fabro_workflows::run_status::{RunStatus, RunStatusRecord, write_run_status};
|
||||
|
||||
use crate::args::RunsRemoveArgs;
|
||||
use crate::cli_config::load_cli_settings;
|
||||
use crate::args::{GlobalArgs, RunsRemoveArgs};
|
||||
use crate::cli_config::load_cli_settings_with_globals;
|
||||
use crate::store;
|
||||
|
||||
use super::short_run_id;
|
||||
|
||||
pub(crate) async fn remove_command(args: &RunsRemoveArgs) -> Result<()> {
|
||||
let cli_settings = load_cli_settings()?;
|
||||
pub(crate) async fn remove_command(args: &RunsRemoveArgs, globals: &GlobalArgs) -> Result<()> {
|
||||
let cli_settings = load_cli_settings_with_globals(globals)?;
|
||||
let base = runs_base(&cli_settings.storage_dir());
|
||||
let store = crate::store::build_store(&cli_settings.storage_dir())?;
|
||||
let store = store::build_store(&cli_settings.storage_dir())?;
|
||||
remove_from(args, store.as_ref(), &base).await
|
||||
}
|
||||
|
||||
|
|
@ -48,10 +49,7 @@ async fn remove_from(args: &RunsRemoveArgs, store: &dyn Store, base: &Path) -> R
|
|||
write_run_status(&run.path, RunStatus::Removing, None);
|
||||
if let Ok(Some(run_store)) = store.open_run_reader(&run.run_id).await {
|
||||
if let Err(err) = run_store
|
||||
.put_status(&fabro_workflows::run_status::RunStatusRecord::new(
|
||||
RunStatus::Removing,
|
||||
None,
|
||||
))
|
||||
.put_status(&RunStatusRecord::new(RunStatus::Removing, None))
|
||||
.await
|
||||
{
|
||||
warn!(
|
||||
|
|
|
|||
|
|
@ -7,12 +7,12 @@ use fabro_store::{NodeVisitRef, RunSnapshot, RunStore};
|
|||
use fabro_workflows::run_lookup::{resolve_run_combined, runs_base};
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::args::StoreDumpArgs;
|
||||
use crate::cli_config::load_cli_settings;
|
||||
use crate::args::{GlobalArgs, StoreDumpArgs};
|
||||
use crate::cli_config::load_cli_settings_with_globals;
|
||||
use crate::store;
|
||||
|
||||
pub(crate) async fn dump_command(args: &StoreDumpArgs) -> Result<()> {
|
||||
let cli_settings = load_cli_settings()?;
|
||||
pub(crate) async fn dump_command(args: &StoreDumpArgs, globals: &GlobalArgs) -> Result<()> {
|
||||
let cli_settings = load_cli_settings_with_globals(globals)?;
|
||||
let base = runs_base(&cli_settings.storage_dir());
|
||||
let store = store::build_store(&cli_settings.storage_dir())?;
|
||||
let run = resolve_run_combined(store.as_ref(), &base, &args.run).await?;
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@ mod dump;
|
|||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::args::{StoreCommand, StoreNamespace};
|
||||
use crate::args::{GlobalArgs, StoreCommand, StoreNamespace};
|
||||
|
||||
pub(crate) async fn dispatch(ns: StoreNamespace) -> Result<()> {
|
||||
pub(crate) async fn dispatch(ns: StoreNamespace, globals: &GlobalArgs) -> Result<()> {
|
||||
match ns.command {
|
||||
StoreCommand::Dump(args) => dump::dump_command(&args).await,
|
||||
StoreCommand::Dump(args) => dump::dump_command(&args, globals).await,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,16 +9,17 @@ use fabro_config::FabroSettingsExt;
|
|||
use fabro_workflows::run_lookup::{logs_base, runs_base, scan_runs_combined};
|
||||
use fabro_workflows::run_status::RunStatus;
|
||||
|
||||
use crate::args::DfArgs;
|
||||
use crate::cli_config::load_cli_settings;
|
||||
use crate::args::{DfArgs, GlobalArgs};
|
||||
use crate::cli_config::load_cli_settings_with_globals;
|
||||
use crate::shared::format_size;
|
||||
use crate::store;
|
||||
|
||||
pub(super) async fn df_command(args: &DfArgs) -> Result<()> {
|
||||
let cli_settings = load_cli_settings()?;
|
||||
pub(super) async fn df_command(args: &DfArgs, globals: &GlobalArgs) -> Result<()> {
|
||||
let cli_settings = load_cli_settings_with_globals(globals)?;
|
||||
let data_dir = cli_settings.storage_dir();
|
||||
let runs_base_dir = runs_base(&data_dir);
|
||||
let logs_base_dir = logs_base(&data_dir);
|
||||
let store = crate::store::build_store(&data_dir)?;
|
||||
let store = store::build_store(&data_dir)?;
|
||||
df_from(
|
||||
args,
|
||||
store.as_ref(),
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@ mod prune;
|
|||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::args::{SystemCommand, SystemNamespace};
|
||||
use crate::args::{GlobalArgs, SystemCommand, SystemNamespace};
|
||||
|
||||
pub(crate) use prune::parse_duration;
|
||||
|
||||
pub(crate) async fn dispatch(ns: SystemNamespace) -> Result<()> {
|
||||
pub(crate) async fn dispatch(ns: SystemNamespace, globals: &GlobalArgs) -> Result<()> {
|
||||
match ns.command {
|
||||
SystemCommand::Prune(args) => prune::prune_command(&args).await,
|
||||
SystemCommand::Df(args) => df::df_command(&args).await,
|
||||
SystemCommand::Prune(args) => prune::prune_command(&args, globals).await,
|
||||
SystemCommand::Df(args) => df::df_command(&args, globals).await,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,14 +8,15 @@ use tracing::{debug, info};
|
|||
|
||||
use fabro_workflows::run_lookup::{StatusFilter, filter_runs, runs_base, scan_runs_combined};
|
||||
|
||||
use crate::args::RunsPruneArgs;
|
||||
use crate::cli_config::load_cli_settings;
|
||||
use crate::args::{GlobalArgs, RunsPruneArgs};
|
||||
use crate::cli_config::load_cli_settings_with_globals;
|
||||
use crate::shared::format_size;
|
||||
use crate::store;
|
||||
|
||||
pub(super) async fn prune_command(args: &RunsPruneArgs) -> Result<()> {
|
||||
let cli_settings = load_cli_settings()?;
|
||||
pub(super) async fn prune_command(args: &RunsPruneArgs, globals: &GlobalArgs) -> Result<()> {
|
||||
let cli_settings = load_cli_settings_with_globals(globals)?;
|
||||
let base = runs_base(&cli_settings.storage_dir());
|
||||
let store = crate::store::build_store(&cli_settings.storage_dir())?;
|
||||
let store = store::build_store(&cli_settings.storage_dir())?;
|
||||
prune_from(args, store.as_ref(), &base).await
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ async fn main_inner() -> (String, Result<()>) {
|
|||
Commands::Llm(ns) => commands::llm::dispatch(ns, &globals).await?,
|
||||
Commands::Exec(args) => commands::exec::execute(args, &globals).await?,
|
||||
Commands::RunCmd(cmd) => commands::run::dispatch(cmd, &globals).await?,
|
||||
Commands::Preflight(args) => commands::preflight::execute(args).await?,
|
||||
Commands::Preflight(args) => commands::preflight::execute(args, &globals).await?,
|
||||
Commands::Validate(args) => {
|
||||
let styles = Styles::detect_stderr();
|
||||
commands::validate::run(&args, &styles)?;
|
||||
|
|
@ -178,14 +178,15 @@ async fn main_inner() -> (String, Result<()>) {
|
|||
Commands::Parse(args) => {
|
||||
commands::parse::run(&args)?;
|
||||
}
|
||||
Commands::Asset(ns) => commands::asset::dispatch(ns)?,
|
||||
Commands::Store(ns) => commands::store::dispatch(ns).await?,
|
||||
Commands::RunsCmd(cmd) => commands::runs::dispatch(cmd).await?,
|
||||
Commands::Asset(ns) => commands::asset::dispatch(ns, &globals)?,
|
||||
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::Serve(args) => {
|
||||
let styles: &'static Styles = Box::leak(Box::new(Styles::detect_stderr()));
|
||||
fabro_server::serve::serve_command(args, styles).await?;
|
||||
fabro_server::serve::serve_command(args, styles, globals.storage_dir.clone())
|
||||
.await?;
|
||||
}
|
||||
Commands::Doctor { verbose, dry_run } => {
|
||||
let cli_settings = cli_config::load_cli_settings()?;
|
||||
|
|
@ -207,16 +208,16 @@ async fn main_inner() -> (String, Result<()>) {
|
|||
Commands::Install { web_url } => {
|
||||
commands::install::run_install(&web_url).await?;
|
||||
}
|
||||
Commands::Pr(ns) => commands::pr::dispatch(ns).await?,
|
||||
Commands::Pr(ns) => commands::pr::dispatch(ns, &globals).await?,
|
||||
Commands::Secret(ns) => commands::secret::dispatch(ns)?,
|
||||
Commands::Config(ns) => commands::config::dispatch(ns)?,
|
||||
Commands::Config(ns) => commands::config::dispatch(ns, &globals)?,
|
||||
Commands::Workflow(ns) => commands::workflow::dispatch(ns)?,
|
||||
Commands::Skill(ns) => commands::skill::dispatch(ns)?,
|
||||
Commands::Upgrade(args) => {
|
||||
commands::upgrade::run_upgrade(args).await?;
|
||||
}
|
||||
Commands::Provider(ns) => commands::provider::dispatch(ns).await?,
|
||||
Commands::System(ns) => commands::system::dispatch(ns).await?,
|
||||
Commands::System(ns) => commands::system::dispatch(ns, &globals).await?,
|
||||
Commands::SendAnalytics { path } => {
|
||||
let result = sender::upload(&path).await;
|
||||
let _ = std::fs::remove_file(&path);
|
||||
|
|
@ -306,6 +307,46 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_global_storage_dir_after_subcommand() {
|
||||
let cli = Cli::try_parse_from([
|
||||
"fabro",
|
||||
"run",
|
||||
"test/simple.fabro",
|
||||
"--storage-dir",
|
||||
"/tmp/fabro",
|
||||
])
|
||||
.expect("should parse");
|
||||
assert_eq!(
|
||||
cli.globals.storage_dir.as_deref(),
|
||||
Some(std::path::Path::new("/tmp/fabro"))
|
||||
);
|
||||
match *cli.command {
|
||||
Commands::RunCmd(RunCommands::Run(args)) => {
|
||||
assert_eq!(
|
||||
args.workflow.as_deref(),
|
||||
Some(std::path::Path::new("test/simple.fabro"))
|
||||
);
|
||||
}
|
||||
_ => panic!("unexpected command variant"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "server")]
|
||||
fn parse_server_url_conflicts_with_storage_dir() {
|
||||
let result = Cli::try_parse_from([
|
||||
"fabro",
|
||||
"--storage-dir",
|
||||
"/tmp/fabro",
|
||||
"--server-url",
|
||||
"http://localhost:3000",
|
||||
"model",
|
||||
"list",
|
||||
]);
|
||||
assert!(result.is_err(), "should fail with conflicting global flags");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_store_dump_command() {
|
||||
let cli = Cli::try_parse_from(["fabro", "store", "dump", "ABC123", "-o", "./out"])
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
source: lib/crates/fabro-cli/tests/it/cli.rs
|
||||
assertion_line: 349
|
||||
assertion_line: 745
|
||||
expression: stdout
|
||||
---
|
||||
Start the HTTP API server
|
||||
|
|
@ -16,18 +16,22 @@ Options:
|
|||
Host address to bind to [default: 127.0.0.1]
|
||||
--no-upgrade-check
|
||||
Disable automatic upgrade check
|
||||
--mode <MODE>
|
||||
Execution mode: standalone (in-process) or server (delegate to API)
|
||||
--model <MODEL>
|
||||
Override default LLM model
|
||||
--quiet
|
||||
Suppress non-essential output
|
||||
--provider <PROVIDER>
|
||||
Override default LLM provider
|
||||
--server-url <SERVER_URL>
|
||||
Server URL (overrides server.base_url from cli.toml)
|
||||
--verbose
|
||||
Enable verbose output
|
||||
--dry-run
|
||||
Execute with simulated LLM backend
|
||||
--storage-dir <STORAGE_DIR>
|
||||
Storage directory (default: ~/.fabro)
|
||||
--sandbox <SANDBOX>
|
||||
Sandbox for agent tools
|
||||
--server-url <SERVER_URL>
|
||||
Server URL (overrides server.base_url from cli.toml)
|
||||
--max-concurrent-runs <MAX_CONCURRENT_RUNS>
|
||||
Maximum number of concurrent run executions
|
||||
--config <CONFIG>
|
||||
|
|
@ -64,7 +64,11 @@ pub struct ServeArgs {
|
|||
///
|
||||
/// Returns an error if the server fails to bind or encounters a fatal error.
|
||||
#[allow(clippy::print_stderr)]
|
||||
pub async fn serve_command(args: ServeArgs, styles: &'static Styles) -> anyhow::Result<()> {
|
||||
pub async fn serve_command(
|
||||
args: ServeArgs,
|
||||
styles: &'static Styles,
|
||||
storage_dir_override: Option<PathBuf>,
|
||||
) -> anyhow::Result<()> {
|
||||
// Resolve dry-run mode (same pattern as run.rs)
|
||||
let dry_run_mode = if args.dry_run {
|
||||
true
|
||||
|
|
@ -91,7 +95,7 @@ pub async fn serve_command(args: ServeArgs, styles: &'static Styles) -> anyhow::
|
|||
// Initialize data directory and SQLite database
|
||||
let config_path = args.config;
|
||||
let server_settings = load_server_settings(config_path.as_deref())?;
|
||||
let data_dir = resolve_storage_dir(&server_settings);
|
||||
let data_dir = storage_dir_override.unwrap_or_else(|| resolve_storage_dir(&server_settings));
|
||||
|
||||
// Shared config for live reloading
|
||||
let shared_settings = Arc::new(RwLock::new(server_settings));
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ use fabro_llm::types::{
|
|||
ContentPart, FinishReason, Message as LlmMessage, Request as LlmRequest,
|
||||
Response as LlmResponse, Role, StreamEvent, ToolChoice, ToolDefinition, Usage,
|
||||
};
|
||||
use fabro_retro::retro::{Retro, derive_retro};
|
||||
use fabro_retro::retro;
|
||||
use fabro_retro::retro::Retro;
|
||||
use fabro_store::{InMemoryStore, Store};
|
||||
use fabro_util::redact::redact_jsonl_line;
|
||||
use fabro_workflows::error::FabroError;
|
||||
|
|
@ -820,10 +821,10 @@ async fn execute_run(state: Arc<AppState>, run_id: String) {
|
|||
Ok(events) => fabro_workflows::extract_stage_durations_from_events(&events),
|
||||
Err(err) => {
|
||||
tracing::warn!(run_id = %run_id, error = %err, "Failed to load run events from store");
|
||||
fabro_retro::retro::extract_stage_durations(&run_options.run_dir)
|
||||
retro::extract_stage_durations(&run_options.run_dir)
|
||||
}
|
||||
};
|
||||
let retro = derive_retro(
|
||||
let retro = retro::derive_retro(
|
||||
&run_id,
|
||||
"workflow",
|
||||
"",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue