Serve fork, rewind, retry and the timeline on the runs API

`GET /runs/{id}/timeline` lists the run's checkpoints with their Petri
positions, stages, commits and diff summaries, and its fork origin.
`POST /runs/{id}/fork` resolves a target on that timeline, creates the
new run, seeds it through `fabro_petri::fork` and queues it in resume
mode, so its worker restores the checkpoint into a fresh workspace and
continues from the position; a position inside a parallel branch is
refused with 400 before the run exists. `POST /runs/{id}/rewind` is that
fork of a terminal run followed by the source's archive and its
`run.superseded` record (207 when the archive fails); `POST
/runs/{id}/retry` forks a terminal run at its last checkpoint, rerunning
the stage that failed. The projection carries `forked_from`. The Rust
and TypeScript clients gain the four calls.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Bryan Helmkamp 2026-09-18 23:17:25 -04:00
parent f18f02206b
commit e7d55d6ec7
No known key found for this signature in database
17 changed files with 1397 additions and 6 deletions

View file

@ -2297,6 +2297,197 @@ paths:
schema:
$ref: "#/components/schemas/ErrorResponse"
/api/v1/runs/{id}/retry:
post:
operationId: retryRun
tags: [Runs]
summary: Retry Run
description: >
Creates a new run from the terminal source run's last checkpoint and
starts it. When the source failed on a stage, that stage runs again on
the files of the stage before it; otherwise the new run continues from
the last checkpoint as it stands. The new run records `retried_from`
and `fork_source_ref`; the source run is left unchanged. Active and
archived runs are not retryable.
parameters:
- $ref: "#/components/parameters/RunId"
responses:
"201":
description: New retry run created and started
content:
application/json:
schema:
$ref: "#/components/schemas/Run"
"400":
description: The source has no checkpoint to retry from
headers:
x-request-id:
$ref: "#/components/headers/XRequestId"
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"404":
description: Run not found
headers:
x-request-id:
$ref: "#/components/headers/XRequestId"
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"409":
description: Source run is not retryable
headers:
x-request-id:
$ref: "#/components/headers/XRequestId"
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/api/v1/runs/{id}/rewind:
post:
operationId: rewindRun
tags: [Runs]
summary: Rewind Run
description: >
Creates a new run from a checkpoint of a terminal source run and
starts it, then archives the source run and records
`run.superseded_by` on it. Returns 207 when the new run was created
but the source archive step failed.
parameters:
- $ref: "#/components/parameters/RunId"
requestBody:
required: false
content:
application/json:
schema:
$ref: "#/components/schemas/RewindRequest"
responses:
"200":
description: Source archived and new run created
content:
application/json:
schema:
$ref: "#/components/schemas/RewindResponse"
"207":
description: New run created but source archive failed
content:
application/json:
schema:
$ref: "#/components/schemas/RewindResponse"
"400":
description: Invalid rewind target
headers:
x-request-id:
$ref: "#/components/headers/XRequestId"
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"404":
description: Run not found
headers:
x-request-id:
$ref: "#/components/headers/XRequestId"
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"409":
description: Source run is archived or is not terminal
headers:
x-request-id:
$ref: "#/components/headers/XRequestId"
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/api/v1/runs/{id}/fork:
post:
operationId: forkRun
tags: [Runs]
summary: Fork Run
description: >
Creates a new run from a checkpoint of the source run and starts it.
The new run holds the source's records up to the checkpoint's
position and continues from there in a fresh workspace restored to
the checkpoint's commit. The source run is left untouched. A
checkpoint inside a parallel branch cannot be forked at; fork at the
parallel stage instead.
parameters:
- $ref: "#/components/parameters/RunId"
requestBody:
required: false
content:
application/json:
schema:
$ref: "#/components/schemas/ForkRequest"
responses:
"200":
description: New run created and started
content:
application/json:
schema:
$ref: "#/components/schemas/ForkResponse"
"400":
description: Invalid fork target
headers:
x-request-id:
$ref: "#/components/headers/XRequestId"
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"404":
description: Run not found
headers:
x-request-id:
$ref: "#/components/headers/XRequestId"
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"409":
description: Source run is archived
headers:
x-request-id:
$ref: "#/components/headers/XRequestId"
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/api/v1/runs/{id}/timeline:
get:
operationId: getRunTimeline
tags: [Runs]
summary: Get Run Timeline
description: >
Returns the run's checkpoints in the order they were recorded, each
at its Petri position with the commit it made and the stage it
belongs to, and where the run was forked from when it is a fork.
parameters:
- $ref: "#/components/parameters/RunId"
responses:
"200":
description: Run checkpoint timeline
content:
application/json:
schema:
$ref: "#/components/schemas/RunTimelineResponse"
"404":
description: Run not found
headers:
x-request-id:
$ref: "#/components/headers/XRequestId"
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/api/v1/runs/{id}/unarchive:
post:
operationId: unarchiveRun
@ -12726,6 +12917,11 @@ components:
retried_from:
type: ["string", "null"]
description: Source run ID when this run was created by manual retry.
forked_from:
oneOf:
- $ref: "#/components/schemas/ForkOrigin"
- type: "null"
description: Where the run's records came from when it is a fork.
git_identity:
oneOf:
- $ref: "#/components/schemas/GitIdentity"
@ -13076,6 +13272,176 @@ components:
type: ["string", "null"]
format: uri
ForkOrigin:
description: >-
Where a forked run came from: the source run, the position (an
execution and a firing of its root invocation) the source's records
were kept up to, and whether that firing runs again in the fork.
type: object
required:
- source_run_id
- execution
- firing
- rerun_last
properties:
source_run_id:
type: string
execution:
type: integer
format: uint64
firing:
type: integer
format: uint64
rerun_last:
type: boolean
ForkRequest:
description: Request body for creating a new run from a source run checkpoint.
type: object
properties:
target:
type: ["string", "null"]
description: Optional checkpoint target such as `@2`, `build`, or `build@1`. Defaults to the latest checkpoint.
ForkResponse:
description: Response returned after creating and starting a forked run.
type: object
required:
- source_run_id
- new_run_id
- target
- checkpoint_sha
- execution
- firing
- rerun_last
properties:
source_run_id:
type: string
new_run_id:
type: string
target:
type: string
description: The checkpoint the fork was resolved to, as `@ordinal`.
checkpoint_sha:
type: string
description: The commit the new run's workspace starts on.
execution:
type: integer
format: uint64
description: The Petri execution of the fork position.
firing:
type: integer
format: uint64
description: The Petri firing of the fork position.
rerun_last:
type: boolean
description: Whether the position's stage runs again in the new run.
RewindRequest:
description: Request body for creating a replacement run from a source run checkpoint.
type: object
properties:
target:
type: ["string", "null"]
description: Optional checkpoint target such as `@2`, `build`, or `build@1`. Defaults to the latest checkpoint.
RewindResponse:
description: Response returned after rewind creates and starts a new run.
type: object
required:
- source_run_id
- new_run_id
- target
- checkpoint_sha
- execution
- firing
- archived
properties:
source_run_id:
type: string
new_run_id:
type: string
target:
type: string
description: The checkpoint the rewind was resolved to, as `@ordinal`.
checkpoint_sha:
type: string
execution:
type: integer
format: uint64
firing:
type: integer
format: uint64
archived:
type: boolean
archive_error:
type: ["string", "null"]
RunTimelineResponse:
description: The run's checkpoints in order, and its fork origin when it is a fork.
type: object
required:
- entries
properties:
entries:
type: array
items:
$ref: "#/components/schemas/TimelineEntryResponse"
forked_from:
oneOf:
- $ref: "#/components/schemas/ForkOrigin"
- type: "null"
TimelineEntryResponse:
description: One checkpoint of a run.
type: object
required:
- ordinal
- checkpoint_seq
- execution
- firing
- attempt
- node_name
- visit
properties:
ordinal:
type: integer
format: uint64
description: 1-based, in the order the checkpoints were recorded; the `@ordinal` a fork target names.
checkpoint_seq:
type: integer
format: uint64
description: The checkpoint record's position among the run's platform records.
execution:
type: integer
format: uint64
description: The Petri execution the checkpoint belongs to.
firing:
type: integer
format: uint64
description: The Petri firing the checkpoint belongs to.
attempt:
type: integer
format: uint32
description: The attempt of the firing whose files the commit holds, from 1.
stage:
type: ["string", "null"]
description: The stage id (`node@visit`) when the projection shows the stage.
node_name:
type: string
visit:
type: integer
format: uint32
workspace:
type: ["string", "null"]
description: The Petri workspace id the commit was made in.
run_commit_sha:
type: ["string", "null"]
diff_summary:
oneOf:
- $ref: "#/components/schemas/DiffSummary"
- type: "null"
BoardColumn:
description: |
Status bucket for a run, shared by list and kanban renderings and by

