@legioncodeinc/hive 0.2.0 → 0.3.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 (108) hide show
  1. package/README.md +2 -2
  2. package/dist/daemon/dashboard/app.js +26 -25
  3. package/dist/daemon/dashboard/host.d.ts +7 -0
  4. package/dist/daemon/dashboard/host.js +16 -0
  5. package/dist/daemon/dashboard/host.js.map +1 -1
  6. package/dist/daemon/dashboard/web-assets.d.ts +2 -0
  7. package/dist/daemon/dashboard/web-assets.js +19 -0
  8. package/dist/daemon/dashboard/web-assets.js.map +1 -1
  9. package/dist/daemon/gate.d.ts +2 -2
  10. package/dist/daemon/gate.js +15 -4
  11. package/dist/daemon/gate.js.map +1 -1
  12. package/dist/daemon/installer/bin-resolver.d.ts +34 -0
  13. package/dist/daemon/installer/bin-resolver.js +86 -0
  14. package/dist/daemon/installer/bin-resolver.js.map +1 -0
  15. package/dist/daemon/installer/config.d.ts +63 -0
  16. package/dist/daemon/installer/config.js +74 -0
  17. package/dist/daemon/installer/config.js.map +1 -0
  18. package/dist/daemon/installer/detection.d.ts +20 -0
  19. package/dist/daemon/installer/detection.js +73 -0
  20. package/dist/daemon/installer/detection.js.map +1 -0
  21. package/dist/daemon/installer/funnel-telemetry.d.ts +54 -0
  22. package/dist/daemon/installer/funnel-telemetry.js +134 -0
  23. package/dist/daemon/installer/funnel-telemetry.js.map +1 -0
  24. package/dist/daemon/installer/index.d.ts +12 -0
  25. package/dist/daemon/installer/index.js +10 -0
  26. package/dist/daemon/installer/index.js.map +1 -0
  27. package/dist/daemon/installer/install-state.d.ts +50 -0
  28. package/dist/daemon/installer/install-state.js +142 -0
  29. package/dist/daemon/installer/install-state.js.map +1 -0
  30. package/dist/daemon/installer/manifest-snapshot.json +25 -0
  31. package/dist/daemon/installer/manifest.d.ts +47 -0
  32. package/dist/daemon/installer/manifest.js +103 -0
  33. package/dist/daemon/installer/manifest.js.map +1 -0
  34. package/dist/daemon/installer/products.d.ts +33 -0
  35. package/dist/daemon/installer/products.js +42 -0
  36. package/dist/daemon/installer/products.js.map +1 -0
  37. package/dist/daemon/installer/routes.d.ts +43 -0
  38. package/dist/daemon/installer/routes.js +201 -0
  39. package/dist/daemon/installer/routes.js.map +1 -0
  40. package/dist/daemon/installer/security.d.ts +33 -0
  41. package/dist/daemon/installer/security.js +80 -0
  42. package/dist/daemon/installer/security.js.map +1 -0
  43. package/dist/daemon/installer/spawn.d.ts +48 -0
  44. package/dist/daemon/installer/spawn.js +51 -0
  45. package/dist/daemon/installer/spawn.js.map +1 -0
  46. package/dist/daemon/installer/token.d.ts +23 -0
  47. package/dist/daemon/installer/token.js +56 -0
  48. package/dist/daemon/installer/token.js.map +1 -0
  49. package/dist/daemon/server.d.ts +6 -0
  50. package/dist/daemon/server.js +7 -0
  51. package/dist/daemon/server.js.map +1 -1
  52. package/dist/dashboard/web/app.js +42 -20
  53. package/dist/dashboard/web/app.js.map +1 -1
  54. package/dist/dashboard/web/boot-route.d.ts +11 -7
  55. package/dist/dashboard/web/boot-route.js +12 -6
  56. package/dist/dashboard/web/boot-route.js.map +1 -1
  57. package/dist/dashboard/web/main.js +2 -1
  58. package/dist/dashboard/web/main.js.map +1 -1
  59. package/dist/dashboard/web/onboarding/advanced-picker.d.ts +16 -0
  60. package/dist/dashboard/web/onboarding/advanced-picker.js +59 -0
  61. package/dist/dashboard/web/onboarding/advanced-picker.js.map +1 -0
  62. package/dist/dashboard/web/onboarding/contracts.d.ts +188 -0
  63. package/dist/dashboard/web/onboarding/contracts.js +161 -0
  64. package/dist/dashboard/web/onboarding/contracts.js.map +1 -0
  65. package/dist/dashboard/web/onboarding/health-view.d.ts +17 -0
  66. package/dist/dashboard/web/onboarding/health-view.js +79 -0
  67. package/dist/dashboard/web/onboarding/health-view.js.map +1 -0
  68. package/dist/dashboard/web/onboarding/install-card.d.ts +27 -0
  69. package/dist/dashboard/web/onboarding/install-card.js +170 -0
  70. package/dist/dashboard/web/onboarding/install-card.js.map +1 -0
  71. package/dist/dashboard/web/onboarding/login-step.d.ts +29 -0
  72. package/dist/dashboard/web/onboarding/login-step.js +104 -0
  73. package/dist/dashboard/web/onboarding/login-step.js.map +1 -0
  74. package/dist/dashboard/web/onboarding/onboarding-client.d.ts +52 -0
  75. package/dist/dashboard/web/onboarding/onboarding-client.js +133 -0
  76. package/dist/dashboard/web/onboarding/onboarding-client.js.map +1 -0
  77. package/dist/dashboard/web/onboarding/onboarding-hero.d.ts +24 -0
  78. package/dist/dashboard/web/onboarding/onboarding-hero.js +70 -0
  79. package/dist/dashboard/web/onboarding/onboarding-hero.js.map +1 -0
  80. package/dist/dashboard/web/onboarding/onboarding-screen.d.ts +43 -0
  81. package/dist/dashboard/web/onboarding/onboarding-screen.js +161 -0
  82. package/dist/dashboard/web/onboarding/onboarding-screen.js.map +1 -0
  83. package/dist/dashboard/web/onboarding/onboarding-selection-store.d.ts +20 -0
  84. package/dist/dashboard/web/onboarding/onboarding-selection-store.js +76 -0
  85. package/dist/dashboard/web/onboarding/onboarding-selection-store.js.map +1 -0
  86. package/dist/dashboard/web/onboarding/product-copy.d.ts +40 -0
  87. package/dist/dashboard/web/onboarding/product-copy.js +70 -0
  88. package/dist/dashboard/web/onboarding/product-copy.js.map +1 -0
  89. package/dist/dashboard/web/onboarding/use-install-dwell.d.ts +22 -0
  90. package/dist/dashboard/web/onboarding/use-install-dwell.js +37 -0
  91. package/dist/dashboard/web/onboarding/use-install-dwell.js.map +1 -0
  92. package/dist/dashboard/web/onboarding/use-onboarding-token.d.ts +9 -0
  93. package/dist/dashboard/web/onboarding/use-onboarding-token.js +34 -0
  94. package/dist/dashboard/web/onboarding/use-onboarding-token.js.map +1 -0
  95. package/dist/dashboard/web/route-daemon-owner.d.ts +7 -0
  96. package/dist/dashboard/web/route-daemon-owner.js +15 -0
  97. package/dist/dashboard/web/route-daemon-owner.js.map +1 -0
  98. package/dist/dashboard/web/wire.d.ts +1 -1
  99. package/dist/shared/onboarding-types.d.ts +79 -0
  100. package/dist/shared/onboarding-types.js +12 -0
  101. package/dist/shared/onboarding-types.js.map +1 -0
  102. package/dist/telemetry/emit.d.ts +30 -3
  103. package/dist/telemetry/emit.js +25 -2
  104. package/dist/telemetry/emit.js.map +1 -1
  105. package/dist/telemetry/onboarding-session-ledger.d.ts +31 -0
  106. package/dist/telemetry/onboarding-session-ledger.js +78 -0
  107. package/dist/telemetry/onboarding-session-ledger.js.map +1 -0
  108. package/package.json +1 -1
