The swarm lives in the swarm UI; this tab lists the hive's agents. The tab is 4G3NTS at #agents, its pane and count pill follow, and the module that renders the containers is agents.js. Comments and docs that named SW4RM or swarm.js follow.
107 lines
3.6 KiB
TypeScript
107 lines
3.6 KiB
TypeScript
// JobqRollup.tsx — <JobqRollup>, a compact one-line banner over
|
|
// GET .../api/jobq/rollup's pre-tallied `Vec<hive_jobq_wire::StateCount>`
|
|
// (any endpoint serving that shape works — hive-c0re and swarm-controller
|
|
// both do). Extracted out of dashboard/src/agents.js's hand-rolled
|
|
// queue-summary banner, which this component now replaces there too —
|
|
// same "one shared component, not two parallel implementations" pattern
|
|
// `JobqGraph` already established for the rebuild-queue tree view.
|
|
//
|
|
// Reads `roots` (not `nodes`) — "N running / M queued" has always meant
|
|
// *operations*, not raw steps (one rebuild is ~7 nodes but 1 root).
|
|
// `Finishing` counts as running (own work done, subtree still going,
|
|
// still in flight) — the rollup endpoint doesn't collapse the two
|
|
// itself, so this component does. Renders nothing at all when there's
|
|
// no active work, matching the banner it replaces (an idle queue is not
|
|
// worth a line of chrome).
|
|
//
|
|
// `queueHref`, when given, adds a "view queue →" link (dashboard links to
|
|
// the dashboard's #rebuild tab; swarm-ui's /jobs page omits it — a link to the page
|
|
// you're already on is noise).
|
|
//
|
|
// The glyph carries `.spinner` (`@hive/shared/base.css`, imported by
|
|
// both consumers) for the "actively happening" spin — a static
|
|
// screenshot can't tell a frozen spinner from a missing one.
|
|
//
|
|
// JSX (swarm-ui) — `<JobqRollup endpoint="..." queueHref="..." />`. Or
|
|
// plain `render(h(JobqRollup, props), container)` (dashboard/src/
|
|
// agents.js, no JSX pragma needed) — call again with a bumped
|
|
// `refreshToken` to force a refetch. No mount wrapper: `render` is
|
|
// already the re-render/diff entry point.
|
|
|
|
import { useState, useEffect } from "preact/hooks";
|
|
|
|
// Mirrors `hive_jobq_wire::StateSchema` — only the subset this banner
|
|
// cares about, not the full union `JobqGraph.tsx` mirrors, since a
|
|
// rollup row's `state` is read by exact string match, not rendered.
|
|
type NodeState =
|
|
| "Pending"
|
|
| "Running"
|
|
| "Finishing"
|
|
| "Done"
|
|
| "Failed"
|
|
| "Cancelled"
|
|
| "Skipped";
|
|
|
|
interface StateCount {
|
|
state: NodeState;
|
|
nodes: number;
|
|
roots: number;
|
|
}
|
|
|
|
export interface JobqRollupProps {
|
|
endpoint?: string;
|
|
queueHref?: string;
|
|
refreshToken?: number;
|
|
}
|
|
|
|
export function JobqRollup({
|
|
endpoint,
|
|
queueHref,
|
|
refreshToken = 0,
|
|
}: JobqRollupProps) {
|
|
const [counts, setCounts] = useState<StateCount[]>([]);
|
|
|
|
useEffect(() => {
|
|
if (!endpoint) return undefined;
|
|
let cancelled = false;
|
|
// Best-effort: a failed fetch leaves the previous snapshot in
|
|
// place rather than wiping the banner on a network blip — same
|
|
// rule the hand-rolled version followed.
|
|
(async () => {
|
|
try {
|
|
const r = await fetch(endpoint);
|
|
if (!r.ok) return;
|
|
const data = (await r.json()) as StateCount[];
|
|
if (!cancelled) setCounts(data);
|
|
} catch {
|
|
// ignore — keep the previous snapshot
|
|
}
|
|
})();
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, [endpoint, refreshToken]);
|
|
|
|
const byState = new Map(counts.map((c) => [c.state, c]));
|
|
const running =
|
|
(byState.get("Running")?.roots ?? 0) +
|
|
(byState.get("Finishing")?.roots ?? 0);
|
|
const queued = byState.get("Pending")?.roots ?? 0;
|
|
if (!running && !queued) return null;
|
|
|
|
const parts: string[] = [];
|
|
if (running) parts.push(`${running} running`);
|
|
if (queued) parts.push(`${queued} queued`);
|
|
|
|
return (
|
|
<div class="jqr-summary">
|
|
<span class="jqr-glyph spinner">◐</span> <strong>build queue</strong> —{" "}
|
|
{parts.join(" · ")}{" "}
|
|
{queueHref && (
|
|
<a class="jqr-link" href={queueHref}>
|
|
view queue →
|
|
</a>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|