@legioncodeinc/hive 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/README.md +262 -0
- package/dist/cli-commands.d.ts +41 -0
- package/dist/cli-commands.js +76 -0
- package/dist/cli-commands.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +46 -0
- package/dist/cli.js.map +1 -0
- package/dist/daemon/dashboard/app.js +118 -0
- package/dist/daemon/dashboard/host.d.ts +90 -0
- package/dist/daemon/dashboard/host.js +172 -0
- package/dist/daemon/dashboard/host.js.map +1 -0
- package/dist/daemon/dashboard/web-assets.d.ts +88 -0
- package/dist/daemon/dashboard/web-assets.js +195 -0
- package/dist/daemon/dashboard/web-assets.js.map +1 -0
- package/dist/daemon/fleet-status.d.ts +15 -0
- package/dist/daemon/fleet-status.js +56 -0
- package/dist/daemon/fleet-status.js.map +1 -0
- package/dist/daemon/gate.d.ts +55 -0
- package/dist/daemon/gate.js +128 -0
- package/dist/daemon/gate.js.map +1 -0
- package/dist/daemon/proxy.d.ts +38 -0
- package/dist/daemon/proxy.js +126 -0
- package/dist/daemon/proxy.js.map +1 -0
- package/dist/daemon/registry.d.ts +31 -0
- package/dist/daemon/registry.js +120 -0
- package/dist/daemon/registry.js.map +1 -0
- package/dist/daemon/server.d.ts +41 -0
- package/dist/daemon/server.js +126 -0
- package/dist/daemon/server.js.map +1 -0
- package/dist/daemon/setup-auth.d.ts +37 -0
- package/dist/daemon/setup-auth.js +51 -0
- package/dist/daemon/setup-auth.js.map +1 -0
- package/dist/daemon/telemetry-proxy.d.ts +47 -0
- package/dist/daemon/telemetry-proxy.js +63 -0
- package/dist/daemon/telemetry-proxy.js.map +1 -0
- package/dist/dashboard/contracts.d.ts +235 -0
- package/dist/dashboard/contracts.js +43 -0
- package/dist/dashboard/contracts.js.map +1 -0
- package/dist/dashboard/web/app.d.ts +44 -0
- package/dist/dashboard/web/app.js +179 -0
- package/dist/dashboard/web/app.js.map +1 -0
- package/dist/dashboard/web/boot-route.d.ts +23 -0
- package/dist/dashboard/web/boot-route.js +29 -0
- package/dist/dashboard/web/boot-route.js.map +1 -0
- package/dist/dashboard/web/build-graph-button.d.ts +46 -0
- package/dist/dashboard/web/build-graph-button.js +81 -0
- package/dist/dashboard/web/build-graph-button.js.map +1 -0
- package/dist/dashboard/web/buzzing-screen.d.ts +30 -0
- package/dist/dashboard/web/buzzing-screen.js +135 -0
- package/dist/dashboard/web/buzzing-screen.js.map +1 -0
- package/dist/dashboard/web/folder-picker.d.ts +54 -0
- package/dist/dashboard/web/folder-picker.js +125 -0
- package/dist/dashboard/web/folder-picker.js.map +1 -0
- package/dist/dashboard/web/graph-layout.d.ts +101 -0
- package/dist/dashboard/web/graph-layout.js +0 -0
- package/dist/dashboard/web/graph-layout.js.map +1 -0
- package/dist/dashboard/web/harness-strip.d.ts +38 -0
- package/dist/dashboard/web/harness-strip.js +65 -0
- package/dist/dashboard/web/harness-strip.js.map +1 -0
- package/dist/dashboard/web/health-rail.d.ts +26 -0
- package/dist/dashboard/web/health-rail.js +61 -0
- package/dist/dashboard/web/health-rail.js.map +1 -0
- package/dist/dashboard/web/main.d.ts +22 -0
- package/dist/dashboard/web/main.js +51 -0
- package/dist/dashboard/web/main.js.map +1 -0
- package/dist/dashboard/web/needs-project.d.ts +51 -0
- package/dist/dashboard/web/needs-project.js +69 -0
- package/dist/dashboard/web/needs-project.js.map +1 -0
- package/dist/dashboard/web/page-frame.d.ts +95 -0
- package/dist/dashboard/web/page-frame.js +98 -0
- package/dist/dashboard/web/page-frame.js.map +1 -0
- package/dist/dashboard/web/pages/coming-soon.d.ts +23 -0
- package/dist/dashboard/web/pages/coming-soon.js +19 -0
- package/dist/dashboard/web/pages/coming-soon.js.map +1 -0
- package/dist/dashboard/web/pages/dashboard.d.ts +32 -0
- package/dist/dashboard/web/pages/dashboard.js +205 -0
- package/dist/dashboard/web/pages/dashboard.js.map +1 -0
- package/dist/dashboard/web/pages/graph.d.ts +72 -0
- package/dist/dashboard/web/pages/graph.js +362 -0
- package/dist/dashboard/web/pages/graph.js.map +1 -0
- package/dist/dashboard/web/pages/harnesses.d.ts +65 -0
- package/dist/dashboard/web/pages/harnesses.js +210 -0
- package/dist/dashboard/web/pages/harnesses.js.map +1 -0
- package/dist/dashboard/web/pages/health.d.ts +22 -0
- package/dist/dashboard/web/pages/health.js +91 -0
- package/dist/dashboard/web/pages/health.js.map +1 -0
- package/dist/dashboard/web/pages/lifecycle-panel.d.ts +60 -0
- package/dist/dashboard/web/pages/lifecycle-panel.js +187 -0
- package/dist/dashboard/web/pages/lifecycle-panel.js.map +1 -0
- package/dist/dashboard/web/pages/logs.d.ts +55 -0
- package/dist/dashboard/web/pages/logs.js +311 -0
- package/dist/dashboard/web/pages/logs.js.map +1 -0
- package/dist/dashboard/web/pages/memories.d.ts +35 -0
- package/dist/dashboard/web/pages/memories.js +439 -0
- package/dist/dashboard/web/pages/memories.js.map +1 -0
- package/dist/dashboard/web/pages/projects.d.ts +38 -0
- package/dist/dashboard/web/pages/projects.js +325 -0
- package/dist/dashboard/web/pages/projects.js.map +1 -0
- package/dist/dashboard/web/pages/roi-chart.d.ts +52 -0
- package/dist/dashboard/web/pages/roi-chart.js +133 -0
- package/dist/dashboard/web/pages/roi-chart.js.map +1 -0
- package/dist/dashboard/web/pages/roi.d.ts +93 -0
- package/dist/dashboard/web/pages/roi.js +375 -0
- package/dist/dashboard/web/pages/roi.js.map +1 -0
- package/dist/dashboard/web/pages/settings.d.ts +103 -0
- package/dist/dashboard/web/pages/settings.js +420 -0
- package/dist/dashboard/web/pages/settings.js.map +1 -0
- package/dist/dashboard/web/pages/sync.d.ts +59 -0
- package/dist/dashboard/web/pages/sync.js +280 -0
- package/dist/dashboard/web/pages/sync.js.map +1 -0
- package/dist/dashboard/web/panels.d.ts +178 -0
- package/dist/dashboard/web/panels.js +471 -0
- package/dist/dashboard/web/panels.js.map +1 -0
- package/dist/dashboard/web/primitives.d.ts +80 -0
- package/dist/dashboard/web/primitives.js +208 -0
- package/dist/dashboard/web/primitives.js.map +1 -0
- package/dist/dashboard/web/readiness-splash.d.ts +39 -0
- package/dist/dashboard/web/readiness-splash.js +201 -0
- package/dist/dashboard/web/readiness-splash.js.map +1 -0
- package/dist/dashboard/web/registry.d.ts +96 -0
- package/dist/dashboard/web/registry.js +99 -0
- package/dist/dashboard/web/registry.js.map +1 -0
- package/dist/dashboard/web/router.d.ts +48 -0
- package/dist/dashboard/web/router.js +76 -0
- package/dist/dashboard/web/router.js.map +1 -0
- package/dist/dashboard/web/scope-context.d.ts +155 -0
- package/dist/dashboard/web/scope-context.js +266 -0
- package/dist/dashboard/web/scope-context.js.map +1 -0
- package/dist/dashboard/web/service-icons.d.ts +33 -0
- package/dist/dashboard/web/service-icons.js +80 -0
- package/dist/dashboard/web/service-icons.js.map +1 -0
- package/dist/dashboard/web/setup-gate.d.ts +116 -0
- package/dist/dashboard/web/setup-gate.js +272 -0
- package/dist/dashboard/web/setup-gate.js.map +1 -0
- package/dist/dashboard/web/sidebar.d.ts +63 -0
- package/dist/dashboard/web/sidebar.js +91 -0
- package/dist/dashboard/web/sidebar.js.map +1 -0
- package/dist/dashboard/web/use-fleet-telemetry.d.ts +111 -0
- package/dist/dashboard/web/use-fleet-telemetry.js +278 -0
- package/dist/dashboard/web/use-fleet-telemetry.js.map +1 -0
- package/dist/dashboard/web/wire.d.ts +1937 -0
- package/dist/dashboard/web/wire.js +1837 -0
- package/dist/dashboard/web/wire.js.map +1 -0
- package/dist/errors.d.ts +5 -0
- package/dist/errors.js +11 -0
- package/dist/errors.js.map +1 -0
- package/dist/install/registry.d.ts +35 -0
- package/dist/install/registry.js +110 -0
- package/dist/install/registry.js.map +1 -0
- package/dist/lock.d.ts +10 -0
- package/dist/lock.js +87 -0
- package/dist/lock.js.map +1 -0
- package/dist/service/commands.d.ts +15 -0
- package/dist/service/commands.js +49 -0
- package/dist/service/commands.js.map +1 -0
- package/dist/service/index.d.ts +38 -0
- package/dist/service/index.js +174 -0
- package/dist/service/index.js.map +1 -0
- package/dist/service/platform.d.ts +37 -0
- package/dist/service/platform.js +67 -0
- package/dist/service/platform.js.map +1 -0
- package/dist/service/templates.d.ts +10 -0
- package/dist/service/templates.js +120 -0
- package/dist/service/templates.js.map +1 -0
- package/dist/shared/constants.d.ts +15 -0
- package/dist/shared/constants.js +19 -0
- package/dist/shared/constants.js.map +1 -0
- package/dist/shared/daemon-routing.d.ts +11 -0
- package/dist/shared/daemon-routing.js +38 -0
- package/dist/shared/daemon-routing.js.map +1 -0
- package/dist/shared/fleet-readiness.d.ts +18 -0
- package/dist/shared/fleet-readiness.js +9 -0
- package/dist/shared/fleet-readiness.js.map +1 -0
- package/dist/shared/fleet-telemetry.d.ts +68 -0
- package/dist/shared/fleet-telemetry.js +61 -0
- package/dist/shared/fleet-telemetry.js.map +1 -0
- package/dist/shared/lifecycle-flags.d.ts +34 -0
- package/dist/shared/lifecycle-flags.js +39 -0
- package/dist/shared/lifecycle-flags.js.map +1 -0
- package/dist/shared/memory-types.d.ts +55 -0
- package/dist/shared/memory-types.js +75 -0
- package/dist/shared/memory-types.js.map +1 -0
- package/dist/shared/service-status.d.ts +82 -0
- package/dist/shared/service-status.js +97 -0
- package/dist/shared/service-status.js.map +1 -0
- package/dist/telemetry/emit.d.ts +197 -0
- package/dist/telemetry/emit.js +196 -0
- package/dist/telemetry/emit.js.map +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dashboard APP SHELL — PRD-037b (the app-shell split).
|
|
3
|
+
*
|
|
4
|
+
* PRD-024 made `/dashboard` a single long-scrolling page (the old `App`). PRD-037 turns it into a
|
|
5
|
+
* left-nav MULTI-PAGE shell: a persistent `<Sidebar>` (037a) beside a content `<Outlet>` that mounts
|
|
6
|
+
* the registry-matched page (037c) for the active hash route (037b). The current monolithic content
|
|
7
|
+
* moved VERBATIM onto the Dashboard route (`pages/dashboard.tsx`, D-6 lift-and-shift) — this module
|
|
8
|
+
* no longer renders that body; it renders the CHROME and routes to it.
|
|
9
|
+
*
|
|
10
|
+
* What the shell OWNS (hoisted up from the old page, D-5):
|
|
11
|
+
* - The SHARED wire client (built ONCE, passed to every page via PageProps — pages never
|
|
12
|
+
* `createWireClient` themselves).
|
|
13
|
+
* - The path router (PRD-003c `usePathRoute`) → `{ route, navigate }`. The sidebar's `onNavigate`
|
|
14
|
+
* is a thin pass-through to `navigate` (037a never touches `location.pathname` directly).
|
|
15
|
+
* - The `/health` LIVENESS poll + `daemonUp`. When the daemon is unreachable the CONTENT region
|
|
16
|
+
* swaps for the `ConnectivityBanner` while the SIDEBAR stays mounted (D-5); on Retry/reconnect the
|
|
17
|
+
* active page restores and re-hydrates. The per-subsystem health STRIP stays page content (it
|
|
18
|
+
* polls `/health` reasons inside `DashboardPage`); the shell owns only the coarse up/down.
|
|
19
|
+
* - The org/workspace identity (the sidebar sub-line) + the "Pollinate now" action + the `pollinating`
|
|
20
|
+
* pulse, all relocated from the old `Header` into the shell chrome (D-5). `pollinating` flows DOWN to
|
|
21
|
+
* the active page (the Dashboard graph/cards pulse on it).
|
|
22
|
+
* - The collapsed/responsive state (037a AC-6): a manual toggle plus an auto-collapse under the
|
|
23
|
+
* host's `@media (max-width:900px)` breakpoint.
|
|
24
|
+
*
|
|
25
|
+
* PRD-003c: routing is now path-based (History API), with hive's server (`gate.ts`, `host.ts`)
|
|
26
|
+
* as the landing-decision authority — the client router only reflects the path the server already
|
|
27
|
+
* authorized. Still no new dependency. Security posture inherited unchanged (local-mode-only,
|
|
28
|
+
* XSS-safe, no token/secret in the shell/route/registry/pill — D-9).
|
|
29
|
+
*/
|
|
30
|
+
import React from "react";
|
|
31
|
+
import { type WireClient } from "./wire.js";
|
|
32
|
+
/** Props for {@link Shell} — the injected wire client + the asset base (same contract as the old App). */
|
|
33
|
+
export interface ShellProps {
|
|
34
|
+
/** The wire client (injected by a unit test with a mocked fetch; defaults to the live one). */
|
|
35
|
+
readonly client?: WireClient;
|
|
36
|
+
/** The base path the logo/assets are served under (the host serves them beside the page). */
|
|
37
|
+
readonly assetBase?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The dashboard shell (037b). Builds the shared wire, runs the hash router + the liveness poll, owns
|
|
41
|
+
* the pollinate action + the collapsed state, and renders the sidebar beside the routed content (or the
|
|
42
|
+
* ConnectivityBanner when the daemon is down — sidebar stays mounted, D-5).
|
|
43
|
+
*/
|
|
44
|
+
export declare function Shell({ client, assetBase }?: ShellProps): React.JSX.Element;
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* The dashboard APP SHELL — PRD-037b (the app-shell split).
|
|
4
|
+
*
|
|
5
|
+
* PRD-024 made `/dashboard` a single long-scrolling page (the old `App`). PRD-037 turns it into a
|
|
6
|
+
* left-nav MULTI-PAGE shell: a persistent `<Sidebar>` (037a) beside a content `<Outlet>` that mounts
|
|
7
|
+
* the registry-matched page (037c) for the active hash route (037b). The current monolithic content
|
|
8
|
+
* moved VERBATIM onto the Dashboard route (`pages/dashboard.tsx`, D-6 lift-and-shift) — this module
|
|
9
|
+
* no longer renders that body; it renders the CHROME and routes to it.
|
|
10
|
+
*
|
|
11
|
+
* What the shell OWNS (hoisted up from the old page, D-5):
|
|
12
|
+
* - The SHARED wire client (built ONCE, passed to every page via PageProps — pages never
|
|
13
|
+
* `createWireClient` themselves).
|
|
14
|
+
* - The path router (PRD-003c `usePathRoute`) → `{ route, navigate }`. The sidebar's `onNavigate`
|
|
15
|
+
* is a thin pass-through to `navigate` (037a never touches `location.pathname` directly).
|
|
16
|
+
* - The `/health` LIVENESS poll + `daemonUp`. When the daemon is unreachable the CONTENT region
|
|
17
|
+
* swaps for the `ConnectivityBanner` while the SIDEBAR stays mounted (D-5); on Retry/reconnect the
|
|
18
|
+
* active page restores and re-hydrates. The per-subsystem health STRIP stays page content (it
|
|
19
|
+
* polls `/health` reasons inside `DashboardPage`); the shell owns only the coarse up/down.
|
|
20
|
+
* - The org/workspace identity (the sidebar sub-line) + the "Pollinate now" action + the `pollinating`
|
|
21
|
+
* pulse, all relocated from the old `Header` into the shell chrome (D-5). `pollinating` flows DOWN to
|
|
22
|
+
* the active page (the Dashboard graph/cards pulse on it).
|
|
23
|
+
* - The collapsed/responsive state (037a AC-6): a manual toggle plus an auto-collapse under the
|
|
24
|
+
* host's `@media (max-width:900px)` breakpoint.
|
|
25
|
+
*
|
|
26
|
+
* PRD-003c: routing is now path-based (History API), with hive's server (`gate.ts`, `host.ts`)
|
|
27
|
+
* as the landing-decision authority — the client router only reflects the path the server already
|
|
28
|
+
* authorized. Still no new dependency. Security posture inherited unchanged (local-mode-only,
|
|
29
|
+
* XSS-safe, no token/secret in the shell/route/registry/pill — D-9).
|
|
30
|
+
*/
|
|
31
|
+
import React from "react";
|
|
32
|
+
import { Button } from "./primitives.js";
|
|
33
|
+
import { ConnectivityBanner } from "./panels.js";
|
|
34
|
+
import { HEALTH_ROUTE, HealthRail } from "./health-rail.js";
|
|
35
|
+
import { matchRoute, ROUTES } from "./registry.js";
|
|
36
|
+
import { Sidebar } from "./sidebar.js";
|
|
37
|
+
import { usePathRoute } from "./router.js";
|
|
38
|
+
import { PAGE_MAX_WIDTH, usePoll } from "./page-frame.js";
|
|
39
|
+
import { FirstRunBindCTA } from "./needs-project.js";
|
|
40
|
+
import { ScopeProvider, useScopeSwitcher } from "./scope-context.js";
|
|
41
|
+
import { createWireClient, EMPTY_SETTINGS } from "./wire.js";
|
|
42
|
+
/** How often the shell probes `/health` for coarse liveness (ms). */
|
|
43
|
+
const HEALTH_POLL_MS = 5000;
|
|
44
|
+
/** The narrow-viewport breakpoint that auto-collapses the sidebar (mirrors host.ts's media rule). */
|
|
45
|
+
const NARROW_BREAKPOINT = 900;
|
|
46
|
+
/**
|
|
47
|
+
* The content OUTLET — looks up the registry-matched page for the active route and mounts it with the
|
|
48
|
+
* shared {@link PageProps} (037b). Capped at the readable page max-width (D-8). When the daemon is
|
|
49
|
+
* down the shell renders the banner INSTEAD of this (so this only ever renders for an up daemon).
|
|
50
|
+
*
|
|
51
|
+
* PRD-059b (b-AC-1 / M-AC-2): on the DASHBOARD route, when the projects enumeration has resolved with
|
|
52
|
+
* ZERO locally-bound projects, the PRIMARY content is the first-run "Pick a folder to start" CTA — not
|
|
53
|
+
* an empty page. It keys off the switcher's `projectsHydrated` flag so it never flashes before the read
|
|
54
|
+
* resolves, and is gated to the Dashboard route so the Projects/Settings pages stay reachable to bind.
|
|
55
|
+
*/
|
|
56
|
+
function Outlet({ route, pageProps, navigate }) {
|
|
57
|
+
const entry = matchRoute(route);
|
|
58
|
+
const PageComponent = entry.component;
|
|
59
|
+
const { projects, projectsHydrated } = useScopeSwitcher();
|
|
60
|
+
// 037c OQ-2: per-route document title from the registry label (cheap, nice for deep links).
|
|
61
|
+
React.useEffect(() => {
|
|
62
|
+
if (typeof document !== "undefined")
|
|
63
|
+
document.title = `honeycomb · ${entry.label}`;
|
|
64
|
+
}, [entry.label]);
|
|
65
|
+
// b-AC-1: zero locally-bound projects (after the read resolved) + the Dashboard route → the first-run
|
|
66
|
+
// CTA is the primary content. `boundLocally` is the per-device binding bit (the inbox/registry-only
|
|
67
|
+
// projects don't count as "active"); an unbound workspace shows the CTA, a bound one shows the page.
|
|
68
|
+
const hasBound = projects.some((p) => p.boundLocally && p.projectId !== "__unsorted__");
|
|
69
|
+
const showFirstRun = entry.route === "/" && projectsHydrated && !hasBound;
|
|
70
|
+
return (_jsx("div", { style: { flex: 1, minWidth: 0, padding: "28px 28px 48px" }, children: _jsx("div", { style: { maxWidth: PAGE_MAX_WIDTH, margin: "0 auto" }, children: showFirstRun ? _jsx(FirstRunBindCTA, { wire: pageProps.wire, navigate: navigate, assetBase: pageProps.assetBase }) : _jsx(PageComponent, { ...pageProps }) }) }));
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The dashboard shell (037b). Builds the shared wire, runs the hash router + the liveness poll, owns
|
|
74
|
+
* the pollinate action + the collapsed state, and renders the sidebar beside the routed content (or the
|
|
75
|
+
* ConnectivityBanner when the daemon is down — sidebar stays mounted, D-5).
|
|
76
|
+
*/
|
|
77
|
+
export function Shell({ client, assetBase = "assets" } = {}) {
|
|
78
|
+
const wire = React.useMemo(() => client ?? createWireClient(), [client]);
|
|
79
|
+
const { route, navigate } = usePathRoute();
|
|
80
|
+
// ── shell-owned state ──
|
|
81
|
+
const [daemonUp, setDaemonUp] = React.useState(true);
|
|
82
|
+
// The shell owns the SINGLE `/health` poll; `reasons` flow DOWN to the home's subsystem strip via
|
|
83
|
+
// PageProps so the page never polls `/health` a second time (the former double-poll, now deduped).
|
|
84
|
+
const [healthReasons, setHealthReasons] = React.useState(null);
|
|
85
|
+
const [settings, setSettings] = React.useState(EMPTY_SETTINGS);
|
|
86
|
+
const [pollinating, setPollinating] = React.useState(false);
|
|
87
|
+
// Collapsed: a manual toggle OR an auto-collapse under the narrow breakpoint (037a AC-6). We track
|
|
88
|
+
// both an explicit user choice and the viewport, preferring the user's choice once they toggle.
|
|
89
|
+
const [collapsed, setCollapsed] = React.useState(false);
|
|
90
|
+
const daemonUrl = settings.settings.port ? `http://127.0.0.1:${settings.settings.port}` : "http://127.0.0.1:3850";
|
|
91
|
+
/** Hydrate the org/workspace identity for the sidebar sub-line (the shell's slice of settings). */
|
|
92
|
+
const hydrateIdentity = React.useCallback(async () => {
|
|
93
|
+
setSettings(await wire.settings());
|
|
94
|
+
}, [wire]);
|
|
95
|
+
// Hydrate the identity once on mount.
|
|
96
|
+
React.useEffect(() => {
|
|
97
|
+
void hydrateIdentity();
|
|
98
|
+
}, [hydrateIdentity]);
|
|
99
|
+
// Track the previous liveness so identity re-hydration fires ONLY on a down→up recovery (the mount
|
|
100
|
+
// effect already hydrates once) — not on every healthy 5s tick. Null = no probe resolved yet.
|
|
101
|
+
const prevDaemonUpRef = React.useRef(null);
|
|
102
|
+
// D-5: the shell owns the /health LIVENESS poll + the daemon-down swap. On RECOVERY (down→up) re-hydrate
|
|
103
|
+
// the identity (the active page re-hydrates itself on remount). Via `usePoll` so it inherits the shared
|
|
104
|
+
// background-tab pause (a hidden dashboard stops probing) and stops on unmount. `reasons` are captured
|
|
105
|
+
// here and passed DOWN — the page's former duplicate /health poll is gone.
|
|
106
|
+
usePoll(async () => {
|
|
107
|
+
const { up, reasons } = await wire.health();
|
|
108
|
+
setDaemonUp(up);
|
|
109
|
+
setHealthReasons(reasons);
|
|
110
|
+
if (up && prevDaemonUpRef.current === false)
|
|
111
|
+
void hydrateIdentity();
|
|
112
|
+
prevDaemonUpRef.current = up;
|
|
113
|
+
}, HEALTH_POLL_MS);
|
|
114
|
+
// 037a AC-6: auto-collapse under the narrow breakpoint. Tracks the viewport via matchMedia; the
|
|
115
|
+
// listener is cleaned up on unmount. A manual toggle still works (it sets `collapsed` directly).
|
|
116
|
+
React.useEffect(() => {
|
|
117
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function")
|
|
118
|
+
return;
|
|
119
|
+
const mq = window.matchMedia(`(max-width: ${NARROW_BREAKPOINT}px)`);
|
|
120
|
+
const apply = (matches) => {
|
|
121
|
+
if (matches)
|
|
122
|
+
setCollapsed(true);
|
|
123
|
+
};
|
|
124
|
+
apply(mq.matches);
|
|
125
|
+
const onChange = (e) => apply(e.matches);
|
|
126
|
+
mq.addEventListener("change", onChange);
|
|
127
|
+
return () => mq.removeEventListener("change", onChange);
|
|
128
|
+
}, []);
|
|
129
|
+
// A SYNCHRONOUS in-flight guard for the pollinate action: `setPollinating(true)` is async, so a render-state
|
|
130
|
+
// guard alone leaves a race window where a rapid second activation fires `wire.pollinate()` twice. The ref
|
|
131
|
+
// flips immediately; the reset timer is tracked so it can be cleared on unmount.
|
|
132
|
+
const pollinateInFlightRef = React.useRef(false);
|
|
133
|
+
const pollinateResetTimerRef = React.useRef(null);
|
|
134
|
+
React.useEffect(() => {
|
|
135
|
+
return () => {
|
|
136
|
+
if (pollinateResetTimerRef.current !== null)
|
|
137
|
+
clearTimeout(pollinateResetTimerRef.current);
|
|
138
|
+
};
|
|
139
|
+
}, []);
|
|
140
|
+
// D-5: the "Pollinate now" action, relocated into the shell chrome. POST the Wave-1 trigger; reflect
|
|
141
|
+
// the 202 ack by pulsing the active page's graph/cards (`pollinating` flows down via PageProps).
|
|
142
|
+
const pollinate = React.useCallback(async () => {
|
|
143
|
+
if (pollinateInFlightRef.current)
|
|
144
|
+
return; // synchronous re-entry guard (no double POST on rapid clicks)
|
|
145
|
+
pollinateInFlightRef.current = true;
|
|
146
|
+
setPollinating(true);
|
|
147
|
+
const ack = await wire.pollinate();
|
|
148
|
+
if (ack.triggered) {
|
|
149
|
+
// A real pass was queued: pulse briefly while it streams into the live log, then settle.
|
|
150
|
+
pollinateResetTimerRef.current = setTimeout(() => {
|
|
151
|
+
setPollinating(false);
|
|
152
|
+
pollinateInFlightRef.current = false;
|
|
153
|
+
}, 4200);
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
// Honestly reflect the skip (disabled / unavailable) — no fake forever spinner.
|
|
157
|
+
setPollinating(false);
|
|
158
|
+
pollinateInFlightRef.current = false;
|
|
159
|
+
}
|
|
160
|
+
}, [wire]);
|
|
161
|
+
const identity = `${settings.orgName || settings.orgId || "local"} · ${settings.workspace || "default"}`;
|
|
162
|
+
// The PageProps every routed page receives (D-7). One shared wire, the liveness flag, the asset
|
|
163
|
+
// base, and the shell-owned pollinating pulse.
|
|
164
|
+
const pageProps = { wire, daemonUp, assetBase, pollinating, healthReasons };
|
|
165
|
+
return (_jsx(ScopeProvider, { wire: wire, children: _jsxs("div", { style: { display: "flex", minHeight: "100vh", background: "var(--bg-canvas)" }, children: [_jsx(Sidebar, { entries: ROUTES, activeRoute: route, onNavigate: navigate, daemonUp: daemonUp, identity: identity, assetBase: assetBase, collapsed: collapsed, onToggleCollapsed: () => setCollapsed((c) => !c) }), _jsxs("div", { style: { flex: 1, minWidth: 0, display: "flex", flexDirection: "column" }, children: [_jsx(HealthRail, { onOpenHealth: () => navigate(HEALTH_ROUTE) }), _jsxs("div", { style: {
|
|
166
|
+
display: "flex",
|
|
167
|
+
alignItems: "center",
|
|
168
|
+
gap: 12,
|
|
169
|
+
padding: "16px 28px",
|
|
170
|
+
borderBottom: "1px solid var(--border-subtle)",
|
|
171
|
+
}, children: [_jsx("span", { style: { flex: 1 } }), _jsx(Button, { variant: "pollinate", onClick: () => void pollinate(), disabled: pollinating, children: pollinating ? "pollinating…" : "Pollinate now" })] }), daemonUp ? (_jsx(Outlet, { route: route, pageProps: pageProps, navigate: navigate })) : (_jsx("div", { style: { flex: 1, minWidth: 0, padding: "28px 28px 48px" }, children: _jsx("div", { style: { maxWidth: PAGE_MAX_WIDTH, margin: "0 auto" }, children: _jsx(ConnectivityBanner, { url: daemonUrl, onRetry: () => {
|
|
172
|
+
void wire.health().then(({ up }) => {
|
|
173
|
+
setDaemonUp(up);
|
|
174
|
+
if (up)
|
|
175
|
+
void hydrateIdentity();
|
|
176
|
+
});
|
|
177
|
+
} }) }) }))] })] }) }));
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=app.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../../../src/dashboard/web/app.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bundle's BOOT SCREEN selector — PRD-003c (m-AC-6 / m-AC-7 / m-AC-8).
|
|
3
|
+
*
|
|
4
|
+
* By the time this bundle runs, hive's SERVER gate (`hive/src/daemon/gate.ts`, PRD-003a)
|
|
5
|
+
* has already evaluated health-then-auth and served the shell ONLY for a path it authorized:
|
|
6
|
+
* `/buzzing` (fleet not healthy, or landed there directly — always exempt), `/login` (logged out,
|
|
7
|
+
* or landed there directly — always exempt), or any other path (healthy + authenticated). This
|
|
8
|
+
* pure function is the whole boot decision `main.tsx` needs: a plain lookup from `location.pathname`
|
|
9
|
+
* to which top-level screen to mount. It never re-derives health or auth itself — doing so would
|
|
10
|
+
* resurrect the retired `ReadinessSplash`→`SetupGate` nested client gate (ADR-0004, m-AC-6).
|
|
11
|
+
*/
|
|
12
|
+
export type BootScreen = "buzzing" | "login" | "shell";
|
|
13
|
+
/** The `/buzzing` route (PRD-002's readiness splash, now the gate-exempt readiness screen). */
|
|
14
|
+
export declare const BUZZING_PATH: "/buzzing";
|
|
15
|
+
/** The `/login` route (PRD-003b's device-flow guided setup, now the gate-exempt login screen). */
|
|
16
|
+
export declare const LOGIN_PATH: "/login";
|
|
17
|
+
/**
|
|
18
|
+
* Resolve which top-level screen `main.tsx` should mount for the current path. `/buzzing` and
|
|
19
|
+
* `/login` are the two gate-exempt screens (m-AC-7 / m-AC-8); every other path — including `/` and
|
|
20
|
+
* every registry route (`/projects`, `/harnesses`, ...) — mounts the authenticated `<Shell>`,
|
|
21
|
+
* whose own path router (`router.tsx`) then resolves the specific page via `registry.tsx`.
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolveBootScreen(pathname: string): BootScreen;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bundle's BOOT SCREEN selector — PRD-003c (m-AC-6 / m-AC-7 / m-AC-8).
|
|
3
|
+
*
|
|
4
|
+
* By the time this bundle runs, hive's SERVER gate (`hive/src/daemon/gate.ts`, PRD-003a)
|
|
5
|
+
* has already evaluated health-then-auth and served the shell ONLY for a path it authorized:
|
|
6
|
+
* `/buzzing` (fleet not healthy, or landed there directly — always exempt), `/login` (logged out,
|
|
7
|
+
* or landed there directly — always exempt), or any other path (healthy + authenticated). This
|
|
8
|
+
* pure function is the whole boot decision `main.tsx` needs: a plain lookup from `location.pathname`
|
|
9
|
+
* to which top-level screen to mount. It never re-derives health or auth itself — doing so would
|
|
10
|
+
* resurrect the retired `ReadinessSplash`→`SetupGate` nested client gate (ADR-0004, m-AC-6).
|
|
11
|
+
*/
|
|
12
|
+
/** The `/buzzing` route (PRD-002's readiness splash, now the gate-exempt readiness screen). */
|
|
13
|
+
export const BUZZING_PATH = "/buzzing";
|
|
14
|
+
/** The `/login` route (PRD-003b's device-flow guided setup, now the gate-exempt login screen). */
|
|
15
|
+
export const LOGIN_PATH = "/login";
|
|
16
|
+
/**
|
|
17
|
+
* Resolve which top-level screen `main.tsx` should mount for the current path. `/buzzing` and
|
|
18
|
+
* `/login` are the two gate-exempt screens (m-AC-7 / m-AC-8); every other path — including `/` and
|
|
19
|
+
* every registry route (`/projects`, `/harnesses`, ...) — mounts the authenticated `<Shell>`,
|
|
20
|
+
* whose own path router (`router.tsx`) then resolves the specific page via `registry.tsx`.
|
|
21
|
+
*/
|
|
22
|
+
export function resolveBootScreen(pathname) {
|
|
23
|
+
if (pathname === BUZZING_PATH)
|
|
24
|
+
return "buzzing";
|
|
25
|
+
if (pathname === LOGIN_PATH)
|
|
26
|
+
return "login";
|
|
27
|
+
return "shell";
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=boot-route.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boot-route.js","sourceRoot":"","sources":["../../../src/dashboard/web/boot-route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,+FAA+F;AAC/F,MAAM,CAAC,MAAM,YAAY,GAAG,UAAmB,CAAC;AAEhD,kGAAkG;AAClG,MAAM,CAAC,MAAM,UAAU,GAAG,QAAiB,CAAC;AAE5C;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IACjD,IAAI,QAAQ,KAAK,YAAY;QAAE,OAAO,SAAS,CAAC;IAChD,IAAI,QAAQ,KAAK,UAAU;QAAE,OAAO,OAAO,CAAC;IAC5C,OAAO,OAAO,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shared "Build graph" BUTTON — the codebase-graph build trigger, used on BOTH graph surfaces.
|
|
3
|
+
*
|
|
4
|
+
* Two places show the codebase-graph empty state — the full-page Graph (`pages/graph.tsx`) and the home
|
|
5
|
+
* GraphPanel (`panels.tsx`). Before, each showed a dead `honeycomb graph build` CLI hint (a command that
|
|
6
|
+
* does not exist — the graph is ONLY buildable from this UI). This single component replaces that with a
|
|
7
|
+
* working primary button wired to the daemon's already-served `POST /api/graph/build` (via
|
|
8
|
+
* `wire.buildGraph()`), so the logic lives in ONE place (jscpd discipline) and both surfaces behave
|
|
9
|
+
* identically.
|
|
10
|
+
*
|
|
11
|
+
* Behavior (mirrors the shell's `pollinate` action in `app.tsx`):
|
|
12
|
+
* - A SYNCHRONOUS in-flight ref guards re-entry: `setBuilding(true)` is async, so a render-state guard
|
|
13
|
+
* alone leaves a race where a rapid double-click fires `wire.buildGraph()` twice. The ref flips
|
|
14
|
+
* IMMEDIATELY, so exactly one POST goes out per build even on a double-click.
|
|
15
|
+
* - While in flight the button reads "Building…" and is disabled.
|
|
16
|
+
* - On a `{ built: true }` ack it calls `onBuilt()` so the surface RE-HYDRATES its graph source (re-run
|
|
17
|
+
* `wire.graph()`) and the fresh LOCAL snapshot renders WITHOUT a manual reload (no eventual-consistency
|
|
18
|
+
* wait — the build wrote a local file the next `GET /api/graph` reads immediately).
|
|
19
|
+
* - On a `{ built: false }` ack (the daemon rejected/failed, or the wire timed out/degraded) it shows an
|
|
20
|
+
* honest inline error line inviting a retry — the same button is right there, so there is NO CLI hint
|
|
21
|
+
* (the old `honeycomb graph build` command never existed).
|
|
22
|
+
*
|
|
23
|
+
* The wire NEVER throws (it degrades to a failure ack), but the host's `onBuilt` re-hydrate MAY (its type
|
|
24
|
+
* allows an async refresh). A `try/finally` therefore always clears the in-flight ref AND the "Building…"
|
|
25
|
+
* state, so a throwing re-hydrate can never wedge the button permanently disabled or stuck. Every visual
|
|
26
|
+
* value is an existing `var(--…)` DS token; no new dependency.
|
|
27
|
+
*/
|
|
28
|
+
import React from "react";
|
|
29
|
+
import type { WireClient } from "./wire.js";
|
|
30
|
+
/** Props for {@link BuildGraphButton}. */
|
|
31
|
+
export interface BuildGraphButtonProps {
|
|
32
|
+
/** The shared wire client (the page/panel threads the one the shell built — never `createWireClient`). */
|
|
33
|
+
readonly wire: WireClient;
|
|
34
|
+
/**
|
|
35
|
+
* Called after a SUCCESSFUL build (`{ built: true }`) so the host surface re-hydrates its graph source
|
|
36
|
+
* (re-run `wire.graph()`) and the fresh snapshot renders without a reload. May be async; its result is
|
|
37
|
+
* awaited so the button stays in flight until the re-hydrate settles.
|
|
38
|
+
*/
|
|
39
|
+
readonly onBuilt: () => void | Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The shared Build-graph button. Renders a primary button that triggers the real daemon build, re-hydrates
|
|
43
|
+
* the active graph on success, and surfaces an honest inline error inviting a retry on failure. Mirrors the
|
|
44
|
+
* `pollinateInFlightRef` synchronous re-entry guard so a double-click POSTs exactly once.
|
|
45
|
+
*/
|
|
46
|
+
export declare function BuildGraphButton({ wire, onBuilt }: BuildGraphButtonProps): React.JSX.Element;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* The shared "Build graph" BUTTON — the codebase-graph build trigger, used on BOTH graph surfaces.
|
|
4
|
+
*
|
|
5
|
+
* Two places show the codebase-graph empty state — the full-page Graph (`pages/graph.tsx`) and the home
|
|
6
|
+
* GraphPanel (`panels.tsx`). Before, each showed a dead `honeycomb graph build` CLI hint (a command that
|
|
7
|
+
* does not exist — the graph is ONLY buildable from this UI). This single component replaces that with a
|
|
8
|
+
* working primary button wired to the daemon's already-served `POST /api/graph/build` (via
|
|
9
|
+
* `wire.buildGraph()`), so the logic lives in ONE place (jscpd discipline) and both surfaces behave
|
|
10
|
+
* identically.
|
|
11
|
+
*
|
|
12
|
+
* Behavior (mirrors the shell's `pollinate` action in `app.tsx`):
|
|
13
|
+
* - A SYNCHRONOUS in-flight ref guards re-entry: `setBuilding(true)` is async, so a render-state guard
|
|
14
|
+
* alone leaves a race where a rapid double-click fires `wire.buildGraph()` twice. The ref flips
|
|
15
|
+
* IMMEDIATELY, so exactly one POST goes out per build even on a double-click.
|
|
16
|
+
* - While in flight the button reads "Building…" and is disabled.
|
|
17
|
+
* - On a `{ built: true }` ack it calls `onBuilt()` so the surface RE-HYDRATES its graph source (re-run
|
|
18
|
+
* `wire.graph()`) and the fresh LOCAL snapshot renders WITHOUT a manual reload (no eventual-consistency
|
|
19
|
+
* wait — the build wrote a local file the next `GET /api/graph` reads immediately).
|
|
20
|
+
* - On a `{ built: false }` ack (the daemon rejected/failed, or the wire timed out/degraded) it shows an
|
|
21
|
+
* honest inline error line inviting a retry — the same button is right there, so there is NO CLI hint
|
|
22
|
+
* (the old `honeycomb graph build` command never existed).
|
|
23
|
+
*
|
|
24
|
+
* The wire NEVER throws (it degrades to a failure ack), but the host's `onBuilt` re-hydrate MAY (its type
|
|
25
|
+
* allows an async refresh). A `try/finally` therefore always clears the in-flight ref AND the "Building…"
|
|
26
|
+
* state, so a throwing re-hydrate can never wedge the button permanently disabled or stuck. Every visual
|
|
27
|
+
* value is an existing `var(--…)` DS token; no new dependency.
|
|
28
|
+
*/
|
|
29
|
+
import React from "react";
|
|
30
|
+
import { Button } from "./primitives.js";
|
|
31
|
+
/**
|
|
32
|
+
* The shared Build-graph button. Renders a primary button that triggers the real daemon build, re-hydrates
|
|
33
|
+
* the active graph on success, and surfaces an honest inline error inviting a retry on failure. Mirrors the
|
|
34
|
+
* `pollinateInFlightRef` synchronous re-entry guard so a double-click POSTs exactly once.
|
|
35
|
+
*/
|
|
36
|
+
export function BuildGraphButton({ wire, onBuilt }) {
|
|
37
|
+
const [building, setBuilding] = React.useState(false);
|
|
38
|
+
const [failed, setFailed] = React.useState(false);
|
|
39
|
+
// Synchronous re-entry guard (no double POST on rapid clicks — mirrors app.tsx's pollinateInFlightRef).
|
|
40
|
+
const inFlightRef = React.useRef(false);
|
|
41
|
+
// Guard against a state update after unmount (the build is slow; the surface may unmount mid-build).
|
|
42
|
+
const aliveRef = React.useRef(true);
|
|
43
|
+
React.useEffect(() => {
|
|
44
|
+
aliveRef.current = true;
|
|
45
|
+
return () => {
|
|
46
|
+
aliveRef.current = false;
|
|
47
|
+
};
|
|
48
|
+
}, []);
|
|
49
|
+
const onBuild = React.useCallback(async () => {
|
|
50
|
+
if (inFlightRef.current)
|
|
51
|
+
return; // synchronous guard — exactly one POST per build
|
|
52
|
+
inFlightRef.current = true;
|
|
53
|
+
setBuilding(true);
|
|
54
|
+
setFailed(false);
|
|
55
|
+
let built = false;
|
|
56
|
+
try {
|
|
57
|
+
const ack = await wire.buildGraph();
|
|
58
|
+
built = ack.built;
|
|
59
|
+
if (ack.built) {
|
|
60
|
+
// Re-hydrate the active graph source so the fresh LOCAL snapshot renders without a reload.
|
|
61
|
+
await onBuilt();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// The wire itself never throws; a throwing `onBuilt` re-hydrate must NOT wedge the guard. The
|
|
66
|
+
// build already succeeded if `built` is true (the snapshot is on disk) — we just couldn't refresh
|
|
67
|
+
// the view, so we don't flag it as a build failure; the `void onBuild()` caller swallows nothing.
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
// ALWAYS clear the guard + the in-flight visual, on every path — otherwise a throw leaves the
|
|
71
|
+
// button permanently disabled and stuck on "Building…".
|
|
72
|
+
inFlightRef.current = false;
|
|
73
|
+
if (aliveRef.current) {
|
|
74
|
+
setBuilding(false);
|
|
75
|
+
setFailed(!built);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}, [wire, onBuilt]);
|
|
79
|
+
return (_jsxs("div", { "data-testid": "build-graph", style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 8 }, children: [_jsx(Button, { variant: "primary", onClick: () => void onBuild(), disabled: building, "data-testid": "build-graph-button", children: building ? "Building…" : "Build graph" }), failed && (_jsx("span", { "data-testid": "build-graph-error", style: { fontFamily: "var(--font-mono)", fontSize: 12, color: "var(--severity-critical)" }, children: "Build failed \u2014 the daemon could not build the graph. Try again." }))] }));
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=build-graph-button.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-graph-button.js","sourceRoot":"","sources":["../../../src/dashboard/web/build-graph-button.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAezC;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAAE,IAAI,EAAE,OAAO,EAAyB;IACxE,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACtD,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAClD,wGAAwG;IACxG,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACxC,qGAAqG;IACrG,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACpC,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACpB,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC;QACxB,OAAO,GAAG,EAAE;YACX,QAAQ,CAAC,OAAO,GAAG,KAAK,CAAC;QAC1B,CAAC,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,OAAO,GAAG,KAAK,CAAC,WAAW,CAAC,KAAK,IAAmB,EAAE;QAC3D,IAAI,WAAW,CAAC,OAAO;YAAE,OAAO,CAAC,iDAAiD;QAClF,WAAW,CAAC,OAAO,GAAG,IAAI,CAAC;QAC3B,WAAW,CAAC,IAAI,CAAC,CAAC;QAClB,SAAS,CAAC,KAAK,CAAC,CAAC;QACjB,IAAI,KAAK,GAAG,KAAK,CAAC;QAClB,IAAI,CAAC;YACJ,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;YACpC,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC;YAClB,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;gBACf,2FAA2F;gBAC3F,MAAM,OAAO,EAAE,CAAC;YACjB,CAAC;QACF,CAAC;QAAC,MAAM,CAAC;YACR,8FAA8F;YAC9F,kGAAkG;YAClG,kGAAkG;QACnG,CAAC;gBAAS,CAAC;YACV,8FAA8F;YAC9F,wDAAwD;YACxD,WAAW,CAAC,OAAO,GAAG,KAAK,CAAC;YAC5B,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;gBACtB,WAAW,CAAC,KAAK,CAAC,CAAC;gBACnB,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC;YACnB,CAAC;QACF,CAAC;IACF,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IAEpB,OAAO,CACN,8BAAiB,aAAa,EAAC,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,EAAE,aAC/G,KAAC,MAAM,IAAC,OAAO,EAAC,SAAS,EAAC,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,OAAO,EAAE,EAAE,QAAQ,EAAE,QAAQ,iBAAc,oBAAoB,YAC3G,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,aAAa,GAC/B,EACR,MAAM,IAAI,CACV,8BAAkB,mBAAmB,EAAC,KAAK,EAAE,EAAE,UAAU,EAAE,kBAAkB,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,0BAA0B,EAAE,qFAEzH,CACP,IACI,CACN,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `/buzzing` READINESS screen — hive PRD-004a. The addressable successor to PRD-002's
|
|
3
|
+
* `ReadinessSplash` (retired by this PRD, per PRD-004's "Overlap and supersession": "the
|
|
4
|
+
* `ReadinessSplash` becomes `/buzzing`"), now rendering the real per-service tile grid ADR-0004
|
|
5
|
+
* calls for instead of a coarse per-daemon list.
|
|
6
|
+
*
|
|
7
|
+
* Two independent concerns, kept deliberately separate:
|
|
8
|
+
* - DISMISSAL (bz-AC-9/bz-AC-10): polls `GET /api/fleet-status` and reuses the EXISTING, pinned
|
|
9
|
+
* `isFleetReady()` predicate (PRD-002a) — the identical rule the server gate (`gate.ts`)
|
|
10
|
+
* applies — so "ready" means one thing everywhere. Once ready, this screen hard-navigates to
|
|
11
|
+
* `/` so the SERVER gate re-evaluates health+auth on the fresh request and lands the operator
|
|
12
|
+
* on the dashboard or `/login`, whichever the (now-current) auth state calls for.
|
|
13
|
+
* - TILE RENDERING (bz-AC-1..8): the shared {@link useFleetTelemetry} hook (SSE-first,
|
|
14
|
+
* `/api/fleet-status` fail-soft), rendered one tile per registered service via the shared
|
|
15
|
+
* {@link ServiceStateIcon} vocabulary (PRD-004b). A single bad service only ever changes ITS
|
|
16
|
+
* tile (sd-AC-8/sd-AC-9) — the tile list itself is keyed by name and independent per row.
|
|
17
|
+
*/
|
|
18
|
+
import React from "react";
|
|
19
|
+
export interface BuzzingScreenProps {
|
|
20
|
+
readonly assetBase: string;
|
|
21
|
+
/** Override the `/api/fleet-status` dismissal-poll interval (ms). Defaults to 1500. */
|
|
22
|
+
readonly pollMs?: number;
|
|
23
|
+
/** Test seam: called instead of the real navigation once the fleet is ready. */
|
|
24
|
+
readonly onReady?: () => void;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The `/buzzing` readiness screen. Uses the shared telemetry hook for the tile grid and its own,
|
|
28
|
+
* independent `isFleetReady()` poll for dismissal (see module doc for why the two are separate).
|
|
29
|
+
*/
|
|
30
|
+
export declare function BuzzingScreen({ assetBase, pollMs, onReady }: BuzzingScreenProps): React.JSX.Element;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* The `/buzzing` READINESS screen — hive PRD-004a. The addressable successor to PRD-002's
|
|
4
|
+
* `ReadinessSplash` (retired by this PRD, per PRD-004's "Overlap and supersession": "the
|
|
5
|
+
* `ReadinessSplash` becomes `/buzzing`"), now rendering the real per-service tile grid ADR-0004
|
|
6
|
+
* calls for instead of a coarse per-daemon list.
|
|
7
|
+
*
|
|
8
|
+
* Two independent concerns, kept deliberately separate:
|
|
9
|
+
* - DISMISSAL (bz-AC-9/bz-AC-10): polls `GET /api/fleet-status` and reuses the EXISTING, pinned
|
|
10
|
+
* `isFleetReady()` predicate (PRD-002a) — the identical rule the server gate (`gate.ts`)
|
|
11
|
+
* applies — so "ready" means one thing everywhere. Once ready, this screen hard-navigates to
|
|
12
|
+
* `/` so the SERVER gate re-evaluates health+auth on the fresh request and lands the operator
|
|
13
|
+
* on the dashboard or `/login`, whichever the (now-current) auth state calls for.
|
|
14
|
+
* - TILE RENDERING (bz-AC-1..8): the shared {@link useFleetTelemetry} hook (SSE-first,
|
|
15
|
+
* `/api/fleet-status` fail-soft), rendered one tile per registered service via the shared
|
|
16
|
+
* {@link ServiceStateIcon} vocabulary (PRD-004b). A single bad service only ever changes ITS
|
|
17
|
+
* tile (sd-AC-8/sd-AC-9) — the tile list itself is keyed by name and independent per row.
|
|
18
|
+
*/
|
|
19
|
+
import React from "react";
|
|
20
|
+
import { isFleetReady } from "../../shared/fleet-readiness.js";
|
|
21
|
+
import { SERVICE_STATE_COLOR, SERVICE_STATE_LABEL, ServiceStateIcon } from "./service-icons.js";
|
|
22
|
+
import { useFleetTelemetry } from "./use-fleet-telemetry.js";
|
|
23
|
+
/** One service tile (bz-AC-3): the shared bee-state icon, the service name, and its state label. */
|
|
24
|
+
function ServiceTile({ service }) {
|
|
25
|
+
return (_jsxs("li", { "data-testid": `buzzing-tile-${service.name}`, "data-state": service.state, style: {
|
|
26
|
+
display: "flex",
|
|
27
|
+
alignItems: "center",
|
|
28
|
+
gap: 12,
|
|
29
|
+
padding: "10px 14px",
|
|
30
|
+
borderRadius: "var(--radius-md)",
|
|
31
|
+
border: "1px solid var(--border-subtle)",
|
|
32
|
+
background: "var(--bg-elevated)",
|
|
33
|
+
}, children: [_jsx("span", { style: { color: SERVICE_STATE_COLOR[service.state], display: "inline-flex", flex: "none" }, children: _jsx(ServiceStateIcon, { state: service.state }) }), _jsx("span", { style: { flex: 1, fontFamily: "var(--font-sans)", fontSize: "var(--text-sm)", color: "var(--text-primary)" }, children: service.name }), _jsx("span", { "data-testid": `buzzing-tile-state-${service.name}`, style: { fontFamily: "var(--font-mono)", fontSize: "var(--text-xs)", color: "var(--text-secondary)", textTransform: "lowercase" }, children: SERVICE_STATE_LABEL[service.state] })] }));
|
|
34
|
+
}
|
|
35
|
+
/** The distinct "no service enumerated yet" indicator (mirrors the old doctor-unreachable state). */
|
|
36
|
+
function AwaitingRegistrationIndicator() {
|
|
37
|
+
return (_jsxs("div", { "data-testid": "buzzing-empty", style: {
|
|
38
|
+
display: "flex",
|
|
39
|
+
flexDirection: "column",
|
|
40
|
+
alignItems: "center",
|
|
41
|
+
gap: 12,
|
|
42
|
+
width: "100%",
|
|
43
|
+
padding: "16px 20px",
|
|
44
|
+
background: "var(--bg-inset)",
|
|
45
|
+
border: "1px solid var(--border-subtle)",
|
|
46
|
+
borderRadius: "var(--radius-lg)",
|
|
47
|
+
}, children: [_jsx("span", { role: "img", "aria-label": "doctor supervisor: unreachable", style: { width: 8, height: 8, borderRadius: "50%", background: "var(--text-tertiary)", animation: "hc-readiness-pulse var(--dur-pollinate) var(--ease-in-out) infinite alternate" } }), _jsx("p", { style: { fontSize: "var(--text-sm)", color: "var(--text-secondary)", margin: 0, lineHeight: 1.5, textAlign: "center" }, children: "Waiting on doctor. No services are registered yet." })] }));
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The `/buzzing` readiness screen. Uses the shared telemetry hook for the tile grid and its own,
|
|
51
|
+
* independent `isFleetReady()` poll for dismissal (see module doc for why the two are separate).
|
|
52
|
+
*/
|
|
53
|
+
export function BuzzingScreen({ assetBase, pollMs = 1500, onReady }) {
|
|
54
|
+
const telemetry = useFleetTelemetry();
|
|
55
|
+
const [ready, setReady] = React.useState(false);
|
|
56
|
+
// bz-AC-9/bz-AC-10: reuse the pinned `isFleetReady()` rule so dismissal means the same thing
|
|
57
|
+
// here as it does in the server gate. Stops polling once ready (sticky, mirrors the retired
|
|
58
|
+
// ReadinessSplash's rs-AC-7/rs-AC-9 discipline).
|
|
59
|
+
React.useEffect(() => {
|
|
60
|
+
if (ready)
|
|
61
|
+
return;
|
|
62
|
+
let alive = true;
|
|
63
|
+
// One readiness request at a time: a slow/hung daemon must never stack overlapping polls
|
|
64
|
+
// (this screen already shares the endpoint with useFleetTelemetry's own fallback poll).
|
|
65
|
+
let inFlight = false;
|
|
66
|
+
const tick = async () => {
|
|
67
|
+
if (inFlight)
|
|
68
|
+
return;
|
|
69
|
+
inFlight = true;
|
|
70
|
+
try {
|
|
71
|
+
const response = await fetch("/api/fleet-status");
|
|
72
|
+
const next = (await response.json());
|
|
73
|
+
if (!alive)
|
|
74
|
+
return;
|
|
75
|
+
if (isFleetReady(next))
|
|
76
|
+
setReady(true);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// Keep the screen visible; the next poll retries.
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
inFlight = false;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
void tick();
|
|
86
|
+
const id = setInterval(() => void tick(), pollMs);
|
|
87
|
+
return () => {
|
|
88
|
+
alive = false;
|
|
89
|
+
clearInterval(id);
|
|
90
|
+
};
|
|
91
|
+
}, [ready, pollMs]);
|
|
92
|
+
// On ready: navigate away. A HARD navigation (not a client-side route swap) so the SERVER gate
|
|
93
|
+
// re-runs health+auth on the fresh request (PRD-003a) and lands the operator correctly.
|
|
94
|
+
React.useEffect(() => {
|
|
95
|
+
if (!ready)
|
|
96
|
+
return;
|
|
97
|
+
if (onReady !== undefined) {
|
|
98
|
+
onReady();
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (typeof window !== "undefined")
|
|
102
|
+
window.location.assign("/");
|
|
103
|
+
}, [ready, onReady]);
|
|
104
|
+
return (_jsxs("div", { "data-testid": "buzzing-screen", style: {
|
|
105
|
+
display: "flex",
|
|
106
|
+
alignItems: "center",
|
|
107
|
+
justifyContent: "center",
|
|
108
|
+
minHeight: "100vh",
|
|
109
|
+
padding: 32,
|
|
110
|
+
background: "var(--bg-canvas)",
|
|
111
|
+
textAlign: "center",
|
|
112
|
+
}, children: [_jsxs("div", { style: {
|
|
113
|
+
display: "flex",
|
|
114
|
+
flexDirection: "column",
|
|
115
|
+
alignItems: "center",
|
|
116
|
+
gap: 24,
|
|
117
|
+
width: "100%",
|
|
118
|
+
maxWidth: 460,
|
|
119
|
+
padding: "40px 32px",
|
|
120
|
+
background: "var(--bg-surface)",
|
|
121
|
+
border: "1px solid var(--border-default)",
|
|
122
|
+
borderRadius: "var(--radius-xl)",
|
|
123
|
+
}, children: [_jsx("span", { "aria-hidden": "true", style: {
|
|
124
|
+
display: "inline-flex",
|
|
125
|
+
alignItems: "center",
|
|
126
|
+
justifyContent: "center",
|
|
127
|
+
flex: "none",
|
|
128
|
+
width: 88,
|
|
129
|
+
height: 88,
|
|
130
|
+
borderRadius: "50%",
|
|
131
|
+
background: "var(--honey-subtle)",
|
|
132
|
+
border: "1px solid var(--honey-border)",
|
|
133
|
+
}, children: _jsx("img", { src: `${assetBase}/honeycomb-memory-cluster.svg`, width: 48, height: 48, alt: "" }) }), _jsx("h1", { style: { fontSize: "var(--text-xl)", fontWeight: 700, color: "var(--text-primary)", margin: 0, letterSpacing: "-0.02em" }, children: "Waiting for the hive\u2026" }), _jsxs("div", { "data-testid": "buzzing-status-region", role: "status", "aria-live": "polite", style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 12, width: "100%" }, children: [_jsx("p", { style: { fontSize: "var(--text-sm)", color: "var(--text-secondary)", margin: 0, lineHeight: 1.5 }, children: "Starting required services. This usually takes a few seconds on a cold boot." }), telemetry.services.length === 0 ? (_jsx(AwaitingRegistrationIndicator, {})) : (_jsx("ul", { "data-testid": "buzzing-tile-grid", style: { listStyle: "none", margin: 0, padding: 0, display: "flex", flexDirection: "column", gap: 8, width: "100%", textAlign: "left" }, children: telemetry.services.map((service) => (_jsx(ServiceTile, { service: service }, service.name))) }))] })] }), _jsx("style", { children: "@keyframes hc-readiness-pulse { from { opacity: .45 } to { opacity: 1 } }" })] }));
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=buzzing-screen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buzzing-screen.js","sourceRoot":"","sources":["../../../src/dashboard/web/buzzing-screen.tsx"],"names":[],"mappings":"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"}
|