View file

@ -35,7 +35,7 @@ pub(super) fn routes() -> Router<Arc<AppState>> {
.route("/runs/{id}/unarchive", post(unarchive_run))
}
async fn run_response(state: &AppState, id: RunId, status: StatusCode) -> Response {
pub(super) async fn run_response(state: &AppState, id: RunId, status: StatusCode) -> Response {
match state.stores.run_summaries.get(&id, Utc::now()).await {
Ok(Some(summary)) => {
(status, Json(state.decorate_run_summary(summary).await)).into_response()
@ -116,7 +116,21 @@ pub(in crate::server) async fn queue_run_start(
));
}
}
queue_run(state, id, &run_state, resume, actor).await
}
/// Queue the run for the scheduler: record that its start was requested
/// and that it is runnable (or pending approval), and register it as a
/// managed run in start or resume mode. The caller has checked that the run
/// may be queued; a fork, seeded to resume, is queued here without the
/// checkpoint check a resume of an interrupted run makes.
pub(super) async fn queue_run(
state: &AppState,
id: RunId,
run_state: &fabro_store::RunProjection,
resume: bool,
actor: Principal,
) -> Result<(), ApiError> {
let run_dir = Storage::new(state.server_storage_dir())
.run_scratch(&id)
.root()
@ -810,7 +824,7 @@ async fn batch_delete_runs(
}
#[derive(Clone, Copy)]
enum ArchiveAction {
pub(super) enum ArchiveAction {
Archive,
Unarchive,
}
@ -986,7 +1000,7 @@ fn batch_result_failure(
/// Archive a terminal run, or unarchive one: idempotent either way, refused
/// with a precondition error when the run is not terminal.
async fn run_archive_operation(
pub(super) async fn run_archive_operation(
state: &AppState,
id: &RunId,
actor: Option<Principal>,

View file

@ -0,0 +1,364 @@
//! A run's checkpoint timeline, and the runs made from it: fork, rewind and
//! retry (the integration plan's F5.1).
//!
//! The timeline is the run's `checkpoint` platform records, labelled with
//! the stages the projector folded them onto. A fork resolves a target on
//! that timeline (`@ordinal`, a node, or `node@visit`; the latest checkpoint
//! by default), creates the new run's row (`fabro_workflow::operations`),
//! seeds its records, checkpoints, snapshots and run branch from the source
//! (`fabro_petri::fork`), and queues it in resume mode, so its worker
//! acquires a fresh workspace, restores the checkpoint's commit into it and
//! continues from the position. A rewind is a fork of a terminal run that
//! archives the source and records `run.superseded` on it; a retry is a
//! fork of a terminal run at its last checkpoint, with the failed stage run
//! again when the run failed on one.
use std::sync::Arc;
use axum::extract::{Path, State};
use axum::http::{HeaderMap, StatusCode};
use axum::response::{IntoResponse, Response};
use axum::routing::{get, post};
use axum::{Json, Router};
use fabro_api::types as api;
use fabro_config::Storage;
use fabro_petri::SqliteRunStore;
use fabro_petri::fork::{self as petri_fork, ForkError, ForkRequest};
use fabro_petri::petri::RunStore;
use fabro_petri::platform_records::SqlitePlatformRecords;
use fabro_store::{PlatformRecordKind, RunProjection};
use fabro_types::{FailureReason, Principal, RunId};
use fabro_util::error as error_util;
use fabro_workflow::Error as WorkflowError;
use fabro_workflow::operations::{self, ForkTarget, ResolvedForkTarget, RunTimeline};
use tracing::{error, warn};
use super::super::{
ApiError, AppState, RequireRunManagementTarget, RequiredUser, parse_run_id_path, run_records,
};
use super::lifecycle::{ArchiveAction, queue_run, run_archive_operation, run_response};
use super::runs::run_provenance;
pub(super) fn routes() -> Router<Arc<AppState>> {
Router::new()
.route("/runs/{id}/timeline", get(run_timeline))
.route("/runs/{id}/fork", post(fork_run))
.route("/runs/{id}/rewind", post(rewind_run))
.route("/runs/{id}/retry", post(retry_run))
}
/// Which operation a fork is made for: what it requires of the source and
/// what it records.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum ForkKind {
Fork,
Rewind,
Retry,
}
/// A fork made and queued.
struct ForkOutcome {
source_run_id: RunId,
new_run_id: RunId,
target: ResolvedForkTarget,
rerun_last: bool,
}
async fn run_timeline(
_auth: RequiredUser,
State(state): State<Arc<AppState>>,
Path(id): Path<String>,
) -> Response {
let id = match parse_run_id_path(&id) {
Ok(id) => id,
Err(response) => return response,
};
let projection = match run_records::require_projection(state.as_ref(), id).await {
Ok(projection) => projection,
Err(err) => return err.into_response(),
};
match timeline(state.as_ref(), id).await {
Ok(timeline) => Json(timeline_response(&timeline, &projection)).into_response(),
Err(err) => err.into_response(),
}
}
async fn fork_run(
RequireRunManagementTarget(id, actor): RequireRunManagementTarget,
State(state): State<Arc<AppState>>,
headers: HeaderMap,
body: Option<Json<api::ForkRequest>>,
) -> Response {
let target = match parse_fork_target(body.and_then(|Json(body)| body.target)) {
Ok(target) => target,
Err(err) => return err.into_response(),
};
match fork_at(state.as_ref(), id, actor, &headers, ForkKind::Fork, target).await {
Ok(outcome) => (
StatusCode::OK,
Json(api::ForkResponse {
source_run_id: outcome.source_run_id.to_string(),
new_run_id: outcome.new_run_id.to_string(),
target: outcome.target.response_target(),
checkpoint_sha: outcome.target.checkpoint_sha.clone(),
execution: outcome.target.position.execution,
firing: outcome.target.position.firing,
rerun_last: outcome.rerun_last,
}),
)
.into_response(),
Err(err) => err.into_response(),
}
}
async fn rewind_run(
RequireRunManagementTarget(id, actor): RequireRunManagementTarget,
State(state): State<Arc<AppState>>,
headers: HeaderMap,
body: Option<Json<api::RewindRequest>>,
) -> Response {
let target = match parse_fork_target(body.and_then(|Json(body)| body.target)) {
Ok(target) => target,
Err(err) => return err.into_response(),
};
let outcome = match fork_at(
state.as_ref(),
id,
actor.clone(),
&headers,
ForkKind::Rewind,
target,
)
.await
{
Ok(outcome) => outcome,
Err(err) => return err.into_response(),
};
// The source is replaced: archived, and marked with what replaced it.
// A failed archive leaves the new run in place and says so.
let archived = run_archive_operation(state.as_ref(), &id, Some(actor), ArchiveAction::Archive)
.await
.map(|_| ());
if archived.is_ok() {
let record = operations::superseded_record(outcome.new_run_id, &outcome.target);
if let Err(err) = run_records::append(state.as_ref(), id, record).await {
error!(
source_run_id = %id,
new_run_id = %outcome.new_run_id,
error = %err,
"the rewound run was archived but its superseded record was not written"
);
}
}
let (status, archive_error) = match archived {
Ok(()) => (StatusCode::OK, None),
Err(err) => (StatusCode::MULTI_STATUS, Some(err.to_string())),
};
(
status,
Json(api::RewindResponse {
source_run_id: outcome.source_run_id.to_string(),
new_run_id: outcome.new_run_id.to_string(),
target: outcome.target.response_target(),
checkpoint_sha: outcome.target.checkpoint_sha.clone(),
execution: outcome.target.position.execution,
firing: outcome.target.position.firing,
archived: archive_error.is_none(),
archive_error,
}),
)
.into_response()
}
async fn retry_run(
RequireRunManagementTarget(id, actor): RequireRunManagementTarget,
State(state): State<Arc<AppState>>,
headers: HeaderMap,
) -> Response {
match fork_at(state.as_ref(), id, actor, &headers, ForkKind::Retry, None).await {
Ok(outcome) => run_response(state.as_ref(), outcome.new_run_id, StatusCode::CREATED).await,
Err(err) => err.into_response(),
}
}
/// The run's timeline: its checkpoint records, labelled through the
/// projector's fold state.
async fn timeline(state: &AppState, id: RunId) -> Result<RunTimeline, ApiError> {
let checkpoints = state
.stores
.run_summaries
.platform_records()
.read_kind(&id, PlatformRecordKind::Checkpoint)
.await
.map_err(|err| ApiError::new(StatusCode::INTERNAL_SERVER_ERROR, err.to_string()))?;
let labels = petri_fork::stage_labels(&state.stores.run_summaries.pool(), id)
.await
.map_err(|err| ApiError::new(StatusCode::INTERNAL_SERVER_ERROR, err.to_string()))?;
Ok(RunTimeline::build(&checkpoints, &labels))
}
fn timeline_response(
timeline: &RunTimeline,
projection: &RunProjection,
) -> api::RunTimelineResponse {
api::RunTimelineResponse {
entries: timeline
.entries
.iter()
.map(|entry| api::TimelineEntryResponse {
ordinal: u64::try_from(entry.ordinal).unwrap_or(u64::MAX),
checkpoint_seq: entry.checkpoint_seq,
execution: entry.position.execution,
firing: entry.position.firing,
attempt: entry.position.attempt,
stage: entry.stage_id.clone(),
node_name: entry.node_name.clone(),
visit: entry.visit,
workspace: entry.workspace.clone(),
run_commit_sha: entry.run_commit_sha.clone(),
diff_summary: entry.diff_summary.as_ref().map(|summary| api::DiffSummary {
files_changed: summary.files_changed,
additions: summary.additions,
deletions: summary.deletions,
}),
})
.collect(),
forked_from: projection.forked_from.as_ref().map(fork_origin_response),
}
}
fn fork_origin_response(origin: &fabro_types::ForkOrigin) -> api::ForkOrigin {
api::ForkOrigin {
source_run_id: origin.source_run_id.to_string(),
execution: origin.execution,
firing: origin.firing,
rerun_last: origin.rerun_last,
}
}
fn parse_fork_target(target: Option<String>) -> Result<Option<ForkTarget>, ApiError> {
target
.map(|target| {
target
.parse::<ForkTarget>()
.map_err(workflow_operation_error)
})
.transpose()
}
/// Make the fork: check the source, resolve the target, create the new
/// run's row, seed it from the source, and queue it in resume mode.
async fn fork_at(
state: &AppState,
id: RunId,
actor: Principal,
headers: &HeaderMap,
kind: ForkKind,
target: Option<ForkTarget>,
) -> Result<ForkOutcome, ApiError> {
let source = run_records::require_projection(state, id).await?;
match kind {
ForkKind::Fork => operations::ensure_forkable(&source, &id),
ForkKind::Rewind => operations::ensure_rewindable(&source, &id),
ForkKind::Retry => operations::ensure_retryable(&source, &id),
}
.map_err(workflow_operation_error)?;
let timeline = timeline(state, id).await?;
let entry = match kind {
ForkKind::Retry => timeline.latest(),
ForkKind::Fork | ForkKind::Rewind => timeline.resolve_or_latest(target.as_ref()),
}
.map_err(workflow_operation_error)?;
let resolved = ResolvedForkTarget::of(entry).map_err(workflow_operation_error)?;
let rerun_last = kind == ForkKind::Retry && operations::reruns_last(source.status);
// A position Petri would refuse is refused before the new run exists.
let position = petri_fork::position(resolved.position.execution, resolved.position.firing);
let store: Arc<dyn RunStore> = state
.petri_projector
.observe_store(Arc::new(SqliteRunStore::new(state.db_pool.clone())));
petri_fork::check(store.as_ref(), id, position)
.await
.map_err(|err| fork_error(&err))?;
let new_run_id = RunId::new();
let storage = Storage::new(state.server_storage_dir());
let source_run_dir = storage.run_scratch(&id).root().to_path_buf();
let run_dir = storage.run_scratch(&new_run_id).root().to_path_buf();
let provenance = (kind == ForkKind::Retry).then(|| run_provenance(headers, &actor));
operations::persist_forked_run(state.store_ref().as_ref(), &operations::ForkedRunInput {
source: &source,
new_run_id,
run_dir: run_dir.clone(),
checkpoint_sha: resolved.checkpoint_sha.clone(),
provenance,
web_url: state.run_web_url(&new_run_id),
retried_from: (kind == ForkKind::Retry).then_some(id),
})
.await
.map_err(workflow_operation_error)?;
let seeded = petri_fork::fork(ForkRequest {
source: id,
fork: new_run_id,
source_run_dir: source_run_dir.join("petri"),
fork_run_dir: run_dir.join("petri"),
store,
records: Arc::new(SqlitePlatformRecords::new(Arc::clone(
&state.stores.run_summaries,
))),
position,
rerun_last,
settings: source.spec.settings.run.clone(),
})
.await;
if let Err(err) = seeded {
// The new run's row exists and holds nothing to continue from: it
// is reported failed with the reason, rather than left submitted.
let message = error_util::collect_chain(&err).join(": ");
warn!(source_run_id = %id, new_run_id = %new_run_id, error = %message, "the fork could not be seeded");
if let Err(record_err) = run_records::lifecycle(
state,
new_run_id,
run_records::failed(FailureReason::WorkflowError, message.clone()),
)
.await
{
error!(new_run_id = %new_run_id, error = %record_err, "the fork's failure was not recorded");
}
return Err(fork_error(&err));
}
// The fork continues as a run left in flight does: queued in resume
// mode, on the projection its seeded records folded to.
let projection = run_records::require_projection(state, new_run_id).await?;
queue_run(state, new_run_id, &projection, true, actor).await?;
Ok(ForkOutcome {
source_run_id: id,
new_run_id,
target: resolved,
rerun_last,
})
}
/// A refused position is the caller's mistake; anything else is the
/// server's.
fn fork_error(err: &ForkError) -> ApiError {
let message = error_util::collect_chain(err).join(": ");
let status = match err {
ForkError::Refused(_) => StatusCode::BAD_REQUEST,
_ => StatusCode::INTERNAL_SERVER_ERROR,
};
ApiError::new(status, message)
}
fn workflow_operation_error(err: WorkflowError) -> ApiError {
match err {
WorkflowError::Parse(message) | WorkflowError::Validation(message) => {
ApiError::bad_request(message)
}
WorkflowError::Precondition(message) => ApiError::new(StatusCode::CONFLICT, message),
WorkflowError::RunNotFound(_) => ApiError::not_found("Run not found."),
err => ApiError::new(StatusCode::INTERNAL_SERVER_ERROR, err.to_string()),
}
}

View file

@ -14,6 +14,7 @@ mod environments;
pub(in crate::server) mod events;
pub(in crate::server) mod graph;
pub(in crate::server) mod lifecycle;
mod lineage;
mod llm_sse;
mod mcp_servers;
mod models;
@ -214,6 +215,7 @@ pub(super) fn real_routes() -> Router<Arc<AppState>> {
.merge(sandbox::routes())
.merge(sandboxes::routes())
.merge(lifecycle::routes())
.merge(lineage::routes())
.merge(steer::routes())
.merge(pair::routes())
.merge(petri::routes())

View file

@ -78,6 +78,13 @@ pub struct SessionEventStream {
buffered_events: VecDeque<SessionEvent>,
}
/// What a rewind returned: the response, and the status that says whether
/// the source was archived.
pub struct RewindRunResult {
pub status: u16,
pub response: types::RewindResponse,
}
#[derive(Default)]
struct ListStoreRunsOptions {
parent_id: Option<RunId>,
@ -1393,6 +1400,74 @@ impl Client {
convert_type(response.into_inner())
}
/// The run's checkpoint timeline, and where it was forked from.
pub async fn run_timeline(&self, run_id: &RunId) -> Result<types::RunTimelineResponse> {
let response = self
.send_api(|client| async move {
client
.get_run_timeline()
.id(run_id.to_string())
.send()
.await
})
.await?;
Ok(response.into_inner())
}
/// Fork the run at a checkpoint into a new run, started in resume mode.
pub async fn fork_run(
&self,
run_id: &RunId,
request: types::ForkRequest,
) -> Result<types::ForkResponse> {
let response = self
.send_api(|client| async move {
client
.fork_run()
.id(run_id.to_string())
.body(request)
.send()
.await
})
.await?;
Ok(response.into_inner())
}
/// Rewind the run to a checkpoint: a fork that archives and supersedes
/// the source. The status says whether the archive succeeded (200) or
/// the new run was made without it (207).
pub async fn rewind_run(
&self,
run_id: &RunId,
request: types::RewindRequest,
) -> Result<RewindRunResult> {
let response = self
.send_api(|client| async move {
client
.rewind_run()
.id(run_id.to_string())
.body(request)
.send()
.await
})
.await?;
let status = response.status().as_u16();
Ok(RewindRunResult {
status,
response: response.into_inner(),
})
}
/// Retry a terminal run from its last checkpoint: the new run.
pub async fn retry_run(&self, run_id: &RunId) -> Result<Run> {
let response = self
.send_api(
|client| async move { client.retry_run().id(run_id.to_string()).send().await },
)
.await?;
convert_type(response.into_inner())
}
pub async fn resolve_run(&self, selector: &str) -> Result<Run> {
let response = self
.send_api(|client| async move {

View file

@ -12,8 +12,8 @@ pub use auth_store::{
AuthEntry, AuthStore, AuthStoreError, DevTokenEntry, LockError, OAuthEntry, StoredSubject,
};
pub use client::{
Client, RunStreamItemStream, RunStreamPage, SessionEventStream, TransportConnector,
apply_bearer_token_auth,
Client, RewindRunResult, RunStreamItemStream, RunStreamPage, SessionEventStream,
TransportConnector, apply_bearer_token_auth,
};
pub use credential::{Credential, CredentialFallback};
pub use error::{

View file

@ -164,6 +164,9 @@ models/failure-reason.ts
models/file-checkpoint.ts
models/file-diff.ts
models/folder-run-target.ts
models/fork-origin.ts
models/fork-request.ts
models/fork-response.ts
models/fork-source-ref.ts
models/git-author-settings.ts
models/git-context.ts
@ -364,6 +367,8 @@ models/repository-ref.ts
models/resolved-automation-git-workflow-source.ts
models/review-target-kind.ts
models/review-target.ts
models/rewind-request.ts
models/rewind-response.ts
models/root-response-urls.ts
models/root-response.ts
models/run-agent-settings.ts
@ -441,6 +446,7 @@ models/run-stream-item-kind.ts
models/run-stream-item.ts
models/run-superseded-by-props.ts
models/run-target.ts
models/run-timeline-response.ts
models/run-timestamps.ts
models/run-timing.ts
models/run-usage-stage.ts
@ -487,7 +493,6 @@ models/server-sandbox-provider-settings.ts
models/server-sandbox-settings.ts
models/server-scheduler-settings.ts
models/server-settings.ts
models/server-slate-db-settings.ts
models/server-storage-settings.ts
models/server-web-settings.ts
models/session-detail.ts
@ -539,6 +544,7 @@ models/system-repair-run-issue.ts
models/system-repair-runs-response.ts
models/system-resources-response.ts
models/system-run-counts.ts
models/timeline-entry-response.ts
models/tls-mode.ts
models/todo-list-kind.ts
models/todo-list-projection.ts

View file

@ -42,6 +42,10 @@ import type { DenyRunRequest } from '../models';
// @ts-ignore
import type { ErrorResponse } from '../models';
// @ts-ignore
import type { ForkRequest } from '../models';
// @ts-ignore
import type { ForkResponse } from '../models';
// @ts-ignore
import type { LinkRunPullRequestRequest } from '../models';
// @ts-ignore
import type { MergeRunPullRequestRequest } from '../models';
@ -60,12 +64,18 @@ import type { PullRequestResponse } from '../models';
// @ts-ignore
import type { RenderWorkflowGraphRequest } from '../models';
// @ts-ignore
import type { RewindRequest } from '../models';
// @ts-ignore
import type { RewindResponse } from '../models';
// @ts-ignore
import type { Run } from '../models';
// @ts-ignore
import type { RunIntent } from '../models';
// @ts-ignore
import type { RunManifest } from '../models';
// @ts-ignore
import type { RunTimelineResponse } from '../models';
// @ts-ignore
import type { StartRunRequest } from '../models';
// @ts-ignore
import type { UpdateRunParentRequest } from '../models';
@ -535,6 +545,49 @@ export const RunsApiAxiosParamCreator = function (configuration?: Configuration)
options: localVarRequestOptions,
};
},
/**
* Creates a new run from a checkpoint of the source run and starts it. The new run holds the source\'s records up to the checkpoint\'s position and continues from there in a fresh workspace restored to the checkpoint\'s commit. The source run is left untouched. A checkpoint inside a parallel branch cannot be forked at; fork at the parallel stage instead.
* @summary Fork Run
* @param {string} id Unique run identifier (ULID).
* @param {ForkRequest} [forkRequest]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
forkRun: async (id: string, forkRequest?: ForkRequest, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'id' is not null or undefined
assertParamExists('forkRun', 'id', id)
const localVarPath = `/api/v1/runs/{id}/fork`
.replace(`{${"id"}}`, encodeURIComponent(String(id)));
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
// authentication SessionCookie required
// authentication BearerAuth required
// http bearer authentication required
await setBearerAuthToObject(localVarHeaderParameter, configuration)
localVarHeaderParameter['Content-Type'] = 'application/json';
localVarHeaderParameter['Accept'] = 'application/json';
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
localVarRequestOptions.data = serializeDataIfNeeded(forkRequest, localVarRequestOptions, configuration)
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
* Returns the stored pull request record for a run plus live GitHub details when available.
* @summary Get Run Pull Request
@ -615,6 +668,46 @@ export const RunsApiAxiosParamCreator = function (configuration?: Configuration)
options: localVarRequestOptions,
};
},
/**
* Returns the run\'s checkpoints in the order they were recorded, each at its Petri position with the commit it made and the stage it belongs to, and where the run was forked from when it is a fork.
* @summary Get Run Timeline
* @param {string} id Unique run identifier (ULID).
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getRunTimeline: async (id: string, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'id' is not null or undefined
assertParamExists('getRunTimeline', 'id', id)
const localVarPath = `/api/v1/runs/{id}/timeline`
.replace(`{${"id"}}`, encodeURIComponent(String(id)));
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
// authentication SessionCookie required
// authentication BearerAuth required
// http bearer authentication required
await setBearerAuthToObject(localVarHeaderParameter, configuration)
localVarHeaderParameter['Accept'] = 'application/json';
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
* Links a run under an orchestration parent. Parent links are mutable for all run states, including archived and terminal runs.
* @summary Link Run Parent
@ -1070,6 +1163,89 @@ export const RunsApiAxiosParamCreator = function (configuration?: Configuration)
options: localVarRequestOptions,
};
},
/**
* Creates a new run from the terminal source run\'s last checkpoint and starts it. When the source failed on a stage, that stage runs again on the files of the stage before it; otherwise the new run continues from the last checkpoint as it stands. The new run records `retried_from` and `fork_source_ref`; the source run is left unchanged. Active and archived runs are not retryable.
* @summary Retry Run
* @param {string} id Unique run identifier (ULID).
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
retryRun: async (id: string, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'id' is not null or undefined
assertParamExists('retryRun', 'id', id)
const localVarPath = `/api/v1/runs/{id}/retry`
.replace(`{${"id"}}`, encodeURIComponent(String(id)));
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
// authentication SessionCookie required
// authentication BearerAuth required
// http bearer authentication required
await setBearerAuthToObject(localVarHeaderParameter, configuration)
localVarHeaderParameter['Accept'] = 'application/json';
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
* Creates a new run from a checkpoint of a terminal source run and starts it, then archives the source run and records `run.superseded_by` on it. 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 {*} [options] Override http request option.
* @throws {RequiredError}
*/
rewindRun: async (id: string, rewindRequest?: RewindRequest, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'id' is not null or undefined
assertParamExists('rewindRun', 'id', id)
const localVarPath = `/api/v1/runs/{id}/rewind`
.replace(`{${"id"}}`, encodeURIComponent(String(id)));
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
// authentication SessionCookie required
// authentication BearerAuth required
// http bearer authentication required
await setBearerAuthToObject(localVarHeaderParameter, configuration)
localVarHeaderParameter['Content-Type'] = 'application/json';
localVarHeaderParameter['Accept'] = 'application/json';
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
localVarRequestOptions.data = serializeDataIfNeeded(rewindRequest, localVarRequestOptions, configuration)
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
* Validates runtime readiness for a workflow manifest without creating a run.
* @summary Validate Workflow Manifest
@ -1555,6 +1731,20 @@ export const RunsApiFp = function(configuration?: Configuration) {
const localVarOperationServerBasePath = operationServerMap['RunsApi.denyRun']?.[localVarOperationServerIndex]?.url;
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
},
/**
* Creates a new run from a checkpoint of the source run and starts it. The new run holds the source\'s records up to the checkpoint\'s position and continues from there in a fresh workspace restored to the checkpoint\'s commit. The source run is left untouched. A checkpoint inside a parallel branch cannot be forked at; fork at the parallel stage instead.
* @summary Fork Run
* @param {string} id Unique run identifier (ULID).
* @param {ForkRequest} [forkRequest]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async forkRun(id: string, forkRequest?: ForkRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<ForkResponse>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.forkRun(id, forkRequest, options);
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
const localVarOperationServerBasePath = operationServerMap['RunsApi.forkRun']?.[localVarOperationServerIndex]?.url;
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
},
/**
* Returns the stored pull request record for a run plus live GitHub details when available.
* @summary Get Run Pull Request
@ -1581,6 +1771,19 @@ export const RunsApiFp = function(configuration?: Configuration) {
const localVarOperationServerBasePath = operationServerMap['RunsApi.getRunPullRequestCreation']?.[localVarOperationServerIndex]?.url;
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
},
/**
* Returns the run\'s checkpoints in the order they were recorded, each at its Petri position with the commit it made and the stage it belongs to, and where the run was forked from when it is a fork.
* @summary Get Run Timeline
* @param {string} id Unique run identifier (ULID).
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async getRunTimeline(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<RunTimelineResponse>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.getRunTimeline(id, options);
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
const localVarOperationServerBasePath = operationServerMap['RunsApi.getRunTimeline']?.[localVarOperationServerIndex]?.url;
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
},
/**
* Links a run under an orchestration parent. Parent links are mutable for all run states, including archived and terminal runs.
* @summary Link Run Parent
@ -1721,6 +1924,33 @@ export const RunsApiFp = function(configuration?: Configuration) {
const localVarOperationServerBasePath = operationServerMap['RunsApi.retrieveRunGraphSource']?.[localVarOperationServerIndex]?.url;
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
},
/**
* Creates a new run from the terminal source run\'s last checkpoint and starts it. When the source failed on a stage, that stage runs again on the files of the stage before it; otherwise the new run continues from the last checkpoint as it stands. The new run records `retried_from` and `fork_source_ref`; the source run is left unchanged. Active and archived runs are not retryable.
* @summary Retry Run
* @param {string} id Unique run identifier (ULID).
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async retryRun(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Run>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.retryRun(id, options);
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
const localVarOperationServerBasePath = operationServerMap['RunsApi.retryRun']?.[localVarOperationServerIndex]?.url;
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
},
/**
* Creates a new run from a checkpoint of a terminal source run and starts it, then archives the source run and records `run.superseded_by` on it. 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 {*} [options] Override http request option.
* @throws {RequiredError}
*/
async rewindRun(id: string, rewindRequest?: RewindRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<RewindResponse>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.rewindRun(id, rewindRequest, options);
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
const localVarOperationServerBasePath = operationServerMap['RunsApi.rewindRun']?.[localVarOperationServerIndex]?.url;
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
},
/**
* Validates runtime readiness for a workflow manifest without creating a run.
* @summary Validate Workflow Manifest
@ -1949,6 +2179,17 @@ export const RunsApiFactory = function (configuration?: Configuration, basePath?
denyRun(id: string, denyRunRequest?: DenyRunRequest, options?: RawAxiosRequestConfig): AxiosPromise<Run> {
return localVarFp.denyRun(id, denyRunRequest, options).then((request) => request(axios, basePath));
},
/**
* Creates a new run from a checkpoint of the source run and starts it. The new run holds the source\'s records up to the checkpoint\'s position and continues from there in a fresh workspace restored to the checkpoint\'s commit. The source run is left untouched. A checkpoint inside a parallel branch cannot be forked at; fork at the parallel stage instead.
* @summary Fork Run
* @param {string} id Unique run identifier (ULID).
* @param {ForkRequest} [forkRequest]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
forkRun(id: string, forkRequest?: ForkRequest, options?: RawAxiosRequestConfig): AxiosPromise<ForkResponse> {
return localVarFp.forkRun(id, forkRequest, options).then((request) => request(axios, basePath));
},
/**
* Returns the stored pull request record for a run plus live GitHub details when available.
* @summary Get Run Pull Request
@ -1969,6 +2210,16 @@ export const RunsApiFactory = function (configuration?: Configuration, basePath?
getRunPullRequestCreation(id: string, options?: RawAxiosRequestConfig): AxiosPromise<PullRequestCreation> {
return localVarFp.getRunPullRequestCreation(id, options).then((request) => request(axios, basePath));
},
/**
* Returns the run\'s checkpoints in the order they were recorded, each at its Petri position with the commit it made and the stage it belongs to, and where the run was forked from when it is a fork.
* @summary Get Run Timeline
* @param {string} id Unique run identifier (ULID).
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getRunTimeline(id: string, options?: RawAxiosRequestConfig): AxiosPromise<RunTimelineResponse> {
return localVarFp.getRunTimeline(id, options).then((request) => request(axios, basePath));
},
/**
* Links a run under an orchestration parent. Parent links are mutable for all run states, including archived and terminal runs.
* @summary Link Run Parent
@ -2079,6 +2330,27 @@ export const RunsApiFactory = function (configuration?: Configuration, basePath?
retrieveRunGraphSource(id: string, options?: RawAxiosRequestConfig): AxiosPromise<string> {
return localVarFp.retrieveRunGraphSource(id, options).then((request) => request(axios, basePath));
},
/**
* Creates a new run from the terminal source run\'s last checkpoint and starts it. When the source failed on a stage, that stage runs again on the files of the stage before it; otherwise the new run continues from the last checkpoint as it stands. The new run records `retried_from` and `fork_source_ref`; the source run is left unchanged. Active and archived runs are not retryable.
* @summary Retry Run
* @param {string} id Unique run identifier (ULID).
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
retryRun(id: string, options?: RawAxiosRequestConfig): AxiosPromise<Run> {
return localVarFp.retryRun(id, options).then((request) => request(axios, basePath));
},
/**
* Creates a new run from a checkpoint of a terminal source run and starts it, then archives the source run and records `run.superseded_by` on it. 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 {*} [options] Override http request option.
* @throws {RequiredError}
*/
rewindRun(id: string, rewindRequest?: RewindRequest, options?: RawAxiosRequestConfig): AxiosPromise<RewindResponse> {
return localVarFp.rewindRun(id, rewindRequest, options).then((request) => request(axios, basePath));
},
/**
* Validates runtime readiness for a workflow manifest without creating a run.
* @summary Validate Workflow Manifest
@ -2292,6 +2564,18 @@ export class RunsApi extends BaseAPI {
return RunsApiFp(this.configuration).denyRun(id, denyRunRequest, options).then((request) => request(this.axios, this.basePath));
}
/**
* Creates a new run from a checkpoint of the source run and starts it. The new run holds the source\'s records up to the checkpoint\'s position and continues from there in a fresh workspace restored to the checkpoint\'s commit. The source run is left untouched. A checkpoint inside a parallel branch cannot be forked at; fork at the parallel stage instead.
* @summary Fork Run
* @param {string} id Unique run identifier (ULID).
* @param {ForkRequest} [forkRequest]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
public forkRun(id: string, forkRequest?: ForkRequest, options?: RawAxiosRequestConfig) {
return RunsApiFp(this.configuration).forkRun(id, forkRequest, options).then((request) => request(this.axios, this.basePath));
}
/**
* Returns the stored pull request record for a run plus live GitHub details when available.
* @summary Get Run Pull Request
@ -2314,6 +2598,17 @@ export class RunsApi extends BaseAPI {
return RunsApiFp(this.configuration).getRunPullRequestCreation(id, options).then((request) => request(this.axios, this.basePath));
}
/**
* Returns the run\'s checkpoints in the order they were recorded, each at its Petri position with the commit it made and the stage it belongs to, and where the run was forked from when it is a fork.
* @summary Get Run Timeline
* @param {string} id Unique run identifier (ULID).
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
public getRunTimeline(id: string, options?: RawAxiosRequestConfig) {
return RunsApiFp(this.configuration).getRunTimeline(id, options).then((request) => request(this.axios, this.basePath));
}
/**
* Links a run under an orchestration parent. Parent links are mutable for all run states, including archived and terminal runs.
* @summary Link Run Parent
@ -2434,6 +2729,29 @@ export class RunsApi extends BaseAPI {
return RunsApiFp(this.configuration).retrieveRunGraphSource(id, options).then((request) => request(this.axios, this.basePath));
}
/**
* Creates a new run from the terminal source run\'s last checkpoint and starts it. When the source failed on a stage, that stage runs again on the files of the stage before it; otherwise the new run continues from the last checkpoint as it stands. The new run records `retried_from` and `fork_source_ref`; the source run is left unchanged. Active and archived runs are not retryable.
* @summary Retry Run
* @param {string} id Unique run identifier (ULID).
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
public retryRun(id: string, options?: RawAxiosRequestConfig) {
return RunsApiFp(this.configuration).retryRun(id, options).then((request) => request(this.axios, this.basePath));
}
/**
* Creates a new run from a checkpoint of a terminal source run and starts it, then archives the source run and records `run.superseded_by` on it. 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 {*} [options] Override http request option.
* @throws {RequiredError}
*/
public rewindRun(id: string, rewindRequest?: RewindRequest, options?: RawAxiosRequestConfig) {
return RunsApiFp(this.configuration).rewindRun(id, rewindRequest, options).then((request) => request(this.axios, this.basePath));
}
/**
* Validates runtime readiness for a workflow manifest without creating a run.
* @summary Validate Workflow Manifest

View file

@ -0,0 +1,25 @@
/* tslint:disable */
/* eslint-disable */
/**
* Fabro Run API
* HTTP API for managing Fabro workflow run executions.
*
* The version of the OpenAPI document: 0.2.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
/**
* Where a forked run came from: the source run, the position (an execution and a firing of its root invocation) the source\'s records were kept up to, and whether that firing runs again in the fork.
*/
export interface ForkOrigin {
'source_run_id': string;
'execution': number;
'firing': number;
'rerun_last': boolean;
}

View file

@ -0,0 +1,25 @@
/* tslint:disable */
/* eslint-disable */
/**
* Fabro Run API
* HTTP API for managing Fabro workflow run executions.
*
* The version of the OpenAPI document: 0.2.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
/**
* Request body for creating a new run from a source run checkpoint.
*/
export interface ForkRequest {
/**
* Optional checkpoint target such as `@2`, `build`, or `build@1`. Defaults to the latest checkpoint.
*/
'target'?: string | null;
}

View file

@ -0,0 +1,43 @@
/* tslint:disable */
/* eslint-disable */
/**
* Fabro Run API
* HTTP API for managing Fabro workflow run executions.
*
* The version of the OpenAPI document: 0.2.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
/**
* Response returned after creating and starting a forked run.
*/
export interface ForkResponse {
'source_run_id': string;
'new_run_id': string;
/**
* The checkpoint the fork was resolved to, as `@ordinal`.
*/
'target': string;
/**
* The commit the new run\'s workspace starts on.
*/
'checkpoint_sha': string;
/**
* The Petri execution of the fork position.
*/
'execution': number;
/**
* The Petri firing of the fork position.
*/
'firing': number;
/**
* Whether the position\'s stage runs again in the new run.
*/
'rerun_last': boolean;
}

View file

@ -135,6 +135,9 @@ export * from './failure-reason';
export * from './file-checkpoint';
export * from './file-diff';
export * from './folder-run-target';
export * from './fork-origin';
export * from './fork-request';
export * from './fork-response';
export * from './fork-source-ref';
export * from './git-author-settings';
export * from './git-context';
@ -334,6 +337,8 @@ export * from './repository-ref';
export * from './resolved-automation-git-workflow-source';
export * from './review-target';
export * from './review-target-kind';
export * from './rewind-request';
export * from './rewind-response';
export * from './root-response';
export * from './root-response-urls';
export * from './run';
@ -412,6 +417,7 @@ export * from './run-stream-item';
export * from './run-stream-item-kind';
export * from './run-superseded-by-props';
export * from './run-target';
export * from './run-timeline-response';
export * from './run-timestamps';
export * from './run-timing';
export * from './run-usage';
@ -508,6 +514,7 @@ export * from './system-repair-run-issue';
export * from './system-repair-runs-response';
export * from './system-resources-response';
export * from './system-run-counts';
export * from './timeline-entry-response';
export * from './tls-mode';
export * from './todo-list-kind';
export * from './todo-list-projection';

View file

@ -0,0 +1,25 @@
/* tslint:disable */
/* eslint-disable */
/**
* Fabro Run API
* HTTP API for managing Fabro workflow run executions.
*
* The version of the OpenAPI document: 0.2.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
/**
* Request body for creating a replacement run from a source run checkpoint.
*/
export interface RewindRequest {
/**
* Optional checkpoint target such as `@2`, `build`, or `build@1`. Defaults to the latest checkpoint.
*/
'target'?: string | null;
}

View file

@ -0,0 +1,32 @@
/* tslint:disable */
/* eslint-disable */
/**
* Fabro Run API
* HTTP API for managing Fabro workflow run executions.
*
* The version of the OpenAPI document: 0.2.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
/**
* Response returned after rewind creates and starts a new run.
*/
export interface RewindResponse {
'source_run_id': string;
'new_run_id': string;
/**
* The checkpoint the rewind was resolved to, as `@ordinal`.
*/
'target': string;
'checkpoint_sha': string;
'execution': number;
'firing': number;
'archived': boolean;
'archive_error'?: string | null;
}

View file

@ -21,6 +21,9 @@ import type { CheckpointRecord } from './checkpoint-record';
import type { Conclusion } from './conclusion';
// May contain unused imports in some cases
// @ts-ignore
import type { ForkOrigin } from './fork-origin';
// May contain unused imports in some cases
// @ts-ignore
import type { GitIdentity } from './git-identity';
// May contain unused imports in some cases
// @ts-ignore
@ -86,6 +89,7 @@ export interface RunProjection {
* Source run ID when this run was created by manual retry.
*/
'retried_from'?: string | null;
'forked_from'?: ForkOrigin | null;
'git_identity'?: GitIdentity | null;
'pending_interviews': { [key: string]: PendingInterviewRecord; };
/**

View file

@ -0,0 +1,29 @@
/* tslint:disable */
/* eslint-disable */
/**
* Fabro Run API
* HTTP API for managing Fabro workflow run executions.
*
* The version of the OpenAPI document: 0.2.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
// May contain unused imports in some cases
// @ts-ignore
import type { ForkOrigin } from './fork-origin';
// May contain unused imports in some cases
// @ts-ignore
import type { TimelineEntryResponse } from './timeline-entry-response';
/**
* The run\'s checkpoints in order, and its fork origin when it is a fork.
*/
export interface RunTimelineResponse {
'entries': Array<TimelineEntryResponse>;
'forked_from'?: ForkOrigin | null;
}

View file

@ -0,0 +1,56 @@
/* tslint:disable */
/* eslint-disable */
/**
* Fabro Run API
* HTTP API for managing Fabro workflow run executions.
*
* The version of the OpenAPI document: 0.2.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
// May contain unused imports in some cases
// @ts-ignore
import type { DiffSummary } from './diff-summary';
/**
* One checkpoint of a run.
*/
export interface TimelineEntryResponse {
/**
* 1-based, in the order the checkpoints were recorded; the `@ordinal` a fork target names.
*/
'ordinal': number;
/**
* The checkpoint record\'s position among the run\'s platform records.
*/
'checkpoint_seq': number;
/**
* The Petri execution the checkpoint belongs to.
*/
'execution': number;
/**
* The Petri firing the checkpoint belongs to.
*/
'firing': number;
/**
* The attempt of the firing whose files the commit holds, from 1.
*/
'attempt': number;
/**
* The stage id (`node@visit`) when the projection shows the stage.
*/
'stage'?: string | null;
'node_name': string;
'visit': number;
/**
* The Petri workspace id the commit was made in.
*/
'workspace'?: string | null;
'run_commit_sha'?: string | null;
'diff_summary'?: DiffSummary | null;
}