Files
stack/apps/web/src/lib/api/runner-jobs.ts
Jason Woltje 05b1a93ccb
Some checks failed
ci/woodpecker/push/web Pipeline failed
feat(web): add logs and telemetry page with filtering and auto-refresh (#480)
Co-authored-by: Jason Woltje <jason@diversecanvas.com>
Co-committed-by: Jason Woltje <jason@diversecanvas.com>
2026-02-23 04:38:15 +00:00

164 lines
3.8 KiB
TypeScript

/**
* Runner Jobs API Client
* Handles runner-job-related API requests
*/
import { apiGet, type ApiResponse } from "./client";
/**
* Runner job status enum (matches backend RunnerJobStatus)
*/
export enum RunnerJobStatus {
PENDING = "PENDING",
QUEUED = "QUEUED",
RUNNING = "RUNNING",
COMPLETED = "COMPLETED",
FAILED = "FAILED",
CANCELLED = "CANCELLED",
}
/**
* Runner job response interface (matches Prisma RunnerJob model)
*/
export interface RunnerJob {
id: string;
workspaceId: string;
agentTaskId: string | null;
type: string;
status: RunnerJobStatus;
priority: number;
progressPercent: number;
version: number;
result: Record<string, unknown> | null;
error: string | null;
createdAt: string;
startedAt: string | null;
completedAt: string | null;
}
/**
* Filters for querying runner jobs
*/
export interface RunnerJobFilters {
workspaceId?: string;
status?: RunnerJobStatus | RunnerJobStatus[];
type?: string;
agentTaskId?: string;
page?: number;
limit?: number;
}
/**
* Paginated runner jobs response
*/
export interface PaginatedRunnerJobs {
data: RunnerJob[];
meta?: {
total?: number;
page?: number;
limit?: number;
};
}
/**
* Fetch runner jobs with optional filters
*/
export async function fetchRunnerJobs(filters?: RunnerJobFilters): Promise<RunnerJob[]> {
const params = new URLSearchParams();
if (filters?.status) {
const statuses = Array.isArray(filters.status) ? filters.status : [filters.status];
for (const s of statuses) {
params.append("status", s);
}
}
if (filters?.type) {
params.append("type", filters.type);
}
if (filters?.agentTaskId) {
params.append("agentTaskId", filters.agentTaskId);
}
if (filters?.page !== undefined) {
params.append("page", String(filters.page));
}
if (filters?.limit !== undefined) {
params.append("limit", String(filters.limit));
}
const queryString = params.toString();
const endpoint = queryString ? `/api/runner-jobs?${queryString}` : "/api/runner-jobs";
const response = await apiGet<ApiResponse<RunnerJob[]>>(endpoint, filters?.workspaceId);
return response.data;
}
/**
* Fetch a single runner job by ID
*/
export async function fetchRunnerJob(id: string, workspaceId?: string): Promise<RunnerJob> {
return apiGet<RunnerJob>(`/api/runner-jobs/${id}`, workspaceId);
}
// ─── Job Steps ────────────────────────────────────────────────────────
/**
* Job step phase enum (matches backend JobStepPhase)
*/
export enum JobStepPhase {
SETUP = "SETUP",
EXECUTION = "EXECUTION",
VALIDATION = "VALIDATION",
CLEANUP = "CLEANUP",
}
/**
* Job step type enum (matches backend JobStepType)
*/
export enum JobStepType {
COMMAND = "COMMAND",
AI_ACTION = "AI_ACTION",
GATE = "GATE",
ARTIFACT = "ARTIFACT",
}
/**
* Job step status enum (matches backend JobStepStatus)
*/
export enum JobStepStatus {
PENDING = "PENDING",
RUNNING = "RUNNING",
COMPLETED = "COMPLETED",
FAILED = "FAILED",
SKIPPED = "SKIPPED",
}
/**
* Job step response interface (matches Prisma JobStep model)
*/
export interface JobStep {
id: string;
jobId: string;
ordinal: number;
phase: JobStepPhase;
name: string;
type: JobStepType;
status: JobStepStatus;
output: string | null;
tokensInput: number | null;
tokensOutput: number | null;
startedAt: string | null;
completedAt: string | null;
durationMs: number | null;
}
/**
* Fetch job steps for a specific runner job
*/
export async function fetchJobSteps(jobId: string, workspaceId?: string): Promise<JobStep[]> {
const response = await apiGet<ApiResponse<JobStep[]>>(
`/api/runner-jobs/${jobId}/steps`,
workspaceId
);
return response.data;
}