@blackbelt-technology/pi-agent-dashboard 0.5.3 → 0.5.4
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/AGENTS.md +19 -30
- package/README.md +69 -1
- package/docs/architecture.md +89 -165
- package/package.json +10 -7
- package/packages/extension/package.json +2 -2
- package/packages/extension/src/__tests__/bridge-default-model-gate.test.ts +47 -0
- package/packages/extension/src/__tests__/bridge-followup-chat-order.test.ts +215 -0
- package/packages/extension/src/__tests__/bridge-followup-multi-entry.test.ts +202 -0
- package/packages/extension/src/__tests__/bridge-queue-update-forward.test.ts +77 -0
- package/packages/extension/src/__tests__/bridge-retry-ordering.test.ts +148 -0
- package/packages/extension/src/__tests__/bridge-shadow-queue-drain.test.ts +221 -0
- package/packages/extension/src/__tests__/bridge-shadow-queue-gate.test.ts +299 -0
- package/packages/extension/src/__tests__/bridge-shutdown-reset.test.ts +238 -0
- package/packages/extension/src/__tests__/bridge-slash-command-routing.test.ts +127 -31
- package/packages/extension/src/__tests__/command-handler.test.ts +105 -3
- package/packages/extension/src/__tests__/fixtures/usage-limit-error-strings.ts +127 -0
- package/packages/extension/src/__tests__/source-detector.test.ts +15 -0
- package/packages/extension/src/__tests__/usage-limit-orderer.test.ts +12 -0
- package/packages/extension/src/bridge-default-model-gate.ts +32 -0
- package/packages/extension/src/bridge.ts +299 -20
- package/packages/extension/src/command-handler.ts +53 -7
- package/packages/extension/src/dashboard-default-adapter.ts +5 -0
- package/packages/extension/src/prompt-bus.ts +15 -0
- package/packages/extension/src/slash-dispatch.ts +30 -15
- package/packages/extension/src/source-detector.ts +13 -5
- package/packages/extension/src/usage-limit-orderer.ts +18 -1
- package/packages/server/bin/pi-dashboard.mjs +62 -14
- package/packages/server/package.json +9 -5
- package/packages/server/src/__tests__/browser-gateway-register-handler.test.ts +69 -0
- package/packages/server/src/__tests__/cli-env-no-clobber.test.ts +46 -0
- package/packages/server/src/__tests__/cli-no-bootstrap-references.test.ts +69 -0
- package/packages/server/src/__tests__/cli-parse.test.ts +9 -10
- package/packages/server/src/__tests__/cli-version.test.ts +151 -0
- package/packages/server/src/__tests__/directory-service-openspec-enabled.test.ts +9 -0
- package/packages/server/src/__tests__/directory-service-refresh-force.test.ts +9 -0
- package/packages/server/src/__tests__/directory-service-specs-mtime.test.ts +9 -0
- package/packages/server/src/__tests__/directory-service-toctou.test.ts +9 -0
- package/packages/server/src/__tests__/directory-service.test.ts +9 -0
- package/packages/server/src/__tests__/doctor-route.test.ts +53 -0
- package/packages/server/src/__tests__/event-wiring-queue-state.test.ts +156 -0
- package/packages/server/src/__tests__/event-wiring-resume-clear.test.ts +105 -0
- package/packages/server/src/__tests__/health-shape.test.ts +35 -12
- package/packages/server/src/__tests__/installed-package-enricher.test.ts +12 -12
- package/packages/server/src/__tests__/is-activity-event.test.ts +4 -7
- package/packages/server/src/__tests__/package-routes.test.ts +6 -2
- package/packages/server/src/__tests__/pi-changelog-routes.test.ts +10 -13
- package/packages/server/src/__tests__/pi-core-checker.test.ts +2 -2
- package/packages/server/src/__tests__/pi-version-skew.test.ts +3 -2
- package/packages/server/src/__tests__/plugin-activation-routes.test.ts +267 -0
- package/packages/server/src/__tests__/plugin-intent-cache.test.ts +75 -0
- package/packages/server/src/__tests__/preferences-store.test.ts +196 -0
- package/packages/server/src/__tests__/reattach-placement.test.ts +9 -0
- package/packages/server/src/__tests__/recommended-routes.test.ts +2 -2
- package/packages/server/src/__tests__/recovery-server.test.ts +203 -0
- package/packages/server/src/__tests__/session-action-handler-clear-queue.test.ts +153 -0
- package/packages/server/src/__tests__/session-action-handler-headless-reload.test.ts +43 -0
- package/packages/server/src/__tests__/session-order-manager.test.ts +9 -0
- package/packages/server/src/__tests__/session-order-reboot.test.ts +9 -0
- package/packages/server/src/__tests__/session-ordering-integration.test.ts +9 -0
- package/packages/server/src/browser-gateway.ts +83 -5
- package/packages/server/src/browser-handlers/directory-handler.ts +69 -0
- package/packages/server/src/browser-handlers/session-action-handler.ts +89 -0
- package/packages/server/src/browser-handlers/subscription-handler.ts +23 -0
- package/packages/server/src/changelog-parser.ts +1 -1
- package/packages/server/src/cli.ts +68 -250
- package/packages/server/src/event-status-extraction.ts +14 -62
- package/packages/server/src/event-wiring.ts +23 -10
- package/packages/server/src/memory-session-manager.ts +4 -0
- package/packages/server/src/pi-core-checker.ts +1 -1
- package/packages/server/src/pi-dev-version-check.ts +1 -1
- package/packages/server/src/pi-version-skew.ts +24 -46
- package/packages/server/src/plugin-intent-cache.ts +67 -0
- package/packages/server/src/preferences-store.ts +199 -13
- package/packages/server/src/recovery-server.ts +366 -0
- package/packages/server/src/routes/__tests__/manifest-route.test.ts +138 -0
- package/packages/server/src/routes/doctor-routes.ts +26 -21
- package/packages/server/src/routes/manifest-route.ts +162 -0
- package/packages/server/src/routes/openspec-routes.ts +4 -25
- package/packages/server/src/routes/pi-changelog-routes.ts +5 -24
- package/packages/server/src/routes/pi-core-routes.ts +3 -23
- package/packages/server/src/routes/plugin-activation-routes.ts +193 -0
- package/packages/server/src/routes/recommended-routes.ts +21 -0
- package/packages/server/src/routes/system-routes.ts +73 -11
- package/packages/server/src/server.ts +105 -307
- package/packages/server/src/session-api.ts +5 -63
- package/packages/shared/package.json +1 -1
- package/packages/shared/src/__tests__/binary-lookup-resolveJiti.test.ts +28 -0
- package/packages/shared/src/__tests__/binary-lookup-spawn-env.test.ts +61 -0
- package/packages/shared/src/__tests__/binary-lookup.test.ts +16 -0
- package/packages/shared/src/__tests__/bridge-register.test.ts +67 -0
- package/packages/shared/src/__tests__/ci-electron-no-side-effects.test.ts +129 -0
- package/packages/shared/src/__tests__/config.test.ts +40 -0
- package/packages/shared/src/__tests__/dashboard-paths.test.ts +81 -0
- package/packages/shared/src/__tests__/ensure-windows-path.test.ts +112 -0
- package/packages/shared/src/__tests__/intent-types.test.ts +120 -0
- package/packages/shared/src/__tests__/jiti-packages-parity.test.ts +85 -0
- package/packages/shared/src/__tests__/legacy-managed-dir.test.ts +59 -0
- package/packages/shared/src/__tests__/no-direct-child-process.test.ts +12 -0
- package/packages/shared/src/__tests__/no-electron-execpath-spawn.test.ts +149 -0
- package/packages/shared/src/__tests__/no-flow-command-route-claims.test.ts +71 -0
- package/packages/shared/src/__tests__/no-flow-references-in-shell.test.ts +221 -0
- package/packages/shared/src/__tests__/no-managed-dir-reference.test.ts +134 -0
- package/packages/shared/src/__tests__/no-pi-dashboard-version-jiti-gate.test.ts +41 -0
- package/packages/shared/src/__tests__/no-primitive-direct-import.test.ts +235 -0
- package/packages/shared/src/__tests__/no-server-imports-in-resolver.test.ts +53 -0
- package/packages/shared/src/__tests__/node-spawn-jiti-contract.test.ts +54 -101
- package/packages/shared/src/__tests__/node-spawn.test.ts +29 -13
- package/packages/shared/src/__tests__/pi-package-resolver.test.ts +300 -0
- package/packages/shared/src/__tests__/plugin-activation-contracts.test.ts +74 -0
- package/packages/shared/src/__tests__/plugin-bridge-classify-source.test.ts +73 -0
- package/packages/shared/src/__tests__/plugin-bridge-register-extended.test.ts +17 -5
- package/packages/shared/src/__tests__/plugin-bridge-register-packages.test.ts +233 -0
- package/packages/shared/src/__tests__/plugin-bridge-register.test.ts +19 -9
- package/packages/shared/src/__tests__/publish-workflow-contract.test.ts +154 -15
- package/packages/shared/src/__tests__/recommended-extensions.test.ts +28 -10
- package/packages/shared/src/__tests__/resolver-parity-with-scanner.test.ts +76 -0
- package/packages/shared/src/__tests__/server-identity.test.ts +127 -0
- package/packages/shared/src/__tests__/server-launcher.test.ts +35 -0
- package/packages/shared/src/__tests__/source-matching.test.ts +5 -5
- package/packages/shared/src/__tests__/sync-versions-spec.test.ts +76 -0
- package/packages/shared/src/__tests__/tool-registry-definitions.test.ts +50 -2
- package/packages/shared/src/bridge-register.ts +35 -2
- package/packages/shared/src/browser-protocol.ts +176 -2
- package/packages/shared/src/config.ts +12 -0
- package/packages/shared/src/dashboard-paths.ts +69 -0
- package/packages/shared/src/dashboard-plugin/index.ts +2 -0
- package/packages/shared/src/dashboard-plugin/intent-types.ts +93 -0
- package/packages/shared/src/dashboard-plugin/manifest-types.ts +55 -1
- package/packages/shared/src/dashboard-plugin/plugin-status.ts +82 -0
- package/packages/shared/src/dashboard-plugin/slot-props.ts +11 -0
- package/packages/shared/src/dashboard-plugin/slot-types.ts +16 -2
- package/packages/shared/src/dashboard-plugin/ui-primitives.ts +287 -0
- package/packages/shared/src/dashboard-starter.ts +22 -0
- package/packages/shared/src/doctor-core.ts +49 -27
- package/packages/shared/src/launch-source-types.ts +9 -9
- package/packages/shared/src/legacy-managed-dir.ts +97 -0
- package/packages/shared/src/mdns-discovery.ts +4 -1
- package/packages/shared/src/pi-package-resolver.ts +388 -0
- package/packages/shared/src/platform/binary-lookup.ts +27 -3
- package/packages/shared/src/platform/ensure-windows-path.ts +95 -0
- package/packages/shared/src/platform/exec.ts +22 -0
- package/packages/shared/src/platform/node-spawn.ts +42 -41
- package/packages/shared/src/plugin-bridge-register.ts +275 -18
- package/packages/shared/src/protocol.ts +94 -2
- package/packages/shared/src/recommended-extensions.ts +34 -10
- package/packages/shared/src/server-identity.ts +74 -5
- package/packages/shared/src/server-launcher.ts +20 -0
- package/packages/shared/src/source-matching.ts +1 -1
- package/packages/shared/src/tool-registry/__tests__/node-script-toargv-fallback.test.ts +84 -0
- package/packages/shared/src/tool-registry/definitions.ts +91 -7
- package/packages/shared/src/types.ts +12 -8
- package/scripts/maybe-patch-package.cjs +44 -0
- package/packages/server/src/__tests__/bootstrap-install-from-list.test.ts +0 -263
- package/packages/server/src/__tests__/bootstrap-queue.test.ts +0 -120
- package/packages/server/src/__tests__/bootstrap-routes.test.ts +0 -125
- package/packages/server/src/__tests__/bootstrap-state.test.ts +0 -119
- package/packages/server/src/__tests__/cli-bootstrap.test.ts +0 -36
- package/packages/server/src/__tests__/event-status-extraction-flow.test.ts +0 -55
- package/packages/server/src/__tests__/legacy-pi-cleanup.test.ts +0 -149
- package/packages/server/src/__tests__/post-install-openspec-refresh.test.ts +0 -180
- package/packages/server/src/__tests__/post-install-rescan.test.ts +0 -134
- package/packages/server/src/__tests__/system-routes-reextract.test.ts +0 -91
- package/packages/server/src/bootstrap-install-from-list.ts +0 -232
- package/packages/server/src/bootstrap-queue.ts +0 -130
- package/packages/server/src/bootstrap-state.ts +0 -159
- package/packages/server/src/legacy-pi-cleanup.ts +0 -151
- package/packages/server/src/routes/bootstrap-routes.ts +0 -125
- package/packages/shared/src/__tests__/bootstrap/README.md +0 -133
- package/packages/shared/src/__tests__/bootstrap/__snapshots__/cube.test.ts.snap +0 -378
- package/packages/shared/src/__tests__/bootstrap/assertions.ts +0 -136
- package/packages/shared/src/__tests__/bootstrap/cube.test.ts +0 -47
- package/packages/shared/src/__tests__/bootstrap/cube.ts +0 -66
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/a-electron.test.ts.snap +0 -84
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/b-npm-global.test.ts.snap +0 -90
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/c-dev-monorepo.test.ts.snap +0 -34
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/d-overrides.test.ts.snap +0 -20
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/e-stale-partial.test.ts.snap +0 -62
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/f-cwd-variants.test.ts.snap +0 -34
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/g-windows-specifics.test.ts.snap +0 -49
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/j-path-gui-minimal.test.ts.snap +0 -12
- package/packages/shared/src/__tests__/bootstrap/families/a-electron.test.ts +0 -156
- package/packages/shared/src/__tests__/bootstrap/families/b-npm-global.test.ts +0 -157
- package/packages/shared/src/__tests__/bootstrap/families/c-dev-monorepo.test.ts +0 -102
- package/packages/shared/src/__tests__/bootstrap/families/d-overrides.test.ts +0 -76
- package/packages/shared/src/__tests__/bootstrap/families/e-stale-partial.test.ts +0 -94
- package/packages/shared/src/__tests__/bootstrap/families/f-cwd-variants.test.ts +0 -87
- package/packages/shared/src/__tests__/bootstrap/families/g-windows-specifics.test.ts +0 -143
- package/packages/shared/src/__tests__/bootstrap/families/h-home-drift.test.ts +0 -64
- package/packages/shared/src/__tests__/bootstrap/families/i-malformed-settings.test.ts +0 -77
- package/packages/shared/src/__tests__/bootstrap/families/index.ts +0 -19
- package/packages/shared/src/__tests__/bootstrap/families/j-path-gui-minimal.test.ts +0 -61
- package/packages/shared/src/__tests__/bootstrap/families/k-dashboard-absent.test.ts +0 -50
- package/packages/shared/src/__tests__/bootstrap/families/l-instance-coordination.test.ts +0 -272
- package/packages/shared/src/__tests__/bootstrap/fixtures/dev-monorepo.ts +0 -58
- package/packages/shared/src/__tests__/bootstrap/fixtures/electron-layout.ts +0 -84
- package/packages/shared/src/__tests__/bootstrap/fixtures/index.ts +0 -9
- package/packages/shared/src/__tests__/bootstrap/fixtures/managed-install.ts +0 -85
- package/packages/shared/src/__tests__/bootstrap/fixtures/npm-global-layout.ts +0 -122
- package/packages/shared/src/__tests__/bootstrap/fixtures/pi-versions.ts +0 -36
- package/packages/shared/src/__tests__/bootstrap/fixtures/settings-json.ts +0 -39
- package/packages/shared/src/__tests__/bootstrap/harness.smoke.test.ts +0 -220
- package/packages/shared/src/__tests__/bootstrap/harness.ts +0 -413
- package/packages/shared/src/__tests__/bootstrap/scenarios-skipped.ts +0 -125
- package/packages/shared/src/__tests__/bootstrap/scenarios.ts +0 -132
- package/packages/shared/src/__tests__/bootstrap-install-resolve-npm.test.ts +0 -72
- package/packages/shared/src/__tests__/install-managed-node-bootstrap-order.test.ts +0 -68
- package/packages/shared/src/__tests__/install-managed-node.test.ts +0 -192
- package/packages/shared/src/__tests__/installable-list.test.ts +0 -130
- package/packages/shared/src/__tests__/no-installable-list-in-bridge.test.ts +0 -52
- package/packages/shared/src/bootstrap-install.ts +0 -406
- package/packages/shared/src/installable-list.ts +0 -152
- package/packages/shared/src/launch-source-flag.ts +0 -14
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI primitive registry — public contract types.
|
|
3
|
+
*
|
|
4
|
+
* Defines the stable string keys plugins use to look up dashboard-provided
|
|
5
|
+
* UI primitives (components + helpers) at runtime via `useUiPrimitive(key)`.
|
|
6
|
+
* Each key has a typed contract in `UiPrimitiveMap`; the contract is the
|
|
7
|
+
* primitive's public API — adding optional props is non-breaking, renaming
|
|
8
|
+
* or removing required props is breaking.
|
|
9
|
+
*
|
|
10
|
+
* The dashboard's main.tsx is responsible for registering an implementation
|
|
11
|
+
* for every key in `UI_PRIMITIVE_KEYS`. The registry runtime itself lives in
|
|
12
|
+
* `@blackbelt-technology/dashboard-plugin-runtime`; this file is just types
|
|
13
|
+
* and key constants so it can be imported safely from any layer (no React
|
|
14
|
+
* runtime cost for non-renderer consumers).
|
|
15
|
+
*
|
|
16
|
+
* See change: add-plugin-ui-primitive-registry.
|
|
17
|
+
*/
|
|
18
|
+
import type { ComponentType, ReactNode } from "react";
|
|
19
|
+
import type { ModelInfo } from "../types.js";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Frozen set of stable string keys identifying registered UI primitives.
|
|
23
|
+
*
|
|
24
|
+
* Keys are namespaced under `ui:` so they're easy to grep and so future
|
|
25
|
+
* registries (e.g. server-side or extension-specific) won't collide.
|
|
26
|
+
*
|
|
27
|
+
* Adding a key is non-breaking. Renaming or removing a key requires a
|
|
28
|
+
* deprecation cycle (register both keys for one minor release with a
|
|
29
|
+
* warning, then remove).
|
|
30
|
+
*/
|
|
31
|
+
export const UI_PRIMITIVE_KEYS = {
|
|
32
|
+
/** Reusable card container with status-colored border, header, optional stats line. */
|
|
33
|
+
agentCard: "ui:agent-card",
|
|
34
|
+
/** Markdown renderer with code highlighting, math, mermaid, tables, lightbox. */
|
|
35
|
+
markdownContent: "ui:markdown-content",
|
|
36
|
+
/** Modal yes/no confirmation dialog. */
|
|
37
|
+
confirmDialog: "ui:confirm-dialog",
|
|
38
|
+
/** Base modal portal: renders children at `document.body` with body-scroll lock. */
|
|
39
|
+
dialogPortal: "ui:dialog-portal",
|
|
40
|
+
/** Typeahead-filtered selection dialog with keyboard navigation. */
|
|
41
|
+
searchableSelectDialog: "ui:searchable-select-dialog",
|
|
42
|
+
/** Zoom in/out/reset button group, paired with a zoom-pan controller. */
|
|
43
|
+
zoomControls: "ui:zoom-controls",
|
|
44
|
+
/** Format a token count as a human-readable string (e.g. 12000 → "12k"). */
|
|
45
|
+
formatTokens: "ui:format-tokens",
|
|
46
|
+
/** Format a duration in milliseconds as a human-readable string. */
|
|
47
|
+
formatDuration: "ui:format-duration",
|
|
48
|
+
/** Horizontal row of action buttons; used by intent-driven plugin contributions. */
|
|
49
|
+
actionList: "ui:action-list",
|
|
50
|
+
/** Status pill (badge) with state-tinted background + optional icon. */
|
|
51
|
+
statusPill: "ui:status-pill",
|
|
52
|
+
/** Model picker with provider filter, typeahead, keyboard navigation, pending-state. */
|
|
53
|
+
modelSelector: "ui:model-selector",
|
|
54
|
+
/** Floating panel anchored to a DOM element; dismisses on outside-click/Esc. */
|
|
55
|
+
popover: "ui:popover",
|
|
56
|
+
/** Rich tool-call card matching the main chat view (per-tool renderers,
|
|
57
|
+
* collapsible output, status icon). Used by plugin timelines that want
|
|
58
|
+
* parity with the dashboard chat tool rendering. */
|
|
59
|
+
toolCallStep: "ui:tool-call-step",
|
|
60
|
+
/** Reasoning / thinking block with collapsible content + elapsed-time badge.
|
|
61
|
+
* Matches the main chat view's thinking rendering. */
|
|
62
|
+
thinkingBlock: "ui:thinking-block",
|
|
63
|
+
} as const;
|
|
64
|
+
|
|
65
|
+
/** Union of all valid UI primitive keys (literal-string narrowed). */
|
|
66
|
+
export type UiPrimitiveKey = (typeof UI_PRIMITIVE_KEYS)[keyof typeof UI_PRIMITIVE_KEYS];
|
|
67
|
+
|
|
68
|
+
// ── Public contract types ──────────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Public prop signature for the agent-card primitive.
|
|
72
|
+
*
|
|
73
|
+
* Mirrors `AgentCardShell` in client-utils. Optional fields stay optional;
|
|
74
|
+
* required fields stay required. Adding a new optional prop is non-breaking;
|
|
75
|
+
* renaming `name` to `title` would be breaking.
|
|
76
|
+
*/
|
|
77
|
+
export interface UiAgentCardProps {
|
|
78
|
+
name: string;
|
|
79
|
+
status: string;
|
|
80
|
+
headerRight?: ReactNode;
|
|
81
|
+
stats?: ReactNode;
|
|
82
|
+
onClick?: () => void;
|
|
83
|
+
selected?: boolean;
|
|
84
|
+
children?: ReactNode;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Public prop signature for the markdown-content primitive. */
|
|
88
|
+
export interface UiMarkdownContentProps {
|
|
89
|
+
content: string;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Public prop signature for the confirm-dialog primitive. */
|
|
93
|
+
export interface UiConfirmDialogProps {
|
|
94
|
+
message: string;
|
|
95
|
+
confirmLabel?: string;
|
|
96
|
+
onConfirm: () => void;
|
|
97
|
+
onCancel: () => void;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Public prop signature for the dialog-portal primitive. */
|
|
101
|
+
export interface UiDialogPortalProps {
|
|
102
|
+
children: ReactNode;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* One option in a searchable-select-dialog. Mirrors `SelectOption` in
|
|
107
|
+
* client-utils so the existing component implementation is contract-compatible
|
|
108
|
+
* without adapter shims.
|
|
109
|
+
*/
|
|
110
|
+
export interface UiSelectOption {
|
|
111
|
+
value: string;
|
|
112
|
+
label: string;
|
|
113
|
+
description?: string;
|
|
114
|
+
badge?: string;
|
|
115
|
+
badgeColor?: string;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Public prop signature for the searchable-select-dialog primitive. */
|
|
119
|
+
export interface UiSearchableSelectDialogProps {
|
|
120
|
+
title: string;
|
|
121
|
+
options: UiSelectOption[];
|
|
122
|
+
onSelect: (value: string) => void;
|
|
123
|
+
onCancel: () => void;
|
|
124
|
+
placeholder?: string;
|
|
125
|
+
emptyMessage?: string;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Public prop signature for the zoom-controls primitive. */
|
|
129
|
+
export interface UiZoomControlsProps {
|
|
130
|
+
onZoomIn: () => void;
|
|
131
|
+
onZoomOut: () => void;
|
|
132
|
+
onReset: () => void;
|
|
133
|
+
scale: number;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** A single entry in an action-list primitive. */
|
|
137
|
+
export interface UiActionListItem {
|
|
138
|
+
/** Display label for the action button. */
|
|
139
|
+
label: string;
|
|
140
|
+
/** Optional MDI icon key from `@mdi/js` (e.g. `mdiPlay`). */
|
|
141
|
+
icon?: string;
|
|
142
|
+
/** Optional tooltip on hover. */
|
|
143
|
+
tooltip?: string;
|
|
144
|
+
/** Optional click handler (wired by IntentRenderer from action descriptor). */
|
|
145
|
+
onClick?: () => void;
|
|
146
|
+
/** Optional disabled flag. */
|
|
147
|
+
disabled?: boolean;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Public prop signature for the action-list primitive. */
|
|
151
|
+
export interface UiActionListProps {
|
|
152
|
+
actions: UiActionListItem[];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Stable state tokens for the status-pill primitive. */
|
|
156
|
+
export type UiStatusPillState =
|
|
157
|
+
| "running"
|
|
158
|
+
| "success"
|
|
159
|
+
| "error"
|
|
160
|
+
| "info"
|
|
161
|
+
| "warn"
|
|
162
|
+
| "muted";
|
|
163
|
+
|
|
164
|
+
/** Public prop signature for the status-pill primitive. */
|
|
165
|
+
export interface UiStatusPillProps {
|
|
166
|
+
state: UiStatusPillState;
|
|
167
|
+
text: string;
|
|
168
|
+
/** Optional MDI icon key from `@mdi/js`. */
|
|
169
|
+
icon?: string;
|
|
170
|
+
/** Optional tooltip on hover. */
|
|
171
|
+
tooltip?: string;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Public prop signature for the model-selector primitive.
|
|
176
|
+
*
|
|
177
|
+
* Mirrors the existing `ModelSelector` component (`packages/client/src/
|
|
178
|
+
* components/ModelSelector.tsx`) used by `StatusBar`. The primitive exposes
|
|
179
|
+
* the same model-picker UX (provider filter, typeahead, keyboard navigation,
|
|
180
|
+
* pending-state with 10 s timeout) to plugins without forcing them to depend
|
|
181
|
+
* on the client package.
|
|
182
|
+
*
|
|
183
|
+
* Role/preset props that historically existed on `ModelSelector` are NOT part
|
|
184
|
+
* of this contract — role management is a host concern (see
|
|
185
|
+
* `BuiltInRolesSettings` in roles-plugin) layered on top.
|
|
186
|
+
*/
|
|
187
|
+
export interface UiModelSelectorProps {
|
|
188
|
+
/** Currently-selected model label in `"<provider>/<id>"` form, or undefined. */
|
|
189
|
+
current?: string;
|
|
190
|
+
/** Available models. When undefined, the primitive renders non-interactive text. */
|
|
191
|
+
models?: ModelInfo[];
|
|
192
|
+
/** Invoked with the full `"<provider>/<id>"` string of the chosen model. */
|
|
193
|
+
onSelect: (modelLabel: string) => void;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Public prop signature for the popover primitive.
|
|
198
|
+
*
|
|
199
|
+
* A floating panel positioned relative to `anchorEl`. Dismisses on Esc or
|
|
200
|
+
* clicks outside the popover (and outside the anchor). The host owns the
|
|
201
|
+
* open/closed state; rendering the primitive at all is the "open" signal.
|
|
202
|
+
*
|
|
203
|
+
* Differs from `dialogPortal`: no scroll lock, no modal backdrop, position
|
|
204
|
+
* is computed from the anchor's viewport rect (not centered).
|
|
205
|
+
*/
|
|
206
|
+
export interface UiPopoverProps {
|
|
207
|
+
/** DOM element the popover is anchored to. */
|
|
208
|
+
anchorEl: HTMLElement;
|
|
209
|
+
/** Invoked when the user clicks outside or presses Esc. */
|
|
210
|
+
onDismiss: () => void;
|
|
211
|
+
/** Popover content (any React tree). */
|
|
212
|
+
children: ReactNode;
|
|
213
|
+
/** Optional gap (px) between anchor and popover edge. Default 6. */
|
|
214
|
+
offset?: number;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// ── The map ────────────────────────────────────────────────────────────────
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Type-level mapping from each `UI_PRIMITIVE_KEYS` value to its public
|
|
221
|
+
* implementation contract.
|
|
222
|
+
*
|
|
223
|
+
* Component contracts use `ComponentType<P>` (React functional or class
|
|
224
|
+
* component); helper contracts use plain function signatures. The runtime
|
|
225
|
+
* registry uses this map to type-check both registration and lookup.
|
|
226
|
+
*
|
|
227
|
+
* Adding a key: extend `UI_PRIMITIVE_KEYS` AND add the corresponding entry
|
|
228
|
+
* here. TypeScript will fail builds that reference the new key without
|
|
229
|
+
* matching registration in main.tsx.
|
|
230
|
+
*/
|
|
231
|
+
export interface UiPrimitiveMap {
|
|
232
|
+
"ui:agent-card": ComponentType<UiAgentCardProps>;
|
|
233
|
+
"ui:markdown-content": ComponentType<UiMarkdownContentProps>;
|
|
234
|
+
"ui:confirm-dialog": ComponentType<UiConfirmDialogProps>;
|
|
235
|
+
"ui:dialog-portal": ComponentType<UiDialogPortalProps>;
|
|
236
|
+
"ui:searchable-select-dialog": ComponentType<UiSearchableSelectDialogProps>;
|
|
237
|
+
"ui:zoom-controls": ComponentType<UiZoomControlsProps>;
|
|
238
|
+
"ui:format-tokens": (n: number) => string;
|
|
239
|
+
"ui:format-duration": (ms: number) => string;
|
|
240
|
+
"ui:action-list": ComponentType<UiActionListProps>;
|
|
241
|
+
"ui:status-pill": ComponentType<UiStatusPillProps>;
|
|
242
|
+
"ui:model-selector": ComponentType<UiModelSelectorProps>;
|
|
243
|
+
"ui:popover": ComponentType<UiPopoverProps>;
|
|
244
|
+
"ui:tool-call-step": ComponentType<UiToolCallStepProps>;
|
|
245
|
+
"ui:thinking-block": ComponentType<UiThinkingBlockProps>;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Image attachment shape carried by tool-call rendering. Structurally
|
|
250
|
+
* compatible with the shell's `ChatImage` and the shared
|
|
251
|
+
* `ImageContent` (the `type: "image"` field is optional here so plugins
|
|
252
|
+
* don't need to manufacture it).
|
|
253
|
+
*/
|
|
254
|
+
export interface UiToolCallImage {
|
|
255
|
+
data: string;
|
|
256
|
+
mimeType: string;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Public prop signature for the rich tool-call card. The shell registers
|
|
261
|
+
* an implementation backed by its `ToolCallStep` component (which fans
|
|
262
|
+
* out to per-tool renderers — Bash, Read, Edit, Write, Agent, AskUser,
|
|
263
|
+
* Generic). Optional fields stay optional; required fields stay required.
|
|
264
|
+
*/
|
|
265
|
+
export interface UiToolCallStepProps {
|
|
266
|
+
toolName: string;
|
|
267
|
+
/** Identifier used by per-tool renderers to scope click handlers; synthetic ids are fine. */
|
|
268
|
+
toolCallId: string;
|
|
269
|
+
args?: Record<string, unknown>;
|
|
270
|
+
status: "running" | "complete" | "error";
|
|
271
|
+
result?: string;
|
|
272
|
+
images?: UiToolCallImage[];
|
|
273
|
+
toolDetails?: Record<string, unknown>;
|
|
274
|
+
startedAt?: number;
|
|
275
|
+
duration?: number;
|
|
276
|
+
/** Session id passed through to per-tool renderers that need session scope. */
|
|
277
|
+
sessionId?: string;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/** Public prop signature for the thinking-block primitive. */
|
|
281
|
+
export interface UiThinkingBlockProps {
|
|
282
|
+
content: string;
|
|
283
|
+
isStreaming?: boolean;
|
|
284
|
+
defaultExpanded?: boolean;
|
|
285
|
+
startedAt?: number;
|
|
286
|
+
duration?: number;
|
|
287
|
+
}
|
|
@@ -31,3 +31,25 @@ export function parseDashboardStarter(
|
|
|
31
31
|
);
|
|
32
32
|
return "Standalone";
|
|
33
33
|
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* `LaunchSource` — lowercase alias of `DashboardStarter` exposed on
|
|
37
|
+
* `/api/health` as a stable client contract for arm-aware gating
|
|
38
|
+
* (notably hiding pi-core update UI when running under Electron, since
|
|
39
|
+
* the bundled `node_modules/` is read-only under the .app).
|
|
40
|
+
*
|
|
41
|
+
* Detection follows `parseDashboardStarter` then lowercases:
|
|
42
|
+
* DASHBOARD_STARTER=Electron → "electron"
|
|
43
|
+
* DASHBOARD_STARTER=Bridge → "bridge"
|
|
44
|
+
* else → "standalone"
|
|
45
|
+
*
|
|
46
|
+
* See change: eliminate-electron-runtime-install (task 3.2).
|
|
47
|
+
*/
|
|
48
|
+
export type LaunchSource = "electron" | "standalone" | "bridge";
|
|
49
|
+
|
|
50
|
+
export function parseLaunchSource(
|
|
51
|
+
env: Record<string, string | undefined>,
|
|
52
|
+
): LaunchSource {
|
|
53
|
+
const s = parseDashboardStarter(env);
|
|
54
|
+
return s === "Electron" ? "electron" : s === "Bridge" ? "bridge" : "standalone";
|
|
55
|
+
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* See change: doctor-rich-output (proposal.md, design.md).
|
|
12
12
|
*/
|
|
13
13
|
import { execSync } from "./platform/exec.js";
|
|
14
|
-
import { existsSync, readFileSync, statSync, renameSync, appendFileSync } from "node:fs";
|
|
14
|
+
import { existsSync, readFileSync, statSync, renameSync, appendFileSync, rmSync } from "node:fs";
|
|
15
15
|
import path from "node:path";
|
|
16
16
|
|
|
17
17
|
// ─── Types ─────────────────────────────────────────────────────────────
|
|
@@ -290,8 +290,7 @@ function rotateDoctorLogIfNeeded(logPath: string): void {
|
|
|
290
290
|
} catch {
|
|
291
291
|
// try once more after best-effort cleanup
|
|
292
292
|
try {
|
|
293
|
-
|
|
294
|
-
if (existsSync(rotated)) fs.rmSync(rotated, { force: true });
|
|
293
|
+
if (existsSync(rotated)) rmSync(rotated, { force: true });
|
|
295
294
|
renameSync(logPath, rotated);
|
|
296
295
|
} catch {
|
|
297
296
|
// give up silently
|
|
@@ -321,7 +320,7 @@ export const SECTION_OF: Record<string, DoctorSection> = {
|
|
|
321
320
|
// server
|
|
322
321
|
"Dashboard server code": "server",
|
|
323
322
|
"Offline packages bundle": "server",
|
|
324
|
-
"TypeScript loader
|
|
323
|
+
"TypeScript loader": "server",
|
|
325
324
|
"Dashboard server": "server",
|
|
326
325
|
"Server starter": "server",
|
|
327
326
|
"Installable list": "server",
|
|
@@ -399,10 +398,10 @@ export const SUGGESTIONS: Record<string, SuggestionFn> = {
|
|
|
399
398
|
status === "ok"
|
|
400
399
|
? undefined
|
|
401
400
|
: "Offline packages bundle absent. First-run install will require network access to `registry.npmjs.org`.",
|
|
402
|
-
"TypeScript loader
|
|
401
|
+
"TypeScript loader": (status) =>
|
|
403
402
|
status === "ok"
|
|
404
403
|
? undefined
|
|
405
|
-
: "
|
|
404
|
+
: "No TypeScript loader (jiti or tsx) found. Required to run the dashboard server. Run the setup wizard (Help → Setup).",
|
|
406
405
|
"Dashboard server": (status) =>
|
|
407
406
|
status === "ok"
|
|
408
407
|
? undefined
|
|
@@ -548,43 +547,66 @@ export async function runSharedChecks(deps: SharedChecksDeps): Promise<DoctorChe
|
|
|
548
547
|
}),
|
|
549
548
|
);
|
|
550
549
|
|
|
551
|
-
//
|
|
550
|
+
// TypeScript loader (jiti preferred; tsx accepted as fallback)
|
|
551
|
+
// The dashboard server runs via jiti by default (see shared/server-launcher.ts
|
|
552
|
+
// resolveJiti). tsx was the legacy choice and is still accepted if jiti is
|
|
553
|
+
// unavailable. The check passes when EITHER loader is resolvable so users
|
|
554
|
+
// running on jiti don't see a spurious error.
|
|
552
555
|
checks.push(
|
|
553
|
-
await safeCheck("TypeScript loader
|
|
556
|
+
await safeCheck("TypeScript loader", "server", () => {
|
|
557
|
+
const managedJitiPkg = path.join(managedDir, "node_modules", "jiti", "package.json");
|
|
554
558
|
const managedTsxPkg = path.join(managedDir, "node_modules", "tsx", "package.json");
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
559
|
+
|
|
560
|
+
function readVersion(pkgPath: string): string | null {
|
|
561
|
+
try {
|
|
562
|
+
if (!existsSync(pkgPath)) return null;
|
|
563
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
564
|
+
return pkg.version || null;
|
|
565
|
+
} catch {
|
|
566
|
+
return null;
|
|
560
567
|
}
|
|
561
|
-
} catch {
|
|
562
|
-
// ignore
|
|
563
568
|
}
|
|
569
|
+
const jitiVersion = readVersion(managedJitiPkg);
|
|
570
|
+
const tsxVersion = readVersion(managedTsxPkg);
|
|
571
|
+
|
|
564
572
|
let systemTsx: string | null = null;
|
|
565
573
|
const lookupCmd = process.platform === "win32" ? "where tsx" : "which tsx"; // platform-branch-ok: localised PATH-lookup primitive
|
|
566
574
|
const lookup = safeExec(lookupCmd, { timeoutMs: 5000 });
|
|
567
575
|
if (lookup.ok) {
|
|
568
576
|
systemTsx = lookup.stdout.trim().split("\n")[0] || null;
|
|
569
577
|
}
|
|
570
|
-
|
|
571
|
-
if (
|
|
578
|
+
|
|
579
|
+
if (jitiVersion) {
|
|
572
580
|
return {
|
|
573
|
-
name: "TypeScript loader
|
|
581
|
+
name: "TypeScript loader",
|
|
574
582
|
section: "server",
|
|
575
|
-
status: "
|
|
576
|
-
message:
|
|
577
|
-
|
|
578
|
-
|
|
583
|
+
status: "ok",
|
|
584
|
+
message: `jiti v${jitiVersion} (managed) at ${path.dirname(managedJitiPkg)}`,
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
if (tsxVersion) {
|
|
588
|
+
return {
|
|
589
|
+
name: "TypeScript loader",
|
|
590
|
+
section: "server",
|
|
591
|
+
status: "ok",
|
|
592
|
+
message: `tsx v${tsxVersion} (managed) at ${path.dirname(managedTsxPkg)}`,
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
if (systemTsx) {
|
|
596
|
+
return {
|
|
597
|
+
name: "TypeScript loader",
|
|
598
|
+
section: "server",
|
|
599
|
+
status: "ok",
|
|
600
|
+
message: `tsx (system) at ${systemTsx}`,
|
|
579
601
|
};
|
|
580
602
|
}
|
|
581
603
|
return {
|
|
582
|
-
name: "TypeScript loader
|
|
604
|
+
name: "TypeScript loader",
|
|
583
605
|
section: "server",
|
|
584
|
-
status: "
|
|
585
|
-
message:
|
|
586
|
-
|
|
587
|
-
|
|
606
|
+
status: "error",
|
|
607
|
+
message: "Not found — required to run the dashboard server",
|
|
608
|
+
detail: `Looked under ${managedJitiPkg}, ${managedTsxPkg}, and on PATH`,
|
|
609
|
+
fixable: true,
|
|
588
610
|
};
|
|
589
611
|
}),
|
|
590
612
|
);
|
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
* LaunchSource — discriminated union describing how the dashboard server was (or should be) started.
|
|
3
3
|
*
|
|
4
4
|
* "attach" — a server is already running; Electron attaches to it.
|
|
5
|
-
* "
|
|
6
|
-
* "
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* "bundled" — bundled Electron resources provide the server (immutable .app/Contents/Resources tree).
|
|
6
|
+
* "devMonorepo" — running from a checked-out monorepo (dev workflow only, gated by ELECTRON_DEV).
|
|
7
|
+
*
|
|
8
|
+
* Pre-R3 layouts (`piExtension`, `npmGlobal`, `extracted`) are removed:
|
|
9
|
+
* the immutable bundle is the only runtime layout under packaged Electron.
|
|
10
|
+
* See change: eliminate-electron-runtime-install.
|
|
9
11
|
*/
|
|
10
12
|
|
|
11
|
-
export type SourceKind = "attach" | "
|
|
13
|
+
export type SourceKind = "attach" | "bundled" | "devMonorepo";
|
|
12
14
|
|
|
13
15
|
export type LaunchSource =
|
|
14
16
|
| { kind: "attach"; url: string; starter: "Bridge" | "Standalone" | "Electron" }
|
|
15
|
-
| { kind: "
|
|
16
|
-
| { kind: "
|
|
17
|
-
| { kind: "npmGlobal"; cliPath: string; cwd: string }
|
|
18
|
-
| { kind: "extracted"; cliPath: string; cwd: string; didExtract?: boolean };
|
|
17
|
+
| { kind: "bundled"; cliPath: string; cwd: string }
|
|
18
|
+
| { kind: "devMonorepo"; cliPath: string; cwd: string };
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detect the legacy `~/.pi-dashboard/` install directory left behind from
|
|
3
|
+
* pre-R3 versions where the Electron app (and the standalone bootstrap
|
|
4
|
+
* orchestrator) installed pi/openspec/tsx at runtime into a user-writable
|
|
5
|
+
* directory.
|
|
6
|
+
*
|
|
7
|
+
* Under the immutable-bundle architecture (change:
|
|
8
|
+
* eliminate-electron-runtime-install) nothing reads from or writes to
|
|
9
|
+
* this directory on the Electron arm. This module exists solely so the
|
|
10
|
+
* Doctor UI can surface an advisory row, and the server CLI can log a
|
|
11
|
+
* one-time hint, telling the user the directory is safe to delete.
|
|
12
|
+
*
|
|
13
|
+
* NEVER move runtime install logic back into this directory. If you find
|
|
14
|
+
* yourself reaching for `~/.pi-dashboard/`, you are working against R3.
|
|
15
|
+
*/
|
|
16
|
+
import fs from "node:fs";
|
|
17
|
+
import path from "node:path";
|
|
18
|
+
import os from "node:os";
|
|
19
|
+
|
|
20
|
+
export type LegacyManagedDir =
|
|
21
|
+
| { present: false }
|
|
22
|
+
| { present: true; path: string; pkgCount: number; sizeMb: number };
|
|
23
|
+
|
|
24
|
+
export interface DetectDeps {
|
|
25
|
+
/** Override HOME for tests. */
|
|
26
|
+
homedir?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const LEGACY_DIRNAME = ".pi-" + "dashboard"; // split literal so the no-managed-dir lint stays clean
|
|
30
|
+
|
|
31
|
+
function getLegacyDirPath(env?: DetectDeps): string {
|
|
32
|
+
return path.join(env?.homedir ?? os.homedir(), LEGACY_DIRNAME);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Sum file sizes under a directory tree, capped to avoid pathological scans. */
|
|
36
|
+
function dirSizeBytes(dir: string, cap = 500 * 1024 * 1024): number {
|
|
37
|
+
let total = 0;
|
|
38
|
+
const stack: string[] = [dir];
|
|
39
|
+
while (stack.length > 0 && total < cap) {
|
|
40
|
+
const cur = stack.pop()!;
|
|
41
|
+
let entries: fs.Dirent[];
|
|
42
|
+
try {
|
|
43
|
+
entries = fs.readdirSync(cur, { withFileTypes: true });
|
|
44
|
+
} catch {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
for (const e of entries) {
|
|
48
|
+
const full = path.join(cur, e.name);
|
|
49
|
+
try {
|
|
50
|
+
if (e.isSymbolicLink()) continue;
|
|
51
|
+
if (e.isDirectory()) {
|
|
52
|
+
stack.push(full);
|
|
53
|
+
} else if (e.isFile()) {
|
|
54
|
+
const st = fs.statSync(full);
|
|
55
|
+
total += st.size;
|
|
56
|
+
if (total >= cap) return cap;
|
|
57
|
+
}
|
|
58
|
+
} catch {
|
|
59
|
+
/* skip unreadable */
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return total;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function countDirectChildren(dir: string): number {
|
|
67
|
+
try {
|
|
68
|
+
return fs.readdirSync(dir).length;
|
|
69
|
+
} catch {
|
|
70
|
+
return 0;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Detect whether the legacy `~/.pi-dashboard/` directory is present.
|
|
76
|
+
* Returns `{ present: false }` when missing. When present, returns a
|
|
77
|
+
* `pkgCount` (entries directly under `node_modules/`, 0 if missing) and
|
|
78
|
+
* `sizeMb` (recursive byte sum, capped at 500 MB).
|
|
79
|
+
*/
|
|
80
|
+
export function detectLegacyManagedDir(deps: DetectDeps = {}): LegacyManagedDir {
|
|
81
|
+
const dir = getLegacyDirPath(deps);
|
|
82
|
+
try {
|
|
83
|
+
const st = fs.statSync(dir);
|
|
84
|
+
if (!st.isDirectory()) return { present: false };
|
|
85
|
+
} catch {
|
|
86
|
+
return { present: false };
|
|
87
|
+
}
|
|
88
|
+
const nodeModules = path.join(dir, "node_modules");
|
|
89
|
+
const pkgCount = countDirectChildren(nodeModules);
|
|
90
|
+
const sizeMb = Math.round(dirSizeBytes(dir) / (1024 * 1024));
|
|
91
|
+
return { present: true, path: dir, pkgCount, sizeMb };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Path-only accessor for callers that want to display the path without scanning. */
|
|
95
|
+
export function getLegacyManagedDirPath(deps: DetectDeps = {}): string {
|
|
96
|
+
return getLegacyDirPath(deps);
|
|
97
|
+
}
|
|
@@ -5,8 +5,11 @@
|
|
|
5
5
|
import { Bonjour, type Service, type Browser } from "bonjour-service";
|
|
6
6
|
import os from "node:os";
|
|
7
7
|
import { EventEmitter } from "node:events";
|
|
8
|
+
import { createRequire } from "node:module";
|
|
8
9
|
import { isDashboardRunning } from "./server-identity.js";
|
|
9
10
|
|
|
11
|
+
const _require = createRequire(import.meta.url);
|
|
12
|
+
|
|
10
13
|
const SERVICE_TYPE = "pi-dashboard";
|
|
11
14
|
|
|
12
15
|
export interface DiscoveredServer {
|
|
@@ -43,7 +46,7 @@ export function advertiseDashboard(port: number, piPort: number): void {
|
|
|
43
46
|
const bonjour = getBonjour();
|
|
44
47
|
const pkg = { version: "0.0.0" }; // Will be replaced by actual version
|
|
45
48
|
try {
|
|
46
|
-
const pkgJson =
|
|
49
|
+
const pkgJson = _require("../../package.json");
|
|
47
50
|
pkg.version = pkgJson.version ?? "0.0.0";
|
|
48
51
|
} catch { /* ignore */ }
|
|
49
52
|
|