mirror of
https://github.com/0xWheatyz/handler.git
synced 2026-08-30 19:16:25 +00:00
Merge branch 'main' into claude/webapp-multi-page-refactor-85v1od
# Conflicts: # src/handler/api/static/404.html # src/handler/api/static/_next/static/QCIYKmybhnvk7okhoCnyi/_buildManifest.js # src/handler/api/static/_next/static/QCIYKmybhnvk7okhoCnyi/_ssgManifest.js # src/handler/api/static/_next/static/ga21jhjKhYsutf8F3vo_-/_buildManifest.js # src/handler/api/static/_next/static/ga21jhjKhYsutf8F3vo_-/_ssgManifest.js # src/handler/api/static/_next/static/zI_TsbchBb39diXA-dnfw/_buildManifest.js # src/handler/api/static/_next/static/zI_TsbchBb39diXA-dnfw/_ssgManifest.js # src/handler/api/static/index.html # src/handler/api/static/index.txt
This commit is contained in:
@@ -216,6 +216,10 @@ export function DashboardProvider({
|
||||
const [events, setEvents] = useState<AgentEvent[]>([]);
|
||||
const eventsRef = useRef<AgentEvent[]>([]);
|
||||
eventsRef.current = events;
|
||||
/* Bumped on every selectRun: an in-flight loadRun that resolves after the user picked
|
||||
* a different run must drop its writes, or run A's checkmark/log/events land on run
|
||||
* B's panel (the 5s tick keeps polls in flight constantly). */
|
||||
const runGenRef = useRef(0);
|
||||
const [approvals, setApprovals] = useState<Approval[]>([]);
|
||||
const [hosts, setHosts] = useState<Host[]>([]);
|
||||
const [commands, setCommands] = useState<Command[]>([]);
|
||||
@@ -275,12 +279,15 @@ export function DashboardProvider({
|
||||
|
||||
const loadRun = useCallback(async (projectId: string, name: string) => {
|
||||
const path = `/projects/${encodeURIComponent(projectId)}/agents/${encodeURIComponent(name)}`;
|
||||
const gen = runGenRef.current;
|
||||
const stale = () => gen !== runGenRef.current;
|
||||
try {
|
||||
const cm = await clientRef.current.api<Checkmark>(`${path}/checkmark`);
|
||||
if (stale()) return;
|
||||
setCheckmark(cm);
|
||||
setCheckmarkMissing(false);
|
||||
} catch (e) {
|
||||
if (e instanceof AuthError) return;
|
||||
if (e instanceof AuthError || stale()) return;
|
||||
if ((e as ApiError).status === 404) {
|
||||
setCheckmark(null);
|
||||
setCheckmarkMissing(true);
|
||||
@@ -290,9 +297,10 @@ export function DashboardProvider({
|
||||
const entries = await clientRef.current.api<LogEntry[]>(
|
||||
`${path}/log?limit=${LOG_LIMIT}&offset=${logOffsetRef.current}`,
|
||||
);
|
||||
if (stale()) return;
|
||||
setLog(entries);
|
||||
} catch (e) {
|
||||
swallow(e);
|
||||
if (!stale()) swallow(e);
|
||||
}
|
||||
try {
|
||||
// Cursor poll: only events newer than what we already hold come back.
|
||||
@@ -301,9 +309,16 @@ export function DashboardProvider({
|
||||
const fresh = await clientRef.current.api<AgentEvent[]>(
|
||||
`${path}/events?after_id=${after}&limit=500`,
|
||||
);
|
||||
if (fresh.length) setEvents((prev) => [...prev, ...fresh]);
|
||||
if (stale() || fresh.length === 0) return;
|
||||
// Dedup by id at append time: overlapping polls for the same run can both fetch
|
||||
// from the same cursor; only genuinely-new rows may append.
|
||||
setEvents((prev) => {
|
||||
const last = prev.length ? prev[prev.length - 1].id : 0;
|
||||
const add = fresh.filter((e) => e.id > last);
|
||||
return add.length ? [...prev, ...add] : prev;
|
||||
});
|
||||
} catch (e) {
|
||||
swallow(e);
|
||||
if (!stale()) swallow(e);
|
||||
}
|
||||
}, []);
|
||||
|
||||
@@ -424,6 +439,7 @@ export function DashboardProvider({
|
||||
|
||||
const selectRun = useCallback(
|
||||
(projectId: string, name: string) => {
|
||||
runGenRef.current += 1; // invalidate any in-flight loadRun for the previous run
|
||||
setSelectedRun({ projectId, name });
|
||||
setLogOffset(0);
|
||||
logOffsetRef.current = 0;
|
||||
|
||||
Reference in New Issue
Block a user