mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-15 23:32:46 +00:00
chore: remove unused verification, retros, and sessions endpoints
These endpoints had zero CLI callers and served only the web UI demo. Verification and retros were `not_implemented` stubs in real mode; sessions had an in-memory implementation but no CLI usage. Removing them shrinks the API surface and eliminates ~9,000 lines of dead code. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4352508f55
commit
5b63e25b28
78 changed files with 1961 additions and 11684 deletions
|
|
@ -1,97 +0,0 @@
|
|||
import { formatDurationSecs } from "../lib/format";
|
||||
|
||||
export type SmoothnessRating = "effortless" | "smooth" | "bumpy" | "struggled" | "failed";
|
||||
|
||||
type LearningCategory = "repo" | "code" | "workflow" | "tool";
|
||||
|
||||
export interface Learning {
|
||||
category: LearningCategory;
|
||||
text: string;
|
||||
}
|
||||
|
||||
type FrictionKind = "retry" | "timeout" | "wrong_approach" | "tool_failure" | "ambiguity";
|
||||
|
||||
export interface FrictionPoint {
|
||||
kind: FrictionKind;
|
||||
description: string;
|
||||
stage_id?: string;
|
||||
}
|
||||
|
||||
type OpenItemKind = "tech_debt" | "follow_up" | "investigation" | "test_gap";
|
||||
|
||||
export interface OpenItem {
|
||||
kind: OpenItemKind;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface StageRetro {
|
||||
stage_id: string;
|
||||
stage_label: string;
|
||||
status: string;
|
||||
duration_ms: number;
|
||||
retries: number;
|
||||
cost?: number;
|
||||
notes?: string;
|
||||
failure_reason?: string;
|
||||
files_touched: string[];
|
||||
}
|
||||
|
||||
export interface AggregateStats {
|
||||
total_duration_ms: number;
|
||||
total_cost?: number;
|
||||
total_retries: number;
|
||||
files_touched: string[];
|
||||
stages_completed: number;
|
||||
stages_failed: number;
|
||||
}
|
||||
|
||||
export interface Retro {
|
||||
run_id: string;
|
||||
workflow_name: string;
|
||||
goal: string;
|
||||
timestamp: string;
|
||||
smoothness?: SmoothnessRating;
|
||||
stages: StageRetro[];
|
||||
stats: AggregateStats;
|
||||
intent?: string;
|
||||
outcome?: string;
|
||||
learnings?: Learning[];
|
||||
friction_points?: FrictionPoint[];
|
||||
open_items?: OpenItem[];
|
||||
}
|
||||
|
||||
export const smoothnessConfig: Record<SmoothnessRating, { label: string; bg: string; text: string; dot: string }> = {
|
||||
effortless: { label: "Effortless", bg: "bg-emerald-500/15", text: "text-emerald-400", dot: "bg-emerald-400" },
|
||||
smooth: { label: "Smooth", bg: "bg-mint/15", text: "text-mint", dot: "bg-mint" },
|
||||
bumpy: { label: "Bumpy", bg: "bg-amber/15", text: "text-amber", dot: "bg-amber" },
|
||||
struggled: { label: "Struggled", bg: "bg-orange-500/15", text: "text-orange-400", dot: "bg-orange-400" },
|
||||
failed: { label: "Failed", bg: "bg-coral/15", text: "text-coral", dot: "bg-coral" },
|
||||
};
|
||||
|
||||
export const learningCategoryConfig: Record<LearningCategory, { label: string; text: string }> = {
|
||||
repo: { label: "Repo", text: "text-teal-400" },
|
||||
code: { label: "Code", text: "text-sky-400" },
|
||||
workflow: { label: "Workflow", text: "text-violet-400" },
|
||||
tool: { label: "Tool", text: "text-amber" },
|
||||
};
|
||||
|
||||
export const frictionKindConfig: Record<FrictionKind, { label: string; text: string }> = {
|
||||
retry: { label: "Retry", text: "text-amber" },
|
||||
timeout: { label: "Timeout", text: "text-coral" },
|
||||
wrong_approach: { label: "Wrong Approach", text: "text-orange-400" },
|
||||
tool_failure: { label: "Tool Failure", text: "text-coral" },
|
||||
ambiguity: { label: "Ambiguity", text: "text-violet-400" },
|
||||
};
|
||||
|
||||
export const openItemKindConfig: Record<OpenItemKind, { label: string; text: string }> = {
|
||||
tech_debt: { label: "Tech Debt", text: "text-orange-400" },
|
||||
follow_up: { label: "Follow-up", text: "text-teal-400" },
|
||||
investigation: { label: "Investigation", text: "text-sky-400" },
|
||||
test_gap: { label: "Test Gap", text: "text-coral" },
|
||||
};
|
||||
|
||||
function formatDurationMs(ms: number): string {
|
||||
return formatDurationSecs(Math.floor(ms / 1000));
|
||||
}
|
||||
|
||||
export { formatDurationMs };
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
export type VerificationResult = "pass" | "fail" | "skip" | "na";
|
||||
|
||||
export type VerificationType = "ai" | "automated" | "analysis" | "ai-analysis";
|
||||
|
||||
export interface Criterion {
|
||||
name: string;
|
||||
description: string;
|
||||
type: VerificationType | null;
|
||||
status: VerificationResult;
|
||||
}
|
||||
|
||||
export interface VerificationCategory {
|
||||
name: string;
|
||||
question: string;
|
||||
status: VerificationResult;
|
||||
criteria: Criterion[];
|
||||
}
|
||||
|
||||
export const statusConfig = {
|
||||
pass: {
|
||||
label: "Pass",
|
||||
color: "text-mint",
|
||||
bg: "bg-mint/15",
|
||||
dot: "bg-mint",
|
||||
border: "border-l-mint/50",
|
||||
},
|
||||
fail: {
|
||||
label: "Fail",
|
||||
color: "text-coral",
|
||||
bg: "bg-coral/15",
|
||||
dot: "bg-coral",
|
||||
border: "border-l-coral/50",
|
||||
},
|
||||
skip: {
|
||||
label: "Skip",
|
||||
color: "text-fg-muted",
|
||||
bg: "bg-overlay",
|
||||
dot: "bg-fg-muted",
|
||||
border: "border-l-fg-muted/50",
|
||||
},
|
||||
na: {
|
||||
label: "N/A",
|
||||
color: "text-fg-muted",
|
||||
bg: "bg-overlay",
|
||||
dot: "bg-fg-muted",
|
||||
border: "border-l-fg-muted/50",
|
||||
},
|
||||
} as const satisfies Record<
|
||||
VerificationResult,
|
||||
{ label: string; color: string; bg: string; dot: string; border: string }
|
||||
>;
|
||||
|
||||
export const typeConfig = {
|
||||
ai: { label: "AI", color: "text-teal-300", bg: "bg-teal-500/10" },
|
||||
automated: { label: "Automated", color: "text-mint", bg: "bg-mint/10" },
|
||||
analysis: { label: "Analysis", color: "text-amber", bg: "bg-amber/10" },
|
||||
"ai-analysis": { label: "AI + Analysis", color: "text-teal-300", bg: "bg-teal-500/10" },
|
||||
} as const satisfies Record<
|
||||
VerificationType,
|
||||
{ label: string; color: string; bg: string }
|
||||
>;
|
||||
|
||||
export type VerificationMode = "active" | "evaluate" | "disabled";
|
||||
|
||||
export interface CriterionPerformance {
|
||||
f1: number | null;
|
||||
passAt1: number | null;
|
||||
mode: VerificationMode;
|
||||
evaluations: VerificationResult[];
|
||||
}
|
||||
|
||||
export const modeConfig = {
|
||||
active: { label: "Active", color: "text-mint", bg: "bg-mint/10" },
|
||||
evaluate: { label: "Evaluate", color: "text-amber", bg: "bg-amber/10" },
|
||||
disabled: { label: "Disabled", color: "text-fg-muted", bg: "bg-overlay" },
|
||||
} as const satisfies Record<
|
||||
VerificationMode,
|
||||
{ label: string; color: string; bg: string }
|
||||
>;
|
||||
|
||||
export function getCriteriaSummary(criteria: readonly Criterion[]) {
|
||||
return {
|
||||
passing: criteria.filter((c) => c.status === "pass").length,
|
||||
failing: criteria.filter((c) => c.status === "fail").length,
|
||||
na: criteria.filter((c) => c.status === "na").length,
|
||||
total: criteria.length,
|
||||
};
|
||||
}
|
||||
|
||||
export function slugify(name: string): string {
|
||||
return name
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, "-")
|
||||
.replace(/(^-|-$)/g, "");
|
||||
}
|
||||
|
||||
|
|
@ -11,8 +11,6 @@ import {
|
|||
Bars3Icon,
|
||||
BeakerIcon,
|
||||
ChartBarIcon,
|
||||
CheckBadgeIcon,
|
||||
LightBulbIcon,
|
||||
MoonIcon,
|
||||
PlayIcon,
|
||||
RectangleStackIcon,
|
||||
|
|
@ -30,8 +28,6 @@ export async function loader() {
|
|||
const navigation = [
|
||||
{ name: "Workflows", href: "/workflows", icon: RectangleStackIcon },
|
||||
{ name: "Runs", href: "/runs", icon: PlayIcon },
|
||||
{ name: "Verification", href: "/verification/criteria", icon: CheckBadgeIcon },
|
||||
{ name: "Retros", href: "/retros", icon: LightBulbIcon },
|
||||
{ name: "Insights", href: "/insights", icon: ChartBarIcon },
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -22,46 +22,3 @@ export function timeUntil(iso: string): string {
|
|||
return relativeTime(Math.floor((new Date(iso).getTime() - Date.now()) / 1000), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a human-readable date label for grouping (e.g. "Today", "Yesterday", "Previous 7 days").
|
||||
*/
|
||||
function dateLabel(iso: string): string {
|
||||
const now = new Date();
|
||||
const date = new Date(iso);
|
||||
const startOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
||||
const startOfYesterday = new Date(startOfToday.getTime() - 86_400_000);
|
||||
const startOf7DaysAgo = new Date(startOfToday.getTime() - 7 * 86_400_000);
|
||||
|
||||
if (date >= startOfToday) return "Today";
|
||||
if (date >= startOfYesterday) return "Yesterday";
|
||||
if (date >= startOf7DaysAgo) return "Previous 7 days";
|
||||
return "Older";
|
||||
}
|
||||
|
||||
interface SessionItem {
|
||||
id: string;
|
||||
title: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
interface SessionGroup {
|
||||
label: string;
|
||||
sessions: SessionItem[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Group a flat list of sessions (already sorted newest-first) into date-labeled groups.
|
||||
*/
|
||||
export function groupSessionsByDate(sessions: SessionItem[]): SessionGroup[] {
|
||||
const groups: SessionGroup[] = [];
|
||||
let current: SessionGroup | undefined;
|
||||
for (const s of sessions) {
|
||||
const label = dateLabel(s.created_at);
|
||||
if (!current || current.label !== label) {
|
||||
current = { label, sessions: [] };
|
||||
groups.push(current);
|
||||
}
|
||||
current.sessions.push(s);
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import * as Setup from "./routes/setup";
|
|||
import * as SetupComplete from "./routes/setup-complete";
|
||||
import * as AuthLogin from "./routes/auth-login";
|
||||
import * as Start from "./routes/start";
|
||||
import * as SessionDetail from "./routes/session-detail";
|
||||
import * as Workflows from "./routes/workflows";
|
||||
import * as WorkflowDetail from "./routes/workflow-detail";
|
||||
import * as WorkflowDefinition from "./routes/workflow-definition";
|
||||
|
|
@ -26,12 +25,6 @@ import * as RunSettings from "./routes/run-settings";
|
|||
import * as RunGraph from "./routes/run-graph";
|
||||
import * as RunFiles from "./routes/run-files";
|
||||
import * as RunUsage from "./routes/run-usage";
|
||||
import * as RunRetro from "./routes/run-retro";
|
||||
import * as VerificationCriteria from "./routes/verification-criteria";
|
||||
import * as VerificationCriterion from "./routes/verification-criterion";
|
||||
import * as VerificationControls from "./routes/verification-controls";
|
||||
import * as VerificationControl from "./routes/verification-control";
|
||||
import * as Retros from "./routes/retros";
|
||||
import * as Insights from "./routes/insights";
|
||||
import * as InsightsEditor from "./routes/insights-editor";
|
||||
import * as InsightsNew from "./routes/insights-new";
|
||||
|
|
@ -97,7 +90,6 @@ export const routes: RouteObject[] = [
|
|||
}),
|
||||
children: [
|
||||
route("start", Start),
|
||||
route("sessions/:sessionId", SessionDetail),
|
||||
route("workflows", Workflows),
|
||||
route("workflows/:name", WorkflowDetail, {
|
||||
children: [
|
||||
|
|
@ -115,14 +107,8 @@ export const routes: RouteObject[] = [
|
|||
route("graph", RunGraph),
|
||||
route("files", RunFiles),
|
||||
route("usage", RunUsage),
|
||||
route("retro", RunRetro),
|
||||
],
|
||||
}),
|
||||
route("verification/criteria", VerificationCriteria),
|
||||
route("verification/criteria/:id", VerificationCriterion),
|
||||
route("verification/controls", VerificationControls),
|
||||
route("verification/controls/:id", VerificationControl),
|
||||
route("retros", Retros),
|
||||
route("insights", Insights, {
|
||||
children: [
|
||||
indexRoute(InsightsEditor),
|
||||
|
|
|
|||
|
|
@ -1,159 +0,0 @@
|
|||
import { useState } from "react";
|
||||
import { useNavigate } from "react-router";
|
||||
import { MagnifyingGlassIcon, ChevronDownIcon } from "@heroicons/react/24/outline";
|
||||
import { smoothnessConfig, formatDurationMs } from "../data/retros";
|
||||
import type { SmoothnessRating } from "../data/retros";
|
||||
import { apiJson } from "../api";
|
||||
import type { PaginatedRetroList } from "@qltysh/fabro-api-client";
|
||||
|
||||
interface RetroRow {
|
||||
run_id: string;
|
||||
workflow_name: string;
|
||||
goal: string;
|
||||
timestamp: string;
|
||||
smoothness?: SmoothnessRating;
|
||||
total_duration_ms: number;
|
||||
friction_point_count: number;
|
||||
}
|
||||
|
||||
export async function loader({ request }: any) {
|
||||
const { data: apiRetros } = await apiJson<PaginatedRetroList>("/retros", { request });
|
||||
const retros: RetroRow[] = apiRetros.map((r) => ({
|
||||
run_id: r.run.id,
|
||||
workflow_name: r.workflow.slug,
|
||||
goal: r.run.title,
|
||||
timestamp: r.timestamp,
|
||||
smoothness: r.smoothness as SmoothnessRating | undefined,
|
||||
total_duration_ms: r.stats.total_duration_ms,
|
||||
friction_point_count: r.friction_point_count,
|
||||
}));
|
||||
return { retros };
|
||||
}
|
||||
|
||||
export function meta({}: any) {
|
||||
return [{ title: "Retros \u2014 Fabro" }];
|
||||
}
|
||||
|
||||
const smoothnessOptions: Array<{ value: SmoothnessRating; label: string }> = [
|
||||
{ value: "effortless", label: "Effortless" },
|
||||
{ value: "smooth", label: "Smooth" },
|
||||
{ value: "bumpy", label: "Bumpy" },
|
||||
{ value: "struggled", label: "Struggled" },
|
||||
{ value: "failed", label: "Failed" },
|
||||
];
|
||||
|
||||
function SmoothnesssBadge({ smoothness }: { smoothness: SmoothnessRating | undefined }) {
|
||||
if (!smoothness) {
|
||||
return <span className="text-xs text-fg-muted">--</span>;
|
||||
}
|
||||
const config = smoothnessConfig[smoothness];
|
||||
return (
|
||||
<span className={`inline-flex items-center gap-1.5 rounded-full px-2.5 py-0.5 text-xs font-medium ${config.bg} ${config.text}`}>
|
||||
<span className={`size-1.5 rounded-full ${config.dot}`} />
|
||||
{config.label}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function formatTimestamp(ts: string): string {
|
||||
const date = new Date(ts);
|
||||
return date.toLocaleDateString("en-US", {
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
hour: "numeric",
|
||||
minute: "2-digit",
|
||||
});
|
||||
}
|
||||
|
||||
function truncate(text: string, maxLength: number): string {
|
||||
if (text.length <= maxLength) return text;
|
||||
return text.slice(0, maxLength) + "\u2026";
|
||||
}
|
||||
|
||||
export default function Retros({ loaderData }: any) {
|
||||
const { retros } = loaderData;
|
||||
const navigate = useNavigate();
|
||||
const [query, setQuery] = useState("");
|
||||
const [smoothnessFilter, setSmoothnessFilter] = useState<SmoothnessRating | "all">("all");
|
||||
|
||||
if (retros.length === 0) {
|
||||
return <p className="py-8 text-center text-sm text-fg-muted">No retrospectives yet.</p>;
|
||||
}
|
||||
|
||||
const lowerQuery = query.toLowerCase();
|
||||
const filtered = retros.filter(
|
||||
(r) =>
|
||||
(smoothnessFilter === "all" || r.smoothness === smoothnessFilter) &&
|
||||
(r.goal.toLowerCase().includes(lowerQuery) ||
|
||||
r.workflow_name.toLowerCase().includes(lowerQuery)),
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex gap-3">
|
||||
<div className="relative flex-1">
|
||||
<MagnifyingGlassIcon className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-fg-muted" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search retros…"
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
className="w-full rounded-md border border-line bg-panel/80 py-2 pl-9 pr-3 text-sm text-fg-2 placeholder-fg-muted outline-none transition-colors focus:border-focus focus:ring-0"
|
||||
/>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<select
|
||||
value={smoothnessFilter}
|
||||
onChange={(e) => setSmoothnessFilter(e.target.value as SmoothnessRating | "all")}
|
||||
className="appearance-none rounded-md border border-line bg-panel/80 py-2 pl-3 pr-8 text-sm text-fg-2 outline-none transition-colors focus:border-focus focus:ring-0"
|
||||
>
|
||||
<option value="all">All smoothness</option>
|
||||
{smoothnessOptions.map((opt) => (
|
||||
<option key={opt.value} value={opt.value}>{opt.label}</option>
|
||||
))}
|
||||
</select>
|
||||
<ChevronDownIcon className="pointer-events-none absolute right-2 top-1/2 size-4 -translate-y-1/2 text-fg-muted" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="rounded-md border border-line overflow-hidden">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-line bg-panel/60 text-left text-xs text-fg-muted">
|
||||
<th className="px-4 py-2.5 font-medium">Workflow</th>
|
||||
<th className="px-4 py-2.5 font-medium">Goal</th>
|
||||
<th className="px-4 py-2.5 font-medium">Smoothness</th>
|
||||
<th className="px-4 py-2.5 font-medium text-right">Duration</th>
|
||||
<th className="px-4 py-2.5 font-medium text-right">Frictions</th>
|
||||
<th className="px-4 py-2.5 font-medium text-right">When</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{filtered.map((retro) => (
|
||||
<tr key={retro.run_id} className="border-b border-line last:border-b-0 transition-colors hover:bg-overlay cursor-pointer" onClick={() => navigate(`/runs/${retro.run_id}/retro`)}>
|
||||
<td className="px-4 py-3 font-mono text-xs font-medium text-teal-500">
|
||||
{retro.workflow_name}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-fg-2">
|
||||
{truncate(retro.goal, 60)}
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<SmoothnesssBadge smoothness={retro.smoothness} />
|
||||
</td>
|
||||
<td className="px-4 py-3 text-right font-mono text-xs tabular-nums text-fg-3">
|
||||
{formatDurationMs(retro.total_duration_ms)}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-right font-mono text-xs tabular-nums text-fg-3">
|
||||
{retro.friction_point_count}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-right font-mono text-xs text-fg-muted">
|
||||
{formatTimestamp(retro.timestamp)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -11,7 +11,6 @@ const tabs = [
|
|||
{ name: "Overview", path: "", count: null },
|
||||
{ name: "Stages", path: "/stages/detect-drift", count: null },
|
||||
{ name: "Files Changed", path: "/files", count: null },
|
||||
{ name: "Retro", path: "/retro", count: null },
|
||||
{ name: "Usage", path: "/usage", count: null },
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,231 +0,0 @@
|
|||
import { Link } from "react-router";
|
||||
import {
|
||||
smoothnessConfig,
|
||||
learningCategoryConfig,
|
||||
frictionKindConfig,
|
||||
openItemKindConfig,
|
||||
formatDurationMs,
|
||||
} from "../data/retros";
|
||||
import type { Retro } from "../data/retros";
|
||||
import { apiJson } from "../api";
|
||||
|
||||
export async function loader({ request, params }: any) {
|
||||
const retro = await apiJson<Retro>(`/runs/${params.id}/retro`, { request });
|
||||
return { retro };
|
||||
}
|
||||
|
||||
export function meta({ data }: any) {
|
||||
const retro = data?.retro;
|
||||
return [{ title: retro ? `Retro: ${retro.goal} \u2014 Fabro` : "Retro \u2014 Fabro" }];
|
||||
}
|
||||
|
||||
function formatCost(cost: number | undefined): string {
|
||||
if (cost == null) return "--";
|
||||
return `$${cost.toFixed(2)}`;
|
||||
}
|
||||
|
||||
export default function RunRetro({ loaderData }: any) {
|
||||
const { retro } = loaderData;
|
||||
|
||||
if (!retro) {
|
||||
return <p className="py-8 text-center text-sm text-fg-muted">No retrospective found for this run.</p>;
|
||||
}
|
||||
|
||||
const smoothness = retro.smoothness ? smoothnessConfig[retro.smoothness] : null;
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Smoothness + Summary Header */}
|
||||
<div className="flex items-start gap-4">
|
||||
{smoothness && (
|
||||
<span className={`inline-flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-semibold ${smoothness.bg} ${smoothness.text}`}>
|
||||
<span className={`size-2.5 rounded-full ${smoothness.dot}`} />
|
||||
{smoothness.label}
|
||||
</span>
|
||||
)}
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="text-sm text-fg-3">{retro.goal}</p>
|
||||
<p className="mt-1 font-mono text-xs text-fg-muted">
|
||||
{retro.workflow_name} · {new Date(retro.timestamp).toLocaleString()}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Aggregate Stats */}
|
||||
<div className="grid grid-cols-2 gap-3 sm:grid-cols-4">
|
||||
<StatCard label="Duration" value={formatDurationMs(retro.stats.total_duration_ms)} />
|
||||
<StatCard label="Cost" value={formatCost(retro.stats.total_cost)} />
|
||||
<StatCard label="Retries" value={String(retro.stats.total_retries)} warn={retro.stats.total_retries > 0} />
|
||||
<StatCard label="Files" value={String(retro.stats.files_touched.length)} />
|
||||
</div>
|
||||
|
||||
{/* Intent + Outcome */}
|
||||
{(retro.intent ?? retro.outcome) && (
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
{retro.intent && (
|
||||
<div className="rounded-md border border-line bg-panel/60 p-4">
|
||||
<h3 className="text-xs font-medium uppercase tracking-wider text-fg-muted">Intent</h3>
|
||||
<p className="mt-2 text-sm leading-relaxed text-fg-3">{retro.intent}</p>
|
||||
</div>
|
||||
)}
|
||||
{retro.outcome && (
|
||||
<div className="rounded-md border border-line bg-panel/60 p-4">
|
||||
<h3 className="text-xs font-medium uppercase tracking-wider text-fg-muted">Outcome</h3>
|
||||
<p className="mt-2 text-sm leading-relaxed text-fg-3">{retro.outcome}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Learnings */}
|
||||
{retro.learnings && retro.learnings.length > 0 && (
|
||||
<div>
|
||||
<h3 className="mb-3 text-xs font-medium uppercase tracking-wider text-fg-muted">Learnings</h3>
|
||||
<div className="space-y-2">
|
||||
{retro.learnings.map((learning, i) => {
|
||||
const config = learningCategoryConfig[learning.category];
|
||||
return (
|
||||
<div key={learning.text} className="flex items-start gap-3 rounded-md border border-line bg-panel/60 px-4 py-3">
|
||||
<span className={`mt-0.5 shrink-0 rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wider ${config.text} bg-overlay`}>
|
||||
{config.label}
|
||||
</span>
|
||||
<p className="text-sm leading-relaxed text-fg-3">{learning.text}</p>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Friction Points */}
|
||||
{retro.friction_points && retro.friction_points.length > 0 && (
|
||||
<div>
|
||||
<h3 className="mb-3 text-xs font-medium uppercase tracking-wider text-fg-muted">Friction Points</h3>
|
||||
<div className="space-y-2">
|
||||
{retro.friction_points.map((fp, i) => {
|
||||
const config = frictionKindConfig[fp.kind];
|
||||
return (
|
||||
<div key={fp.description} className="flex items-start gap-3 rounded-md border border-line bg-panel/60 px-4 py-3">
|
||||
<span className={`mt-0.5 shrink-0 rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wider ${config.text} bg-overlay`}>
|
||||
{config.label}
|
||||
</span>
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="text-sm leading-relaxed text-fg-3">{fp.description}</p>
|
||||
{fp.stage_id && (
|
||||
<p className="mt-1 font-mono text-xs text-fg-muted">
|
||||
Stage: {retro.stages.find((s) => s.stage_id === fp.stage_id)?.stage_label ?? fp.stage_id}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Open Items */}
|
||||
{retro.open_items && retro.open_items.length > 0 && (
|
||||
<div>
|
||||
<h3 className="mb-3 text-xs font-medium uppercase tracking-wider text-fg-muted">Open Items</h3>
|
||||
<div className="space-y-2">
|
||||
{retro.open_items.map((item, i) => {
|
||||
const config = openItemKindConfig[item.kind];
|
||||
return (
|
||||
<div key={item.description} className="flex items-start gap-3 rounded-md border border-line bg-panel/60 px-4 py-3">
|
||||
<span className={`mt-0.5 shrink-0 rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wider ${config.text} bg-overlay`}>
|
||||
{config.label}
|
||||
</span>
|
||||
<p className="text-sm leading-relaxed text-fg-3">{item.description}</p>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Stage Breakdown */}
|
||||
<div>
|
||||
<h3 className="mb-3 text-xs font-medium uppercase tracking-wider text-fg-muted">Stage Breakdown</h3>
|
||||
<div className="rounded-md border border-line overflow-hidden">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-line bg-panel/60 text-left text-xs text-fg-muted">
|
||||
<th className="px-4 py-2.5 font-medium">Stage</th>
|
||||
<th className="px-4 py-2.5 font-medium">Status</th>
|
||||
<th className="px-4 py-2.5 font-medium text-right">Duration</th>
|
||||
<th className="px-4 py-2.5 font-medium text-right">Retries</th>
|
||||
<th className="px-4 py-2.5 font-medium text-right">Cost</th>
|
||||
<th className="px-4 py-2.5 font-medium text-right">Files</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{retro.stages.map((stage) => (
|
||||
<tr key={stage.stage_id} className="border-b border-line last:border-b-0">
|
||||
<td className="px-4 py-3">
|
||||
<Link
|
||||
to={`/runs/${retro.run_id}/stages/${stage.stage_id}`}
|
||||
className="text-fg-2 hover:text-fg"
|
||||
>
|
||||
{stage.stage_label}
|
||||
</Link>
|
||||
{stage.notes && (
|
||||
<p className="mt-1 text-xs text-fg-muted">{stage.notes}</p>
|
||||
)}
|
||||
{stage.failure_reason && (
|
||||
<p className="mt-1 text-xs text-coral/80">{stage.failure_reason}</p>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<StageStatusBadge status={stage.status} />
|
||||
</td>
|
||||
<td className="px-4 py-3 text-right font-mono text-xs tabular-nums text-fg-3">
|
||||
{formatDurationMs(stage.duration_ms)}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-right font-mono text-xs tabular-nums">
|
||||
<span className={stage.retries > 0 ? "text-amber" : "text-fg-3"}>
|
||||
{stage.retries}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3 text-right font-mono text-xs tabular-nums text-fg-3">
|
||||
{formatCost(stage.cost)}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-right font-mono text-xs tabular-nums text-fg-3">
|
||||
{stage.files_touched.length}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function StatCard({ label, value, warn }: { label: string; value: string; warn?: boolean }) {
|
||||
return (
|
||||
<div className="rounded-md border border-line bg-panel/60 px-4 py-3">
|
||||
<p className="text-xs font-medium uppercase tracking-wider text-fg-muted">{label}</p>
|
||||
<p className={`mt-1 font-mono text-lg font-semibold tabular-nums ${warn ? "text-amber" : "text-fg"}`}>
|
||||
{value}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function StageStatusBadge({ status }: { status: string }) {
|
||||
const styles: Record<string, string> = {
|
||||
completed: "text-mint",
|
||||
running: "text-teal-500",
|
||||
pending: "text-fg-muted",
|
||||
failed: "text-coral",
|
||||
cancelled: "text-fg-muted",
|
||||
};
|
||||
const colorClass = styles[status] ?? "text-fg-3";
|
||||
return (
|
||||
<span className={`text-xs font-medium capitalize ${colorClass}`}>
|
||||
{status}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,448 +0,0 @@
|
|||
import { useState } from "react";
|
||||
import { Link, useParams } from "react-router";
|
||||
import {
|
||||
ChatBubbleLeftIcon,
|
||||
ClipboardDocumentIcon,
|
||||
CheckIcon,
|
||||
PencilSquareIcon,
|
||||
UserIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { ToolRow, ToolBlock } from "../components/tool-use";
|
||||
import type { ToolUse } from "../components/tool-use";
|
||||
import { timeAgo, groupSessionsByDate } from "../lib/time";
|
||||
import { apiJson } from "../api";
|
||||
import type { SessionDetail as ApiSessionDetail, PaginatedSessionList } from "@qltysh/fabro-api-client";
|
||||
|
||||
export const handle = { hideHeader: true, wide: true };
|
||||
|
||||
export function meta({}: any) {
|
||||
return [{ title: "Session — Fabro" }];
|
||||
}
|
||||
|
||||
export async function loader({ request, params }: any) {
|
||||
const [apiSession, { data: apiSessions }] = await Promise.all([
|
||||
apiJson<ApiSessionDetail>(`/sessions/${params.sessionId}`, { request }),
|
||||
apiJson<PaginatedSessionList>("/sessions", { request }),
|
||||
]);
|
||||
const session: Session = {
|
||||
id: apiSession.id,
|
||||
title: apiSession.title,
|
||||
model: apiSession.model.id,
|
||||
created_at: apiSession.created_at,
|
||||
updated_at: apiSession.updated_at,
|
||||
turns: apiSession.turns.map((t): Turn => {
|
||||
switch (t.kind) {
|
||||
case "tool":
|
||||
return {
|
||||
kind: "tool",
|
||||
tools: t.tools.map((tu) => ({
|
||||
id: tu.id,
|
||||
toolName: tu.tool_name,
|
||||
input: tu.input,
|
||||
result: tu.result,
|
||||
isError: tu.is_error,
|
||||
durationMs: tu.duration_ms,
|
||||
})),
|
||||
};
|
||||
case "user":
|
||||
return { kind: "user", content: t.content, created_at: t.created_at };
|
||||
case "assistant":
|
||||
return { kind: "assistant", content: t.content };
|
||||
}
|
||||
}),
|
||||
};
|
||||
const sessionGroups = groupSessionsByDate(
|
||||
apiSessions.map((s) => ({ id: s.id, title: s.title, created_at: s.created_at }))
|
||||
);
|
||||
return { session, sessionGroups };
|
||||
}
|
||||
|
||||
type Turn =
|
||||
| { kind: "user"; content: string; created_at?: string }
|
||||
| { kind: "assistant"; content: string }
|
||||
| { kind: "tool"; tools: ToolUse[] };
|
||||
|
||||
interface Session {
|
||||
id: string;
|
||||
title: string;
|
||||
model: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
turns: Turn[];
|
||||
}
|
||||
|
||||
// Keep hardcoded sessions as fallback
|
||||
const sessions: Record<string, Session> = {
|
||||
s1: {
|
||||
id: "s1",
|
||||
title: "Add rate limiting to auth endpoints",
|
||||
model: "Opus 4.6",
|
||||
created_at: "2026-03-06T14:30:00Z",
|
||||
updated_at: "2026-03-06T15:45:00Z",
|
||||
turns: [
|
||||
{
|
||||
kind: "user",
|
||||
created_at: "2026-02-28T10:00:00Z",
|
||||
content: "Add rate limiting to the auth endpoints. We're getting hit with brute force attempts on /api/auth/login and /api/auth/register. Use a sliding window approach with Redis, 10 requests per minute per IP.",
|
||||
},
|
||||
{
|
||||
kind: "assistant",
|
||||
content: "I'll implement sliding window rate limiting using Redis. Let me first look at the existing auth routes and middleware setup.",
|
||||
},
|
||||
{
|
||||
kind: "tool",
|
||||
tools: [
|
||||
{
|
||||
id: "toolu_s1_01",
|
||||
toolName: "read_file",
|
||||
input: `{ "path": "src/routes/auth.ts" }`,
|
||||
result: `import { Router } from "express";\nimport { loginHandler } from "../handlers/auth";\nimport { registerHandler } from "../handlers/auth";\n\nconst router = Router();\nrouter.post("/login", loginHandler);\nrouter.post("/register", registerHandler);\n\nexport default router;`,
|
||||
isError: false,
|
||||
durationMs: 45,
|
||||
},
|
||||
{
|
||||
id: "toolu_s1_02",
|
||||
toolName: "read_file",
|
||||
input: `{ "path": "src/middleware/index.ts" }`,
|
||||
result: `export { authMiddleware } from "./auth";\nexport { corsMiddleware } from "./cors";\nexport { loggingMiddleware } from "./logging";`,
|
||||
isError: false,
|
||||
durationMs: 38,
|
||||
},
|
||||
{
|
||||
id: "toolu_s1_03",
|
||||
toolName: "search_files",
|
||||
input: `{ "pattern": "redis", "path": "src/" }`,
|
||||
result: `src/lib/redis.ts:1: import Redis from "ioredis";\nsrc/lib/redis.ts:3: export const redis = new Redis(process.env.REDIS_URL);`,
|
||||
isError: false,
|
||||
durationMs: 210,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
kind: "assistant",
|
||||
content: "Good — there's already a Redis client at `src/lib/redis.ts`. I'll create a rate limiting middleware using the sliding window algorithm and apply it to the auth routes.\n\nHere's the plan:\n1. Create `src/middleware/rate-limit.ts` with sliding window logic\n2. Apply it to `/login` and `/register` routes\n3. Return `429 Too Many Requests` when the limit is exceeded",
|
||||
},
|
||||
{
|
||||
kind: "tool",
|
||||
tools: [
|
||||
{
|
||||
id: "toolu_s1_04",
|
||||
toolName: "write_file",
|
||||
input: `{ "path": "src/middleware/rate-limit.ts" }`,
|
||||
result: `File written: src/middleware/rate-limit.ts (47 lines)`,
|
||||
isError: false,
|
||||
durationMs: 65,
|
||||
},
|
||||
{
|
||||
id: "toolu_s1_05",
|
||||
toolName: "edit_file",
|
||||
input: `{ "path": "src/routes/auth.ts", "operations": [{ "type": "insert", "line": 3 }] }`,
|
||||
result: `Applied 1 edit to src/routes/auth.ts`,
|
||||
isError: false,
|
||||
durationMs: 42,
|
||||
},
|
||||
{
|
||||
id: "toolu_s1_06",
|
||||
toolName: "edit_file",
|
||||
input: `{ "path": "src/middleware/index.ts", "operations": [{ "type": "append" }] }`,
|
||||
result: `Applied 1 edit to src/middleware/index.ts`,
|
||||
isError: false,
|
||||
durationMs: 55,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
kind: "assistant",
|
||||
content: "Done. I've created the rate limiter and wired it up:\n\n- **`src/middleware/rate-limit.ts`** — Sliding window rate limiter using Redis sorted sets. Each request adds a timestamped entry, expired entries are pruned, and the count is checked against the limit.\n- **`src/routes/auth.ts`** — Applied `rateLimit({ windowMs: 60_000, max: 10 })` to both `/login` and `/register`.\n- **`src/middleware/index.ts`** — Re-exported the new middleware.\n\nWhen the limit is exceeded, clients get a `429` response with a `Retry-After` header indicating seconds until the window resets.",
|
||||
},
|
||||
],
|
||||
},
|
||||
s2: {
|
||||
id: "s2",
|
||||
title: "Fix config parsing for nested values",
|
||||
model: "Sonnet 4.6",
|
||||
created_at: "2026-03-06T12:30:00Z",
|
||||
updated_at: "2026-03-06T13:15:00Z",
|
||||
turns: [
|
||||
{
|
||||
kind: "user",
|
||||
created_at: "2026-02-28T10:00:00Z",
|
||||
content: "The CLI crashes when parsing nested TOML config values like [database.connection]. Can you debug and fix this?",
|
||||
},
|
||||
{
|
||||
kind: "assistant",
|
||||
content: "Let me look at the config parser to understand how nested keys are handled.",
|
||||
},
|
||||
{
|
||||
kind: "tool",
|
||||
tools: [
|
||||
{
|
||||
id: "toolu_s2_01",
|
||||
toolName: "read_file",
|
||||
input: `{ "path": "src/config/parser.ts" }`,
|
||||
result: `export function parseConfig(raw: string): Record<string, string> {\n const result: Record<string, string> = {};\n for (const line of raw.split("\\n")) {\n const match = line.match(/^(\\w+)\\s*=\\s*(.+)$/);\n if (match) result[match[1]] = match[2].trim();\n }\n return result;\n}`,
|
||||
isError: false,
|
||||
durationMs: 52,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
kind: "assistant",
|
||||
content: "Found the issue. The parser uses a flat `Record<string, string>` and the regex `^(\\w+)` only matches simple keys — it can't handle TOML section headers like `[database.connection]` or dotted keys like `database.host`.\n\nI'll rewrite the parser to support nested sections and dotted keys, producing a properly nested object.",
|
||||
},
|
||||
{
|
||||
kind: "tool",
|
||||
tools: [
|
||||
{
|
||||
id: "toolu_s2_02",
|
||||
toolName: "write_file",
|
||||
input: `{ "path": "src/config/parser.ts" }`,
|
||||
result: `File written: src/config/parser.ts (62 lines)`,
|
||||
isError: false,
|
||||
durationMs: 78,
|
||||
},
|
||||
{
|
||||
id: "toolu_s2_03",
|
||||
toolName: "run_command",
|
||||
input: `{ "command": "npm test -- --grep 'config parser'" }`,
|
||||
result: `PASS src/config/__tests__/parser.test.ts\n config parser\n ✓ parses flat keys (2ms)\n ✓ parses section headers (1ms)\n ✓ parses dotted keys (1ms)\n ✓ handles nested sections (1ms)\n\nTest Suites: 1 passed\nTests: 4 passed`,
|
||||
isError: false,
|
||||
durationMs: 2150,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
kind: "assistant",
|
||||
content: "Fixed. The parser now:\n\n1. Tracks the current section header (e.g. `[database.connection]`)\n2. Constructs nested objects from dotted paths\n3. Handles both `key = value` within sections and bare `dotted.key = value` syntax\n\nAll 4 tests pass, including the new nested section case.",
|
||||
},
|
||||
],
|
||||
},
|
||||
s3: {
|
||||
id: "s3",
|
||||
title: "Migrate to React Router v7",
|
||||
model: "Opus 4.6",
|
||||
created_at: "2026-03-05T10:00:00Z",
|
||||
updated_at: "2026-03-05T11:30:00Z",
|
||||
turns: [
|
||||
{ kind: "user", created_at: "2026-02-26T10:00:00Z", content: "Help me migrate our app from React Router v6 to v7. We're using createBrowserRouter with data loaders." },
|
||||
{ kind: "assistant", content: "I'll audit your current router setup and identify what needs to change for v7. Let me scan the codebase." },
|
||||
{
|
||||
kind: "tool",
|
||||
tools: [
|
||||
{ id: "toolu_s3_01", toolName: "search_files", input: `{ "pattern": "createBrowserRouter" }`, result: `src/main.tsx:5: import { createBrowserRouter } from "react-router-dom";\nsrc/main.tsx:8: const router = createBrowserRouter([`, isError: false, durationMs: 180 },
|
||||
{ id: "toolu_s3_02", toolName: "read_file", input: `{ "path": "package.json" }`, result: `"react-router-dom": "^6.22.0"`, isError: false, durationMs: 35 },
|
||||
],
|
||||
},
|
||||
{ kind: "assistant", content: "You're on React Router 6.22. The migration to v7 involves:\n\n1. Replace `react-router-dom` with `react-router` (single package)\n2. Update `createBrowserRouter` import path\n3. Migrate data loaders to the new `any` type\n4. Update any `useLoaderData` calls to be type-safe\n\nLet me make these changes." },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
// Fallback for unknown session IDs
|
||||
function makeFallbackSession(id: string): Session {
|
||||
return {
|
||||
id,
|
||||
title: "Session",
|
||||
model: "Opus 4.6",
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
turns: [
|
||||
{ kind: "user", created_at: "2026-02-28T10:00:00Z", content: "Hello, let's get started." },
|
||||
{ kind: "assistant", content: "Sure! What would you like to work on?" },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
interface SessionGroupType {
|
||||
label: string;
|
||||
sessions: { id: string; title: string; created_at: string }[];
|
||||
}
|
||||
|
||||
const sessionGroups: SessionGroupType[] = [
|
||||
{
|
||||
label: "Today",
|
||||
sessions: [
|
||||
{ id: "s1", title: "Add rate limiting to auth endpoints", created_at: "2026-03-06T14:30:00Z" },
|
||||
{ id: "s2", title: "Fix config parsing for nested values", created_at: "2026-03-06T12:30:00Z" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Yesterday",
|
||||
sessions: [
|
||||
{ id: "s3", title: "Migrate to React Router v7", created_at: "2026-03-05T10:00:00Z" },
|
||||
{ id: "s4", title: "Add dark mode toggle", created_at: "2026-03-05T09:00:00Z" },
|
||||
{ id: "s5", title: "Update OpenAPI spec for v3", created_at: "2026-03-05T08:00:00Z" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Previous 7 days",
|
||||
sessions: [
|
||||
{ id: "s6", title: "Terraform module for Redis cluster", created_at: "2026-03-03T15:00:00Z" },
|
||||
{ id: "s7", title: "Add pipeline event types", created_at: "2026-03-01T11:00:00Z" },
|
||||
{ id: "s8", title: "Implement webhook retry logic", created_at: "2026-02-28T09:00:00Z" },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
function CopyButton({ text }: { text: string }) {
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
function handleCopy() {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleCopy}
|
||||
className="inline-flex items-center justify-center rounded-md border border-line bg-panel/80 p-1.5 text-fg-muted transition-colors hover:border-line-strong hover:text-fg-3"
|
||||
aria-label="Copy"
|
||||
>
|
||||
{copied
|
||||
? <CheckIcon className="size-3.5" />
|
||||
: <ClipboardDocumentIcon className="size-3.5" />}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
function UserBlock({ content, created_at }: { content: string; created_at?: string }) {
|
||||
return (
|
||||
<div className="group">
|
||||
<div className="flex gap-3">
|
||||
<div className="flex size-7 shrink-0 items-center justify-center rounded-full bg-panel border border-line-strong">
|
||||
<UserIcon className="size-3.5 text-fg-3" />
|
||||
</div>
|
||||
<div className="min-w-0 flex-1 pt-0.5">
|
||||
<pre className="whitespace-pre-wrap font-sans text-sm leading-relaxed text-fg-2">{content}</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div className="ml-10 mt-1 flex h-6 items-center gap-2 opacity-0 transition-opacity group-hover:opacity-100">
|
||||
<CopyButton text={content} />
|
||||
{created_at != null && <span className="text-[11px] text-fg-muted">{timeAgo(created_at)}</span>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function AssistantBlock({ content, showCopy }: { content: string; showCopy: boolean }) {
|
||||
return (
|
||||
<div className="group">
|
||||
<div className="flex gap-3">
|
||||
<div className="flex size-7 shrink-0 items-center justify-center rounded-full bg-teal-500/10 border border-teal-500/20">
|
||||
<ChatBubbleLeftIcon className="size-3.5 text-teal-500" />
|
||||
</div>
|
||||
<div className="min-w-0 flex-1 pt-0.5">
|
||||
<pre className="whitespace-pre-wrap font-sans text-sm leading-relaxed text-fg-3">{content}</pre>
|
||||
</div>
|
||||
</div>
|
||||
{showCopy && (
|
||||
<div className="ml-10 mt-1 h-6 opacity-0 transition-opacity group-hover:opacity-100">
|
||||
<CopyButton text={content} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SessionSidebar({ activeId, groups }: { activeId: string; groups: SessionGroupType[] }) {
|
||||
return (
|
||||
<aside className="w-64 shrink-0 border-r border-line flex flex-col h-[calc(100vh-4rem)]">
|
||||
<div className="p-3">
|
||||
<Link
|
||||
to="/start"
|
||||
className="flex w-full items-center gap-2 rounded-lg border border-line bg-panel/60 px-3 py-2 text-sm text-fg-2 transition-colors hover:bg-panel hover:border-line-strong"
|
||||
>
|
||||
<PencilSquareIcon className="size-4 text-fg-muted" />
|
||||
New session
|
||||
</Link>
|
||||
</div>
|
||||
<nav className="flex-1 overflow-y-auto px-3 pb-4">
|
||||
{groups.map((group) => (
|
||||
<div key={group.label} className="mt-4 first:mt-1">
|
||||
<p className="px-2 mb-1.5 text-[11px] font-medium uppercase tracking-wider text-fg-muted">
|
||||
{group.label}
|
||||
</p>
|
||||
<ul className="space-y-0.5">
|
||||
{group.sessions.map((s) => (
|
||||
<li key={s.id}>
|
||||
<Link
|
||||
to={`/sessions/${s.id}`}
|
||||
className={`flex w-full flex-col rounded-lg px-2.5 py-2 text-left transition-colors ${
|
||||
activeId === s.id
|
||||
? "bg-overlay text-fg-2"
|
||||
: "text-fg-3 hover:bg-overlay"
|
||||
}`}
|
||||
>
|
||||
<span className="truncate text-sm">{s.title}</span>
|
||||
<span className="flex items-center gap-1.5 mt-0.5">
|
||||
<span className="text-[11px] text-fg-muted">{timeAgo(s.created_at)}</span>
|
||||
</span>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</nav>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
|
||||
export default function SessionDetail({ loaderData }: any) {
|
||||
const { session, sessionGroups: loaderGroups } = loaderData;
|
||||
|
||||
return (
|
||||
<div className="flex -mx-4 sm:-mx-6 lg:-mx-8 -my-6">
|
||||
<SessionSidebar activeId={session.id} groups={loaderGroups} />
|
||||
|
||||
<div className="flex-1 flex flex-col min-h-[calc(100vh-4rem)]">
|
||||
<div className="border-b border-line px-6 py-3 flex items-center gap-3">
|
||||
<h1 className="text-sm font-medium text-fg-2">{session.title}</h1>
|
||||
<span className="text-xs text-fg-muted">{timeAgo(session.created_at)}</span>
|
||||
<span className="ml-auto font-mono text-xs text-fg-muted">{session.model}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 overflow-y-auto px-6 py-6">
|
||||
<div className="mx-auto max-w-3xl space-y-5">
|
||||
{session.turns.map((turn, i) => {
|
||||
switch (turn.kind) {
|
||||
case "user":
|
||||
return <UserBlock key={`turn-${i}`} content={turn.content} created_at={turn.created_at} />;
|
||||
case "assistant": {
|
||||
const next = session.turns[i + 1];
|
||||
const showCopy = next?.kind !== "tool";
|
||||
return <AssistantBlock key={`turn-${i}`} content={turn.content} showCopy={showCopy} />;
|
||||
}
|
||||
case "tool":
|
||||
return <div key={`turn-${i}`} className="pl-10"><ToolBlock tools={turn.tools} /></div>;
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-line px-6 py-4">
|
||||
<div className="mx-auto max-w-3xl">
|
||||
<div className="flex items-start gap-3 rounded-lg border border-line bg-panel/80 px-4 py-3 focus-within:border-focus">
|
||||
<textarea
|
||||
placeholder="Send a message..."
|
||||
rows={1}
|
||||
className="flex-1 resize-none bg-transparent text-sm text-fg-2 placeholder-fg-muted outline-none"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="flex size-8 shrink-0 items-center justify-center rounded-md bg-teal-500 text-white transition-colors hover:bg-teal-400"
|
||||
>
|
||||
<svg viewBox="0 0 20 20" fill="currentColor" className="size-4" aria-hidden="true">
|
||||
<path d="M3.105 2.288a.75.75 0 0 0-.826.95l1.414 4.926A1.5 1.5 0 0 0 5.135 9.25h6.115a.75.75 0 0 1 0 1.5H5.135a1.5 1.5 0 0 0-1.442 1.086l-1.414 4.926a.75.75 0 0 0 .826.95l14.095-5.637a.75.75 0 0 0 0-1.395L3.105 2.289Z" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -11,18 +11,12 @@ import {
|
|||
FolderIcon,
|
||||
} from "@heroicons/react/16/solid";
|
||||
import {
|
||||
BoltIcon,
|
||||
BugAntIcon,
|
||||
CodeBracketIcon,
|
||||
MagnifyingGlassIcon,
|
||||
PencilSquareIcon,
|
||||
ShieldCheckIcon,
|
||||
XMarkIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { Link } from "react-router";
|
||||
import { apiJson, getAuthMe } from "../api";
|
||||
import { timeAgo, groupSessionsByDate } from "../lib/time";
|
||||
import type { PaginatedSessionList } from "@qltysh/fabro-api-client";
|
||||
import { getAuthMe } from "../api";
|
||||
|
||||
export const handle = { hideHeader: true, wide: true };
|
||||
|
||||
|
|
@ -32,11 +26,7 @@ export function meta({}: any) {
|
|||
|
||||
export async function loader({ request }: any) {
|
||||
const { features } = await getAuthMe();
|
||||
const { data: apiSessions } = await apiJson<PaginatedSessionList>("/sessions", { request });
|
||||
const sessionGroups = groupSessionsByDate(
|
||||
apiSessions.map((s) => ({ id: s.id, title: s.title, created_at: s.created_at }))
|
||||
);
|
||||
return { sessionGroups, features };
|
||||
return { features };
|
||||
}
|
||||
|
||||
const projects = [
|
||||
|
|
@ -59,45 +49,8 @@ function BranchIcon({ className }: { className?: string }) {
|
|||
);
|
||||
}
|
||||
|
||||
function SessionSidebar({ groups }: { groups: { label: string; sessions: { id: string; title: string; created_at: string }[] }[] }) {
|
||||
return (
|
||||
<aside className="w-64 shrink-0 border-r border-line flex flex-col h-[calc(100vh-4rem)]">
|
||||
<div className="p-3">
|
||||
<div className="flex w-full items-center gap-2 rounded-lg border border-teal-500/20 bg-panel/60 px-3 py-2 text-sm text-fg-2">
|
||||
<PencilSquareIcon className="size-4 text-teal-500" />
|
||||
New session
|
||||
</div>
|
||||
</div>
|
||||
<nav className="flex-1 overflow-y-auto px-3 pb-4">
|
||||
{groups.map((group) => (
|
||||
<div key={group.label} className="mt-4 first:mt-1">
|
||||
<p className="px-2 mb-1.5 text-[11px] font-medium uppercase tracking-wider text-fg-muted">
|
||||
{group.label}
|
||||
</p>
|
||||
<ul className="space-y-0.5">
|
||||
{group.sessions.map((session) => (
|
||||
<li key={session.id}>
|
||||
<Link
|
||||
to={`/sessions/${session.id}`}
|
||||
className="flex w-full flex-col rounded-lg px-2.5 py-2 text-left transition-colors text-fg-3 hover:bg-overlay"
|
||||
>
|
||||
<span className="truncate text-sm">{session.title}</span>
|
||||
<span className="flex items-center gap-1.5 mt-0.5">
|
||||
<span className="text-[11px] text-fg-muted">{timeAgo(session.created_at)}</span>
|
||||
</span>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</nav>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Start({ loaderData }: any) {
|
||||
const { sessionGroups, features } = loaderData;
|
||||
const { features } = loaderData;
|
||||
const [prompt, setPrompt] = useState("");
|
||||
const [project, setProject] = useState(projects[0]);
|
||||
const [branch, setBranch] = useState(branches[0]);
|
||||
|
|
@ -129,8 +82,6 @@ export default function Start({ loaderData }: any) {
|
|||
|
||||
return (
|
||||
<div className="flex -mx-4 sm:-mx-6 lg:-mx-8 -my-6">
|
||||
<SessionSidebar groups={sessionGroups} />
|
||||
|
||||
<div className="flex-1 flex flex-col items-center pt-[12vh] px-4">
|
||||
<div className="w-full max-w-2xl">
|
||||
<h1 className="flex items-center justify-center gap-3 text-[2rem] font-medium tracking-tight text-fg-2 text-center mb-8">
|
||||
|
|
|
|||
|
|
@ -1,396 +0,0 @@
|
|||
import { Link, useParams } from "react-router";
|
||||
import {
|
||||
ChevronRightIcon,
|
||||
LightBulbIcon,
|
||||
ClipboardDocumentListIcon,
|
||||
BookOpenIcon,
|
||||
FunnelIcon,
|
||||
Bars3BottomLeftIcon,
|
||||
WrenchIcon,
|
||||
PaintBrushIcon,
|
||||
CheckBadgeIcon,
|
||||
BugAntIcon,
|
||||
BoltIcon,
|
||||
BeakerIcon,
|
||||
StarIcon,
|
||||
ComputerDesktopIcon,
|
||||
CubeTransparentIcon,
|
||||
ArrowsRightLeftIcon,
|
||||
DocumentDuplicateIcon,
|
||||
SparklesIcon,
|
||||
ArchiveBoxXMarkIcon,
|
||||
ShieldExclamationIcon,
|
||||
ServerStackIcon,
|
||||
ExclamationTriangleIcon,
|
||||
LockClosedIcon,
|
||||
PuzzlePieceIcon,
|
||||
ArrowUturnLeftIcon,
|
||||
EyeIcon,
|
||||
CurrencyDollarIcon,
|
||||
ClipboardDocumentCheckIcon,
|
||||
CpuChipIcon,
|
||||
FingerPrintIcon,
|
||||
HandRaisedIcon,
|
||||
ScaleIcon,
|
||||
MapPinIcon,
|
||||
DocumentTextIcon,
|
||||
ShieldCheckIcon,
|
||||
WrenchScrewdriverIcon,
|
||||
KeyIcon,
|
||||
RocketLaunchIcon,
|
||||
BuildingLibraryIcon,
|
||||
CheckCircleIcon,
|
||||
XCircleIcon,
|
||||
MinusCircleIcon,
|
||||
} from "@heroicons/react/20/solid";
|
||||
import {
|
||||
slugify,
|
||||
typeConfig,
|
||||
modeConfig,
|
||||
statusConfig,
|
||||
} from "../data/verifications";
|
||||
import type {
|
||||
VerificationType,
|
||||
VerificationMode,
|
||||
VerificationResult,
|
||||
} from "../data/verifications";
|
||||
import { apiJson } from "../api";
|
||||
import { timeAgo } from "../lib/time";
|
||||
import type { VerificationDetailResponse } from "@qltysh/fabro-api-client";
|
||||
|
||||
export const handle = { hideHeader: true };
|
||||
|
||||
export async function loader({ request, params }: any) {
|
||||
const data = await apiJson<VerificationDetailResponse>(`/verification/controls/${params.id}`, { request });
|
||||
return { data };
|
||||
}
|
||||
|
||||
export function meta({ data }: any) {
|
||||
const name = data?.data?.control?.name ?? "Verification";
|
||||
return [{ title: `${name} — Verification — Fabro` }];
|
||||
}
|
||||
|
||||
type IconComponent = React.ComponentType<{ className?: string }>;
|
||||
|
||||
const criterionIcons: Record<string, IconComponent> = {
|
||||
"Motivation": LightBulbIcon,
|
||||
"Specifications": ClipboardDocumentListIcon,
|
||||
"Documentation": BookOpenIcon,
|
||||
"Minimization": FunnelIcon,
|
||||
"Formatting": Bars3BottomLeftIcon,
|
||||
"Linting": WrenchIcon,
|
||||
"Style": PaintBrushIcon,
|
||||
"Completeness": CheckBadgeIcon,
|
||||
"Defects": BugAntIcon,
|
||||
"Performance": BoltIcon,
|
||||
"Test Coverage": BeakerIcon,
|
||||
"Test Quality": StarIcon,
|
||||
"E2E Coverage": ComputerDesktopIcon,
|
||||
"Architecture": CubeTransparentIcon,
|
||||
"Interfaces": ArrowsRightLeftIcon,
|
||||
"Duplication": DocumentDuplicateIcon,
|
||||
"Simplicity": SparklesIcon,
|
||||
"Dead Code": ArchiveBoxXMarkIcon,
|
||||
"Vulnerabilities": ShieldExclamationIcon,
|
||||
"IaC Scanning": ServerStackIcon,
|
||||
"Dependency Alerts": ExclamationTriangleIcon,
|
||||
"Security Controls": LockClosedIcon,
|
||||
"Compatibility": PuzzlePieceIcon,
|
||||
"Rollout / Rollback": ArrowUturnLeftIcon,
|
||||
"Observability": EyeIcon,
|
||||
"Cost": CurrencyDollarIcon,
|
||||
"Change Control": ClipboardDocumentCheckIcon,
|
||||
"AI Governance": CpuChipIcon,
|
||||
"Privacy": FingerPrintIcon,
|
||||
"Accessibility": HandRaisedIcon,
|
||||
"Licensing": ScaleIcon,
|
||||
};
|
||||
|
||||
const categoryIcons: Record<string, IconComponent> = {
|
||||
"Traceability": MapPinIcon,
|
||||
"Readability": DocumentTextIcon,
|
||||
"Reliability": ShieldCheckIcon,
|
||||
"Code Coverage": BeakerIcon,
|
||||
"Maintainability": WrenchScrewdriverIcon,
|
||||
"Security": KeyIcon,
|
||||
"Deployability": RocketLaunchIcon,
|
||||
"Compliance": BuildingLibraryIcon,
|
||||
};
|
||||
|
||||
function TypeBadge({ type }: { type: VerificationType | null }) {
|
||||
if (type === null) return null;
|
||||
const config = typeConfig[type];
|
||||
return (
|
||||
<span
|
||||
className={`rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wider ${config.color} ${config.bg}`}
|
||||
>
|
||||
{config.label}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function ModeBadge({ mode }: { mode: VerificationMode }) {
|
||||
const config = modeConfig[mode];
|
||||
return (
|
||||
<span
|
||||
className={`rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wider ${config.color} ${config.bg}`}
|
||||
>
|
||||
{config.label}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function StatCard({ label, value, warn }: { label: string; value: string; warn?: boolean }) {
|
||||
return (
|
||||
<div className="rounded-md border border-line bg-panel/60 px-4 py-3">
|
||||
<p className="text-xs font-medium uppercase tracking-wider text-fg-muted">{label}</p>
|
||||
<p className={`mt-1 font-mono text-lg font-semibold tabular-nums ${warn ? "text-amber" : "text-fg"}`}>
|
||||
{value}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function EvaluationBar({ evaluations }: { evaluations: readonly VerificationResult[] }) {
|
||||
if (evaluations.length === 0) {
|
||||
return <p className="text-sm italic text-fg-muted">No evaluations yet</p>;
|
||||
}
|
||||
|
||||
const passCount = evaluations.filter((e) => e === "pass").length;
|
||||
const failCount = evaluations.filter((e) => e === "fail").length;
|
||||
const skipCount = evaluations.filter((e) => e === "skip").length;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex items-center gap-1">
|
||||
{evaluations.map((result, i) => (
|
||||
<div
|
||||
key={`eval-${i}`}
|
||||
className={`h-6 flex-1 rounded-sm ${
|
||||
result === "pass"
|
||||
? "bg-mint/70"
|
||||
: result === "fail"
|
||||
? "bg-coral/70"
|
||||
: "bg-navy-600/50"
|
||||
}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-2 flex gap-4 text-xs text-fg-muted">
|
||||
<span className="flex items-center gap-1">
|
||||
<span className="inline-block size-2 rounded-sm bg-mint/70" />
|
||||
{passCount} pass
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<span className="inline-block size-2 rounded-sm bg-coral/70" />
|
||||
{failCount} fail
|
||||
</span>
|
||||
{skipCount > 0 && (
|
||||
<span className="flex items-center gap-1">
|
||||
<span className="inline-block size-2 rounded-sm bg-navy-600/50" />
|
||||
{skipCount} skip
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ResultIcon({ result }: { result: VerificationResult }) {
|
||||
const config = statusConfig[result];
|
||||
if (result === "pass") return <CheckCircleIcon className={`size-4 ${config.color}`} />;
|
||||
if (result === "fail") return <XCircleIcon className={`size-4 ${config.color}`} />;
|
||||
return <MinusCircleIcon className={`size-4 ${config.color}`} />;
|
||||
}
|
||||
|
||||
export default function VerificationDetail({ loaderData }: any) {
|
||||
const { data } = loaderData;
|
||||
const { control: controlInfo, performance: apiPerf, control_detail: apiDetail, recent_results: apiRecentResults, siblings: apiSiblings } = data;
|
||||
|
||||
const criterion = {
|
||||
name: controlInfo.name,
|
||||
description: controlInfo.description,
|
||||
type: (controlInfo.type ?? null) as VerificationType | null,
|
||||
};
|
||||
const categoryName = controlInfo.criterion.name;
|
||||
const performance = {
|
||||
f1: apiPerf.f1 ?? null,
|
||||
passAt1: apiPerf.pass_at_1 ?? null,
|
||||
mode: apiPerf.mode as VerificationMode,
|
||||
evaluations: apiPerf.evaluations as VerificationResult[],
|
||||
};
|
||||
const detail = apiDetail ? {
|
||||
description: apiDetail.rationale,
|
||||
checks: apiDetail.checks,
|
||||
passExample: apiDetail.pass_example,
|
||||
failExample: apiDetail.fail_example,
|
||||
} : null;
|
||||
const recentResults = apiRecentResults.map((r) => ({
|
||||
runId: r.run.id,
|
||||
runTitle: r.run.title,
|
||||
workflow: r.workflow.slug,
|
||||
result: r.result as VerificationResult,
|
||||
timestamp: r.timestamp,
|
||||
}));
|
||||
const siblings = apiSiblings.map((s) => ({
|
||||
name: s.name,
|
||||
slug: s.slug,
|
||||
type: (s.type ?? null) as VerificationType | null,
|
||||
mode: (s.mode ?? "disabled") as VerificationMode,
|
||||
}));
|
||||
|
||||
const Icon = criterionIcons[criterion.name];
|
||||
const CatIcon = categoryIcons[categoryName];
|
||||
|
||||
const passRate = performance.evaluations.length > 0
|
||||
? (performance.evaluations.filter((e) => e === "pass").length / performance.evaluations.length * 100).toFixed(0)
|
||||
: null;
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Breadcrumb */}
|
||||
<nav className="flex items-center gap-1 text-sm text-fg-muted">
|
||||
<Link to="/verification/criteria" className="text-fg-3 hover:text-fg">Verification</Link>
|
||||
<ChevronRightIcon className="size-3" />
|
||||
<span className="text-fg-3">{categoryName}</span>
|
||||
<ChevronRightIcon className="size-3" />
|
||||
<span>{criterion.name}</span>
|
||||
</nav>
|
||||
|
||||
{/* Header */}
|
||||
<div className="flex items-start gap-3">
|
||||
{Icon && <Icon className="mt-0.5 size-6 text-fg-3" />}
|
||||
<div className="min-w-0 flex-1">
|
||||
<h2 className="text-xl font-semibold text-fg">{criterion.name}</h2>
|
||||
<p className="mt-1 text-sm text-fg-muted">{criterion.description}</p>
|
||||
<div className="mt-2 flex items-center gap-2">
|
||||
<TypeBadge type={criterion.type} />
|
||||
<ModeBadge mode={performance.mode} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Description block */}
|
||||
{detail && (
|
||||
<div className="rounded-md border border-line bg-panel/60 px-5 py-4">
|
||||
<p className="text-sm leading-relaxed text-fg-2">{detail.description}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Stat cards */}
|
||||
<div className="grid grid-cols-4 gap-3">
|
||||
<StatCard label="Accuracy (F1)" value={performance.f1 != null ? performance.f1.toFixed(2) : "—"} />
|
||||
<StatCard label="pass@1" value={performance.passAt1 != null ? performance.passAt1.toFixed(2) : "—"} />
|
||||
<StatCard label="Pass Rate" value={passRate != null ? `${passRate}%` : "—"} />
|
||||
<StatCard label="Total Evals" value={String(performance.evaluations.length)} />
|
||||
</div>
|
||||
|
||||
{/* Evaluation history */}
|
||||
<div>
|
||||
<h3 className="mb-3 text-sm font-semibold text-fg">Evaluation History</h3>
|
||||
<EvaluationBar evaluations={performance.evaluations} />
|
||||
</div>
|
||||
|
||||
{/* Recent runs table */}
|
||||
<div>
|
||||
<h3 className="mb-3 text-sm font-semibold text-fg">Recent Runs</h3>
|
||||
<div className="rounded-md border border-line overflow-hidden">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-line bg-panel/60 text-left text-xs text-fg-muted">
|
||||
<th className="py-2.5 pl-4 pr-3 font-medium">Run</th>
|
||||
<th className="py-2.5 px-3 font-medium w-8">Result</th>
|
||||
<th className="py-2.5 px-3 font-medium">Workflow</th>
|
||||
<th className="py-2.5 pl-3 pr-4 font-medium text-right">Time</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{recentResults.map((run) => (
|
||||
<tr key={run.runId} className="border-b border-line last:border-b-0 cursor-pointer transition-colors hover:bg-overlay">
|
||||
<td className="py-2.5 pl-4 pr-3">
|
||||
<Link to={`/runs/${run.runId}`} className="font-medium text-fg-2 hover:text-fg">
|
||||
{run.runTitle}
|
||||
</Link>
|
||||
</td>
|
||||
<td className="py-2.5 px-3">
|
||||
<ResultIcon result={run.result} />
|
||||
</td>
|
||||
<td className="py-2.5 px-3 text-fg-muted">{run.workflow}</td>
|
||||
<td className="py-2.5 pl-3 pr-4 text-right text-fg-muted">{timeAgo(run.timestamp)}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* What this checks / Examples */}
|
||||
{detail && (
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="rounded-md border border-line bg-panel/60 px-5 py-4">
|
||||
<h3 className="mb-3 text-sm font-semibold text-fg">What This Checks</h3>
|
||||
<ul className="space-y-2 text-sm text-fg-2">
|
||||
{detail.checks.map((check) => (
|
||||
<li key={check} className="flex items-start gap-2">
|
||||
<span className="mt-1.5 size-1.5 shrink-0 rounded-full bg-fg-muted" />
|
||||
{check}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<div className="rounded-md border border-line bg-panel/60 px-5 py-4">
|
||||
<h3 className="mb-2 text-sm font-semibold text-mint">Pass Example</h3>
|
||||
<p className="text-sm text-fg-2">{detail.passExample}</p>
|
||||
</div>
|
||||
<div className="rounded-md border border-line bg-panel/60 px-5 py-4">
|
||||
<h3 className="mb-2 text-sm font-semibold text-coral">Fail Example</h3>
|
||||
<p className="text-sm text-fg-2">{detail.failExample}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Sibling controls */}
|
||||
{siblings.length > 0 && (
|
||||
<div>
|
||||
<h3 className="mb-3 text-sm font-semibold text-fg">
|
||||
{CatIcon && <CatIcon className="mr-1.5 inline size-4 text-fg-3" />}
|
||||
Other {categoryName} Controls
|
||||
</h3>
|
||||
<div className="rounded-md border border-line overflow-hidden">
|
||||
<table className="w-full text-sm">
|
||||
<tbody>
|
||||
{siblings.map((sibling) => {
|
||||
const SibIcon = criterionIcons[sibling.name];
|
||||
return (
|
||||
<tr key={sibling.name} className="border-b border-line last:border-b-0 cursor-pointer transition-colors hover:bg-overlay">
|
||||
<td className="w-8 py-2.5 pl-4 pr-0">
|
||||
{SibIcon && <SibIcon className="size-4 text-fg-3" />}
|
||||
</td>
|
||||
<td className="py-2.5 pl-2 pr-3">
|
||||
<Link
|
||||
to={`/verification/controls/${sibling.slug}`}
|
||||
className="font-medium text-fg-2 hover:text-fg"
|
||||
>
|
||||
{sibling.name}
|
||||
</Link>
|
||||
</td>
|
||||
<td className="py-2.5 px-3 text-fg-muted" />
|
||||
<td className="whitespace-nowrap py-2.5 px-3 text-right">
|
||||
<TypeBadge type={sibling.type} />
|
||||
</td>
|
||||
<td className="whitespace-nowrap py-2.5 pl-3 pr-4">
|
||||
<ModeBadge mode={sibling.mode} />
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,180 +0,0 @@
|
|||
import { useNavigate } from "react-router";
|
||||
import {
|
||||
LightBulbIcon,
|
||||
ClipboardDocumentListIcon,
|
||||
BookOpenIcon,
|
||||
FunnelIcon,
|
||||
Bars3BottomLeftIcon,
|
||||
WrenchIcon,
|
||||
PaintBrushIcon,
|
||||
CheckBadgeIcon,
|
||||
BugAntIcon,
|
||||
BoltIcon,
|
||||
BeakerIcon,
|
||||
StarIcon,
|
||||
ComputerDesktopIcon,
|
||||
CubeTransparentIcon,
|
||||
ArrowsRightLeftIcon,
|
||||
DocumentDuplicateIcon,
|
||||
SparklesIcon,
|
||||
ArchiveBoxXMarkIcon,
|
||||
ShieldExclamationIcon,
|
||||
ServerStackIcon,
|
||||
ExclamationTriangleIcon,
|
||||
LockClosedIcon,
|
||||
PuzzlePieceIcon,
|
||||
ArrowUturnLeftIcon,
|
||||
EyeIcon,
|
||||
CurrencyDollarIcon,
|
||||
ClipboardDocumentCheckIcon,
|
||||
CpuChipIcon,
|
||||
FingerPrintIcon,
|
||||
HandRaisedIcon,
|
||||
ScaleIcon,
|
||||
} from "@heroicons/react/20/solid";
|
||||
import {
|
||||
typeConfig,
|
||||
modeConfig,
|
||||
} from "../data/verifications";
|
||||
import type {
|
||||
VerificationType,
|
||||
VerificationMode,
|
||||
} from "../data/verifications";
|
||||
import { apiJson } from "../api";
|
||||
import type { VerificationControlListItem } from "@qltysh/fabro-api-client";
|
||||
|
||||
export async function loader({ request }: any) {
|
||||
const { data: controls } = await apiJson<{ data: VerificationControlListItem[] }>("/verification/controls", { request });
|
||||
return { controls };
|
||||
}
|
||||
|
||||
export const handle = { wide: true };
|
||||
|
||||
export function meta({}: any) {
|
||||
return [{ title: "Controls — Verification — Fabro" }];
|
||||
}
|
||||
|
||||
type IconComponent = React.ComponentType<{ className?: string }>;
|
||||
|
||||
const controlIcons: Record<string, IconComponent> = {
|
||||
"Motivation": LightBulbIcon,
|
||||
"Specifications": ClipboardDocumentListIcon,
|
||||
"Documentation": BookOpenIcon,
|
||||
"Minimization": FunnelIcon,
|
||||
"Formatting": Bars3BottomLeftIcon,
|
||||
"Linting": WrenchIcon,
|
||||
"Style": PaintBrushIcon,
|
||||
"Completeness": CheckBadgeIcon,
|
||||
"Defects": BugAntIcon,
|
||||
"Performance": BoltIcon,
|
||||
"Test Coverage": BeakerIcon,
|
||||
"Test Quality": StarIcon,
|
||||
"E2E Coverage": ComputerDesktopIcon,
|
||||
"Architecture": CubeTransparentIcon,
|
||||
"Interfaces": ArrowsRightLeftIcon,
|
||||
"Duplication": DocumentDuplicateIcon,
|
||||
"Simplicity": SparklesIcon,
|
||||
"Dead Code": ArchiveBoxXMarkIcon,
|
||||
"Vulnerabilities": ShieldExclamationIcon,
|
||||
"IaC Scanning": ServerStackIcon,
|
||||
"Dependency Alerts": ExclamationTriangleIcon,
|
||||
"Security Controls": LockClosedIcon,
|
||||
"Compatibility": PuzzlePieceIcon,
|
||||
"Rollout / Rollback": ArrowUturnLeftIcon,
|
||||
"Observability": EyeIcon,
|
||||
"Cost": CurrencyDollarIcon,
|
||||
"Change Control": ClipboardDocumentCheckIcon,
|
||||
"AI Governance": CpuChipIcon,
|
||||
"Privacy": FingerPrintIcon,
|
||||
"Accessibility": HandRaisedIcon,
|
||||
"Licensing": ScaleIcon,
|
||||
};
|
||||
|
||||
function TypeBadge({ type }: { type: VerificationType | null }) {
|
||||
if (type === null) return null;
|
||||
const config = typeConfig[type];
|
||||
return (
|
||||
<span
|
||||
className={`rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wider ${config.color} ${config.bg}`}
|
||||
>
|
||||
{config.label}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function ModeBadge({ mode }: { mode: VerificationMode }) {
|
||||
const config = modeConfig[mode];
|
||||
return (
|
||||
<span
|
||||
className={`rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wider ${config.color} ${config.bg}`}
|
||||
>
|
||||
{config.label}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
export default function VerificationControls({ loaderData }: any) {
|
||||
const { controls } = loaderData;
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="rounded-md border border-line overflow-hidden">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-line bg-panel/60 text-left text-xs text-fg-muted">
|
||||
<th className="w-8 py-2.5 pl-4 pr-0 font-medium" />
|
||||
<th className="py-2.5 pl-2 pr-3 font-medium">Control</th>
|
||||
<th className="py-2.5 px-3 font-medium">Description</th>
|
||||
<th className="py-2.5 px-3 font-medium">Criterion</th>
|
||||
<th className="py-2.5 px-3 font-medium text-right">Type</th>
|
||||
<th className="py-2.5 px-3 font-medium text-right">Accuracy (F1)</th>
|
||||
<th className="py-2.5 px-3 font-medium text-right">pass@1</th>
|
||||
<th className="py-2.5 pl-3 pr-4 font-medium">Mode</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{controls.map((control) => {
|
||||
const Icon = controlIcons[control.name];
|
||||
const mode = (control.mode ?? "disabled") as VerificationMode;
|
||||
return (
|
||||
<tr
|
||||
key={control.slug}
|
||||
className="border-b border-line last:border-b-0 cursor-pointer transition-colors hover:bg-overlay"
|
||||
onClick={() => navigate(`/verification/controls/${control.slug}`)}
|
||||
>
|
||||
<td className="w-8 py-2.5 pl-4 pr-0">
|
||||
{Icon && <Icon className="size-4 text-fg-3" />}
|
||||
</td>
|
||||
<td className="whitespace-nowrap py-2.5 pl-2 pr-3 font-medium text-fg-2">
|
||||
{control.name}
|
||||
</td>
|
||||
<td className="py-2.5 px-3 text-fg-muted">
|
||||
{control.description || (
|
||||
<span className="italic">Not configured</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="whitespace-nowrap py-2.5 px-3 text-xs text-fg-muted">
|
||||
{control.criterion.name}
|
||||
</td>
|
||||
<td className="whitespace-nowrap py-2.5 px-3 text-right">
|
||||
<TypeBadge type={(control.type ?? null) as VerificationType | null} />
|
||||
</td>
|
||||
<td className="whitespace-nowrap py-2.5 px-3 text-right font-mono text-xs tabular-nums text-fg-2">
|
||||
{control.f1 != null ? control.f1.toFixed(2) : <span className="text-fg-muted">—</span>}
|
||||
</td>
|
||||
<td className="whitespace-nowrap py-2.5 px-3 text-right font-mono text-xs tabular-nums text-fg-2">
|
||||
{control.pass_at_1 != null ? control.pass_at_1.toFixed(2) : <span className="text-fg-muted">—</span>}
|
||||
</td>
|
||||
<td className="whitespace-nowrap py-2.5 pl-3 pr-4">
|
||||
<ModeBadge mode={mode} />
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,449 +0,0 @@
|
|||
import { useState } from "react";
|
||||
import { useNavigate } from "react-router";
|
||||
import {
|
||||
Disclosure,
|
||||
DisclosureButton,
|
||||
DisclosurePanel,
|
||||
} from "@headlessui/react";
|
||||
import {
|
||||
ChevronRightIcon,
|
||||
LightBulbIcon,
|
||||
ClipboardDocumentListIcon,
|
||||
BookOpenIcon,
|
||||
FunnelIcon,
|
||||
Bars3BottomLeftIcon,
|
||||
WrenchIcon,
|
||||
PaintBrushIcon,
|
||||
CheckBadgeIcon,
|
||||
BugAntIcon,
|
||||
BoltIcon,
|
||||
BeakerIcon,
|
||||
StarIcon,
|
||||
ComputerDesktopIcon,
|
||||
CubeTransparentIcon,
|
||||
ArrowsRightLeftIcon,
|
||||
DocumentDuplicateIcon,
|
||||
SparklesIcon,
|
||||
ArchiveBoxXMarkIcon,
|
||||
ShieldExclamationIcon,
|
||||
ServerStackIcon,
|
||||
ExclamationTriangleIcon,
|
||||
LockClosedIcon,
|
||||
PuzzlePieceIcon,
|
||||
ArrowUturnLeftIcon,
|
||||
EyeIcon,
|
||||
CurrencyDollarIcon,
|
||||
ClipboardDocumentCheckIcon,
|
||||
CpuChipIcon,
|
||||
FingerPrintIcon,
|
||||
HandRaisedIcon,
|
||||
ScaleIcon,
|
||||
MapPinIcon,
|
||||
DocumentTextIcon,
|
||||
ShieldCheckIcon,
|
||||
WrenchScrewdriverIcon,
|
||||
KeyIcon,
|
||||
RocketLaunchIcon,
|
||||
BuildingLibraryIcon,
|
||||
} from "@heroicons/react/20/solid";
|
||||
import {
|
||||
MagnifyingGlassIcon,
|
||||
ChevronDownIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import {
|
||||
typeConfig,
|
||||
modeConfig,
|
||||
slugify,
|
||||
} from "../data/verifications";
|
||||
import type {
|
||||
VerificationType,
|
||||
VerificationMode,
|
||||
VerificationResult,
|
||||
VerificationCategory,
|
||||
CriterionPerformance,
|
||||
} from "../data/verifications";
|
||||
import { apiJson } from "../api";
|
||||
import type { VerificationCriterion as ApiVerificationCriterion } from "@qltysh/fabro-api-client";
|
||||
|
||||
export async function loader({ request }: any) {
|
||||
const { data: apiCategories } = await apiJson<{ data: ApiVerificationCriterion[] }>("/verification/criteria", { request });
|
||||
const categories: VerificationCategory[] = apiCategories.map((cat) => ({
|
||||
name: cat.name,
|
||||
question: cat.question,
|
||||
status: "pass" as const,
|
||||
criteria: cat.controls.map((c) => ({
|
||||
name: c.name,
|
||||
description: c.description,
|
||||
type: (c.type ?? null) as VerificationType | null,
|
||||
status: "pass" as const,
|
||||
})),
|
||||
}));
|
||||
// Build a performance map from API data
|
||||
const criterionPerformance: Record<string, CriterionPerformance> = {};
|
||||
for (const cat of apiCategories) {
|
||||
for (const ctrl of cat.controls) {
|
||||
criterionPerformance[ctrl.name] = {
|
||||
f1: ctrl.f1 ?? null,
|
||||
passAt1: ctrl.pass_at_1 ?? null,
|
||||
mode: (ctrl.mode ?? "disabled") as VerificationMode,
|
||||
evaluations: (ctrl.evaluations ?? []) as VerificationResult[],
|
||||
};
|
||||
}
|
||||
}
|
||||
return { categories, criterionPerformance };
|
||||
}
|
||||
|
||||
export const handle = { wide: true };
|
||||
|
||||
export function meta({}: any) {
|
||||
return [{ title: "Verifications — Fabro" }];
|
||||
}
|
||||
|
||||
type IconComponent = React.ComponentType<{ className?: string }>;
|
||||
|
||||
function TrafficLightIcon({ className }: { className?: string }) {
|
||||
return (
|
||||
<svg viewBox="0 0 20 20" fill="currentColor" className={className}>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M7 3a3 3 0 0 1 6 0v14a3 3 0 0 1-6 0V3Zm3 1a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Zm0 5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Zm0 5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
const criterionIcons: Record<string, IconComponent> = {
|
||||
"Motivation": LightBulbIcon,
|
||||
"Specifications": ClipboardDocumentListIcon,
|
||||
"Documentation": BookOpenIcon,
|
||||
"Minimization": FunnelIcon,
|
||||
"Formatting": Bars3BottomLeftIcon,
|
||||
"Linting": WrenchIcon,
|
||||
"Style": PaintBrushIcon,
|
||||
"Completeness": CheckBadgeIcon,
|
||||
"Defects": BugAntIcon,
|
||||
"Performance": BoltIcon,
|
||||
"Test Coverage": BeakerIcon,
|
||||
"Test Quality": StarIcon,
|
||||
"E2E Coverage": ComputerDesktopIcon,
|
||||
"Architecture": CubeTransparentIcon,
|
||||
"Interfaces": ArrowsRightLeftIcon,
|
||||
"Duplication": DocumentDuplicateIcon,
|
||||
"Simplicity": SparklesIcon,
|
||||
"Dead Code": ArchiveBoxXMarkIcon,
|
||||
"Vulnerabilities": ShieldExclamationIcon,
|
||||
"IaC Scanning": ServerStackIcon,
|
||||
"Dependency Alerts": ExclamationTriangleIcon,
|
||||
"Security Controls": LockClosedIcon,
|
||||
"Compatibility": PuzzlePieceIcon,
|
||||
"Rollout / Rollback": ArrowUturnLeftIcon,
|
||||
"Observability": EyeIcon,
|
||||
"Cost": CurrencyDollarIcon,
|
||||
"Change Control": ClipboardDocumentCheckIcon,
|
||||
"AI Governance": CpuChipIcon,
|
||||
"Privacy": FingerPrintIcon,
|
||||
"Accessibility": HandRaisedIcon,
|
||||
"Licensing": ScaleIcon,
|
||||
};
|
||||
|
||||
const categoryIcons: Record<string, IconComponent> = {
|
||||
"Traceability": MapPinIcon,
|
||||
"Readability": DocumentTextIcon,
|
||||
"Reliability": ShieldCheckIcon,
|
||||
"Code Coverage": TrafficLightIcon,
|
||||
"Maintainability": WrenchScrewdriverIcon,
|
||||
"Security": KeyIcon,
|
||||
"Deployability": RocketLaunchIcon,
|
||||
"Compliance": BuildingLibraryIcon,
|
||||
};
|
||||
|
||||
function TypeBadge({ type }: { type: VerificationType | null }) {
|
||||
if (type === null) return null;
|
||||
const config = typeConfig[type];
|
||||
return (
|
||||
<span
|
||||
className={`rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wider ${config.color} ${config.bg}`}
|
||||
>
|
||||
{config.label}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
type ViewMode = "grouped" | "ungrouped";
|
||||
|
||||
function CriterionRow({ slug, children }: { slug: string; children: React.ReactNode }) {
|
||||
const navigate = useNavigate();
|
||||
return (
|
||||
<tr
|
||||
className="border-b border-line last:border-b-0 cursor-pointer transition-colors hover:bg-overlay"
|
||||
onClick={() => navigate(`/verification/controls/${slug}`)}
|
||||
>
|
||||
{children}
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
function CategoryCard({ category, perfMap }: { category: VerificationCategory; perfMap: Record<string, CriterionPerformance> }) {
|
||||
return (
|
||||
<Disclosure
|
||||
as="div"
|
||||
className="rounded-md border border-line overflow-hidden"
|
||||
>
|
||||
<DisclosureButton className="group flex w-full items-center gap-3 px-4 py-3.5 text-left transition-colors hover:bg-overlay">
|
||||
{(() => {
|
||||
const CatIcon = categoryIcons[category.name];
|
||||
return CatIcon ? <CatIcon className="size-5 shrink-0 text-fg-3" /> : null;
|
||||
})()}
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex items-baseline gap-3">
|
||||
<span className="shrink-0 text-sm font-semibold text-fg">
|
||||
{category.name}
|
||||
</span>
|
||||
<span className="truncate text-xs text-fg-muted">
|
||||
{category.question}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<span className="shrink-0 text-xs text-fg-muted">
|
||||
<span className="font-mono tabular-nums">{category.criteria.length}</span> controls
|
||||
</span>
|
||||
<ChevronRightIcon className="size-4 shrink-0 text-fg-muted transition-transform duration-200 group-data-open:rotate-90" />
|
||||
</DisclosureButton>
|
||||
|
||||
<DisclosurePanel
|
||||
transition
|
||||
className="origin-top transition duration-200 ease-out data-closed:-translate-y-1 data-closed:opacity-0"
|
||||
>
|
||||
<div className="border-t border-line">
|
||||
<table className="w-full text-sm">
|
||||
<tbody>
|
||||
{category.criteria.map((criterion) => {
|
||||
const Icon = criterionIcons[criterion.name];
|
||||
const perf = perfMap[criterion.name];
|
||||
return (
|
||||
<CriterionRow key={criterion.name} slug={slugify(criterion.name)}>
|
||||
<td className="w-8 py-2.5 pl-5 pr-0">
|
||||
{Icon && <Icon className="size-4 text-fg-3" />}
|
||||
</td>
|
||||
<td className="whitespace-nowrap py-2.5 pl-2 pr-3 font-medium text-fg-2">
|
||||
{criterion.name}
|
||||
</td>
|
||||
<td className="py-2.5 px-3 text-fg-muted">
|
||||
{criterion.description || (
|
||||
<span className="italic">Not configured</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="whitespace-nowrap py-2.5 pl-3 pr-1 text-right">
|
||||
<TypeBadge type={criterion.type} />
|
||||
</td>
|
||||
<td className="whitespace-nowrap py-2.5 px-1">
|
||||
{perf && <ModeBadge mode={perf.mode} />}
|
||||
</td>
|
||||
<td className="whitespace-nowrap py-2.5 pl-1 pr-4">
|
||||
{perf && <EvaluationDots evaluations={perf.evaluations} />}
|
||||
</td>
|
||||
</CriterionRow>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</DisclosurePanel>
|
||||
</Disclosure>
|
||||
);
|
||||
}
|
||||
|
||||
function GroupedView({ categories, perfMap }: { categories: readonly VerificationCategory[]; perfMap: Record<string, CriterionPerformance> }) {
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
{categories.map((category) => (
|
||||
<CategoryCard key={category.name} category={category} perfMap={perfMap} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ModeBadge({ mode }: { mode: VerificationMode }) {
|
||||
const config = modeConfig[mode];
|
||||
return (
|
||||
<span
|
||||
className={`rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wider ${config.color} ${config.bg}`}
|
||||
>
|
||||
{config.label}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function EvaluationDots({ evaluations }: { evaluations: readonly VerificationResult[] }) {
|
||||
if (evaluations.length === 0) {
|
||||
return <span className="text-xs italic text-fg-muted">—</span>;
|
||||
}
|
||||
return (
|
||||
<div className="flex items-center gap-0.5">
|
||||
{evaluations.map((result, i) => (
|
||||
<span
|
||||
key={`eval-${i}`}
|
||||
className={`inline-block size-2.5 rounded-sm ${
|
||||
result === "pass"
|
||||
? "bg-mint/70"
|
||||
: result === "fail"
|
||||
? "bg-coral/70"
|
||||
: "bg-navy-600/50"
|
||||
}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function UngroupedView({ categories, perfMap }: { categories: readonly VerificationCategory[]; perfMap: Record<string, CriterionPerformance> }) {
|
||||
return (
|
||||
<div className="rounded-md border border-line overflow-hidden">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-line bg-panel/60 text-left text-xs text-fg-muted">
|
||||
<th className="w-8 py-2.5 pl-4 pr-0 font-medium" />
|
||||
<th className="py-2.5 pl-2 pr-3 font-medium">Verification</th>
|
||||
<th className="py-2.5 px-3 font-medium">Description</th>
|
||||
<th className="py-2.5 px-3 font-medium">Category</th>
|
||||
<th className="py-2.5 px-3 font-medium text-right">Type</th>
|
||||
<th className="py-2.5 px-3 font-medium text-right">Accuracy (F1)</th>
|
||||
<th className="py-2.5 px-3 font-medium text-right">pass@1</th>
|
||||
<th className="py-2.5 px-3 font-medium">Mode</th>
|
||||
<th className="py-2.5 pl-3 pr-4 font-medium">Evaluations</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{categories.flatMap((category) =>
|
||||
category.criteria.map((criterion) => {
|
||||
const Icon = criterionIcons[criterion.name];
|
||||
const perf = perfMap[criterion.name];
|
||||
return (
|
||||
<CriterionRow key={`${category.name}-${criterion.name}`} slug={slugify(criterion.name)}>
|
||||
<td className="w-8 py-2.5 pl-4 pr-0">
|
||||
{Icon && <Icon className="size-4 text-fg-3" />}
|
||||
</td>
|
||||
<td className="whitespace-nowrap py-2.5 pl-2 pr-3 font-medium text-fg-2">
|
||||
{criterion.name}
|
||||
</td>
|
||||
<td className="py-2.5 px-3 text-fg-muted">
|
||||
{criterion.description || (
|
||||
<span className="italic">Not configured</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="whitespace-nowrap py-2.5 px-3 text-xs text-fg-muted">
|
||||
{category.name}
|
||||
</td>
|
||||
<td className="whitespace-nowrap py-2.5 px-3 text-right">
|
||||
<TypeBadge type={criterion.type} />
|
||||
</td>
|
||||
<td className="whitespace-nowrap py-2.5 px-3 text-right font-mono text-xs tabular-nums text-fg-2">
|
||||
{perf?.f1 != null ? perf.f1.toFixed(2) : <span className="text-fg-muted">—</span>}
|
||||
</td>
|
||||
<td className="whitespace-nowrap py-2.5 px-3 text-right font-mono text-xs tabular-nums text-fg-2">
|
||||
{perf?.passAt1 != null ? perf.passAt1.toFixed(2) : <span className="text-fg-muted">—</span>}
|
||||
</td>
|
||||
<td className="whitespace-nowrap py-2.5 px-3">
|
||||
{perf && <ModeBadge mode={perf.mode} />}
|
||||
</td>
|
||||
<td className="whitespace-nowrap py-2.5 pl-3 pr-4">
|
||||
{perf && <EvaluationDots evaluations={perf.evaluations} />}
|
||||
</td>
|
||||
</CriterionRow>
|
||||
);
|
||||
}),
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function filterCategories(
|
||||
categories: readonly VerificationCategory[],
|
||||
query: string,
|
||||
modeFilter: VerificationMode | "all",
|
||||
perfMap: Record<string, CriterionPerformance>,
|
||||
): VerificationCategory[] {
|
||||
const lowerQuery = query.toLowerCase();
|
||||
return categories
|
||||
.map((category) => {
|
||||
const filtered = category.criteria.filter((c) => {
|
||||
const perf = perfMap[c.name];
|
||||
const matchesMode = modeFilter === "all" || perf?.mode === modeFilter;
|
||||
const matchesQuery =
|
||||
lowerQuery === "" ||
|
||||
c.name.toLowerCase().includes(lowerQuery) ||
|
||||
c.description.toLowerCase().includes(lowerQuery) ||
|
||||
category.name.toLowerCase().includes(lowerQuery);
|
||||
return matchesMode && matchesQuery;
|
||||
});
|
||||
return { ...category, criteria: filtered };
|
||||
})
|
||||
.filter((category) => category.criteria.length > 0);
|
||||
}
|
||||
|
||||
export default function Verifications({ loaderData }: any) {
|
||||
const { categories: verificationCategories, criterionPerformance } = loaderData;
|
||||
const [view, setView] = useState<ViewMode>("grouped");
|
||||
const [query, setQuery] = useState("");
|
||||
const [modeFilter, setModeFilter] = useState<VerificationMode | "all">("all");
|
||||
|
||||
const filtered = filterCategories(verificationCategories, query, modeFilter, criterionPerformance);
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{/* Toolbar */}
|
||||
<div className="flex gap-3">
|
||||
<div className="relative flex-1">
|
||||
<MagnifyingGlassIcon className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-fg-muted" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search verifications…"
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
className="w-full rounded-md border border-line bg-panel/80 py-2 pl-9 pr-3 text-sm text-fg-2 placeholder-fg-muted outline-none transition-colors focus:border-focus focus:ring-0"
|
||||
/>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<select
|
||||
value={modeFilter}
|
||||
onChange={(e) => setModeFilter(e.target.value as VerificationMode | "all")}
|
||||
className="appearance-none rounded-md border border-line bg-panel/80 py-2 pl-3 pr-8 text-sm text-fg-2 outline-none transition-colors focus:border-focus focus:ring-0"
|
||||
>
|
||||
<option value="all">All modes</option>
|
||||
<option value="active">Active</option>
|
||||
<option value="evaluate">Evaluate</option>
|
||||
<option value="disabled">Disabled</option>
|
||||
</select>
|
||||
<ChevronDownIcon className="pointer-events-none absolute right-2 top-1/2 size-4 -translate-y-1/2 text-fg-muted" />
|
||||
</div>
|
||||
<div className="flex items-center gap-1 rounded-md border border-line bg-panel/80 p-0.5">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setView("grouped")}
|
||||
className={`inline-flex items-center gap-1.5 rounded px-2.5 py-1 text-xs font-medium transition-colors ${view === "grouped" ? "bg-overlay text-teal-500" : "text-fg-muted hover:text-fg-3"}`}
|
||||
>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" className="size-3.5" aria-hidden="true">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M2.25 7.125C2.25 6.504 2.754 6 3.375 6h6c.621 0 1.125.504 1.125 1.125v3.75c0 .621-.504 1.125-1.125 1.125h-6a1.125 1.125 0 0 1-1.125-1.125v-3.75ZM14.25 8.625c0-.621.504-1.125 1.125-1.125h5.25c.621 0 1.125.504 1.125 1.125v8.25c0 .621-.504 1.125-1.125 1.125h-5.25a1.125 1.125 0 0 1-1.125-1.125v-8.25ZM3.75 16.125c0-.621.504-1.125 1.125-1.125h5.25c.621 0 1.125.504 1.125 1.125v1.5c0 .621-.504 1.125-1.125 1.125h-5.25a1.125 1.125 0 0 1-1.125-1.125v-1.5Z" />
|
||||
</svg>
|
||||
Grouped
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setView("ungrouped")}
|
||||
className={`inline-flex items-center gap-1.5 rounded px-2.5 py-1 text-xs font-medium transition-colors ${view === "ungrouped" ? "bg-overlay text-teal-500" : "text-fg-muted hover:text-fg-3"}`}
|
||||
>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" className="size-3.5" aria-hidden="true">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M3.75 12h16.5m-16.5 3.75h16.5M3.75 19.5h16.5M5.625 4.5h12.75a1.875 1.875 0 0 1 0 3.75H5.625a1.875 1.875 0 0 1 0-3.75Z" />
|
||||
</svg>
|
||||
List
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{view === "grouped" ? <GroupedView categories={filtered} perfMap={criterionPerformance} /> : <UngroupedView categories={filtered} perfMap={criterionPerformance} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,255 +0,0 @@
|
|||
import { Link, useNavigate } from "react-router";
|
||||
import {
|
||||
ChevronRightIcon,
|
||||
LightBulbIcon,
|
||||
ClipboardDocumentListIcon,
|
||||
BookOpenIcon,
|
||||
FunnelIcon,
|
||||
Bars3BottomLeftIcon,
|
||||
WrenchIcon,
|
||||
PaintBrushIcon,
|
||||
CheckBadgeIcon,
|
||||
BugAntIcon,
|
||||
BoltIcon,
|
||||
BeakerIcon,
|
||||
StarIcon,
|
||||
ComputerDesktopIcon,
|
||||
CubeTransparentIcon,
|
||||
ArrowsRightLeftIcon,
|
||||
DocumentDuplicateIcon,
|
||||
SparklesIcon,
|
||||
ArchiveBoxXMarkIcon,
|
||||
ShieldExclamationIcon,
|
||||
ServerStackIcon,
|
||||
ExclamationTriangleIcon,
|
||||
LockClosedIcon,
|
||||
PuzzlePieceIcon,
|
||||
ArrowUturnLeftIcon,
|
||||
EyeIcon,
|
||||
CurrencyDollarIcon,
|
||||
ClipboardDocumentCheckIcon,
|
||||
CpuChipIcon,
|
||||
FingerPrintIcon,
|
||||
HandRaisedIcon,
|
||||
ScaleIcon,
|
||||
MapPinIcon,
|
||||
DocumentTextIcon,
|
||||
ShieldCheckIcon,
|
||||
WrenchScrewdriverIcon,
|
||||
KeyIcon,
|
||||
RocketLaunchIcon,
|
||||
BuildingLibraryIcon,
|
||||
} from "@heroicons/react/20/solid";
|
||||
import {
|
||||
typeConfig,
|
||||
modeConfig,
|
||||
slugify,
|
||||
} from "../data/verifications";
|
||||
import type {
|
||||
VerificationType,
|
||||
VerificationMode,
|
||||
VerificationResult,
|
||||
} from "../data/verifications";
|
||||
import { apiJson } from "../api";
|
||||
import type { VerificationCriterionDetail } from "@qltysh/fabro-api-client";
|
||||
|
||||
export const handle = { hideHeader: true };
|
||||
|
||||
export async function loader({ request, params }: any) {
|
||||
const data = await apiJson<VerificationCriterionDetail>(`/verification/criteria/${params.id}`, { request });
|
||||
return { data };
|
||||
}
|
||||
|
||||
export function meta({ data }: any) {
|
||||
const name = data?.data?.name ?? "Criterion";
|
||||
return [{ title: `${name} — Verification — Fabro` }];
|
||||
}
|
||||
|
||||
type IconComponent = React.ComponentType<{ className?: string }>;
|
||||
|
||||
function TrafficLightIcon({ className }: { className?: string }) {
|
||||
return (
|
||||
<svg viewBox="0 0 20 20" fill="currentColor" className={className}>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M7 3a3 3 0 0 1 6 0v14a3 3 0 0 1-6 0V3Zm3 1a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Zm0 5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Zm0 5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
const controlIcons: Record<string, IconComponent> = {
|
||||
"Motivation": LightBulbIcon,
|
||||
"Specifications": ClipboardDocumentListIcon,
|
||||
"Documentation": BookOpenIcon,
|
||||
"Minimization": FunnelIcon,
|
||||
"Formatting": Bars3BottomLeftIcon,
|
||||
"Linting": WrenchIcon,
|
||||
"Style": PaintBrushIcon,
|
||||
"Completeness": CheckBadgeIcon,
|
||||
"Defects": BugAntIcon,
|
||||
"Performance": BoltIcon,
|
||||
"Test Coverage": BeakerIcon,
|
||||
"Test Quality": StarIcon,
|
||||
"E2E Coverage": ComputerDesktopIcon,
|
||||
"Architecture": CubeTransparentIcon,
|
||||
"Interfaces": ArrowsRightLeftIcon,
|
||||
"Duplication": DocumentDuplicateIcon,
|
||||
"Simplicity": SparklesIcon,
|
||||
"Dead Code": ArchiveBoxXMarkIcon,
|
||||
"Vulnerabilities": ShieldExclamationIcon,
|
||||
"IaC Scanning": ServerStackIcon,
|
||||
"Dependency Alerts": ExclamationTriangleIcon,
|
||||
"Security Controls": LockClosedIcon,
|
||||
"Compatibility": PuzzlePieceIcon,
|
||||
"Rollout / Rollback": ArrowUturnLeftIcon,
|
||||
"Observability": EyeIcon,
|
||||
"Cost": CurrencyDollarIcon,
|
||||
"Change Control": ClipboardDocumentCheckIcon,
|
||||
"AI Governance": CpuChipIcon,
|
||||
"Privacy": FingerPrintIcon,
|
||||
"Accessibility": HandRaisedIcon,
|
||||
"Licensing": ScaleIcon,
|
||||
};
|
||||
|
||||
const categoryIcons: Record<string, IconComponent> = {
|
||||
"Traceability": MapPinIcon,
|
||||
"Readability": DocumentTextIcon,
|
||||
"Reliability": ShieldCheckIcon,
|
||||
"Code Coverage": TrafficLightIcon,
|
||||
"Maintainability": WrenchScrewdriverIcon,
|
||||
"Security": KeyIcon,
|
||||
"Deployability": RocketLaunchIcon,
|
||||
"Compliance": BuildingLibraryIcon,
|
||||
};
|
||||
|
||||
function TypeBadge({ type }: { type: VerificationType | null }) {
|
||||
if (type === null) return null;
|
||||
const config = typeConfig[type];
|
||||
return (
|
||||
<span
|
||||
className={`rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wider ${config.color} ${config.bg}`}
|
||||
>
|
||||
{config.label}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function ModeBadge({ mode }: { mode: VerificationMode }) {
|
||||
const config = modeConfig[mode];
|
||||
return (
|
||||
<span
|
||||
className={`rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wider ${config.color} ${config.bg}`}
|
||||
>
|
||||
{config.label}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function EvaluationDots({ evaluations }: { evaluations: readonly VerificationResult[] }) {
|
||||
if (evaluations.length === 0) {
|
||||
return <span className="text-xs italic text-fg-muted">—</span>;
|
||||
}
|
||||
return (
|
||||
<div className="flex items-center gap-0.5">
|
||||
{evaluations.map((result, i) => (
|
||||
<span
|
||||
key={`eval-${i}`}
|
||||
className={`inline-block size-2.5 rounded-sm ${
|
||||
result === "pass"
|
||||
? "bg-mint/70"
|
||||
: result === "fail"
|
||||
? "bg-coral/70"
|
||||
: "bg-navy-600/50"
|
||||
}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function VerificationCriterion({ loaderData }: any) {
|
||||
const { data } = loaderData;
|
||||
const navigate = useNavigate();
|
||||
|
||||
const CatIcon = categoryIcons[data.name];
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Breadcrumb */}
|
||||
<nav className="flex items-center gap-1 text-sm text-fg-muted">
|
||||
<Link to="/verification/criteria" className="text-fg-3 hover:text-fg">Verification</Link>
|
||||
<ChevronRightIcon className="size-3" />
|
||||
<span>{data.name}</span>
|
||||
</nav>
|
||||
|
||||
{/* Header */}
|
||||
<div className="flex items-start gap-3">
|
||||
{CatIcon && <CatIcon className="mt-0.5 size-6 text-fg-3" />}
|
||||
<div className="min-w-0 flex-1">
|
||||
<h2 className="text-xl font-semibold text-fg">{data.name}</h2>
|
||||
<p className="mt-1 text-sm text-fg-muted">{data.question}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Controls table */}
|
||||
<div className="rounded-md border border-line overflow-hidden">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-line bg-panel/60 text-left text-xs text-fg-muted">
|
||||
<th className="w-8 py-2.5 pl-4 pr-0 font-medium" />
|
||||
<th className="py-2.5 pl-2 pr-3 font-medium">Control</th>
|
||||
<th className="py-2.5 px-3 font-medium">Description</th>
|
||||
<th className="py-2.5 px-3 font-medium text-right">Type</th>
|
||||
<th className="py-2.5 px-3 font-medium text-right">Accuracy (F1)</th>
|
||||
<th className="py-2.5 px-3 font-medium text-right">pass@1</th>
|
||||
<th className="py-2.5 px-3 font-medium">Mode</th>
|
||||
<th className="py-2.5 pl-3 pr-4 font-medium">Evaluations</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data.controls.map((control) => {
|
||||
const Icon = controlIcons[control.name];
|
||||
const mode = (control.mode ?? "disabled") as VerificationMode;
|
||||
return (
|
||||
<tr
|
||||
key={control.slug}
|
||||
className="border-b border-line last:border-b-0 cursor-pointer transition-colors hover:bg-overlay"
|
||||
onClick={() => navigate(`/verification/controls/${slugify(control.name)}`)}
|
||||
>
|
||||
<td className="w-8 py-2.5 pl-4 pr-0">
|
||||
{Icon && <Icon className="size-4 text-fg-3" />}
|
||||
</td>
|
||||
<td className="whitespace-nowrap py-2.5 pl-2 pr-3 font-medium text-fg-2">
|
||||
{control.name}
|
||||
</td>
|
||||
<td className="py-2.5 px-3 text-fg-muted">
|
||||
{control.description || (
|
||||
<span className="italic">Not configured</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="whitespace-nowrap py-2.5 px-3 text-right">
|
||||
<TypeBadge type={(control.type ?? null) as VerificationType | null} />
|
||||
</td>
|
||||
<td className="whitespace-nowrap py-2.5 px-3 text-right font-mono text-xs tabular-nums text-fg-2">
|
||||
{control.f1 != null ? control.f1.toFixed(2) : <span className="text-fg-muted">—</span>}
|
||||
</td>
|
||||
<td className="whitespace-nowrap py-2.5 px-3 text-right font-mono text-xs tabular-nums text-fg-2">
|
||||
{control.pass_at_1 != null ? control.pass_at_1.toFixed(2) : <span className="text-fg-muted">—</span>}
|
||||
</td>
|
||||
<td className="whitespace-nowrap py-2.5 px-3">
|
||||
<ModeBadge mode={mode} />
|
||||
</td>
|
||||
<td className="whitespace-nowrap py-2.5 pl-3 pr-4">
|
||||
<EvaluationDots evaluations={(control.evaluations ?? []) as VerificationResult[]} />
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
2
apps/fabro-web/dist/assets/app.css
vendored
2
apps/fabro-web/dist/assets/app.css
vendored
File diff suppressed because one or more lines are too long
1944
apps/fabro-web/dist/assets/entry-k7vnt1v8.js
vendored
1944
apps/fabro-web/dist/assets/entry-k7vnt1v8.js
vendored
File diff suppressed because one or more lines are too long
1944
apps/fabro-web/dist/assets/entry-xtvaf517.js
vendored
Normal file
1944
apps/fabro-web/dist/assets/entry-xtvaf517.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
apps/fabro-web/dist/index.html
vendored
2
apps/fabro-web/dist/index.html
vendored
|
|
@ -61,7 +61,7 @@
|
|||
<script type="module" src="/assets/chunk-sadshphz.js"></script>
|
||||
<script type="module" src="/assets/chunk-pmthkscp.js"></script>
|
||||
<script type="module" src="/assets/chunk-v61ks9f7.js"></script>
|
||||
<script type="module" src="/assets/entry-k7vnt1v8.js"></script>
|
||||
<script type="module" src="/assets/entry-xtvaf517.js"></script>
|
||||
<script type="module" src="/assets/chunk-n1k68xa8.js"></script>
|
||||
<script type="module" src="/assets/chunk-rsph5pvm.js"></script>
|
||||
<script type="module" src="/assets/chunk-9t57pdty.js"></script>
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -19,6 +19,5 @@ pub mod server_config {
|
|||
pub use fabro_config::server::*;
|
||||
pub use fabro_types::Settings;
|
||||
}
|
||||
pub mod sessions;
|
||||
pub mod tls;
|
||||
pub mod web_auth;
|
||||
|
|
|
|||
|
|
@ -57,8 +57,6 @@ use crate::error::ApiError;
|
|||
use crate::jwt_auth::{AuthMode, AuthenticatedService};
|
||||
use crate::run_manifest;
|
||||
use crate::secret_store::{SecretStore, SecretStoreError};
|
||||
use crate::sessions as sessions_mod;
|
||||
use crate::sessions::{SessionStore, new_session_store};
|
||||
use crate::static_files;
|
||||
use crate::web_auth;
|
||||
use fabro_interview::{Answer, Interviewer, QuestionType, WebInterviewer};
|
||||
|
|
@ -249,7 +247,7 @@ pub struct AppState {
|
|||
max_concurrent_runs: usize,
|
||||
scheduler_notify: Notify,
|
||||
global_event_tx: broadcast::Sender<EventEnvelope>,
|
||||
pub sessions: SessionStore,
|
||||
|
||||
pub(crate) secret_store: AsyncRwLock<SecretStore>,
|
||||
pub(crate) settings: Arc<RwLock<Settings>>,
|
||||
pub(crate) config_path: PathBuf,
|
||||
|
|
@ -404,7 +402,6 @@ fn demo_routes() -> Router<Arc<AppState>> {
|
|||
.route("/runs/{id}/pause", post(demo::pause_stub))
|
||||
.route("/runs/{id}/unpause", post(demo::unpause_stub))
|
||||
.route("/runs/{id}/graph", get(demo::get_run_graph))
|
||||
.route("/runs/{id}/retro", get(demo::get_run_retro))
|
||||
.route("/runs/{id}/stages", get(demo::get_run_stages))
|
||||
.route("/runs/{id}/artifacts", get(demo::list_run_artifacts_stub))
|
||||
.route(
|
||||
|
|
@ -435,35 +432,6 @@ fn demo_routes() -> Router<Arc<AppState>> {
|
|||
.route("/workflows", get(demo::list_workflows))
|
||||
.route("/workflows/{name}", get(demo::get_workflow))
|
||||
.route("/workflows/{name}/runs", get(demo::list_workflow_runs))
|
||||
.route(
|
||||
"/verification/criteria",
|
||||
get(demo::list_verification_criteria),
|
||||
)
|
||||
.route(
|
||||
"/verification/criteria/{id}",
|
||||
get(demo::get_verification_criterion),
|
||||
)
|
||||
.route(
|
||||
"/verification/controls",
|
||||
get(demo::list_verification_controls),
|
||||
)
|
||||
.route(
|
||||
"/verification/controls/{id}",
|
||||
get(demo::get_verification_control),
|
||||
)
|
||||
.route(
|
||||
"/verification/signoffs",
|
||||
get(demo::list_signoffs).post(demo::create_signoff_stub),
|
||||
)
|
||||
.route("/verification/signoffs/{id}", get(demo::get_signoff))
|
||||
.route("/retros", get(demo::list_retros))
|
||||
.route(
|
||||
"/sessions",
|
||||
get(demo::list_sessions).post(demo::create_session_stub),
|
||||
)
|
||||
.route("/sessions/{id}", get(demo::get_session))
|
||||
.route("/sessions/{id}/messages", post(demo::send_message_stub))
|
||||
.route("/sessions/{id}/events", get(demo::session_events_stub))
|
||||
.route(
|
||||
"/insights/queries",
|
||||
get(demo::list_saved_queries).post(demo::save_query_stub),
|
||||
|
|
@ -517,7 +485,6 @@ fn real_routes() -> Router<Arc<AppState>> {
|
|||
.route("/runs/{id}/pause", post(pause_run))
|
||||
.route("/runs/{id}/unpause", post(unpause_run))
|
||||
.route("/runs/{id}/graph", get(get_graph))
|
||||
.route("/runs/{id}/retro", get(get_retro))
|
||||
.route("/runs/{id}/stages", get(not_implemented))
|
||||
.route("/runs/{id}/artifacts", get(list_run_artifacts))
|
||||
.route("/runs/{id}/stages/{stageId}/turns", get(not_implemented))
|
||||
|
|
@ -542,26 +509,6 @@ fn real_routes() -> Router<Arc<AppState>> {
|
|||
.route("/workflows", get(not_implemented))
|
||||
.route("/workflows/{name}", get(not_implemented))
|
||||
.route("/workflows/{name}/runs", get(not_implemented))
|
||||
.route("/verification/criteria", get(not_implemented))
|
||||
.route("/verification/criteria/{id}", get(not_implemented))
|
||||
.route("/verification/controls", get(not_implemented))
|
||||
.route("/verification/controls/{id}", get(not_implemented))
|
||||
.route(
|
||||
"/verification/signoffs",
|
||||
get(not_implemented).post(not_implemented),
|
||||
)
|
||||
.route("/verification/signoffs/{id}", get(not_implemented))
|
||||
.route("/retros", get(not_implemented))
|
||||
.route(
|
||||
"/sessions",
|
||||
get(sessions_mod::list_sessions).post(sessions_mod::create_session),
|
||||
)
|
||||
.route("/sessions/{id}", get(sessions_mod::retrieve_session))
|
||||
.route("/sessions/{id}/messages", post(sessions_mod::send_message))
|
||||
.route(
|
||||
"/sessions/{id}/events",
|
||||
get(sessions_mod::stream_session_events),
|
||||
)
|
||||
.route(
|
||||
"/insights/queries",
|
||||
get(not_implemented).post(not_implemented),
|
||||
|
|
@ -1432,7 +1379,6 @@ pub(crate) fn build_app_state_with_path(
|
|||
max_concurrent_runs,
|
||||
scheduler_notify: Notify::new(),
|
||||
global_event_tx,
|
||||
sessions: new_session_store(),
|
||||
secret_store: AsyncRwLock::new(secret_store),
|
||||
settings,
|
||||
config_path,
|
||||
|
|
@ -3554,40 +3500,6 @@ async fn create_completion(
|
|||
}
|
||||
}
|
||||
|
||||
async fn get_retro(
|
||||
_auth: AuthenticatedService,
|
||||
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 runs = state.runs.lock().expect("runs lock poisoned");
|
||||
if !runs.contains_key(&id) {
|
||||
return ApiError::not_found("Run not found.").into_response();
|
||||
}
|
||||
}
|
||||
|
||||
match state.store.open_run_reader(&id).await {
|
||||
Ok(run_store) => match run_store.state().await {
|
||||
Ok(run_state) => match run_state.retro {
|
||||
Some(retro) => (StatusCode::OK, Json(retro)).into_response(),
|
||||
None => (StatusCode::OK, Json(serde_json::json!(null))).into_response(),
|
||||
},
|
||||
Err(err) => {
|
||||
tracing::warn!(run_id = %id, error = %err, "Failed to load retro state from store");
|
||||
(StatusCode::OK, Json(serde_json::json!(null))).into_response()
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
tracing::warn!(run_id = %id, error = %err, "Failed to open run store reader");
|
||||
ApiError::not_found("Run not found.").into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Render DOT source to a styled image via `render_dot` on a blocking thread.
|
||||
pub(crate) async fn render_graph_bytes(dot_source: &str, format: GraphFormat) -> Response {
|
||||
use fabro_graphviz::render::render_dot;
|
||||
|
|
|
|||
|
|
@ -1,635 +0,0 @@
|
|||
use std::collections::HashMap;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use axum::Json;
|
||||
#[cfg(test)]
|
||||
use axum::body::to_bytes;
|
||||
use axum::extract::{Path, Query, State};
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::sse::{Event, Sse};
|
||||
use axum::response::{IntoResponse, Response};
|
||||
use fabro_llm::generate::{GenerateParams, stream as llm_stream};
|
||||
use fabro_llm::types::{Message as LlmMessage, StreamEvent};
|
||||
use tokio::sync::broadcast;
|
||||
use tokio_stream::wrappers::BroadcastStream;
|
||||
|
||||
use fabro_api::types::{
|
||||
AssistantTurn, AssistantTurnKind, CreateSessionRequest, CreateSessionResponse, ModelReference,
|
||||
PaginatedSessionList, PaginationMeta, SendMessageRequest, SendMessageResponse, SessionDetail,
|
||||
SessionListItem, SessionTurn, UserTurn, UserTurnKind,
|
||||
};
|
||||
|
||||
use crate::error::ApiError;
|
||||
use crate::jwt_auth::AuthenticatedService;
|
||||
use crate::server::{AppState, PaginationParams};
|
||||
|
||||
pub type SessionStore = Arc<RwLock<HashMap<uuid::Uuid, SessionState>>>;
|
||||
|
||||
pub fn new_session_store() -> SessionStore {
|
||||
Arc::new(RwLock::new(HashMap::new()))
|
||||
}
|
||||
|
||||
pub struct SessionState {
|
||||
pub id: uuid::Uuid,
|
||||
pub title: String,
|
||||
pub model_id: String,
|
||||
pub model_provider: Option<String>,
|
||||
pub system_prompt: Option<String>,
|
||||
pub turns: Vec<SessionTurn>,
|
||||
pub created_at: chrono::DateTime<chrono::Utc>,
|
||||
pub updated_at: chrono::DateTime<chrono::Utc>,
|
||||
pub event_tx: broadcast::Sender<SessionEvent>,
|
||||
pub generation_seq: Arc<AtomicU64>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum SessionEvent {
|
||||
TextDelta {
|
||||
delta: String,
|
||||
},
|
||||
AssistantTurnComplete {
|
||||
content: String,
|
||||
created_at: chrono::DateTime<chrono::Utc>,
|
||||
},
|
||||
Done,
|
||||
Error {
|
||||
message: String,
|
||||
},
|
||||
}
|
||||
|
||||
fn generate_title(content: &str) -> String {
|
||||
let trimmed = content.trim();
|
||||
if trimmed.len() <= 60 {
|
||||
return trimmed.to_string();
|
||||
}
|
||||
// Find a word boundary near 60 chars
|
||||
match trimmed[..60].rfind(' ') {
|
||||
Some(pos) => format!("{}…", &trimmed[..pos]),
|
||||
None => format!("{}…", &trimmed[..60]),
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_model(model_arg: Option<String>) -> (String, Option<String>) {
|
||||
let raw = model_arg.unwrap_or_else(|| {
|
||||
fabro_model::Catalog::builtin()
|
||||
.list(None)
|
||||
.first()
|
||||
.map_or_else(|| "claude-sonnet-4-5".to_string(), |m| m.id.clone())
|
||||
});
|
||||
match fabro_model::Catalog::builtin().get(&raw) {
|
||||
Some(info) => (info.id.clone(), Some(info.provider.to_string())),
|
||||
None => (raw, None),
|
||||
}
|
||||
}
|
||||
|
||||
fn turns_to_messages(turns: &[SessionTurn]) -> Vec<LlmMessage> {
|
||||
turns
|
||||
.iter()
|
||||
.filter_map(|turn| match turn {
|
||||
SessionTurn::UserTurn(t) => Some(LlmMessage::user(&t.content)),
|
||||
SessionTurn::AssistantTurn(t) => Some(LlmMessage::assistant(&t.content)),
|
||||
SessionTurn::ToolTurn(_) => None,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn spawn_generation(store: SessionStore, session_id: uuid::Uuid, dry_run: bool, seq_at_start: u64) {
|
||||
tokio::spawn(async move {
|
||||
use futures_util::StreamExt;
|
||||
let (event_tx, model_id, model_provider, system_prompt, messages, generation_seq) = {
|
||||
let store = store.read().expect("session store lock poisoned");
|
||||
let Some(session) = store.get(&session_id) else {
|
||||
return;
|
||||
};
|
||||
(
|
||||
session.event_tx.clone(),
|
||||
session.model_id.clone(),
|
||||
session.model_provider.clone(),
|
||||
session.system_prompt.clone(),
|
||||
turns_to_messages(&session.turns),
|
||||
Arc::clone(&session.generation_seq),
|
||||
)
|
||||
};
|
||||
|
||||
if dry_run {
|
||||
let content = "This is a dry-run response.".to_string();
|
||||
let now = chrono::Utc::now();
|
||||
let _ = event_tx.send(SessionEvent::TextDelta {
|
||||
delta: content.clone(),
|
||||
});
|
||||
let _ = event_tx.send(SessionEvent::AssistantTurnComplete {
|
||||
content: content.clone(),
|
||||
created_at: now,
|
||||
});
|
||||
|
||||
// Append assistant turn to session
|
||||
{
|
||||
let mut store = store.write().expect("session store lock poisoned");
|
||||
if let Some(session) = store.get_mut(&session_id) {
|
||||
session
|
||||
.turns
|
||||
.push(SessionTurn::AssistantTurn(AssistantTurn {
|
||||
kind: AssistantTurnKind::Assistant,
|
||||
content,
|
||||
created_at: now,
|
||||
}));
|
||||
session.updated_at = now;
|
||||
}
|
||||
}
|
||||
let _ = event_tx.send(SessionEvent::Done);
|
||||
return;
|
||||
}
|
||||
|
||||
let mut params = GenerateParams::new(&model_id)
|
||||
.messages(messages)
|
||||
.max_tokens(4096);
|
||||
if let Some(ref provider) = model_provider {
|
||||
params = params.provider(provider);
|
||||
}
|
||||
if let Some(ref system) = system_prompt {
|
||||
params = params.system(system);
|
||||
}
|
||||
|
||||
let stream_result = match llm_stream(params).await {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
let _ = event_tx.send(SessionEvent::Error {
|
||||
message: format!("LLM error: {e}"),
|
||||
});
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let mut stream_result = stream_result;
|
||||
let mut full_text = String::new();
|
||||
while let Some(event) = stream_result.next().await {
|
||||
// Check if generation was superseded by a new message
|
||||
if generation_seq.load(Ordering::Relaxed) != seq_at_start {
|
||||
return;
|
||||
}
|
||||
match event {
|
||||
Ok(StreamEvent::TextDelta { delta, .. }) => {
|
||||
full_text.push_str(&delta);
|
||||
let _ = event_tx.send(SessionEvent::TextDelta { delta });
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = event_tx.send(SessionEvent::Error {
|
||||
message: format!("Stream error: {e}"),
|
||||
});
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let now = chrono::Utc::now();
|
||||
let _ = event_tx.send(SessionEvent::AssistantTurnComplete {
|
||||
content: full_text.clone(),
|
||||
created_at: now,
|
||||
});
|
||||
|
||||
// Append assistant turn to session
|
||||
{
|
||||
let mut store = store.write().expect("session store lock poisoned");
|
||||
if let Some(session) = store.get_mut(&session_id) {
|
||||
session
|
||||
.turns
|
||||
.push(SessionTurn::AssistantTurn(AssistantTurn {
|
||||
kind: AssistantTurnKind::Assistant,
|
||||
content: full_text,
|
||||
created_at: now,
|
||||
}));
|
||||
session.updated_at = now;
|
||||
}
|
||||
}
|
||||
let _ = event_tx.send(SessionEvent::Done);
|
||||
});
|
||||
}
|
||||
|
||||
pub async fn create_session(
|
||||
_auth: AuthenticatedService,
|
||||
State(state): State<Arc<AppState>>,
|
||||
Json(req): Json<CreateSessionRequest>,
|
||||
) -> Response {
|
||||
let (model_id, model_provider) = resolve_model(req.model);
|
||||
let now = chrono::Utc::now();
|
||||
let session_id = uuid::Uuid::new_v4();
|
||||
let title = generate_title(&req.content);
|
||||
|
||||
let (event_tx, _) = broadcast::channel(256);
|
||||
let generation_seq = Arc::new(AtomicU64::new(1));
|
||||
|
||||
let user_turn = SessionTurn::UserTurn(UserTurn {
|
||||
kind: UserTurnKind::User,
|
||||
content: req.content,
|
||||
created_at: now,
|
||||
});
|
||||
|
||||
let session = SessionState {
|
||||
id: session_id,
|
||||
title: title.clone(),
|
||||
model_id: model_id.clone(),
|
||||
model_provider: model_provider.clone(),
|
||||
system_prompt: req.system,
|
||||
turns: vec![user_turn],
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
event_tx: event_tx.clone(),
|
||||
generation_seq: Arc::clone(&generation_seq),
|
||||
};
|
||||
|
||||
{
|
||||
let mut store = state.sessions.write().expect("session store lock poisoned");
|
||||
store.insert(session_id, session);
|
||||
}
|
||||
|
||||
spawn_generation(Arc::clone(&state.sessions), session_id, state.dry_run(), 1);
|
||||
|
||||
(
|
||||
StatusCode::CREATED,
|
||||
Json(CreateSessionResponse {
|
||||
id: session_id,
|
||||
title,
|
||||
model: ModelReference { id: model_id },
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}),
|
||||
)
|
||||
.into_response()
|
||||
}
|
||||
|
||||
pub async fn retrieve_session(
|
||||
_auth: AuthenticatedService,
|
||||
State(state): State<Arc<AppState>>,
|
||||
Path(id): Path<uuid::Uuid>,
|
||||
) -> Response {
|
||||
let store = state.sessions.read().expect("session store lock poisoned");
|
||||
match store.get(&id) {
|
||||
Some(session) => (
|
||||
StatusCode::OK,
|
||||
Json(SessionDetail {
|
||||
id: session.id,
|
||||
title: session.title.clone(),
|
||||
model: ModelReference {
|
||||
id: session.model_id.clone(),
|
||||
},
|
||||
created_at: session.created_at,
|
||||
updated_at: session.updated_at,
|
||||
turns: session.turns.clone(),
|
||||
}),
|
||||
)
|
||||
.into_response(),
|
||||
None => ApiError::not_found("Session not found.").into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send_message(
|
||||
_auth: AuthenticatedService,
|
||||
State(state): State<Arc<AppState>>,
|
||||
Path(id): Path<uuid::Uuid>,
|
||||
Json(req): Json<SendMessageRequest>,
|
||||
) -> Response {
|
||||
let seq = {
|
||||
let mut store = state.sessions.write().expect("session store lock poisoned");
|
||||
match store.get_mut(&id) {
|
||||
Some(session) => {
|
||||
let now = chrono::Utc::now();
|
||||
session.turns.push(SessionTurn::UserTurn(UserTurn {
|
||||
kind: UserTurnKind::User,
|
||||
content: req.content,
|
||||
created_at: now,
|
||||
}));
|
||||
session.updated_at = now;
|
||||
session.generation_seq.fetch_add(1, Ordering::Relaxed) + 1
|
||||
}
|
||||
None => return ApiError::not_found("Session not found.").into_response(),
|
||||
}
|
||||
};
|
||||
|
||||
spawn_generation(Arc::clone(&state.sessions), id, state.dry_run(), seq);
|
||||
|
||||
(
|
||||
StatusCode::ACCEPTED,
|
||||
Json(SendMessageResponse { accepted: true }),
|
||||
)
|
||||
.into_response()
|
||||
}
|
||||
|
||||
pub async fn stream_session_events(
|
||||
_auth: AuthenticatedService,
|
||||
State(state): State<Arc<AppState>>,
|
||||
Path(id): Path<uuid::Uuid>,
|
||||
) -> Response {
|
||||
use tokio_stream::StreamExt;
|
||||
let rx = {
|
||||
let store = state.sessions.read().expect("session store lock poisoned");
|
||||
match store.get(&id) {
|
||||
Some(session) => session.event_tx.subscribe(),
|
||||
None => return ApiError::not_found("Session not found.").into_response(),
|
||||
}
|
||||
};
|
||||
|
||||
let stream = BroadcastStream::new(rx).filter_map(|result| match result {
|
||||
Ok(event) => {
|
||||
let sse: Option<Event> = match event {
|
||||
SessionEvent::TextDelta { delta } => Some(
|
||||
Event::default()
|
||||
.event("content_delta")
|
||||
.data(serde_json::json!({"delta": delta}).to_string()),
|
||||
),
|
||||
SessionEvent::AssistantTurnComplete {
|
||||
content,
|
||||
created_at,
|
||||
} => Some(
|
||||
Event::default().event("assistant_turn").data(
|
||||
serde_json::json!({
|
||||
"kind": "assistant",
|
||||
"content": content,
|
||||
"created_at": created_at,
|
||||
})
|
||||
.to_string(),
|
||||
),
|
||||
),
|
||||
SessionEvent::Done => Some(Event::default().event("done").data("{}")),
|
||||
SessionEvent::Error { message } => Some(
|
||||
Event::default()
|
||||
.event("error")
|
||||
.data(serde_json::json!({"message": message}).to_string()),
|
||||
),
|
||||
};
|
||||
sse.map(Ok::<_, std::convert::Infallible>)
|
||||
}
|
||||
Err(_) => None,
|
||||
});
|
||||
|
||||
Sse::new(stream).into_response()
|
||||
}
|
||||
|
||||
pub async fn list_sessions(
|
||||
_auth: AuthenticatedService,
|
||||
State(state): State<Arc<AppState>>,
|
||||
Query(pagination): Query<PaginationParams>,
|
||||
) -> Response {
|
||||
let store = state.sessions.read().expect("session store lock poisoned");
|
||||
let limit = pagination.limit.clamp(1, 100) as usize;
|
||||
let offset = pagination.offset as usize;
|
||||
|
||||
let mut items: Vec<SessionListItem> = store
|
||||
.values()
|
||||
.map(|session| {
|
||||
let last_message_preview = session
|
||||
.turns
|
||||
.last()
|
||||
.map(|t| match t {
|
||||
SessionTurn::UserTurn(u) => u.content.clone(),
|
||||
SessionTurn::AssistantTurn(a) => a.content.clone(),
|
||||
SessionTurn::ToolTurn(_) => String::new(),
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let preview = if last_message_preview.len() > 100 {
|
||||
format!("{}…", &last_message_preview[..100])
|
||||
} else {
|
||||
last_message_preview
|
||||
};
|
||||
SessionListItem {
|
||||
id: session.id,
|
||||
title: session.title.clone(),
|
||||
model: ModelReference {
|
||||
id: session.model_id.clone(),
|
||||
},
|
||||
last_message_preview: preview,
|
||||
created_at: session.created_at,
|
||||
updated_at: session.updated_at,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Sort by updated_at desc
|
||||
items.sort_by(|a, b| b.updated_at.cmp(&a.updated_at));
|
||||
|
||||
let page: Vec<_> = items.into_iter().skip(offset).take(limit + 1).collect();
|
||||
let has_more = page.len() > limit;
|
||||
let data: Vec<_> = page.into_iter().take(limit).collect();
|
||||
|
||||
(
|
||||
StatusCode::OK,
|
||||
Json(PaginatedSessionList {
|
||||
data,
|
||||
meta: PaginationMeta { has_more },
|
||||
}),
|
||||
)
|
||||
.into_response()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use axum::body::Body;
|
||||
use axum::http::Request;
|
||||
use tower::ServiceExt;
|
||||
|
||||
use crate::jwt_auth::AuthMode;
|
||||
use crate::server::{build_router, create_app_state_with_options};
|
||||
|
||||
fn dry_run_app() -> axum::Router {
|
||||
let state = create_app_state_with_options(
|
||||
fabro_types::Settings {
|
||||
dry_run: Some(true),
|
||||
..Default::default()
|
||||
},
|
||||
5,
|
||||
);
|
||||
build_router(state, AuthMode::Disabled)
|
||||
}
|
||||
|
||||
async fn body_json(body: Body) -> serde_json::Value {
|
||||
let bytes = to_bytes(body, usize::MAX).await.unwrap();
|
||||
serde_json::from_slice(&bytes).unwrap()
|
||||
}
|
||||
|
||||
fn api(path: &str) -> String {
|
||||
format!("/api/v1{path}")
|
||||
}
|
||||
|
||||
async fn create_test_session(app: &axum::Router) -> serde_json::Value {
|
||||
let req = Request::builder()
|
||||
.method("POST")
|
||||
.uri(api("/sessions"))
|
||||
.header("content-type", "application/json")
|
||||
.body(Body::from(
|
||||
serde_json::to_string(&serde_json::json!({
|
||||
"content": "Hello, world!"
|
||||
}))
|
||||
.unwrap(),
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
let response = app.clone().oneshot(req).await.unwrap();
|
||||
assert_eq!(response.status(), StatusCode::CREATED);
|
||||
body_json(response.into_body()).await
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn create_session_returns_201() {
|
||||
let app = dry_run_app();
|
||||
let body = create_test_session(&app).await;
|
||||
|
||||
assert!(body["id"].is_string());
|
||||
assert!(body["title"].is_string());
|
||||
assert!(body["model"]["id"].is_string());
|
||||
assert!(body["created_at"].is_string());
|
||||
assert!(body["updated_at"].is_string());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn retrieve_session_after_create() {
|
||||
let app = dry_run_app();
|
||||
let create_body = create_test_session(&app).await;
|
||||
let session_id = create_body["id"].as_str().unwrap();
|
||||
|
||||
// Give generation task a moment to complete
|
||||
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
|
||||
|
||||
let req = Request::builder()
|
||||
.method("GET")
|
||||
.uri(api(&format!("/sessions/{session_id}")))
|
||||
.body(Body::empty())
|
||||
.unwrap();
|
||||
|
||||
let response = app.oneshot(req).await.unwrap();
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
|
||||
let body = body_json(response.into_body()).await;
|
||||
assert_eq!(body["id"].as_str().unwrap(), session_id);
|
||||
assert!(body["turns"].is_array());
|
||||
// Should have at least the initial user turn
|
||||
assert!(!body["turns"].as_array().unwrap().is_empty());
|
||||
assert_eq!(body["turns"][0]["kind"], "user");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn retrieve_session_not_found() {
|
||||
let app = dry_run_app();
|
||||
|
||||
let req = Request::builder()
|
||||
.method("GET")
|
||||
.uri(api("/sessions/a1b2c3d4-e5f6-7890-abcd-ef1234567890"))
|
||||
.body(Body::empty())
|
||||
.unwrap();
|
||||
|
||||
let response = app.oneshot(req).await.unwrap();
|
||||
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn send_message_returns_202() {
|
||||
let app = dry_run_app();
|
||||
let create_body = create_test_session(&app).await;
|
||||
let session_id = create_body["id"].as_str().unwrap();
|
||||
|
||||
let req = Request::builder()
|
||||
.method("POST")
|
||||
.uri(api(&format!("/sessions/{session_id}/messages")))
|
||||
.header("content-type", "application/json")
|
||||
.body(Body::from(
|
||||
serde_json::to_string(&serde_json::json!({
|
||||
"content": "Follow up question"
|
||||
}))
|
||||
.unwrap(),
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
let response = app.oneshot(req).await.unwrap();
|
||||
assert_eq!(response.status(), StatusCode::ACCEPTED);
|
||||
|
||||
let body = body_json(response.into_body()).await;
|
||||
assert_eq!(body["accepted"], true);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn send_message_not_found() {
|
||||
let app = dry_run_app();
|
||||
|
||||
let req = Request::builder()
|
||||
.method("POST")
|
||||
.uri(api(
|
||||
"/sessions/a1b2c3d4-e5f6-7890-abcd-ef1234567890/messages",
|
||||
))
|
||||
.header("content-type", "application/json")
|
||||
.body(Body::from(
|
||||
serde_json::to_string(&serde_json::json!({
|
||||
"content": "Hello"
|
||||
}))
|
||||
.unwrap(),
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
let response = app.oneshot(req).await.unwrap();
|
||||
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn list_sessions_empty() {
|
||||
let app = dry_run_app();
|
||||
|
||||
let req = Request::builder()
|
||||
.method("GET")
|
||||
.uri(api("/sessions"))
|
||||
.body(Body::empty())
|
||||
.unwrap();
|
||||
|
||||
let response = app.oneshot(req).await.unwrap();
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
|
||||
let body = body_json(response.into_body()).await;
|
||||
assert!(body["data"].as_array().unwrap().is_empty());
|
||||
assert_eq!(body["meta"]["has_more"], false);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn list_sessions_after_create() {
|
||||
let app = dry_run_app();
|
||||
let _create_body = create_test_session(&app).await;
|
||||
|
||||
let req = Request::builder()
|
||||
.method("GET")
|
||||
.uri(api("/sessions"))
|
||||
.body(Body::empty())
|
||||
.unwrap();
|
||||
|
||||
let response = app.oneshot(req).await.unwrap();
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
|
||||
let body = body_json(response.into_body()).await;
|
||||
assert_eq!(body["data"].as_array().unwrap().len(), 1);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn stream_events_dry_run() {
|
||||
let app = dry_run_app();
|
||||
let create_body = create_test_session(&app).await;
|
||||
let session_id = create_body["id"].as_str().unwrap();
|
||||
|
||||
// Give the generation task a moment to produce events
|
||||
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
||||
|
||||
let req = Request::builder()
|
||||
.method("GET")
|
||||
.uri(api(&format!("/sessions/{session_id}/events")))
|
||||
.body(Body::empty())
|
||||
.unwrap();
|
||||
|
||||
let response = app.oneshot(req).await.unwrap();
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
response
|
||||
.headers()
|
||||
.get("content-type")
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
"text/event-stream"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -53,14 +53,6 @@ const ENDPOINTS: &[PaginatedEndpoint] = &[
|
|||
path: "/api/v1/workflows/implement/runs",
|
||||
name: "listWorkflowRuns",
|
||||
},
|
||||
PaginatedEndpoint {
|
||||
path: "/api/v1/retros",
|
||||
name: "listRetros",
|
||||
},
|
||||
PaginatedEndpoint {
|
||||
path: "/api/v1/sessions",
|
||||
name: "listSessions",
|
||||
},
|
||||
PaginatedEndpoint {
|
||||
path: "/api/v1/insights/queries",
|
||||
name: "listSavedQueries",
|
||||
|
|
|
|||
|
|
@ -5,16 +5,13 @@ api/human-in-the-loop-api.ts
|
|||
api/insights-api.ts
|
||||
api/models-api.ts
|
||||
api/repos-api.ts
|
||||
api/retros-api.ts
|
||||
api/run-internals-api.ts
|
||||
api/run-outputs-api.ts
|
||||
api/runs-api.ts
|
||||
api/secrets-api.ts
|
||||
api/sessions-api.ts
|
||||
api/settings-api.ts
|
||||
api/system-api.ts
|
||||
api/usage-api.ts
|
||||
api/verification-api.ts
|
||||
api/workflows-api.ts
|
||||
base.ts
|
||||
common.ts
|
||||
|
|
@ -30,7 +27,6 @@ models/artifact-entry.ts
|
|||
models/artifact-list-response.ts
|
||||
models/artifacts-settings.ts
|
||||
models/assistant-stage-turn.ts
|
||||
models/assistant-turn.ts
|
||||
models/auth-settings.ts
|
||||
models/board-column.ts
|
||||
models/check-run-status.ts
|
||||
|
|
@ -43,15 +39,7 @@ models/completion-response.ts
|
|||
models/completion-tool-choice.ts
|
||||
models/completion-tool-definition.ts
|
||||
models/completion-usage.ts
|
||||
models/control-detail.ts
|
||||
models/control-info.ts
|
||||
models/control-performance.ts
|
||||
models/control-reference.ts
|
||||
models/create-completion-request.ts
|
||||
models/create-session-request.ts
|
||||
models/create-session-response.ts
|
||||
models/create-signoff-request.ts
|
||||
models/criterion-reference.ts
|
||||
models/daytona-settings-network-one-of.ts
|
||||
models/daytona-settings-network.ts
|
||||
models/daytona-settings.ts
|
||||
|
|
@ -74,8 +62,6 @@ models/execute-query-response.ts
|
|||
models/features.ts
|
||||
models/file-checkpoint.ts
|
||||
models/file-diff.ts
|
||||
models/friction-kind.ts
|
||||
models/friction-point.ts
|
||||
models/git-author-settings.ts
|
||||
models/git-hub-settings.ts
|
||||
models/git-settings.ts
|
||||
|
|
@ -85,8 +71,6 @@ models/hook-definition.ts
|
|||
models/index.ts
|
||||
models/internal-run-status.ts
|
||||
models/internal-stage-status.ts
|
||||
models/learning-category.ts
|
||||
models/learning.ts
|
||||
models/llm-settings.ts
|
||||
models/local-sandbox-settings.ts
|
||||
models/log-settings.ts
|
||||
|
|
@ -109,22 +93,15 @@ models/model-test-result.ts
|
|||
models/model.ts
|
||||
models/node-state.ts
|
||||
models/node-status-record.ts
|
||||
models/open-item-kind.ts
|
||||
models/open-item.ts
|
||||
models/paginated-api-question-list.ts
|
||||
models/paginated-event-list.ts
|
||||
models/paginated-history-entry-list.ts
|
||||
models/paginated-model-list.ts
|
||||
models/paginated-retro-list.ts
|
||||
models/paginated-run-file-list.ts
|
||||
models/paginated-run-list.ts
|
||||
models/paginated-run-stage-list.ts
|
||||
models/paginated-saved-query-list.ts
|
||||
models/paginated-session-list.ts
|
||||
models/paginated-signoff-list.ts
|
||||
models/paginated-stage-turn-list.ts
|
||||
models/paginated-verification-control-list.ts
|
||||
models/paginated-verification-criterion-list.ts
|
||||
models/paginated-workflow-list.ts
|
||||
models/pagination-meta.ts
|
||||
models/preflight-check-detail.ts
|
||||
|
|
@ -140,16 +117,12 @@ models/prune-runs-request.ts
|
|||
models/prune-runs-response.ts
|
||||
models/pull-request-settings.ts
|
||||
models/question-type.ts
|
||||
models/recent-control-result.ts
|
||||
models/render-workflow-graph-direction.ts
|
||||
models/render-workflow-graph-format.ts
|
||||
models/render-workflow-graph-request.ts
|
||||
models/repo-check-response-permissions.ts
|
||||
models/repo-check-response.ts
|
||||
models/repository-reference.ts
|
||||
models/retro-detail.ts
|
||||
models/retro-list-item.ts
|
||||
models/retro-stats.ts
|
||||
models/root-response-urls.ts
|
||||
models/root-response.ts
|
||||
models/run-artifact-entry.ts
|
||||
|
|
@ -180,25 +153,15 @@ models/save-query-request.ts
|
|||
models/saved-query.ts
|
||||
models/secret-list-response.ts
|
||||
models/secret-metadata.ts
|
||||
models/send-message-request.ts
|
||||
models/send-message-response.ts
|
||||
models/server-settings-exec.ts
|
||||
models/server-settings-fabro.ts
|
||||
models/server-settings-server-tls.ts
|
||||
models/server-settings-server.ts
|
||||
models/server-settings.ts
|
||||
models/session-detail.ts
|
||||
models/session-list-item.ts
|
||||
models/session-turn.ts
|
||||
models/set-secret-request.ts
|
||||
models/setup-settings.ts
|
||||
models/sibling-control.ts
|
||||
models/signoff-status.ts
|
||||
models/signoff.ts
|
||||
models/smoothness-rating.ts
|
||||
models/ssh-access-request.ts
|
||||
models/ssh-access-response.ts
|
||||
models/stage-retro.ts
|
||||
models/stage-status.ts
|
||||
models/stage-turn.ts
|
||||
models/start-run-request.ts
|
||||
|
|
@ -212,22 +175,12 @@ models/system-stage-turn.ts
|
|||
models/tls-settings.ts
|
||||
models/token-usage.ts
|
||||
models/tool-stage-turn.ts
|
||||
models/tool-turn.ts
|
||||
models/tool-use.ts
|
||||
models/usage-by-model.ts
|
||||
models/usage-stage-ref.ts
|
||||
models/usage-stage.ts
|
||||
models/usage-totals.ts
|
||||
models/user-response.ts
|
||||
models/user-turn.ts
|
||||
models/verification-control-list-item.ts
|
||||
models/verification-control.ts
|
||||
models/verification-criterion-detail.ts
|
||||
models/verification-criterion.ts
|
||||
models/verification-detail-response.ts
|
||||
models/verification-mode.ts
|
||||
models/verification-result.ts
|
||||
models/verification-type.ts
|
||||
models/web-settings.ts
|
||||
models/webhook-settings.ts
|
||||
models/workflow-detail.ts
|
||||
|
|
|
|||
|
|
@ -20,15 +20,12 @@ export * from './api/human-in-the-loop-api';
|
|||
export * from './api/insights-api';
|
||||
export * from './api/models-api';
|
||||
export * from './api/repos-api';
|
||||
export * from './api/retros-api';
|
||||
export * from './api/run-internals-api';
|
||||
export * from './api/run-outputs-api';
|
||||
export * from './api/runs-api';
|
||||
export * from './api/secrets-api';
|
||||
export * from './api/sessions-api';
|
||||
export * from './api/settings-api';
|
||||
export * from './api/system-api';
|
||||
export * from './api/usage-api';
|
||||
export * from './api/verification-api';
|
||||
export * from './api/workflows-api';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,237 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
import type { Configuration } from '../configuration';
|
||||
import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios';
|
||||
import globalAxios from 'axios';
|
||||
// Some imports not used depending on template conditions
|
||||
// @ts-ignore
|
||||
import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction, replaceWithSerializableTypeIfNeeded } from '../common';
|
||||
// @ts-ignore
|
||||
import { BASE_PATH, COLLECTION_FORMATS, type RequestArgs, BaseAPI, RequiredError, operationServerMap } from '../base';
|
||||
// @ts-ignore
|
||||
import type { ErrorResponse } from '../models';
|
||||
// @ts-ignore
|
||||
import type { PaginatedRetroList } from '../models';
|
||||
// @ts-ignore
|
||||
import type { RetroDetail } from '../models';
|
||||
// @ts-ignore
|
||||
import type { SmoothnessRating } from '../models';
|
||||
/**
|
||||
* RetrosApi - axios parameter creator
|
||||
*/
|
||||
export const RetrosApiAxiosParamCreator = function (configuration?: Configuration) {
|
||||
return {
|
||||
/**
|
||||
* Returns a paginated list of run retrospectives ordered by recency, with smoothness ratings and summary statistics.
|
||||
* @summary List Retros
|
||||
* @param {string} [workflow] Filter retros by workflow slug.
|
||||
* @param {SmoothnessRating} [smoothness] Filter retros by smoothness rating.
|
||||
* @param {number} [pageLimit] Maximum number of items to return per page.
|
||||
* @param {number} [pageOffset] Number of items to skip before returning results.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
listRetros: async (workflow?: string, smoothness?: SmoothnessRating, pageLimit?: number, pageOffset?: number, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
const localVarPath = `/api/v1/retros`;
|
||||
// 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 mTLS required
|
||||
await setApiKeyToObject(localVarHeaderParameter, "X-mTLS-Client-CN", configuration)
|
||||
|
||||
// authentication BearerAuth required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
if (workflow !== undefined) {
|
||||
localVarQueryParameter['workflow'] = workflow;
|
||||
}
|
||||
|
||||
if (smoothness !== undefined) {
|
||||
localVarQueryParameter['smoothness'] = smoothness;
|
||||
}
|
||||
|
||||
if (pageLimit !== undefined) {
|
||||
localVarQueryParameter['page[limit]'] = pageLimit;
|
||||
}
|
||||
|
||||
if (pageOffset !== undefined) {
|
||||
localVarQueryParameter['page[offset]'] = pageOffset;
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Returns the retrospective analysis for a completed run, or null if the retro has not been generated yet.
|
||||
* @summary Retrieve Retro
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
retrieveRetro: async (id: string, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'id' is not null or undefined
|
||||
assertParamExists('retrieveRetro', 'id', id)
|
||||
const localVarPath = `/api/v1/runs/{id}/retro`
|
||||
.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 mTLS required
|
||||
await setApiKeyToObject(localVarHeaderParameter, "X-mTLS-Client-CN", configuration)
|
||||
|
||||
// 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,
|
||||
};
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* RetrosApi - functional programming interface
|
||||
*/
|
||||
export const RetrosApiFp = function(configuration?: Configuration) {
|
||||
const localVarAxiosParamCreator = RetrosApiAxiosParamCreator(configuration)
|
||||
return {
|
||||
/**
|
||||
* Returns a paginated list of run retrospectives ordered by recency, with smoothness ratings and summary statistics.
|
||||
* @summary List Retros
|
||||
* @param {string} [workflow] Filter retros by workflow slug.
|
||||
* @param {SmoothnessRating} [smoothness] Filter retros by smoothness rating.
|
||||
* @param {number} [pageLimit] Maximum number of items to return per page.
|
||||
* @param {number} [pageOffset] Number of items to skip before returning results.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async listRetros(workflow?: string, smoothness?: SmoothnessRating, pageLimit?: number, pageOffset?: number, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<PaginatedRetroList>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.listRetros(workflow, smoothness, pageLimit, pageOffset, options);
|
||||
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||
const localVarOperationServerBasePath = operationServerMap['RetrosApi.listRetros']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
* Returns the retrospective analysis for a completed run, or null if the retro has not been generated yet.
|
||||
* @summary Retrieve Retro
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async retrieveRetro(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<RetroDetail>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.retrieveRetro(id, options);
|
||||
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||
const localVarOperationServerBasePath = operationServerMap['RetrosApi.retrieveRetro']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* RetrosApi - factory interface
|
||||
*/
|
||||
export const RetrosApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
|
||||
const localVarFp = RetrosApiFp(configuration)
|
||||
return {
|
||||
/**
|
||||
* Returns a paginated list of run retrospectives ordered by recency, with smoothness ratings and summary statistics.
|
||||
* @summary List Retros
|
||||
* @param {string} [workflow] Filter retros by workflow slug.
|
||||
* @param {SmoothnessRating} [smoothness] Filter retros by smoothness rating.
|
||||
* @param {number} [pageLimit] Maximum number of items to return per page.
|
||||
* @param {number} [pageOffset] Number of items to skip before returning results.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
listRetros(workflow?: string, smoothness?: SmoothnessRating, pageLimit?: number, pageOffset?: number, options?: RawAxiosRequestConfig): AxiosPromise<PaginatedRetroList> {
|
||||
return localVarFp.listRetros(workflow, smoothness, pageLimit, pageOffset, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
* Returns the retrospective analysis for a completed run, or null if the retro has not been generated yet.
|
||||
* @summary Retrieve Retro
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
retrieveRetro(id: string, options?: RawAxiosRequestConfig): AxiosPromise<RetroDetail> {
|
||||
return localVarFp.retrieveRetro(id, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* RetrosApi - object-oriented interface
|
||||
*/
|
||||
export class RetrosApi extends BaseAPI {
|
||||
/**
|
||||
* Returns a paginated list of run retrospectives ordered by recency, with smoothness ratings and summary statistics.
|
||||
* @summary List Retros
|
||||
* @param {string} [workflow] Filter retros by workflow slug.
|
||||
* @param {SmoothnessRating} [smoothness] Filter retros by smoothness rating.
|
||||
* @param {number} [pageLimit] Maximum number of items to return per page.
|
||||
* @param {number} [pageOffset] Number of items to skip before returning results.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
public listRetros(workflow?: string, smoothness?: SmoothnessRating, pageLimit?: number, pageOffset?: number, options?: RawAxiosRequestConfig) {
|
||||
return RetrosApiFp(this.configuration).listRetros(workflow, smoothness, pageLimit, pageOffset, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the retrospective analysis for a completed run, or null if the retro has not been generated yet.
|
||||
* @summary Retrieve Retro
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
public retrieveRetro(id: string, options?: RawAxiosRequestConfig) {
|
||||
return RetrosApiFp(this.configuration).retrieveRetro(id, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,468 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
import type { Configuration } from '../configuration';
|
||||
import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios';
|
||||
import globalAxios from 'axios';
|
||||
// Some imports not used depending on template conditions
|
||||
// @ts-ignore
|
||||
import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction, replaceWithSerializableTypeIfNeeded } from '../common';
|
||||
// @ts-ignore
|
||||
import { BASE_PATH, COLLECTION_FORMATS, type RequestArgs, BaseAPI, RequiredError, operationServerMap } from '../base';
|
||||
// @ts-ignore
|
||||
import type { CreateSessionRequest } from '../models';
|
||||
// @ts-ignore
|
||||
import type { CreateSessionResponse } from '../models';
|
||||
// @ts-ignore
|
||||
import type { ErrorResponse } from '../models';
|
||||
// @ts-ignore
|
||||
import type { PaginatedSessionList } from '../models';
|
||||
// @ts-ignore
|
||||
import type { SendMessageRequest } from '../models';
|
||||
// @ts-ignore
|
||||
import type { SendMessageResponse } from '../models';
|
||||
// @ts-ignore
|
||||
import type { SessionDetail } from '../models';
|
||||
/**
|
||||
* SessionsApi - axios parameter creator
|
||||
*/
|
||||
export const SessionsApiAxiosParamCreator = function (configuration?: Configuration) {
|
||||
return {
|
||||
/**
|
||||
* Start a new interactive chat session. The initial user prompt is required; a model may optionally be specified.
|
||||
* @summary Create Session
|
||||
* @param {CreateSessionRequest} createSessionRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
createSession: async (createSessionRequest: CreateSessionRequest, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'createSessionRequest' is not null or undefined
|
||||
assertParamExists('createSession', 'createSessionRequest', createSessionRequest)
|
||||
const localVarPath = `/api/v1/sessions`;
|
||||
// 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 mTLS required
|
||||
await setApiKeyToObject(localVarHeaderParameter, "X-mTLS-Client-CN", configuration)
|
||||
|
||||
// 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(createSessionRequest, localVarRequestOptions, configuration)
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Returns sessions ordered by recency (newest first).
|
||||
* @summary List Sessions
|
||||
* @param {number} [pageLimit] Maximum number of items to return per page.
|
||||
* @param {number} [pageOffset] Number of items to skip before returning results.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
listSessions: async (pageLimit?: number, pageOffset?: number, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
const localVarPath = `/api/v1/sessions`;
|
||||
// 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 mTLS required
|
||||
await setApiKeyToObject(localVarHeaderParameter, "X-mTLS-Client-CN", configuration)
|
||||
|
||||
// authentication BearerAuth required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
if (pageLimit !== undefined) {
|
||||
localVarQueryParameter['page[limit]'] = pageLimit;
|
||||
}
|
||||
|
||||
if (pageOffset !== undefined) {
|
||||
localVarQueryParameter['page[offset]'] = pageOffset;
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Returns the full session detail including all conversation turns.
|
||||
* @summary Retrieve Session
|
||||
* @param {string} id Unique session identifier.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
retrieveSession: async (id: string, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'id' is not null or undefined
|
||||
assertParamExists('retrieveSession', 'id', id)
|
||||
const localVarPath = `/api/v1/sessions/{id}`
|
||||
.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 mTLS required
|
||||
await setApiKeyToObject(localVarHeaderParameter, "X-mTLS-Client-CN", configuration)
|
||||
|
||||
// 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,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Append a user message to an existing session. The server will process it and produce assistant and tool turns asynchronously via the event stream.
|
||||
* @summary Send Session Message
|
||||
* @param {string} id Unique session identifier.
|
||||
* @param {SendMessageRequest} sendMessageRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
sendSessionMessage: async (id: string, sendMessageRequest: SendMessageRequest, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'id' is not null or undefined
|
||||
assertParamExists('sendSessionMessage', 'id', id)
|
||||
// verify required parameter 'sendMessageRequest' is not null or undefined
|
||||
assertParamExists('sendSessionMessage', 'sendMessageRequest', sendMessageRequest)
|
||||
const localVarPath = `/api/v1/sessions/{id}/messages`
|
||||
.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 mTLS required
|
||||
await setApiKeyToObject(localVarHeaderParameter, "X-mTLS-Client-CN", configuration)
|
||||
|
||||
// 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(sendMessageRequest, localVarRequestOptions, configuration)
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Opens a server-sent event (SSE) stream for real-time session updates. Each SSE frame includes a sequential numeric `id:` field that supports resumption via the `Last-Event-ID` request header. The stream emits the following SSE event types: - `event: content_delta` — data: `{\"delta\": \"...\"}` (incremental text chunk) - `event: assistant_turn` — data: `AssistantTurn` JSON object - `event: tool_turn` — data: `ToolTurn` JSON object - `event: done` — data: `{}` (stream complete) - `event: error` — data: `{\"message\": \"...\"}` (error occurred) Each SSE frame has an `id:` line (sequential integer), an `event:` line (the event type), and a `data:` line (the JSON payload).
|
||||
* @summary Stream Session Events
|
||||
* @param {string} id Unique session identifier.
|
||||
* @param {string} [lastEventID] SSE reconnection header. When provided, the server resumes the stream after the event with this ID. IDs are 0-based sequential integers assigned to each emitted SSE frame.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
streamSessionEvents: async (id: string, lastEventID?: string, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'id' is not null or undefined
|
||||
assertParamExists('streamSessionEvents', 'id', id)
|
||||
const localVarPath = `/api/v1/sessions/{id}/events`
|
||||
.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 mTLS required
|
||||
await setApiKeyToObject(localVarHeaderParameter, "X-mTLS-Client-CN", configuration)
|
||||
|
||||
// authentication BearerAuth required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
localVarHeaderParameter['Accept'] = 'text/event-stream,application/json';
|
||||
|
||||
if (lastEventID != null) {
|
||||
localVarHeaderParameter['Last-Event-ID'] = String(lastEventID);
|
||||
}
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* SessionsApi - functional programming interface
|
||||
*/
|
||||
export const SessionsApiFp = function(configuration?: Configuration) {
|
||||
const localVarAxiosParamCreator = SessionsApiAxiosParamCreator(configuration)
|
||||
return {
|
||||
/**
|
||||
* Start a new interactive chat session. The initial user prompt is required; a model may optionally be specified.
|
||||
* @summary Create Session
|
||||
* @param {CreateSessionRequest} createSessionRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async createSession(createSessionRequest: CreateSessionRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<CreateSessionResponse>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.createSession(createSessionRequest, options);
|
||||
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||
const localVarOperationServerBasePath = operationServerMap['SessionsApi.createSession']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
* Returns sessions ordered by recency (newest first).
|
||||
* @summary List Sessions
|
||||
* @param {number} [pageLimit] Maximum number of items to return per page.
|
||||
* @param {number} [pageOffset] Number of items to skip before returning results.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async listSessions(pageLimit?: number, pageOffset?: number, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<PaginatedSessionList>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.listSessions(pageLimit, pageOffset, options);
|
||||
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||
const localVarOperationServerBasePath = operationServerMap['SessionsApi.listSessions']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
* Returns the full session detail including all conversation turns.
|
||||
* @summary Retrieve Session
|
||||
* @param {string} id Unique session identifier.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async retrieveSession(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<SessionDetail>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.retrieveSession(id, options);
|
||||
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||
const localVarOperationServerBasePath = operationServerMap['SessionsApi.retrieveSession']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
* Append a user message to an existing session. The server will process it and produce assistant and tool turns asynchronously via the event stream.
|
||||
* @summary Send Session Message
|
||||
* @param {string} id Unique session identifier.
|
||||
* @param {SendMessageRequest} sendMessageRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async sendSessionMessage(id: string, sendMessageRequest: SendMessageRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<SendMessageResponse>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.sendSessionMessage(id, sendMessageRequest, options);
|
||||
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||
const localVarOperationServerBasePath = operationServerMap['SessionsApi.sendSessionMessage']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
* Opens a server-sent event (SSE) stream for real-time session updates. Each SSE frame includes a sequential numeric `id:` field that supports resumption via the `Last-Event-ID` request header. The stream emits the following SSE event types: - `event: content_delta` — data: `{\"delta\": \"...\"}` (incremental text chunk) - `event: assistant_turn` — data: `AssistantTurn` JSON object - `event: tool_turn` — data: `ToolTurn` JSON object - `event: done` — data: `{}` (stream complete) - `event: error` — data: `{\"message\": \"...\"}` (error occurred) Each SSE frame has an `id:` line (sequential integer), an `event:` line (the event type), and a `data:` line (the JSON payload).
|
||||
* @summary Stream Session Events
|
||||
* @param {string} id Unique session identifier.
|
||||
* @param {string} [lastEventID] SSE reconnection header. When provided, the server resumes the stream after the event with this ID. IDs are 0-based sequential integers assigned to each emitted SSE frame.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async streamSessionEvents(id: string, lastEventID?: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<string>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.streamSessionEvents(id, lastEventID, options);
|
||||
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||
const localVarOperationServerBasePath = operationServerMap['SessionsApi.streamSessionEvents']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* SessionsApi - factory interface
|
||||
*/
|
||||
export const SessionsApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
|
||||
const localVarFp = SessionsApiFp(configuration)
|
||||
return {
|
||||
/**
|
||||
* Start a new interactive chat session. The initial user prompt is required; a model may optionally be specified.
|
||||
* @summary Create Session
|
||||
* @param {CreateSessionRequest} createSessionRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
createSession(createSessionRequest: CreateSessionRequest, options?: RawAxiosRequestConfig): AxiosPromise<CreateSessionResponse> {
|
||||
return localVarFp.createSession(createSessionRequest, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
* Returns sessions ordered by recency (newest first).
|
||||
* @summary List Sessions
|
||||
* @param {number} [pageLimit] Maximum number of items to return per page.
|
||||
* @param {number} [pageOffset] Number of items to skip before returning results.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
listSessions(pageLimit?: number, pageOffset?: number, options?: RawAxiosRequestConfig): AxiosPromise<PaginatedSessionList> {
|
||||
return localVarFp.listSessions(pageLimit, pageOffset, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
* Returns the full session detail including all conversation turns.
|
||||
* @summary Retrieve Session
|
||||
* @param {string} id Unique session identifier.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
retrieveSession(id: string, options?: RawAxiosRequestConfig): AxiosPromise<SessionDetail> {
|
||||
return localVarFp.retrieveSession(id, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
* Append a user message to an existing session. The server will process it and produce assistant and tool turns asynchronously via the event stream.
|
||||
* @summary Send Session Message
|
||||
* @param {string} id Unique session identifier.
|
||||
* @param {SendMessageRequest} sendMessageRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
sendSessionMessage(id: string, sendMessageRequest: SendMessageRequest, options?: RawAxiosRequestConfig): AxiosPromise<SendMessageResponse> {
|
||||
return localVarFp.sendSessionMessage(id, sendMessageRequest, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
* Opens a server-sent event (SSE) stream for real-time session updates. Each SSE frame includes a sequential numeric `id:` field that supports resumption via the `Last-Event-ID` request header. The stream emits the following SSE event types: - `event: content_delta` — data: `{\"delta\": \"...\"}` (incremental text chunk) - `event: assistant_turn` — data: `AssistantTurn` JSON object - `event: tool_turn` — data: `ToolTurn` JSON object - `event: done` — data: `{}` (stream complete) - `event: error` — data: `{\"message\": \"...\"}` (error occurred) Each SSE frame has an `id:` line (sequential integer), an `event:` line (the event type), and a `data:` line (the JSON payload).
|
||||
* @summary Stream Session Events
|
||||
* @param {string} id Unique session identifier.
|
||||
* @param {string} [lastEventID] SSE reconnection header. When provided, the server resumes the stream after the event with this ID. IDs are 0-based sequential integers assigned to each emitted SSE frame.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
streamSessionEvents(id: string, lastEventID?: string, options?: RawAxiosRequestConfig): AxiosPromise<string> {
|
||||
return localVarFp.streamSessionEvents(id, lastEventID, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* SessionsApi - object-oriented interface
|
||||
*/
|
||||
export class SessionsApi extends BaseAPI {
|
||||
/**
|
||||
* Start a new interactive chat session. The initial user prompt is required; a model may optionally be specified.
|
||||
* @summary Create Session
|
||||
* @param {CreateSessionRequest} createSessionRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
public createSession(createSessionRequest: CreateSessionRequest, options?: RawAxiosRequestConfig) {
|
||||
return SessionsApiFp(this.configuration).createSession(createSessionRequest, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sessions ordered by recency (newest first).
|
||||
* @summary List Sessions
|
||||
* @param {number} [pageLimit] Maximum number of items to return per page.
|
||||
* @param {number} [pageOffset] Number of items to skip before returning results.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
public listSessions(pageLimit?: number, pageOffset?: number, options?: RawAxiosRequestConfig) {
|
||||
return SessionsApiFp(this.configuration).listSessions(pageLimit, pageOffset, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full session detail including all conversation turns.
|
||||
* @summary Retrieve Session
|
||||
* @param {string} id Unique session identifier.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
public retrieveSession(id: string, options?: RawAxiosRequestConfig) {
|
||||
return SessionsApiFp(this.configuration).retrieveSession(id, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a user message to an existing session. The server will process it and produce assistant and tool turns asynchronously via the event stream.
|
||||
* @summary Send Session Message
|
||||
* @param {string} id Unique session identifier.
|
||||
* @param {SendMessageRequest} sendMessageRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
public sendSessionMessage(id: string, sendMessageRequest: SendMessageRequest, options?: RawAxiosRequestConfig) {
|
||||
return SessionsApiFp(this.configuration).sendSessionMessage(id, sendMessageRequest, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a server-sent event (SSE) stream for real-time session updates. Each SSE frame includes a sequential numeric `id:` field that supports resumption via the `Last-Event-ID` request header. The stream emits the following SSE event types: - `event: content_delta` — data: `{\"delta\": \"...\"}` (incremental text chunk) - `event: assistant_turn` — data: `AssistantTurn` JSON object - `event: tool_turn` — data: `ToolTurn` JSON object - `event: done` — data: `{}` (stream complete) - `event: error` — data: `{\"message\": \"...\"}` (error occurred) Each SSE frame has an `id:` line (sequential integer), an `event:` line (the event type), and a `data:` line (the JSON payload).
|
||||
* @summary Stream Session Events
|
||||
* @param {string} id Unique session identifier.
|
||||
* @param {string} [lastEventID] SSE reconnection header. When provided, the server resumes the stream after the event with this ID. IDs are 0-based sequential integers assigned to each emitted SSE frame.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
public streamSessionEvents(id: string, lastEventID?: string, options?: RawAxiosRequestConfig) {
|
||||
return SessionsApiFp(this.configuration).streamSessionEvents(id, lastEventID, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,647 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
import type { Configuration } from '../configuration';
|
||||
import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios';
|
||||
import globalAxios from 'axios';
|
||||
// Some imports not used depending on template conditions
|
||||
// @ts-ignore
|
||||
import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction, replaceWithSerializableTypeIfNeeded } from '../common';
|
||||
// @ts-ignore
|
||||
import { BASE_PATH, COLLECTION_FORMATS, type RequestArgs, BaseAPI, RequiredError, operationServerMap } from '../base';
|
||||
// @ts-ignore
|
||||
import type { CreateSignoffRequest } from '../models';
|
||||
// @ts-ignore
|
||||
import type { ErrorResponse } from '../models';
|
||||
// @ts-ignore
|
||||
import type { PaginatedSignoffList } from '../models';
|
||||
// @ts-ignore
|
||||
import type { PaginatedVerificationControlList } from '../models';
|
||||
// @ts-ignore
|
||||
import type { PaginatedVerificationCriterionList } from '../models';
|
||||
// @ts-ignore
|
||||
import type { Signoff } from '../models';
|
||||
// @ts-ignore
|
||||
import type { VerificationCriterionDetail } from '../models';
|
||||
// @ts-ignore
|
||||
import type { VerificationDetailResponse } from '../models';
|
||||
/**
|
||||
* VerificationApi - axios parameter creator
|
||||
*/
|
||||
export const VerificationApiAxiosParamCreator = function (configuration?: Configuration) {
|
||||
return {
|
||||
/**
|
||||
* Creates a new signoff for a (control, repository, commit SHA) tuple. Multiple signoffs are allowed per tuple; the latest one wins for display purposes.
|
||||
* @summary Create Signoff
|
||||
* @param {CreateSignoffRequest} createSignoffRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
createSignoff: async (createSignoffRequest: CreateSignoffRequest, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'createSignoffRequest' is not null or undefined
|
||||
assertParamExists('createSignoff', 'createSignoffRequest', createSignoffRequest)
|
||||
const localVarPath = `/api/v1/verification/signoffs`;
|
||||
// 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 mTLS required
|
||||
await setApiKeyToObject(localVarHeaderParameter, "X-mTLS-Client-CN", configuration)
|
||||
|
||||
// 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(createSignoffRequest, localVarRequestOptions, configuration)
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Returns a paginated list of signoffs, optionally filtered by control, repository, and/or commit SHA.
|
||||
* @summary List Signoffs
|
||||
* @param {string} [control] Filter signoffs by control slug.
|
||||
* @param {string} [repository] Filter signoffs by repository name.
|
||||
* @param {string} [commitSha] Filter signoffs by commit SHA.
|
||||
* @param {number} [pageLimit] Maximum number of items to return per page.
|
||||
* @param {number} [pageOffset] Number of items to skip before returning results.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
listSignoffs: async (control?: string, repository?: string, commitSha?: string, pageLimit?: number, pageOffset?: number, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
const localVarPath = `/api/v1/verification/signoffs`;
|
||||
// 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 mTLS required
|
||||
await setApiKeyToObject(localVarHeaderParameter, "X-mTLS-Client-CN", configuration)
|
||||
|
||||
// authentication BearerAuth required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
if (control !== undefined) {
|
||||
localVarQueryParameter['control'] = control;
|
||||
}
|
||||
|
||||
if (repository !== undefined) {
|
||||
localVarQueryParameter['repository'] = repository;
|
||||
}
|
||||
|
||||
if (commitSha !== undefined) {
|
||||
localVarQueryParameter['commit_sha'] = commitSha;
|
||||
}
|
||||
|
||||
if (pageLimit !== undefined) {
|
||||
localVarQueryParameter['page[limit]'] = pageLimit;
|
||||
}
|
||||
|
||||
if (pageOffset !== undefined) {
|
||||
localVarQueryParameter['page[offset]'] = pageOffset;
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Returns a flat paginated list of all verification controls across all criteria.
|
||||
* @summary List Verification Controls
|
||||
* @param {number} [pageLimit] Maximum number of items to return per page.
|
||||
* @param {number} [pageOffset] Number of items to skip before returning results.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
listVerificationControls: async (pageLimit?: number, pageOffset?: number, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
const localVarPath = `/api/v1/verification/controls`;
|
||||
// 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 mTLS required
|
||||
await setApiKeyToObject(localVarHeaderParameter, "X-mTLS-Client-CN", configuration)
|
||||
|
||||
// authentication BearerAuth required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
if (pageLimit !== undefined) {
|
||||
localVarQueryParameter['page[limit]'] = pageLimit;
|
||||
}
|
||||
|
||||
if (pageOffset !== undefined) {
|
||||
localVarQueryParameter['page[offset]'] = pageOffset;
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Returns paginated verification criteria with their controls and performance metrics. Each criterion contains controls; retrieve a specific control via `/api/v1/verification/controls/{id}`.
|
||||
* @summary List Verification Criteria
|
||||
* @param {number} [pageLimit] Maximum number of items to return per page.
|
||||
* @param {number} [pageOffset] Number of items to skip before returning results.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
listVerificationCriteria: async (pageLimit?: number, pageOffset?: number, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
const localVarPath = `/api/v1/verification/criteria`;
|
||||
// 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 mTLS required
|
||||
await setApiKeyToObject(localVarHeaderParameter, "X-mTLS-Client-CN", configuration)
|
||||
|
||||
// authentication BearerAuth required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
if (pageLimit !== undefined) {
|
||||
localVarQueryParameter['page[limit]'] = pageLimit;
|
||||
}
|
||||
|
||||
if (pageOffset !== undefined) {
|
||||
localVarQueryParameter['page[offset]'] = pageOffset;
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Returns a specific signoff by ID.
|
||||
* @summary Retrieve Signoff
|
||||
* @param {string} id Unique identifier of a signoff (ULID).
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
retrieveSignoff: async (id: string, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'id' is not null or undefined
|
||||
assertParamExists('retrieveSignoff', 'id', id)
|
||||
const localVarPath = `/api/v1/verification/signoffs/{id}`
|
||||
.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 mTLS required
|
||||
await setApiKeyToObject(localVarHeaderParameter, "X-mTLS-Client-CN", configuration)
|
||||
|
||||
// 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,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Returns detailed information about a specific verification control, including performance data, recent results, and sibling controls in the same criterion.
|
||||
* @summary Retrieve Verification Control
|
||||
* @param {string} id URL-safe slug identifying a verification control.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
retrieveVerificationControl: async (id: string, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'id' is not null or undefined
|
||||
assertParamExists('retrieveVerificationControl', 'id', id)
|
||||
const localVarPath = `/api/v1/verification/controls/{id}`
|
||||
.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 mTLS required
|
||||
await setApiKeyToObject(localVarHeaderParameter, "X-mTLS-Client-CN", configuration)
|
||||
|
||||
// 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,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Returns a specific verification criterion with its controls and performance metrics.
|
||||
* @summary Retrieve Verification Criterion
|
||||
* @param {string} id URL-safe slug identifying a verification criterion.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
retrieveVerificationCriterion: async (id: string, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'id' is not null or undefined
|
||||
assertParamExists('retrieveVerificationCriterion', 'id', id)
|
||||
const localVarPath = `/api/v1/verification/criteria/{id}`
|
||||
.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 mTLS required
|
||||
await setApiKeyToObject(localVarHeaderParameter, "X-mTLS-Client-CN", configuration)
|
||||
|
||||
// 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,
|
||||
};
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* VerificationApi - functional programming interface
|
||||
*/
|
||||
export const VerificationApiFp = function(configuration?: Configuration) {
|
||||
const localVarAxiosParamCreator = VerificationApiAxiosParamCreator(configuration)
|
||||
return {
|
||||
/**
|
||||
* Creates a new signoff for a (control, repository, commit SHA) tuple. Multiple signoffs are allowed per tuple; the latest one wins for display purposes.
|
||||
* @summary Create Signoff
|
||||
* @param {CreateSignoffRequest} createSignoffRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async createSignoff(createSignoffRequest: CreateSignoffRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Signoff>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.createSignoff(createSignoffRequest, options);
|
||||
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||
const localVarOperationServerBasePath = operationServerMap['VerificationApi.createSignoff']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
* Returns a paginated list of signoffs, optionally filtered by control, repository, and/or commit SHA.
|
||||
* @summary List Signoffs
|
||||
* @param {string} [control] Filter signoffs by control slug.
|
||||
* @param {string} [repository] Filter signoffs by repository name.
|
||||
* @param {string} [commitSha] Filter signoffs by commit SHA.
|
||||
* @param {number} [pageLimit] Maximum number of items to return per page.
|
||||
* @param {number} [pageOffset] Number of items to skip before returning results.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async listSignoffs(control?: string, repository?: string, commitSha?: string, pageLimit?: number, pageOffset?: number, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<PaginatedSignoffList>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.listSignoffs(control, repository, commitSha, pageLimit, pageOffset, options);
|
||||
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||
const localVarOperationServerBasePath = operationServerMap['VerificationApi.listSignoffs']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
* Returns a flat paginated list of all verification controls across all criteria.
|
||||
* @summary List Verification Controls
|
||||
* @param {number} [pageLimit] Maximum number of items to return per page.
|
||||
* @param {number} [pageOffset] Number of items to skip before returning results.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async listVerificationControls(pageLimit?: number, pageOffset?: number, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<PaginatedVerificationControlList>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.listVerificationControls(pageLimit, pageOffset, options);
|
||||
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||
const localVarOperationServerBasePath = operationServerMap['VerificationApi.listVerificationControls']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
* Returns paginated verification criteria with their controls and performance metrics. Each criterion contains controls; retrieve a specific control via `/api/v1/verification/controls/{id}`.
|
||||
* @summary List Verification Criteria
|
||||
* @param {number} [pageLimit] Maximum number of items to return per page.
|
||||
* @param {number} [pageOffset] Number of items to skip before returning results.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async listVerificationCriteria(pageLimit?: number, pageOffset?: number, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<PaginatedVerificationCriterionList>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.listVerificationCriteria(pageLimit, pageOffset, options);
|
||||
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||
const localVarOperationServerBasePath = operationServerMap['VerificationApi.listVerificationCriteria']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
* Returns a specific signoff by ID.
|
||||
* @summary Retrieve Signoff
|
||||
* @param {string} id Unique identifier of a signoff (ULID).
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async retrieveSignoff(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Signoff>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.retrieveSignoff(id, options);
|
||||
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||
const localVarOperationServerBasePath = operationServerMap['VerificationApi.retrieveSignoff']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
* Returns detailed information about a specific verification control, including performance data, recent results, and sibling controls in the same criterion.
|
||||
* @summary Retrieve Verification Control
|
||||
* @param {string} id URL-safe slug identifying a verification control.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async retrieveVerificationControl(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<VerificationDetailResponse>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.retrieveVerificationControl(id, options);
|
||||
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||
const localVarOperationServerBasePath = operationServerMap['VerificationApi.retrieveVerificationControl']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
* Returns a specific verification criterion with its controls and performance metrics.
|
||||
* @summary Retrieve Verification Criterion
|
||||
* @param {string} id URL-safe slug identifying a verification criterion.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async retrieveVerificationCriterion(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<VerificationCriterionDetail>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.retrieveVerificationCriterion(id, options);
|
||||
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||
const localVarOperationServerBasePath = operationServerMap['VerificationApi.retrieveVerificationCriterion']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* VerificationApi - factory interface
|
||||
*/
|
||||
export const VerificationApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
|
||||
const localVarFp = VerificationApiFp(configuration)
|
||||
return {
|
||||
/**
|
||||
* Creates a new signoff for a (control, repository, commit SHA) tuple. Multiple signoffs are allowed per tuple; the latest one wins for display purposes.
|
||||
* @summary Create Signoff
|
||||
* @param {CreateSignoffRequest} createSignoffRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
createSignoff(createSignoffRequest: CreateSignoffRequest, options?: RawAxiosRequestConfig): AxiosPromise<Signoff> {
|
||||
return localVarFp.createSignoff(createSignoffRequest, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
* Returns a paginated list of signoffs, optionally filtered by control, repository, and/or commit SHA.
|
||||
* @summary List Signoffs
|
||||
* @param {string} [control] Filter signoffs by control slug.
|
||||
* @param {string} [repository] Filter signoffs by repository name.
|
||||
* @param {string} [commitSha] Filter signoffs by commit SHA.
|
||||
* @param {number} [pageLimit] Maximum number of items to return per page.
|
||||
* @param {number} [pageOffset] Number of items to skip before returning results.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
listSignoffs(control?: string, repository?: string, commitSha?: string, pageLimit?: number, pageOffset?: number, options?: RawAxiosRequestConfig): AxiosPromise<PaginatedSignoffList> {
|
||||
return localVarFp.listSignoffs(control, repository, commitSha, pageLimit, pageOffset, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
* Returns a flat paginated list of all verification controls across all criteria.
|
||||
* @summary List Verification Controls
|
||||
* @param {number} [pageLimit] Maximum number of items to return per page.
|
||||
* @param {number} [pageOffset] Number of items to skip before returning results.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
listVerificationControls(pageLimit?: number, pageOffset?: number, options?: RawAxiosRequestConfig): AxiosPromise<PaginatedVerificationControlList> {
|
||||
return localVarFp.listVerificationControls(pageLimit, pageOffset, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
* Returns paginated verification criteria with their controls and performance metrics. Each criterion contains controls; retrieve a specific control via `/api/v1/verification/controls/{id}`.
|
||||
* @summary List Verification Criteria
|
||||
* @param {number} [pageLimit] Maximum number of items to return per page.
|
||||
* @param {number} [pageOffset] Number of items to skip before returning results.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
listVerificationCriteria(pageLimit?: number, pageOffset?: number, options?: RawAxiosRequestConfig): AxiosPromise<PaginatedVerificationCriterionList> {
|
||||
return localVarFp.listVerificationCriteria(pageLimit, pageOffset, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
* Returns a specific signoff by ID.
|
||||
* @summary Retrieve Signoff
|
||||
* @param {string} id Unique identifier of a signoff (ULID).
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
retrieveSignoff(id: string, options?: RawAxiosRequestConfig): AxiosPromise<Signoff> {
|
||||
return localVarFp.retrieveSignoff(id, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
* Returns detailed information about a specific verification control, including performance data, recent results, and sibling controls in the same criterion.
|
||||
* @summary Retrieve Verification Control
|
||||
* @param {string} id URL-safe slug identifying a verification control.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
retrieveVerificationControl(id: string, options?: RawAxiosRequestConfig): AxiosPromise<VerificationDetailResponse> {
|
||||
return localVarFp.retrieveVerificationControl(id, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
* Returns a specific verification criterion with its controls and performance metrics.
|
||||
* @summary Retrieve Verification Criterion
|
||||
* @param {string} id URL-safe slug identifying a verification criterion.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
retrieveVerificationCriterion(id: string, options?: RawAxiosRequestConfig): AxiosPromise<VerificationCriterionDetail> {
|
||||
return localVarFp.retrieveVerificationCriterion(id, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* VerificationApi - object-oriented interface
|
||||
*/
|
||||
export class VerificationApi extends BaseAPI {
|
||||
/**
|
||||
* Creates a new signoff for a (control, repository, commit SHA) tuple. Multiple signoffs are allowed per tuple; the latest one wins for display purposes.
|
||||
* @summary Create Signoff
|
||||
* @param {CreateSignoffRequest} createSignoffRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
public createSignoff(createSignoffRequest: CreateSignoffRequest, options?: RawAxiosRequestConfig) {
|
||||
return VerificationApiFp(this.configuration).createSignoff(createSignoffRequest, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a paginated list of signoffs, optionally filtered by control, repository, and/or commit SHA.
|
||||
* @summary List Signoffs
|
||||
* @param {string} [control] Filter signoffs by control slug.
|
||||
* @param {string} [repository] Filter signoffs by repository name.
|
||||
* @param {string} [commitSha] Filter signoffs by commit SHA.
|
||||
* @param {number} [pageLimit] Maximum number of items to return per page.
|
||||
* @param {number} [pageOffset] Number of items to skip before returning results.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
public listSignoffs(control?: string, repository?: string, commitSha?: string, pageLimit?: number, pageOffset?: number, options?: RawAxiosRequestConfig) {
|
||||
return VerificationApiFp(this.configuration).listSignoffs(control, repository, commitSha, pageLimit, pageOffset, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a flat paginated list of all verification controls across all criteria.
|
||||
* @summary List Verification Controls
|
||||
* @param {number} [pageLimit] Maximum number of items to return per page.
|
||||
* @param {number} [pageOffset] Number of items to skip before returning results.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
public listVerificationControls(pageLimit?: number, pageOffset?: number, options?: RawAxiosRequestConfig) {
|
||||
return VerificationApiFp(this.configuration).listVerificationControls(pageLimit, pageOffset, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns paginated verification criteria with their controls and performance metrics. Each criterion contains controls; retrieve a specific control via `/api/v1/verification/controls/{id}`.
|
||||
* @summary List Verification Criteria
|
||||
* @param {number} [pageLimit] Maximum number of items to return per page.
|
||||
* @param {number} [pageOffset] Number of items to skip before returning results.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
public listVerificationCriteria(pageLimit?: number, pageOffset?: number, options?: RawAxiosRequestConfig) {
|
||||
return VerificationApiFp(this.configuration).listVerificationCriteria(pageLimit, pageOffset, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specific signoff by ID.
|
||||
* @summary Retrieve Signoff
|
||||
* @param {string} id Unique identifier of a signoff (ULID).
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
public retrieveSignoff(id: string, options?: RawAxiosRequestConfig) {
|
||||
return VerificationApiFp(this.configuration).retrieveSignoff(id, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns detailed information about a specific verification control, including performance data, recent results, and sibling controls in the same criterion.
|
||||
* @summary Retrieve Verification Control
|
||||
* @param {string} id URL-safe slug identifying a verification control.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
public retrieveVerificationControl(id: string, options?: RawAxiosRequestConfig) {
|
||||
return VerificationApiFp(this.configuration).retrieveVerificationControl(id, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specific verification criterion with its controls and performance metrics.
|
||||
* @summary Retrieve Verification Criterion
|
||||
* @param {string} id URL-safe slug identifying a verification criterion.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
public retrieveVerificationCriterion(id: string, options?: RawAxiosRequestConfig) {
|
||||
return VerificationApiFp(this.configuration).retrieveVerificationCriterion(id, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* An assistant response turn.
|
||||
*/
|
||||
export interface AssistantTurn {
|
||||
'kind': AssistantTurnKindEnum;
|
||||
/**
|
||||
* Text content of the assistant response.
|
||||
*/
|
||||
'content': string;
|
||||
/**
|
||||
* Timestamp when the turn was created.
|
||||
*/
|
||||
'created_at': string;
|
||||
}
|
||||
|
||||
export const AssistantTurnKindEnum = {
|
||||
ASSISTANT: 'assistant'
|
||||
} as const;
|
||||
|
||||
export type AssistantTurnKindEnum = typeof AssistantTurnKindEnum[keyof typeof AssistantTurnKindEnum];
|
||||
|
||||
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Detailed information about a verification control including checks and examples.
|
||||
*/
|
||||
export interface ControlDetail {
|
||||
/**
|
||||
* Detailed prose description of the control\'s purpose and rationale.
|
||||
*/
|
||||
'rationale': string;
|
||||
/**
|
||||
* Specific checks performed by this control.
|
||||
*/
|
||||
'checks': Array<string>;
|
||||
/**
|
||||
* Example scenario where the control passes.
|
||||
*/
|
||||
'pass_example': string;
|
||||
/**
|
||||
* Example scenario where the control fails.
|
||||
*/
|
||||
'fail_example': string;
|
||||
}
|
||||
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { CriterionReference } from './criterion-reference';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { VerificationType } from './verification-type';
|
||||
|
||||
/**
|
||||
* Core metadata about a verification control.
|
||||
*/
|
||||
export interface ControlInfo {
|
||||
/**
|
||||
* Human-readable control name.
|
||||
*/
|
||||
'name': string;
|
||||
/**
|
||||
* URL-safe slug.
|
||||
*/
|
||||
'slug': string;
|
||||
/**
|
||||
* Short description of what the control verifies.
|
||||
*/
|
||||
'description': string;
|
||||
'type'?: VerificationType;
|
||||
'criterion': CriterionReference;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { VerificationMode } from './verification-mode';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { VerificationResult } from './verification-result';
|
||||
|
||||
/**
|
||||
* Performance metrics for a verification control.
|
||||
*/
|
||||
export interface ControlPerformance {
|
||||
'mode': VerificationMode;
|
||||
/**
|
||||
* F1 score of the control\'s AI evaluator.
|
||||
*/
|
||||
'f1'?: number;
|
||||
/**
|
||||
* Pass@1 rate.
|
||||
*/
|
||||
'pass_at_1'?: number;
|
||||
/**
|
||||
* Recent evaluation results (newest first).
|
||||
*/
|
||||
'evaluations': Array<VerificationResult>;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Reference to a verification control by slug.
|
||||
*/
|
||||
export interface ControlReference {
|
||||
/**
|
||||
* Control slug.
|
||||
*/
|
||||
'slug': string;
|
||||
}
|
||||
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Request body for starting a new session.
|
||||
*/
|
||||
export interface CreateSessionRequest {
|
||||
/**
|
||||
* The initial user message to start the session.
|
||||
*/
|
||||
'content': string;
|
||||
/**
|
||||
* LLM model to use. If omitted, the server default is used.
|
||||
*/
|
||||
'model'?: string;
|
||||
/**
|
||||
* System prompt for the session.
|
||||
*/
|
||||
'system'?: string;
|
||||
}
|
||||
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { ModelReference } from './model-reference';
|
||||
|
||||
/**
|
||||
* Response returned after successfully creating a session.
|
||||
*/
|
||||
export interface CreateSessionResponse {
|
||||
/**
|
||||
* Unique identifier for the newly created session.
|
||||
*/
|
||||
'id': string;
|
||||
/**
|
||||
* Server-generated title for the session.
|
||||
*/
|
||||
'title': string;
|
||||
'model': ModelReference;
|
||||
/**
|
||||
* Timestamp when the session was created.
|
||||
*/
|
||||
'created_at': string;
|
||||
/**
|
||||
* Timestamp when the session was last updated (equal to created_at at creation time).
|
||||
*/
|
||||
'updated_at': string;
|
||||
}
|
||||
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { SignoffStatus } from './signoff-status';
|
||||
|
||||
/**
|
||||
* Request body to create a new signoff.
|
||||
*/
|
||||
export interface CreateSignoffRequest {
|
||||
/**
|
||||
* Control slug or ID.
|
||||
*/
|
||||
'control': string;
|
||||
/**
|
||||
* Repository name.
|
||||
*/
|
||||
'repository': string;
|
||||
/**
|
||||
* Git commit SHA.
|
||||
*/
|
||||
'commit_sha': string;
|
||||
'status': SignoffStatus;
|
||||
/**
|
||||
* Optional URL with more details.
|
||||
*/
|
||||
'url'?: string;
|
||||
/**
|
||||
* Optional human-readable description.
|
||||
*/
|
||||
'description'?: string;
|
||||
/**
|
||||
* Freeform string identifying the logical origin.
|
||||
*/
|
||||
'source'?: string;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Reference to a verification criterion by name.
|
||||
*/
|
||||
export interface CriterionReference {
|
||||
/**
|
||||
* Criterion name.
|
||||
*/
|
||||
'name': string;
|
||||
}
|
||||
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Type of friction encountered during a run.
|
||||
*/
|
||||
|
||||
export const FrictionKind = {
|
||||
RETRY: 'retry',
|
||||
TIMEOUT: 'timeout',
|
||||
WRONG_APPROACH: 'wrong_approach',
|
||||
TOOL_FAILURE: 'tool_failure',
|
||||
AMBIGUITY: 'ambiguity'
|
||||
} as const;
|
||||
|
||||
export type FrictionKind = typeof FrictionKind[keyof typeof FrictionKind];
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { FrictionKind } from './friction-kind';
|
||||
|
||||
/**
|
||||
* A point where the run encountered difficulty.
|
||||
*/
|
||||
export interface FrictionPoint {
|
||||
'kind': FrictionKind;
|
||||
/**
|
||||
* Description of the friction encountered.
|
||||
*/
|
||||
'description': string;
|
||||
/**
|
||||
* Stage where the friction occurred, if applicable.
|
||||
*/
|
||||
'stage_id'?: string;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -8,7 +8,6 @@ export * from './artifact-entry';
|
|||
export * from './artifact-list-response';
|
||||
export * from './artifacts-settings';
|
||||
export * from './assistant-stage-turn';
|
||||
export * from './assistant-turn';
|
||||
export * from './auth-settings';
|
||||
export * from './board-column';
|
||||
export * from './check-run';
|
||||
|
|
@ -21,15 +20,7 @@ export * from './completion-response';
|
|||
export * from './completion-tool-choice';
|
||||
export * from './completion-tool-definition';
|
||||
export * from './completion-usage';
|
||||
export * from './control-detail';
|
||||
export * from './control-info';
|
||||
export * from './control-performance';
|
||||
export * from './control-reference';
|
||||
export * from './create-completion-request';
|
||||
export * from './create-session-request';
|
||||
export * from './create-session-response';
|
||||
export * from './create-signoff-request';
|
||||
export * from './criterion-reference';
|
||||
export * from './daytona-settings';
|
||||
export * from './daytona-settings-network';
|
||||
export * from './daytona-settings-network-one-of';
|
||||
|
|
@ -52,8 +43,6 @@ export * from './execute-query-response-rows-inner-inner';
|
|||
export * from './features';
|
||||
export * from './file-checkpoint';
|
||||
export * from './file-diff';
|
||||
export * from './friction-kind';
|
||||
export * from './friction-point';
|
||||
export * from './git-author-settings';
|
||||
export * from './git-hub-settings';
|
||||
export * from './git-settings';
|
||||
|
|
@ -62,8 +51,6 @@ export * from './history-entry';
|
|||
export * from './hook-definition';
|
||||
export * from './internal-run-status';
|
||||
export * from './internal-stage-status';
|
||||
export * from './learning';
|
||||
export * from './learning-category';
|
||||
export * from './llm-settings';
|
||||
export * from './local-sandbox-settings';
|
||||
export * from './log-settings';
|
||||
|
|
@ -86,22 +73,15 @@ export * from './model-test-mode';
|
|||
export * from './model-test-result';
|
||||
export * from './node-state';
|
||||
export * from './node-status-record';
|
||||
export * from './open-item';
|
||||
export * from './open-item-kind';
|
||||
export * from './paginated-api-question-list';
|
||||
export * from './paginated-event-list';
|
||||
export * from './paginated-history-entry-list';
|
||||
export * from './paginated-model-list';
|
||||
export * from './paginated-retro-list';
|
||||
export * from './paginated-run-file-list';
|
||||
export * from './paginated-run-list';
|
||||
export * from './paginated-run-stage-list';
|
||||
export * from './paginated-saved-query-list';
|
||||
export * from './paginated-session-list';
|
||||
export * from './paginated-signoff-list';
|
||||
export * from './paginated-stage-turn-list';
|
||||
export * from './paginated-verification-control-list';
|
||||
export * from './paginated-verification-criterion-list';
|
||||
export * from './paginated-workflow-list';
|
||||
export * from './pagination-meta';
|
||||
export * from './preflight-check-detail';
|
||||
|
|
@ -117,16 +97,12 @@ export * from './prune-runs-request';
|
|||
export * from './prune-runs-response';
|
||||
export * from './pull-request-settings';
|
||||
export * from './question-type';
|
||||
export * from './recent-control-result';
|
||||
export * from './render-workflow-graph-direction';
|
||||
export * from './render-workflow-graph-format';
|
||||
export * from './render-workflow-graph-request';
|
||||
export * from './repo-check-response';
|
||||
export * from './repo-check-response-permissions';
|
||||
export * from './repository-reference';
|
||||
export * from './retro-detail';
|
||||
export * from './retro-list-item';
|
||||
export * from './retro-stats';
|
||||
export * from './root-response';
|
||||
export * from './root-response-urls';
|
||||
export * from './run-artifact-entry';
|
||||
|
|
@ -157,25 +133,15 @@ export * from './save-query-request';
|
|||
export * from './saved-query';
|
||||
export * from './secret-list-response';
|
||||
export * from './secret-metadata';
|
||||
export * from './send-message-request';
|
||||
export * from './send-message-response';
|
||||
export * from './server-settings';
|
||||
export * from './server-settings-exec';
|
||||
export * from './server-settings-fabro';
|
||||
export * from './server-settings-server';
|
||||
export * from './server-settings-server-tls';
|
||||
export * from './session-detail';
|
||||
export * from './session-list-item';
|
||||
export * from './session-turn';
|
||||
export * from './set-secret-request';
|
||||
export * from './setup-settings';
|
||||
export * from './sibling-control';
|
||||
export * from './signoff';
|
||||
export * from './signoff-status';
|
||||
export * from './smoothness-rating';
|
||||
export * from './ssh-access-request';
|
||||
export * from './ssh-access-response';
|
||||
export * from './stage-retro';
|
||||
export * from './stage-status';
|
||||
export * from './stage-turn';
|
||||
export * from './start-run-request';
|
||||
|
|
@ -189,22 +155,12 @@ export * from './system-stage-turn';
|
|||
export * from './tls-settings';
|
||||
export * from './token-usage';
|
||||
export * from './tool-stage-turn';
|
||||
export * from './tool-turn';
|
||||
export * from './tool-use';
|
||||
export * from './usage-by-model';
|
||||
export * from './usage-stage';
|
||||
export * from './usage-stage-ref';
|
||||
export * from './usage-totals';
|
||||
export * from './user-response';
|
||||
export * from './user-turn';
|
||||
export * from './verification-control';
|
||||
export * from './verification-control-list-item';
|
||||
export * from './verification-criterion';
|
||||
export * from './verification-criterion-detail';
|
||||
export * from './verification-detail-response';
|
||||
export * from './verification-mode';
|
||||
export * from './verification-result';
|
||||
export * from './verification-type';
|
||||
export * from './web-settings';
|
||||
export * from './webhook-settings';
|
||||
export * from './workflow-detail';
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Category of a learning insight.
|
||||
*/
|
||||
|
||||
export const LearningCategory = {
|
||||
REPO: 'repo',
|
||||
CODE: 'code',
|
||||
WORKFLOW: 'workflow',
|
||||
TOOL: 'tool'
|
||||
} as const;
|
||||
|
||||
export type LearningCategory = typeof LearningCategory[keyof typeof LearningCategory];
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { LearningCategory } from './learning-category';
|
||||
|
||||
/**
|
||||
* An insight discovered during the run.
|
||||
*/
|
||||
export interface Learning {
|
||||
'category': LearningCategory;
|
||||
/**
|
||||
* Description of the learning.
|
||||
*/
|
||||
'text': string;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Type of open item identified during a run.
|
||||
*/
|
||||
|
||||
export const OpenItemKind = {
|
||||
TECH_DEBT: 'tech_debt',
|
||||
FOLLOW_UP: 'follow_up',
|
||||
INVESTIGATION: 'investigation',
|
||||
TEST_GAP: 'test_gap'
|
||||
} as const;
|
||||
|
||||
export type OpenItemKind = typeof OpenItemKind[keyof typeof OpenItemKind];
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { OpenItemKind } from './open-item-kind';
|
||||
|
||||
/**
|
||||
* A follow-up item identified during the run.
|
||||
*/
|
||||
export interface OpenItem {
|
||||
'kind': OpenItemKind;
|
||||
/**
|
||||
* Description of the open item.
|
||||
*/
|
||||
'description': string;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { PaginationMeta } from './pagination-meta';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { RetroListItem } from './retro-list-item';
|
||||
|
||||
/**
|
||||
* Paginated list of run retrospectives.
|
||||
*/
|
||||
export interface PaginatedRetroList {
|
||||
'data': Array<RetroListItem>;
|
||||
'meta': PaginationMeta;
|
||||
}
|
||||
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { PaginationMeta } from './pagination-meta';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { RunVerification } from './run-verification';
|
||||
|
||||
/**
|
||||
* Paginated list of run verification categories.
|
||||
*/
|
||||
export interface PaginatedRunVerificationList {
|
||||
'data': Array<RunVerification>;
|
||||
'meta': PaginationMeta;
|
||||
}
|
||||
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { PaginationMeta } from './pagination-meta';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { SessionListItem } from './session-list-item';
|
||||
|
||||
/**
|
||||
* Paginated list of sessions.
|
||||
*/
|
||||
export interface PaginatedSessionList {
|
||||
'data': Array<SessionListItem>;
|
||||
'meta': PaginationMeta;
|
||||
}
|
||||
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { PaginationMeta } from './pagination-meta';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { Signoff } from './signoff';
|
||||
|
||||
/**
|
||||
* Paginated list of signoffs.
|
||||
*/
|
||||
export interface PaginatedSignoffList {
|
||||
'data': Array<Signoff>;
|
||||
'meta': PaginationMeta;
|
||||
}
|
||||
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { PaginationMeta } from './pagination-meta';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { VerificationControlListItem } from './verification-control-list-item';
|
||||
|
||||
/**
|
||||
* Paginated list of verification controls.
|
||||
*/
|
||||
export interface PaginatedVerificationControlList {
|
||||
'data': Array<VerificationControlListItem>;
|
||||
'meta': PaginationMeta;
|
||||
}
|
||||
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { PaginationMeta } from './pagination-meta';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { VerificationCriterion } from './verification-criterion';
|
||||
|
||||
/**
|
||||
* Paginated list of verification criteria.
|
||||
*/
|
||||
export interface PaginatedVerificationCriterionList {
|
||||
'data': Array<VerificationCriterion>;
|
||||
'meta': PaginationMeta;
|
||||
}
|
||||
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { RunReference } from './run-reference';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { VerificationResult } from './verification-result';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { WorkflowReference } from './workflow-reference';
|
||||
|
||||
/**
|
||||
* Result of a recent verification control evaluation for a specific run.
|
||||
*/
|
||||
export interface RecentControlResult {
|
||||
'run': RunReference;
|
||||
'workflow': WorkflowReference;
|
||||
'result': VerificationResult;
|
||||
/**
|
||||
* ISO 8601 timestamp of the evaluation.
|
||||
*/
|
||||
'timestamp': string;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { FrictionPoint } from './friction-point';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { Learning } from './learning';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { OpenItem } from './open-item';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { RetroStats } from './retro-stats';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { SmoothnessRating } from './smoothness-rating';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { StageRetro } from './stage-retro';
|
||||
|
||||
/**
|
||||
* Full retrospective analysis for a completed run.
|
||||
*/
|
||||
export interface RetroDetail {
|
||||
/**
|
||||
* Unique run identifier.
|
||||
*/
|
||||
'run_id': string;
|
||||
/**
|
||||
* Workflow slug that produced this run.
|
||||
*/
|
||||
'workflow_name': string;
|
||||
/**
|
||||
* The goal that was set for the run.
|
||||
*/
|
||||
'goal': string;
|
||||
/**
|
||||
* ISO 8601 timestamp when the retro was generated.
|
||||
*/
|
||||
'timestamp': string;
|
||||
/**
|
||||
* Absent when the retro has been generated from quantitative data but not yet enriched by the retro agent.
|
||||
*/
|
||||
'smoothness'?: SmoothnessRating;
|
||||
/**
|
||||
* Per-stage retrospective data.
|
||||
*/
|
||||
'stages': Array<StageRetro>;
|
||||
'stats': RetroStats;
|
||||
/**
|
||||
* What the agent intended to accomplish.
|
||||
*/
|
||||
'intent'?: string;
|
||||
/**
|
||||
* What actually happened during the run.
|
||||
*/
|
||||
'outcome'?: string;
|
||||
/**
|
||||
* Insights discovered during the run.
|
||||
*/
|
||||
'learnings'?: Array<Learning>;
|
||||
/**
|
||||
* Points where the run encountered difficulty.
|
||||
*/
|
||||
'friction_points'?: Array<FrictionPoint>;
|
||||
/**
|
||||
* Follow-up items identified during the run.
|
||||
*/
|
||||
'open_items'?: Array<OpenItem>;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { RetroStats } from './retro-stats';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { RunReference } from './run-reference';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { SmoothnessRating } from './smoothness-rating';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { WorkflowReference } from './workflow-reference';
|
||||
|
||||
/**
|
||||
* Summary of a run retrospective shown in list views.
|
||||
*/
|
||||
export interface RetroListItem {
|
||||
'run': RunReference;
|
||||
'workflow': WorkflowReference;
|
||||
/**
|
||||
* Timestamp when the retro was generated.
|
||||
*/
|
||||
'timestamp': string;
|
||||
/**
|
||||
* Absent when the retro has been generated from quantitative data but not yet enriched by the retro agent.
|
||||
*/
|
||||
'smoothness'?: SmoothnessRating;
|
||||
'stats': RetroStats;
|
||||
/**
|
||||
* Number of friction points identified in the retro.
|
||||
*/
|
||||
'friction_point_count': number;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Summary statistics for a run retrospective.
|
||||
*/
|
||||
export interface RetroStats {
|
||||
/**
|
||||
* Total run duration in milliseconds.
|
||||
*/
|
||||
'total_duration_ms': number;
|
||||
/**
|
||||
* Total cost in USD. Absent when cost data is unavailable from the model provider.
|
||||
*/
|
||||
'total_cost'?: number;
|
||||
/**
|
||||
* Total number of retries across all stages.
|
||||
*/
|
||||
'total_retries': number;
|
||||
/**
|
||||
* List of files modified during the run.
|
||||
*/
|
||||
'files_touched': Array<string>;
|
||||
/**
|
||||
* Number of stages that completed successfully.
|
||||
*/
|
||||
'stages_completed': number;
|
||||
/**
|
||||
* Number of stages that failed.
|
||||
*/
|
||||
'stages_failed': number;
|
||||
}
|
||||
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { VerificationResult } from './verification-result';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { VerificationType } from './verification-type';
|
||||
|
||||
/**
|
||||
* A verification control result within a run.
|
||||
*/
|
||||
export interface RunVerificationControl {
|
||||
/**
|
||||
* Human-readable control name.
|
||||
*/
|
||||
'name': string;
|
||||
/**
|
||||
* URL-safe slug for linking to verification detail page.
|
||||
*/
|
||||
'slug': string;
|
||||
/**
|
||||
* Short description of what the control verifies.
|
||||
*/
|
||||
'description': string;
|
||||
'type': VerificationType;
|
||||
'status': VerificationResult;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { RunVerificationControl } from './run-verification-control';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { VerificationResult } from './verification-result';
|
||||
|
||||
/**
|
||||
* Verification results for a category within a run.
|
||||
*/
|
||||
export interface RunVerification {
|
||||
/**
|
||||
* Category name.
|
||||
*/
|
||||
'name': string;
|
||||
/**
|
||||
* The guiding question for this verification category.
|
||||
*/
|
||||
'question': string;
|
||||
'status': VerificationResult;
|
||||
/**
|
||||
* Individual control results within this category.
|
||||
*/
|
||||
'controls': Array<RunVerificationControl>;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Request body for sending a follow-up message in an existing session.
|
||||
*/
|
||||
export interface SendMessageRequest {
|
||||
/**
|
||||
* The user message text.
|
||||
*/
|
||||
'content': string;
|
||||
}
|
||||
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Acknowledgement that the message was accepted for asynchronous processing.
|
||||
*/
|
||||
export interface SendMessageResponse {
|
||||
/**
|
||||
* Whether the message was accepted for processing.
|
||||
*/
|
||||
'accepted': boolean;
|
||||
}
|
||||
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { ModelReference } from './model-reference';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { SessionTurn } from './session-turn';
|
||||
|
||||
/**
|
||||
* Full session record including metadata and the complete conversation history.
|
||||
*/
|
||||
export interface SessionDetail {
|
||||
/**
|
||||
* Unique session identifier.
|
||||
*/
|
||||
'id': string;
|
||||
/**
|
||||
* Short title summarizing the session topic.
|
||||
*/
|
||||
'title': string;
|
||||
'model': ModelReference;
|
||||
/**
|
||||
* Timestamp when the session was created.
|
||||
*/
|
||||
'created_at': string;
|
||||
/**
|
||||
* Timestamp when the session was last updated (e.g. new turn added).
|
||||
*/
|
||||
'updated_at': string;
|
||||
/**
|
||||
* Ordered list of conversation turns.
|
||||
*/
|
||||
'turns': Array<SessionTurn>;
|
||||
}
|
||||
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { ModelReference } from './model-reference';
|
||||
|
||||
/**
|
||||
* Summary of a session shown in list views.
|
||||
*/
|
||||
export interface SessionListItem {
|
||||
/**
|
||||
* Unique session identifier.
|
||||
*/
|
||||
'id': string;
|
||||
/**
|
||||
* Short title summarizing the session topic.
|
||||
*/
|
||||
'title': string;
|
||||
'model': ModelReference;
|
||||
/**
|
||||
* Truncated snippet of the most recent turn\'s content.
|
||||
*/
|
||||
'last_message_preview': string;
|
||||
/**
|
||||
* Timestamp when the session was created.
|
||||
*/
|
||||
'created_at': string;
|
||||
/**
|
||||
* Timestamp when the session was last updated (e.g. new turn added).
|
||||
*/
|
||||
'updated_at': string;
|
||||
}
|
||||
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { AssistantTurn } from './assistant-turn';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { ToolTurn } from './tool-turn';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { ToolUse } from './tool-use';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { UserTurn } from './user-turn';
|
||||
|
||||
/**
|
||||
* @type SessionTurn
|
||||
* A single turn in a session conversation — a user message, assistant response, or tool invocation block.
|
||||
*/
|
||||
export type SessionTurn = { kind: 'assistant' } & AssistantTurn | { kind: 'tool' } & ToolTurn | { kind: 'user' } & UserTurn;
|
||||
|
||||
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { VerificationMode } from './verification-mode';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { VerificationType } from './verification-type';
|
||||
|
||||
/**
|
||||
* Summary of a sibling verification control in the same category.
|
||||
*/
|
||||
export interface SiblingControl {
|
||||
/**
|
||||
* Human-readable control name.
|
||||
*/
|
||||
'name': string;
|
||||
/**
|
||||
* URL-safe slug.
|
||||
*/
|
||||
'slug': string;
|
||||
'type'?: VerificationType;
|
||||
'mode'?: VerificationMode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Status of a signoff.
|
||||
*/
|
||||
|
||||
export const SignoffStatus = {
|
||||
PASS: 'pass',
|
||||
FAIL: 'fail',
|
||||
PENDING: 'pending'
|
||||
} as const;
|
||||
|
||||
export type SignoffStatus = typeof SignoffStatus[keyof typeof SignoffStatus];
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { ControlReference } from './control-reference';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { RepositoryReference } from './repository-reference';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { SignoffStatus } from './signoff-status';
|
||||
|
||||
/**
|
||||
* A stamp of approval for a (control, repository, commit SHA) tuple.
|
||||
*/
|
||||
export interface Signoff {
|
||||
/**
|
||||
* Unique identifier (ULID).
|
||||
*/
|
||||
'id': string;
|
||||
'control': ControlReference;
|
||||
'repository': RepositoryReference;
|
||||
/**
|
||||
* Git commit SHA this signoff applies to.
|
||||
*/
|
||||
'commit_sha': string;
|
||||
'status': SignoffStatus;
|
||||
/**
|
||||
* Optional URL with more details about the signoff.
|
||||
*/
|
||||
'url'?: string;
|
||||
/**
|
||||
* Optional human-readable description.
|
||||
*/
|
||||
'description'?: string;
|
||||
/**
|
||||
* Freeform string identifying the logical origin of the signoff.
|
||||
*/
|
||||
'source'?: string;
|
||||
/**
|
||||
* When the signoff was created.
|
||||
*/
|
||||
'created_at': string;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Qualitative assessment of how smoothly a run executed.
|
||||
*/
|
||||
|
||||
export const SmoothnessRating = {
|
||||
EFFORTLESS: 'effortless',
|
||||
SMOOTH: 'smooth',
|
||||
BUMPY: 'bumpy',
|
||||
STRUGGLED: 'struggled',
|
||||
FAILED: 'failed'
|
||||
} as const;
|
||||
|
||||
export type SmoothnessRating = typeof SmoothnessRating[keyof typeof SmoothnessRating];
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Retrospective data for a single stage in the workflow.
|
||||
*/
|
||||
export interface StageRetro {
|
||||
/**
|
||||
* Identifier of the stage in the workflow graph.
|
||||
*/
|
||||
'stage_id': string;
|
||||
/**
|
||||
* Human-readable label for the stage.
|
||||
*/
|
||||
'stage_label': string;
|
||||
/**
|
||||
* Final status of the stage.
|
||||
*/
|
||||
'status': string;
|
||||
/**
|
||||
* Stage duration in milliseconds.
|
||||
*/
|
||||
'duration_ms': number;
|
||||
/**
|
||||
* Number of retries for this stage.
|
||||
*/
|
||||
'retries': number;
|
||||
/**
|
||||
* Cost in USD for this stage. Absent when cost data is unavailable.
|
||||
*/
|
||||
'cost'?: number;
|
||||
/**
|
||||
* Optional notes about this stage\'s execution.
|
||||
*/
|
||||
'notes'?: string;
|
||||
/**
|
||||
* Reason the stage failed, if applicable.
|
||||
*/
|
||||
'failure_reason'?: string;
|
||||
/**
|
||||
* Files modified during this stage.
|
||||
*/
|
||||
'files_touched': Array<string>;
|
||||
}
|
||||
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { ToolUse } from './tool-use';
|
||||
|
||||
/**
|
||||
* A tool invocation turn.
|
||||
*/
|
||||
export interface ToolTurn {
|
||||
'kind': ToolTurnKindEnum;
|
||||
/**
|
||||
* Tool invocations for this turn.
|
||||
*/
|
||||
'tools': Array<ToolUse>;
|
||||
/**
|
||||
* Timestamp when the turn was created.
|
||||
*/
|
||||
'created_at': string;
|
||||
}
|
||||
|
||||
export const ToolTurnKindEnum = {
|
||||
TOOL: 'tool'
|
||||
} as const;
|
||||
|
||||
export type ToolTurnKindEnum = typeof ToolTurnKindEnum[keyof typeof ToolTurnKindEnum];
|
||||
|
||||
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A user message turn.
|
||||
*/
|
||||
export interface UserTurn {
|
||||
'kind': UserTurnKindEnum;
|
||||
/**
|
||||
* Text content of the user message.
|
||||
*/
|
||||
'content': string;
|
||||
/**
|
||||
* Timestamp when the turn was created.
|
||||
*/
|
||||
'created_at': string;
|
||||
}
|
||||
|
||||
export const UserTurnKindEnum = {
|
||||
USER: 'user'
|
||||
} as const;
|
||||
|
||||
export type UserTurnKindEnum = typeof UserTurnKindEnum[keyof typeof UserTurnKindEnum];
|
||||
|
||||
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { CriterionReference } from './criterion-reference';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { VerificationMode } from './verification-mode';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { VerificationType } from './verification-type';
|
||||
|
||||
/**
|
||||
* A verification control in a flat list view with criterion reference.
|
||||
*/
|
||||
export interface VerificationControlListItem {
|
||||
/**
|
||||
* Human-readable control name.
|
||||
*/
|
||||
'name': string;
|
||||
/**
|
||||
* URL-safe slug for API lookups.
|
||||
*/
|
||||
'slug': string;
|
||||
/**
|
||||
* Short description of what the control verifies.
|
||||
*/
|
||||
'description': string;
|
||||
'type': VerificationType;
|
||||
'mode'?: VerificationMode;
|
||||
/**
|
||||
* F1 score of the control\'s AI evaluator.
|
||||
*/
|
||||
'f1'?: number;
|
||||
/**
|
||||
* Pass@1 rate.
|
||||
*/
|
||||
'pass_at_1'?: number;
|
||||
'criterion': CriterionReference;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { VerificationMode } from './verification-mode';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { VerificationResult } from './verification-result';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { VerificationType } from './verification-type';
|
||||
|
||||
/**
|
||||
* A verification control within a category, with performance metrics.
|
||||
*/
|
||||
export interface VerificationControl {
|
||||
/**
|
||||
* Human-readable control name.
|
||||
*/
|
||||
'name': string;
|
||||
/**
|
||||
* URL-safe slug for API lookups.
|
||||
*/
|
||||
'slug': string;
|
||||
/**
|
||||
* Short description of what the control verifies.
|
||||
*/
|
||||
'description': string;
|
||||
'type': VerificationType;
|
||||
'mode'?: VerificationMode;
|
||||
/**
|
||||
* F1 score of the control\'s AI evaluator.
|
||||
*/
|
||||
'f1'?: number;
|
||||
/**
|
||||
* Pass@1 rate — probability of passing on the first evaluation.
|
||||
*/
|
||||
'pass_at_1'?: number;
|
||||
/**
|
||||
* Recent evaluation results (newest first).
|
||||
*/
|
||||
'evaluations'?: Array<VerificationResult>;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { VerificationControl } from './verification-control';
|
||||
|
||||
/**
|
||||
* Detail view of a verification criterion with inline controls and performance metrics.
|
||||
*/
|
||||
export interface VerificationCriterionDetail {
|
||||
/**
|
||||
* Criterion name.
|
||||
*/
|
||||
'name': string;
|
||||
/**
|
||||
* Guiding question for the criterion.
|
||||
*/
|
||||
'question': string;
|
||||
/**
|
||||
* Verification controls in this criterion with performance metrics.
|
||||
*/
|
||||
'controls': Array<VerificationControl>;
|
||||
}
|
||||
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { VerificationControl } from './verification-control';
|
||||
|
||||
/**
|
||||
* A group of related verification controls.
|
||||
*/
|
||||
export interface VerificationCriterion {
|
||||
/**
|
||||
* Criterion name.
|
||||
*/
|
||||
'name': string;
|
||||
/**
|
||||
* Guiding question for the criterion.
|
||||
*/
|
||||
'question': string;
|
||||
/**
|
||||
* Verification controls in this criterion.
|
||||
*/
|
||||
'controls': Array<VerificationControl>;
|
||||
}
|
||||
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { ControlDetail } from './control-detail';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { ControlInfo } from './control-info';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { ControlPerformance } from './control-performance';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { RecentControlResult } from './recent-control-result';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { SiblingControl } from './sibling-control';
|
||||
|
||||
/**
|
||||
* Complete detail view of a verification control with performance, examples, and recent results.
|
||||
*/
|
||||
export interface VerificationDetailResponse {
|
||||
'control': ControlInfo;
|
||||
'performance': ControlPerformance;
|
||||
'control_detail': ControlDetail;
|
||||
/**
|
||||
* Recent evaluation results across runs.
|
||||
*/
|
||||
'recent_results': Array<RecentControlResult>;
|
||||
/**
|
||||
* Other controls in the same category.
|
||||
*/
|
||||
'siblings': Array<SiblingControl>;
|
||||
}
|
||||
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Operational mode of a verification control.
|
||||
*/
|
||||
|
||||
export const VerificationMode = {
|
||||
ACTIVE: 'active',
|
||||
EVALUATE: 'evaluate',
|
||||
DISABLED: 'disabled'
|
||||
} as const;
|
||||
|
||||
export type VerificationMode = typeof VerificationMode[keyof typeof VerificationMode];
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Outcome of a verification control evaluation. `skip`: evaluation was intentionally skipped (e.g., control is disabled). `na`: control does not apply to this run (e.g., Python lint on a Rust-only change).
|
||||
*/
|
||||
|
||||
export const VerificationResult = {
|
||||
PASS: 'pass',
|
||||
FAIL: 'fail',
|
||||
SKIP: 'skip',
|
||||
NA: 'na'
|
||||
} as const;
|
||||
|
||||
export type VerificationResult = typeof VerificationResult[keyof typeof VerificationResult];
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* 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
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The evaluation method used by a verification control.
|
||||
*/
|
||||
|
||||
export const VerificationType = {
|
||||
AI: 'ai',
|
||||
AUTOMATED: 'automated',
|
||||
ANALYSIS: 'analysis',
|
||||
AI_ANALYSIS: 'ai-analysis'
|
||||
} as const;
|
||||
|
||||
export type VerificationType = typeof VerificationType[keyof typeof VerificationType];
|
||||
|
||||
|
||||
|
||||
Loading…
Add table
Reference in a new issue