@legioncodeinc/hive 0.2.1 → 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.
- package/README.md +2 -2
- 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 +86 -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 +50 -0
- package/dist/daemon/installer/install-state.js +142 -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 +48 -0
- package/dist/daemon/installer/spawn.js +51 -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 +27 -0
- package/dist/dashboard/web/onboarding/install-card.js +170 -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 +161 -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 +22 -0
- package/dist/dashboard/web/onboarding/use-install-dwell.js +37 -0
- package/dist/dashboard/web/onboarding/use-install-dwell.js.map +1 -0
- package/dist/dashboard/web/onboarding/use-onboarding-token.d.ts +9 -0
- package/dist/dashboard/web/onboarding/use-onboarding-token.js +34 -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 +1 -1
|
@@ -46,6 +46,13 @@ export declare const DASHBOARD_LOGO_PATH: "/honeycomb-memory-cluster.svg";
|
|
|
46
46
|
* six known filenames 404s (no attacker-controlled path component).
|
|
47
47
|
*/
|
|
48
48
|
export declare const DASHBOARD_FONT_PATH: "/fonts/:name";
|
|
49
|
+
/**
|
|
50
|
+
* The same-origin path prefix the host serves the product brand marks under (PRD-009a). `:name` is
|
|
51
|
+
* validated by a safe-name regex in `web-assets.ts` `brandAsset()` (leaf `*.svg` only, no traversal),
|
|
52
|
+
* so anything unsafe 404s. `/assets/` is gate-exempt (`gate.ts`) so the onboarding cards load their
|
|
53
|
+
* marks even when the shell has redirected the browser to a gated screen.
|
|
54
|
+
*/
|
|
55
|
+
export declare const DASHBOARD_BRAND_PATH: "/assets/brand/:name";
|
|
49
56
|
/** Options for {@link mountDashboardHost}. */
|
|
50
57
|
export interface MountDashboardHostOptions {
|
|
51
58
|
/**
|
|
@@ -45,6 +45,13 @@ export const DASHBOARD_LOGO_PATH = "/honeycomb-memory-cluster.svg";
|
|
|
45
45
|
* six known filenames 404s (no attacker-controlled path component).
|
|
46
46
|
*/
|
|
47
47
|
export const DASHBOARD_FONT_PATH = "/fonts/:name";
|
|
48
|
+
/**
|
|
49
|
+
* The same-origin path prefix the host serves the product brand marks under (PRD-009a). `:name` is
|
|
50
|
+
* validated by a safe-name regex in `web-assets.ts` `brandAsset()` (leaf `*.svg` only, no traversal),
|
|
51
|
+
* so anything unsafe 404s. `/assets/` is gate-exempt (`gate.ts`) so the onboarding cards load their
|
|
52
|
+
* marks even when the shell has redirected the browser to a gated screen.
|
|
53
|
+
*/
|
|
54
|
+
export const DASHBOARD_BRAND_PATH = "/assets/brand/:name";
|
|
48
55
|
/**
|
|
49
56
|
* The asset base the app resolves host-served assets under. hive serves the mark at the ROOT
|
|
50
57
|
* (`/honeycomb-memory-cluster.svg`), so the base is empty — the app's `${assetBase}/…svg`
|
|
@@ -139,6 +146,15 @@ export function mountDashboardAssets(app, options = {}) {
|
|
|
139
146
|
"cache-control": "public, max-age=31536000, immutable",
|
|
140
147
|
});
|
|
141
148
|
});
|
|
149
|
+
// GET /assets/brand/<file>.svg — the product brand marks (PRD-009a). `:name` is validated by a
|
|
150
|
+
// safe-name regex in `brandAsset()` (leaf `*.svg` only, no `..`, no separators); an unsafe or
|
|
151
|
+
// missing name 404s. The marks carry no secret; a modest revalidating cache is plenty over loopback.
|
|
152
|
+
app.get(DASHBOARD_BRAND_PATH, (c) => {
|
|
153
|
+
const asset = assets.brandAsset(c.req.param("name"));
|
|
154
|
+
if (asset === null)
|
|
155
|
+
return c.text("brand asset not found", 404);
|
|
156
|
+
return c.body(asset.body, 200, { "content-type": asset.contentType, "cache-control": "public, max-age=3600" });
|
|
157
|
+
});
|
|
142
158
|
}
|
|
143
159
|
/**
|
|
144
160
|
* Attach the SPA shell CATCH-ALL onto hive's Hono app (PRD-003a g-AC-1 / g-AC-2). Register this
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host.js","sourceRoot":"","sources":["../../../src/daemon/dashboard/host.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAGH,OAAO,EAAE,eAAe,EAAkB,MAAM,iBAAiB,CAAC;AAElE,kGAAkG;AAClG,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAY,CAAC;AAEhD,kEAAkE;AAClE,MAAM,CAAC,MAAM,kBAAkB,GAAG,SAAkB,CAAC;AAErD,kFAAkF;AAClF,MAAM,CAAC,MAAM,kBAAkB,GAAG,aAAsB,CAAC;AAEzD,8DAA8D;AAC9D,MAAM,CAAC,MAAM,mBAAmB,GAAG,+BAAwC,CAAC;AAE5E;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,cAAuB,CAAC;AAE3D;;;;GAIG;AACH,MAAM,UAAU,GAAG,EAAW,CAAC;AAY/B;;;;GAIG;AACH,MAAM,UAAU,GAAG;IAClB,sEAAsE;IACtE,uEAAuE;IACvE,yEAAyE;IACzE,sEAAsE;IACtE,8EAA8E;IAC9E,kFAAkF;IAClF,4BAA4B;IAC5B,kDAAkD;IAClD,yEAAyE;IACzE,sFAAsF;IACtF,GAAG;IACH,4DAA4D;CAC5D,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb;;;;;GAKG;AACH,MAAM,UAAU,WAAW;IAC1B,OAAO;QACN,iBAAiB;QACjB,kBAAkB;QAClB,QAAQ;QACR,wBAAwB;QACxB,sEAAsE;QACtE,iCAAiC;QACjC,gCAAgC,kBAAkB,IAAI;QACtD,0BAA0B,mBAAmB,IAAI;QACjD,UAAU,UAAU,UAAU;QAC9B,SAAS;QACT,QAAQ;QACR,mCAAmC,UAAU,UAAU;QACvD,8BAA8B,kBAAkB,aAAa;QAC7D,SAAS;QACT,SAAS;KACT,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACd,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAS,EAAE,UAAqC,EAAE;IACtF,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,eAAe,EAAE,CAAC;IAEnD,2EAA2E;IAC3E,GAAG,CAAC,GAAG,CAAC,kBAAkB,EAAE,CAAC,CAAC,EAAE,EAAE;QACjC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC;QAC7B,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,CAAC,CAAC,IAAI,CAAC,4BAA4B,EAAE,GAAG,CAAC,CAAC;QACrE,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,cAAc,EAAE,KAAK,CAAC,WAAW,EAAE,eAAe,EAAE,UAAU,EAAE,CAAC,CAAC;IACpG,CAAC,CAAC,CAAC;IAEH,wDAAwD;IACxD,GAAG,CAAC,GAAG,CAAC,kBAAkB,EAAE,CAAC,CAAC,EAAE,EAAE;QACjC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,EAAE,CAAC;QAC3B,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,CAAC,CAAC,IAAI,CAAC,8BAA8B,EAAE,GAAG,CAAC,CAAC;QACvE,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,cAAc,EAAE,KAAK,CAAC,WAAW,EAAE,eAAe,EAAE,UAAU,EAAE,CAAC,CAAC;IACpG,CAAC,CAAC,CAAC;IAEH,2FAA2F;IAC3F,6FAA6F;IAC7F,GAAG,CAAC,GAAG,CAAC,mBAAmB,EAAE,CAAC,CAAC,EAAE,EAAE;QAClC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,CAAC,CAAC,IAAI,CAAC,4BAA4B,EAAE,GAAG,CAAC,CAAC;QACrE,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,cAAc,EAAE,KAAK,CAAC,WAAW,EAAE,eAAe,EAAE,UAAU,EAAE,CAAC,CAAC;IACpG,CAAC,CAAC,CAAC;IAEH,4FAA4F;IAC5F,6FAA6F;IAC7F,+FAA+F;IAC/F,GAAG,CAAC,GAAG,CAAC,mBAAmB,EAAE,CAAC,CAAC,EAAE,EAAE;QAClC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;QAC/C,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,CAAC,CAAC,IAAI,CAAC,0BAA0B,EAAE,GAAG,CAAC,CAAC;QACnE,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE;YAC9B,cAAc,EAAE,KAAK,CAAC,WAAW;YACjC,eAAe,EAAE,qCAAqC;SACtD,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,2BAA2B,CAAC,GAAS;IACpD,iGAAiG;IACjG,6FAA6F;IAC7F,6FAA6F;IAC7F,+FAA+F;IAC/F,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE;QAClB,CAAC,CAAC,MAAM,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;QACtC,OAAO,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAS,EAAE,UAAqC,EAAE;IACpF,oBAAoB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACnC,2BAA2B,CAAC,GAAG,CAAC,CAAC;AAClC,CAAC"}
|
|
1
|
+
{"version":3,"file":"host.js","sourceRoot":"","sources":["../../../src/daemon/dashboard/host.ts"],"names":[],"mappings":"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"}
|
|
@@ -77,6 +77,8 @@ export interface WebAssets {
|
|
|
77
77
|
appJs(): ServedAsset | null;
|
|
78
78
|
/** A brand font's bytes + content type for an allow-listed `name`, or `null` (→ 404) otherwise. */
|
|
79
79
|
font(name: string): ServedBinaryAsset | null;
|
|
80
|
+
/** A brand SVG for a safe leaf `*.svg` `name` under `assets/brand/`, or `null` (→ 404) otherwise. */
|
|
81
|
+
brandAsset(name: string): ServedAsset | null;
|
|
80
82
|
}
|
|
81
83
|
/**
|
|
82
84
|
* Build the host-side asset reader. Resolves the assets + bundle dirs once and reads on
|
|
@@ -55,6 +55,15 @@ const FONT_FILES = [
|
|
|
55
55
|
const FONT_ALLOW = new Set(FONT_FILES);
|
|
56
56
|
/** The same-origin path hive serves the fonts under (origin-rooted; see {@link rewriteFontUrls}). */
|
|
57
57
|
const FONT_ROUTE_PREFIX = "/fonts/";
|
|
58
|
+
/**
|
|
59
|
+
* The on-disk dir (under `assets/`) the product brand marks live in, and the safe-name pattern the
|
|
60
|
+
* host route enforces (PRD-009a). Only a leaf `*.svg` filename drawn from a conservative charset is
|
|
61
|
+
* served; anything with a path separator, a `..`, or a non-`.svg` extension is rejected (404). This
|
|
62
|
+
* is the "allowlist .svg filenames, no path traversal" contract the onboarding UI's product cards
|
|
63
|
+
* load their marks through (`/assets/brand/doctor-mark.svg`, etc.).
|
|
64
|
+
*/
|
|
65
|
+
const BRAND_DIR = "brand";
|
|
66
|
+
const BRAND_SVG_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]*\.svg$/;
|
|
58
67
|
/** Map a font filename to its `content-type` by extension (`.woff2` → `font/woff2`, `.ttf` → `font/ttf`). */
|
|
59
68
|
function fontContentType(name) {
|
|
60
69
|
return name.endsWith(".woff2") ? "font/woff2" : "font/ttf";
|
|
@@ -190,6 +199,16 @@ export function createWebAssets(options = {}) {
|
|
|
190
199
|
const js = readSoft(join(bundleDir, DASHBOARD_APP_BUNDLE));
|
|
191
200
|
return js === null ? null : { body: js, contentType: "text/javascript; charset=utf-8" };
|
|
192
201
|
},
|
|
202
|
+
brandAsset(name) {
|
|
203
|
+
if (assetsDir === null)
|
|
204
|
+
return null;
|
|
205
|
+
// Safe leaf filename ONLY: no separators, no `..`, `.svg` extension. No attacker-controlled
|
|
206
|
+
// path component reaches `join` — a rejected name never touches the filesystem.
|
|
207
|
+
if (name.includes("..") || !BRAND_SVG_NAME_RE.test(name))
|
|
208
|
+
return null;
|
|
209
|
+
const svg = readSoft(join(assetsDir, BRAND_DIR, name));
|
|
210
|
+
return svg === null ? null : { body: svg, contentType: "image/svg+xml" };
|
|
211
|
+
},
|
|
193
212
|
};
|
|
194
213
|
}
|
|
195
214
|
//# sourceMappingURL=web-assets.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web-assets.js","sourceRoot":"","sources":["../../../src/daemon/dashboard/web-assets.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"web-assets.js","sourceRoot":"","sources":["../../../src/daemon/dashboard/web-assets.ts"],"names":[],"mappings":"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"}
|
package/dist/daemon/gate.d.ts
CHANGED
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
import type { MiddlewareHandler } from "hono";
|
|
34
34
|
import { type FetchImpl as FleetFetchImpl } from "./fleet-status.js";
|
|
35
35
|
import { type SetupAuthFetchImpl } from "./setup-auth.js";
|
|
36
|
-
/** The
|
|
37
|
-
export declare const GATE_EXEMPT_ROUTES: readonly ["/buzzing", "/login"];
|
|
36
|
+
/** The gate-exempt SCREENS (g-AC-7 / g-AC-8): always served directly, never redirected. */
|
|
37
|
+
export declare const GATE_EXEMPT_ROUTES: readonly ["/buzzing", "/login", "/onboarding"];
|
|
38
38
|
export interface CreatePortalGateOptions {
|
|
39
39
|
/** The fetch used for the health check (defaults to the global `fetch`; a test injects a fake). */
|
|
40
40
|
readonly fleetStatusFetch?: FleetFetchImpl;
|
package/dist/daemon/gate.js
CHANGED
|
@@ -38,8 +38,14 @@ import { fetchSetupAuthenticated } from "./setup-auth.js";
|
|
|
38
38
|
const BUZZING_ROUTE = "/buzzing";
|
|
39
39
|
/** The fixed redirect target for a logged-out operator (g-AC-4). A hard-coded literal, never derived. */
|
|
40
40
|
const LOGIN_ROUTE = "/login";
|
|
41
|
-
/**
|
|
42
|
-
|
|
41
|
+
/**
|
|
42
|
+
* The onboarding installer screen (PRD-009). Gate-exempt like `/buzzing` and `/login`: on first run
|
|
43
|
+
* the fleet is by definition unhealthy and the operator unauthenticated, so `/onboarding` must serve
|
|
44
|
+
* its SPA shell directly rather than being redirected by the health-then-auth precedence.
|
|
45
|
+
*/
|
|
46
|
+
const ONBOARDING_ROUTE = "/onboarding";
|
|
47
|
+
/** The gate-exempt SCREENS (g-AC-7 / g-AC-8): always served directly, never redirected. */
|
|
48
|
+
export const GATE_EXEMPT_ROUTES = [BUZZING_ROUTE, LOGIN_ROUTE, ONBOARDING_ROUTE];
|
|
43
49
|
/**
|
|
44
50
|
* hive's OWN liveness route + the bundled SPA asset routes — never a page navigation to gate.
|
|
45
51
|
* `/health` is deliberately NOT in this set (see {@link isInfraPath}): PRD-005b claims that same
|
|
@@ -47,8 +53,13 @@ export const GATE_EXEMPT_ROUTES = [BUZZING_ROUTE, LOGIN_ROUTE];
|
|
|
47
53
|
* rather than a blanket exemption.
|
|
48
54
|
*/
|
|
49
55
|
const GATE_EXEMPT_INFRA_PATHS = new Set(["/app.js", "/styles.css", "/honeycomb-memory-cluster.svg"]);
|
|
50
|
-
/**
|
|
51
|
-
|
|
56
|
+
/**
|
|
57
|
+
* Path PREFIXES that are data-plane / static-asset traffic (the BFF proxy, fonts, and the brand
|
|
58
|
+
* SVG assets under `/assets/`), never a gated page route. `/assets/` is exempt (PRD-009a) so the
|
|
59
|
+
* onboarding product-mark SVGs load even when the shell has redirected the browser to a gated
|
|
60
|
+
* screen, exactly like the other bundled asset routes.
|
|
61
|
+
*/
|
|
62
|
+
const GATE_EXEMPT_INFRA_PREFIXES = ["/api/", "/setup/", "/fonts/", "/assets/"];
|
|
52
63
|
/**
|
|
53
64
|
* hive's own machine-liveness path. Ambiguous on purpose (PRD-005b): a health-probe/monitoring
|
|
54
65
|
* caller (doctor's own `/health` probe, an ops tool) never sends `Accept: text/html`, while a
|
package/dist/daemon/gate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gate.js","sourceRoot":"","sources":["../../src/daemon/gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAIH,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAoC,MAAM,mBAAmB,CAAC;AACvF,OAAO,EAAE,uBAAuB,EAA2B,MAAM,iBAAiB,CAAC;AAEnF,sGAAsG;AACtG,MAAM,aAAa,GAAG,UAAmB,CAAC;AAE1C,yGAAyG;AACzG,MAAM,WAAW,GAAG,QAAiB,CAAC;AAEtC,
|
|
1
|
+
{"version":3,"file":"gate.js","sourceRoot":"","sources":["../../src/daemon/gate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PRD-009a: argv-safe resolution of the JS entry points the installer spawns (is-AC-6).
|
|
3
|
+
*
|
|
4
|
+
* npm and each product ship a `.cmd`/shell shim on the global `bin` path that cannot be spawned
|
|
5
|
+
* with `shell:false` on Windows. Instead of ever touching those shims, we resolve the underlying
|
|
6
|
+
* `*.js` entry from the package's own `package.json#bin` field and spawn it as
|
|
7
|
+
* `process.execPath [entry.js, ...args]`. No `.cmd`, no shell, anywhere.
|
|
8
|
+
*
|
|
9
|
+
* Layout facts:
|
|
10
|
+
* - global node_modules: `<prefix>/lib/node_modules` (POSIX) or `<prefix>/node_modules` (Windows)
|
|
11
|
+
* - npm's own entry: `npm/bin/npm-cli.js` inside global node_modules (or a require.resolve fallback)
|
|
12
|
+
* - node ships npm next to the executable, so npm-cli.js is locatable relative to `process.execPath`
|
|
13
|
+
* even before we know the prefix (used to run `npm prefix -g` itself)
|
|
14
|
+
*/
|
|
15
|
+
import type { InstallerConfig } from "./config.js";
|
|
16
|
+
/** The global node_modules directory for a resolved npm prefix, per platform. */
|
|
17
|
+
export declare function globalNodeModulesDir(prefix: string, platform: NodeJS.Platform): string;
|
|
18
|
+
/**
|
|
19
|
+
* Resolve the absolute `*.js` entry for `binName` inside `<nodeModulesDir>/<packageName>`, reading
|
|
20
|
+
* the package's `package.json#bin`. Returns `null` when the package, its manifest, or the bin entry
|
|
21
|
+
* cannot be resolved (the caller treats that as a registration failure, is-AC-13).
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolvePackageBinJs(config: InstallerConfig, nodeModulesDir: string, packageName: string, binName: string): string | null;
|
|
24
|
+
/**
|
|
25
|
+
* Locate `npm/bin/npm-cli.js` relative to `process.execPath` (node ships npm beside its binary),
|
|
26
|
+
* so we can run `npm prefix -g` before the prefix is known. Falls back to `require.resolve`.
|
|
27
|
+
*/
|
|
28
|
+
export declare function locateNpmCliJs(config: InstallerConfig): string | null;
|
|
29
|
+
/**
|
|
30
|
+
* The default `npm prefix -g` resolver: locate npm-cli.js relative to the node binary and spawn it
|
|
31
|
+
* argv-safe, returning the trimmed prefix. Returns `null` when npm-cli.js cannot be located or the
|
|
32
|
+
* command fails. The installer service memoizes this so it runs at most once per daemon session.
|
|
33
|
+
*/
|
|
34
|
+
export declare function resolveNpmPrefixViaCli(config: InstallerConfig): Promise<string | null>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PRD-009a: argv-safe resolution of the JS entry points the installer spawns (is-AC-6).
|
|
3
|
+
*
|
|
4
|
+
* npm and each product ship a `.cmd`/shell shim on the global `bin` path that cannot be spawned
|
|
5
|
+
* with `shell:false` on Windows. Instead of ever touching those shims, we resolve the underlying
|
|
6
|
+
* `*.js` entry from the package's own `package.json#bin` field and spawn it as
|
|
7
|
+
* `process.execPath [entry.js, ...args]`. No `.cmd`, no shell, anywhere.
|
|
8
|
+
*
|
|
9
|
+
* Layout facts:
|
|
10
|
+
* - global node_modules: `<prefix>/lib/node_modules` (POSIX) or `<prefix>/node_modules` (Windows)
|
|
11
|
+
* - npm's own entry: `npm/bin/npm-cli.js` inside global node_modules (or a require.resolve fallback)
|
|
12
|
+
* - node ships npm next to the executable, so npm-cli.js is locatable relative to `process.execPath`
|
|
13
|
+
* even before we know the prefix (used to run `npm prefix -g` itself)
|
|
14
|
+
*/
|
|
15
|
+
import { dirname, join } from "node:path";
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
/** The global node_modules directory for a resolved npm prefix, per platform. */
|
|
18
|
+
export function globalNodeModulesDir(prefix, platform) {
|
|
19
|
+
return platform === "win32" ? join(prefix, "node_modules") : join(prefix, "lib", "node_modules");
|
|
20
|
+
}
|
|
21
|
+
/** npm's `bin` field can be a bare string or a `{ name: relPath }` map. */
|
|
22
|
+
const BinFieldSchema = z.union([z.string(), z.record(z.string(), z.string())]);
|
|
23
|
+
const PackageJsonBinSchema = z.object({ bin: BinFieldSchema.optional() });
|
|
24
|
+
/**
|
|
25
|
+
* Resolve the absolute `*.js` entry for `binName` inside `<nodeModulesDir>/<packageName>`, reading
|
|
26
|
+
* the package's `package.json#bin`. Returns `null` when the package, its manifest, or the bin entry
|
|
27
|
+
* cannot be resolved (the caller treats that as a registration failure, is-AC-13).
|
|
28
|
+
*/
|
|
29
|
+
export function resolvePackageBinJs(config, nodeModulesDir, packageName, binName) {
|
|
30
|
+
const packageDir = join(nodeModulesDir, packageName);
|
|
31
|
+
const raw = config.readTextFile(join(packageDir, "package.json"));
|
|
32
|
+
if (raw === null)
|
|
33
|
+
return null;
|
|
34
|
+
let parsedJson;
|
|
35
|
+
try {
|
|
36
|
+
parsedJson = JSON.parse(raw);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const parsed = PackageJsonBinSchema.safeParse(parsedJson);
|
|
42
|
+
if (!parsed.success || parsed.data.bin === undefined)
|
|
43
|
+
return null;
|
|
44
|
+
const bin = parsed.data.bin;
|
|
45
|
+
const relEntry = typeof bin === "string" ? bin : bin[binName];
|
|
46
|
+
if (relEntry === undefined || relEntry.length === 0)
|
|
47
|
+
return null;
|
|
48
|
+
const entry = join(packageDir, relEntry);
|
|
49
|
+
return config.fileExists(entry) ? entry : null;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Locate `npm/bin/npm-cli.js` relative to `process.execPath` (node ships npm beside its binary),
|
|
53
|
+
* so we can run `npm prefix -g` before the prefix is known. Falls back to `require.resolve`.
|
|
54
|
+
*/
|
|
55
|
+
export function locateNpmCliJs(config) {
|
|
56
|
+
const execDir = dirname(config.execPath);
|
|
57
|
+
const candidates = config.platform === "win32"
|
|
58
|
+
? [join(execDir, "node_modules", "npm", "bin", "npm-cli.js")]
|
|
59
|
+
: [join(dirname(execDir), "lib", "node_modules", "npm", "bin", "npm-cli.js")];
|
|
60
|
+
for (const candidate of candidates) {
|
|
61
|
+
if (config.fileExists(candidate))
|
|
62
|
+
return candidate;
|
|
63
|
+
}
|
|
64
|
+
return config.requireResolve("npm/bin/npm-cli.js");
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The default `npm prefix -g` resolver: locate npm-cli.js relative to the node binary and spawn it
|
|
68
|
+
* argv-safe, returning the trimmed prefix. Returns `null` when npm-cli.js cannot be located or the
|
|
69
|
+
* command fails. The installer service memoizes this so it runs at most once per daemon session.
|
|
70
|
+
*/
|
|
71
|
+
export async function resolveNpmPrefixViaCli(config) {
|
|
72
|
+
const npmCli = locateNpmCliJs(config);
|
|
73
|
+
if (npmCli === null)
|
|
74
|
+
return null;
|
|
75
|
+
try {
|
|
76
|
+
const outcome = await config.spawn(config.execPath, [npmCli, "prefix", "-g"]);
|
|
77
|
+
if (outcome.code !== 0)
|
|
78
|
+
return null;
|
|
79
|
+
const prefix = outcome.stdoutTail.trim();
|
|
80
|
+
return prefix.length > 0 ? prefix : null;
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=bin-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin-resolver.js","sourceRoot":"","sources":["../../../src/daemon/installer/bin-resolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PRD-009a: the installer service's injectable configuration (its test seams).
|
|
3
|
+
*
|
|
4
|
+
* Every side effect the installer performs, the manifest fetch, filesystem reads, the token file,
|
|
5
|
+
* child-process spawns, npm-prefix resolution, and the clock, is reached through this object, so a
|
|
6
|
+
* test never hits the network, real npm, or the real filesystem (mirroring how `fleet-status.ts`
|
|
7
|
+
* takes a `FetchImpl`). Production defaults are assembled by {@link createInstallerConfig}.
|
|
8
|
+
*/
|
|
9
|
+
import { type SpawnFn } from "./spawn.js";
|
|
10
|
+
/** The primary fleet manifest URL served by the install site (MV-2). */
|
|
11
|
+
export declare const MANIFEST_URL: "https://get.theapiary.sh/hive-release.json";
|
|
12
|
+
/**
|
|
13
|
+
* Second-chance fallback when {@link MANIFEST_URL} is unreachable. The private GitHub raw URL may
|
|
14
|
+
* 404 without auth; it is tried only after the primary fetch fails, before the bundled snapshot.
|
|
15
|
+
*/
|
|
16
|
+
export declare const MANIFEST_FALLBACK_URL: "https://raw.githubusercontent.com/legioncodeinc/the-apiary/main/hive-release.json";
|
|
17
|
+
/** The bootstrap-minted one-time onboarding token file (mode 0600), read lazily per request (is-AC-9). */
|
|
18
|
+
export declare const ONBOARDING_TOKEN_PATH: string;
|
|
19
|
+
/** The bounded timeout for the single network manifest fetch before falling back to the snapshot. */
|
|
20
|
+
export declare const MANIFEST_TIMEOUT_MS: 5000;
|
|
21
|
+
/** The minimal fetch surface the manifest resolver needs (a mock in tests). */
|
|
22
|
+
export type ManifestFetch = (input: string, init?: {
|
|
23
|
+
readonly signal?: AbortSignal;
|
|
24
|
+
}) => Promise<Response>;
|
|
25
|
+
/** The full seam set. Every field is overridable; {@link createInstallerConfig} fills the rest. */
|
|
26
|
+
export interface InstallerConfig {
|
|
27
|
+
/** The primary raw manifest URL fetched once per session (is-AC-4, MV-2). */
|
|
28
|
+
readonly manifestUrl: string;
|
|
29
|
+
/** Fallback manifest URL when the primary fetch fails (MV-2). */
|
|
30
|
+
readonly manifestFallbackUrl: string;
|
|
31
|
+
/** The manifest fetch implementation (defaults to the global `fetch`). */
|
|
32
|
+
readonly manifestFetch: ManifestFetch;
|
|
33
|
+
/** The bounded manifest-fetch timeout in ms. */
|
|
34
|
+
readonly manifestTimeoutMs: number;
|
|
35
|
+
/** The onboarding token file path (read lazily, deleted on completion). */
|
|
36
|
+
readonly tokenPath: string;
|
|
37
|
+
/** Existence check (a fake map in tests). */
|
|
38
|
+
readonly fileExists: (path: string) => boolean;
|
|
39
|
+
/** UTF-8 read returning `null` on any error (a fake map in tests). */
|
|
40
|
+
readonly readTextFile: (path: string) => string | null;
|
|
41
|
+
/** Best-effort delete used to invalidate the token (never throws). */
|
|
42
|
+
readonly deleteFile: (path: string) => void;
|
|
43
|
+
/** Resolve npm's global prefix (`npm prefix -g`), cached by the service. */
|
|
44
|
+
readonly resolveNpmPrefix: () => Promise<string | null>;
|
|
45
|
+
/** The OS platform, deciding global node_modules + bin path shape. */
|
|
46
|
+
readonly platform: NodeJS.Platform;
|
|
47
|
+
/** The argv-safe spawn seam (is-AC-6). */
|
|
48
|
+
readonly spawn: SpawnFn;
|
|
49
|
+
/** A `require.resolve`-style fallback for locating `npm/bin/npm-cli.js`; `null` when unresolvable. */
|
|
50
|
+
readonly requireResolve: (specifier: string) => string | null;
|
|
51
|
+
/** The clock (injected in tests). */
|
|
52
|
+
readonly now: () => number;
|
|
53
|
+
/** hive's own running version, the authoritative answer for hive detection. */
|
|
54
|
+
readonly hiveVersion: string;
|
|
55
|
+
/** The node executable used as the argv[0] for every spawn (`process.execPath`). */
|
|
56
|
+
readonly execPath: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Assemble the production {@link InstallerConfig}. `resolveNpmPrefix` is intentionally left to the
|
|
60
|
+
* service (it needs the other seams and memoizes the result), so it is injected by the service
|
|
61
|
+
* wiring rather than defaulted here; a test provides its own.
|
|
62
|
+
*/
|
|
63
|
+
export declare function createInstallerConfig(overrides?: Partial<InstallerConfig>): InstallerConfig;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PRD-009a: the installer service's injectable configuration (its test seams).
|
|
3
|
+
*
|
|
4
|
+
* Every side effect the installer performs, the manifest fetch, filesystem reads, the token file,
|
|
5
|
+
* child-process spawns, npm-prefix resolution, and the clock, is reached through this object, so a
|
|
6
|
+
* test never hits the network, real npm, or the real filesystem (mirroring how `fleet-status.ts`
|
|
7
|
+
* takes a `FetchImpl`). Production defaults are assembled by {@link createInstallerConfig}.
|
|
8
|
+
*/
|
|
9
|
+
import { createRequire } from "node:module";
|
|
10
|
+
import { existsSync, readFileSync, rmSync } from "node:fs";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { platform as osPlatform } from "node:process";
|
|
13
|
+
import { HIVE_VERSION, HONEYCOMB_HOME_DIR } from "../../shared/constants.js";
|
|
14
|
+
import { createNodeSpawn } from "./spawn.js";
|
|
15
|
+
/** The primary fleet manifest URL served by the install site (MV-2). */
|
|
16
|
+
export const MANIFEST_URL = "https://get.theapiary.sh/hive-release.json";
|
|
17
|
+
/**
|
|
18
|
+
* Second-chance fallback when {@link MANIFEST_URL} is unreachable. The private GitHub raw URL may
|
|
19
|
+
* 404 without auth; it is tried only after the primary fetch fails, before the bundled snapshot.
|
|
20
|
+
*/
|
|
21
|
+
export const MANIFEST_FALLBACK_URL = "https://raw.githubusercontent.com/legioncodeinc/the-apiary/main/hive-release.json";
|
|
22
|
+
/** The bootstrap-minted one-time onboarding token file (mode 0600), read lazily per request (is-AC-9). */
|
|
23
|
+
export const ONBOARDING_TOKEN_PATH = join(HONEYCOMB_HOME_DIR, "hive", "onboarding-token");
|
|
24
|
+
/** The bounded timeout for the single network manifest fetch before falling back to the snapshot. */
|
|
25
|
+
export const MANIFEST_TIMEOUT_MS = 5000;
|
|
26
|
+
function defaultReadTextFile(path) {
|
|
27
|
+
try {
|
|
28
|
+
return existsSync(path) ? readFileSync(path, "utf8") : null;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function defaultDeleteFile(path) {
|
|
35
|
+
try {
|
|
36
|
+
rmSync(path, { force: true });
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Best effort: a delete failure still leaves the in-memory invalidation flag set (see token.ts).
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function defaultRequireResolve(specifier) {
|
|
43
|
+
try {
|
|
44
|
+
return createRequire(import.meta.url).resolve(specifier);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Assemble the production {@link InstallerConfig}. `resolveNpmPrefix` is intentionally left to the
|
|
52
|
+
* service (it needs the other seams and memoizes the result), so it is injected by the service
|
|
53
|
+
* wiring rather than defaulted here; a test provides its own.
|
|
54
|
+
*/
|
|
55
|
+
export function createInstallerConfig(overrides = {}) {
|
|
56
|
+
return {
|
|
57
|
+
manifestUrl: overrides.manifestUrl ?? MANIFEST_URL,
|
|
58
|
+
manifestFallbackUrl: overrides.manifestFallbackUrl ?? MANIFEST_FALLBACK_URL,
|
|
59
|
+
manifestFetch: overrides.manifestFetch ?? ((input, init) => fetch(input, init)),
|
|
60
|
+
manifestTimeoutMs: overrides.manifestTimeoutMs ?? MANIFEST_TIMEOUT_MS,
|
|
61
|
+
tokenPath: overrides.tokenPath ?? ONBOARDING_TOKEN_PATH,
|
|
62
|
+
fileExists: overrides.fileExists ?? ((path) => existsSync(path)),
|
|
63
|
+
readTextFile: overrides.readTextFile ?? defaultReadTextFile,
|
|
64
|
+
deleteFile: overrides.deleteFile ?? defaultDeleteFile,
|
|
65
|
+
resolveNpmPrefix: overrides.resolveNpmPrefix ?? (async () => null),
|
|
66
|
+
platform: overrides.platform ?? osPlatform,
|
|
67
|
+
spawn: overrides.spawn ?? createNodeSpawn(),
|
|
68
|
+
requireResolve: overrides.requireResolve ?? defaultRequireResolve,
|
|
69
|
+
now: overrides.now ?? Date.now,
|
|
70
|
+
hiveVersion: overrides.hiveVersion ?? HIVE_VERSION,
|
|
71
|
+
execPath: overrides.execPath ?? process.execPath
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/daemon/installer/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PRD-009a: fleet detection from LOCAL evidence, without requiring doctor (is-AC-1/2).
|
|
3
|
+
*
|
|
4
|
+
* fleet-status alone cannot answer "is doctor installed" pre-doctor (it depends on doctor's status
|
|
5
|
+
* page being reachable), so detection derives each product's state from local evidence only:
|
|
6
|
+
* 1. in-memory install state (an in-progress or failed install the daemon is tracking), then
|
|
7
|
+
* 2. the presence + version of the package under the global node_modules directory.
|
|
8
|
+
* hive is always installed (it is the daemon answering this request), reported at its own version.
|
|
9
|
+
* Nothing here is taken from the request.
|
|
10
|
+
*/
|
|
11
|
+
import type { DetectResponse, ProductSlug } from "../../shared/onboarding-types.js";
|
|
12
|
+
import type { InstallerConfig } from "./config.js";
|
|
13
|
+
import type { InstallStateStore } from "./install-state.js";
|
|
14
|
+
/**
|
|
15
|
+
* The installed version of a single product from the global node_modules, or `undefined` when it is
|
|
16
|
+
* not installed. Used by the install endpoint's pinned-version short-circuit (is-AC-15).
|
|
17
|
+
*/
|
|
18
|
+
export declare function installedVersion(config: InstallerConfig, slug: ProductSlug): Promise<string | undefined>;
|
|
19
|
+
/** Produce the full per-product detection map (is-AC-1/2). */
|
|
20
|
+
export declare function detectFleet(config: InstallerConfig, store: InstallStateStore): Promise<DetectResponse>;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PRD-009a: fleet detection from LOCAL evidence, without requiring doctor (is-AC-1/2).
|
|
3
|
+
*
|
|
4
|
+
* fleet-status alone cannot answer "is doctor installed" pre-doctor (it depends on doctor's status
|
|
5
|
+
* page being reachable), so detection derives each product's state from local evidence only:
|
|
6
|
+
* 1. in-memory install state (an in-progress or failed install the daemon is tracking), then
|
|
7
|
+
* 2. the presence + version of the package under the global node_modules directory.
|
|
8
|
+
* hive is always installed (it is the daemon answering this request), reported at its own version.
|
|
9
|
+
* Nothing here is taken from the request.
|
|
10
|
+
*/
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
import { globalNodeModulesDir } from "./bin-resolver.js";
|
|
13
|
+
import { PRODUCT_PACKAGES, PRODUCT_SLUGS } from "./products.js";
|
|
14
|
+
import { isInstallableProduct } from "./products.js";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
const InstalledPackageSchema = z.object({ version: z.string().optional() });
|
|
17
|
+
/** Read the installed version of `packageName` from its global node_modules package.json, if present. */
|
|
18
|
+
function readInstalledVersion(config, nodeModulesDir, packageName) {
|
|
19
|
+
const raw = config.readTextFile(join(nodeModulesDir, packageName, "package.json"));
|
|
20
|
+
if (raw === null)
|
|
21
|
+
return undefined;
|
|
22
|
+
try {
|
|
23
|
+
const parsed = InstalledPackageSchema.safeParse(JSON.parse(raw));
|
|
24
|
+
return parsed.success ? parsed.data.version : undefined;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function detectFromBinEvidence(config, nodeModulesDir, slug) {
|
|
31
|
+
if (nodeModulesDir === null)
|
|
32
|
+
return { state: "not_installed" };
|
|
33
|
+
const version = readInstalledVersion(config, nodeModulesDir, PRODUCT_PACKAGES[slug]);
|
|
34
|
+
return version === undefined ? { state: "not_installed" } : { state: "installed", version };
|
|
35
|
+
}
|
|
36
|
+
function detectProduct(config, store, nodeModulesDir, slug) {
|
|
37
|
+
// hive is the running daemon: authoritative, and never a portal install target.
|
|
38
|
+
if (slug === "hive")
|
|
39
|
+
return { state: "installed", version: config.hiveVersion };
|
|
40
|
+
// An in-flight or failed install the daemon is actively tracking wins over on-disk evidence.
|
|
41
|
+
if (isInstallableProduct(slug)) {
|
|
42
|
+
const snapshot = store.detectState(slug);
|
|
43
|
+
if (snapshot.status === "in_progress")
|
|
44
|
+
return { state: "install_in_progress" };
|
|
45
|
+
if (snapshot.status === "failed") {
|
|
46
|
+
return snapshot.error === undefined
|
|
47
|
+
? { state: "install_failed" }
|
|
48
|
+
: { state: "install_failed", error: snapshot.error };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return detectFromBinEvidence(config, nodeModulesDir, slug);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The installed version of a single product from the global node_modules, or `undefined` when it is
|
|
55
|
+
* not installed. Used by the install endpoint's pinned-version short-circuit (is-AC-15).
|
|
56
|
+
*/
|
|
57
|
+
export async function installedVersion(config, slug) {
|
|
58
|
+
const prefix = await config.resolveNpmPrefix();
|
|
59
|
+
if (prefix === null)
|
|
60
|
+
return undefined;
|
|
61
|
+
return readInstalledVersion(config, globalNodeModulesDir(prefix, config.platform), PRODUCT_PACKAGES[slug]);
|
|
62
|
+
}
|
|
63
|
+
/** Produce the full per-product detection map (is-AC-1/2). */
|
|
64
|
+
export async function detectFleet(config, store) {
|
|
65
|
+
const prefix = await config.resolveNpmPrefix();
|
|
66
|
+
const nodeModulesDir = prefix === null ? null : globalNodeModulesDir(prefix, config.platform);
|
|
67
|
+
const products = {};
|
|
68
|
+
for (const slug of PRODUCT_SLUGS) {
|
|
69
|
+
products[slug] = detectProduct(config, store, nodeModulesDir, slug);
|
|
70
|
+
}
|
|
71
|
+
return { products };
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=detection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detection.js","sourceRoot":"","sources":["../../../src/daemon/installer/detection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PRD-009c: daemon-side onboarding funnel emission.
|
|
3
|
+
*
|
|
4
|
+
* Every funnel byte leaves through {@link emitTelemetry} in `src/telemetry/emit.ts`. UI-originated
|
|
5
|
+
* milestones arrive via the token-gated event route; install and auth milestones are observed from
|
|
6
|
+
* daemon state transitions. Session-scoped dedupe lives in `onboarding-session-ledger.ts`.
|
|
7
|
+
*/
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import type { InstallableProduct, InstallStage } from "../../shared/onboarding-types.js";
|
|
10
|
+
import { type EmitDeps } from "../../telemetry/emit.js";
|
|
11
|
+
import type { InstallerConfig } from "./config.js";
|
|
12
|
+
/** UI-reported funnel events validated at the event route (tm-AC-1). */
|
|
13
|
+
export declare const UI_FUNNEL_EVENTS: readonly ["onboarding_started", "mode_selected", "login_shown", "dashboard_reached"];
|
|
14
|
+
export type UiFunnelEvent = (typeof UI_FUNNEL_EVENTS)[number];
|
|
15
|
+
/** Closed zod schema for `POST /api/onboarding/event` bodies. */
|
|
16
|
+
export declare const OnboardingEventBodySchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
17
|
+
event: z.ZodLiteral<"mode_selected">;
|
|
18
|
+
properties: z.ZodObject<{
|
|
19
|
+
mode: z.ZodEnum<{
|
|
20
|
+
standard: "standard";
|
|
21
|
+
advanced: "advanced";
|
|
22
|
+
}>;
|
|
23
|
+
}, z.core.$strip>;
|
|
24
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
25
|
+
event: z.ZodEnum<{
|
|
26
|
+
onboarding_started: "onboarding_started";
|
|
27
|
+
login_shown: "login_shown";
|
|
28
|
+
dashboard_reached: "dashboard_reached";
|
|
29
|
+
}>;
|
|
30
|
+
properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
31
|
+
}, z.core.$strip>]>;
|
|
32
|
+
export type ParsedOnboardingEvent = z.infer<typeof OnboardingEventBodySchema>;
|
|
33
|
+
export interface FunnelTelemetryDeps {
|
|
34
|
+
readonly config: InstallerConfig;
|
|
35
|
+
readonly emitDeps?: EmitDeps;
|
|
36
|
+
readonly stateDir?: string;
|
|
37
|
+
readonly clock?: () => string;
|
|
38
|
+
}
|
|
39
|
+
/** The funnel emitter wired into installer routes and the install state machine. */
|
|
40
|
+
export interface FunnelTelemetry {
|
|
41
|
+
/** Handle a validated UI event body from the event route. */
|
|
42
|
+
recordUiEvent(body: ParsedOnboardingEvent): void;
|
|
43
|
+
/** Emit when a new install begins (not attach, not short-circuit). */
|
|
44
|
+
recordProductInstallStarted(product: InstallableProduct): void;
|
|
45
|
+
/** Emit when an install reaches the completed stage. */
|
|
46
|
+
recordProductInstallCompleted(product: InstallableProduct): void;
|
|
47
|
+
/** Emit when an install fails at a closed stage. */
|
|
48
|
+
recordProductInstallFailed(product: InstallableProduct, stage: InstallStage): void;
|
|
49
|
+
/** Observe fleet readiness; emits `health_check_passed` once when ready becomes true. */
|
|
50
|
+
observeHealthReady(ready: boolean): void;
|
|
51
|
+
/** Observe auth; emits `login_completed` once when authenticated flips true (tm-AC-1). */
|
|
52
|
+
observeAuthenticated(authenticated: boolean): void;
|
|
53
|
+
}
|
|
54
|
+
export declare function createFunnelTelemetry(deps: FunnelTelemetryDeps): FunnelTelemetry;
|