4runr-os 1.0.23 → 1.0.36
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/dist/collectors/collector_assets.d.ts +19 -0
- package/dist/collectors/collector_assets.d.ts.map +1 -0
- package/dist/collectors/collector_assets.js +72 -0
- package/dist/collectors/collector_assets.js.map +1 -0
- package/dist/collectors/collector_capabilities.d.ts +19 -0
- package/dist/collectors/collector_capabilities.d.ts.map +1 -0
- package/dist/collectors/collector_capabilities.js +61 -0
- package/dist/collectors/collector_capabilities.js.map +1 -0
- package/dist/collectors/collector_feed.d.ts +30 -0
- package/dist/collectors/collector_feed.d.ts.map +1 -0
- package/dist/collectors/collector_feed.js +78 -0
- package/dist/collectors/collector_feed.js.map +1 -0
- package/dist/collectors/collector_geo.d.ts +12 -0
- package/dist/collectors/collector_geo.d.ts.map +1 -0
- package/dist/collectors/collector_geo.js +248 -0
- package/dist/collectors/collector_geo.js.map +1 -0
- package/dist/collectors/collector_network.d.ts +22 -0
- package/dist/collectors/collector_network.d.ts.map +1 -0
- package/dist/collectors/collector_network.js +83 -0
- package/dist/collectors/collector_network.js.map +1 -0
- package/dist/collectors/collector_posture.d.ts +25 -0
- package/dist/collectors/collector_posture.d.ts.map +1 -0
- package/dist/collectors/collector_posture.js +207 -0
- package/dist/collectors/collector_posture.js.map +1 -0
- package/dist/collectors/collector_resources.d.ts +21 -0
- package/dist/collectors/collector_resources.d.ts.map +1 -0
- package/dist/collectors/collector_resources.js +87 -0
- package/dist/collectors/collector_resources.js.map +1 -0
- package/dist/collectors/helpers.d.ts +21 -0
- package/dist/collectors/helpers.d.ts.map +1 -0
- package/dist/collectors/helpers.js +49 -0
- package/dist/collectors/helpers.js.map +1 -0
- package/dist/core/collectorRegistry.d.ts +43 -0
- package/dist/core/collectorRegistry.d.ts.map +1 -0
- package/dist/core/collectorRegistry.js +76 -0
- package/dist/core/collectorRegistry.js.map +1 -0
- package/dist/core/constants.d.ts +25 -0
- package/dist/core/constants.d.ts.map +1 -0
- package/dist/core/constants.js +33 -0
- package/dist/core/constants.js.map +1 -0
- package/dist/core/eventBus.d.ts +36 -0
- package/dist/core/eventBus.d.ts.map +1 -0
- package/dist/core/eventBus.js +52 -0
- package/dist/core/eventBus.js.map +1 -0
- package/dist/core/types.d.ts +128 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +6 -0
- package/dist/core/types.js.map +1 -0
- package/dist/events.d.ts +35 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +89 -0
- package/dist/events.js.map +1 -0
- package/dist/index.js +301 -36
- package/dist/index.js.map +1 -1
- package/dist/models.d.ts +74 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +7 -0
- package/dist/models.js.map +1 -0
- package/dist/state/getCollectorResults.d.ts +18 -0
- package/dist/state/getCollectorResults.d.ts.map +1 -0
- package/dist/state/getCollectorResults.js +25 -0
- package/dist/state/getCollectorResults.js.map +1 -0
- package/dist/state/historyStore.d.ts +35 -0
- package/dist/state/historyStore.d.ts.map +1 -0
- package/dist/state/historyStore.js +58 -0
- package/dist/state/historyStore.js.map +1 -0
- package/dist/state/uiStateBuilder.d.ts +12 -0
- package/dist/state/uiStateBuilder.d.ts.map +1 -0
- package/dist/state/uiStateBuilder.js +169 -0
- package/dist/state/uiStateBuilder.js.map +1 -0
- package/dist/state/uiStateScheduler.d.ts +43 -0
- package/dist/state/uiStateScheduler.d.ts.map +1 -0
- package/dist/state/uiStateScheduler.js +118 -0
- package/dist/state/uiStateScheduler.js.map +1 -0
- package/dist/store.d.ts +3 -1
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +2 -0
- package/dist/store.js.map +1 -1
- package/dist/system-state.d.ts +96 -0
- package/dist/system-state.d.ts.map +1 -0
- package/dist/system-state.js +273 -0
- package/dist/system-state.js.map +1 -0
- package/dist/ui/boot/sequence.d.ts +10 -0
- package/dist/ui/boot/sequence.d.ts.map +1 -0
- package/dist/ui/boot/sequence.js +173 -0
- package/dist/ui/boot/sequence.js.map +1 -0
- package/dist/ui/constraints/layoutSpec.d.ts +47 -0
- package/dist/ui/constraints/layoutSpec.d.ts.map +1 -0
- package/dist/ui/constraints/layoutSpec.js +60 -0
- package/dist/ui/constraints/layoutSpec.js.map +1 -0
- package/dist/ui/constraints/unknownHandling.d.ts +29 -0
- package/dist/ui/constraints/unknownHandling.d.ts.map +1 -0
- package/dist/ui/constraints/unknownHandling.js +60 -0
- package/dist/ui/constraints/unknownHandling.js.map +1 -0
- package/dist/ui/drilldowns/feed.d.ts +11 -0
- package/dist/ui/drilldowns/feed.d.ts.map +1 -0
- package/dist/ui/drilldowns/feed.js +68 -0
- package/dist/ui/drilldowns/feed.js.map +1 -0
- package/dist/ui/drilldowns/index.d.ts +7 -0
- package/dist/ui/drilldowns/index.d.ts.map +1 -0
- package/dist/ui/drilldowns/index.js +8 -0
- package/dist/ui/drilldowns/index.js.map +1 -0
- package/dist/ui/drilldowns/posture.d.ts +11 -0
- package/dist/ui/drilldowns/posture.d.ts.map +1 -0
- package/dist/ui/drilldowns/posture.js +74 -0
- package/dist/ui/drilldowns/posture.js.map +1 -0
- package/dist/ui/intelligence-posture-view.d.ts +22 -0
- package/dist/ui/intelligence-posture-view.d.ts.map +1 -0
- package/dist/ui/intelligence-posture-view.js +169 -0
- package/dist/ui/intelligence-posture-view.js.map +1 -0
- package/dist/ui/navigation/keymaps.d.ts +26 -0
- package/dist/ui/navigation/keymaps.d.ts.map +1 -0
- package/dist/ui/navigation/keymaps.js +135 -0
- package/dist/ui/navigation/keymaps.js.map +1 -0
- package/dist/ui/navigation/palette.d.ts +10 -0
- package/dist/ui/navigation/palette.d.ts.map +1 -0
- package/dist/ui/navigation/palette.js +133 -0
- package/dist/ui/navigation/palette.js.map +1 -0
- package/dist/ui/navigation/state.d.ts +47 -0
- package/dist/ui/navigation/state.d.ts.map +1 -0
- package/dist/ui/navigation/state.js +84 -0
- package/dist/ui/navigation/state.js.map +1 -0
- package/dist/ui/navigation/types.d.ts +38 -0
- package/dist/ui/navigation/types.d.ts.map +1 -0
- package/dist/ui/navigation/types.js +36 -0
- package/dist/ui/navigation/types.js.map +1 -0
- package/dist/ui/panels/active-assets.d.ts +12 -0
- package/dist/ui/panels/active-assets.d.ts.map +1 -0
- package/dist/ui/panels/active-assets.js +83 -0
- package/dist/ui/panels/active-assets.js.map +1 -0
- package/dist/ui/panels/capability-flags.d.ts +12 -0
- package/dist/ui/panels/capability-flags.d.ts.map +1 -0
- package/dist/ui/panels/capability-flags.js +59 -0
- package/dist/ui/panels/capability-flags.js.map +1 -0
- package/dist/ui/panels/command-surface.d.ts +12 -0
- package/dist/ui/panels/command-surface.d.ts.map +1 -0
- package/dist/ui/panels/command-surface.js +55 -0
- package/dist/ui/panels/command-surface.js.map +1 -0
- package/dist/ui/panels/network-origin.d.ts +12 -0
- package/dist/ui/panels/network-origin.d.ts.map +1 -0
- package/dist/ui/panels/network-origin.js +79 -0
- package/dist/ui/panels/network-origin.js.map +1 -0
- package/dist/ui/panels/operations-feed.d.ts +12 -0
- package/dist/ui/panels/operations-feed.d.ts.map +1 -0
- package/dist/ui/panels/operations-feed.js +90 -0
- package/dist/ui/panels/operations-feed.js.map +1 -0
- package/dist/ui/panels/posture.d.ts +12 -0
- package/dist/ui/panels/posture.d.ts.map +1 -0
- package/dist/ui/panels/posture.js +84 -0
- package/dist/ui/panels/posture.js.map +1 -0
- package/dist/ui/panels/resources.d.ts +11 -0
- package/dist/ui/panels/resources.d.ts.map +1 -0
- package/dist/ui/panels/resources.js +88 -0
- package/dist/ui/panels/resources.js.map +1 -0
- package/dist/ui/primitives/Panel.d.ts +25 -0
- package/dist/ui/primitives/Panel.d.ts.map +1 -0
- package/dist/ui/primitives/Panel.js +59 -0
- package/dist/ui/primitives/Panel.js.map +1 -0
- package/dist/ui/rendering/metricRenderer.d.ts +24 -0
- package/dist/ui/rendering/metricRenderer.d.ts.map +1 -0
- package/dist/ui/rendering/metricRenderer.js +86 -0
- package/dist/ui/rendering/metricRenderer.js.map +1 -0
- package/dist/ui/runtime/hub.d.ts +12 -0
- package/dist/ui/runtime/hub.d.ts.map +1 -0
- package/dist/ui/runtime/hub.js +468 -0
- package/dist/ui/runtime/hub.js.map +1 -0
- package/dist/ui/runtime/hubValidation.d.ts +23 -0
- package/dist/ui/runtime/hubValidation.d.ts.map +1 -0
- package/dist/ui/runtime/hubValidation.js +90 -0
- package/dist/ui/runtime/hubValidation.js.map +1 -0
- package/dist/ui/runtime/index.d.ts +29 -0
- package/dist/ui/runtime/index.d.ts.map +1 -0
- package/dist/ui/runtime/index.js +299 -0
- package/dist/ui/runtime/index.js.map +1 -0
- package/dist/ui/runtime/no-tui.d.ts +12 -0
- package/dist/ui/runtime/no-tui.d.ts.map +1 -0
- package/dist/ui/runtime/no-tui.js +77 -0
- package/dist/ui/runtime/no-tui.js.map +1 -0
- package/dist/ui/runtime/state-builder.d.ts +13 -0
- package/dist/ui/runtime/state-builder.d.ts.map +1 -0
- package/dist/ui/runtime/state-builder.js +114 -0
- package/dist/ui/runtime/state-builder.js.map +1 -0
- package/dist/ui/runtime/terminalSizeCheck.d.ts +10 -0
- package/dist/ui/runtime/terminalSizeCheck.d.ts.map +1 -0
- package/dist/ui/runtime/terminalSizeCheck.js +51 -0
- package/dist/ui/runtime/terminalSizeCheck.js.map +1 -0
- package/dist/ui/runtime/tuiLogGate.d.ts +22 -0
- package/dist/ui/runtime/tuiLogGate.d.ts.map +1 -0
- package/dist/ui/runtime/tuiLogGate.js +68 -0
- package/dist/ui/runtime/tuiLogGate.js.map +1 -0
- package/dist/ui/state/types.d.ts +72 -0
- package/dist/ui/state/types.d.ts.map +1 -0
- package/dist/ui/state/types.js +6 -0
- package/dist/ui/state/types.js.map +1 -0
- package/dist/ui/theme/borders.d.ts +20 -0
- package/dist/ui/theme/borders.d.ts.map +1 -0
- package/dist/ui/theme/borders.js +55 -0
- package/dist/ui/theme/borders.js.map +1 -0
- package/dist/ui/theme/tokens.d.ts +28 -0
- package/dist/ui/theme/tokens.d.ts.map +1 -0
- package/dist/ui/theme/tokens.js +50 -0
- package/dist/ui/theme/tokens.js.map +1 -0
- package/dist/ui/theme/typography.d.ts +14 -0
- package/dist/ui/theme/typography.d.ts.map +1 -0
- package/dist/ui/theme/typography.js +30 -0
- package/dist/ui/theme/typography.js.map +1 -0
- package/dist/ui/widgets/flagRow.d.ts +25 -0
- package/dist/ui/widgets/flagRow.d.ts.map +1 -0
- package/dist/ui/widgets/flagRow.js +57 -0
- package/dist/ui/widgets/flagRow.js.map +1 -0
- package/dist/ui/widgets/index.d.ts +9 -0
- package/dist/ui/widgets/index.d.ts.map +1 -0
- package/dist/ui/widgets/index.js +9 -0
- package/dist/ui/widgets/index.js.map +1 -0
- package/dist/ui/widgets/meter.d.ts +18 -0
- package/dist/ui/widgets/meter.d.ts.map +1 -0
- package/dist/ui/widgets/meter.js +38 -0
- package/dist/ui/widgets/meter.js.map +1 -0
- package/dist/ui/widgets/miniMap.d.ts +26 -0
- package/dist/ui/widgets/miniMap.d.ts.map +1 -0
- package/dist/ui/widgets/miniMap.js +94 -0
- package/dist/ui/widgets/miniMap.js.map +1 -0
- package/dist/ui/widgets/sparkline.d.ts +17 -0
- package/dist/ui/widgets/sparkline.d.ts.map +1 -0
- package/dist/ui/widgets/sparkline.js +63 -0
- package/dist/ui/widgets/sparkline.js.map +1 -0
- package/dist/ui-primitives.d.ts +5 -2
- package/dist/ui-primitives.d.ts.map +1 -1
- package/dist/ui-primitives.js +22 -9
- package/dist/ui-primitives.js.map +1 -1
- package/package.json +5 -4
- package/dist/ui/home-view.d.ts +0 -27
- package/dist/ui/home-view.d.ts.map +0 -1
- package/dist/ui/home-view.js +0 -127
- package/dist/ui/home-view.js.map +0 -1
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Panel Primitive
|
|
3
|
+
* Phase 1: Reusable panel component with consistent styling
|
|
4
|
+
*/
|
|
5
|
+
import blessed from 'neo-blessed';
|
|
6
|
+
import { getBorderStyle } from '../theme/borders.js';
|
|
7
|
+
import { renderPanelTitle } from '../theme/typography.js';
|
|
8
|
+
// Type assertion for blessed (neo-blessed doesn't have full TypeScript support)
|
|
9
|
+
const blessedLib = blessed;
|
|
10
|
+
/**
|
|
11
|
+
* Create a consistent panel widget
|
|
12
|
+
*/
|
|
13
|
+
export function createPanel(screen, options) {
|
|
14
|
+
const { title, top, left, width, height, content = [], focused = false, panelId, severity = 'NEUTRAL', } = options;
|
|
15
|
+
// Get border style (Phase 7: glow system)
|
|
16
|
+
const borderStyle = getBorderStyle(panelId || 'POSTURE', focused, severity);
|
|
17
|
+
// Render title with typography standard
|
|
18
|
+
const titleText = renderPanelTitle(title, focused);
|
|
19
|
+
const panel = blessedLib.box({
|
|
20
|
+
top,
|
|
21
|
+
left,
|
|
22
|
+
width,
|
|
23
|
+
height,
|
|
24
|
+
border: {
|
|
25
|
+
type: borderStyle.borderType,
|
|
26
|
+
},
|
|
27
|
+
style: {
|
|
28
|
+
border: {
|
|
29
|
+
fg: borderStyle.borderColor,
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
tags: true,
|
|
33
|
+
});
|
|
34
|
+
// Title area (top border label)
|
|
35
|
+
const titleBox = blessedLib.box({
|
|
36
|
+
top: 0,
|
|
37
|
+
left: 1,
|
|
38
|
+
width: title.length + 4, // Account for padding
|
|
39
|
+
height: 1,
|
|
40
|
+
content: titleText,
|
|
41
|
+
tags: true,
|
|
42
|
+
});
|
|
43
|
+
panel.append(titleBox);
|
|
44
|
+
// Content area
|
|
45
|
+
if (content.length > 0) {
|
|
46
|
+
const contentText = content.slice(0, height - 2).join('\n');
|
|
47
|
+
const contentBox = blessedLib.box({
|
|
48
|
+
top: 1,
|
|
49
|
+
left: 1,
|
|
50
|
+
width: width - 2,
|
|
51
|
+
height: height - 2,
|
|
52
|
+
content: contentText,
|
|
53
|
+
tags: true,
|
|
54
|
+
});
|
|
55
|
+
panel.append(contentBox);
|
|
56
|
+
}
|
|
57
|
+
return panel;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=Panel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Panel.js","sourceRoot":"","sources":["../../../src/ui/primitives/Panel.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,OAAO,MAAM,aAAa,CAAC;AAGlC,OAAO,EAAE,cAAc,EAAoB,MAAM,qBAAqB,CAAC;AACvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG1D,gFAAgF;AAChF,MAAM,UAAU,GAAQ,OAAO,CAAC;AAiBhC;;GAEG;AACH,MAAM,UAAU,WAAW,CACzB,MAAsB,EACtB,OAAqB;IAErB,MAAM,EACJ,KAAK,EACL,GAAG,EACH,IAAI,EACJ,KAAK,EACL,MAAM,EACN,OAAO,GAAG,EAAE,EACZ,OAAO,GAAG,KAAK,EACf,OAAO,EACP,QAAQ,GAAG,SAAS,GACrB,GAAG,OAAO,CAAC;IAEZ,0CAA0C;IAC1C,MAAM,WAAW,GAAG,cAAc,CAChC,OAAO,IAAI,SAAS,EACpB,OAAO,EACP,QAAQ,CACT,CAAC;IAEF,wCAAwC;IACxC,MAAM,SAAS,GAAG,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAEnD,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC;QAC3B,GAAG;QACH,IAAI;QACJ,KAAK;QACL,MAAM;QACN,MAAM,EAAE;YACN,IAAI,EAAE,WAAW,CAAC,UAAU;SAC7B;QACD,KAAK,EAAE;YACL,MAAM,EAAE;gBACN,EAAE,EAAE,WAAW,CAAC,WAAW;aAC5B;SACF;QACD,IAAI,EAAE,IAAI;KACX,CAAC,CAAC;IAEH,gCAAgC;IAChC,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC;QAC9B,GAAG,EAAE,CAAC;QACN,IAAI,EAAE,CAAC;QACP,KAAK,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,sBAAsB;QAC/C,MAAM,EAAE,CAAC;QACT,OAAO,EAAE,SAAS;QAClB,IAAI,EAAE,IAAI;KACX,CAAC,CAAC;IAEH,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAEvB,eAAe;IACf,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5D,MAAM,UAAU,GAAG,UAAU,CAAC,GAAG,CAAC;YAChC,GAAG,EAAE,CAAC;YACN,IAAI,EAAE,CAAC;YACP,KAAK,EAAE,KAAK,GAAG,CAAC;YAChB,MAAM,EAAE,MAAM,GAAG,CAAC;YAClB,OAAO,EAAE,WAAW;YACpB,IAAI,EAAE,IAAI;SACX,CAAC,CAAC;QACH,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC3B,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metric Renderer
|
|
3
|
+
* Phase 9: UI rendering guardrail - no raw values
|
|
4
|
+
*/
|
|
5
|
+
import type { CollectorResult } from '../../core/types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Render a metric with status awareness
|
|
8
|
+
*
|
|
9
|
+
* Behavior:
|
|
10
|
+
* - LIVE → show value
|
|
11
|
+
* - STALE → show value + STALE (Xm)
|
|
12
|
+
* - UNAVAILABLE → show UNAVAILABLE + message + action
|
|
13
|
+
* - DISABLED → show DISABLED + action
|
|
14
|
+
*/
|
|
15
|
+
export declare function renderMetric(label: string, collectorResult: CollectorResult<any>, valuePath: string | ((data: any) => string), options?: {
|
|
16
|
+
labelWidth?: number;
|
|
17
|
+
valueColor?: string;
|
|
18
|
+
formatValue?: (value: any) => string;
|
|
19
|
+
}): string;
|
|
20
|
+
/**
|
|
21
|
+
* Helper to format timestamp as relative time
|
|
22
|
+
*/
|
|
23
|
+
export declare function formatRelativeTime(ts: number): string;
|
|
24
|
+
//# sourceMappingURL=metricRenderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metricRenderer.d.ts","sourceRoot":"","sources":["../../../src/ui/rendering/metricRenderer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAmB,MAAM,qBAAqB,CAAC;AAI5E;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,EACb,eAAe,EAAE,eAAe,CAAC,GAAG,CAAC,EACrC,SAAS,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,GAAG,KAAK,MAAM,CAAC,EAC3C,OAAO,GAAE;IACP,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,MAAM,CAAC;CACjC,GACL,MAAM,CAiDR;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAerD"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metric Renderer
|
|
3
|
+
* Phase 9: UI rendering guardrail - no raw values
|
|
4
|
+
*/
|
|
5
|
+
import { getBlessedColor } from '../theme/tokens.js';
|
|
6
|
+
import { formatValue } from '../constraints/unknownHandling.js';
|
|
7
|
+
/**
|
|
8
|
+
* Render a metric with status awareness
|
|
9
|
+
*
|
|
10
|
+
* Behavior:
|
|
11
|
+
* - LIVE → show value
|
|
12
|
+
* - STALE → show value + STALE (Xm)
|
|
13
|
+
* - UNAVAILABLE → show UNAVAILABLE + message + action
|
|
14
|
+
* - DISABLED → show DISABLED + action
|
|
15
|
+
*/
|
|
16
|
+
export function renderMetric(label, collectorResult, valuePath, options = {}) {
|
|
17
|
+
const { labelWidth = 12, valueColor, formatValue: customFormatter } = options;
|
|
18
|
+
const status = collectorResult.status;
|
|
19
|
+
const data = collectorResult.data;
|
|
20
|
+
// Extract value from data
|
|
21
|
+
let value;
|
|
22
|
+
if (typeof valuePath === 'function') {
|
|
23
|
+
value = data ? valuePath(data) : undefined;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
// Simple path access (e.g., "cpuPercent")
|
|
27
|
+
value = data ? data[valuePath] : undefined;
|
|
28
|
+
}
|
|
29
|
+
// Format value
|
|
30
|
+
const formatter = customFormatter || ((v) => formatValue(v));
|
|
31
|
+
const formattedValue = value !== undefined ? formatter(value) : undefined;
|
|
32
|
+
// Build output based on status
|
|
33
|
+
let output;
|
|
34
|
+
let statusColor = getBlessedColor('mutedGrey');
|
|
35
|
+
if (status === 'LIVE') {
|
|
36
|
+
// LIVE → show value
|
|
37
|
+
const displayValue = formattedValue || 'UNKNOWN';
|
|
38
|
+
const color = valueColor || getBlessedColor('accentCyan');
|
|
39
|
+
output = `${label.padEnd(labelWidth)}: {${color}-fg}${displayValue}{/}`;
|
|
40
|
+
}
|
|
41
|
+
else if (status === 'STALE') {
|
|
42
|
+
// STALE → show value + STALE (Xm)
|
|
43
|
+
const ageMinutes = Math.floor((Date.now() - collectorResult.asOf) / 60000);
|
|
44
|
+
const displayValue = formattedValue || 'UNKNOWN';
|
|
45
|
+
const color = valueColor || getBlessedColor('warnAmber');
|
|
46
|
+
output = `${label.padEnd(labelWidth)}: {${color}-fg}${displayValue}{/} {${getBlessedColor('warnAmber')}-fg}STALE (${ageMinutes}m){/}`;
|
|
47
|
+
}
|
|
48
|
+
else if (status === 'UNAVAILABLE') {
|
|
49
|
+
// UNAVAILABLE → show UNAVAILABLE + message + action
|
|
50
|
+
const reason = collectorResult.message || collectorResult.reasonCode || 'Unknown reason';
|
|
51
|
+
const action = collectorResult.action ? ` (run: ${collectorResult.action})` : '';
|
|
52
|
+
output = `${label.padEnd(labelWidth)}: {${getBlessedColor('criticalRed')}-fg}UNAVAILABLE{/} — ${reason}${action}`;
|
|
53
|
+
}
|
|
54
|
+
else if (status === 'DISABLED') {
|
|
55
|
+
// DISABLED → show DISABLED + action
|
|
56
|
+
const action = collectorResult.action ? ` (run: ${collectorResult.action})` : '';
|
|
57
|
+
output = `${label.padEnd(labelWidth)}: {${getBlessedColor('mutedGrey')}-fg}DISABLED{/}${action}`;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
// Fallback
|
|
61
|
+
output = `${label.padEnd(labelWidth)}: {${getBlessedColor('mutedGrey')}-fg}UNKNOWN{/}`;
|
|
62
|
+
}
|
|
63
|
+
return output;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Helper to format timestamp as relative time
|
|
67
|
+
*/
|
|
68
|
+
export function formatRelativeTime(ts) {
|
|
69
|
+
const ageMs = Date.now() - ts;
|
|
70
|
+
const ageMinutes = Math.floor(ageMs / 60000);
|
|
71
|
+
const ageHours = Math.floor(ageMinutes / 60);
|
|
72
|
+
const ageDays = Math.floor(ageHours / 24);
|
|
73
|
+
if (ageDays > 0) {
|
|
74
|
+
return `${ageDays}d`;
|
|
75
|
+
}
|
|
76
|
+
else if (ageHours > 0) {
|
|
77
|
+
return `${ageHours}h`;
|
|
78
|
+
}
|
|
79
|
+
else if (ageMinutes > 0) {
|
|
80
|
+
return `${ageMinutes}m`;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
return '<1m';
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=metricRenderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metricRenderer.js","sourceRoot":"","sources":["../../../src/ui/rendering/metricRenderer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hub Grid Layout
|
|
3
|
+
* Phase 3: Render from UiState snapshot (no direct system calls)
|
|
4
|
+
*/
|
|
5
|
+
import type { Widgets } from 'neo-blessed';
|
|
6
|
+
import type { UiState } from '../../core/types.js';
|
|
7
|
+
/**
|
|
8
|
+
* Render hub grid layout
|
|
9
|
+
* Phase 9: Validates hub snapshot before rendering
|
|
10
|
+
*/
|
|
11
|
+
export declare function renderHub(screen: Widgets.Screen, uiState: UiState, focusedPanelId?: string): void;
|
|
12
|
+
//# sourceMappingURL=hub.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hub.d.ts","sourceRoot":"","sources":["../../../src/ui/runtime/hub.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAanD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAuEjG"}
|
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hub Grid Layout
|
|
3
|
+
* Phase 3: Render from UiState snapshot (no direct system calls)
|
|
4
|
+
*/
|
|
5
|
+
import { statusToSeverity } from '../theme/borders.js';
|
|
6
|
+
import { LINE_BUDGETS } from '../constraints/layoutSpec.js';
|
|
7
|
+
import { validateHubSnapshot, renderSafeHoldScreen } from './hubValidation.js';
|
|
8
|
+
/**
|
|
9
|
+
* Render hub grid layout
|
|
10
|
+
* Phase 9: Validates hub snapshot before rendering
|
|
11
|
+
*/
|
|
12
|
+
export function renderHub(screen, uiState, focusedPanelId) {
|
|
13
|
+
// Phase 9: Validate hub snapshot before rendering
|
|
14
|
+
const validation = validateHubSnapshot(uiState);
|
|
15
|
+
if (!validation.valid) {
|
|
16
|
+
// Show safe hold screen
|
|
17
|
+
const blessed = require('neo-blessed');
|
|
18
|
+
const blessedLib = blessed;
|
|
19
|
+
const { getBlessedColor } = require('../theme/tokens.js');
|
|
20
|
+
screen.children.forEach((child) => child.destroy());
|
|
21
|
+
const safeHoldBox = blessedLib.box({
|
|
22
|
+
top: 0,
|
|
23
|
+
left: 0,
|
|
24
|
+
width: screen.width,
|
|
25
|
+
height: screen.height,
|
|
26
|
+
border: { type: 'line' },
|
|
27
|
+
style: {
|
|
28
|
+
border: { fg: getBlessedColor('criticalRed') },
|
|
29
|
+
},
|
|
30
|
+
tags: true,
|
|
31
|
+
});
|
|
32
|
+
const content = renderSafeHoldScreen(validation.errors);
|
|
33
|
+
safeHoldBox.setContent(content.join('\n'));
|
|
34
|
+
screen.append(safeHoldBox);
|
|
35
|
+
screen.render();
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const screenWidth = screen.width;
|
|
39
|
+
const screenHeight = screen.height;
|
|
40
|
+
// Calculate panel dimensions
|
|
41
|
+
const leftWidth = Math.floor(screenWidth * 0.3);
|
|
42
|
+
const leftPanelHeight = Math.floor((screenHeight - 2) / 3);
|
|
43
|
+
const centerWidth = Math.floor(screenWidth * 0.4);
|
|
44
|
+
const centerHeight = screenHeight - 2;
|
|
45
|
+
const rightWidth = screenWidth - leftWidth - centerWidth;
|
|
46
|
+
const rightPanelHeight = Math.floor((screenHeight - 2) / 2);
|
|
47
|
+
// Get severity for posture panel
|
|
48
|
+
const postureSeverity = statusToSeverity(uiState.posture.posture);
|
|
49
|
+
// Panel 1: POSTURE (Left top)
|
|
50
|
+
const panel1 = renderPosturePanelFromUiState(screen, 0, 0, leftWidth, leftPanelHeight, uiState, focusedPanelId === "POSTURE", postureSeverity);
|
|
51
|
+
screen.append(panel1);
|
|
52
|
+
// Panel 2: RESOURCES (Left middle)
|
|
53
|
+
const panel2 = renderResourcesPanelFromUiState(screen, leftPanelHeight, 0, leftWidth, leftPanelHeight, uiState, focusedPanelId === "RESOURCES");
|
|
54
|
+
screen.append(panel2);
|
|
55
|
+
// Panel 3: ACTIVE ASSETS (Left bottom)
|
|
56
|
+
const panel3 = renderActiveAssetsPanelFromUiState(screen, leftPanelHeight * 2, 0, leftWidth, leftPanelHeight, uiState, focusedPanelId === "ASSETS");
|
|
57
|
+
screen.append(panel3);
|
|
58
|
+
// Panel 4: OPERATIONS FEED (Center main)
|
|
59
|
+
const panel4 = renderOperationsFeedPanelFromUiState(screen, 0, leftWidth, centerWidth, centerHeight, uiState, focusedPanelId === "FEED");
|
|
60
|
+
screen.append(panel4);
|
|
61
|
+
// Panel 5: GEO INTEL (Right top) - Phase 5
|
|
62
|
+
const panel5 = renderGeoIntelPanelFromUiState(screen, 0, leftWidth + centerWidth, rightWidth, rightPanelHeight, uiState, focusedPanelId === "NETWORK");
|
|
63
|
+
screen.append(panel5);
|
|
64
|
+
// Panel 6: CAPABILITY FLAGS (Right bottom)
|
|
65
|
+
const panel6 = renderCapabilityFlagsPanelFromUiState(screen, rightPanelHeight, leftWidth + centerWidth, rightWidth, rightPanelHeight, uiState, focusedPanelId === "CAPABILITIES");
|
|
66
|
+
screen.append(panel6);
|
|
67
|
+
// Panel 7: COMMAND SURFACE + STATUS STRIP (Bottom)
|
|
68
|
+
const panel7 = renderCommandSurfaceFromUiState(screen, screenHeight - 1, 0, screenWidth, 1, uiState);
|
|
69
|
+
screen.append(panel7);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Helper: Render posture panel from UiState
|
|
73
|
+
*/
|
|
74
|
+
function renderPosturePanelFromUiState(screen, top, left, width, height, uiState, focused = false, severity = 'NEUTRAL') {
|
|
75
|
+
const { createPanel } = require('../primitives/Panel.js');
|
|
76
|
+
const { getBlessedColor } = require('../theme/tokens.js');
|
|
77
|
+
const { formatLabelValue } = require('../theme/typography.js');
|
|
78
|
+
const { formatValue } = require('../constraints/unknownHandling.js');
|
|
79
|
+
const { statusToSeverity: statusToSev } = require('../theme/borders.js');
|
|
80
|
+
const content = [];
|
|
81
|
+
content.push(''); // Blank line after title
|
|
82
|
+
// Phase 8: Enforce line budget (max 6 content lines for small panels)
|
|
83
|
+
const maxLines = LINE_BUDGETS.SMALL_PANEL;
|
|
84
|
+
const posture = formatValue(uiState.posture.posture);
|
|
85
|
+
const postureSeverity = statusToSev(uiState.posture.posture);
|
|
86
|
+
const postureColor = postureSeverity === 'OK' ? getBlessedColor('successGreen') :
|
|
87
|
+
postureSeverity === 'CRITICAL' ? getBlessedColor('criticalRed') :
|
|
88
|
+
postureSeverity === 'WARNING' ? getBlessedColor('warnAmber') : getBlessedColor('mutedGrey');
|
|
89
|
+
content.push(formatLabelValue('POSTURE', posture, 12, postureColor));
|
|
90
|
+
const risk = formatValue(uiState.posture.risk);
|
|
91
|
+
const riskSeverity = risk === 'LOW' ? 'OK' : risk === 'HIGH' ? 'CRITICAL' : 'WARNING';
|
|
92
|
+
const riskColor = riskSeverity === 'OK' ? getBlessedColor('successGreen') :
|
|
93
|
+
riskSeverity === 'CRITICAL' ? getBlessedColor('criticalRed') :
|
|
94
|
+
riskSeverity === 'WARNING' ? getBlessedColor('warnAmber') : getBlessedColor('mutedGrey');
|
|
95
|
+
content.push(formatLabelValue('RISK', risk, 12, riskColor));
|
|
96
|
+
const connected = uiState.posture.connected === true ? 'YES' :
|
|
97
|
+
uiState.posture.connected === false ? 'NO' : formatValue(uiState.posture.connected);
|
|
98
|
+
const target = uiState.posture.target !== 'UNKNOWN' ? ` (${formatValue(uiState.posture.target)})` : '';
|
|
99
|
+
const connectedColor = connected === 'YES' ? getBlessedColor('successGreen') : getBlessedColor('warnAmber');
|
|
100
|
+
content.push(formatLabelValue('CONNECTED', `${connected}${target}`, 12, connectedColor));
|
|
101
|
+
const lastChange = uiState.posture.lastChange !== 'UNKNOWN'
|
|
102
|
+
? new Date(uiState.posture.lastChange).toLocaleTimeString()
|
|
103
|
+
: formatValue(uiState.posture.lastChange);
|
|
104
|
+
content.push(formatLabelValue('LAST CHANGE', lastChange, 12));
|
|
105
|
+
if (uiState.posture.cause !== 'UNKNOWN' && uiState.posture.posture !== 'OPERATIONAL') {
|
|
106
|
+
content.push(formatLabelValue('CAUSE', formatValue(uiState.posture.cause), 12, getBlessedColor('warnAmber')));
|
|
107
|
+
}
|
|
108
|
+
// Enforce line budget
|
|
109
|
+
if (content.length > maxLines + 1) { // +1 for blank line
|
|
110
|
+
content.splice(maxLines + 1);
|
|
111
|
+
}
|
|
112
|
+
return createPanel(screen, {
|
|
113
|
+
title: 'POSTURE',
|
|
114
|
+
top,
|
|
115
|
+
left,
|
|
116
|
+
width,
|
|
117
|
+
height,
|
|
118
|
+
content,
|
|
119
|
+
focused,
|
|
120
|
+
panelId: 'POSTURE',
|
|
121
|
+
severity,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Helper: Render resources panel from UiState
|
|
126
|
+
*/
|
|
127
|
+
function renderResourcesPanelFromUiState(screen, top, left, width, height, uiState, focused = false) {
|
|
128
|
+
const { createPanel } = require('../primitives/Panel.js');
|
|
129
|
+
const { getBlessedColor } = require('../theme/tokens.js');
|
|
130
|
+
const { renderSparkline } = require('../widgets/index.js');
|
|
131
|
+
const content = [];
|
|
132
|
+
// CPU with sparkline
|
|
133
|
+
const cpuValue = uiState.resources.cpuPercent;
|
|
134
|
+
const cpuLabel = typeof cpuValue === 'number' ? `${cpuValue}%` : 'UNKNOWN';
|
|
135
|
+
const cpuSamples = uiState.history?.cpu || [];
|
|
136
|
+
const cpuSparkline = renderSparkline({
|
|
137
|
+
samples: cpuSamples,
|
|
138
|
+
min: 0,
|
|
139
|
+
max: 100,
|
|
140
|
+
width: Math.min(24, width - 15), // Leave room for label
|
|
141
|
+
});
|
|
142
|
+
content.push(`CPU: {${getBlessedColor('accentCyan')}-fg}${cpuLabel}{/} ${cpuSparkline}`);
|
|
143
|
+
content.push(`LOAD: ${uiState.resources.load1m}`);
|
|
144
|
+
// RAM with sparkline
|
|
145
|
+
const ramUsed = uiState.resources.ramUsedMb;
|
|
146
|
+
const ramTotal = uiState.resources.ramTotalMb;
|
|
147
|
+
let ramLabel;
|
|
148
|
+
let ramPercent = null;
|
|
149
|
+
if (typeof ramUsed === 'number' && typeof ramTotal === 'number') {
|
|
150
|
+
ramPercent = (ramUsed / ramTotal) * 100;
|
|
151
|
+
ramLabel = `${(ramUsed / 1024).toFixed(1)}/${(ramTotal / 1024).toFixed(1)}GB (${ramPercent.toFixed(0)}%)`;
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
ramLabel = `${ramUsed}M / ${ramTotal}M`;
|
|
155
|
+
}
|
|
156
|
+
const ramSamples = uiState.history?.ram || [];
|
|
157
|
+
const ramSparkline = renderSparkline({
|
|
158
|
+
samples: ramSamples,
|
|
159
|
+
min: 0,
|
|
160
|
+
max: 100,
|
|
161
|
+
width: Math.min(24, width - 20), // Leave room for label
|
|
162
|
+
});
|
|
163
|
+
content.push(`RAM: {${getBlessedColor('accentCyan')}-fg}${ramLabel}{/} ${ramSparkline}`);
|
|
164
|
+
content.push(`DISK: ${uiState.resources.diskUsedGb}G / ${uiState.resources.diskTotalGb}G`);
|
|
165
|
+
const uptimeSec = uiState.resources.uptimeSec;
|
|
166
|
+
const uptime = typeof uptimeSec === 'number'
|
|
167
|
+
? `${Math.floor(uptimeSec / 3600)}h ${Math.floor((uptimeSec % 3600) / 60)}m`
|
|
168
|
+
: 'UNKNOWN';
|
|
169
|
+
content.push(`UPTIME: ${uptime}`);
|
|
170
|
+
content.push(`PROCS: ${uiState.resources.procCount}`);
|
|
171
|
+
return createPanel(screen, {
|
|
172
|
+
title: 'RESOURCES',
|
|
173
|
+
top,
|
|
174
|
+
left,
|
|
175
|
+
width,
|
|
176
|
+
height,
|
|
177
|
+
content,
|
|
178
|
+
focused,
|
|
179
|
+
panelId: 'RESOURCES',
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Helper: Render active assets panel from UiState
|
|
184
|
+
*/
|
|
185
|
+
function renderActiveAssetsPanelFromUiState(screen, top, left, width, height, uiState, focused = false) {
|
|
186
|
+
const { createPanel } = require('../primitives/Panel.js');
|
|
187
|
+
const { getBlessedColor } = require('../theme/tokens.js');
|
|
188
|
+
const blessed = require('neo-blessed');
|
|
189
|
+
const content = [];
|
|
190
|
+
content.push(`TOTAL: {${getBlessedColor('accentCyan')}-fg}${uiState.assets.total}{/}`);
|
|
191
|
+
const activeColor = uiState.assets.active !== 'UNKNOWN' && uiState.assets.active > 0
|
|
192
|
+
? getBlessedColor('successGreen')
|
|
193
|
+
: getBlessedColor('mutedGrey');
|
|
194
|
+
content.push(`ACTIVE: {${activeColor}-fg}${uiState.assets.active}{/}`);
|
|
195
|
+
content.push(`IDLE: {${getBlessedColor('mutedGrey')}-fg}${uiState.assets.idle}{/}`);
|
|
196
|
+
const errorColor = uiState.assets.error !== 'UNKNOWN' && typeof uiState.assets.error === 'number' && uiState.assets.error > 0
|
|
197
|
+
? getBlessedColor('criticalRed')
|
|
198
|
+
: getBlessedColor('mutedGrey');
|
|
199
|
+
content.push(`ERROR: {${errorColor}-fg}${uiState.assets.error}{/}`);
|
|
200
|
+
// Activity meter
|
|
201
|
+
const { renderMeter } = require('../widgets/index.js');
|
|
202
|
+
const total = uiState.assets.total;
|
|
203
|
+
const active = uiState.assets.active;
|
|
204
|
+
let activityValue = "UNKNOWN";
|
|
205
|
+
let activityLabel = '';
|
|
206
|
+
if (typeof total === 'number' && typeof active === 'number' && total > 0) {
|
|
207
|
+
activityValue = (active / total) * 100; // Percentage
|
|
208
|
+
activityLabel = `A:${active} I:${uiState.assets.idle} E:${uiState.assets.error}`;
|
|
209
|
+
}
|
|
210
|
+
const activityMeter = renderMeter({
|
|
211
|
+
value: activityValue,
|
|
212
|
+
min: 0,
|
|
213
|
+
max: 100,
|
|
214
|
+
width: 10,
|
|
215
|
+
labelLeft: 'ACTIVITY',
|
|
216
|
+
labelRight: activityLabel,
|
|
217
|
+
});
|
|
218
|
+
content.push(`{${getBlessedColor('accentCyan')}-fg}${activityMeter}{/}`);
|
|
219
|
+
if (uiState.assets.highlight !== 'UNKNOWN') {
|
|
220
|
+
content.push(`HIGHLIGHT: {${getBlessedColor('accentCyan')}-fg}${uiState.assets.highlight}{/}`);
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
content.push(`HIGHLIGHT: {${getBlessedColor('mutedGrey')}-fg}NONE{/}`);
|
|
224
|
+
}
|
|
225
|
+
content.push(`LAST ACTION: {${getBlessedColor('accentCyan')}-fg}${uiState.assets.lastAction}{/}`);
|
|
226
|
+
return createPanel(screen, {
|
|
227
|
+
title: 'ACTIVE ASSETS',
|
|
228
|
+
top,
|
|
229
|
+
left,
|
|
230
|
+
width,
|
|
231
|
+
height,
|
|
232
|
+
content,
|
|
233
|
+
focused,
|
|
234
|
+
panelId: 'ASSETS',
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Helper: Render operations feed panel from UiState
|
|
239
|
+
*/
|
|
240
|
+
function renderOperationsFeedPanelFromUiState(screen, top, left, width, height, uiState, focused = false) {
|
|
241
|
+
const { createPanel } = require('../primitives/Panel.js');
|
|
242
|
+
const { getBlessedColor } = require('../theme/tokens.js');
|
|
243
|
+
const blessed = require('neo-blessed');
|
|
244
|
+
const content = [];
|
|
245
|
+
if (uiState.feed.events.length > 0) {
|
|
246
|
+
uiState.feed.events.slice(0, height - 2).forEach(event => {
|
|
247
|
+
const time = new Date(event.ts).toLocaleTimeString();
|
|
248
|
+
const color = event.tag === 'SEC' || event.tag === 'DB'
|
|
249
|
+
? getBlessedColor('criticalRed')
|
|
250
|
+
: event.tag === 'NET'
|
|
251
|
+
? getBlessedColor('warnAmber')
|
|
252
|
+
: getBlessedColor('accentCyan');
|
|
253
|
+
content.push(`{${color}-fg}[${time}] [${event.tag}] ${event.msg}{/}`);
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
content.push(`{${getBlessedColor('mutedGrey')}-fg}NO RECENT OPERATIONS{/}`);
|
|
258
|
+
}
|
|
259
|
+
return createPanel(screen, {
|
|
260
|
+
title: 'OPERATIONS FEED',
|
|
261
|
+
top,
|
|
262
|
+
left,
|
|
263
|
+
width,
|
|
264
|
+
height,
|
|
265
|
+
content,
|
|
266
|
+
focused,
|
|
267
|
+
panelId: 'FEED',
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Helper: Render network/origin panel from UiState
|
|
272
|
+
*/
|
|
273
|
+
function renderNetworkOriginPanelFromUiState(screen, top, left, width, height, uiState) {
|
|
274
|
+
const { createPanel } = require('../primitives/Panel.js');
|
|
275
|
+
const { getBlessedColor } = require('../theme/tokens.js');
|
|
276
|
+
const blessed = require('neo-blessed');
|
|
277
|
+
const content = [];
|
|
278
|
+
content.push(`ORIGIN: {${getBlessedColor('accentCyan')}-fg}${uiState.network.originRegion} (${uiState.network.originClass}){/}`);
|
|
279
|
+
content.push(`GATEWAY: {${getBlessedColor('accentCyan')}-fg}${uiState.network.gatewayRegion}{/}`);
|
|
280
|
+
content.push(`DATABASE: {${getBlessedColor('accentCyan')}-fg}${uiState.network.dbRegion}{/}`);
|
|
281
|
+
// Latency with meter
|
|
282
|
+
const latencyClass = uiState.network.latencyClass;
|
|
283
|
+
const latencyMs = uiState.network.latencyMs;
|
|
284
|
+
const latencyColor = latencyClass === 'LOW' ? getBlessedColor('successGreen') :
|
|
285
|
+
latencyClass === 'HIGH' ? getBlessedColor('criticalRed') :
|
|
286
|
+
latencyClass === 'MEDIUM' ? getBlessedColor('warnAmber') : getBlessedColor('mutedGrey');
|
|
287
|
+
const { renderMeter } = require('../widgets/index.js');
|
|
288
|
+
const latencyLabel = latencyMs !== 'UNKNOWN' ? `${latencyMs}ms` : 'UNKNOWN';
|
|
289
|
+
const latencyMeter = renderMeter({
|
|
290
|
+
value: latencyMs !== 'UNKNOWN' ? latencyMs : 'UNKNOWN',
|
|
291
|
+
min: 0,
|
|
292
|
+
max: 300, // 300ms max for meter
|
|
293
|
+
width: 10,
|
|
294
|
+
labelLeft: 'LAT',
|
|
295
|
+
labelRight: latencyLabel,
|
|
296
|
+
});
|
|
297
|
+
content.push(`{${latencyColor}-fg}${latencyMeter}{/}`);
|
|
298
|
+
content.push(`PUBLIC IP: {${getBlessedColor('mutedGrey')}-fg}${uiState.network.publicIpMasked}{/}`);
|
|
299
|
+
return createPanel(screen, {
|
|
300
|
+
title: 'NETWORK / ORIGIN',
|
|
301
|
+
top,
|
|
302
|
+
left,
|
|
303
|
+
width,
|
|
304
|
+
height,
|
|
305
|
+
content,
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Helper: Render GEO INTEL panel from UiState
|
|
310
|
+
* Phase 5: Geographic situational awareness
|
|
311
|
+
*/
|
|
312
|
+
function renderGeoIntelPanelFromUiState(screen, top, left, width, height, uiState, focused = false) {
|
|
313
|
+
const { createPanel } = require('../primitives/Panel.js');
|
|
314
|
+
const { getBlessedColor } = require('../theme/tokens.js');
|
|
315
|
+
const { renderMiniMap } = require('../widgets/index.js');
|
|
316
|
+
const content = [];
|
|
317
|
+
const geo = uiState.geo;
|
|
318
|
+
if (!geo || (geo.originLabel === "UNKNOWN" && geo.gatewayRegion === "UNKNOWN" && geo.dbRegion === "UNKNOWN")) {
|
|
319
|
+
// All geo fields unknown
|
|
320
|
+
content.push(`GEO: {${getBlessedColor('mutedGrey')}-fg}UNAVAILABLE{/}`);
|
|
321
|
+
content.push(`REASON: {${getBlessedColor('mutedGrey')}-fg}NO_SOURCE_DATA{/}`);
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
// ORIGIN
|
|
325
|
+
content.push(`ORIGIN: {${getBlessedColor('accentCyan')}-fg}${geo.originLabel}{/}`);
|
|
326
|
+
// ROUTE
|
|
327
|
+
content.push(`ROUTE: {${getBlessedColor('accentCyan')}-fg}${geo.routeLabel}{/}`);
|
|
328
|
+
// GATEWAY
|
|
329
|
+
content.push(`GW: {${getBlessedColor('accentCyan')}-fg}${geo.gatewayRegion}{/}`);
|
|
330
|
+
// DATABASE
|
|
331
|
+
content.push(`DB: {${getBlessedColor('accentCyan')}-fg}${geo.dbRegion}{/}`);
|
|
332
|
+
// PING
|
|
333
|
+
const pingLabel = geo.pingMs !== "UNKNOWN"
|
|
334
|
+
? `${geo.pingMs}ms ${geo.pingClass}`
|
|
335
|
+
: 'UNKNOWN';
|
|
336
|
+
const pingColor = geo.pingClass === 'LOW' ? getBlessedColor('successGreen') :
|
|
337
|
+
geo.pingClass === 'HIGH' ? getBlessedColor('criticalRed') :
|
|
338
|
+
geo.pingClass === 'MEDIUM' ? getBlessedColor('warnAmber') : getBlessedColor('mutedGrey');
|
|
339
|
+
content.push(`PING: {${pingColor}-fg}${pingLabel}{/}`);
|
|
340
|
+
}
|
|
341
|
+
// Mini-map (optional, only if enabled and coordinates known)
|
|
342
|
+
const enableMiniMap = process.env.ENABLE_MINIMAP === '1';
|
|
343
|
+
if (enableMiniMap && geo && geo.originLatLon && geo.originLatLon !== "UNKNOWN" &&
|
|
344
|
+
geo.gatewayLatLon && geo.gatewayLatLon !== "UNKNOWN" &&
|
|
345
|
+
typeof geo.originLatLon === 'object' && typeof geo.gatewayLatLon === 'object') {
|
|
346
|
+
const mapWidth = Math.min(20, width - 2);
|
|
347
|
+
const mapHeight = Math.min(10, height - content.length - 2);
|
|
348
|
+
if (mapHeight > 0 && mapWidth > 0) {
|
|
349
|
+
const mapLines = renderMiniMap({
|
|
350
|
+
width: mapWidth,
|
|
351
|
+
height: mapHeight,
|
|
352
|
+
origin: geo.originLatLon,
|
|
353
|
+
gateway: geo.gatewayLatLon,
|
|
354
|
+
db: geo.dbLatLon && geo.dbLatLon !== "UNKNOWN" && typeof geo.dbLatLon === 'object' ? geo.dbLatLon : undefined,
|
|
355
|
+
});
|
|
356
|
+
content.push(''); // Blank line separator
|
|
357
|
+
content.push(...mapLines);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
// Ensure max 5 lines (or more if mini-map enabled)
|
|
361
|
+
const maxLines = enableMiniMap ? height - 2 : 5;
|
|
362
|
+
const finalContent = content.slice(0, maxLines);
|
|
363
|
+
return createPanel(screen, {
|
|
364
|
+
title: 'GEO INTEL',
|
|
365
|
+
top,
|
|
366
|
+
left,
|
|
367
|
+
width,
|
|
368
|
+
height,
|
|
369
|
+
content: finalContent,
|
|
370
|
+
focused,
|
|
371
|
+
panelId: 'NETWORK',
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Helper: Render capability flags panel from UiState
|
|
376
|
+
*/
|
|
377
|
+
function renderCapabilityFlagsPanelFromUiState(screen, top, left, width, height, uiState, focused = false) {
|
|
378
|
+
const { createPanel } = require('../primitives/Panel.js');
|
|
379
|
+
const { getBlessedColor } = require('../theme/tokens.js');
|
|
380
|
+
const blessed = require('neo-blessed');
|
|
381
|
+
const content = [];
|
|
382
|
+
// Build flags
|
|
383
|
+
const { renderFlagRow, capabilityToLevel, getFlagColorToken } = require('../widgets/index.js');
|
|
384
|
+
const flags = [
|
|
385
|
+
{
|
|
386
|
+
key: 'GW',
|
|
387
|
+
value: uiState.capabilities.gateway,
|
|
388
|
+
level: capabilityToLevel(uiState.capabilities.gateway, 'gateway'),
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
key: 'DB',
|
|
392
|
+
value: uiState.capabilities.database,
|
|
393
|
+
level: capabilityToLevel(uiState.capabilities.database, 'database'),
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
key: 'MEM',
|
|
397
|
+
value: uiState.capabilities.memory,
|
|
398
|
+
level: capabilityToLevel(uiState.capabilities.memory, 'memory'),
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
key: 'SEC',
|
|
402
|
+
value: uiState.capabilities.security,
|
|
403
|
+
level: capabilityToLevel(uiState.capabilities.security, 'security'),
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
key: 'RL',
|
|
407
|
+
value: uiState.capabilities.rateLimit,
|
|
408
|
+
level: capabilityToLevel(uiState.capabilities.rateLimit, 'rateLimit'),
|
|
409
|
+
},
|
|
410
|
+
];
|
|
411
|
+
// Render flag row with colors
|
|
412
|
+
const flagRow = renderFlagRow(flags, 5);
|
|
413
|
+
const coloredParts = [];
|
|
414
|
+
const parts = flagRow.split(' ');
|
|
415
|
+
parts.forEach((part, idx) => {
|
|
416
|
+
const flag = flags[idx];
|
|
417
|
+
if (flag) {
|
|
418
|
+
const colorToken = getFlagColorToken(flag.level);
|
|
419
|
+
const color = getBlessedColor(colorToken);
|
|
420
|
+
coloredParts.push(`{${color}-fg}${part}{/}`);
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
423
|
+
coloredParts.push(part);
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
content.push(coloredParts.join(' '));
|
|
427
|
+
return createPanel(screen, {
|
|
428
|
+
title: 'CAPABILITY FLAGS',
|
|
429
|
+
top,
|
|
430
|
+
left,
|
|
431
|
+
width,
|
|
432
|
+
height,
|
|
433
|
+
content,
|
|
434
|
+
focused,
|
|
435
|
+
panelId: 'CAPABILITIES',
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Helper: Render command surface from UiState
|
|
440
|
+
*/
|
|
441
|
+
function renderCommandSurfaceFromUiState(screen, top, left, width, height, uiState) {
|
|
442
|
+
const blessed = require('neo-blessed');
|
|
443
|
+
const blessedLib = blessed;
|
|
444
|
+
const { getBlessedColor } = require('../theme/tokens.js');
|
|
445
|
+
const commands = 'start deploy inspect metrics system';
|
|
446
|
+
const commandText = `{${getBlessedColor('accentCyan')}-fg}${commands}{/}`;
|
|
447
|
+
const mode = uiState.posture.connected === true ? 'REMOTE' :
|
|
448
|
+
uiState.posture.connected === false ? 'OFFLINE' : 'UNKNOWN';
|
|
449
|
+
const connect = uiState.posture.connected === true ? 'CONNECTED' : 'DISCONNECTED';
|
|
450
|
+
const posture = uiState.posture.posture;
|
|
451
|
+
const activeRuns = typeof uiState.assets.active === 'number' ? uiState.assets.active : 0;
|
|
452
|
+
const lastCmd = 'posture';
|
|
453
|
+
const statusParts = [mode, connect, posture, `${activeRuns}`, lastCmd];
|
|
454
|
+
const statusText = statusParts.join(' | ');
|
|
455
|
+
const fullText = `${commandText} | {${getBlessedColor('mutedGrey')}-fg}${statusText}{/}`;
|
|
456
|
+
return blessedLib.box({
|
|
457
|
+
top,
|
|
458
|
+
left,
|
|
459
|
+
width,
|
|
460
|
+
height,
|
|
461
|
+
content: fullText,
|
|
462
|
+
tags: true,
|
|
463
|
+
style: {
|
|
464
|
+
fg: getBlessedColor('standardWhite'),
|
|
465
|
+
},
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
//# sourceMappingURL=hub.js.map
|