Server: the /companies/:companyId/live-runs endpoint now returns issueId extracted from contextSnapshot, so the UI can match runs to issues without N+1 queries. UI (Issues.tsx): fetches company live runs (with 5s polling), builds a set of issue IDs with active runs, and shows a pulsing "Live" badge in each matching issue row — matching the existing blue live indicator style from AgentDetail. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
44 lines
1.7 KiB
TypeScript
44 lines
1.7 KiB
TypeScript
import type { HeartbeatRun, HeartbeatRunEvent } from "@paperclip/shared";
|
|
import { api } from "./client";
|
|
|
|
export interface ActiveRunForIssue extends HeartbeatRun {
|
|
agentId: string;
|
|
agentName: string;
|
|
adapterType: string;
|
|
}
|
|
|
|
export interface LiveRunForIssue {
|
|
id: string;
|
|
status: string;
|
|
invocationSource: string;
|
|
triggerDetail: string | null;
|
|
startedAt: string | null;
|
|
finishedAt: string | null;
|
|
createdAt: string;
|
|
agentId: string;
|
|
agentName: string;
|
|
adapterType: string;
|
|
issueId?: string | null;
|
|
}
|
|
|
|
export const heartbeatsApi = {
|
|
list: (companyId: string, agentId?: string) => {
|
|
const params = agentId ? `?agentId=${agentId}` : "";
|
|
return api.get<HeartbeatRun[]>(`/companies/${companyId}/heartbeat-runs${params}`);
|
|
},
|
|
events: (runId: string, afterSeq = 0, limit = 200) =>
|
|
api.get<HeartbeatRunEvent[]>(
|
|
`/heartbeat-runs/${runId}/events?afterSeq=${encodeURIComponent(String(afterSeq))}&limit=${encodeURIComponent(String(limit))}`,
|
|
),
|
|
log: (runId: string, offset = 0, limitBytes = 256000) =>
|
|
api.get<{ runId: string; store: string; logRef: string; content: string; nextOffset?: number }>(
|
|
`/heartbeat-runs/${runId}/log?offset=${encodeURIComponent(String(offset))}&limitBytes=${encodeURIComponent(String(limitBytes))}`,
|
|
),
|
|
cancel: (runId: string) => api.post<void>(`/heartbeat-runs/${runId}/cancel`, {}),
|
|
liveRunsForIssue: (issueId: string) =>
|
|
api.get<LiveRunForIssue[]>(`/issues/${issueId}/live-runs`),
|
|
activeRunForIssue: (issueId: string) =>
|
|
api.get<ActiveRunForIssue | null>(`/issues/${issueId}/active-run`),
|
|
liveRunsForCompany: (companyId: string) =>
|
|
api.get<LiveRunForIssue[]>(`/companies/${companyId}/live-runs`),
|
|
};
|