@@ -0,0 +1,104 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /**
3
+ * The onboarding LOGIN STEP, PRD-009b ob-AC-14/ob-AC-15. Closes the known device-code display gap
4
+ * by reusing the EXACT wire contract `GuidedSetup` (`src/dashboard/web/setup-gate.tsx`) already
5
+ * defines (`POST /setup/login`, polled `GET /setup/state`) WITHOUT modifying that module: this is
6
+ * the sibling component the task brief calls for, sharing `wire.ts`'s types/client rather than
7
+ * duplicating the device-flow request shapes.
8
+ *
9
+ * Unlike `/login`'s `GuidedSetup` (which waits for an explicit "First time setup" click), the
10
+ * onboarding flow has already walked the operator through installs and a health check, so this
11
+ * step begins the device flow automatically on mount: one fewer click at the end of a long guided
12
+ * sequence. Once `/setup/state.authenticated` flips true it fires `dashboard_reached`, best-effort
13
+ * POSTs `/api/onboarding/complete`, then hard-navigates to `/` (ob-AC-15) so the server gate
14
+ * revalidates and serves the authoritative dashboard, the same discipline `LoginScreen` follows.
15
+ */
16
+ import React from "react";
17
+ import { createWireClient, FRESH_SETUP_STATE } from "../wire.js";
18
+ /** Mirrors `setup-gate.tsx`'s `SETUP_POLL_MS`, the live-transition poll cadence. */
19
+ export const LOGIN_STEP_POLL_MS = 2500;
20
+ /**
21
+ * The grant display (ob-AC-14): the `user_code` prominently, plus `verification_uri_complete`
22
+ * falling back to `verification_uri`, byte-identical fallback rule to `GuidedSetup`'s.
23
+ */
24
+ function LoginGrant({ grant }) {
25
+ return (_jsxs("div", { "data-testid": "onboarding-login-grant", style: { display: "flex", flexDirection: "column", gap: 10, alignItems: "center" }, children: [_jsx("p", { style: { fontFamily: "var(--font-sans)", fontSize: "var(--text-sm)", color: "var(--text-secondary)", margin: 0 }, children: "Enter this code to finish linking your Deeplake account:" }), _jsx("code", { "data-testid": "onboarding-login-code", style: { fontFamily: "var(--font-mono)", fontSize: "var(--text-lg)", color: "var(--honey)", letterSpacing: "0.08em" }, children: grant.user_code }), _jsx("a", { href: grant.verification_uri_complete ?? grant.verification_uri, target: "_blank", rel: "noreferrer", "data-testid": "onboarding-login-verification-link", style: { fontSize: "var(--text-sm)", color: "var(--text-secondary)" }, children: "Open the verification page" }), _jsx("span", { style: { fontSize: "var(--text-xs)", color: "var(--text-tertiary)" }, children: "Waiting for you to finish in the browser\u2026" })] }));
26
+ }
27
+ export function LoginStep({ onboardingClient, wire: wireOverride, onAuthenticated, pollMs = LOGIN_STEP_POLL_MS }) {
28
+ const wire = React.useMemo(() => wireOverride ?? createWireClient(), [wireOverride]);
29
+ const [state, setState] = React.useState(FRESH_SETUP_STATE);
30
+ const [grant, setGrant] = React.useState(null);
31
+ const [error, setError] = React.useState(false);
32
+ const beginRef = React.useRef(false);
33
+ const grantShownEventRef = React.useRef(false);
34
+ const navigatedRef = React.useRef(false);
35
+ // Auto-begin the device flow on mount (see module doc for why onboarding skips the button
36
+ // `GuidedSetup` shows on the standalone `/login` route).
37
+ React.useEffect(() => {
38
+ if (beginRef.current)
39
+ return;
40
+ beginRef.current = true;
41
+ void (async () => {
42
+ const result = await wire.setupLogin();
43
+ if (result === null) {
44
+ setError(true);
45
+ return;
46
+ }
47
+ setGrant(result);
48
+ })();
49
+ }, [wire]);
50
+ // ob-AC-14, `login_shown` fires the moment the grant (the code the operator must see) renders.
51
+ React.useEffect(() => {
52
+ if (grant === null || grantShownEventRef.current)
53
+ return;
54
+ grantShownEventRef.current = true;
55
+ onboardingClient.sendEvent("login_shown");
56
+ }, [grant, onboardingClient]);
57
+ // Poll `/setup/state` for the live authenticated transition, exactly like `LoginScreen` does.
58
+ React.useEffect(() => {
59
+ if (state.authenticated)
60
+ return;
61
+ let alive = true;
62
+ const tick = async () => {
63
+ const next = await wire.setupState();
64
+ if (alive)
65
+ setState(next);
66
+ };
67
+ void tick();
68
+ const id = setInterval(() => void tick(), pollMs);
69
+ return () => {
70
+ alive = false;
71
+ clearInterval(id);
72
+ };
73
+ }, [wire, state.authenticated, pollMs]);
74
+ // ob-AC-15, authenticated: fire `dashboard_reached`, best-effort POST complete, then hard-nav.
75
+ React.useEffect(() => {
76
+ if (!state.authenticated || navigatedRef.current)
77
+ return;
78
+ navigatedRef.current = true;
79
+ onboardingClient.sendEvent("dashboard_reached");
80
+ void (async () => {
81
+ await onboardingClient.complete();
82
+ if (onAuthenticated !== undefined) {
83
+ onAuthenticated();
84
+ return;
85
+ }
86
+ if (typeof window !== "undefined")
87
+ window.location.assign("/");
88
+ })();
89
+ }, [state.authenticated, onboardingClient, onAuthenticated]);
90
+ if (state.authenticated)
91
+ return _jsx(_Fragment, {});
92
+ return (_jsxs("div", { "data-testid": "onboarding-login-step", style: {
93
+ display: "flex",
94
+ flexDirection: "column",
95
+ alignItems: "center",
96
+ justifyContent: "center",
97
+ gap: 18,
98
+ minHeight: "100vh",
99
+ padding: 28,
100
+ background: "var(--bg-canvas)",
101
+ textAlign: "center",
102
+ }, children: [_jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8, maxWidth: 460 }, children: [_jsx("h1", { style: { fontSize: "var(--text-xl)", fontWeight: 700, color: "var(--text-primary)", margin: 0, letterSpacing: "-0.02em" }, children: "One last step: link Deeplake" }), _jsx("p", { style: { fontSize: "var(--text-sm)", color: "var(--text-secondary)", margin: 0, lineHeight: 1.5 }, children: "Your fleet is up. Link your Deeplake account and you are on your dashboard." })] }), grant !== null ? (_jsx(LoginGrant, { grant: grant })) : error ? (_jsxs("p", { "data-testid": "onboarding-login-error", style: { fontSize: "var(--text-sm)", color: "var(--severity-critical)", margin: 0 }, children: ["Could not start the login. Retry, or run ", _jsx("code", { children: "honeycomb login" }), " in your terminal."] })) : (_jsx("p", { style: { fontSize: "var(--text-sm)", color: "var(--text-secondary)", margin: 0 }, children: "Starting login\u2026" }))] }));
103
+ }
104
+ //# sourceMappingURL=login-step.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login-step.js","sourceRoot":"","sources":["../../../../src/dashboard/web/onboarding/login-step.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * The `/onboarding` route's WIRE CLIENT, PRD-009b, talking to the PRD-009a installer service.
3
+ * Mirrors `wire.ts`'s fetch+zod discipline (fail-soft, never throws into React) but is kept
4
+ * LOCAL to the onboarding feature folder per the file-ownership split with the daemon-side agent
5
+ * building `src/daemon/installer/**` in parallel.
6
+ *
7
+ * Every call carries the one-time onboarding token as the `x-onboarding-token` header (the PRD's
8
+ * "carried on every installer call"), EXCEPT the SSE subscription: `EventSource` cannot set request
9
+ * headers, so the token rides as a `?t=` query param there instead (the implementation note both
10
+ * the parent brief and PRD-009a's SSE contract call for).
11
+ */
12
+ import { type DetectResponse, type HealthResponse, type InstallableProduct, type InstallProgressEvent, type InstallStartResult } from "./contracts.js";
13
+ /** The header carrying the one-time onboarding token on every installer call (never the SSE query). */
14
+ export declare const ONBOARDING_TOKEN_HEADER: "x-onboarding-token";
15
+ /** A fetch-like function, injectable so tests never hit the network (mirrors `wire.ts`'s `FetchLike`). */
16
+ type FetchLike = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
17
+ /** An `EventSource`-constructor-like injectable, so tests never open a real connection. */
18
+ type EventSourceCtorLike = new (url: string | URL) => EventSource;
19
+ export interface OnboardingClientOptions {
20
+ /** Prefixed onto every request path (defaults to same-origin, `""`). A test injects a base. */
21
+ readonly origin?: string;
22
+ /** Defaults to the global `fetch`; a test injects a mock. */
23
+ readonly fetchImpl?: FetchLike;
24
+ /** Defaults to the global `EventSource`; a test injects a fake (jsdom has none, see module doc below). */
25
+ readonly eventSourceCtor?: EventSourceCtorLike;
26
+ }
27
+ /** The onboarding wire surface every onboarding component reads/writes through. */
28
+ export interface OnboardingClient {
29
+ /** `GET /api/onboarding/detect`, never assumes a product set; a failure degrades to {@link EMPTY_DETECTION}. */
30
+ detect(): Promise<DetectResponse>;
31
+ /** `POST /api/onboarding/install`, starts (or short-circuits) one product's install. `null` on failure. */
32
+ startInstall(product: InstallableProduct): Promise<InstallStartResult | null>;
33
+ /**
34
+ * `GET /api/onboarding/install/:product/events` (SSE). Returns an unsubscribe function. In an
35
+ * `EventSource`-less environment (jsdom, mirroring `use-fleet-telemetry.ts`'s documented gap)
36
+ * this is a no-op subscription, the caller's install-card falls back to its optimistic local
37
+ * stage tracking rather than throwing.
38
+ */
39
+ subscribeInstallEvents(product: InstallableProduct, onEvent: (event: InstallProgressEvent) => void): () => void;
40
+ /** `GET /api/onboarding/health`, a failure degrades to {@link UNREACHABLE_HEALTH} (never `ready:true`). */
41
+ health(): Promise<HealthResponse>;
42
+ /** `POST /api/onboarding/complete` (204). Best-effort: never throws, never blocks the caller's navigation. */
43
+ complete(): Promise<void>;
44
+ /**
45
+ * `POST /api/onboarding/event` (202), the UI funnel chokepoint. FIRE-AND-FORGET by design: the
46
+ * caller never awaits this (a slow/broken telemetry endpoint must never stall the guided flow).
47
+ */
48
+ sendEvent(event: string, properties?: Record<string, string>): void;
49
+ }
50
+ /** Build the onboarding wire client bound to one onboarding token (read once from `?t=`, kept in memory). */
51
+ export declare function createOnboardingClient(token: string, options?: OnboardingClientOptions): OnboardingClient;
52
+ export {};
@@ -0,0 +1,133 @@
1
+ /**
2
+ * The `/onboarding` route's WIRE CLIENT, PRD-009b, talking to the PRD-009a installer service.
3
+ * Mirrors `wire.ts`'s fetch+zod discipline (fail-soft, never throws into React) but is kept
4
+ * LOCAL to the onboarding feature folder per the file-ownership split with the daemon-side agent
5
+ * building `src/daemon/installer/**` in parallel.
6
+ *
7
+ * Every call carries the one-time onboarding token as the `x-onboarding-token` header (the PRD's
8
+ * "carried on every installer call"), EXCEPT the SSE subscription: `EventSource` cannot set request
9
+ * headers, so the token rides as a `?t=` query param there instead (the implementation note both
10
+ * the parent brief and PRD-009a's SSE contract call for).
11
+ */
12
+ import { DetectResponseSchema, EMPTY_DETECTION, InstallProgressEventSchema, InstallRefusalResponseSchema, InstallStartResponseSchema, parseHealthResponse, UNREACHABLE_HEALTH, } from "./contracts.js";
13
+ /** The header carrying the one-time onboarding token on every installer call (never the SSE query). */
14
+ export const ONBOARDING_TOKEN_HEADER = "x-onboarding-token";
15
+ function tokenHeaders(token) {
16
+ return token !== "" ? { [ONBOARDING_TOKEN_HEADER]: token } : {};
17
+ }
18
+ /** Build the onboarding wire client bound to one onboarding token (read once from `?t=`, kept in memory). */
19
+ export function createOnboardingClient(token, options = {}) {
20
+ const origin = options.origin ?? "";
21
+ const fetchImpl = options.fetchImpl ?? fetch;
22
+ const url = (path) => `${origin}${path}`;
23
+ return {
24
+ async detect() {
25
+ try {
26
+ const res = await fetchImpl(url("/api/onboarding/detect"), {
27
+ headers: { accept: "application/json", ...tokenHeaders(token) },
28
+ });
29
+ if (!res.ok)
30
+ return EMPTY_DETECTION;
31
+ const parsed = DetectResponseSchema.safeParse(await res.json());
32
+ return parsed.success ? parsed.data : EMPTY_DETECTION;
33
+ }
34
+ catch {
35
+ return EMPTY_DETECTION;
36
+ }
37
+ },
38
+ async startInstall(product) {
39
+ try {
40
+ const res = await fetchImpl(url("/api/onboarding/install"), {
41
+ method: "POST",
42
+ headers: { "content-type": "application/json", accept: "application/json", ...tokenHeaders(token) },
43
+ body: JSON.stringify({ product }),
44
+ });
45
+ if (res.status === 409) {
46
+ const parsed = InstallRefusalResponseSchema.safeParse(await res.json());
47
+ return parsed.success ? parsed.data : null;
48
+ }
49
+ if (!res.ok)
50
+ return null;
51
+ const parsed = InstallStartResponseSchema.safeParse(await res.json());
52
+ return parsed.success ? parsed.data : null;
53
+ }
54
+ catch {
55
+ return null;
56
+ }
57
+ },
58
+ subscribeInstallEvents(product, onEvent) {
59
+ const EventSourceCtor = options.eventSourceCtor ?? globalThis.EventSource;
60
+ if (EventSourceCtor === undefined) {
61
+ // jsdom (this repo's test environment) has no EventSource, mirroring the exact gap
62
+ // `use-fleet-telemetry.ts` documents. Tests inject a fake constructor to exercise this path.
63
+ return () => { };
64
+ }
65
+ const qs = token !== "" ? `?t=${encodeURIComponent(token)}` : "";
66
+ let source = null;
67
+ try {
68
+ source = new EventSourceCtor(url(`/api/onboarding/install/${encodeURIComponent(product)}/events${qs}`));
69
+ }
70
+ catch {
71
+ return () => { };
72
+ }
73
+ // The contract names no custom SSE event type, so this subscribes to the default unnamed
74
+ // `message` frame (see the onboarding-client research note in the final report: if the
75
+ // daemon's implementation ends up naming the event, this is a one-line integration fix).
76
+ const handler = (event) => {
77
+ const raw = typeof event.data === "string" ? event.data : String(event.data);
78
+ try {
79
+ const parsed = InstallProgressEventSchema.safeParse(JSON.parse(raw));
80
+ if (parsed.success)
81
+ onEvent(parsed.data);
82
+ }
83
+ catch {
84
+ // A malformed frame is dropped; the next frame (or the server's on-subscribe
85
+ // current-stage resend, ob-AC-17) carries the truth forward.
86
+ }
87
+ };
88
+ source.addEventListener("message", handler);
89
+ return () => {
90
+ try {
91
+ source?.removeEventListener("message", handler);
92
+ source?.close();
93
+ }
94
+ catch {
95
+ // Closing an already-closed/errored source must never throw into the caller's cleanup.
96
+ }
97
+ };
98
+ },
99
+ async health() {
100
+ try {
101
+ const res = await fetchImpl(url("/api/onboarding/health"), {
102
+ headers: { accept: "application/json", ...tokenHeaders(token) },
103
+ });
104
+ if (!res.ok)
105
+ return UNREACHABLE_HEALTH;
106
+ return parseHealthResponse(await res.json());
107
+ }
108
+ catch {
109
+ return UNREACHABLE_HEALTH;
110
+ }
111
+ },
112
+ async complete() {
113
+ try {
114
+ await fetchImpl(url("/api/onboarding/complete"), { method: "POST", headers: tokenHeaders(token) });
115
+ }
116
+ catch {
117
+ // Best-effort: the login step navigates to the dashboard regardless (never gets the
118
+ // operator stuck because a completion beacon failed to land).
119
+ }
120
+ },
121
+ sendEvent(event, properties) {
122
+ const body = properties !== undefined ? { event, properties } : { event };
123
+ // Fire-and-forget: the caller never awaits this, so a slow/broken telemetry endpoint can
124
+ // never stall the guided flow. Swallow every failure silently (fail-soft telemetry posture).
125
+ void fetchImpl(url("/api/onboarding/event"), {
126
+ method: "POST",
127
+ headers: { "content-type": "application/json", ...tokenHeaders(token) },
128
+ body: JSON.stringify(body),
129
+ }).catch(() => { });
130
+ },
131
+ };
132
+ }
133
+ //# sourceMappingURL=onboarding-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"onboarding-client.js","sourceRoot":"","sources":["../../../../src/dashboard/web/onboarding/onboarding-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * The first-run HERO, PRD-009b ob-AC-4/ob-AC-5. A staggered entrance anchored by the Hive mark:
3
+ * the product logos rise and fade in one by one on a spring-like curve, the Hive mark settles
4
+ * center LAST, then the two choice buttons fade in. Pure CSS animation/transition (no new
5
+ * dependency); `prefers-reduced-motion` collapses every entrance to a plain opacity fade with no
6
+ * transform (see the `hc-onboarding-anim` rule below: the SAFE default IS the reduced-motion form,
7
+ * and a `(prefers-reduced-motion: no-preference)` media query is what OPTS IN to the richer motion,
8
+ * so an environment that cannot evaluate the media query at all still gets the safe fade).
9
+ */
10
+ import React from "react";
11
+ /** ob-AC-5, the exact two choices, verbatim copy. */
12
+ export type OnboardingMode = "standard" | "advanced";
13
+ export interface OnboardingHeroProps {
14
+ readonly assetBase: string;
15
+ readonly onChooseStandard: () => void;
16
+ readonly onChooseAdvanced: () => void;
17
+ /** The per-entry stagger delay (ms) between each product logo. Overridable for tests. */
18
+ readonly staggerMs?: number;
19
+ }
20
+ /**
21
+ * The first-run hero + the two-choice entry point. Renders unconditionally once the caller has
22
+ * already decided this IS a first-run (no detection re-derivation happens here, per ob-AC-2).
23
+ */
24
+ export declare function OnboardingHero({ assetBase, onChooseStandard, onChooseAdvanced, staggerMs }: OnboardingHeroProps): React.JSX.Element;
@@ -0,0 +1,70 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { productLogoUrl } from "./product-copy.js";
3
+ /** The hero's hardcoded hero mark, in the order they animate in BEFORE the Hive mark settles last. */
4
+ const HERO_PRODUCT_MARKS = ["honeycomb", "doctor", "nectar"];
5
+ /** One staggered hero entry (a product logo, or the Hive mark itself). */
6
+ function HeroEntry({ src, delayMs, size, isAnchor, testId, }) {
7
+ return (_jsx("span", { "data-testid": testId, className: `hc-onboarding-anim${isAnchor ? " hc-onboarding-hero-anchor" : " hc-onboarding-hero-logo"}`, style: { display: "inline-flex", animationDelay: `${delayMs}ms` }, children: _jsx("img", { src: src, width: size, height: size, alt: "" }) }));
8
+ }
9
+ /**
10
+ * The first-run hero + the two-choice entry point. Renders unconditionally once the caller has
11
+ * already decided this IS a first-run (no detection re-derivation happens here, per ob-AC-2).
12
+ */
13
+ export function OnboardingHero({ assetBase, onChooseStandard, onChooseAdvanced, staggerMs = 140 }) {
14
+ return (_jsxs("div", { "data-testid": "onboarding-hero", style: {
15
+ display: "flex",
16
+ alignItems: "center",
17
+ justifyContent: "center",
18
+ minHeight: "100vh",
19
+ padding: 32,
20
+ background: "var(--bg-canvas)",
21
+ textAlign: "center",
22
+ }, children: [_jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 36, width: "100%", maxWidth: 620 }, children: [_jsxs("div", { "data-testid": "onboarding-hero-marks", style: { display: "flex", alignItems: "center", justifyContent: "center", gap: 20 }, children: [HERO_PRODUCT_MARKS.map((product, i) => (_jsx(HeroEntry, { testId: `onboarding-hero-logo-${product}`, src: productLogoUrl(product, assetBase), delayMs: i * staggerMs, size: 44, isAnchor: false }, product))), _jsx(HeroEntry, { testId: "onboarding-hero-mark", src: productLogoUrl("hive", assetBase), delayMs: HERO_PRODUCT_MARKS.length * staggerMs, size: 56, isAnchor: true })] }), _jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [_jsx("h1", { style: { fontSize: "var(--text-3xl)", fontWeight: 700, color: "var(--text-primary)", margin: 0, letterSpacing: "-0.02em" }, children: "Welcome to the hive" }), _jsx("p", { style: { fontFamily: "var(--font-sans)", fontSize: "var(--text-base)", color: "var(--text-secondary)", margin: 0 }, children: "Let\u2019s get the rest of your fleet running." })] }), _jsxs("div", { "data-testid": "onboarding-choices", className: "hc-onboarding-anim", style: {
23
+ animationDelay: `${(HERO_PRODUCT_MARKS.length + 1) * staggerMs}ms`,
24
+ display: "flex",
25
+ gap: 16,
26
+ flexWrap: "wrap",
27
+ justifyContent: "center",
28
+ }, children: [_jsxs("button", { type: "button", "data-testid": "onboarding-standard-button", onClick: onChooseStandard, style: choiceButtonStyle("primary"), children: [_jsx("span", { style: { fontWeight: 700 }, children: "Standard User" }), _jsx("span", { style: choiceSubtextStyle("primary"), children: "Install the fleet (recommended)" })] }), _jsxs("button", { type: "button", "data-testid": "onboarding-advanced-button", onClick: onChooseAdvanced, style: choiceButtonStyle("secondary"), children: [_jsx("span", { style: { fontWeight: 700 }, children: "Advanced User" }), _jsx("span", { style: choiceSubtextStyle("secondary"), children: "Custom installation" })] })] })] }), _jsx("style", { children: [
29
+ // SAFE default: a plain opacity fade, no transform, this IS the reduced-motion form.
30
+ ".hc-onboarding-anim { opacity: 0; animation: hc-onboarding-fade var(--dur-slow) var(--ease-out) both; }",
31
+ "@keyframes hc-onboarding-fade { from { opacity: 0 } to { opacity: 1 } }",
32
+ // Motion allowed: swap in the richer rise-and-settle keyframes, staggered by the
33
+ // inline `animationDelay` each entry already carries.
34
+ "@media (prefers-reduced-motion: no-preference) {",
35
+ " .hc-onboarding-hero-logo { animation-name: hc-onboarding-rise; animation-duration: 620ms; }",
36
+ // The Hive mark settling last gets a spring-like overshoot curve, distinct from the
37
+ // plain ease-out the product logos use, so it visually "settles" rather than just arriving.
38
+ " .hc-onboarding-hero-anchor { animation-name: hc-onboarding-settle; animation-duration: 760ms; animation-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1); }",
39
+ "}",
40
+ "@keyframes hc-onboarding-rise { from { opacity: 0; transform: translateY(18px) scale(0.9); } to { opacity: 1; transform: translateY(0) scale(1); } }",
41
+ "@keyframes hc-onboarding-settle { from { opacity: 0; transform: translateY(24px) scale(0.7); } to { opacity: 1; transform: translateY(0) scale(1); } }",
42
+ ].join("\n") })] }));
43
+ }
44
+ function choiceButtonStyle(variant) {
45
+ return {
46
+ display: "flex",
47
+ flexDirection: "column",
48
+ alignItems: "flex-start",
49
+ gap: 4,
50
+ minWidth: 220,
51
+ padding: "16px 22px",
52
+ borderRadius: "var(--radius-lg)",
53
+ border: variant === "primary" ? "1px solid transparent" : "1px solid var(--border-strong)",
54
+ background: variant === "primary" ? "var(--honey)" : "var(--bg-elevated)",
55
+ color: variant === "primary" ? "var(--honey-on)" : "var(--text-primary)",
56
+ cursor: "pointer",
57
+ fontFamily: "var(--font-sans)",
58
+ fontSize: "var(--text-base)",
59
+ textAlign: "left",
60
+ };
61
+ }
62
+ function choiceSubtextStyle(variant) {
63
+ return {
64
+ fontSize: "var(--text-xs)",
65
+ fontWeight: 500,
66
+ color: variant === "primary" ? "var(--honey-on)" : "var(--text-secondary)",
67
+ opacity: variant === "primary" ? 0.85 : 1,
68
+ };
69
+ }
70
+ //# sourceMappingURL=onboarding-hero.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"onboarding-hero.js","sourceRoot":"","sources":["../../../../src/dashboard/web/onboarding/onboarding-hero.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * The `/onboarding` route's TOP-LEVEL screen, PRD-009b. Mirrors `/buzzing`/`/login`'s posture
3
+ * (`buzzing-screen.tsx`, `setup-gate.tsx`): a single top-level component `main.tsx` mounts directly
4
+ * (via `boot-route.ts`), owning its own state machine rather than nesting inside the authenticated
5
+ * `<Shell>`. The state machine:
6
+ *
7
+ * detect (+ an upfront health read for ob-AC-3) ─┬─ all installed AND healthy → short-circuit
8
+ * ├─ a remaining product is mid-flight/failed →
9
+ * │ resume straight into "installing" (ob-AC-16/17)
10
+ * └─ otherwise → the first-run hero (ob-AC-4/5)
11
+ * hero ─ Standard → installing(all remaining, fixed order) (ob-AC-6)
12
+ * └ Advanced → picker → installing(exactly the chosen subset) (ob-AC-7)
13
+ * installing(queue, index) ── each card completes+dwells → advance → next index, else → health
14
+ * health ── polls until ready → login
15
+ * login ── device-code display, then a hard navigation to `/` on authenticated (ob-AC-14/15)
16
+ *
17
+ * Resume honesty (ob-AC-16): the Standard/Advanced choice is persisted client-side
18
+ * (onboarding-selection-store), so a resumed install uses `buildResumeQueue`, which excludes a
19
+ * not-installed product the operator explicitly deselected in Advanced. Without that memory, resume
20
+ * would fall back to only products that are genuinely mid-flight or failed, never silently
21
+ * reinstalling a deselected one.
22
+ */
23
+ import React from "react";
24
+ import { type OnboardingClient } from "./onboarding-client.js";
25
+ import type { WireClient } from "../wire.js";
26
+ export interface OnboardingScreenProps {
27
+ readonly assetBase?: string;
28
+ /** Test seam: inject a fake onboarding client, bypassing the real token + fetch wiring entirely. */
29
+ readonly client?: OnboardingClient;
30
+ /** Test seam: inject a fake proxied setup wire client for the login step. */
31
+ readonly wire?: WireClient;
32
+ /** Test seam: override the ~30s install dwell (ob-AC-11). */
33
+ readonly minDwellMs?: number;
34
+ /** Test seam: override the health-check poll interval. */
35
+ readonly healthPollMs?: number;
36
+ /** Test seam: override the login step's `/setup/state` poll interval. */
37
+ readonly loginPollMs?: number;
38
+ /** Test seam: called instead of the real hard navigation from the short-circuit summary. */
39
+ readonly onShortCircuitNavigate?: () => void;
40
+ /** Test seam: called instead of the real hard navigation once login completes (ob-AC-15). */
41
+ readonly onAuthenticated?: () => void;
42
+ }
43
+ export declare function OnboardingScreen({ assetBase, client: clientOverride, wire, minDwellMs, healthPollMs, loginPollMs, onShortCircuitNavigate, onAuthenticated, }: OnboardingScreenProps): React.JSX.Element;
@@ -0,0 +1,161 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * The `/onboarding` route's TOP-LEVEL screen, PRD-009b. Mirrors `/buzzing`/`/login`'s posture
4
+ * (`buzzing-screen.tsx`, `setup-gate.tsx`): a single top-level component `main.tsx` mounts directly
5
+ * (via `boot-route.ts`), owning its own state machine rather than nesting inside the authenticated
6
+ * `<Shell>`. The state machine:
7
+ *
8
+ * detect (+ an upfront health read for ob-AC-3) ─┬─ all installed AND healthy → short-circuit
9
+ * ├─ a remaining product is mid-flight/failed →
10
+ * │ resume straight into "installing" (ob-AC-16/17)
11
+ * └─ otherwise → the first-run hero (ob-AC-4/5)
12
+ * hero ─ Standard → installing(all remaining, fixed order) (ob-AC-6)
13
+ * └ Advanced → picker → installing(exactly the chosen subset) (ob-AC-7)
14
+ * installing(queue, index) ── each card completes+dwells → advance → next index, else → health
15
+ * health ── polls until ready → login
16
+ * login ── device-code display, then a hard navigation to `/` on authenticated (ob-AC-14/15)
17
+ *
18
+ * Resume honesty (ob-AC-16): the Standard/Advanced choice is persisted client-side
19
+ * (onboarding-selection-store), so a resumed install uses `buildResumeQueue`, which excludes a
20
+ * not-installed product the operator explicitly deselected in Advanced. Without that memory, resume
21
+ * would fall back to only products that are genuinely mid-flight or failed, never silently
22
+ * reinstalling a deselected one.
23
+ */
24
+ import React from "react";
25
+ import { AdvancedPicker } from "./advanced-picker.js";
26
+ import { buildResumeQueue, DEFAULT_PRODUCT_DETECTION, detectionFor, hasResumableInstall, isFleetFullyInstalled, remainingProducts, } from "./contracts.js";
27
+ import { clearSelection, persistSelection, readSelection } from "./onboarding-selection-store.js";
28
+ import { HealthView } from "./health-view.js";
29
+ import { InstallCard } from "./install-card.js";
30
+ import { LoginStep } from "./login-step.js";
31
+ import { createOnboardingClient } from "./onboarding-client.js";
32
+ import { OnboardingHero } from "./onboarding-hero.js";
33
+ import { useOnboardingToken } from "./use-onboarding-token.js";
34
+ import { Button } from "../primitives.js";
35
+ /** An empty install queue means nothing more to do, skip straight to the health gate. */
36
+ function installingOrHealth(queue) {
37
+ return queue.length === 0 ? { kind: "health" } : { kind: "installing", queue, index: 0 };
38
+ }
39
+ /** ob-AC-3, the terminal "nothing to do" screen for an already-installed, healthy machine. */
40
+ function ShortCircuitSummary({ onGoToDashboard }) {
41
+ return (_jsx("div", { "data-testid": "onboarding-short-circuit", style: { display: "flex", alignItems: "center", justifyContent: "center", minHeight: "100vh", padding: 32, background: "var(--bg-canvas)", textAlign: "center" }, children: _jsxs("div", { style: {
42
+ display: "flex",
43
+ flexDirection: "column",
44
+ alignItems: "center",
45
+ gap: 18,
46
+ maxWidth: 440,
47
+ padding: "40px 32px",
48
+ background: "var(--bg-surface)",
49
+ border: "1px solid var(--border-default)",
50
+ borderRadius: "var(--radius-xl)",
51
+ }, children: [_jsx("h1", { style: { fontSize: "var(--text-xl)", fontWeight: 700, color: "var(--text-primary)", margin: 0, letterSpacing: "-0.02em" }, children: "Your fleet is already up" }), _jsx("p", { style: { fontFamily: "var(--font-sans)", fontSize: "var(--text-sm)", color: "var(--text-secondary)", margin: 0, lineHeight: 1.5 }, children: "Every product is installed and the fleet reads healthy. Nothing to install here." }), _jsx(Button, { variant: "primary", size: "lg", "data-testid": "onboarding-go-to-dashboard", onClick: onGoToDashboard, children: "Go to dashboard" })] }) }));
52
+ }
53
+ /** The brief pre-detection placeholder, visible only for the moment detect+health are in flight. */
54
+ function LoadingPlaceholder() {
55
+ return (_jsx("div", { "data-testid": "onboarding-loading", style: { display: "flex", alignItems: "center", justifyContent: "center", minHeight: "100vh", background: "var(--bg-canvas)" }, children: _jsx("p", { style: { fontFamily: "var(--font-mono)", fontSize: "var(--text-xs)", color: "var(--text-tertiary)" }, children: "Checking what is already installed\u2026" }) }));
56
+ }
57
+ export function OnboardingScreen({ assetBase = "assets", client: clientOverride, wire, minDwellMs, healthPollMs, loginPollMs, onShortCircuitNavigate, onAuthenticated, }) {
58
+ const token = useOnboardingToken();
59
+ // A test-injected client bypasses the token gate entirely (it never talks to the real token
60
+ // wiring); real usage waits for `useOnboardingToken` to resolve the `?t=` query param first.
61
+ const tokenReady = clientOverride !== undefined || token !== "";
62
+ const client = React.useMemo(() => clientOverride ?? createOnboardingClient(token), [clientOverride, token]);
63
+ const [phase, setPhase] = React.useState({ kind: "loading" });
64
+ const [detection, setDetection] = React.useState(null);
65
+ const startedEventFiredRef = React.useRef(false);
66
+ React.useEffect(() => {
67
+ if (!tokenReady || startedEventFiredRef.current)
68
+ return;
69
+ startedEventFiredRef.current = true;
70
+ client.sendEvent("onboarding_started");
71
+ }, [client, tokenReady]);
72
+ React.useEffect(() => {
73
+ if (!tokenReady)
74
+ return;
75
+ let alive = true;
76
+ void (async () => {
77
+ const [detectResult, healthResult] = await Promise.all([client.detect(), client.health()]);
78
+ if (!alive)
79
+ return;
80
+ setDetection(detectResult);
81
+ if (isFleetFullyInstalled(detectResult) && healthResult.ready) {
82
+ setPhase({ kind: "short-circuit" });
83
+ }
84
+ else if (hasResumableInstall(detectResult)) {
85
+ // Resume honors the operator's persisted subset so a deselected product is never
86
+ // silently reinstalled; falls back to only mid-flight/failed products when unknown.
87
+ setPhase(installingOrHealth(buildResumeQueue(detectResult, readSelection())));
88
+ }
89
+ else {
90
+ setPhase({ kind: "hero" });
91
+ }
92
+ })();
93
+ return () => {
94
+ alive = false;
95
+ };
96
+ }, [client, tokenReady]);
97
+ const chooseMode = React.useCallback((mode) => {
98
+ client.sendEvent("mode_selected", { mode });
99
+ if (mode === "standard") {
100
+ const queue = detection !== null ? remainingProducts(detection) : [];
101
+ persistSelection(queue);
102
+ setPhase(installingOrHealth(queue));
103
+ }
104
+ else {
105
+ setPhase({ kind: "picker" });
106
+ }
107
+ }, [client, detection]);
108
+ const confirmAdvanced = React.useCallback((selected) => {
109
+ persistSelection(selected);
110
+ setPhase(installingOrHealth(selected));
111
+ }, []);
112
+ const advanceInstall = React.useCallback(() => {
113
+ setPhase((current) => {
114
+ if (current.kind !== "installing")
115
+ return current;
116
+ const nextIndex = current.index + 1;
117
+ return nextIndex >= current.queue.length ? { kind: "health" } : { kind: "installing", queue: current.queue, index: nextIndex };
118
+ });
119
+ }, []);
120
+ const goToLogin = React.useCallback(() => setPhase({ kind: "login" }), []);
121
+ const handleShortCircuitNavigate = React.useCallback(() => {
122
+ clearSelection();
123
+ if (onShortCircuitNavigate !== undefined) {
124
+ onShortCircuitNavigate();
125
+ return;
126
+ }
127
+ if (typeof window !== "undefined")
128
+ window.location.assign("/");
129
+ }, [onShortCircuitNavigate]);
130
+ // Onboarding reached its terminal state (login complete): drop the persisted subset so a later
131
+ // visit starts clean rather than resuming against a stale choice.
132
+ const handleAuthenticated = React.useCallback(() => {
133
+ clearSelection();
134
+ if (onAuthenticated !== undefined)
135
+ onAuthenticated();
136
+ }, [onAuthenticated]);
137
+ switch (phase.kind) {
138
+ case "loading":
139
+ return _jsx(LoadingPlaceholder, {});
140
+ case "short-circuit":
141
+ return _jsx(ShortCircuitSummary, { onGoToDashboard: handleShortCircuitNavigate });
142
+ case "hero":
143
+ return _jsx(OnboardingHero, { assetBase: assetBase, onChooseStandard: () => chooseMode("standard"), onChooseAdvanced: () => chooseMode("advanced") });
144
+ case "picker":
145
+ return _jsx(AdvancedPicker, { products: detection !== null ? remainingProducts(detection) : [], assetBase: assetBase, onConfirm: confirmAdvanced });
146
+ case "installing": {
147
+ const product = phase.queue[phase.index];
148
+ const initialDetection = detection !== null ? detectionFor(detection, product) : DEFAULT_PRODUCT_DETECTION;
149
+ return (_jsx(InstallCard, { product: product, initialDetection: initialDetection, client: client, assetBase: assetBase, onAdvance: advanceInstall, minDwellMs: minDwellMs }, product));
150
+ }
151
+ case "health":
152
+ return _jsx(HealthView, { client: client, onReady: goToLogin, pollMs: healthPollMs });
153
+ case "login":
154
+ return _jsx(LoginStep, { onboardingClient: client, wire: wire, onAuthenticated: handleAuthenticated, pollMs: loginPollMs });
155
+ default: {
156
+ const exhaustive = phase;
157
+ return exhaustive;
158
+ }
159
+ }
160
+ }
161
+ //# sourceMappingURL=onboarding-screen.js.map