diff --git a/apps/fabro-web/app/data/runs.test.ts b/apps/fabro-web/app/data/runs.test.ts index 16dfecbe7..0ed38a8b0 100644 --- a/apps/fabro-web/app/data/runs.test.ts +++ b/apps/fabro-web/app/data/runs.test.ts @@ -33,6 +33,7 @@ describe("mapRunListItem", () => { expect(item.title).toBe("Server supplied title"); expect(item.workflow).toBe("fix_build"); expect(item.repo).toBe("myrepo"); + expect(item.sourceDirectory).toBe("/home/user/myrepo"); expect(item.elapsed).toBeDefined(); expect(item.column).toBe("running"); expect(item.lifecycleStatus).toBe("paused"); @@ -86,6 +87,7 @@ describe("mapRunSummaryToRunItem", () => { expect(item.title).toBe("Fix the build"); expect(item.workflow).toBe("fix_build"); expect(item.repo).toBe("myrepo"); + expect(item.sourceDirectory).toBe("/home/user/myrepo"); expect(item.elapsed).toBeDefined(); expect(item.lifecycleStatus).toBe("running"); }); @@ -113,6 +115,7 @@ describe("mapRunSummaryToRunItem", () => { expect(item.title).toBe("Untitled run"); expect(item.workflow).toBe("unknown"); expect(item.repo).toBe("unknown"); + expect(item.sourceDirectory).toBeUndefined(); }); test("recognizes canonical blocked and queued run statuses", () => { diff --git a/apps/fabro-web/app/data/runs.ts b/apps/fabro-web/app/data/runs.ts index fa0041b87..3922b7cd3 100644 --- a/apps/fabro-web/app/data/runs.ts +++ b/apps/fabro-web/app/data/runs.ts @@ -33,6 +33,8 @@ export interface RunItem { comments?: number; question?: string; sandboxId?: string; + sandboxWorkingDirectory?: string; + sourceDirectory?: string; } export type ColumnStatus = "initializing" | "running" | "blocked" | "succeeded" | "failed"; @@ -80,7 +82,9 @@ export function mapRunListItem(item: RunListItem): RunItem { resources: item.sandbox?.resources ? `${item.sandbox.resources.cpu} CPU / ${item.sandbox.resources.memory} GB` : undefined, comments: item.pull_request?.comments, question: item.question?.text, - sandboxId: item.sandbox?.id, + sandboxId: item.sandbox?.id ?? undefined, + sandboxWorkingDirectory: item.sandbox?.working_directory ?? undefined, + sourceDirectory: item.source_directory ?? undefined, }; } @@ -95,6 +99,7 @@ export function mapRunSummaryToRunItem(summary: RunSummary): RunItem { workflow: summary.workflow_slug ?? summary.workflow_name ?? "unknown", lifecycleStatus, lifecycleStatusLabel: lifecycleStatusLabel(summary.status), + sourceDirectory: summary.source_directory ?? undefined, elapsed: summary.elapsed_secs != null ? formatElapsedSecs(summary.elapsed_secs) diff --git a/apps/fabro-web/app/lib/queries.ts b/apps/fabro-web/app/lib/queries.ts index e10cd2a60..70fa799d2 100644 --- a/apps/fabro-web/app/lib/queries.ts +++ b/apps/fabro-web/app/lib/queries.ts @@ -7,6 +7,7 @@ import type { PaginatedRunStageList, PaginatedStageTurnList, RunBilling, + RunProjection, ServerSettings, RunSummary, } from "@qltysh/fabro-api-client"; @@ -69,6 +70,13 @@ export function useRun(id: string | undefined) { ); } +export function useRunState(id: string | undefined) { + return useSWR( + id ? queryKeys.runs.state(id) : null, + apiNullableFetcher, + ); +} + export function useRunFiles(id: string | undefined) { return useSWR( id ? queryKeys.runs.files(id) : null, diff --git a/apps/fabro-web/app/lib/query-keys.ts b/apps/fabro-web/app/lib/query-keys.ts index 7dcbc90ee..11421bf5a 100644 --- a/apps/fabro-web/app/lib/query-keys.ts +++ b/apps/fabro-web/app/lib/query-keys.ts @@ -28,6 +28,7 @@ export const queryKeys = { }, runs: { detail: (id: string) => `/api/v1/runs/${pathSegment(id)}`, + state: (id: string) => `/api/v1/runs/${pathSegment(id)}/state`, files: (id: string) => `/api/v1/runs/${pathSegment(id)}/files`, stages: (id: string) => `/api/v1/runs/${pathSegment(id)}/stages`, graph: (id: string, direction?: "LR" | "TB") => diff --git a/apps/fabro-web/app/routes/run-detail.tsx b/apps/fabro-web/app/routes/run-detail.tsx index deeb3887f..f2dc5aa82 100644 --- a/apps/fabro-web/app/routes/run-detail.tsx +++ b/apps/fabro-web/app/routes/run-detail.tsx @@ -22,7 +22,7 @@ import { type PreviewMutationResult, } from "../lib/mutations"; import { useRunEvents } from "../lib/run-events"; -import { useRun, useRunQuestionText } from "../lib/queries"; +import { useRun, useRunQuestionText, useRunState } from "../lib/queries"; import { canArchive, canCancel, @@ -94,13 +94,22 @@ function buildRunDetailRun(summary: RunSummary): RunDetailRun { }; } +function sandboxWorkingDirectoryFromState( + state: { sandbox?: { working_directory?: unknown } | null } | null | undefined, +): string | undefined { + const value = state?.sandbox?.working_directory; + return typeof value === "string" && value.trim() ? value : undefined; +} + export function meta({ data }: any) { const run = data?.run; return [{ title: run ? `${run.title} — Fabro` : "Run — Fabro" }]; } export default function RunDetail({ params }: { params: { id: string } }) { + const demoMode = useDemoMode(); const runQuery = useRun(params.id); + const runStateQuery = useRunState(demoMode ? undefined : params.id); const run = runQuery.data ? buildRunDetailRun(runQuery.data) : null; const statusKind = runQuery.data?.status?.kind; const blockedQuestion = useRunQuestionText(params.id, statusKind === "blocked"); @@ -111,7 +120,6 @@ export default function RunDetail({ params }: { params: { id: string } }) { const archiveMutation = useArchiveRun(params.id); const unarchiveMutation = useUnarchiveRun(params.id); const { push, dismiss } = useToast(); - const demoMode = useDemoMode(); const tabs = allTabs.filter((t) => !t.demoOnly || demoMode); const lifecycleToastStateRef = useRef(INITIAL_LIFECYCLE_TOAST_STATE); @@ -170,6 +178,8 @@ export default function RunDetail({ params }: { params: { id: string } }) { const cancelPending = cancelMutation.isMutating; const archivePending = archiveMutation.isMutating; const unarchivePending = unarchiveMutation.isMutating; + const sandboxWorkingDirectory = + run.sandboxWorkingDirectory ?? sandboxWorkingDirectoryFromState(runStateQuery.data); return (
@@ -200,6 +210,22 @@ export default function RunDetail({ params }: { params: { id: string } }) { {run.elapsed} )}
+ {(run.sourceDirectory || sandboxWorkingDirectory) && ( +
+ {run.sourceDirectory && ( + + Source{" "} + {run.sourceDirectory} + + )} + {sandboxWorkingDirectory && ( + + Sandbox{" "} + {sandboxWorkingDirectory} + + )} +
+ )}
diff --git a/docs/internal/events.md b/docs/internal/events.md index b47fb3393..819ca17f6 100644 --- a/docs/internal/events.md +++ b/docs/internal/events.md @@ -34,6 +34,54 @@ Every serialized run event envelope, whether streamed over SSE, returned by `fab ## Run events +### `run.created` + +Emitted when the run record is created. + +```json +{ + "id": "...", "ts": "...", "run_id": "...", + "event": "run.created", + "properties": { + "workflow_slug": "my-workflow", + "run_dir": "/home/user/.fabro/storage/scratch/20260428-01JQXYZ", + "source_directory": "/home/user/src/my-project", + "repo_origin_url": "https://github.com/acme/my-project.git", + "base_branch": "main", + "pre_run_git": { + "display_base_sha": "abc123", + "local_dirty": "clean", + "push_outcome": { + "type": "succeeded", + "remote": "origin", + "branch": "main" + } + }, + "fork_source_ref": null, + "checkpoints_disabled": false + } +} +``` + +| Property | Type | Description | +|----------|------|-------------| +| `settings` | object | Workflow settings snapshot | +| `graph` | object | Parsed workflow graph | +| `workflow_source` | string? | Workflow source text | +| `workflow_config` | string? | Workflow config text | +| `labels` | object | Run labels | +| `run_dir` | string | Local scratch directory for the run | +| `source_directory` | string? | Submitter-side source directory | +| `repo_origin_url` | string? | Normalized repository origin URL used by clone-based sandboxes | +| `base_branch` | string? | Submitter-side base branch | +| `workflow_slug` | string? | Workflow slug | +| `db_prefix` | string? | Store prefix used for the run | +| `provenance` | object? | Actor and request provenance | +| `manifest_blob` | string? | Blob id for the submitted manifest | +| `pre_run_git` | object? | Submitter-side pre-run git context and push outcome | +| `fork_source_ref` | object? | Source run/checkpoint reference when this run was forked | +| `checkpoints_disabled` | boolean | Whether checkpointing was intentionally disabled | + ### `run.started` Emitted when the workflow run begins. @@ -1449,7 +1497,10 @@ Emitted after the engine completes sandbox initialization (distinct from `sandbo "properties": { "working_directory": "/workspace/my-project", "provider": "daytona", - "identifier": "sandbox-123" + "identifier": "sandbox-123", + "repo_cloned": true, + "clone_origin_url": "https://github.com/acme/my-project.git", + "clone_branch": "main" } } ``` @@ -1459,6 +1510,9 @@ Emitted after the engine completes sandbox initialization (distinct from `sandbo | `working_directory` | string | Working directory inside sandbox | | `provider` | string | Sandbox provider | | `identifier` | string? | Provider-specific sandbox identifier | +| `repo_cloned` | boolean? | Whether the provider cloned a repository into the sandbox | +| `clone_origin_url` | string? | Repository URL cloned into the sandbox, with credentials removed | +| `clone_branch` | string? | Branch requested for the sandbox clone | ### `sandbox.cleanup.started` diff --git a/docs/public/api-reference/fabro-api.yaml b/docs/public/api-reference/fabro-api.yaml index 825d4f1e5..99b51ca34 100644 --- a/docs/public/api-reference/fabro-api.yaml +++ b/docs/public/api-reference/fabro-api.yaml @@ -4697,9 +4697,6 @@ components: target: type: ["string", "null"] description: Optional checkpoint target such as `@2`, `build`, or `build@1`. Defaults to the latest checkpoint. - push: - type: ["boolean", "null"] - description: Whether to push the new run branches. Defaults to true. ForkResponse: description: Response returned after creating a forked run. @@ -4723,9 +4720,6 @@ components: target: type: ["string", "null"] description: Optional checkpoint target such as `@2`, `build`, or `build@1`. Defaults to the latest checkpoint. - push: - type: ["boolean", "null"] - description: Whether to push the new run branches. Defaults to true. RewindResponse: description: Response returned after rewind creates a new run. @@ -5195,13 +5189,15 @@ components: RunSandbox: description: Sandbox environment for a run. type: object - required: - - id properties: id: - type: string + type: ["string", "null"] description: Sandbox identifier. example: sb-a1b2c3d4 + working_directory: + type: ["string", "null"] + description: Path where the run executed inside the sandbox. + example: /workspace resources: $ref: "#/components/schemas/SandboxResources" diff --git a/docs/public/execution/checkpoints.mdx b/docs/public/execution/checkpoints.mdx index b8c2fe891..e64346690 100644 --- a/docs/public/execution/checkpoints.mdx +++ b/docs/public/execution/checkpoints.mdx @@ -43,7 +43,7 @@ Fabro disables Git commit and tag signing for checkpoint commits created inside ### Metadata branch -The metadata branch (`fabro/meta/{run_id}`) is an orphan branch that stores structured run data using Git's object storage directly (via `git2`). It is initialized at run start with: +The metadata branch (`fabro/meta/{run_id}`) is an orphan branch that stores structured run data using Git's object storage directly. Fabro writes it from inside the sandbox with Git plumbing commands, without checking out a metadata worktree. It is initialized at run start with: - **`run.json`** — Current projection snapshot: run spec, start/status records, current checkpoint, conclusion, sandbox, retro state, and other run-level metadata - **`graph.fabro`** — Workflow source for the run @@ -75,7 +75,7 @@ The durable run store also keeps the current checkpoint so `resume`, `inspect`, ## Worktrees -Fabro uses Git worktrees to isolate workflow runs from your working directory. When a run starts in a clean Git repository: +Fabro uses Git worktrees to isolate workflow runs from your working directory. When a local run starts in a Git repository: 1. Fabro records the current HEAD as the **base SHA** 2. Creates a new branch `fabro/run/{run_id}` at that SHA @@ -85,10 +85,10 @@ Fabro uses Git worktrees to isolate workflow runs from your working directory. W This means your original working directory stays untouched while the agent makes changes in the worktree. When the run completes, Fabro removes the worktree and restores your original directory. -If the working directory has uncommitted changes, Fabro skips worktree setup and runs in place, logging a warning. Git checkpointing is disabled in this case. +If the working directory has uncommitted changes, the worktree starts from committed `HEAD` and those uncommitted changes are not included. Fabro logs a warning so you can commit, stash, or run explicitly in place when that is what you want. -For Daytona sandboxes, the worktree is created inside the remote sandbox instead. The metadata branch is still written to the host repository so that runs can be resumed locally. Both the run branch and the metadata branch are pushed to origin after each checkpoint — the run branch is pushed from the sandbox, while the metadata branch is pushed from the host using a GitHub App installation token. +For Docker and Daytona sandboxes, the repository is cloned into the sandbox and checkpoint Git operations run there. Both the run branch and metadata branch are pushed to origin from the sandbox after each checkpoint when pushing is configured. ## Resuming a run @@ -175,7 +175,7 @@ fabro resume Use **rewind** when a terminal run should be abandoned and replaced from an earlier point. Use **fork** when you want to try a different approach while keeping the original run as a reference. -`fabro rewind --list`, `fabro fork --list`, `fabro rewind`, and `fabro fork` are server-backed. The server must be able to read the run's recorded `working_directory`; otherwise these commands fail. Timeline listing reads the metadata branch as-is and does not rebuild a missing metadata branch, so a missing branch appears as an empty timeline. +`fabro rewind --list`, `fabro fork --list`, `fabro rewind`, and `fabro fork` are server-backed. Timeline listing reads durable run-store checkpoints; it does not rebuild missing metadata branches. See [`fabro fork`](/reference/cli#fabro-fork) for the full command reference. @@ -183,11 +183,12 @@ See [`fabro fork`](/reference/cli#fabro-fork) for the full command reference. Git checkpointing activates automatically when: -- The working directory is a clean Git repository (local and Docker sandboxes) -- The sandbox is Daytona (metadata branch on the host, commits inside the sandbox) +- The run uses a Git repository and checkpointing has not been explicitly disabled +- Local runs can create a Git worktree under the run scratch directory +- Docker or Daytona can clone the configured GitHub origin into the sandbox It is skipped when: -- The working directory has uncommitted changes - The working directory is not a Git repository - The run uses `--dry-run` +- The run is explicitly started in place with checkpointing disabled diff --git a/docs/public/execution/environments.mdx b/docs/public/execution/environments.mdx index aec78773e..2cb9cabb0 100644 --- a/docs/public/execution/environments.mdx +++ b/docs/public/execution/environments.mdx @@ -53,7 +53,7 @@ The local sandbox offers no isolation. Agents can read and modify any file on th ## Docker -The Docker sandbox runs all tool operations inside a Docker container. The host working directory is bind-mounted into the container, so file changes are visible on both sides. +The Docker sandbox runs all tool operations inside a Docker container. Docker runs use a provider-owned workspace in the container; when the run has a GitHub origin, Fabro clones that repository into the workspace. ### Prerequisites @@ -76,7 +76,6 @@ The Docker sandbox is configured through the `DockerSandboxConfig`: |---|---|---| | `image` | `fabro-agent:latest` | Docker image to use | | `network_mode` | `bridge` | Docker network mode | -| `extra_mounts` | `[]` | Additional `host:container` bind mounts | | `memory_limit` | unlimited | Memory limit in bytes | | `cpu_quota` | unlimited | CPU quota (microseconds per 100ms period) | | `auto_pull` | `true` | Pull the image if not found locally | @@ -199,7 +198,7 @@ Each provider offers a different level of filesystem isolation: | Provider | Isolation | What agents can access | |---|---|---| | `local` | None | The entire host filesystem. Agents can read and modify any file. | -| `docker` | Container-level | Only the bind-mounted working directory (`/workspace` by default) and whatever is in the container image. Host files outside the mount are inaccessible. | +| `docker` | Container-level | The provider-owned workspace (`/workspace` by default) and whatever is in the container image. Host files are not bind-mounted into the container. | | `daytona` | Full machine | A cloud VM with the repository cloned into `/home/daytona/workspace`. The host filesystem is completely inaccessible. | For `local`, Fabro filters sensitive environment variables (those ending in `_API_KEY`, `_SECRET`, `_TOKEN`, `_PASSWORD`, or `_CREDENTIAL`) but does not restrict file access. Use `docker` or `daytona` when running untrusted workflows. diff --git a/docs/public/execution/run-configuration.mdx b/docs/public/execution/run-configuration.mdx index 3c5dc09e6..00b8e6c78 100644 --- a/docs/public/execution/run-configuration.mdx +++ b/docs/public/execution/run-configuration.mdx @@ -213,7 +213,7 @@ worktree_mode = "always" | Field | Description | |---|---| -| `worktree_mode` | When to create a git worktree for the run: `always`, `clean` (default — only when the working tree is clean), `dirty` (also when dirty), or `never`. | +| `worktree_mode` | Legacy compatibility setting. Local Git runs use a run-scoped worktree by default; the explicit in-place/no-checkpoints path is the supported opt-out. | #### `[run.sandbox.env]` diff --git a/docs/public/reference/cli.mdx b/docs/public/reference/cli.mdx index 92efb2c72..2ac112a14 100644 --- a/docs/public/reference/cli.mdx +++ b/docs/public/reference/cli.mdx @@ -382,7 +382,6 @@ fabro fork [OPTIONS] [TARGET] | Option | Description | | --- | --- | | `--list` | Show the checkpoint timeline instead of forking | -| `--no-push` | Skip pushing new branches to the remote | | `--server ` | Fabro server target: http(s) URL or absolute Unix socket path | ### `fabro graph` @@ -770,7 +769,6 @@ fabro rewind [OPTIONS] [TARGET] | Option | Description | | --- | --- | | `--list` | Show the checkpoint timeline instead of rewinding | -| `--no-push` | Skip force-pushing rewound refs to the remote | | `--server ` | Fabro server target: http(s) URL or absolute Unix socket path | ### `fabro rm` diff --git a/docs/public/reference/run-directory.mdx b/docs/public/reference/run-directory.mdx index f463697b3..1eafe5b55 100644 --- a/docs/public/reference/run-directory.mdx +++ b/docs/public/reference/run-directory.mdx @@ -34,7 +34,7 @@ Large durable values, event streams, checkpoints, diffs, conclusions, and retros ## Reconstructed and export-only layouts -Reconstructed metadata branches and `fabro dump` exports now use the same core layout: +Metadata branch snapshots and `fabro dump` exports now use the same core layout: - `run.json` for the current projection snapshot, including the current checkpoint - `graph.fabro` for workflow source diff --git a/lib/crates/fabro-cli/src/args.rs b/lib/crates/fabro-cli/src/args.rs index 530e4afdf..24625bf22 100644 --- a/lib/crates/fabro-cli/src/args.rs +++ b/lib/crates/fabro-cli/src/args.rs @@ -641,10 +641,6 @@ pub(crate) struct RewindArgs { /// Show the checkpoint timeline instead of rewinding #[arg(long)] pub(crate) list: bool, - - /// Skip force-pushing rewound refs to the remote - #[arg(long)] - pub(crate) no_push: bool, } #[derive(Debug, Args)] @@ -662,10 +658,6 @@ pub(crate) struct ForkArgs { /// Show the checkpoint timeline instead of forking #[arg(long)] pub(crate) list: bool, - - /// Skip pushing new branches to the remote - #[arg(long)] - pub(crate) no_push: bool, } #[derive(Args)] diff --git a/lib/crates/fabro-cli/src/commands/run/fork.rs b/lib/crates/fabro-cli/src/commands/run/fork.rs index fc22d3258..b37509711 100644 --- a/lib/crates/fabro-cli/src/commands/run/fork.rs +++ b/lib/crates/fabro-cli/src/commands/run/fork.rs @@ -27,7 +27,6 @@ pub(crate) async fn run(args: &ForkArgs, styles: &Styles, base_ctx: &CommandCont let response = client .fork_run(&run_id, ForkRequest { target: args.target.clone(), - push: Some(!args.no_push), }) .await?; diff --git a/lib/crates/fabro-cli/src/commands/run/rewind.rs b/lib/crates/fabro-cli/src/commands/run/rewind.rs index 51e2d6e6e..d00fa9738 100644 --- a/lib/crates/fabro-cli/src/commands/run/rewind.rs +++ b/lib/crates/fabro-cli/src/commands/run/rewind.rs @@ -35,7 +35,6 @@ pub(crate) async fn run( let result = client .rewind_run(&run_id, RewindRequest { target: Some(target), - push: Some(!args.no_push), }) .await?; let response = result.response; diff --git a/lib/crates/fabro-cli/tests/it/cmd/fork.rs b/lib/crates/fabro-cli/tests/it/cmd/fork.rs index bafcfd127..f0311d34c 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/fork.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/fork.rs @@ -28,7 +28,6 @@ fn help() { --server Fabro server target: http(s) URL or absolute Unix socket path [env: FABRO_SERVER=] --debug Enable DEBUG-level logging (default is INFO) [env: FABRO_DEBUG=] --list Show the checkpoint timeline instead of forking - --no-push Skip pushing new branches to the remote --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=] @@ -59,7 +58,7 @@ fn fork_latest_prints_new_run_and_resume_hint() { let mut cmd = context.command(); cmd.current_dir(&setup.repo_dir); - cmd.args(["fork", &setup.run.run_id, "--no-push"]); + cmd.args(["fork", &setup.run.run_id]); let (snapshot, output) = run_and_format(&mut cmd, &git_filters(&context)); assert_snapshot!(snapshot, @" @@ -87,7 +86,7 @@ fn fork_from_earlier_checkpoint_uses_expected_sha() { let output = context .command() .current_dir(&setup.repo_dir) - .args(["fork", &setup.run.run_id, "@2", "--json", "--no-push"]) + .args(["fork", &setup.run.run_id, "@2", "--json"]) .output() .expect("fork should execute"); assert!( diff --git a/lib/crates/fabro-cli/tests/it/cmd/resume.rs b/lib/crates/fabro-cli/tests/it/cmd/resume.rs index 82bb30882..06c10efb3 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/resume.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/resume.rs @@ -141,7 +141,7 @@ fn rewind_replacement_run_id( let rewind = context .command() .current_dir(&setup.repo_dir) - .args(["rewind", &setup.run.run_id, "@2", "--no-push", "--json"]) + .args(["rewind", &setup.run.run_id, "@2", "--json"]) .output() .expect("rewind should execute"); assert!( diff --git a/lib/crates/fabro-cli/tests/it/cmd/rewind.rs b/lib/crates/fabro-cli/tests/it/cmd/rewind.rs index 854594f7b..6de76ec2f 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/rewind.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/rewind.rs @@ -28,7 +28,6 @@ fn help() { --server Fabro server target: http(s) URL or absolute Unix socket path [env: FABRO_SERVER=] --debug Enable DEBUG-level logging (default is INFO) [env: FABRO_DEBUG=] --list Show the checkpoint timeline instead of rewinding - --no-push Skip force-pushing rewound refs to the remote --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=] @@ -84,7 +83,7 @@ fn rewind_target_updates_metadata_and_resume_hint() { let mut cmd = context.command(); cmd.current_dir(&setup.repo_dir); - cmd.args(["rewind", &setup.run.run_id, "@2", "--no-push"]); + cmd.args(["rewind", &setup.run.run_id, "@2"]); let (snapshot, output) = run_and_format(&mut cmd, &git_filters(&context)); assert_snapshot!(snapshot, @" @@ -129,7 +128,7 @@ fn rewind_archives_source_and_records_superseded_by() { let mut cmd = context.command(); cmd.current_dir(&setup.repo_dir); - cmd.args(["rewind", &setup.run.run_id, "@2", "--no-push"]); + cmd.args(["rewind", &setup.run.run_id, "@2"]); let output = cmd.output().expect("rewind should execute"); assert!( output.status.success(), diff --git a/lib/crates/fabro-cli/tests/it/scenario/recovery.rs b/lib/crates/fabro-cli/tests/it/scenario/recovery.rs index 2513ec692..9e1fcbd84 100644 --- a/lib/crates/fabro-cli/tests/it/scenario/recovery.rs +++ b/lib/crates/fabro-cli/tests/it/scenario/recovery.rs @@ -79,7 +79,7 @@ fn fork_run_json(context: &TestContext, repo_dir: &Path, source_run_id: &str) -> let output = context .command() .current_dir(repo_dir) - .args(["fork", source_run_id, "--json", "--no-push"]) + .args(["fork", source_run_id, "--json"]) .timeout(std::time::Duration::from_secs(15)) .output() .expect("fork command should execute"); diff --git a/lib/crates/fabro-sandbox/src/sandbox_spec.rs b/lib/crates/fabro-sandbox/src/sandbox_spec.rs index bc594cbf4..fdcf6629a 100644 --- a/lib/crates/fabro-sandbox/src/sandbox_spec.rs +++ b/lib/crates/fabro-sandbox/src/sandbox_spec.rs @@ -46,7 +46,7 @@ pub enum SandboxSpec { }, } -#[derive(Clone, Copy, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum WorkdirStrategy { LocalDirectory, LocalWorktree, @@ -132,40 +132,12 @@ impl SandboxSpec { pub fn workdir_strategy( &self, - worktree_mode: WorktreeMode, - git_is_clean: bool, - checkpoint_present: bool, + _worktree_mode: WorktreeMode, + _git_is_clean: bool, + _checkpoint_present: bool, ) -> WorkdirStrategy { - if checkpoint_present { - return match self { - Self::Local { .. } => WorkdirStrategy::LocalDirectory, - #[allow( - unreachable_patterns, - reason = "Feature-gated variants make this fallback arm reachable on some builds." - )] - _ => WorkdirStrategy::Cloud, - }; - } - match self { - Self::Local { .. } => match worktree_mode { - WorktreeMode::Always => WorkdirStrategy::LocalWorktree, - WorktreeMode::Clean => { - if git_is_clean { - WorkdirStrategy::LocalWorktree - } else { - WorkdirStrategy::LocalDirectory - } - } - WorktreeMode::Dirty => { - if git_is_clean { - WorkdirStrategy::LocalDirectory - } else { - WorkdirStrategy::LocalWorktree - } - } - WorktreeMode::Never => WorkdirStrategy::LocalDirectory, - }, + Self::Local { .. } => WorkdirStrategy::LocalWorktree, #[allow( unreachable_patterns, reason = "Feature-gated variants make this fallback arm reachable on some builds." @@ -238,3 +210,29 @@ impl SandboxSpec { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn local_runs_use_worktrees_for_all_worktree_modes() { + let spec = SandboxSpec::Local { + working_directory: PathBuf::from("/repo"), + }; + + for mode in [ + WorktreeMode::Always, + WorktreeMode::Clean, + WorktreeMode::Dirty, + WorktreeMode::Never, + ] { + for git_is_clean in [true, false] { + assert_eq!( + spec.workdir_strategy(mode, git_is_clean, false), + WorkdirStrategy::LocalWorktree + ); + } + } + } +} diff --git a/lib/crates/fabro-server/src/demo/mod.rs b/lib/crates/fabro-server/src/demo/mod.rs index e75fe00be..286f1a4da 100644 --- a/lib/crates/fabro-server/src/demo/mod.rs +++ b/lib/crates/fabro-server/src/demo/mod.rs @@ -97,6 +97,8 @@ pub(crate) async fn resolve_run( |run| run.workflow_slug.clone(), |run| run.workflow_name.clone(), |run| run.created_at, + |run| run.created_at.to_rfc3339(), + |run| run.repo_origin_url.clone(), ) { Ok(run) => (StatusCode::OK, Json(run.clone())).into_response(), Err(ResolveRunError::InvalidSelector | ResolveRunError::AmbiguousPrefix { .. }) => { @@ -944,8 +946,9 @@ mod runs { fn sandbox(id: &str, cpu: i64, memory: i64) -> RunSandbox { RunSandbox { - id: id.into(), - resources: Some(SandboxResources { cpu, memory }), + id: Some(id.to_string()), + working_directory: Some("/workspace".to_string()), + resources: Some(SandboxResources { cpu, memory }), } } diff --git a/lib/crates/fabro-server/src/run_selector.rs b/lib/crates/fabro-server/src/run_selector.rs index dfab61f33..779c33a59 100644 --- a/lib/crates/fabro-server/src/run_selector.rs +++ b/lib/crates/fabro-server/src/run_selector.rs @@ -30,19 +30,33 @@ impl fmt::Display for ResolveRunError { } } -pub(crate) fn resolve_run_by_selector<'a, T, FRunId, FWorkflowSlug, FWorkflowName, FCreatedAt, K>( +pub(crate) fn resolve_run_by_selector< + 'a, + T, + FRunId, + FWorkflowSlug, + FWorkflowName, + FCreatedAt, + FCreatedAtLabel, + FRepoOriginUrl, + K, +>( runs: &'a [T], selector: &str, run_id: FRunId, workflow_slug: FWorkflowSlug, workflow_name: FWorkflowName, created_at: FCreatedAt, + created_at_label: FCreatedAtLabel, + repo_origin_url: FRepoOriginUrl, ) -> Result<&'a T, ResolveRunError> where FRunId: Fn(&T) -> String, FWorkflowSlug: Fn(&T) -> Option, FWorkflowName: Fn(&T) -> Option, FCreatedAt: Fn(&T) -> K, + FCreatedAtLabel: Fn(&T) -> String, + FRepoOriginUrl: Fn(&T) -> Option, K: Ord, { let selector = selector.trim(); @@ -59,7 +73,22 @@ where count if count > 1 => { return Err(ResolveRunError::AmbiguousPrefix { selector: selector.to_string(), - matches: id_matches.iter().map(|run| run_id(run)).collect(), + matches: id_matches + .iter() + .map(|run| { + let workflow = workflow_name(run) + .or_else(|| workflow_slug(run)) + .unwrap_or_else(|| "-".to_string()); + let origin = repo_origin_url(run).unwrap_or_else(|| "-".to_string()); + format!( + "{} created_at={} workflow={} origin={}", + run_id(run), + created_at_label(run), + workflow, + origin + ) + }) + .collect(), }); } _ => {} diff --git a/lib/crates/fabro-server/src/server.rs b/lib/crates/fabro-server/src/server.rs index 524a27658..1f8818cc4 100644 --- a/lib/crates/fabro-server/src/server.rs +++ b/lib/crates/fabro-server/src/server.rs @@ -2902,14 +2902,18 @@ async fn board_run_metadata( } if let Some(sandbox) = run_state.sandbox { + let mut sandbox_metadata = serde_json::Map::new(); + sandbox_metadata.insert( + "working_directory".to_string(), + serde_json::json!(sandbox.working_directory), + ); if let Some(identifier) = sandbox.identifier { - metadata.insert( - "sandbox".to_string(), - serde_json::json!({ - "id": identifier, - }), - ); + sandbox_metadata.insert("id".to_string(), serde_json::json!(identifier)); } + metadata.insert( + "sandbox".to_string(), + serde_json::Value::Object(sandbox_metadata), + ); } if let Some((_, record)) = @@ -3060,6 +3064,8 @@ async fn resolve_run( |run| run.workflow_slug.clone(), |run| run.workflow_name.clone(), |run| run.run_id.created_at(), + |run| run.run_id.created_at().to_rfc3339(), + |run| run.repo_origin_url.clone(), ) { Ok(run) => (StatusCode::OK, Json(run.clone())).into_response(), Err(err @ (ResolveRunError::InvalidSelector | ResolveRunError::AmbiguousPrefix { .. })) => { @@ -7158,11 +7164,7 @@ async fn rewind_run( Ok(target) => target, Err(err) => return err.into_response(), }; - let input = operations::RewindInput { - run_id: id, - target, - push: request.push.unwrap_or(true), - }; + let input = operations::RewindInput { run_id: id, target }; match Box::pin(operations::rewind( &state.store, &input, @@ -7226,7 +7228,6 @@ async fn fork_run( let input = operations::ForkRunInput { source_run_id: id, target, - push: request.push.unwrap_or(true), }; match Box::pin(operations::fork_run(&state.store, &input)).await { Ok(outcome) => ( @@ -10150,6 +10151,18 @@ slug = "fabro" detail.contains(&run_id_b), "detail should mention second run: {detail}" ); + assert!( + detail.contains("created_at="), + "detail should include creation timestamps: {detail}" + ); + assert!( + detail.contains("workflow="), + "detail should include workflow names: {detail}" + ); + assert!( + detail.contains("origin="), + "detail should include origin URLs: {detail}" + ); } #[tokio::test] @@ -13959,6 +13972,10 @@ provider = "local" assert_eq!(item["pull_request"]["number"].as_u64(), Some(42)); assert_eq!(item["sandbox"]["id"].as_str(), Some("sb-test")); + assert_eq!( + item["sandbox"]["working_directory"].as_str(), + Some("/sandbox/workdir") + ); assert_eq!(item["question"]["text"].as_str(), Some("Ship it?")); } diff --git a/lib/crates/fabro-workflow/src/operations/fork.rs b/lib/crates/fabro-workflow/src/operations/fork.rs index 730a5729a..c2bc35649 100644 --- a/lib/crates/fabro-workflow/src/operations/fork.rs +++ b/lib/crates/fabro-workflow/src/operations/fork.rs @@ -11,7 +11,6 @@ use crate::records::{Checkpoint, RunSpec}; pub struct ForkRunInput { pub source_run_id: RunId, pub target: Option, - pub push: bool, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -391,7 +390,6 @@ mod tests { let outcome = fork_run(&store, &ForkRunInput { source_run_id, target: None, - push: false, }) .await .unwrap(); diff --git a/lib/crates/fabro-workflow/src/operations/rewind.rs b/lib/crates/fabro-workflow/src/operations/rewind.rs index d83b1a6ec..79abf676b 100644 --- a/lib/crates/fabro-workflow/src/operations/rewind.rs +++ b/lib/crates/fabro-workflow/src/operations/rewind.rs @@ -12,7 +12,6 @@ use crate::event::{self, Event}; pub struct RewindInput { pub run_id: RunId, pub target: Option, - pub push: bool, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -57,7 +56,6 @@ pub async fn rewind( let forked = Box::pin(fork::fork_run(store, &ForkRunInput { source_run_id: input.run_id, target: input.target.clone(), - push: input.push, })) .await?; diff --git a/lib/crates/fabro-workflow/src/pipeline/initialize.rs b/lib/crates/fabro-workflow/src/pipeline/initialize.rs index b098c4424..6bcf89564 100644 --- a/lib/crates/fabro-workflow/src/pipeline/initialize.rs +++ b/lib/crates/fabro-workflow/src/pipeline/initialize.rs @@ -31,16 +31,55 @@ use crate::git::RUN_BRANCH_PREFIX; use crate::handler::llm::{AgentApiBackend, AgentCliBackend, BackendRouter}; use crate::handler::{HandlerRegistry, default_registry, sandbox_cancel_token}; use crate::run_options::{GitCheckpointOptions, RunOptions}; +use crate::sandbox_git::GIT_REMOTE; use crate::sandbox_metadata::{SandboxGitRuntime, metadata_branch_name}; use crate::services::{EngineServices, RunServices}; struct WorktreePlan { branch_name: String, - base_sha: String, + base_sha: Option, worktree_path: PathBuf, skip_branch_creation: bool, } +async fn resolve_worktree_base_sha( + sandbox: &dyn Sandbox, + plan: &WorktreePlan, +) -> Result { + if let Some(base_sha) = plan.base_sha.as_ref() { + return Ok(base_sha.clone()); + } + + let result = sandbox + .exec_command( + &format!("{GIT_REMOTE} rev-parse HEAD"), + 10_000, + None, + None, + None, + ) + .await + .map_err(|err| Error::engine(format!("git rev-parse HEAD failed: {err}")))?; + if result.exit_code != 0 { + let output = result.stderr.trim(); + let output = if output.is_empty() { + result.stdout.trim() + } else { + output + }; + return Err(Error::engine(format!( + "git rev-parse HEAD failed (exit {}): {}", + result.exit_code, output + ))); + } + + let base_sha = result.stdout.trim(); + if base_sha.is_empty() { + return Err(Error::engine("git rev-parse HEAD returned no commit sha")); + } + Ok(base_sha.to_string()) +} + async fn run_hooks( hook_runner: Option<&HookRunner>, hook_context: &HookContext, @@ -69,9 +108,9 @@ fn resolve_worktree_plan(options: &mut InitOptions) -> Option { let base_sha = fork_source.checkpoint_sha.clone(); options.run_options.display_base_sha = Some(base_sha.clone()); return Some(WorktreePlan { - branch_name: format!("{RUN_BRANCH_PREFIX}{}", options.run_id), - base_sha, - worktree_path: RunScratch::new(&options.run_options.run_dir).worktree_dir(), + branch_name: format!("{RUN_BRANCH_PREFIX}{}", options.run_id), + base_sha: Some(base_sha), + worktree_path: RunScratch::new(&options.run_options.run_dir).worktree_dir(), skip_branch_creation: false, }); } @@ -83,7 +122,7 @@ fn resolve_worktree_plan(options: &mut InitOptions) -> Option { options.run_options.display_base_sha = Some(base_sha.clone()); return Some(WorktreePlan { branch_name: run_branch.clone(), - base_sha: base_sha.clone(), + base_sha: Some(base_sha.clone()), worktree_path: RunScratch::new(&options.run_options.run_dir) .worktree_dir(), skip_branch_creation: true, @@ -123,21 +162,19 @@ fn resolve_worktree_plan(options: &mut InitOptions) -> Option { if let Some(fork_source) = options.run_options.fork_source_ref.as_ref() { ( format!("{RUN_BRANCH_PREFIX}{}", options.run_id), - fork_source.checkpoint_sha.clone(), + Some(fork_source.checkpoint_sha.clone()), ) } else { - let Some(base_sha) = options - .run_options - .pre_run_git - .as_ref() - .and_then(|git| git.display_base_sha.clone()) - else { - options.run_options.display_base_sha = None; - return None; - }; - (format!("{RUN_BRANCH_PREFIX}{}", options.run_id), base_sha) + ( + format!("{RUN_BRANCH_PREFIX}{}", options.run_id), + options + .run_options + .pre_run_git + .as_ref() + .and_then(|git| git.display_base_sha.clone()), + ) }; - options.run_options.display_base_sha = Some(base_sha.clone()); + options.run_options.display_base_sha = base_sha.clone(); Some(WorktreePlan { branch_name, base_sha, @@ -440,13 +477,6 @@ pub async fn initialize( resolve_devcontainer(&mut options).await?; let worktree_plan = resolve_worktree_plan(&mut options); - if let Some(plan) = worktree_plan.as_ref() { - options.run_options.git = Some(GitCheckpointOptions { - base_sha: Some(plan.base_sha.clone()), - run_branch: Some(plan.branch_name.clone()), - meta_branch: Some(metadata_branch_name(&options.run_id.to_string())), - }); - } let sandbox_event_callback: SandboxEventCallback = { let emitter = Arc::clone(&options.emitter); @@ -465,12 +495,19 @@ pub async fn initialize( .ensure_git_available(&*inner) .await .map_err(|err| Error::engine(format!("sandbox git unavailable: {err}")))?; + let base_sha = resolve_worktree_base_sha(&*inner, plan).await?; + options.run_options.display_base_sha = Some(base_sha.clone()); + options.run_options.git = Some(GitCheckpointOptions { + base_sha: Some(base_sha.clone()), + run_branch: Some(plan.branch_name.clone()), + meta_branch: Some(metadata_branch_name(&options.run_id.to_string())), + }); let mut worktree = WorktreeSandbox::new(inner, WorktreeOptions { - branch_name: plan.branch_name.clone(), - base_sha: plan.base_sha.clone(), - worktree_path: plan.worktree_path.to_string_lossy().into_owned(), + branch_name: plan.branch_name.clone(), + base_sha, + worktree_path: plan.worktree_path.to_string_lossy().into_owned(), skip_branch_creation: plan.skip_branch_creation, - setup_intent: Some(git_setup_intent(&options.run_options)), + setup_intent: Some(git_setup_intent(&options.run_options)), }); worktree.set_event_callback(Arc::clone(&options.emitter).worktree_callback()); match worktree.initialize().await { @@ -715,6 +752,7 @@ mod tests { use fabro_graphviz::graph::{AttrValue, Edge, Graph, Node}; use fabro_interview::AutoApproveInterviewer; use fabro_sandbox::SandboxSpec; + use fabro_sandbox::config::WorktreeMode; use fabro_store::Database; use fabro_types::{RunId, WorkflowSettings, fixtures}; use fabro_vault::{SecretType, Vault}; @@ -839,6 +877,64 @@ mod tests { ) } + #[tokio::test] + async fn resolve_worktree_plan_uses_local_worktree_without_pre_run_git_context() { + let temp = tempfile::tempdir().unwrap(); + let run_dir = temp.path().join("run"); + std::fs::create_dir_all(&run_dir).unwrap(); + let store = memory_store(); + let mut options = InitOptions { + run_id: test_run_id(), + run_store: { + let inner = store.create_run(&test_run_id()).await.unwrap(); + inner.into() + }, + dry_run: false, + emitter: Arc::new(crate::event::Emitter::new(test_run_id())), + sandbox: SandboxSpec::Local { + working_directory: std::env::current_dir().unwrap(), + }, + llm: LlmSpec { + model: "test-model".to_string(), + provider: fabro_llm::Provider::Anthropic, + fallback_chain: Vec::new(), + mcp_servers: Vec::new(), + dry_run: true, + }, + interviewer: Arc::new(AutoApproveInterviewer), + lifecycle: crate::run_options::LifecycleOptions { + setup_commands: vec![], + setup_command_timeout_ms: 1_000, + devcontainer_phases: vec![], + }, + run_options: test_settings(&run_dir), + workflow_path: None, + workflow_bundle: None, + hooks: fabro_hooks::HookSettings { hooks: vec![] }, + sandbox_env: SandboxEnvSpec { + devcontainer_env: HashMap::new(), + toml_env: HashMap::new(), + github_permissions: None, + origin_url: None, + }, + vault: None, + devcontainer: None, + git: None, + worktree_mode: Some(WorktreeMode::Always), + run_control: None, + registry_override: None, + artifact_sink: None, + checkpoint: None, + seed_context: None, + }; + + let plan = resolve_worktree_plan(&mut options); + + assert!(plan.is_some()); + assert!(options.run_options.display_base_sha.is_none()); + assert!(options.run_options.git.is_none()); + } + #[tokio::test] async fn initialize_prepares_sandbox_and_uses_persisted_run_dir() { let temp = tempfile::tempdir().unwrap(); diff --git a/lib/packages/fabro-api-client/src/api/runs-api.ts b/lib/packages/fabro-api-client/src/api/runs-api.ts index 7fa65faff..c403589ae 100644 --- a/lib/packages/fabro-api-client/src/api/runs-api.ts +++ b/lib/packages/fabro-api-client/src/api/runs-api.ts @@ -5,7 +5,7 @@ * HTTP API for managing Fabro workflow run executions. * * The version of the OpenAPI document: 0.1.0 - * + * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * https://openapi-generator.tech @@ -67,7 +67,7 @@ import type { TimelineEntryResponse } from '../models'; export const RunsApiAxiosParamCreator = function (configuration?: Configuration) { return { /** - * Marks a terminal run (`succeeded`, `failed`, or `dead`) as `archived`. Archived runs are hidden from default listings and are read-only until unarchived. Idempotent on already-archived runs. Returns 409 if the run is not terminal. + * Marks a terminal run (`succeeded`, `failed`, or `dead`) as `archived`. Archived runs are hidden from default listings and are read-only until unarchived. Idempotent on already-archived runs. Returns 409 if the run is not terminal. * @summary Archive Run * @param {string} id Unique run identifier (ULID). * @param {*} [options] Override http request option. @@ -189,7 +189,7 @@ export const RunsApiAxiosParamCreator = function (configuration?: Configuration) /** * Creates a new workflow run in `submitted` status from a self-contained manifest. * @summary Create Run - * @param {RunManifest} runManifest + * @param {RunManifest} runManifest * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -231,7 +231,7 @@ export const RunsApiAxiosParamCreator = function (configuration?: Configuration) * Creates a pull request for a completed run on GitHub and persists the record on the server. * @summary Create Run Pull Request * @param {string} id Unique run identifier (ULID). - * @param {CreateRunPullRequestRequest} createRunPullRequestRequest + * @param {CreateRunPullRequestRequest} createRunPullRequestRequest * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -318,10 +318,10 @@ export const RunsApiAxiosParamCreator = function (configuration?: Configuration) }; }, /** - * Creates a new run from a checkpoint of the source run. The source run is left untouched. + * Creates a new run from a checkpoint of the source run. The source run is left untouched. * @summary Fork Run * @param {string} id Unique run identifier (ULID). - * @param {ForkRequest} [forkRequest] + * @param {ForkRequest} [forkRequest] * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -541,7 +541,7 @@ export const RunsApiAxiosParamCreator = function (configuration?: Configuration) * Merges the stored pull request for a run on GitHub. * @summary Merge Run Pull Request * @param {string} id Unique run identifier (ULID). - * @param {MergeRunPullRequestRequest} mergeRunPullRequestRequest + * @param {MergeRunPullRequestRequest} mergeRunPullRequestRequest * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -625,7 +625,7 @@ export const RunsApiAxiosParamCreator = function (configuration?: Configuration) /** * Validates and renders a workflow manifest as SVG without creating a run. * @summary Render Workflow Graph - * @param {RenderWorkflowGraphRequest} renderWorkflowGraphRequest + * @param {RenderWorkflowGraphRequest} renderWorkflowGraphRequest * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -827,10 +827,10 @@ export const RunsApiAxiosParamCreator = function (configuration?: Configuration) }; }, /** - * Creates a new run from an earlier checkpoint of a terminal source run, archives the source run, and records `run.superseded_by` on the source after archive succeeds. Returns 207 when the new run was created but the source archive step failed. + * Creates a new run from an earlier checkpoint of a terminal source run, archives the source run, and records `run.superseded_by` on the source after archive succeeds. Returns 207 when the new run was created but the source archive step failed. * @summary Rewind Run * @param {string} id Unique run identifier (ULID). - * @param {RewindRequest} [rewindRequest] + * @param {RewindRequest} [rewindRequest] * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -872,7 +872,7 @@ export const RunsApiAxiosParamCreator = function (configuration?: Configuration) /** * Validates a workflow manifest without creating a run. * @summary Validate Workflow Manifest - * @param {RunManifest} runManifest + * @param {RunManifest} runManifest * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -914,7 +914,7 @@ export const RunsApiAxiosParamCreator = function (configuration?: Configuration) * Starts a submitted run, queuing it for execution. Provide `resume=true` to resume an interrupted run from checkpoint. Returns 409 if the run is not startable. * @summary Start Run * @param {string} id Unique run identifier (ULID). - * @param {StartRunRequest} [startRunRequest] + * @param {StartRunRequest} [startRunRequest] * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -954,7 +954,7 @@ export const RunsApiAxiosParamCreator = function (configuration?: Configuration) }; }, /** - * Restores an archived run to its prior terminal status. Idempotent on runs that are terminal but not archived (returns the current status without emitting an event). Returns 409 if the run is active. + * Restores an archived run to its prior terminal status. Idempotent on runs that are terminal but not archived (returns the current status without emitting an event). Returns 409 if the run is active. * @summary Unarchive Run * @param {string} id Unique run identifier (ULID). * @param {*} [options] Override http request option. @@ -1043,7 +1043,7 @@ export const RunsApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = RunsApiAxiosParamCreator(configuration) return { /** - * Marks a terminal run (`succeeded`, `failed`, or `dead`) as `archived`. Archived runs are hidden from default listings and are read-only until unarchived. Idempotent on already-archived runs. Returns 409 if the run is not terminal. + * Marks a terminal run (`succeeded`, `failed`, or `dead`) as `archived`. Archived runs are hidden from default listings and are read-only until unarchived. Idempotent on already-archived runs. Returns 409 if the run is not terminal. * @summary Archive Run * @param {string} id Unique run identifier (ULID). * @param {*} [options] Override http request option. @@ -1084,7 +1084,7 @@ export const RunsApiFp = function(configuration?: Configuration) { /** * Creates a new workflow run in `submitted` status from a self-contained manifest. * @summary Create Run - * @param {RunManifest} runManifest + * @param {RunManifest} runManifest * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -1098,7 +1098,7 @@ export const RunsApiFp = function(configuration?: Configuration) { * Creates a pull request for a completed run on GitHub and persists the record on the server. * @summary Create Run Pull Request * @param {string} id Unique run identifier (ULID). - * @param {CreateRunPullRequestRequest} createRunPullRequestRequest + * @param {CreateRunPullRequestRequest} createRunPullRequestRequest * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -1123,10 +1123,10 @@ export const RunsApiFp = function(configuration?: Configuration) { return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); }, /** - * Creates a new run from a checkpoint of the source run. The source run is left untouched. + * Creates a new run from a checkpoint of the source run. The source run is left untouched. * @summary Fork Run * @param {string} id Unique run identifier (ULID). - * @param {ForkRequest} [forkRequest] + * @param {ForkRequest} [forkRequest] * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -1195,7 +1195,7 @@ export const RunsApiFp = function(configuration?: Configuration) { * Merges the stored pull request for a run on GitHub. * @summary Merge Run Pull Request * @param {string} id Unique run identifier (ULID). - * @param {MergeRunPullRequestRequest} mergeRunPullRequestRequest + * @param {MergeRunPullRequestRequest} mergeRunPullRequestRequest * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -1221,7 +1221,7 @@ export const RunsApiFp = function(configuration?: Configuration) { /** * Validates and renders a workflow manifest as SVG without creating a run. * @summary Render Workflow Graph - * @param {RenderWorkflowGraphRequest} renderWorkflowGraphRequest + * @param {RenderWorkflowGraphRequest} renderWorkflowGraphRequest * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -1284,10 +1284,10 @@ export const RunsApiFp = function(configuration?: Configuration) { return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); }, /** - * Creates a new run from an earlier checkpoint of a terminal source run, archives the source run, and records `run.superseded_by` on the source after archive succeeds. Returns 207 when the new run was created but the source archive step failed. + * Creates a new run from an earlier checkpoint of a terminal source run, archives the source run, and records `run.superseded_by` on the source after archive succeeds. Returns 207 when the new run was created but the source archive step failed. * @summary Rewind Run * @param {string} id Unique run identifier (ULID). - * @param {RewindRequest} [rewindRequest] + * @param {RewindRequest} [rewindRequest] * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -1300,7 +1300,7 @@ export const RunsApiFp = function(configuration?: Configuration) { /** * Validates a workflow manifest without creating a run. * @summary Validate Workflow Manifest - * @param {RunManifest} runManifest + * @param {RunManifest} runManifest * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -1314,7 +1314,7 @@ export const RunsApiFp = function(configuration?: Configuration) { * Starts a submitted run, queuing it for execution. Provide `resume=true` to resume an interrupted run from checkpoint. Returns 409 if the run is not startable. * @summary Start Run * @param {string} id Unique run identifier (ULID). - * @param {StartRunRequest} [startRunRequest] + * @param {StartRunRequest} [startRunRequest] * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -1325,7 +1325,7 @@ export const RunsApiFp = function(configuration?: Configuration) { return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); }, /** - * Restores an archived run to its prior terminal status. Idempotent on runs that are terminal but not archived (returns the current status without emitting an event). Returns 409 if the run is active. + * Restores an archived run to its prior terminal status. Idempotent on runs that are terminal but not archived (returns the current status without emitting an event). Returns 409 if the run is active. * @summary Unarchive Run * @param {string} id Unique run identifier (ULID). * @param {*} [options] Override http request option. @@ -1360,7 +1360,7 @@ export const RunsApiFactory = function (configuration?: Configuration, basePath? const localVarFp = RunsApiFp(configuration) return { /** - * Marks a terminal run (`succeeded`, `failed`, or `dead`) as `archived`. Archived runs are hidden from default listings and are read-only until unarchived. Idempotent on already-archived runs. Returns 409 if the run is not terminal. + * Marks a terminal run (`succeeded`, `failed`, or `dead`) as `archived`. Archived runs are hidden from default listings and are read-only until unarchived. Idempotent on already-archived runs. Returns 409 if the run is not terminal. * @summary Archive Run * @param {string} id Unique run identifier (ULID). * @param {*} [options] Override http request option. @@ -1392,7 +1392,7 @@ export const RunsApiFactory = function (configuration?: Configuration, basePath? /** * Creates a new workflow run in `submitted` status from a self-contained manifest. * @summary Create Run - * @param {RunManifest} runManifest + * @param {RunManifest} runManifest * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -1403,7 +1403,7 @@ export const RunsApiFactory = function (configuration?: Configuration, basePath? * Creates a pull request for a completed run on GitHub and persists the record on the server. * @summary Create Run Pull Request * @param {string} id Unique run identifier (ULID). - * @param {CreateRunPullRequestRequest} createRunPullRequestRequest + * @param {CreateRunPullRequestRequest} createRunPullRequestRequest * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -1422,10 +1422,10 @@ export const RunsApiFactory = function (configuration?: Configuration, basePath? return localVarFp.deleteRun(id, force, options).then((request) => request(axios, basePath)); }, /** - * Creates a new run from a checkpoint of the source run. The source run is left untouched. + * Creates a new run from a checkpoint of the source run. The source run is left untouched. * @summary Fork Run * @param {string} id Unique run identifier (ULID). - * @param {ForkRequest} [forkRequest] + * @param {ForkRequest} [forkRequest] * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -1479,7 +1479,7 @@ export const RunsApiFactory = function (configuration?: Configuration, basePath? * Merges the stored pull request for a run on GitHub. * @summary Merge Run Pull Request * @param {string} id Unique run identifier (ULID). - * @param {MergeRunPullRequestRequest} mergeRunPullRequestRequest + * @param {MergeRunPullRequestRequest} mergeRunPullRequestRequest * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -1499,7 +1499,7 @@ export const RunsApiFactory = function (configuration?: Configuration, basePath? /** * Validates and renders a workflow manifest as SVG without creating a run. * @summary Render Workflow Graph - * @param {RenderWorkflowGraphRequest} renderWorkflowGraphRequest + * @param {RenderWorkflowGraphRequest} renderWorkflowGraphRequest * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -1547,10 +1547,10 @@ export const RunsApiFactory = function (configuration?: Configuration, basePath? return localVarFp.retrieveRunGraphSource(id, options).then((request) => request(axios, basePath)); }, /** - * Creates a new run from an earlier checkpoint of a terminal source run, archives the source run, and records `run.superseded_by` on the source after archive succeeds. Returns 207 when the new run was created but the source archive step failed. + * Creates a new run from an earlier checkpoint of a terminal source run, archives the source run, and records `run.superseded_by` on the source after archive succeeds. Returns 207 when the new run was created but the source archive step failed. * @summary Rewind Run * @param {string} id Unique run identifier (ULID). - * @param {RewindRequest} [rewindRequest] + * @param {RewindRequest} [rewindRequest] * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -1560,7 +1560,7 @@ export const RunsApiFactory = function (configuration?: Configuration, basePath? /** * Validates a workflow manifest without creating a run. * @summary Validate Workflow Manifest - * @param {RunManifest} runManifest + * @param {RunManifest} runManifest * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -1571,7 +1571,7 @@ export const RunsApiFactory = function (configuration?: Configuration, basePath? * Starts a submitted run, queuing it for execution. Provide `resume=true` to resume an interrupted run from checkpoint. Returns 409 if the run is not startable. * @summary Start Run * @param {string} id Unique run identifier (ULID). - * @param {StartRunRequest} [startRunRequest] + * @param {StartRunRequest} [startRunRequest] * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -1579,7 +1579,7 @@ export const RunsApiFactory = function (configuration?: Configuration, basePath? return localVarFp.startRun(id, startRunRequest, options).then((request) => request(axios, basePath)); }, /** - * Restores an archived run to its prior terminal status. Idempotent on runs that are terminal but not archived (returns the current status without emitting an event). Returns 409 if the run is active. + * Restores an archived run to its prior terminal status. Idempotent on runs that are terminal but not archived (returns the current status without emitting an event). Returns 409 if the run is active. * @summary Unarchive Run * @param {string} id Unique run identifier (ULID). * @param {*} [options] Override http request option. @@ -1606,7 +1606,7 @@ export const RunsApiFactory = function (configuration?: Configuration, basePath? */ export class RunsApi extends BaseAPI { /** - * Marks a terminal run (`succeeded`, `failed`, or `dead`) as `archived`. Archived runs are hidden from default listings and are read-only until unarchived. Idempotent on already-archived runs. Returns 409 if the run is not terminal. + * Marks a terminal run (`succeeded`, `failed`, or `dead`) as `archived`. Archived runs are hidden from default listings and are read-only until unarchived. Idempotent on already-archived runs. Returns 409 if the run is not terminal. * @summary Archive Run * @param {string} id Unique run identifier (ULID). * @param {*} [options] Override http request option. @@ -1641,7 +1641,7 @@ export class RunsApi extends BaseAPI { /** * Creates a new workflow run in `submitted` status from a self-contained manifest. * @summary Create Run - * @param {RunManifest} runManifest + * @param {RunManifest} runManifest * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -1653,7 +1653,7 @@ export class RunsApi extends BaseAPI { * Creates a pull request for a completed run on GitHub and persists the record on the server. * @summary Create Run Pull Request * @param {string} id Unique run identifier (ULID). - * @param {CreateRunPullRequestRequest} createRunPullRequestRequest + * @param {CreateRunPullRequestRequest} createRunPullRequestRequest * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -1674,10 +1674,10 @@ export class RunsApi extends BaseAPI { } /** - * Creates a new run from a checkpoint of the source run. The source run is left untouched. + * Creates a new run from a checkpoint of the source run. The source run is left untouched. * @summary Fork Run * @param {string} id Unique run identifier (ULID). - * @param {ForkRequest} [forkRequest] + * @param {ForkRequest} [forkRequest] * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -1736,7 +1736,7 @@ export class RunsApi extends BaseAPI { * Merges the stored pull request for a run on GitHub. * @summary Merge Run Pull Request * @param {string} id Unique run identifier (ULID). - * @param {MergeRunPullRequestRequest} mergeRunPullRequestRequest + * @param {MergeRunPullRequestRequest} mergeRunPullRequestRequest * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -1758,7 +1758,7 @@ export class RunsApi extends BaseAPI { /** * Validates and renders a workflow manifest as SVG without creating a run. * @summary Render Workflow Graph - * @param {RenderWorkflowGraphRequest} renderWorkflowGraphRequest + * @param {RenderWorkflowGraphRequest} renderWorkflowGraphRequest * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -1811,10 +1811,10 @@ export class RunsApi extends BaseAPI { } /** - * Creates a new run from an earlier checkpoint of a terminal source run, archives the source run, and records `run.superseded_by` on the source after archive succeeds. Returns 207 when the new run was created but the source archive step failed. + * Creates a new run from an earlier checkpoint of a terminal source run, archives the source run, and records `run.superseded_by` on the source after archive succeeds. Returns 207 when the new run was created but the source archive step failed. * @summary Rewind Run * @param {string} id Unique run identifier (ULID). - * @param {RewindRequest} [rewindRequest] + * @param {RewindRequest} [rewindRequest] * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -1825,7 +1825,7 @@ export class RunsApi extends BaseAPI { /** * Validates a workflow manifest without creating a run. * @summary Validate Workflow Manifest - * @param {RunManifest} runManifest + * @param {RunManifest} runManifest * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -1837,7 +1837,7 @@ export class RunsApi extends BaseAPI { * Starts a submitted run, queuing it for execution. Provide `resume=true` to resume an interrupted run from checkpoint. Returns 409 if the run is not startable. * @summary Start Run * @param {string} id Unique run identifier (ULID). - * @param {StartRunRequest} [startRunRequest] + * @param {StartRunRequest} [startRunRequest] * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -1846,7 +1846,7 @@ export class RunsApi extends BaseAPI { } /** - * Restores an archived run to its prior terminal status. Idempotent on runs that are terminal but not archived (returns the current status without emitting an event). Returns 409 if the run is active. + * Restores an archived run to its prior terminal status. Idempotent on runs that are terminal but not archived (returns the current status without emitting an event). Returns 409 if the run is active. * @summary Unarchive Run * @param {string} id Unique run identifier (ULID). * @param {*} [options] Override http request option. @@ -1867,4 +1867,3 @@ export class RunsApi extends BaseAPI { return RunsApiFp(this.configuration).unpauseRun(id, options).then((request) => request(this.axios, this.basePath)); } } - diff --git a/lib/packages/fabro-api-client/src/models/fork-request.ts b/lib/packages/fabro-api-client/src/models/fork-request.ts index 5da18402a..bc97d216c 100644 --- a/lib/packages/fabro-api-client/src/models/fork-request.ts +++ b/lib/packages/fabro-api-client/src/models/fork-request.ts @@ -22,9 +22,5 @@ export interface ForkRequest { * Optional checkpoint target such as `@2`, `build`, or `build@1`. Defaults to the latest checkpoint. */ 'target'?: string | null; - /** - * Whether to push the new run branches. Defaults to true. - */ - 'push'?: boolean | null; } diff --git a/lib/packages/fabro-api-client/src/models/rewind-request.ts b/lib/packages/fabro-api-client/src/models/rewind-request.ts index 438fd8b84..a0452cfb9 100644 --- a/lib/packages/fabro-api-client/src/models/rewind-request.ts +++ b/lib/packages/fabro-api-client/src/models/rewind-request.ts @@ -22,9 +22,5 @@ export interface RewindRequest { * Optional checkpoint target such as `@2`, `build`, or `build@1`. Defaults to the latest checkpoint. */ 'target'?: string | null; - /** - * Whether to push the new run branches. Defaults to true. - */ - 'push'?: boolean | null; } diff --git a/lib/packages/fabro-api-client/src/models/run-sandbox.ts b/lib/packages/fabro-api-client/src/models/run-sandbox.ts index 832aa4b65..3e2f03482 100644 --- a/lib/packages/fabro-api-client/src/models/run-sandbox.ts +++ b/lib/packages/fabro-api-client/src/models/run-sandbox.ts @@ -24,7 +24,11 @@ export interface RunSandbox { /** * Sandbox identifier. */ - 'id': string; + 'id'?: string | null; + /** + * Path where the run executed inside the sandbox. + */ + 'working_directory'?: string | null; 'resources'?: SandboxResources; }