@legioncodeinc/hive 0.2.1 → 0.3.1
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/README.md +2 -2
- package/assets/brand/activeloop-full-mark-logo-on-dark.svg +209 -0
- package/assets/brand/activeloop-full-mark-logo.svg +208 -0
- package/assets/brand/divider-major.svg +1 -0
- package/assets/brand/divider-minor.svg +1 -0
- package/assets/brand/doctor-mark.svg +1 -0
- package/assets/brand/hive-mark.svg +1 -0
- package/assets/brand/hive-wordmark-black.svg +1 -0
- package/assets/brand/hive-wordmark-on-dark.svg +1 -0
- package/assets/brand/legion-logo-dark.svg +16 -0
- package/assets/brand/legion-logo-light.svg +16 -0
- package/assets/brand/nectar-mark.svg +1 -0
- package/assets/logos/fonts/Inter-Italic-VariableFont_opsz_wght.ttf +0 -0
- package/assets/logos/fonts/Inter-VariableFont_opsz_wght.ttf +0 -0
- package/assets/logos/fonts/JetBrainsMono-Bold.woff2 +0 -0
- package/assets/logos/fonts/JetBrainsMono-Medium.woff2 +0 -0
- package/assets/logos/fonts/JetBrainsMono-Regular.woff2 +0 -0
- package/assets/logos/fonts/JetBrainsMono-SemiBold.woff2 +0 -0
- package/assets/logos/honeycomb-memory-cluster.svg +17 -0
- package/assets/styles.css +11 -0
- package/assets/tokens/base.css +76 -0
- package/assets/tokens/colors.css +111 -0
- package/assets/tokens/fonts.css +32 -0
- package/assets/tokens/spacing.css +48 -0
- package/assets/tokens/typography.css +38 -0
- package/dist/daemon/dashboard/app.js +26 -25
- package/dist/daemon/dashboard/host.d.ts +7 -0
- package/dist/daemon/dashboard/host.js +16 -0
- package/dist/daemon/dashboard/host.js.map +1 -1
- package/dist/daemon/dashboard/web-assets.d.ts +2 -0
- package/dist/daemon/dashboard/web-assets.js +19 -0
- package/dist/daemon/dashboard/web-assets.js.map +1 -1
- package/dist/daemon/gate.d.ts +2 -2
- package/dist/daemon/gate.js +15 -4
- package/dist/daemon/gate.js.map +1 -1
- package/dist/daemon/installer/bin-resolver.d.ts +34 -0
- package/dist/daemon/installer/bin-resolver.js +107 -0
- package/dist/daemon/installer/bin-resolver.js.map +1 -0
- package/dist/daemon/installer/config.d.ts +63 -0
- package/dist/daemon/installer/config.js +74 -0
- package/dist/daemon/installer/config.js.map +1 -0
- package/dist/daemon/installer/detection.d.ts +20 -0
- package/dist/daemon/installer/detection.js +73 -0
- package/dist/daemon/installer/detection.js.map +1 -0
- package/dist/daemon/installer/funnel-telemetry.d.ts +54 -0
- package/dist/daemon/installer/funnel-telemetry.js +134 -0
- package/dist/daemon/installer/funnel-telemetry.js.map +1 -0
- package/dist/daemon/installer/index.d.ts +12 -0
- package/dist/daemon/installer/index.js +10 -0
- package/dist/daemon/installer/index.js.map +1 -0
- package/dist/daemon/installer/install-state.d.ts +56 -0
- package/dist/daemon/installer/install-state.js +159 -0
- package/dist/daemon/installer/install-state.js.map +1 -0
- package/dist/daemon/installer/manifest-snapshot.json +25 -0
- package/dist/daemon/installer/manifest.d.ts +47 -0
- package/dist/daemon/installer/manifest.js +103 -0
- package/dist/daemon/installer/manifest.js.map +1 -0
- package/dist/daemon/installer/products.d.ts +33 -0
- package/dist/daemon/installer/products.js +42 -0
- package/dist/daemon/installer/products.js.map +1 -0
- package/dist/daemon/installer/routes.d.ts +43 -0
- package/dist/daemon/installer/routes.js +201 -0
- package/dist/daemon/installer/routes.js.map +1 -0
- package/dist/daemon/installer/security.d.ts +33 -0
- package/dist/daemon/installer/security.js +80 -0
- package/dist/daemon/installer/security.js.map +1 -0
- package/dist/daemon/installer/spawn.d.ts +49 -0
- package/dist/daemon/installer/spawn.js +63 -0
- package/dist/daemon/installer/spawn.js.map +1 -0
- package/dist/daemon/installer/token.d.ts +23 -0
- package/dist/daemon/installer/token.js +56 -0
- package/dist/daemon/installer/token.js.map +1 -0
- package/dist/daemon/server.d.ts +6 -0
- package/dist/daemon/server.js +7 -0
- package/dist/daemon/server.js.map +1 -1
- package/dist/dashboard/web/app.js +42 -20
- package/dist/dashboard/web/app.js.map +1 -1
- package/dist/dashboard/web/boot-route.d.ts +11 -7
- package/dist/dashboard/web/boot-route.js +12 -6
- package/dist/dashboard/web/boot-route.js.map +1 -1
- package/dist/dashboard/web/main.js +2 -1
- package/dist/dashboard/web/main.js.map +1 -1
- package/dist/dashboard/web/onboarding/advanced-picker.d.ts +16 -0
- package/dist/dashboard/web/onboarding/advanced-picker.js +59 -0
- package/dist/dashboard/web/onboarding/advanced-picker.js.map +1 -0
- package/dist/dashboard/web/onboarding/contracts.d.ts +188 -0
- package/dist/dashboard/web/onboarding/contracts.js +161 -0
- package/dist/dashboard/web/onboarding/contracts.js.map +1 -0
- package/dist/dashboard/web/onboarding/health-view.d.ts +17 -0
- package/dist/dashboard/web/onboarding/health-view.js +79 -0
- package/dist/dashboard/web/onboarding/health-view.js.map +1 -0
- package/dist/dashboard/web/onboarding/install-card.d.ts +28 -0
- package/dist/dashboard/web/onboarding/install-card.js +171 -0
- package/dist/dashboard/web/onboarding/install-card.js.map +1 -0
- package/dist/dashboard/web/onboarding/login-step.d.ts +29 -0
- package/dist/dashboard/web/onboarding/login-step.js +104 -0
- package/dist/dashboard/web/onboarding/login-step.js.map +1 -0
- package/dist/dashboard/web/onboarding/onboarding-client.d.ts +52 -0
- package/dist/dashboard/web/onboarding/onboarding-client.js +133 -0
- package/dist/dashboard/web/onboarding/onboarding-client.js.map +1 -0
- package/dist/dashboard/web/onboarding/onboarding-hero.d.ts +24 -0
- package/dist/dashboard/web/onboarding/onboarding-hero.js +70 -0
- package/dist/dashboard/web/onboarding/onboarding-hero.js.map +1 -0
- package/dist/dashboard/web/onboarding/onboarding-screen.d.ts +43 -0
- package/dist/dashboard/web/onboarding/onboarding-screen.js +193 -0
- package/dist/dashboard/web/onboarding/onboarding-screen.js.map +1 -0
- package/dist/dashboard/web/onboarding/onboarding-selection-store.d.ts +20 -0
- package/dist/dashboard/web/onboarding/onboarding-selection-store.js +76 -0
- package/dist/dashboard/web/onboarding/onboarding-selection-store.js.map +1 -0
- package/dist/dashboard/web/onboarding/product-copy.d.ts +40 -0
- package/dist/dashboard/web/onboarding/product-copy.js +70 -0
- package/dist/dashboard/web/onboarding/product-copy.js.map +1 -0
- package/dist/dashboard/web/onboarding/use-install-dwell.d.ts +27 -0
- package/dist/dashboard/web/onboarding/use-install-dwell.js +42 -0
- package/dist/dashboard/web/onboarding/use-install-dwell.js.map +1 -0
- package/dist/dashboard/web/onboarding/use-onboarding-token.d.ts +14 -0
- package/dist/dashboard/web/onboarding/use-onboarding-token.js +41 -0
- package/dist/dashboard/web/onboarding/use-onboarding-token.js.map +1 -0
- package/dist/dashboard/web/route-daemon-owner.d.ts +7 -0
- package/dist/dashboard/web/route-daemon-owner.js +15 -0
- package/dist/dashboard/web/route-daemon-owner.js.map +1 -0
- package/dist/dashboard/web/wire.d.ts +1 -1
- package/dist/shared/onboarding-types.d.ts +79 -0
- package/dist/shared/onboarding-types.js +12 -0
- package/dist/shared/onboarding-types.js.map +1 -0
- package/dist/telemetry/emit.d.ts +30 -3
- package/dist/telemetry/emit.js +25 -2
- package/dist/telemetry/emit.js.map +1 -1
- package/dist/telemetry/onboarding-session-ledger.d.ts +31 -0
- package/dist/telemetry/onboarding-session-ledger.js +78 -0
- package/dist/telemetry/onboarding-session-ledger.js.map +1 -0
- package/package.json +6 -2
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `/onboarding` route's WIRE CONTRACT, PRD-009b, mirroring the daemon-side installer service
|
|
3
|
+
* from PRD-009a (`src/daemon/installer/`). `src/shared/onboarding-types.ts` is being authored in
|
|
4
|
+
* parallel by the daemon-side agent; these are LOCAL types matching that contract's field names
|
|
5
|
+
* exactly (per the implementation brief), so a later integration pass can point imports at the
|
|
6
|
+
* shared module without changing a single field name.
|
|
7
|
+
*
|
|
8
|
+
* Every schema mirrors the `wire.ts` convention: zod at the boundary, every field `.catch()`-
|
|
9
|
+
* defaulted so a partial/malformed body degrades to a safe empty state rather than throwing into
|
|
10
|
+
* React (the onboarding screen is the FIRST thing a new operator sees: it must never white-screen).
|
|
11
|
+
*/
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
import type { FleetHealth, FleetStatusResponse } from "../../../shared/fleet-readiness.js";
|
|
14
|
+
/** Every product the daemon reports detection for (hive is always present; it is the caller). */
|
|
15
|
+
export declare const ONBOARDING_PRODUCTS: readonly ["honeycomb", "doctor", "hive", "nectar"];
|
|
16
|
+
export type OnboardingProduct = (typeof ONBOARDING_PRODUCTS)[number];
|
|
17
|
+
/** The three INSTALLABLE products, in the fixed order the guided flow walks them (ob-AC-6). */
|
|
18
|
+
export declare const FIXED_PRODUCT_ORDER: readonly ["doctor", "honeycomb", "nectar"];
|
|
19
|
+
export type InstallableProduct = (typeof FIXED_PRODUCT_ORDER)[number];
|
|
20
|
+
export declare function isInstallableProduct(value: string): value is InstallableProduct;
|
|
21
|
+
export declare const PRODUCT_INSTALL_STATES: readonly ["not_installed", "installed", "install_in_progress", "install_failed"];
|
|
22
|
+
export type ProductInstallState = (typeof PRODUCT_INSTALL_STATES)[number];
|
|
23
|
+
declare const ProductErrorSchema: z.ZodObject<{
|
|
24
|
+
stage: z.ZodCatch<z.ZodString>;
|
|
25
|
+
summary: z.ZodCatch<z.ZodString>;
|
|
26
|
+
}, z.core.$strip>;
|
|
27
|
+
export type ProductInstallError = z.infer<typeof ProductErrorSchema>;
|
|
28
|
+
declare const ProductDetectionSchema: z.ZodObject<{
|
|
29
|
+
state: z.ZodCatch<z.ZodEnum<{
|
|
30
|
+
not_installed: "not_installed";
|
|
31
|
+
installed: "installed";
|
|
32
|
+
install_in_progress: "install_in_progress";
|
|
33
|
+
install_failed: "install_failed";
|
|
34
|
+
}>>;
|
|
35
|
+
version: z.ZodOptional<z.ZodString>;
|
|
36
|
+
error: z.ZodOptional<z.ZodObject<{
|
|
37
|
+
stage: z.ZodCatch<z.ZodString>;
|
|
38
|
+
summary: z.ZodCatch<z.ZodString>;
|
|
39
|
+
}, z.core.$strip>>;
|
|
40
|
+
}, z.core.$strip>;
|
|
41
|
+
export type ProductDetection = z.infer<typeof ProductDetectionSchema>;
|
|
42
|
+
/** The safe default for a product the response omitted (never assume installed). */
|
|
43
|
+
export declare const DEFAULT_PRODUCT_DETECTION: ProductDetection;
|
|
44
|
+
export declare const DetectResponseSchema: z.ZodObject<{
|
|
45
|
+
products: z.ZodCatch<z.ZodObject<{
|
|
46
|
+
honeycomb: z.ZodOptional<z.ZodObject<{
|
|
47
|
+
state: z.ZodCatch<z.ZodEnum<{
|
|
48
|
+
not_installed: "not_installed";
|
|
49
|
+
installed: "installed";
|
|
50
|
+
install_in_progress: "install_in_progress";
|
|
51
|
+
install_failed: "install_failed";
|
|
52
|
+
}>>;
|
|
53
|
+
version: z.ZodOptional<z.ZodString>;
|
|
54
|
+
error: z.ZodOptional<z.ZodObject<{
|
|
55
|
+
stage: z.ZodCatch<z.ZodString>;
|
|
56
|
+
summary: z.ZodCatch<z.ZodString>;
|
|
57
|
+
}, z.core.$strip>>;
|
|
58
|
+
}, z.core.$strip>>;
|
|
59
|
+
doctor: z.ZodOptional<z.ZodObject<{
|
|
60
|
+
state: z.ZodCatch<z.ZodEnum<{
|
|
61
|
+
not_installed: "not_installed";
|
|
62
|
+
installed: "installed";
|
|
63
|
+
install_in_progress: "install_in_progress";
|
|
64
|
+
install_failed: "install_failed";
|
|
65
|
+
}>>;
|
|
66
|
+
version: z.ZodOptional<z.ZodString>;
|
|
67
|
+
error: z.ZodOptional<z.ZodObject<{
|
|
68
|
+
stage: z.ZodCatch<z.ZodString>;
|
|
69
|
+
summary: z.ZodCatch<z.ZodString>;
|
|
70
|
+
}, z.core.$strip>>;
|
|
71
|
+
}, z.core.$strip>>;
|
|
72
|
+
hive: z.ZodOptional<z.ZodObject<{
|
|
73
|
+
state: z.ZodCatch<z.ZodEnum<{
|
|
74
|
+
not_installed: "not_installed";
|
|
75
|
+
installed: "installed";
|
|
76
|
+
install_in_progress: "install_in_progress";
|
|
77
|
+
install_failed: "install_failed";
|
|
78
|
+
}>>;
|
|
79
|
+
version: z.ZodOptional<z.ZodString>;
|
|
80
|
+
error: z.ZodOptional<z.ZodObject<{
|
|
81
|
+
stage: z.ZodCatch<z.ZodString>;
|
|
82
|
+
summary: z.ZodCatch<z.ZodString>;
|
|
83
|
+
}, z.core.$strip>>;
|
|
84
|
+
}, z.core.$strip>>;
|
|
85
|
+
nectar: z.ZodOptional<z.ZodObject<{
|
|
86
|
+
state: z.ZodCatch<z.ZodEnum<{
|
|
87
|
+
not_installed: "not_installed";
|
|
88
|
+
installed: "installed";
|
|
89
|
+
install_in_progress: "install_in_progress";
|
|
90
|
+
install_failed: "install_failed";
|
|
91
|
+
}>>;
|
|
92
|
+
version: z.ZodOptional<z.ZodString>;
|
|
93
|
+
error: z.ZodOptional<z.ZodObject<{
|
|
94
|
+
stage: z.ZodCatch<z.ZodString>;
|
|
95
|
+
summary: z.ZodCatch<z.ZodString>;
|
|
96
|
+
}, z.core.$strip>>;
|
|
97
|
+
}, z.core.$strip>>;
|
|
98
|
+
}, z.core.$strip>>;
|
|
99
|
+
}, z.core.$strip>;
|
|
100
|
+
export type DetectResponse = z.infer<typeof DetectResponseSchema>;
|
|
101
|
+
/** The honest "nothing detected yet" default (a fetch failure never fabricates an installed product). */
|
|
102
|
+
export declare const EMPTY_DETECTION: DetectResponse;
|
|
103
|
+
/** Read one product's detection, defaulting to {@link DEFAULT_PRODUCT_DETECTION} when omitted. */
|
|
104
|
+
export declare function detectionFor(detection: DetectResponse, product: OnboardingProduct): ProductDetection;
|
|
105
|
+
/** ob-AC-3, every one of the four products reports `installed`. */
|
|
106
|
+
export declare function isFleetFullyInstalled(detection: DetectResponse): boolean;
|
|
107
|
+
/**
|
|
108
|
+
* The remaining (not-yet-installed) installable products, in the FIXED order, the set both the
|
|
109
|
+
* Standard flow (ob-AC-6, unfiltered) and the Advanced picker (ob-AC-7, pre-checked) start from.
|
|
110
|
+
*/
|
|
111
|
+
export declare function remainingProducts(detection: DetectResponse): readonly InstallableProduct[];
|
|
112
|
+
/**
|
|
113
|
+
* ob-AC-16/ob-AC-17, true when detection shows an install already under way or failed for one of
|
|
114
|
+
* the remaining products, meaning a choice (Standard or Advanced) was already made in a prior
|
|
115
|
+
* visit. The resumed queue is simply {@link remainingProducts} walked from the top: any product
|
|
116
|
+
* already `installed` is filtered out, and the first remaining entry is exactly the one that was
|
|
117
|
+
* mid-flight or failed (re-attached, never re-offered the hero/picker).
|
|
118
|
+
*/
|
|
119
|
+
export declare function hasResumableInstall(detection: DetectResponse): boolean;
|
|
120
|
+
/**
|
|
121
|
+
* The queue to resume with (ob-AC-16), honoring the operator's original subset when it is known.
|
|
122
|
+
* `remainingProducts` alone would reinstall a product the operator explicitly DESELECTED in the
|
|
123
|
+
* Advanced picker, because the daemon has no server-side memory of that subset. When a persisted
|
|
124
|
+
* selection exists (see onboarding-selection-store), the resume queue is the remaining products
|
|
125
|
+
* intersected with it, so a deselected product is never silently reinstalled. When no selection is
|
|
126
|
+
* persisted (a fresh browser, cleared storage), any product that is genuinely mid-flight or failed
|
|
127
|
+
* is still resumed (an in-flight fact, not an assumption); a merely `not_installed` product with no
|
|
128
|
+
* persisted intent is left out, so the flow never installs something the operator did not choose.
|
|
129
|
+
*/
|
|
130
|
+
export declare function buildResumeQueue(detection: DetectResponse, persistedSelection: readonly InstallableProduct[] | null): readonly InstallableProduct[];
|
|
131
|
+
export declare const InstallStartResponseSchema: z.ZodObject<{
|
|
132
|
+
product: z.ZodCatch<z.ZodEnum<{
|
|
133
|
+
honeycomb: "honeycomb";
|
|
134
|
+
nectar: "nectar";
|
|
135
|
+
doctor: "doctor";
|
|
136
|
+
}>>;
|
|
137
|
+
state: z.ZodCatch<z.ZodEnum<{
|
|
138
|
+
installed: "installed";
|
|
139
|
+
install_in_progress: "install_in_progress";
|
|
140
|
+
}>>;
|
|
141
|
+
}, z.core.$strip>;
|
|
142
|
+
export type InstallStartResponse = z.infer<typeof InstallStartResponseSchema>;
|
|
143
|
+
/** `POST /api/onboarding/install` -> 409 refusal (is-AC-5). */
|
|
144
|
+
export declare const InstallRefusalResponseSchema: z.ZodObject<{
|
|
145
|
+
error: z.ZodEnum<{
|
|
146
|
+
unpublished: "unpublished";
|
|
147
|
+
manifest_unresolved: "manifest_unresolved";
|
|
148
|
+
}>;
|
|
149
|
+
}, z.core.$strip>;
|
|
150
|
+
export type InstallRefusalResponse = z.infer<typeof InstallRefusalResponseSchema>;
|
|
151
|
+
export type InstallStartResult = InstallStartResponse | InstallRefusalResponse;
|
|
152
|
+
/** Honest operator copy for installer refusals (409), with the same retry affordance as other failures. */
|
|
153
|
+
export declare function installRefusalMessage(error: InstallRefusalResponse["error"]): string;
|
|
154
|
+
/** The four REAL, observable install stages plus the two terminal outcomes (ob-AC-9: never a percent). */
|
|
155
|
+
export declare const INSTALL_STAGES: readonly ["resolving", "downloading", "linking", "registering_service", "completed", "failed"];
|
|
156
|
+
export type InstallStage = (typeof INSTALL_STAGES)[number];
|
|
157
|
+
/** The four IN-FLIGHT stages, in display order (excludes the two terminal outcomes). */
|
|
158
|
+
export declare const IN_FLIGHT_STAGES: readonly ["resolving", "downloading", "linking", "registering_service"];
|
|
159
|
+
export declare const InstallProgressEventSchema: z.ZodObject<{
|
|
160
|
+
stage: z.ZodCatch<z.ZodEnum<{
|
|
161
|
+
resolving: "resolving";
|
|
162
|
+
downloading: "downloading";
|
|
163
|
+
linking: "linking";
|
|
164
|
+
registering_service: "registering_service";
|
|
165
|
+
completed: "completed";
|
|
166
|
+
failed: "failed";
|
|
167
|
+
}>>;
|
|
168
|
+
detail: z.ZodOptional<z.ZodString>;
|
|
169
|
+
}, z.core.$strip>;
|
|
170
|
+
export type InstallProgressEvent = z.infer<typeof InstallProgressEventSchema>;
|
|
171
|
+
export interface HealthResponse {
|
|
172
|
+
readonly ready: boolean;
|
|
173
|
+
readonly status: FleetStatusResponse;
|
|
174
|
+
}
|
|
175
|
+
/** The honest "daemon unreachable" default a failed/malformed health read degrades to. */
|
|
176
|
+
export declare const UNREACHABLE_HEALTH: HealthResponse;
|
|
177
|
+
/**
|
|
178
|
+
* Parse a `GET /api/onboarding/health` body leniently (mirrors `buzzing-screen.tsx`'s established
|
|
179
|
+
* `as FleetStatusResponse` cast for this exact daemon-owned union, {@link FleetStatusResponse} is
|
|
180
|
+
* a discriminated union keyed on `supervisor`, not a flat shape zod's object schemas model cleanly,
|
|
181
|
+
* and the existing precedent already trusts the daemon's own `isFleetReady`-validated projection).
|
|
182
|
+
*/
|
|
183
|
+
export declare function parseHealthResponse(body: unknown): HealthResponse;
|
|
184
|
+
/** Re-exported so onboarding modules read the daemon health vocabulary from one place. */
|
|
185
|
+
export type { FleetHealth, FleetStatusResponse };
|
|
186
|
+
/** The exact UI-fired event names (product install + health events are daemon-side, never sent here). */
|
|
187
|
+
export declare const ONBOARDING_UI_EVENTS: readonly ["onboarding_started", "mode_selected", "login_shown", "dashboard_reached"];
|
|
188
|
+
export type OnboardingUiEvent = (typeof ONBOARDING_UI_EVENTS)[number];
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `/onboarding` route's WIRE CONTRACT, PRD-009b, mirroring the daemon-side installer service
|
|
3
|
+
* from PRD-009a (`src/daemon/installer/`). `src/shared/onboarding-types.ts` is being authored in
|
|
4
|
+
* parallel by the daemon-side agent; these are LOCAL types matching that contract's field names
|
|
5
|
+
* exactly (per the implementation brief), so a later integration pass can point imports at the
|
|
6
|
+
* shared module without changing a single field name.
|
|
7
|
+
*
|
|
8
|
+
* Every schema mirrors the `wire.ts` convention: zod at the boundary, every field `.catch()`-
|
|
9
|
+
* defaulted so a partial/malformed body degrades to a safe empty state rather than throwing into
|
|
10
|
+
* React (the onboarding screen is the FIRST thing a new operator sees: it must never white-screen).
|
|
11
|
+
*/
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
14
|
+
// Product identity
|
|
15
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
16
|
+
/** Every product the daemon reports detection for (hive is always present; it is the caller). */
|
|
17
|
+
export const ONBOARDING_PRODUCTS = ["honeycomb", "doctor", "hive", "nectar"];
|
|
18
|
+
/** The three INSTALLABLE products, in the fixed order the guided flow walks them (ob-AC-6). */
|
|
19
|
+
export const FIXED_PRODUCT_ORDER = ["doctor", "honeycomb", "nectar"];
|
|
20
|
+
export function isInstallableProduct(value) {
|
|
21
|
+
return FIXED_PRODUCT_ORDER.includes(value);
|
|
22
|
+
}
|
|
23
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
24
|
+
// GET /api/onboarding/detect
|
|
25
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
26
|
+
export const PRODUCT_INSTALL_STATES = ["not_installed", "installed", "install_in_progress", "install_failed"];
|
|
27
|
+
const ProductErrorSchema = z.object({
|
|
28
|
+
stage: z.string().catch(""),
|
|
29
|
+
summary: z.string().catch(""),
|
|
30
|
+
});
|
|
31
|
+
const ProductDetectionSchema = z.object({
|
|
32
|
+
state: z.enum(PRODUCT_INSTALL_STATES).catch("not_installed"),
|
|
33
|
+
version: z.string().optional(),
|
|
34
|
+
error: ProductErrorSchema.optional(),
|
|
35
|
+
});
|
|
36
|
+
/** The safe default for a product the response omitted (never assume installed). */
|
|
37
|
+
export const DEFAULT_PRODUCT_DETECTION = Object.freeze({ state: "not_installed" });
|
|
38
|
+
// Every product key is OPTIONAL (a partial object, not a `z.record` over the enum): the daemon
|
|
39
|
+
// contract never guarantees all four keys ride every response, and "omitted" must degrade to
|
|
40
|
+
// {@link DEFAULT_PRODUCT_DETECTION} per-product rather than failing the WHOLE `products` object
|
|
41
|
+
// (which a zod `z.record` over a finite enum key would do, since that shape requires every key).
|
|
42
|
+
export const DetectResponseSchema = z.object({
|
|
43
|
+
products: z
|
|
44
|
+
.object({
|
|
45
|
+
honeycomb: ProductDetectionSchema.optional(),
|
|
46
|
+
doctor: ProductDetectionSchema.optional(),
|
|
47
|
+
hive: ProductDetectionSchema.optional(),
|
|
48
|
+
nectar: ProductDetectionSchema.optional(),
|
|
49
|
+
})
|
|
50
|
+
.catch({}),
|
|
51
|
+
});
|
|
52
|
+
/** The honest "nothing detected yet" default (a fetch failure never fabricates an installed product). */
|
|
53
|
+
export const EMPTY_DETECTION = Object.freeze({ products: {} });
|
|
54
|
+
/** Read one product's detection, defaulting to {@link DEFAULT_PRODUCT_DETECTION} when omitted. */
|
|
55
|
+
export function detectionFor(detection, product) {
|
|
56
|
+
return detection.products[product] ?? DEFAULT_PRODUCT_DETECTION;
|
|
57
|
+
}
|
|
58
|
+
/** ob-AC-3, every one of the four products reports `installed`. */
|
|
59
|
+
export function isFleetFullyInstalled(detection) {
|
|
60
|
+
return ONBOARDING_PRODUCTS.every((p) => detectionFor(detection, p).state === "installed");
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The remaining (not-yet-installed) installable products, in the FIXED order, the set both the
|
|
64
|
+
* Standard flow (ob-AC-6, unfiltered) and the Advanced picker (ob-AC-7, pre-checked) start from.
|
|
65
|
+
*/
|
|
66
|
+
export function remainingProducts(detection) {
|
|
67
|
+
return FIXED_PRODUCT_ORDER.filter((p) => detectionFor(detection, p).state !== "installed");
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* ob-AC-16/ob-AC-17, true when detection shows an install already under way or failed for one of
|
|
71
|
+
* the remaining products, meaning a choice (Standard or Advanced) was already made in a prior
|
|
72
|
+
* visit. The resumed queue is simply {@link remainingProducts} walked from the top: any product
|
|
73
|
+
* already `installed` is filtered out, and the first remaining entry is exactly the one that was
|
|
74
|
+
* mid-flight or failed (re-attached, never re-offered the hero/picker).
|
|
75
|
+
*/
|
|
76
|
+
export function hasResumableInstall(detection) {
|
|
77
|
+
return remainingProducts(detection).some((p) => {
|
|
78
|
+
const state = detectionFor(detection, p).state;
|
|
79
|
+
return state === "install_in_progress" || state === "install_failed";
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* The queue to resume with (ob-AC-16), honoring the operator's original subset when it is known.
|
|
84
|
+
* `remainingProducts` alone would reinstall a product the operator explicitly DESELECTED in the
|
|
85
|
+
* Advanced picker, because the daemon has no server-side memory of that subset. When a persisted
|
|
86
|
+
* selection exists (see onboarding-selection-store), the resume queue is the remaining products
|
|
87
|
+
* intersected with it, so a deselected product is never silently reinstalled. When no selection is
|
|
88
|
+
* persisted (a fresh browser, cleared storage), any product that is genuinely mid-flight or failed
|
|
89
|
+
* is still resumed (an in-flight fact, not an assumption); a merely `not_installed` product with no
|
|
90
|
+
* persisted intent is left out, so the flow never installs something the operator did not choose.
|
|
91
|
+
*/
|
|
92
|
+
export function buildResumeQueue(detection, persistedSelection) {
|
|
93
|
+
const remaining = remainingProducts(detection);
|
|
94
|
+
if (persistedSelection !== null) {
|
|
95
|
+
return remaining.filter((p) => persistedSelection.includes(p));
|
|
96
|
+
}
|
|
97
|
+
return remaining.filter((p) => {
|
|
98
|
+
const state = detectionFor(detection, p).state;
|
|
99
|
+
return state === "install_in_progress" || state === "install_failed";
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
103
|
+
// POST /api/onboarding/install
|
|
104
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
105
|
+
export const InstallStartResponseSchema = z.object({
|
|
106
|
+
product: z.enum(FIXED_PRODUCT_ORDER).catch("doctor"),
|
|
107
|
+
state: z.enum(["install_in_progress", "installed"]).catch("install_in_progress"),
|
|
108
|
+
});
|
|
109
|
+
/** `POST /api/onboarding/install` -> 409 refusal (is-AC-5). */
|
|
110
|
+
export const InstallRefusalResponseSchema = z.object({
|
|
111
|
+
error: z.enum(["unpublished", "manifest_unresolved"]),
|
|
112
|
+
});
|
|
113
|
+
/** Honest operator copy for installer refusals (409), with the same retry affordance as other failures. */
|
|
114
|
+
export function installRefusalMessage(error) {
|
|
115
|
+
if (error === "unpublished") {
|
|
116
|
+
return "This product is not published to npm yet, so the installer will not pull it. Retry after the release train publishes the package.";
|
|
117
|
+
}
|
|
118
|
+
return "The fleet version manifest could not be resolved, so the installer does not know which package version to install. Retry after the manifest is available.";
|
|
119
|
+
}
|
|
120
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
121
|
+
// GET /api/onboarding/install/:product/events (SSE)
|
|
122
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
123
|
+
/** The four REAL, observable install stages plus the two terminal outcomes (ob-AC-9: never a percent). */
|
|
124
|
+
export const INSTALL_STAGES = ["resolving", "downloading", "linking", "registering_service", "completed", "failed"];
|
|
125
|
+
/** The four IN-FLIGHT stages, in display order (excludes the two terminal outcomes). */
|
|
126
|
+
export const IN_FLIGHT_STAGES = ["resolving", "downloading", "linking", "registering_service"];
|
|
127
|
+
export const InstallProgressEventSchema = z.object({
|
|
128
|
+
stage: z.enum(INSTALL_STAGES).catch("resolving"),
|
|
129
|
+
detail: z.string().optional(),
|
|
130
|
+
});
|
|
131
|
+
/** The honest "daemon unreachable" default a failed/malformed health read degrades to. */
|
|
132
|
+
export const UNREACHABLE_HEALTH = Object.freeze({
|
|
133
|
+
ready: false,
|
|
134
|
+
status: { supervisor: "unreachable", daemons: [] },
|
|
135
|
+
});
|
|
136
|
+
/**
|
|
137
|
+
* Parse a `GET /api/onboarding/health` body leniently (mirrors `buzzing-screen.tsx`'s established
|
|
138
|
+
* `as FleetStatusResponse` cast for this exact daemon-owned union, {@link FleetStatusResponse} is
|
|
139
|
+
* a discriminated union keyed on `supervisor`, not a flat shape zod's object schemas model cleanly,
|
|
140
|
+
* and the existing precedent already trusts the daemon's own `isFleetReady`-validated projection).
|
|
141
|
+
*/
|
|
142
|
+
export function parseHealthResponse(body) {
|
|
143
|
+
if (typeof body !== "object" || body === null)
|
|
144
|
+
return UNREACHABLE_HEALTH;
|
|
145
|
+
const record = body;
|
|
146
|
+
const ready = typeof record.ready === "boolean" ? record.ready : false;
|
|
147
|
+
const status = isFleetStatusShaped(record.status) ? record.status : UNREACHABLE_HEALTH.status;
|
|
148
|
+
return { ready, status };
|
|
149
|
+
}
|
|
150
|
+
function isFleetStatusShaped(value) {
|
|
151
|
+
if (typeof value !== "object" || value === null)
|
|
152
|
+
return false;
|
|
153
|
+
const supervisor = value.supervisor;
|
|
154
|
+
return supervisor === "reachable" || supervisor === "unreachable";
|
|
155
|
+
}
|
|
156
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
157
|
+
// POST /api/onboarding/event, the UI funnel chokepoint (fire-and-forget, never awaited by callers)
|
|
158
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
159
|
+
/** The exact UI-fired event names (product install + health events are daemon-side, never sent here). */
|
|
160
|
+
export const ONBOARDING_UI_EVENTS = ["onboarding_started", "mode_selected", "login_shown", "dashboard_reached"];
|
|
161
|
+
//# sourceMappingURL=contracts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contracts.js","sourceRoot":"","sources":["../../../../src/dashboard/web/onboarding/contracts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The GREEN-LIGHT HEALTH step, PRD-009b ob-AC-13. Polls `GET /api/onboarding/health` until the
|
|
3
|
+
* fleet reads ready, rendering one row per reported daemon using the SAME bee-state vocabulary
|
|
4
|
+
* `/buzzing` renders (`service-icons.tsx` / `service-status.ts`), so a health-conscious operator
|
|
5
|
+
* sees a consistent visual language across the whole onboarding-to-dashboard journey.
|
|
6
|
+
*/
|
|
7
|
+
import React from "react";
|
|
8
|
+
import type { OnboardingClient } from "./onboarding-client.js";
|
|
9
|
+
export interface HealthViewProps {
|
|
10
|
+
readonly client: OnboardingClient;
|
|
11
|
+
/** Called once `ready:true` is observed. */
|
|
12
|
+
readonly onReady: () => void;
|
|
13
|
+
/** Overrides the poll interval (ms). Defaults to 1500, mirroring `/buzzing`'s dismissal poll. */
|
|
14
|
+
readonly pollMs?: number;
|
|
15
|
+
}
|
|
16
|
+
/** ob-AC-13, the fleet reads ready only once; this screen stops polling the instant it does. */
|
|
17
|
+
export declare function HealthView({ client, onReady, pollMs }: HealthViewProps): React.JSX.Element;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* The GREEN-LIGHT HEALTH step, PRD-009b ob-AC-13. Polls `GET /api/onboarding/health` until the
|
|
4
|
+
* fleet reads ready, rendering one row per reported daemon using the SAME bee-state vocabulary
|
|
5
|
+
* `/buzzing` renders (`service-icons.tsx` / `service-status.ts`), so a health-conscious operator
|
|
6
|
+
* sees a consistent visual language across the whole onboarding-to-dashboard journey.
|
|
7
|
+
*/
|
|
8
|
+
import React from "react";
|
|
9
|
+
import { deriveServiceState, fromFleetDaemonStatus } from "../../../shared/service-status.js";
|
|
10
|
+
import { SERVICE_STATE_COLOR, SERVICE_STATE_LABEL, ServiceStateIcon } from "../service-icons.js";
|
|
11
|
+
/** ob-AC-13, the fleet reads ready only once; this screen stops polling the instant it does. */
|
|
12
|
+
export function HealthView({ client, onReady, pollMs = 1500 }) {
|
|
13
|
+
const [daemons, setDaemons] = React.useState([]);
|
|
14
|
+
const [ready, setReady] = React.useState(false);
|
|
15
|
+
React.useEffect(() => {
|
|
16
|
+
if (ready)
|
|
17
|
+
return;
|
|
18
|
+
let alive = true;
|
|
19
|
+
let inFlight = false;
|
|
20
|
+
const tick = async () => {
|
|
21
|
+
if (inFlight)
|
|
22
|
+
return;
|
|
23
|
+
inFlight = true;
|
|
24
|
+
try {
|
|
25
|
+
const result = await client.health();
|
|
26
|
+
if (!alive)
|
|
27
|
+
return;
|
|
28
|
+
setDaemons(result.status.supervisor === "reachable" ? result.status.daemons : []);
|
|
29
|
+
if (result.ready)
|
|
30
|
+
setReady(true);
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
inFlight = false;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
void tick();
|
|
37
|
+
const id = setInterval(() => void tick(), pollMs);
|
|
38
|
+
return () => {
|
|
39
|
+
alive = false;
|
|
40
|
+
clearInterval(id);
|
|
41
|
+
};
|
|
42
|
+
}, [ready, pollMs, client]);
|
|
43
|
+
React.useEffect(() => {
|
|
44
|
+
if (ready)
|
|
45
|
+
onReady();
|
|
46
|
+
}, [ready, onReady]);
|
|
47
|
+
return (_jsx("div", { "data-testid": "onboarding-health-view", style: {
|
|
48
|
+
display: "flex",
|
|
49
|
+
alignItems: "center",
|
|
50
|
+
justifyContent: "center",
|
|
51
|
+
minHeight: "100vh",
|
|
52
|
+
padding: 32,
|
|
53
|
+
background: "var(--bg-canvas)",
|
|
54
|
+
textAlign: "center",
|
|
55
|
+
}, children: _jsxs("div", { style: {
|
|
56
|
+
display: "flex",
|
|
57
|
+
flexDirection: "column",
|
|
58
|
+
alignItems: "center",
|
|
59
|
+
gap: 22,
|
|
60
|
+
width: "100%",
|
|
61
|
+
maxWidth: 480,
|
|
62
|
+
padding: "40px 36px",
|
|
63
|
+
background: "var(--bg-surface)",
|
|
64
|
+
border: "1px solid var(--border-default)",
|
|
65
|
+
borderRadius: "var(--radius-xl)",
|
|
66
|
+
}, children: [_jsx("h1", { style: { fontSize: "var(--text-2xl)", fontWeight: 700, color: "var(--text-primary)", margin: 0, letterSpacing: "-0.02em" }, children: "Bringing the fleet up green" }), _jsx("p", { style: { fontFamily: "var(--font-sans)", fontSize: "var(--text-sm)", color: "var(--text-secondary)", margin: 0, lineHeight: 1.5 }, children: "One last check before your dashboard: every daemon needs to report healthy." }), _jsx("ul", { role: "status", "aria-live": "polite", style: { listStyle: "none", margin: 0, padding: 0, display: "flex", flexDirection: "column", gap: 8, width: "100%", textAlign: "left" }, children: daemons.length === 0 ? (_jsx("li", { style: { fontFamily: "var(--font-mono)", fontSize: "var(--text-xs)", color: "var(--text-tertiary)" }, children: "Checking the fleet\u2026" })) : (daemons.map((daemon) => {
|
|
67
|
+
const state = deriveServiceState({ signal: fromFleetDaemonStatus(daemon), now: Date.now(), firstActiveAt: null });
|
|
68
|
+
return (_jsxs("li", { "data-testid": `onboarding-health-row-${daemon.name}`, "data-state": state, style: {
|
|
69
|
+
display: "flex",
|
|
70
|
+
alignItems: "center",
|
|
71
|
+
gap: 12,
|
|
72
|
+
padding: "10px 14px",
|
|
73
|
+
borderRadius: "var(--radius-md)",
|
|
74
|
+
border: "1px solid var(--border-subtle)",
|
|
75
|
+
background: "var(--bg-elevated)",
|
|
76
|
+
}, children: [_jsx("span", { style: { color: SERVICE_STATE_COLOR[state], display: "inline-flex", flex: "none" }, children: _jsx(ServiceStateIcon, { state: state }) }), _jsx("span", { style: { flex: 1, fontFamily: "var(--font-sans)", fontSize: "var(--text-sm)", color: "var(--text-primary)" }, children: daemon.name }), _jsx("span", { style: { fontFamily: "var(--font-mono)", fontSize: "var(--text-xs)", color: "var(--text-secondary)" }, children: SERVICE_STATE_LABEL[state] })] }, daemon.name));
|
|
77
|
+
})) })] }) }));
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=health-view.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health-view.js","sourceRoot":"","sources":["../../../../src/dashboard/web/onboarding/health-view.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The per-product GUIDED INSTALL CARD, PRD-009b ob-AC-8/ob-AC-9/ob-AC-10/ob-AC-11/ob-AC-12. A
|
|
3
|
+
* full-screen card carrying the product's logo, title, benefit copy, staged (never percent)
|
|
4
|
+
* progress, and the npm-safety reassurance. Honors a brief minimum dwell on success (ob-AC-11,
|
|
5
|
+
* revised: the real install time drives the pace, the dwell only prevents a flash-through) and
|
|
6
|
+
* NEVER masks a failure behind that dwell (ob-AC-12), a `failed` stage renders its truthful error
|
|
7
|
+
* plus a retry affordance the instant it arrives, regardless of elapsed dwell time.
|
|
8
|
+
*
|
|
9
|
+
* Re-entry (ob-AC-16/ob-AC-17): `initialDetection` (the daemon's own truth from `/api/onboarding/
|
|
10
|
+
* detect`) decides the card's opening move, `install_in_progress` RE-ATTACHES the SSE stream
|
|
11
|
+
* without a new install POST; `install_failed` renders the failure immediately and waits for an
|
|
12
|
+
* explicit retry; anything else starts a fresh install.
|
|
13
|
+
*/
|
|
14
|
+
import React from "react";
|
|
15
|
+
import { type InstallableProduct, type ProductDetection } from "./contracts.js";
|
|
16
|
+
import type { OnboardingClient } from "./onboarding-client.js";
|
|
17
|
+
export interface InstallCardProps {
|
|
18
|
+
readonly product: InstallableProduct;
|
|
19
|
+
/** The daemon's detection truth for THIS product at the moment the card mounted (ob-AC-16). */
|
|
20
|
+
readonly initialDetection: ProductDetection;
|
|
21
|
+
readonly client: OnboardingClient;
|
|
22
|
+
readonly assetBase: string;
|
|
23
|
+
/** Called once this product's install has both completed AND satisfied its minimum dwell. */
|
|
24
|
+
readonly onAdvance: () => void;
|
|
25
|
+
/** Overrides the default brief minimum dwell (ob-AC-11, revised). A test injects a short window. */
|
|
26
|
+
readonly minDwellMs?: number;
|
|
27
|
+
}
|
|
28
|
+
export declare function InstallCard({ product, initialDetection, client, assetBase, onAdvance, minDwellMs }: InstallCardProps): React.JSX.Element;
|