@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.
Files changed (190) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +262 -0
  3. package/dist/cli-commands.d.ts +41 -0
  4. package/dist/cli-commands.js +76 -0
  5. package/dist/cli-commands.js.map +1 -0
  6. package/dist/cli.d.ts +2 -0
  7. package/dist/cli.js +46 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/daemon/dashboard/app.js +118 -0
  10. package/dist/daemon/dashboard/host.d.ts +90 -0
  11. package/dist/daemon/dashboard/host.js +172 -0
  12. package/dist/daemon/dashboard/host.js.map +1 -0
  13. package/dist/daemon/dashboard/web-assets.d.ts +88 -0
  14. package/dist/daemon/dashboard/web-assets.js +195 -0
  15. package/dist/daemon/dashboard/web-assets.js.map +1 -0
  16. package/dist/daemon/fleet-status.d.ts +15 -0
  17. package/dist/daemon/fleet-status.js +56 -0
  18. package/dist/daemon/fleet-status.js.map +1 -0
  19. package/dist/daemon/gate.d.ts +55 -0
  20. package/dist/daemon/gate.js +128 -0
  21. package/dist/daemon/gate.js.map +1 -0
  22. package/dist/daemon/proxy.d.ts +38 -0
  23. package/dist/daemon/proxy.js +126 -0
  24. package/dist/daemon/proxy.js.map +1 -0
  25. package/dist/daemon/registry.d.ts +31 -0
  26. package/dist/daemon/registry.js +120 -0
  27. package/dist/daemon/registry.js.map +1 -0
  28. package/dist/daemon/server.d.ts +41 -0
  29. package/dist/daemon/server.js +126 -0
  30. package/dist/daemon/server.js.map +1 -0
  31. package/dist/daemon/setup-auth.d.ts +37 -0
  32. package/dist/daemon/setup-auth.js +51 -0
  33. package/dist/daemon/setup-auth.js.map +1 -0
  34. package/dist/daemon/telemetry-proxy.d.ts +47 -0
  35. package/dist/daemon/telemetry-proxy.js +63 -0
  36. package/dist/daemon/telemetry-proxy.js.map +1 -0
  37. package/dist/dashboard/contracts.d.ts +235 -0
  38. package/dist/dashboard/contracts.js +43 -0
  39. package/dist/dashboard/contracts.js.map +1 -0
  40. package/dist/dashboard/web/app.d.ts +44 -0
  41. package/dist/dashboard/web/app.js +179 -0
  42. package/dist/dashboard/web/app.js.map +1 -0
  43. package/dist/dashboard/web/boot-route.d.ts +23 -0
  44. package/dist/dashboard/web/boot-route.js +29 -0
  45. package/dist/dashboard/web/boot-route.js.map +1 -0
  46. package/dist/dashboard/web/build-graph-button.d.ts +46 -0
  47. package/dist/dashboard/web/build-graph-button.js +81 -0
  48. package/dist/dashboard/web/build-graph-button.js.map +1 -0
  49. package/dist/dashboard/web/buzzing-screen.d.ts +30 -0
  50. package/dist/dashboard/web/buzzing-screen.js +135 -0
  51. package/dist/dashboard/web/buzzing-screen.js.map +1 -0
  52. package/dist/dashboard/web/folder-picker.d.ts +54 -0
  53. package/dist/dashboard/web/folder-picker.js +125 -0
  54. package/dist/dashboard/web/folder-picker.js.map +1 -0
  55. package/dist/dashboard/web/graph-layout.d.ts +101 -0
  56. package/dist/dashboard/web/graph-layout.js +0 -0
  57. package/dist/dashboard/web/graph-layout.js.map +1 -0
  58. package/dist/dashboard/web/harness-strip.d.ts +38 -0
  59. package/dist/dashboard/web/harness-strip.js +65 -0
  60. package/dist/dashboard/web/harness-strip.js.map +1 -0
  61. package/dist/dashboard/web/health-rail.d.ts +26 -0
  62. package/dist/dashboard/web/health-rail.js +61 -0
  63. package/dist/dashboard/web/health-rail.js.map +1 -0
  64. package/dist/dashboard/web/main.d.ts +22 -0
  65. package/dist/dashboard/web/main.js +51 -0
  66. package/dist/dashboard/web/main.js.map +1 -0
  67. package/dist/dashboard/web/needs-project.d.ts +51 -0
  68. package/dist/dashboard/web/needs-project.js +69 -0
  69. package/dist/dashboard/web/needs-project.js.map +1 -0
  70. package/dist/dashboard/web/page-frame.d.ts +95 -0
  71. package/dist/dashboard/web/page-frame.js +98 -0
  72. package/dist/dashboard/web/page-frame.js.map +1 -0
  73. package/dist/dashboard/web/pages/coming-soon.d.ts +23 -0
  74. package/dist/dashboard/web/pages/coming-soon.js +19 -0
  75. package/dist/dashboard/web/pages/coming-soon.js.map +1 -0
  76. package/dist/dashboard/web/pages/dashboard.d.ts +32 -0
  77. package/dist/dashboard/web/pages/dashboard.js +205 -0
  78. package/dist/dashboard/web/pages/dashboard.js.map +1 -0
  79. package/dist/dashboard/web/pages/graph.d.ts +72 -0
  80. package/dist/dashboard/web/pages/graph.js +362 -0
  81. package/dist/dashboard/web/pages/graph.js.map +1 -0
  82. package/dist/dashboard/web/pages/harnesses.d.ts +65 -0
  83. package/dist/dashboard/web/pages/harnesses.js +210 -0
  84. package/dist/dashboard/web/pages/harnesses.js.map +1 -0
  85. package/dist/dashboard/web/pages/health.d.ts +22 -0
  86. package/dist/dashboard/web/pages/health.js +91 -0
  87. package/dist/dashboard/web/pages/health.js.map +1 -0
  88. package/dist/dashboard/web/pages/lifecycle-panel.d.ts +60 -0
  89. package/dist/dashboard/web/pages/lifecycle-panel.js +187 -0
  90. package/dist/dashboard/web/pages/lifecycle-panel.js.map +1 -0
  91. package/dist/dashboard/web/pages/logs.d.ts +55 -0
  92. package/dist/dashboard/web/pages/logs.js +311 -0
  93. package/dist/dashboard/web/pages/logs.js.map +1 -0
  94. package/dist/dashboard/web/pages/memories.d.ts +35 -0
  95. package/dist/dashboard/web/pages/memories.js +439 -0
  96. package/dist/dashboard/web/pages/memories.js.map +1 -0
  97. package/dist/dashboard/web/pages/projects.d.ts +38 -0
  98. package/dist/dashboard/web/pages/projects.js +325 -0
  99. package/dist/dashboard/web/pages/projects.js.map +1 -0
  100. package/dist/dashboard/web/pages/roi-chart.d.ts +52 -0
  101. package/dist/dashboard/web/pages/roi-chart.js +133 -0
  102. package/dist/dashboard/web/pages/roi-chart.js.map +1 -0
  103. package/dist/dashboard/web/pages/roi.d.ts +93 -0
  104. package/dist/dashboard/web/pages/roi.js +375 -0
  105. package/dist/dashboard/web/pages/roi.js.map +1 -0
  106. package/dist/dashboard/web/pages/settings.d.ts +103 -0
  107. package/dist/dashboard/web/pages/settings.js +420 -0
  108. package/dist/dashboard/web/pages/settings.js.map +1 -0
  109. package/dist/dashboard/web/pages/sync.d.ts +59 -0
  110. package/dist/dashboard/web/pages/sync.js +280 -0
  111. package/dist/dashboard/web/pages/sync.js.map +1 -0
  112. package/dist/dashboard/web/panels.d.ts +178 -0
  113. package/dist/dashboard/web/panels.js +471 -0
  114. package/dist/dashboard/web/panels.js.map +1 -0
  115. package/dist/dashboard/web/primitives.d.ts +80 -0
  116. package/dist/dashboard/web/primitives.js +208 -0
  117. package/dist/dashboard/web/primitives.js.map +1 -0
  118. package/dist/dashboard/web/readiness-splash.d.ts +39 -0
  119. package/dist/dashboard/web/readiness-splash.js +201 -0
  120. package/dist/dashboard/web/readiness-splash.js.map +1 -0
  121. package/dist/dashboard/web/registry.d.ts +96 -0
  122. package/dist/dashboard/web/registry.js +99 -0
  123. package/dist/dashboard/web/registry.js.map +1 -0
  124. package/dist/dashboard/web/router.d.ts +48 -0
  125. package/dist/dashboard/web/router.js +76 -0
  126. package/dist/dashboard/web/router.js.map +1 -0
  127. package/dist/dashboard/web/scope-context.d.ts +155 -0
  128. package/dist/dashboard/web/scope-context.js +266 -0
  129. package/dist/dashboard/web/scope-context.js.map +1 -0
  130. package/dist/dashboard/web/service-icons.d.ts +33 -0
  131. package/dist/dashboard/web/service-icons.js +80 -0
  132. package/dist/dashboard/web/service-icons.js.map +1 -0
  133. package/dist/dashboard/web/setup-gate.d.ts +116 -0
  134. package/dist/dashboard/web/setup-gate.js +272 -0
  135. package/dist/dashboard/web/setup-gate.js.map +1 -0
  136. package/dist/dashboard/web/sidebar.d.ts +63 -0
  137. package/dist/dashboard/web/sidebar.js +91 -0
  138. package/dist/dashboard/web/sidebar.js.map +1 -0
  139. package/dist/dashboard/web/use-fleet-telemetry.d.ts +111 -0
  140. package/dist/dashboard/web/use-fleet-telemetry.js +278 -0
  141. package/dist/dashboard/web/use-fleet-telemetry.js.map +1 -0
  142. package/dist/dashboard/web/wire.d.ts +1937 -0
  143. package/dist/dashboard/web/wire.js +1837 -0
  144. package/dist/dashboard/web/wire.js.map +1 -0
  145. package/dist/errors.d.ts +5 -0
  146. package/dist/errors.js +11 -0
  147. package/dist/errors.js.map +1 -0
  148. package/dist/install/registry.d.ts +35 -0
  149. package/dist/install/registry.js +110 -0
  150. package/dist/install/registry.js.map +1 -0
  151. package/dist/lock.d.ts +10 -0
  152. package/dist/lock.js +87 -0
  153. package/dist/lock.js.map +1 -0
  154. package/dist/service/commands.d.ts +15 -0
  155. package/dist/service/commands.js +49 -0
  156. package/dist/service/commands.js.map +1 -0
  157. package/dist/service/index.d.ts +38 -0
  158. package/dist/service/index.js +174 -0
  159. package/dist/service/index.js.map +1 -0
  160. package/dist/service/platform.d.ts +37 -0
  161. package/dist/service/platform.js +67 -0
  162. package/dist/service/platform.js.map +1 -0
  163. package/dist/service/templates.d.ts +10 -0
  164. package/dist/service/templates.js +120 -0
  165. package/dist/service/templates.js.map +1 -0
  166. package/dist/shared/constants.d.ts +15 -0
  167. package/dist/shared/constants.js +19 -0
  168. package/dist/shared/constants.js.map +1 -0
  169. package/dist/shared/daemon-routing.d.ts +11 -0
  170. package/dist/shared/daemon-routing.js +38 -0
  171. package/dist/shared/daemon-routing.js.map +1 -0
  172. package/dist/shared/fleet-readiness.d.ts +18 -0
  173. package/dist/shared/fleet-readiness.js +9 -0
  174. package/dist/shared/fleet-readiness.js.map +1 -0
  175. package/dist/shared/fleet-telemetry.d.ts +68 -0
  176. package/dist/shared/fleet-telemetry.js +61 -0
  177. package/dist/shared/fleet-telemetry.js.map +1 -0
  178. package/dist/shared/lifecycle-flags.d.ts +34 -0
  179. package/dist/shared/lifecycle-flags.js +39 -0
  180. package/dist/shared/lifecycle-flags.js.map +1 -0
  181. package/dist/shared/memory-types.d.ts +55 -0
  182. package/dist/shared/memory-types.js +75 -0
  183. package/dist/shared/memory-types.js.map +1 -0
  184. package/dist/shared/service-status.d.ts +82 -0
  185. package/dist/shared/service-status.js +97 -0
  186. package/dist/shared/service-status.js.map +1 -0
  187. package/dist/telemetry/emit.d.ts +197 -0
  188. package/dist/telemetry/emit.js +196 -0
  189. package/dist/telemetry/emit.js.map +1 -0
  190. package/package.json +50 -0
