@pellux/goodvibes-tui 1.1.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +85 -21
- package/README.md +28 -18
- package/docs/foundation-artifacts/operator-contract.json +4045 -1763
- package/package.json +2 -2
- package/src/audio/spoken-turn-controller.ts +12 -2
- package/src/audio/spoken-turn-wiring.ts +2 -1
- package/src/cli/help.ts +8 -1
- package/src/cli/service-posture.ts +2 -1
- package/src/cli/status.ts +2 -1
- package/src/cli/surface-command.ts +2 -2
- package/src/core/alert-gating.ts +67 -0
- package/src/core/approval-alert.ts +72 -0
- package/src/core/budget-breach-notifier.ts +106 -0
- package/src/core/composer-state.ts +11 -3
- package/src/core/conversation-line-cache.ts +27 -3
- package/src/core/conversation-rendering.ts +90 -14
- package/src/core/conversation.ts +18 -0
- package/src/core/focus-tracker.ts +41 -0
- package/src/core/long-task-notifier.ts +33 -6
- package/src/core/stream-event-wiring.ts +20 -1
- package/src/core/system-message-noise.ts +87 -0
- package/src/core/system-message-router.ts +104 -51
- package/src/core/turn-cancellation.ts +25 -0
- package/src/core/turn-event-wiring.ts +72 -3
- package/src/daemon/cli.ts +29 -2
- package/src/daemon/handlers/register.ts +8 -1
- package/src/daemon/service-commands.ts +329 -0
- package/src/export/cost-utils.ts +36 -0
- package/src/input/autocomplete.ts +27 -1
- package/src/input/command-registry.ts +84 -5
- package/src/input/commands/checkpoint-runtime.ts +280 -0
- package/src/input/commands/codebase-runtime.ts +232 -0
- package/src/input/commands/config.ts +43 -3
- package/src/input/commands/eval.ts +1 -1
- package/src/input/commands/health-runtime.ts +10 -2
- package/src/input/commands/image-runtime.ts +112 -0
- package/src/input/commands/intelligence-runtime.ts +11 -1
- package/src/input/commands/local-auth-runtime.ts +4 -1
- package/src/input/commands/local-runtime.ts +9 -3
- package/src/input/commands/marketplace-runtime.ts +2 -2
- package/src/input/commands/memory.ts +73 -35
- package/src/input/commands/operator-panel-runtime.ts +71 -31
- package/src/input/commands/planning-runtime.ts +116 -4
- package/src/input/commands/platform-sandbox-runtime.ts +1 -6
- package/src/input/commands/plugin-runtime.ts +2 -2
- package/src/input/commands/policy-dispatch.ts +21 -0
- package/src/input/commands/provider-accounts-runtime.ts +1 -1
- package/src/input/commands/qrcode-runtime.ts +26 -6
- package/src/input/commands/recall-review.ts +35 -0
- package/src/input/commands/remote-runtime-setup.ts +5 -3
- package/src/input/commands/remote-runtime.ts +3 -3
- package/src/input/commands/runtime-services.ts +54 -13
- package/src/input/commands/services-runtime.ts +1 -1
- package/src/input/commands/session-content.ts +20 -9
- package/src/input/commands/session-workflow.ts +16 -1
- package/src/input/commands/settings-sync-runtime.ts +16 -4
- package/src/input/commands/shell-core.ts +25 -8
- package/src/input/commands/skills-runtime.ts +2 -8
- package/src/input/commands/subscription-runtime.ts +2 -2
- package/src/input/commands/test-runtime.ts +277 -0
- package/src/input/commands/websearch-runtime.ts +101 -0
- package/src/input/commands/work-plan-runtime.ts +36 -10
- package/src/input/commands/workstream-runtime.ts +488 -0
- package/src/input/commands.ts +12 -0
- package/src/input/config-modal-types.ts +175 -0
- package/src/input/config-modal.ts +592 -0
- package/src/input/feed-context-factory.ts +10 -8
- package/src/input/handler-command-route.ts +37 -20
- package/src/input/handler-content-actions.ts +17 -2
- package/src/input/handler-feed-routes.ts +74 -114
- package/src/input/handler-feed.ts +79 -27
- package/src/input/handler-interactions.ts +1 -3
- package/src/input/handler-modal-routes.ts +136 -6
- package/src/input/handler-modal-stack.ts +14 -7
- package/src/input/handler-modal-token-routes.ts +16 -49
- package/src/input/handler-onboarding-daemon-adopt.ts +149 -0
- package/src/input/handler-onboarding.ts +71 -59
- package/src/input/handler-picker-routes.ts +26 -102
- package/src/input/handler-shortcuts.ts +74 -14
- package/src/input/handler-types.ts +2 -6
- package/src/input/handler-ui-state.ts +10 -22
- package/src/input/handler.ts +12 -29
- package/src/input/keybindings.ts +23 -8
- package/src/input/model-picker-types.ts +24 -6
- package/src/input/model-picker.ts +77 -2
- package/src/input/onboarding/onboarding-runtime-status.ts +10 -1
- package/src/input/onboarding/onboarding-wizard-apply.ts +8 -1
- package/src/input/onboarding/onboarding-wizard-constants.ts +4 -1
- package/src/input/onboarding/onboarding-wizard-network-adopt.ts +136 -0
- package/src/input/onboarding/onboarding-wizard-steps.ts +9 -6
- package/src/input/onboarding/onboarding-wizard-types.ts +3 -1
- package/src/input/panel-integration-actions.ts +9 -170
- package/src/input/panel-mouse-geometry.ts +97 -0
- package/src/input/panel-paste-flood-guard.ts +86 -0
- package/src/input/selection-modal.ts +11 -0
- package/src/input/session-picker-modal.ts +44 -4
- package/src/input/settings-modal-data.ts +146 -2
- package/src/input/settings-modal-mutations.ts +6 -4
- package/src/input/settings-modal-types.ts +4 -2
- package/src/main.ts +49 -51
- package/src/panels/agent-inspector-shared.ts +2 -1
- package/src/panels/base-panel.ts +22 -1
- package/src/panels/builtin/agent.ts +21 -107
- package/src/panels/builtin/development.ts +15 -61
- package/src/panels/builtin/knowledge.ts +8 -32
- package/src/panels/builtin/operations.ts +84 -428
- package/src/panels/builtin/session.ts +21 -112
- package/src/panels/builtin/shared.ts +18 -46
- package/src/panels/builtin-modals.ts +218 -0
- package/src/panels/builtin-panels.ts +5 -0
- package/src/panels/cost-tracker-panel.ts +36 -10
- package/src/panels/diff-panel.ts +12 -43
- package/src/panels/eval-registry.ts +60 -0
- package/src/panels/fleet-deep-link.ts +31 -0
- package/src/panels/fleet-panel-format.ts +62 -0
- package/src/panels/fleet-panel-worktree-detail.ts +48 -0
- package/src/panels/fleet-panel.ts +758 -0
- package/src/panels/fleet-read-model.ts +520 -0
- package/src/panels/fleet-steer.ts +125 -0
- package/src/panels/fleet-stop.ts +153 -0
- package/src/panels/fleet-tabs.ts +230 -0
- package/src/panels/fleet-transcript.ts +356 -0
- package/src/panels/index.ts +7 -31
- package/src/panels/modals/hooks-modal.ts +187 -0
- package/src/panels/modals/keybindings-modal.ts +166 -0
- package/src/panels/modals/knowledge-modal.ts +158 -0
- package/src/panels/modals/local-auth-modal.ts +132 -0
- package/src/panels/modals/marketplace-modal.ts +218 -0
- package/src/panels/modals/memory-modal.ts +180 -0
- package/src/panels/modals/modal-surface-helpers.ts +54 -0
- package/src/panels/modals/modal-theme.ts +42 -0
- package/src/panels/modals/pairing-modal.ts +163 -0
- package/src/panels/modals/planning-modal.ts +304 -0
- package/src/panels/modals/plugins-modal.ts +174 -0
- package/src/panels/modals/policy-modal.ts +256 -0
- package/src/panels/modals/provider-health-modal.ts +136 -0
- package/src/panels/modals/remote-modal.ts +115 -0
- package/src/panels/modals/sandbox-modal.ts +150 -0
- package/src/panels/modals/security-modal.ts +217 -0
- package/src/panels/modals/services-modal.ts +179 -0
- package/src/panels/modals/settings-sync-modal.ts +142 -0
- package/src/panels/modals/skills-modal.ts +189 -0
- package/src/panels/modals/subscription-modal.ts +172 -0
- package/src/panels/modals/work-plan-modal.ts +177 -0
- package/src/panels/panel-confirm-overlay.ts +72 -0
- package/src/panels/panel-manager.ts +123 -9
- package/src/panels/polish-core.ts +38 -25
- package/src/panels/project-planning-answer-actions.ts +8 -11
- package/src/panels/skills-panel.ts +7 -51
- package/src/panels/types.ts +37 -0
- package/src/permissions/hunk-selection.ts +179 -0
- package/src/permissions/prompt.ts +216 -13
- package/src/renderer/autocomplete-overlay.ts +28 -2
- package/src/renderer/compaction-history-modal.ts +19 -3
- package/src/renderer/compaction-preview.ts +13 -2
- package/src/renderer/compaction-quality.ts +100 -0
- package/src/renderer/compositor.ts +2 -3
- package/src/renderer/config-modal.ts +95 -0
- package/src/renderer/conversation-overlays.ts +10 -17
- package/src/renderer/fleet-tab-strip.ts +56 -0
- package/src/renderer/footer-tips.ts +15 -3
- package/src/renderer/fullscreen-primitives.ts +32 -22
- package/src/renderer/git-status.ts +43 -1
- package/src/renderer/help-overlay.ts +2 -2
- package/src/renderer/layout.ts +0 -4
- package/src/renderer/markdown.ts +7 -3
- package/src/renderer/modal-factory.ts +25 -20
- package/src/renderer/model-workspace.ts +62 -4
- package/src/renderer/overlay-box.ts +21 -17
- package/src/renderer/panel-workspace-bar.ts +8 -2
- package/src/renderer/process-indicator.ts +14 -3
- package/src/renderer/selection-modal-overlay.ts +6 -1
- package/src/renderer/session-picker-modal.ts +196 -3
- package/src/renderer/settings-modal-helpers.ts +2 -0
- package/src/renderer/settings-modal.ts +7 -0
- package/src/renderer/shell-surface.ts +8 -1
- package/src/renderer/status-glyphs.ts +14 -15
- package/src/renderer/system-message.ts +15 -3
- package/src/renderer/terminal-bg-probe.ts +339 -0
- package/src/renderer/terminal-escapes.ts +20 -0
- package/src/renderer/theme-mode-config.ts +67 -0
- package/src/renderer/theme.ts +91 -1
- package/src/renderer/thinking.ts +11 -3
- package/src/renderer/tool-call.ts +15 -9
- package/src/renderer/tool-result-summary.ts +148 -0
- package/src/renderer/turn-injection.ts +133 -0
- package/src/renderer/ui-factory.ts +154 -85
- package/src/renderer/ui-primitives.ts +30 -129
- package/src/runtime/bootstrap-command-context.ts +27 -1
- package/src/runtime/bootstrap-command-parts.ts +41 -10
- package/src/runtime/bootstrap-core.ts +47 -15
- package/src/runtime/bootstrap-hook-bridge.ts +7 -0
- package/src/runtime/bootstrap-shell.ts +48 -17
- package/src/runtime/bootstrap.ts +129 -22
- package/src/runtime/code-index-services.ts +135 -0
- package/src/runtime/legacy-daemon-migration.ts +516 -0
- package/src/runtime/memory-fold.ts +26 -0
- package/src/runtime/onboarding/derivation.ts +7 -2
- package/src/runtime/onboarding/snapshot.ts +27 -1
- package/src/runtime/onboarding/types.ts +31 -1
- package/src/runtime/operator-token-cleanup.ts +82 -1
- package/src/runtime/orchestrator-core-services.ts +59 -0
- package/src/runtime/process-lifecycle.ts +3 -1
- package/src/runtime/resume-notice.ts +209 -0
- package/src/runtime/services.ts +98 -46
- package/src/runtime/session-inbound-inputs.ts +252 -0
- package/src/runtime/session-spine-transport.ts +64 -0
- package/src/runtime/terminal-output-guard.ts +15 -8
- package/src/runtime/ui-services.ts +27 -3
- package/src/runtime/workstream-services.ts +327 -0
- package/src/runtime/wrfc-persistence.ts +124 -17
- package/src/shell/blocking-input.ts +68 -4
- package/src/shell/recovery-input-helpers.ts +170 -1
- package/src/shell/ui-openers.ts +171 -26
- package/src/utils/format-duration.ts +8 -18
- package/src/utils/splash-lines.ts +1 -1
- package/src/utils/terminal-width.ts +52 -0
- package/src/version.ts +1 -1
- package/src/panels/agent-inspector-panel.ts +0 -786
- package/src/panels/approval-panel.ts +0 -252
- package/src/panels/automation-control-panel.ts +0 -479
- package/src/panels/cockpit-panel.ts +0 -481
- package/src/panels/cockpit-read-model.ts +0 -238
- package/src/panels/communication-panel.ts +0 -256
- package/src/panels/control-plane-panel.ts +0 -470
- package/src/panels/debug-panel.ts +0 -615
- package/src/panels/docs-panel.ts +0 -384
- package/src/panels/eval-panel.ts +0 -627
- package/src/panels/file-explorer-panel.ts +0 -673
- package/src/panels/file-preview-panel.ts +0 -517
- package/src/panels/hooks-panel.ts +0 -401
- package/src/panels/incident-review-panel.ts +0 -406
- package/src/panels/intelligence-panel.ts +0 -383
- package/src/panels/knowledge-graph-panel.ts +0 -515
- package/src/panels/marketplace-panel.ts +0 -399
- package/src/panels/memory-panel.ts +0 -558
- package/src/panels/ops-control-panel.ts +0 -329
- package/src/panels/ops-strategy-panel.ts +0 -321
- package/src/panels/orchestration-panel.ts +0 -465
- package/src/panels/panel-list-panel.ts +0 -566
- package/src/panels/plan-dashboard-panel.ts +0 -707
- package/src/panels/policy-panel.ts +0 -517
- package/src/panels/project-planning-panel.ts +0 -731
- package/src/panels/provider-health-panel.ts +0 -678
- package/src/panels/provider-health-tracker.ts +0 -310
- package/src/panels/provider-health-views.ts +0 -567
- package/src/panels/qr-panel.ts +0 -280
- package/src/panels/remote-panel.ts +0 -534
- package/src/panels/routes-panel.ts +0 -241
- package/src/panels/sandbox-panel.ts +0 -456
- package/src/panels/security-panel.ts +0 -447
- package/src/panels/services-panel.ts +0 -329
- package/src/panels/session-browser-panel.ts +0 -496
- package/src/panels/settings-sync-panel.ts +0 -398
- package/src/panels/subscription-panel.ts +0 -342
- package/src/panels/symbol-outline-panel.ts +0 -619
- package/src/panels/system-messages-panel.ts +0 -364
- package/src/panels/tasks-panel.ts +0 -608
- package/src/panels/thinking-panel.ts +0 -333
- package/src/panels/tool-inspector-panel.ts +0 -537
- package/src/panels/work-plan-panel.ts +0 -540
- package/src/panels/worktree-panel.ts +0 -360
- package/src/panels/wrfc-panel.ts +0 -790
- package/src/renderer/agent-detail-modal.ts +0 -466
- package/src/renderer/live-tail-modal.ts +0 -156
- package/src/renderer/process-modal.ts +0 -671
- package/src/renderer/qr-renderer.ts +0 -120
|
@@ -1,329 +0,0 @@
|
|
|
1
|
-
import type { Line } from '../types/grid.ts';
|
|
2
|
-
import { createEmptyLine } from '../types/grid.ts';
|
|
3
|
-
import { truncateDisplay } from '../utils/terminal-width.ts';
|
|
4
|
-
import { ScrollableListPanel } from './scrollable-list-panel.ts';
|
|
5
|
-
import type { PanelIntegrationContext } from './types.ts';
|
|
6
|
-
import {
|
|
7
|
-
type ServiceConfig,
|
|
8
|
-
type ServiceInspection,
|
|
9
|
-
type ServiceConnectionTestResult,
|
|
10
|
-
} from '@pellux/goodvibes-sdk/platform/config';
|
|
11
|
-
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
12
|
-
import type { ServiceInspectionQuery, SubscriptionAccessQuery } from '../runtime/ui-service-queries.ts';
|
|
13
|
-
import {
|
|
14
|
-
buildDetailBlock,
|
|
15
|
-
buildKeyValueLine,
|
|
16
|
-
buildKeyboardHints,
|
|
17
|
-
buildPanelLine,
|
|
18
|
-
buildPanelWorkspace,
|
|
19
|
-
buildStatusPill,
|
|
20
|
-
DEFAULT_PANEL_PALETTE,
|
|
21
|
-
} from './polish.ts';
|
|
22
|
-
|
|
23
|
-
// Base chrome only — title band, state colors, and text tokens all come
|
|
24
|
-
// straight from DEFAULT_PANEL_PALETTE (WO-002).
|
|
25
|
-
const C = DEFAULT_PANEL_PALETTE;
|
|
26
|
-
|
|
27
|
-
interface ServicePanelEntry {
|
|
28
|
-
readonly name: string;
|
|
29
|
-
// null when registry.inspect(name) resolved null (e.g. a config entry that
|
|
30
|
-
// disappeared or failed to resolve between getAll() and inspect()) — the
|
|
31
|
-
// row still renders, flagged, instead of crashing the whole refresh.
|
|
32
|
-
readonly inspection: ServiceInspection | null;
|
|
33
|
-
readonly lastTest?: ServiceConnectionTestResult;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function statusLabel(entry: ServicePanelEntry): string {
|
|
37
|
-
if (!entry.inspection) return 'INSPECT FAILED';
|
|
38
|
-
if (!entry.inspection.hasPrimaryCredential) return 'UNCONFIGURED';
|
|
39
|
-
if (entry.lastTest?.ok) return 'HEALTHY';
|
|
40
|
-
if (entry.lastTest && !entry.lastTest.ok) return 'ERROR';
|
|
41
|
-
return 'CONFIGURED';
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function statusColor(entry: ServicePanelEntry): string {
|
|
45
|
-
const label = statusLabel(entry);
|
|
46
|
-
if (label === 'HEALTHY') return C.good;
|
|
47
|
-
if (label === 'ERROR' || label === 'INSPECT FAILED') return C.bad;
|
|
48
|
-
if (label === 'CONFIGURED') return C.warn;
|
|
49
|
-
return C.dim;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function authSummary(config: ServiceConfig, manager: SubscriptionAccessQuery): string {
|
|
53
|
-
const provider = config.providerId ?? config.name;
|
|
54
|
-
const hasOverride = manager.getAccessToken(provider) != null;
|
|
55
|
-
switch (config.authType) {
|
|
56
|
-
case 'bearer':
|
|
57
|
-
return hasOverride ? 'bearer+subscription' : 'bearer';
|
|
58
|
-
case 'basic':
|
|
59
|
-
return 'basic';
|
|
60
|
-
case 'api-key':
|
|
61
|
-
return hasOverride
|
|
62
|
-
? 'oauth-override'
|
|
63
|
-
: config.apiKeyHeader ? `api-key:${config.apiKeyHeader}` : 'api-key';
|
|
64
|
-
case 'oauth':
|
|
65
|
-
return manager.get(provider) != null ? 'oauth(active)' : 'oauth';
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export class ServicesPanel extends ScrollableListPanel<ServicePanelEntry> {
|
|
70
|
-
private readonly registry: ServiceInspectionQuery;
|
|
71
|
-
private readonly subscriptionManager: SubscriptionAccessQuery;
|
|
72
|
-
private entries: ServicePanelEntry[] = [];
|
|
73
|
-
private loading = false;
|
|
74
|
-
// s = open the Subscription panel via the handleInput -> handlePanelIntegrationAction
|
|
75
|
-
// bridge (handleInput has no ctx.panelManager — same pattern used elsewhere in
|
|
76
|
-
// this wave, e.g. session-browser-panel.ts's pendingOpenPanels).
|
|
77
|
-
private pendingOpenSubscription = false;
|
|
78
|
-
|
|
79
|
-
public constructor(
|
|
80
|
-
registry: ServiceInspectionQuery,
|
|
81
|
-
subscriptionManager: SubscriptionAccessQuery,
|
|
82
|
-
) {
|
|
83
|
-
super('services', 'Services', 'V', 'providers');
|
|
84
|
-
this.showSelectionGutter = true; // I5: non-color selection affordance
|
|
85
|
-
this.filterEnabled = true;
|
|
86
|
-
this.filterLabel = 'Filter services';
|
|
87
|
-
this.registry = registry;
|
|
88
|
-
this.subscriptionManager = subscriptionManager;
|
|
89
|
-
void this.refresh();
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
protected override filterMatches(entry: ServicePanelEntry, q: string): boolean {
|
|
93
|
-
return entry.name.toLowerCase().includes(q)
|
|
94
|
-
|| (entry.inspection?.config.baseUrl ?? '').toLowerCase().includes(q);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
public override onActivate(): void {
|
|
98
|
-
super.onActivate();
|
|
99
|
-
if (this.entries.length === 0 && !this.loading) {
|
|
100
|
-
void this.refresh();
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
protected override getPalette() { return C; }
|
|
105
|
-
protected override getEmptyStateMessage() { return ' No services configured.'; }
|
|
106
|
-
protected override getEmptyStateActions() {
|
|
107
|
-
return [
|
|
108
|
-
{ command: '/services auth-review', summary: 'inspect service auth posture and registry config' },
|
|
109
|
-
{ command: 's', summary: 'open the Subscription panel — review provider login state and override posture' },
|
|
110
|
-
];
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
protected getItems(): readonly ServicePanelEntry[] {
|
|
114
|
-
return this.entries;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
protected renderItem(entry: ServicePanelEntry, index: number, selected: boolean, width: number): Line {
|
|
118
|
-
const bg = selected ? C.selectBg : undefined;
|
|
119
|
-
const authText = entry.inspection ? authSummary(entry.inspection.config, this.subscriptionManager) : 'n/a';
|
|
120
|
-
const baseUrlText = entry.inspection ? (entry.inspection.config.baseUrl ?? '(no baseUrl)') : '(inspection failed)';
|
|
121
|
-
return buildPanelLine(width, [
|
|
122
|
-
[' ', C.label, bg],
|
|
123
|
-
[entry.name.padEnd(16), C.value, bg],
|
|
124
|
-
[` ${statusLabel(entry).padEnd(12)}`, statusColor(entry), bg],
|
|
125
|
-
[` ${authText.padEnd(18)}`, C.info, bg],
|
|
126
|
-
[` ${truncateDisplay(baseUrlText, Math.max(0, width - 48))}`, C.dim, bg],
|
|
127
|
-
]);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
public handleInput(key: string): boolean {
|
|
131
|
-
if (!this.filterActive) {
|
|
132
|
-
if (key === 'r') {
|
|
133
|
-
void this.refresh();
|
|
134
|
-
return true;
|
|
135
|
-
}
|
|
136
|
-
if (key === 't') {
|
|
137
|
-
void this.testSelected();
|
|
138
|
-
return true;
|
|
139
|
-
}
|
|
140
|
-
if (key === 'T') {
|
|
141
|
-
void this.testAll();
|
|
142
|
-
return true;
|
|
143
|
-
}
|
|
144
|
-
if (key === 's') {
|
|
145
|
-
this.pendingOpenSubscription = true;
|
|
146
|
-
return true;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
return super.handleInput(key);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/** Drains the 's' key's pending open request via the ctx.panelManager bridge. */
|
|
153
|
-
handlePanelIntegrationAction(_key: string, ctx: PanelIntegrationContext): boolean {
|
|
154
|
-
if (!this.pendingOpenSubscription) return false;
|
|
155
|
-
this.pendingOpenSubscription = false;
|
|
156
|
-
ctx.panelManager.open('subscription');
|
|
157
|
-
return true;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
private async refresh(): Promise<void> {
|
|
161
|
-
this.loading = true;
|
|
162
|
-
this.markDirty();
|
|
163
|
-
try {
|
|
164
|
-
const configs = this.registry.getAll();
|
|
165
|
-
const names = Object.keys(configs).sort((a, b) => a.localeCompare(b));
|
|
166
|
-
const inspections = await Promise.all(
|
|
167
|
-
names.map(async (name) => ({
|
|
168
|
-
name,
|
|
169
|
-
inspection: await this.registry.inspect(name),
|
|
170
|
-
})),
|
|
171
|
-
);
|
|
172
|
-
const previousTests = new Map(this.entries.map((entry) => [entry.name, entry.lastTest] as const));
|
|
173
|
-
this.entries = inspections.map((entry) => ({
|
|
174
|
-
...entry,
|
|
175
|
-
...(previousTests.get(entry.name) ? { lastTest: previousTests.get(entry.name) } : {}),
|
|
176
|
-
}));
|
|
177
|
-
this.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.entries.length - 1));
|
|
178
|
-
} catch (e) {
|
|
179
|
-
this.setError(`Refresh failed: ${summarizeError(e)}`);
|
|
180
|
-
} finally {
|
|
181
|
-
// Always clear the loading flag — a thrown getAll()/inspect() must
|
|
182
|
-
// never leave the panel stuck on the "Loading configured services..." screen.
|
|
183
|
-
this.loading = false;
|
|
184
|
-
this.markDirty();
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
private async testSelected(): Promise<void> {
|
|
189
|
-
// selectedIndex tracks the filtered view, so the acted-on entry must come
|
|
190
|
-
// from getVisibleItems() — this.entries would desync under a '/' filter.
|
|
191
|
-
const selected = this.getSelectedItem();
|
|
192
|
-
if (!selected) return;
|
|
193
|
-
try {
|
|
194
|
-
const result = await this.registry.testConnection(selected.name);
|
|
195
|
-
this.entries = this.entries.map((entry) => (
|
|
196
|
-
entry.name === selected.name
|
|
197
|
-
? { ...entry, lastTest: result }
|
|
198
|
-
: entry
|
|
199
|
-
));
|
|
200
|
-
} catch (e) {
|
|
201
|
-
this.setError(`Test failed: ${summarizeError(e)}`);
|
|
202
|
-
}
|
|
203
|
-
this.markDirty();
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/** T = test every configured service's connection, one call per row (services-runtime.ts's single-service test pattern, looped). */
|
|
207
|
-
private async testAll(): Promise<void> {
|
|
208
|
-
for (const entry of this.entries) {
|
|
209
|
-
try {
|
|
210
|
-
const result = await this.registry.testConnection(entry.name);
|
|
211
|
-
this.entries = this.entries.map((e) => (e.name === entry.name ? { ...e, lastTest: result } : e));
|
|
212
|
-
} catch (e) {
|
|
213
|
-
this.setError(`Test failed for ${entry.name}: ${summarizeError(e)}`);
|
|
214
|
-
}
|
|
215
|
-
this.markDirty();
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
public render(width: number, height: number): Line[] {
|
|
220
|
-
this.clampSelection();
|
|
221
|
-
const intro = 'Credential posture, subscription overrides, and live connection checks for configured services.';
|
|
222
|
-
|
|
223
|
-
if (this.loading && this.entries.length === 0) {
|
|
224
|
-
const lines = buildPanelWorkspace(width, height, {
|
|
225
|
-
title: 'Service Control Room',
|
|
226
|
-
intro,
|
|
227
|
-
sections: [{ lines: [buildPanelLine(width, [[' Loading configured services...', C.info]])] }],
|
|
228
|
-
palette: C,
|
|
229
|
-
});
|
|
230
|
-
while (lines.length < height) lines.push(createEmptyLine(width));
|
|
231
|
-
return lines;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// Health-summary header — surface the most important counts first.
|
|
235
|
-
const counts = { healthy: 0, error: 0, configured: 0, unconfigured: 0 };
|
|
236
|
-
for (const entry of this.entries) {
|
|
237
|
-
const label = statusLabel(entry);
|
|
238
|
-
if (label === 'HEALTHY') counts.healthy++;
|
|
239
|
-
else if (label === 'ERROR') counts.error++;
|
|
240
|
-
else if (label === 'CONFIGURED') counts.configured++;
|
|
241
|
-
else counts.unconfigured++;
|
|
242
|
-
}
|
|
243
|
-
const headerLines: Line[] = [
|
|
244
|
-
buildKeyValueLine(width, [
|
|
245
|
-
{ label: 'services', value: String(this.entries.length), valueColor: this.entries.length > 0 ? C.info : C.dim },
|
|
246
|
-
{ label: 'healthy', value: String(counts.healthy), valueColor: counts.healthy > 0 ? C.good : C.dim },
|
|
247
|
-
{ label: 'errors', value: String(counts.error), valueColor: counts.error > 0 ? C.bad : C.dim },
|
|
248
|
-
{ label: 'unconfigured', value: String(counts.unconfigured), valueColor: counts.unconfigured > 0 ? C.warn : C.dim },
|
|
249
|
-
], C),
|
|
250
|
-
];
|
|
251
|
-
|
|
252
|
-
// Detail must describe the row the (possibly filtered) list highlights.
|
|
253
|
-
const selected = this.getSelectedItem();
|
|
254
|
-
const detailRows: Line[] = [];
|
|
255
|
-
if (selected) {
|
|
256
|
-
const inspect = selected.inspection;
|
|
257
|
-
detailRows.push(buildPanelLine(width, [
|
|
258
|
-
[' Service: ', C.label],
|
|
259
|
-
[selected.name, C.value],
|
|
260
|
-
[' State: ', C.label],
|
|
261
|
-
[statusLabel(selected), statusColor(selected)],
|
|
262
|
-
[' Auth: ', C.label],
|
|
263
|
-
[inspect ? authSummary(inspect.config, this.subscriptionManager) : 'n/a', C.info],
|
|
264
|
-
]));
|
|
265
|
-
if (!inspect) {
|
|
266
|
-
detailRows.push(buildPanelLine(width, [
|
|
267
|
-
[' Inspection failed — the registry could not resolve this service\'s config. Press r to retry.', C.bad],
|
|
268
|
-
]));
|
|
269
|
-
} else {
|
|
270
|
-
detailRows.push(buildPanelLine(width, [
|
|
271
|
-
[' Base URL: ', C.label],
|
|
272
|
-
[truncateDisplay(inspect.config.baseUrl ?? '(no baseUrl)', Math.max(0, width - 13)), C.dim],
|
|
273
|
-
]));
|
|
274
|
-
detailRows.push(buildPanelLine(width, [
|
|
275
|
-
[' Primary credential: ', C.label],
|
|
276
|
-
...buildStatusPill(inspect.hasPrimaryCredential ? 'good' : 'bad', inspect.hasPrimaryCredential ? 'present' : 'missing'),
|
|
277
|
-
[' Webhook URL: ', C.label],
|
|
278
|
-
...buildStatusPill(inspect.hasWebhookUrl ? 'good' : 'info', inspect.hasWebhookUrl ? 'present' : 'missing'),
|
|
279
|
-
[' Signing secret: ', C.label],
|
|
280
|
-
...buildStatusPill(inspect.hasSigningSecret ? 'good' : 'info', inspect.hasSigningSecret ? 'present' : 'missing'),
|
|
281
|
-
[' App token: ', C.label],
|
|
282
|
-
...buildStatusPill(inspect.hasAppToken ? 'good' : 'info', inspect.hasAppToken ? 'present' : 'missing'),
|
|
283
|
-
]));
|
|
284
|
-
}
|
|
285
|
-
if (selected.lastTest) {
|
|
286
|
-
detailRows.push(buildPanelLine(width, [
|
|
287
|
-
[' Last test: ', C.label],
|
|
288
|
-
...buildStatusPill(selected.lastTest.ok ? 'good' : 'bad', selected.lastTest.ok ? 'ok' : 'failed'),
|
|
289
|
-
[' Status: ', C.label],
|
|
290
|
-
[selected.lastTest.status != null ? String(selected.lastTest.status) : 'n/a', C.value],
|
|
291
|
-
[' URL: ', C.label],
|
|
292
|
-
[truncateDisplay(selected.lastTest.testedUrl ?? 'n/a', Math.max(0, width - 34)), C.dim],
|
|
293
|
-
]));
|
|
294
|
-
if (selected.lastTest.error) {
|
|
295
|
-
detailRows.push(buildPanelLine(width, [
|
|
296
|
-
[' Error: ', C.label],
|
|
297
|
-
[truncateDisplay(selected.lastTest.error, Math.max(0, width - 10)), C.bad],
|
|
298
|
-
]));
|
|
299
|
-
}
|
|
300
|
-
} else {
|
|
301
|
-
detailRows.push(buildPanelLine(width, [[' Not tested yet — press t to run a live connection check.', C.dim]]));
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
// Context-aware hints: filter mode swaps to filter-specific keys; the test
|
|
306
|
-
// key only makes sense when there is a service to test.
|
|
307
|
-
const hints = this.filterActive
|
|
308
|
-
? [{ keys: 'type', label: 'filter' }, { keys: 'Enter', label: 'apply' }, { keys: 'Esc', label: 'clear' }]
|
|
309
|
-
: [
|
|
310
|
-
{ keys: 'Up/Down', label: 'move' },
|
|
311
|
-
...(selected ? [{ keys: 't', label: 'test selected' }] : []),
|
|
312
|
-
...(this.entries.length > 0 ? [{ keys: 'T', label: 'test all' }] : []),
|
|
313
|
-
{ keys: 'r', label: 'refresh' },
|
|
314
|
-
{ keys: '/', label: 'filter' },
|
|
315
|
-
{ keys: 's', label: 'subscription panel' },
|
|
316
|
-
];
|
|
317
|
-
|
|
318
|
-
const footer: Line[] = selected
|
|
319
|
-
? [...buildDetailBlock(width, `Service · ${selected.name}`, detailRows, C), buildKeyboardHints(width, hints, C)]
|
|
320
|
-
: [buildKeyboardHints(width, hints, C)];
|
|
321
|
-
|
|
322
|
-
return this.renderList(width, height, {
|
|
323
|
-
title: 'Service Control Room',
|
|
324
|
-
header: headerLines,
|
|
325
|
-
footer,
|
|
326
|
-
emptyMessage: intro,
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
}
|