@legioncodeinc/hive 0.3.1 → 0.4.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 (50) hide show
  1. package/README.md +1 -1
  2. package/dist/daemon/dashboard/app.js +12 -12
  3. package/dist/daemon/installer/config.d.ts +2 -0
  4. package/dist/daemon/installer/config.js +5 -3
  5. package/dist/daemon/installer/config.js.map +1 -1
  6. package/dist/daemon/installer/token.d.ts +8 -7
  7. package/dist/daemon/installer/token.js +40 -18
  8. package/dist/daemon/installer/token.js.map +1 -1
  9. package/dist/daemon/registry.d.ts +6 -0
  10. package/dist/daemon/registry.js +15 -17
  11. package/dist/daemon/registry.js.map +1 -1
  12. package/dist/daemon/server.d.ts +11 -0
  13. package/dist/daemon/server.js +18 -0
  14. package/dist/daemon/server.js.map +1 -1
  15. package/dist/dashboard/web/pages/hive-graph.js +128 -42
  16. package/dist/dashboard/web/pages/hive-graph.js.map +1 -1
  17. package/dist/dashboard/web/wire.d.ts +91 -14
  18. package/dist/dashboard/web/wire.js +71 -0
  19. package/dist/dashboard/web/wire.js.map +1 -1
  20. package/dist/install/registry.d.ts +2 -2
  21. package/dist/install/registry.js +14 -7
  22. package/dist/install/registry.js.map +1 -1
  23. package/dist/lock.d.ts +5 -2
  24. package/dist/lock.js +36 -1
  25. package/dist/lock.js.map +1 -1
  26. package/dist/service/index.d.ts +5 -0
  27. package/dist/service/index.js +9 -1
  28. package/dist/service/index.js.map +1 -1
  29. package/dist/service/templates.d.ts +22 -3
  30. package/dist/service/templates.js +47 -10
  31. package/dist/service/templates.js.map +1 -1
  32. package/dist/shared/apiary-root.d.ts +28 -0
  33. package/dist/shared/apiary-root.js +71 -0
  34. package/dist/shared/apiary-root.js.map +1 -0
  35. package/dist/shared/constants.d.ts +0 -1
  36. package/dist/shared/constants.js +3 -5
  37. package/dist/shared/constants.js.map +1 -1
  38. package/dist/shared/legacy-paths.d.ts +22 -0
  39. package/dist/shared/legacy-paths.js +48 -0
  40. package/dist/shared/legacy-paths.js.map +1 -0
  41. package/dist/shared/registry-paths.d.ts +19 -0
  42. package/dist/shared/registry-paths.js +52 -0
  43. package/dist/shared/registry-paths.js.map +1 -0
  44. package/dist/shared/state-migration.d.ts +13 -0
  45. package/dist/shared/state-migration.js +92 -0
  46. package/dist/shared/state-migration.js.map +1 -0
  47. package/dist/telemetry/emit.d.ts +10 -10
  48. package/dist/telemetry/emit.js +12 -3
  49. package/dist/telemetry/emit.js.map +1 -1
  50. package/package.json +1 -1