@@ -0,0 +1,99 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { DashboardPage } from "./pages/dashboard.js";
3
+ import { GraphPage } from "./pages/graph.js";
4
+ import { HarnessesPage, resolveHarnessSubItems } from "./pages/harnesses.js";
5
+ import { HealthPage } from "./pages/health.js";
6
+ import { LogsPage } from "./pages/logs.js";
7
+ import { MemoriesPage } from "./pages/memories.js";
8
+ import { ProjectsPage } from "./pages/projects.js";
9
+ import { RoiPage } from "./pages/roi.js";
10
+ import { SettingsPage } from "./pages/settings.js";
11
+ import { SyncPage } from "./pages/sync.js";
12
+ /** A 16px inline-SVG icon stroked in `currentColor` (so the sidebar tints it by row color). */
13
+ function Icon({ children }) {
14
+ return (_jsx("svg", { width: 16, height: 16, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.8, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", style: { flex: "none" }, children: children }));
15
+ }
16
+ /** Dashboard — a 2x2 grid of panels (the home overview). */
17
+ const DashboardIcon = (_jsxs(Icon, { children: [_jsx("rect", { x: 3, y: 3, width: 7, height: 7 }), _jsx("rect", { x: 14, y: 3, width: 7, height: 7 }), _jsx("rect", { x: 3, y: 14, width: 7, height: 7 }), _jsx("rect", { x: 14, y: 14, width: 7, height: 7 })] }));
18
+ /** Harnesses — stacked plugs/terminals (the per-host adapters). */
19
+ const HarnessesIcon = (_jsxs(Icon, { children: [_jsx("rect", { x: 3, y: 4, width: 18, height: 16, rx: 2 }), _jsx("path", { d: "M7 9l3 3-3 3" }), _jsx("path", { d: "M13 15h4" })] }));
20
+ /** Projects — stacked folders (the bound-folder manager, PRD-059). */
21
+ const ProjectsIcon = (_jsx(Icon, { children: _jsx("path", { d: "M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" }) }));
22
+ /** Memories — a honeycomb cell (the signature Honeycomb surface). */
23
+ const MemoriesIcon = (_jsxs(Icon, { children: [_jsx("path", { d: "M12 3l7 4v8l-7 4-7-4V7z" }), _jsx("path", { d: "M12 8v8" })] }));
24
+ /** Memory Graph — connected nodes (the knowledge/memory graph). */
25
+ const GraphIcon = (_jsxs(Icon, { children: [_jsx("circle", { cx: 6, cy: 6, r: 2.5 }), _jsx("circle", { cx: 18, cy: 9, r: 2.5 }), _jsx("circle", { cx: 9, cy: 18, r: 2.5 }), _jsx("path", { d: "M8 7.5l8 1.5M8 16l1-7.5" })] }));
26
+ /** Sync — a circular refresh (skill propagation). */
27
+ const SyncIcon = (_jsxs(Icon, { children: [_jsx("path", { d: "M21 12a9 9 0 1 1-3-6.7" }), _jsx("path", { d: "M21 4v4h-4" })] }));
28
+ /** Logs — a document with lines (the live-log stream). */
29
+ const LogsIcon = (_jsxs(Icon, { children: [_jsx("rect", { x: 4, y: 3, width: 16, height: 18, rx: 2 }), _jsx("path", { d: "M8 8h8M8 12h8M8 16h5" })] }));
30
+ /** Health — a pulse line (the fleet health page, PRD-005b/PRD-005c). */
31
+ const HealthIcon = (_jsx(Icon, { children: _jsx("path", { d: "M3 12h4l2-7 4 14 2-7h6" }) }));
32
+ /** ROI — a rising trend line over an axis (the Net-ROI ledger, PRD-060e). */
33
+ const RoiIcon = (_jsxs(Icon, { children: [_jsx("path", { d: "M4 19V5" }), _jsx("path", { d: "M4 19h16" }), _jsx("path", { d: "M7 15l4-5 3 3 5-7" })] }));
34
+ /** Settings — a gear (provider/model/pollinating/vault). */
35
+ const SettingsIcon = (_jsxs(Icon, { children: [_jsx("circle", { cx: 12, cy: 12, r: 3 }), _jsx("path", { d: "M12 2v3M12 19v3M4.2 4.2l2.1 2.1M17.7 17.7l2.1 2.1M2 12h3M19 12h3M4.2 19.8l2.1-2.1M17.7 6.3l2.1-2.1" })] }));
36
+ /**
37
+ * The seven STATIC top-level routes in nav order (037c AC-2). Each is always present and hard-listed
38
+ * here; their content owners are the cross-referenced page PRDs:
39
+ * Dashboard (`/`) → PRD-038 (lift-and-shift today, reorganized there)
40
+ * Harnesses (`/harnesses`) → PRD-039 (may carry a dynamic per-harness group)
41
+ * Memories (`/memories`) → PRD-040
42
+ * Graph (`/graph`) → PRD-041
43
+ * Sync (`/sync`) → PRD-042
44
+ * Logs (`/logs`) → PRD-043
45
+ * Settings (`/settings`) → PRD-044
46
+ */
47
+ /** The Projects page route (PRD-059c) — exported so the first-run CTA can navigate to it (b-AC-4). */
48
+ export const PROJECTS_ROUTE = "/projects";
49
+ export const ROUTES = [
50
+ { route: "/", label: "Dashboard", icon: DashboardIcon, component: DashboardPage },
51
+ // PRD-059c: the Projects management page (bound-folder list + Add/Import/Unbind/Open). Slotted right
52
+ // after Dashboard — the home for "what is Honeycomb sourcing". One registry entry is the whole wiring
53
+ // (037c contract): the sidebar renders the nav item and the outlet routes the path, no sidebar edit.
54
+ { route: PROJECTS_ROUTE, label: "Projects", icon: ProjectsIcon, component: ProjectsPage },
55
+ // PRD-039 (D-6): the `#/harnesses` route is the STATIC top-level entry; its per-harness sub-entries
56
+ // (`#/harnesses/<name>`) are the 037c DYNAMIC group, resolved at render from 039a's live harness
57
+ // list (`resolveHarnessSubItems`). The parent's OQ-3 answer: 039a IS the dynamic data source.
58
+ { route: "/harnesses", label: "Harnesses", icon: HarnessesIcon, component: HarnessesPage, dynamic: { resolve: resolveHarnessSubItems } },
59
+ { route: "/memories", label: "Memories", icon: MemoriesIcon, component: MemoriesPage },
60
+ // PRD-041 follow-up: the codebase-graph view was removed from the dashboard (it was too dense to be
61
+ // useful); this page now shows ONLY the memory/knowledge graph. The daemon still builds the codebase
62
+ // graph in the background (assemble.ts auto-build) for the stale-ref/lifecycle diagnostic.
63
+ { route: "/graph", label: "Memory Graph", icon: GraphIcon, component: GraphPage },
64
+ { route: "/sync", label: "Sync", icon: SyncIcon, component: SyncPage },
65
+ { route: "/logs", label: "Logs", icon: LogsIcon, component: LogsPage },
66
+ // PRD-005b/PRD-005c: the persistent fleet health page (per-service metrics + Deep Lake stats +
67
+ // a live, verbosity-filtered log tail). Note: `/health` is ALSO hive's own machine-liveness
68
+ // path (`server.ts`); content negotiation on the server (`accept: text/html`) keeps both
69
+ // working at the identical literal path (see `gate.ts`/`server.ts`/`pages/health.tsx` docs).
70
+ { route: "/health", label: "Health", icon: HealthIcon, component: HealthPage },
71
+ // PRD-060e: the ROI page (the Net-ROI ledger — measured/modeled savings vs infra + pollination cost,
72
+ // with org/team/agent/project rollups). ONE registry entry is the whole wiring (037c contract): the
73
+ // sidebar renders the nav item and the outlet routes the path, no sidebar/router hand-edit.
74
+ { route: "/roi", label: "ROI", icon: RoiIcon, component: RoiPage },
75
+ { route: "/settings", label: "Settings", icon: SettingsIcon, component: SettingsPage },
76
+ ];
77
+ /** The Dashboard entry — the default every unknown path falls back to (037c AC-3 / 037b AC-4). */
78
+ export const DEFAULT_ROUTE = ROUTES[0];
79
+ /**
80
+ * Resolve a path to its registry entry, defaulting to the Dashboard entry on no match (037c AC-3,
81
+ * feeding 037b AC-4 "unknown route → Dashboard, no blank screen"). The match is exact on the entry
82
+ * `route` — a deep sub-route (e.g. `/harnesses/claude-code` from a dynamic group) resolves to its
83
+ * top-level parent by prefix so the correct page still mounts. `routes` is injectable so a test can
84
+ * prove the plug-in seam with a throwaway entry (037c AC-6) without mutating the global list.
85
+ */
86
+ export function matchRoute(pathname, routes = ROUTES) {
87
+ // Exact match first (the common case: one of the nine top-level routes).
88
+ const exact = routes.find((r) => r.route === pathname);
89
+ if (exact !== undefined)
90
+ return exact;
91
+ // A deep sub-route resolves to its top-level parent (e.g. `/harnesses/x` → the Harnesses entry).
92
+ // Skip the root `/` here so it never greedily matches every path; root is the explicit fallback.
93
+ const parent = routes.find((r) => r.route !== "/" && pathname.startsWith(`${r.route}/`));
94
+ if (parent !== undefined)
95
+ return parent;
96
+ // No match → the Dashboard entry (the registry's default; never a blank screen).
97
+ return routes.find((r) => r.route === "/") ?? routes[0];
98
+ }
99
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/dashboard/web/registry.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * The dashboard CLIENT-SIDE ROUTER — PRD-003c (m-AC-1 / m-AC-2, retiring the hash router).
3
+ *
4
+ * hive's SERVER is now the routing authority (PRD-003a): it evaluates the health-then-auth
5
+ * gate on every request and serves the identical SPA shell for every real, path-based route
6
+ * (`hive/src/daemon/dashboard/host.ts`'s catch-all). This hook's job narrows to what a
7
+ * server-routed SPA needs client-side: read the ALREADY-AUTHORIZED path the server served
8
+ * (`location.pathname`), keep it in sync with History API navigation (back/forward, and this
9
+ * hook's own `navigate`), and re-render. It does NOT decide what the operator is ALLOWED to see —
10
+ * that decision was already made server-side before this bundle ever ran.
11
+ *
12
+ * This RETIRES `useHashRoute` / `routeFromHash` (m-AC-1): routing no longer reads `location.hash`,
13
+ * and `navigate` calls `history.pushState` instead of assigning `location.hash`, so history entries
14
+ * are real paths, not fragments (m-AC-2). Still NO `react-router`, NO new dependency — the History
15
+ * API is a browser primitive, mirroring the hash hook's own no-new-dependency posture.
16
+ */
17
+ /**
18
+ * Parse the current route string from `location.pathname`. Normalizes an empty path to the
19
+ * default `/`. The returned value is the RAW route key (e.g. `/graph`); resolving it to a
20
+ * registry entry (and the unknown→Dashboard fallback) is `matchRoute`'s job (`registry.tsx`), not
21
+ * this parser's — this keeps the hook a pure reflection of the URL, mirroring `routeFromHash`'s
22
+ * old contract one-for-one, just sourced from the path instead of the fragment.
23
+ */
24
+ export declare function routeFromPath(pathname: string): string;
25
+ /**
26
+ * The custom window event `navigate` broadcasts after `pushState`. `pushState` fires no browser
27
+ * event of its own, and several components mount their OWN `usePathRoute()` instance (the Shell in
28
+ * `app.tsx`, `HarnessesPage`), so without a broadcast only the instance whose `navigate` ran would
29
+ * re-render and every other subscriber's `route` would go stale until a real `popstate`.
30
+ */
31
+ export declare const ROUTE_CHANGE_EVENT: "hive:pathchange";
32
+ /** The path-router contract the Shell consumes: the active route + a `navigate` helper. */
33
+ export interface PathRoute {
34
+ /** The active route parsed from `location.pathname` (e.g. `/` or `/graph`). */
35
+ readonly route: string;
36
+ /** Push `r` onto the History API (the `popstate`/local-state sync then re-renders). The ONLY history mutator. */
37
+ readonly navigate: (r: string) => void;
38
+ }
39
+ /**
40
+ * Read the active route from `location.pathname` and re-render on `popstate` (m-AC-2: back/forward
41
+ * resolves the same screens). Subscribes in a `useEffect` and unsubscribes on unmount. `navigate(r)`
42
+ * pushes a new History entry via `history.pushState` — which does NOT itself fire `popstate` — so
43
+ * `navigate` updates the local route state directly too; this keeps exactly one source of truth
44
+ * (the URL) and one mutator (`navigate`), mirroring the retired hash hook's contract. Deep-linking
45
+ * works for free: the initial state reads whatever path the page loaded with (the server already
46
+ * validated it via the gate), so a refresh on `/graph` mounts the Graph route.
47
+ */
48
+ export declare function usePathRoute(): PathRoute;
@@ -0,0 +1,76 @@
1
+ /**
2
+ * The dashboard CLIENT-SIDE ROUTER — PRD-003c (m-AC-1 / m-AC-2, retiring the hash router).
3
+ *
4
+ * hive's SERVER is now the routing authority (PRD-003a): it evaluates the health-then-auth
5
+ * gate on every request and serves the identical SPA shell for every real, path-based route
6
+ * (`hive/src/daemon/dashboard/host.ts`'s catch-all). This hook's job narrows to what a
7
+ * server-routed SPA needs client-side: read the ALREADY-AUTHORIZED path the server served
8
+ * (`location.pathname`), keep it in sync with History API navigation (back/forward, and this
9
+ * hook's own `navigate`), and re-render. It does NOT decide what the operator is ALLOWED to see —
10
+ * that decision was already made server-side before this bundle ever ran.
11
+ *
12
+ * This RETIRES `useHashRoute` / `routeFromHash` (m-AC-1): routing no longer reads `location.hash`,
13
+ * and `navigate` calls `history.pushState` instead of assigning `location.hash`, so history entries
14
+ * are real paths, not fragments (m-AC-2). Still NO `react-router`, NO new dependency — the History
15
+ * API is a browser primitive, mirroring the hash hook's own no-new-dependency posture.
16
+ */
17
+ import React from "react";
18
+ /**
19
+ * Parse the current route string from `location.pathname`. Normalizes an empty path to the
20
+ * default `/`. The returned value is the RAW route key (e.g. `/graph`); resolving it to a
21
+ * registry entry (and the unknown→Dashboard fallback) is `matchRoute`'s job (`registry.tsx`), not
22
+ * this parser's — this keeps the hook a pure reflection of the URL, mirroring `routeFromHash`'s
23
+ * old contract one-for-one, just sourced from the path instead of the fragment.
24
+ */
25
+ export function routeFromPath(pathname) {
26
+ const trimmed = pathname.trim();
27
+ return trimmed === "" ? "/" : trimmed;
28
+ }
29
+ /**
30
+ * The custom window event `navigate` broadcasts after `pushState`. `pushState` fires no browser
31
+ * event of its own, and several components mount their OWN `usePathRoute()` instance (the Shell in
32
+ * `app.tsx`, `HarnessesPage`), so without a broadcast only the instance whose `navigate` ran would
33
+ * re-render and every other subscriber's `route` would go stale until a real `popstate`.
34
+ */
35
+ export const ROUTE_CHANGE_EVENT = "hive:pathchange";
36
+ /**
37
+ * Read the active route from `location.pathname` and re-render on `popstate` (m-AC-2: back/forward
38
+ * resolves the same screens). Subscribes in a `useEffect` and unsubscribes on unmount. `navigate(r)`
39
+ * pushes a new History entry via `history.pushState` — which does NOT itself fire `popstate` — so
40
+ * `navigate` updates the local route state directly too; this keeps exactly one source of truth
41
+ * (the URL) and one mutator (`navigate`), mirroring the retired hash hook's contract. Deep-linking
42
+ * works for free: the initial state reads whatever path the page loaded with (the server already
43
+ * validated it via the gate), so a refresh on `/graph` mounts the Graph route.
44
+ */
45
+ export function usePathRoute() {
46
+ const [route, setRoute] = React.useState(() =>
47
+ // SSR/test-safe initial read: `window` exists in jsdom + the browser; guard defensively.
48
+ typeof window === "undefined" ? "/" : routeFromPath(window.location.pathname));
49
+ React.useEffect(() => {
50
+ if (typeof window === "undefined")
51
+ return;
52
+ const sync = () => setRoute(routeFromPath(window.location.pathname));
53
+ // Re-sync once on mount in case the path changed between the initial render and the effect.
54
+ sync();
55
+ window.addEventListener("popstate", sync);
56
+ window.addEventListener(ROUTE_CHANGE_EVENT, sync);
57
+ return () => {
58
+ window.removeEventListener("popstate", sync);
59
+ window.removeEventListener(ROUTE_CHANGE_EVENT, sync);
60
+ };
61
+ }, []);
62
+ const navigate = React.useCallback((r) => {
63
+ if (typeof window === "undefined")
64
+ return;
65
+ const next = r.startsWith("/") ? r : `/${r}`;
66
+ // `pushState` does not fire `popstate`, so broadcast the change instead: EVERY mounted
67
+ // `usePathRoute()` instance (not just this one) re-syncs from `location.pathname`, keeping
68
+ // the URL the single source of truth across all subscribers.
69
+ if (window.location.pathname !== next) {
70
+ window.history.pushState(null, "", next);
71
+ }
72
+ window.dispatchEvent(new Event(ROUTE_CHANGE_EVENT));
73
+ }, []);
74
+ return { route, navigate };
75
+ }
76
+ //# sourceMappingURL=router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../../../src/dashboard/web/router.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,QAAgB;IAC7C,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;IAChC,OAAO,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC;AACvC,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,iBAA0B,CAAC;AAU7D;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY;IAC3B,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAS,GAAG,EAAE;IACrD,yFAAyF;IACzF,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAC7E,CAAC;IAEF,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACpB,IAAI,OAAO,MAAM,KAAK,WAAW;YAAE,OAAO;QAC1C,MAAM,IAAI,GAAG,GAAS,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC3E,4FAA4F;QAC5F,IAAI,EAAE,CAAC;QACP,MAAM,CAAC,gBAAgB,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QAC1C,MAAM,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC;QAClD,OAAO,GAAG,EAAE;YACX,MAAM,CAAC,mBAAmB,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;YAC7C,MAAM,CAAC,mBAAmB,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC;QACtD,CAAC,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,QAAQ,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC,CAAS,EAAQ,EAAE;QACtD,IAAI,OAAO,MAAM,KAAK,WAAW;YAAE,OAAO;QAC1C,MAAM,IAAI,GAAG,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7C,uFAAuF;QACvF,2FAA2F;QAC3F,6DAA6D;QAC7D,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;YACvC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC;QAC1C,CAAC;QACD,MAAM,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC,CAAC;IACrD,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,155 @@
1
+ /**
2
+ * The Org → Workspace → Project SCOPE context — PRD-050b seam, FILLED by PRD-049e.
3
+ *
4
+ * ════════════════════════════════════════════════════════════════════════════
5
+ * 050b carved the seam (a context + a hook + a slot) so the 049e scope switcher could slot into the
6
+ * sidebar WITHOUT reworking `host.ts`/`renderShell`/the shell. 049e FILLS it: a real
7
+ * {@link ScopeProvider} hydrates the switchable Org→Workspace→Project selection from the daemon's
8
+ * loopback enumeration endpoints, the {@link ScopeSwitcherSlot} renders the dropdowns, and every
9
+ * scope-aware page reads the active project via {@link useScope}. The shell stays scope-UNAWARE —
10
+ * scope lives entirely in the React app, never baked into the server-rendered shell.
11
+ * ════════════════════════════════════════════════════════════════════════════
12
+ *
13
+ * ── The contract (unchanged from 050b — additive only) ──────────────────────
14
+ * - {@link DashboardScope} `{ org, workspace, project? }` — what a page reads.
15
+ * - {@link useScope} — the hook every scope-aware surface uses. 049e makes it reactive to the
16
+ * switcher's selection; a surface written against this hook needs NO change.
17
+ * - {@link ScopeSwitcherSlot} — the sidebar region. 050b returned null; 049e renders the picker.
18
+ *
19
+ * ── Viewer-side selection (49e-AC-4) ─────────────────────────────────────────
20
+ * The selection drives WHICH data the pages show (it stamps the `x-honeycomb-project` read header).
21
+ * It does NOT overwrite a developer's per-folder CLI bindings: there is NO write to `projects.json`
22
+ * anywhere in this module. The selection is persisted ONLY in `localStorage` (the dashboard session),
23
+ * so a reload restores the last view without touching the registry.
24
+ */
25
+ import React from "react";
26
+ import type { ScopeOrgWire, ScopeProjectWire, ScopeWorkspaceWire, WireClient } from "./wire.js";
27
+ /**
28
+ * The active scope the dashboard reads (the 049e contract). `org` is always present; `workspace`
29
+ * and `project` narrow it. 050b shipped the single loopback-local default; 049e makes these
30
+ * switchable from the {@link ScopeSwitcherSlot}.
31
+ */
32
+ export interface DashboardScope {
33
+ /** The active org id (always present; the loopback "local" tenant until the switcher selects one). */
34
+ readonly org: string;
35
+ /** The active workspace id (the `default` sentinel until the switcher selects one). */
36
+ readonly workspace: string;
37
+ /** The active project id, when the scope is narrowed to one (049e); absent → no project selected. */
38
+ readonly project?: string;
39
+ }
40
+ /** The single-scope default the app starts from (the loopback local tenant) until enumeration resolves. */
41
+ export declare const DEFAULT_SCOPE: DashboardScope;
42
+ /** The `localStorage` key the dashboard persists its last viewer-side selection under (49e — viewer-side). */
43
+ export declare const SCOPE_STORAGE_KEY: "honeycomb.dashboard.scope";
44
+ /** The scope context value — the active scope plus a `setScope` the switcher wires to its dropdowns. */
45
+ export interface ScopeContextValue {
46
+ /** The active scope. */
47
+ readonly scope: DashboardScope;
48
+ /**
49
+ * Switch the active scope (the switcher calls this). The default provider value is a NO-OP (a single,
50
+ * un-switchable scope) so a surface that calls it outside a {@link ScopeProvider} is harmless; the
51
+ * real {@link ScopeProvider} replaces it with a setter that re-hydrates the scoped views + persists.
52
+ */
53
+ readonly setScope: (next: DashboardScope) => void;
54
+ }
55
+ /** The React context (the 049e mount point). Defaults to the single loopback scope + a no-op setter. */
56
+ export declare const ScopeContext: React.Context<ScopeContextValue>;
57
+ /**
58
+ * Read the active dashboard scope (the hook every scope-aware surface uses). Reactive to the switcher's
59
+ * selection when rendered inside a {@link ScopeProvider}; returns the single default otherwise. Read
60
+ * scope from HERE, never bake a single-workspace assumption into a page or the shell.
61
+ */
62
+ export declare function useScope(): ScopeContextValue;
63
+ /**
64
+ * The switcher's enumeration state + handlers (PRD-049e). Separate from {@link ScopeContextValue} so a
65
+ * page reads only the lean `{ scope, setScope }` it needs, while the {@link ScopeSwitcherSlot} reads
66
+ * this richer surface. The {@link ScopeProvider} owns both; the slot reads this via {@link useScopeSwitcher}.
67
+ */
68
+ export interface ScopeSwitcherValue {
69
+ /** The orgs the user can access (privilege-scoped by the daemon — 49e-AC-1). */
70
+ readonly orgs: readonly ScopeOrgWire[];
71
+ /** The selected org's workspaces (re-enumerated on org change, after the re-mint — 49e-AC-3). */
72
+ readonly workspaces: readonly ScopeWorkspaceWire[];
73
+ /** The workspace's registry projects (the 049a synced copy, incl. the `__unsorted__` inbox). */
74
+ readonly projects: readonly ScopeProjectWire[];
75
+ /**
76
+ * True once the FIRST projects enumeration has resolved (PRD-059b). The first-run "pick a folder"
77
+ * CTA keys off this so it never flashes before the projects read returns — only AFTER the read
78
+ * resolves with zero locally-bound projects does the CTA show (b-AC-1).
79
+ */
80
+ readonly projectsHydrated: boolean;
81
+ /** True while an org change is re-minting + re-enumerating (the workspace dropdown shows a loading hint). */
82
+ readonly loadingWorkspaces: boolean;
83
+ /**
84
+ * IRD-122 — the last switch's outcome feedback (122-AC-4: no switch is a silent no-op). `null` until a
85
+ * switch runs; then a `{ kind: "persisted" | "view" | "error", message }` the switcher surfaces so the
86
+ * user always sees whether a selection PERSISTED a real scope change, is a VIEW filter, or FAILED.
87
+ */
88
+ readonly switchFeedback: SwitchFeedback | null;
89
+ /**
90
+ * Select an org → PERSIST a real org switch via the daemon (re-mint + save to credentials.json —
91
+ * 122-AC-2), then re-enumerate workspaces; clears the workspace/project. Surfaces persisted/error
92
+ * feedback (122-AC-4). A failed persist does NOT silently change the view.
93
+ */
94
+ readonly selectOrg: (org: string) => void;
95
+ /** Select a workspace → PERSIST the workspace switch via the daemon (IRD-122), then re-enumerate projects. */
96
+ readonly selectWorkspace: (workspace: string) => void;
97
+ /**
98
+ * Select a project → re-scope the pages (VIEW filter only; viewer-side, no registry write — 49e-AC-4
99
+ * / 122-AC-3). Surfaces "view filter" feedback so the user understands the project dropdown changes
100
+ * the VIEW, not where capture is written (that is folder/binding-driven — PRD-059).
101
+ */
102
+ readonly selectProject: (project: string | undefined) => void;
103
+ }
104
+ /** IRD-122 — the outcome of the last switcher action, surfaced so no change is a silent no-op (122-AC-4). */
105
+ export interface SwitchFeedback {
106
+ /** `persisted` = a real org/workspace scope change saved to credentials.json; `view` = a project view filter; `error` = the switch failed. */
107
+ readonly kind: "persisted" | "view" | "error";
108
+ /** A short human message the switcher renders (e.g. "switched to Acme", "view filter", "could not switch"). */
109
+ readonly message: string;
110
+ /** True while the switch is in flight (the org re-mint loading state — 122-AC-4 surfaces it). */
111
+ readonly pending?: boolean;
112
+ }
113
+ /** The switcher-data context (the enumeration + dropdown handlers). The slot reads it; pages do not. */
114
+ export declare const ScopeSwitcherContext: React.Context<ScopeSwitcherValue>;
115
+ /** Read the switcher's enumeration state + handlers (the {@link ScopeSwitcherSlot} uses this). */
116
+ export declare function useScopeSwitcher(): ScopeSwitcherValue;
117
+ /**
118
+ * Read the persisted viewer-side selection from `localStorage` (49e — persistence is acceptable, and
119
+ * viewer-side: a read of the dashboard's own store, never the registry). Fail-soft: a missing/malformed
120
+ * value yields `null` (the provider then resolves from the daemon default), never a throw.
121
+ */
122
+ export declare function loadPersistedScope(): DashboardScope | null;
123
+ /** Persist the viewer-side selection to `localStorage` (49e). Fail-soft — a storage error is swallowed intentionally. */
124
+ export declare function persistScope(scope: DashboardScope): void;
125
+ /**
126
+ * The SCOPE PROVIDER (PRD-049e) — owns the switchable selection + the enumeration state, and feeds both
127
+ * the lean {@link ScopeContext} (pages) and the rich {@link ScopeSwitcherContext} (the slot). Mounted by
128
+ * the {@link import("./app.js").Shell} around the sidebar + outlet, so every page + the switcher share
129
+ * ONE source of truth.
130
+ *
131
+ * On mount it hydrates the orgs (privilege-scoped), then the workspaces + projects for the
132
+ * resolved/persisted org, and restores the persisted project selection (viewer-side). Selecting an Org
133
+ * re-enumerates workspaces (the daemon re-mints the org-bound token FIRST — 49e-AC-3); selecting a
134
+ * Workspace re-enumerates projects; selecting a Project re-scopes the pages (the project flows into
135
+ * `useScope().scope.project`, which the pages thread into the wire fetchers). NO write to `projects.json`
136
+ * happens here — the selection is purely viewer-side (49e-AC-4).
137
+ */
138
+ export declare function ScopeProvider({ wire, children }: {
139
+ wire: WireClient;
140
+ children: React.ReactNode;
141
+ }): React.JSX.Element;
142
+ /** Props for {@link ScopeSwitcherSlot}. `collapsed` mirrors the sidebar's rail state for the compact switcher. */
143
+ export interface ScopeSwitcherSlotProps {
144
+ /** Whether the sidebar is in its collapsed icon-rail (renders a compact, label-free switcher). */
145
+ readonly collapsed: boolean;
146
+ }
147
+ /**
148
+ * The Org→Workspace→Project SWITCHER (PRD-049e) — the body that fills the 050b slot. Three dependent
149
+ * dropdowns hydrated from the {@link useScopeSwitcher} enumeration state; the active selection comes from
150
+ * {@link useScope}. Changing the Org re-enumerates workspaces (after the daemon re-mints — 49e-AC-3),
151
+ * changing the Workspace re-enumerates projects, and changing the Project re-scopes every page
152
+ * (viewer-side; no registry write — 49e-AC-4). In the collapsed rail it renders nothing (the switcher
153
+ * needs labels) — the sidebar expands to switch scope.
154
+ */
155
+ export declare function ScopeSwitcherSlot({ collapsed }: ScopeSwitcherSlotProps): React.JSX.Element | null;