@@ -34,6 +34,8 @@ export interface InstallerConfig {
34
34
  readonly manifestTimeoutMs: number;
35
35
  /** The onboarding token file path (read lazily, deleted on completion). */
36
36
  readonly tokenPath: string;
37
+ /** Legacy-window onboarding token path (mg-AC-9 dual read). */
38
+ readonly legacyTokenPath: string;
37
39
  /** Existence check (a fake map in tests). */
38
40
  readonly fileExists: (path: string) => boolean;
39
41
  /** UTF-8 read returning `null` on any error (a fake map in tests). */
@@ -8,9 +8,10 @@
8
8
  */
9
9
  import { createRequire } from "node:module";
10
10
  import { existsSync, readFileSync, rmSync } from "node:fs";
11
- import { join } from "node:path";
12
11
  import { platform as osPlatform } from "node:process";
13
- import { HIVE_VERSION, HONEYCOMB_HOME_DIR } from "../../shared/constants.js";
12
+ import { HIVE_VERSION } from "../../shared/constants.js";
13
+ import { resolveOnboardingTokenPath } from "../../shared/apiary-root.js";
14
+ import { LEGACY_ONBOARDING_TOKEN_PATH } from "../../shared/legacy-paths.js";
14
15
  import { createNodeSpawn } from "./spawn.js";
15
16
  /** The primary fleet manifest URL served by the install site (MV-2). */
16
17
  export const MANIFEST_URL = "https://get.theapiary.sh/hive-release.json";
@@ -20,7 +21,7 @@ export const MANIFEST_URL = "https://get.theapiary.sh/hive-release.json";
20
21
  */
21
22
  export const MANIFEST_FALLBACK_URL = "https://raw.githubusercontent.com/legioncodeinc/the-apiary/main/hive-release.json";
22
23
  /** 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
+ export const ONBOARDING_TOKEN_PATH = resolveOnboardingTokenPath();
24
25
  /** The bounded timeout for the single network manifest fetch before falling back to the snapshot. */
25
26
  export const MANIFEST_TIMEOUT_MS = 5000;
26
27
  function defaultReadTextFile(path) {
@@ -59,6 +60,7 @@ export function createInstallerConfig(overrides = {}) {
59
60
  manifestFetch: overrides.manifestFetch ?? ((input, init) => fetch(input, init)),
60
61
  manifestTimeoutMs: overrides.manifestTimeoutMs ?? MANIFEST_TIMEOUT_MS,
61
62
  tokenPath: overrides.tokenPath ?? ONBOARDING_TOKEN_PATH,
63
+ legacyTokenPath: overrides.legacyTokenPath ?? LEGACY_ONBOARDING_TOKEN_PATH,
62
64
  fileExists: overrides.fileExists ?? ((path) => existsSync(path)),
63
65
  readTextFile: overrides.readTextFile ?? defaultReadTextFile,
64
66
  deleteFile: overrides.deleteFile ?? defaultDeleteFile,
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/daemon/installer/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/daemon/installer/config.ts"],"names":[],"mappings":"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"}
@@ -1,13 +1,14 @@
1
1
  /**
2
2
  * PRD-009a: the daemon side of the one-time onboarding token contract (is-AC-9/10).
3
3
  *
4
- * The bootstrap mints the token and writes it to `~/.honeycomb/hive/onboarding-token` at mode
5
- * 0600 BEFORE the daemon starts (PRD-009d). The daemon reads it LAZILY at request time, not at
6
- * startup, because on re-entry the daemon may already be running when a fresh bootstrap writes a
7
- * new token. Comparison is constant time (`crypto.timingSafeEqual`). Completion invalidates the
8
- * token: the file is deleted and an in-memory flag is set, so state-changing endpoints refuse all
9
- * requests until a fresh bootstrap mints a new one (is-AC-10). The token is never logged, never
10
- * returned, and never placed in an error body.
4
+ * The bootstrap mints the token and writes it to the hive onboarding-token path at mode 0600 BEFORE
5
+ * the daemon starts (PRD-009d). During the fleet-root migration window the daemon also reads the
6
+ * legacy path when the new path is absent (mg-AC-9). The daemon reads the token LAZILY at request
7
+ * time, not at startup, because on re-entry the daemon may already be running when a fresh bootstrap
8
+ * writes a new token. Comparison is constant time (`crypto.timingSafeEqual`). Completion invalidates
9
+ * the token: whichever path served it is deleted and an in-memory flag is set, so state-changing
10
+ * endpoints refuse all requests until a fresh bootstrap mints a new one (is-AC-10). The token is
11
+ * never logged, never returned, and never placed in an error body.
11
12
  */
12
13
  import type { InstallerConfig } from "./config.js";
13
14
  /** The token gate: whether onboarding is active, whether a presented token is valid, and invalidation. */
@@ -1,13 +1,14 @@
1
1
  /**
2
2
  * PRD-009a: the daemon side of the one-time onboarding token contract (is-AC-9/10).
3
3
  *
4
- * The bootstrap mints the token and writes it to `~/.honeycomb/hive/onboarding-token` at mode
5
- * 0600 BEFORE the daemon starts (PRD-009d). The daemon reads it LAZILY at request time, not at
6
- * startup, because on re-entry the daemon may already be running when a fresh bootstrap writes a
7
- * new token. Comparison is constant time (`crypto.timingSafeEqual`). Completion invalidates the
8
- * token: the file is deleted and an in-memory flag is set, so state-changing endpoints refuse all
9
- * requests until a fresh bootstrap mints a new one (is-AC-10). The token is never logged, never
10
- * returned, and never placed in an error body.
4
+ * The bootstrap mints the token and writes it to the hive onboarding-token path at mode 0600 BEFORE
5
+ * the daemon starts (PRD-009d). During the fleet-root migration window the daemon also reads the
6
+ * legacy path when the new path is absent (mg-AC-9). The daemon reads the token LAZILY at request
7
+ * time, not at startup, because on re-entry the daemon may already be running when a fresh bootstrap
8
+ * writes a new token. Comparison is constant time (`crypto.timingSafeEqual`). Completion invalidates
9
+ * the token: whichever path served it is deleted and an in-memory flag is set, so state-changing
10
+ * endpoints refuse all requests until a fresh bootstrap mints a new one (is-AC-10). The token is
11
+ * never logged, never returned, and never placed in an error body.
11
12
  */
12
13
  import { timingSafeEqual } from "node:crypto";
13
14
  /** Constant-time string comparison. Length is compared first (timingSafeEqual requires equal length). */
@@ -21,35 +22,56 @@ function constantTimeEquals(a, b) {
21
22
  }
22
23
  return timingSafeEqual(bufA, bufB);
23
24
  }
25
+ function readTrimmed(config, path) {
26
+ const raw = config.readTextFile(path);
27
+ if (raw === null)
28
+ return null;
29
+ const trimmed = raw.trim();
30
+ return trimmed.length > 0 ? trimmed : null;
31
+ }
32
+ /**
33
+ * mg-AC-9/10: new path first, legacy path second, and the fallback is ABSENCE-triggered only.
34
+ * When the new-path file EXISTS, the legacy path is never consulted, even if the new file is
35
+ * unreadable or empty (that reads as "no valid token", never as stale legacy data).
36
+ */
37
+ function resolveTokenPathAndValue(config) {
38
+ if (config.fileExists(config.tokenPath)) {
39
+ const primary = readTrimmed(config, config.tokenPath);
40
+ return primary === null ? null : { path: config.tokenPath, value: primary };
41
+ }
42
+ const legacy = readTrimmed(config, config.legacyTokenPath);
43
+ if (legacy !== null)
44
+ return { path: config.legacyTokenPath, value: legacy };
45
+ return null;
46
+ }
24
47
  /** Build the token store over the injected config seams. */
25
48
  export function createTokenStore(config) {
26
49
  let invalidated = false;
27
- const readToken = () => {
28
- const raw = config.readTextFile(config.tokenPath);
29
- if (raw === null)
30
- return null;
31
- const trimmed = raw.trim();
32
- return trimmed.length > 0 ? trimmed : null;
33
- };
50
+ let activePath = null;
34
51
  return {
35
52
  isActive() {
36
53
  if (invalidated)
37
54
  return false;
38
- return config.fileExists(config.tokenPath);
55
+ return resolveTokenPathAndValue(config) !== null;
39
56
  },
40
57
  requireValid(provided) {
41
58
  if (invalidated)
42
59
  return false;
43
60
  if (provided === null || provided === undefined || provided.length === 0)
44
61
  return false;
45
- const stored = readToken();
46
- if (stored === null)
62
+ const resolved = resolveTokenPathAndValue(config);
63
+ if (resolved === null)
47
64
  return false;
48
- return constantTimeEquals(stored, provided);
65
+ activePath = resolved.path;
66
+ return constantTimeEquals(resolved.value, provided);
49
67
  },
50
68
  invalidate() {
51
69
  invalidated = true;
52
70
  config.deleteFile(config.tokenPath);
71
+ config.deleteFile(config.legacyTokenPath);
72
+ if (activePath !== null) {
73
+ config.deleteFile(activePath);
74
+ }
53
75
  }
54
76
  };
55
77
  }
@@ -1 +1 @@
1
- {"version":3,"file":"token.js","sourceRoot":"","sources":["../../../src/daemon/installer/token.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAI9C,yGAAyG;AACzG,SAAS,kBAAkB,CAAC,CAAS,EAAE,CAAS;IAC9C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IACpC,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;QAChC,uFAAuF;QACvF,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC5B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AACrC,CAAC;AAYD,4DAA4D;AAC5D,MAAM,UAAU,gBAAgB,CAAC,MAAuB;IACtD,IAAI,WAAW,GAAG,KAAK,CAAC;IAExB,MAAM,SAAS,GAAG,GAAkB,EAAE;QACpC,MAAM,GAAG,GAAG,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAClD,IAAI,GAAG,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QAC9B,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QAC3B,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7C,CAAC,CAAC;IAEF,OAAO;QACL,QAAQ;YACN,IAAI,WAAW;gBAAE,OAAO,KAAK,CAAC;YAC9B,OAAO,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC7C,CAAC;QACD,YAAY,CAAC,QAAQ;YACnB,IAAI,WAAW;gBAAE,OAAO,KAAK,CAAC;YAC9B,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,KAAK,CAAC;YACvF,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;YAC3B,IAAI,MAAM,KAAK,IAAI;gBAAE,OAAO,KAAK,CAAC;YAClC,OAAO,kBAAkB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC9C,CAAC;QACD,UAAU;YACR,WAAW,GAAG,IAAI,CAAC;YACnB,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACtC,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"token.js","sourceRoot":"","sources":["../../../src/daemon/installer/token.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,9 @@
1
1
  import { type DaemonBases, type DaemonName } from "../shared/daemon-routing.js";
2
+ /**
3
+ * Informational only: the fleet-root registry location snapshotted at module load. Readers must
4
+ * go through {@link readRegistryBody} (which applies the new-then-legacy window chain per call),
5
+ * never read this constant's path directly.
6
+ */
2
7
  export declare const DOCTOR_REGISTRY_PATH: string;
3
8
  export interface ResolveDaemonBasesOptions {
4
9
  readonly registryPath?: string;
@@ -29,3 +34,4 @@ export interface ResolveRegisteredServiceNamesOptions {
29
34
  */
30
35
  export declare function resolveRegisteredServiceNames(options?: ResolveRegisteredServiceNamesOptions): readonly string[];
31
36
  export declare function resolveDaemonBases(options?: ResolveDaemonBasesOptions): DaemonBases;
37
+ export { readRegistryBody } from "../shared/registry-paths.js";
@@ -1,9 +1,13 @@
1
- import { readFileSync } from "node:fs";
2
- import { join } from "node:path";
3
1
  import { z } from "zod";
4
- import { HONEYCOMB_HOME_DIR } from "../shared/constants.js";
2
+ import { resolveFleetRegistryPath } from "../shared/apiary-root.js";
3
+ import { readRegistryBody } from "../shared/registry-paths.js";
5
4
  import { isLoopbackBaseUrl, normalizeDaemonBases } from "../shared/daemon-routing.js";
6
- export const DOCTOR_REGISTRY_PATH = join(HONEYCOMB_HOME_DIR, "doctor.daemons.json");
5
+ /**
6
+ * Informational only: the fleet-root registry location snapshotted at module load. Readers must
7
+ * go through {@link readRegistryBody} (which applies the new-then-legacy window chain per call),
8
+ * never read this constant's path directly.
9
+ */
10
+ export const DOCTOR_REGISTRY_PATH = resolveFleetRegistryPath();
7
11
  const RegistryEntrySchema = z.object({
8
12
  name: z.string().min(1),
9
13
  healthUrl: z.string().url(),
@@ -97,24 +101,18 @@ export function parseRegisteredServiceNames(raw) {
97
101
  * shows no tiles until the fleet-status/SSE feed enumerates services some other way).
98
102
  */
99
103
  export function resolveRegisteredServiceNames(options = {}) {
100
- const registryPath = options.registryPath ?? DOCTOR_REGISTRY_PATH;
101
- const readFile = options.readFile ?? ((path) => readFileSync(path, "utf8"));
102
- try {
103
- return parseRegisteredServiceNames(readFile(registryPath));
104
- }
105
- catch {
104
+ const raw = readRegistryBody(options);
105
+ if (raw === null)
106
106
  return [];
107
- }
107
+ return parseRegisteredServiceNames(raw);
108
108
  }
109
109
  export function resolveDaemonBases(options = {}) {
110
- const registryPath = options.registryPath ?? DOCTOR_REGISTRY_PATH;
111
- const readFile = options.readFile ?? ((path) => readFileSync(path, "utf8"));
112
- try {
113
- return normalizeDaemonBases(parseDoctorRegistry(readFile(registryPath)));
114
- }
115
- catch {
110
+ const raw = readRegistryBody(options);
111
+ if (raw === null) {
116
112
  // Missing or unreadable registry means no daemon has registered yet. Use documented loopback defaults.
117
113
  return normalizeDaemonBases();
118
114
  }
115
+ return normalizeDaemonBases(parseDoctorRegistry(raw));
119
116
  }
117
+ export { readRegistryBody } from "../shared/registry-paths.js";
120
118
  //# sourceMappingURL=registry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/daemon/registry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/daemon/registry.ts"],"names":[],"mappings":"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"}
@@ -30,6 +30,17 @@ export interface CreateHiveOptions {
30
30
  export interface StartHiveOptions extends CreateHiveOptions {
31
31
  readonly lockPaths?: Partial<LockPaths>;
32
32
  readonly serveFn?: ServeFunction;
33
+ /**
34
+ * PRD-010b boot-migration seam. Defaults to the real {@link migrateHiveState}; tests MUST
35
+ * inject a no-op (or the suite-wide isolated home applies) so no test touches the real home.
36
+ */
37
+ readonly migrateState?: () => void;
38
+ /**
39
+ * rc-AC-2/mg-AC-7 registry-upsert seam, invoked AFTER the lock is acquired and the pid file is
40
+ * written (same boot, pinned ordering). Defaults to the real {@link registerHiveWithDoctor};
41
+ * tests inject a no-op or a recorder.
42
+ */
43
+ readonly registerWithDoctor?: () => void;
33
44
  }
34
45
  export interface HiveInstance {
35
46
  readonly app: Hono;
@@ -1,6 +1,8 @@
1
1
  import { serve } from "@hono/node-server";
2
2
  import { Hono } from "hono";
3
3
  import { acquireSingleInstanceLock, releaseSingleInstanceLock } from "../lock.js";
4
+ import { registerHiveWithDoctor } from "../install/registry.js";
5
+ import { migrateHiveState } from "../shared/state-migration.js";
4
6
  import { DOCTOR_STATUS_URL, HIVE_HOST, HIVE_PORT, HIVE_VERSION } from "../shared/constants.js";
5
7
  import { mountDashboardAssets, mountDashboardShellFallback, renderShell } from "./dashboard/host.js";
6
8
  import { fetchFleetStatus } from "./fleet-status.js";
@@ -98,8 +100,24 @@ export function createHive(options = {}) {
98
100
  }
99
101
  export function startHive(options = {}) {
100
102
  const serveFn = options.serveFn ?? serve;
103
+ const migrateState = options.migrateState ?? (() => {
104
+ migrateHiveState();
105
+ });
106
+ const registerWithDoctor = options.registerWithDoctor ?? (() => {
107
+ registerHiveWithDoctor();
108
+ });
109
+ migrateState();
101
110
  const hive = createHive(options);
102
111
  const lockPaths = acquireSingleInstanceLock(options.lockPaths);
112
+ // rc-AC-2/mg-AC-7 ordering: the lock is held and the new pid file is written; NOW upsert the
113
+ // registry entry naming that pid path, in the same boot. Best-effort: boot never fails when the
114
+ // registry file is mid-move.
115
+ try {
116
+ registerWithDoctor();
117
+ }
118
+ catch {
119
+ // Fail-soft by design.
120
+ }
103
121
  let server;
104
122
  try {
105
123
  server = serveFn({
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/daemon/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/daemon/server.ts"],"names":[],"mappings":"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"}
@@ -12,11 +12,97 @@ import { Panel } from "../panels.js";
12
12
  import { Badge, Button } from "../primitives.js";
13
13
  import { isTabHidden, PageFrame } from "../page-frame.js";
14
14
  import { NeedsProjectSelection } from "../needs-project.js";
15
+ import { FolderPicker } from "../folder-picker.js";
15
16
  import { useScope } from "../scope-context.js";
16
17
  import { GraphCanvasFull, KindToggle, ToolButton, IDENTITY_TRANSFORM, GRAPH_VIEW, MIN_ZOOM, MAX_ZOOM, ZOOM_STEP, applyKindFilter, centerOn, distinctKinds, } from "./graph.js";
17
- import { capGraphForRender, EMPTY_GRAPH, EMPTY_HIVE_GRAPH_STATUS, MAX_RENDER_NODES, } from "../wire.js";
18
+ import { capGraphForRender, EMPTY_GRAPH, EMPTY_HIVE_GRAPH_STATUS, MAX_RENDER_NODES, EMPTY_NECTAR_PROJECTS, } from "../wire.js";
18
19
  /** Poll interval for graph + status widgets (mirrors Memory Graph discipline). */
19
20
  const HIVE_GRAPH_POLL_MS = 8000;
21
+ const SURFACE = {
22
+ padding: 16,
23
+ background: "var(--bg-surface)",
24
+ border: "1px solid var(--border-default)",
25
+ borderRadius: "var(--radius-lg)",
26
+ };
27
+ function broodingLabel(state) {
28
+ switch (state) {
29
+ case "active":
30
+ return "brooding";
31
+ case "paused":
32
+ return "paused";
33
+ case "global-paused":
34
+ return "global-paused";
35
+ default: {
36
+ const _exhaustive = state;
37
+ return _exhaustive;
38
+ }
39
+ }
40
+ }
41
+ function broodingBadgeTone(state) {
42
+ switch (state) {
43
+ case "active":
44
+ return "verified";
45
+ case "paused":
46
+ return "neutral";
47
+ case "global-paused":
48
+ return "warning";
49
+ default: {
50
+ const _exhaustive = state;
51
+ return _exhaustive;
52
+ }
53
+ }
54
+ }
55
+ /** PRD-019c — nectar active projects + brooding controls (polls `GET /api/hive-graph/projects`). */
56
+ function NectarProjectsPanel({ wire }) {
57
+ const [projectsWire, setProjectsWire] = React.useState(EMPTY_NECTAR_PROJECTS);
58
+ const [hydrated, setHydrated] = React.useState(false);
59
+ const [busyKey, setBusyKey] = React.useState(null);
60
+ const inFlightRef = React.useRef(false);
61
+ const reList = React.useCallback(async () => {
62
+ const next = await wire.nectarProjects();
63
+ setProjectsWire(next);
64
+ setHydrated(true);
65
+ }, [wire]);
66
+ React.useEffect(() => {
67
+ let alive = true;
68
+ const tick = async () => {
69
+ if (!alive || isTabHidden())
70
+ return;
71
+ await reList();
72
+ };
73
+ void tick();
74
+ const id = setInterval(() => void tick(), HIVE_GRAPH_POLL_MS);
75
+ return () => {
76
+ alive = false;
77
+ clearInterval(id);
78
+ };
79
+ }, [reList]);
80
+ const runBroodingWrite = React.useCallback(async (body, busy) => {
81
+ if (inFlightRef.current || projectsWire.unreachable)
82
+ return;
83
+ inFlightRef.current = true;
84
+ setBusyKey(busy);
85
+ try {
86
+ const ack = await wire.setNectarBrooding(body);
87
+ if (!ack.unreachable) {
88
+ setProjectsWire(ack);
89
+ }
90
+ await reList();
91
+ }
92
+ finally {
93
+ inFlightRef.current = false;
94
+ setBusyKey(null);
95
+ }
96
+ }, [wire, reList, projectsWire.unreachable]);
97
+ const onBound = React.useCallback(() => {
98
+ void reList();
99
+ }, [reList]);
100
+ const controlsDisabled = !hydrated || projectsWire.unreachable || busyKey !== null;
101
+ return (_jsx(Panel, { title: "Nectar projects", right: _jsx(Button, { variant: "secondary", size: "sm", "data-testid": "nectar-global-brooding-toggle", disabled: controlsDisabled, onClick: () => void runBroodingWrite({ global: projectsWire.globalBrooding === "on" ? "paused" : "on" }, "global"), children: projectsWire.globalBrooding === "on" ? "Pause all brooding" : "Resume all brooding" }), children: projectsWire.unreachable ? (_jsx("div", { "data-testid": "nectar-projects-unreachable", style: { fontFamily: "var(--font-mono)", fontSize: 12, color: "var(--severity-critical)" }, children: "Nectar is unreachable \u2014 project brooding controls are disabled." })) : !hydrated ? (_jsx("span", { style: { fontFamily: "var(--font-mono)", fontSize: 12, color: "var(--text-tertiary)" }, children: "loading\u2026" })) : projectsWire.projects.length === 0 ? (_jsxs("div", { "data-testid": "nectar-needs-project", style: { display: "flex", flexDirection: "column", gap: 12 }, children: [_jsx("div", { style: { fontSize: 14, color: "var(--text-secondary)" }, children: "No active nectar projects yet. Pick a folder to activate brooding for a project directory." }), _jsx(FolderPicker, { wire: wire, onBound: onBound })] })) : (_jsx("div", { "data-testid": "nectar-projects-list", style: { display: "flex", flexDirection: "column", gap: 10 }, children: projectsWire.projects.map((project) => (_jsxs("div", { "data-testid": "nectar-project-row", "data-project-id": project.projectId, style: { ...SURFACE, display: "flex", flexDirection: "column", gap: 8 }, children: [_jsxs("div", { style: { display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }, children: [_jsx("span", { "data-testid": "nectar-project-name", style: { fontFamily: "var(--font-mono)", fontSize: 14, fontWeight: 600, color: "var(--honey)" }, children: project.name !== "" ? project.name : project.projectId }), _jsx(Badge, { tone: broodingBadgeTone(project.brooding), mono: true, children: _jsx("span", { "data-testid": "nectar-brooding-badge", children: broodingLabel(project.brooding) }) }), _jsx("span", { style: { flex: 1 } }), _jsx(Button, { variant: "ghost", size: "sm", "data-testid": "nectar-brooding-toggle", disabled: controlsDisabled, onClick: () => void runBroodingWrite({
102
+ projectId: project.projectId,
103
+ brooding: project.brooding === "active" ? "off" : "on",
104
+ }, project.projectId), children: project.brooding === "active" ? "Turn off brooding" : "Turn on brooding" })] }), _jsx("span", { "data-testid": "nectar-project-path", style: { fontFamily: "var(--font-mono)", fontSize: 12, color: "var(--text-tertiary)", wordBreak: "break-all" }, children: project.path })] }, project.projectId))) })) }));
105
+ }
20
106
  function clamp(n, lo, hi) {
21
107
  return Math.min(hi, Math.max(lo, n));
22
108
  }
@@ -191,51 +277,51 @@ export function HiveGraphPage({ wire }) {
191
277
  const onSelect = React.useCallback((id) => setSelected((cur) => (cur === id ? null : id)), []);
192
278
  const clearSelection = React.useCallback(() => setSelected(null), []);
193
279
  const eyebrow = `${rendered.nodes.length} files · ${rendered.edges.length} provenance links`;
194
- return (_jsx(PageFrame, { title: "Hive Graph", eyebrow: project === undefined ? "hive graph" : eyebrow, children: project === undefined ? (_jsx(NeedsProjectSelection, { surface: "Hive Graph" })) : (_jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: [_jsx(Panel, { title: "Pipeline status", right: _jsx(HiveGraphBuildButton, { onBuild: () => wire.hiveGraphBuild() }), children: _jsx(StatusWidgets, { status: status }) }), _jsxs(Panel, { title: "Search", children: [_jsxs("div", { style: { display: "flex", gap: 8, marginBottom: 12 }, children: [_jsx("input", { "aria-label": "search hive graph", "data-testid": "hive-graph-search-input", type: "text", value: searchQuery, disabled: project === undefined, placeholder: "search files and descriptions\u2026", onChange: (e) => setSearchQuery(e.target.value), onKeyDown: (e) => {
195
- if (e.key === "Enter")
196
- void onSearchSubmit();
197
- }, style: {
198
- flex: 1,
199
- height: 30,
200
- padding: "0 12px",
201
- background: "var(--bg-surface)",
202
- border: "1px solid var(--border-default)",
203
- borderRadius: "var(--radius-md)",
204
- color: "var(--text-primary)",
205
- fontFamily: "var(--font-mono)",
206
- fontSize: 13,
207
- } }), _jsx(Button, { variant: "primary", onClick: () => void onSearchSubmit(), disabled: searching || searchQuery.trim() === "", "data-testid": "hive-graph-search-submit", children: searching ? "Searching…" : "Search" })] }), searchResults !== null && _jsx(SearchResults, { results: searchResults, onSelect: (hit) => focusNode(hit.id) })] }), graphUnreachable || !graph.built ? (_jsx(HiveGraphEmptyState, { unreachable: graphUnreachable })) : (_jsxs(_Fragment, { children: [capped && (_jsxs("div", { "data-testid": "hive-graph-truncation-notice", style: {
208
- display: "flex",
209
- alignItems: "center",
210
- gap: 8,
211
- padding: "9px 12px",
212
- background: "var(--bg-elevated)",
213
- border: "1px solid var(--border-subtle)",
214
- borderRadius: "var(--radius-md)",
215
- fontFamily: "var(--font-mono)",
216
- fontSize: 12,
217
- color: "var(--text-tertiary)",
218
- }, children: [_jsx("span", { style: { width: 7, height: 7, borderRadius: "50%", background: "var(--severity-warning)", flex: "none" } }), "Rendering is capped at ", MAX_RENDER_NODES.toLocaleString(), " nodes. Use search and kind filters to focus."] })), _jsxs("div", { style: { display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap" }, children: [_jsx("div", { style: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }, children: kinds.map((k) => {
219
- const count = graph.nodes.filter((n) => n.kind === k).length;
220
- return _jsx(KindToggle, { kind: k, count: count, hidden: hiddenKinds.has(k), onToggle: () => toggleKind(k) }, k);
221
- }) }), _jsx("span", { style: { flex: 1 } }), _jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [_jsx(ToolButton, { label: "\u2212", ariaLabel: "zoom out", onClick: zoomOut }), _jsx(ToolButton, { label: "+", ariaLabel: "zoom in", onClick: zoomIn }), _jsx("button", { type: "button", "data-testid": "hive-graph-fit-view", onClick: fit, style: {
280
+ return (_jsx(PageFrame, { title: "Hive Graph", eyebrow: project === undefined ? "hive graph" : eyebrow, children: _jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: [_jsx(NectarProjectsPanel, { wire: wire }), project === undefined ? (_jsx(NeedsProjectSelection, { surface: "Hive Graph" })) : (_jsxs(_Fragment, { children: [_jsx(Panel, { title: "Pipeline status", right: _jsx(HiveGraphBuildButton, { onBuild: () => wire.hiveGraphBuild() }), children: _jsx(StatusWidgets, { status: status }) }), _jsxs(Panel, { title: "Search", children: [_jsxs("div", { style: { display: "flex", gap: 8, marginBottom: 12 }, children: [_jsx("input", { "aria-label": "search hive graph", "data-testid": "hive-graph-search-input", type: "text", value: searchQuery, disabled: project === undefined, placeholder: "search files and descriptions\u2026", onChange: (e) => setSearchQuery(e.target.value), onKeyDown: (e) => {
281
+ if (e.key === "Enter")
282
+ void onSearchSubmit();
283
+ }, style: {
284
+ flex: 1,
222
285
  height: 30,
223
286
  padding: "0 12px",
224
- background: "transparent",
287
+ background: "var(--bg-surface)",
225
288
  border: "1px solid var(--border-default)",
226
289
  borderRadius: "var(--radius-md)",
227
- color: "var(--text-secondary)",
290
+ color: "var(--text-primary)",
228
291
  fontFamily: "var(--font-mono)",
229
- fontSize: 12,
230
- cursor: "pointer",
231
- }, children: "fit" })] })] }), _jsxs("div", { style: { display: "flex", gap: 16, alignItems: "stretch" }, children: [_jsx("div", { style: {
232
- flex: 1,
233
- minWidth: 0,
234
- height: "70vh",
235
- background: "var(--bg-surface)",
236
- border: "1px solid var(--border-default)",
237
- borderRadius: "var(--radius-lg)",
238
- overflow: "hidden",
239
- }, children: _jsx(GraphCanvasFull, { graph: rendered, selected: selectedNode?.id ?? null, transform: transform, onSelect: onSelect, onClear: clearSelection, onPanZoom: setTransform }) }), selectedNode !== null && (_jsx(FileNodeDetailPanel, { node: selectedNode, file: files[selectedNode.id], derived: derived[selectedNode.id], onClear: clearSelection }))] })] }))] })) }));
292
+ fontSize: 13,
293
+ } }), _jsx(Button, { variant: "primary", onClick: () => void onSearchSubmit(), disabled: searching || searchQuery.trim() === "", "data-testid": "hive-graph-search-submit", children: searching ? "Searching…" : "Search" })] }), searchResults !== null && _jsx(SearchResults, { results: searchResults, onSelect: (hit) => focusNode(hit.id) })] }), graphUnreachable || !graph.built ? (_jsx(HiveGraphEmptyState, { unreachable: graphUnreachable })) : (_jsxs(_Fragment, { children: [capped && (_jsxs("div", { "data-testid": "hive-graph-truncation-notice", style: {
294
+ display: "flex",
295
+ alignItems: "center",
296
+ gap: 8,
297
+ padding: "9px 12px",
298
+ background: "var(--bg-elevated)",
299
+ border: "1px solid var(--border-subtle)",
300
+ borderRadius: "var(--radius-md)",
301
+ fontFamily: "var(--font-mono)",
302
+ fontSize: 12,
303
+ color: "var(--text-tertiary)",
304
+ }, children: [_jsx("span", { style: { width: 7, height: 7, borderRadius: "50%", background: "var(--severity-warning)", flex: "none" } }), "Rendering is capped at ", MAX_RENDER_NODES.toLocaleString(), " nodes. Use search and kind filters to focus."] })), _jsxs("div", { style: { display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap" }, children: [_jsx("div", { style: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }, children: kinds.map((k) => {
305
+ const count = graph.nodes.filter((n) => n.kind === k).length;
306
+ return _jsx(KindToggle, { kind: k, count: count, hidden: hiddenKinds.has(k), onToggle: () => toggleKind(k) }, k);
307
+ }) }), _jsx("span", { style: { flex: 1 } }), _jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [_jsx(ToolButton, { label: "\u2212", ariaLabel: "zoom out", onClick: zoomOut }), _jsx(ToolButton, { label: "+", ariaLabel: "zoom in", onClick: zoomIn }), _jsx("button", { type: "button", "data-testid": "hive-graph-fit-view", onClick: fit, style: {
308
+ height: 30,
309
+ padding: "0 12px",
310
+ background: "transparent",
311
+ border: "1px solid var(--border-default)",
312
+ borderRadius: "var(--radius-md)",
313
+ color: "var(--text-secondary)",
314
+ fontFamily: "var(--font-mono)",
315
+ fontSize: 12,
316
+ cursor: "pointer",
317
+ }, children: "fit" })] })] }), _jsxs("div", { style: { display: "flex", gap: 16, alignItems: "stretch" }, children: [_jsx("div", { style: {
318
+ flex: 1,
319
+ minWidth: 0,
320
+ height: "70vh",
321
+ background: "var(--bg-surface)",
322
+ border: "1px solid var(--border-default)",
323
+ borderRadius: "var(--radius-lg)",
324
+ overflow: "hidden",
325
+ }, children: _jsx(GraphCanvasFull, { graph: rendered, selected: selectedNode?.id ?? null, transform: transform, onSelect: onSelect, onClear: clearSelection, onPanZoom: setTransform }) }), selectedNode !== null && (_jsx(FileNodeDetailPanel, { node: selectedNode, file: files[selectedNode.id], derived: derived[selectedNode.id], onClear: clearSelection }))] })] }))] }))] }) }));
240
326
  }
241
327
  //# sourceMappingURL=hive-graph.js.map