@pellux/goodvibes-agent 0.1.70 → 0.1.71
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 +6 -0
- package/package.json +42 -1
- package/src/agent/skill-discovery.ts +119 -0
- package/src/input/commands/delegation-runtime.ts +0 -8
- package/src/input/commands/experience-runtime.ts +0 -177
- package/src/input/commands/guidance-runtime.ts +0 -69
- package/src/input/commands/local-runtime.ts +1 -57
- package/src/input/commands/local-setup-review.ts +1 -1
- package/src/input/commands/operator-runtime.ts +1 -145
- package/src/input/commands/platform-access-runtime.ts +2 -195
- package/src/input/commands/product-runtime.ts +0 -116
- package/src/input/commands/security-runtime.ts +88 -0
- package/src/input/commands/session-content.ts +0 -97
- package/src/input/commands/shell-core.ts +0 -13
- package/src/input/commands.ts +2 -95
- package/src/panels/builtin/operations.ts +3 -184
- package/src/panels/index.ts +0 -11
- package/src/version.ts +1 -1
- package/src/input/commands/branch-runtime.ts +0 -72
- package/src/input/commands/control-room-runtime.ts +0 -234
- package/src/input/commands/discovery-runtime.ts +0 -61
- package/src/input/commands/hooks-runtime.ts +0 -207
- package/src/input/commands/incident-runtime.ts +0 -106
- package/src/input/commands/integration-runtime.ts +0 -437
- package/src/input/commands/local-setup.ts +0 -288
- package/src/input/commands/managed-runtime.ts +0 -240
- package/src/input/commands/marketplace-runtime.ts +0 -305
- package/src/input/commands/memory-product-runtime.ts +0 -148
- package/src/input/commands/operator-panel-runtime.ts +0 -146
- package/src/input/commands/platform-services-runtime.ts +0 -271
- package/src/input/commands/profile-sync-runtime.ts +0 -110
- package/src/input/commands/provider.ts +0 -363
- package/src/input/commands/remote-runtime-pool.ts +0 -89
- package/src/input/commands/remote-runtime-setup.ts +0 -226
- package/src/input/commands/remote-runtime.ts +0 -432
- package/src/input/commands/replay-runtime.ts +0 -25
- package/src/input/commands/services-runtime.ts +0 -220
- package/src/input/commands/settings-sync-runtime.ts +0 -197
- package/src/input/commands/share-runtime.ts +0 -127
- package/src/input/commands/skills-runtime.ts +0 -226
- package/src/input/commands/teleport-runtime.ts +0 -68
- package/src/panels/cockpit-panel.ts +0 -183
- package/src/panels/communication-panel.ts +0 -153
- package/src/panels/control-plane-panel.ts +0 -211
- package/src/panels/forensics-panel.ts +0 -364
- package/src/panels/hooks-panel.ts +0 -239
- package/src/panels/incident-review-panel.ts +0 -197
- package/src/panels/marketplace-panel.ts +0 -212
- package/src/panels/ops-control-panel.ts +0 -150
- package/src/panels/ops-strategy-panel.ts +0 -235
- package/src/panels/orchestration-panel.ts +0 -272
- package/src/panels/plugins-panel.ts +0 -178
- package/src/panels/remote-panel.ts +0 -449
- package/src/panels/routes-panel.ts +0 -178
- package/src/panels/services-panel.ts +0 -231
- package/src/panels/settings-sync-panel.ts +0 -120
- package/src/panels/skills-panel.ts +0 -431
- package/src/panels/watchers-panel.ts +0 -193
- package/src/verification/live-verifier.ts +0 -588
- package/src/verification/verification-ledger.ts +0 -239
|
@@ -1,449 +0,0 @@
|
|
|
1
|
-
import type { Line } from '../types/grid.ts';
|
|
2
|
-
import { createEmptyLine } from '../types/grid.ts';
|
|
3
|
-
import { BasePanel } from './base-panel.ts';
|
|
4
|
-
import type { UiReadModel, UiRemoteSnapshot } from '../runtime/ui-read-models.ts';
|
|
5
|
-
import {
|
|
6
|
-
buildDetailBlock,
|
|
7
|
-
buildEmptyState,
|
|
8
|
-
buildGuidanceLine,
|
|
9
|
-
buildPanelListRow,
|
|
10
|
-
buildPanelLine,
|
|
11
|
-
buildSummaryBlock,
|
|
12
|
-
buildPanelWorkspace,
|
|
13
|
-
DEFAULT_PANEL_PALETTE,
|
|
14
|
-
resolvePrimaryScrollableSection,
|
|
15
|
-
type PanelWorkspaceSection,
|
|
16
|
-
} from './polish.ts';
|
|
17
|
-
import { truncateDisplay } from '../utils/terminal-width.ts';
|
|
18
|
-
import { getTrackedVisibleWindow } from '../renderer/surface-layout.ts';
|
|
19
|
-
|
|
20
|
-
const C = {
|
|
21
|
-
...DEFAULT_PANEL_PALETTE,
|
|
22
|
-
header: '#94a3b8',
|
|
23
|
-
headerBg: '#1e293b',
|
|
24
|
-
dim: '#475569',
|
|
25
|
-
ok: '#22c55e',
|
|
26
|
-
warn: '#eab308',
|
|
27
|
-
error: '#ef4444',
|
|
28
|
-
} as const;
|
|
29
|
-
|
|
30
|
-
function stateColor(state: string): string {
|
|
31
|
-
switch (state) {
|
|
32
|
-
case 'connected':
|
|
33
|
-
case 'syncing':
|
|
34
|
-
return C.ok;
|
|
35
|
-
case 'degraded':
|
|
36
|
-
case 'reconnecting':
|
|
37
|
-
case 'authenticating':
|
|
38
|
-
case 'initializing':
|
|
39
|
-
return C.warn;
|
|
40
|
-
case 'terminal_failure':
|
|
41
|
-
return C.error;
|
|
42
|
-
default:
|
|
43
|
-
return C.dim;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function formatTimestamp(value?: number): string {
|
|
48
|
-
return value ? new Date(value).toLocaleTimeString() : 'n/a';
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function truncate(text: string, width: number): string {
|
|
52
|
-
return truncateDisplay(text, width);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export class RemotePanel extends BasePanel {
|
|
56
|
-
private readonly readModel?: UiReadModel<UiRemoteSnapshot>;
|
|
57
|
-
private readonly unsub: (() => void) | null;
|
|
58
|
-
private selectedIndex = 0;
|
|
59
|
-
private scrollOffset = 0;
|
|
60
|
-
private browseMode: 'connections' | 'contracts' = 'connections';
|
|
61
|
-
|
|
62
|
-
public constructor(readModel?: UiReadModel<UiRemoteSnapshot>) {
|
|
63
|
-
super('remote', 'Remote', 'R', 'monitoring');
|
|
64
|
-
this.readModel = readModel;
|
|
65
|
-
this.unsub = readModel ? readModel.subscribe(() => this.markDirty()) : null;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
public override onDestroy(): void {
|
|
69
|
-
this.unsub?.();
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
public handleInput(key: string): boolean {
|
|
73
|
-
const activeConnections = this.getActiveConnections();
|
|
74
|
-
const contracts = this.readModel?.getSnapshot().contracts ?? [];
|
|
75
|
-
const browseCount = this.browseMode === 'connections' && activeConnections.length > 0
|
|
76
|
-
? activeConnections.length
|
|
77
|
-
: contracts.length;
|
|
78
|
-
if (key === 'tab' && contracts.length > 0) {
|
|
79
|
-
this.browseMode = this.browseMode === 'connections' ? 'contracts' : 'connections';
|
|
80
|
-
this.selectedIndex = 0;
|
|
81
|
-
this.markDirty();
|
|
82
|
-
return true;
|
|
83
|
-
}
|
|
84
|
-
if (browseCount === 0) return false;
|
|
85
|
-
if (key === 'up' || key === 'k') {
|
|
86
|
-
this.selectedIndex = Math.max(0, this.selectedIndex - 1);
|
|
87
|
-
this.markDirty();
|
|
88
|
-
return true;
|
|
89
|
-
}
|
|
90
|
-
if (key === 'down' || key === 'j') {
|
|
91
|
-
this.selectedIndex = Math.min(browseCount - 1, this.selectedIndex + 1);
|
|
92
|
-
this.markDirty();
|
|
93
|
-
return true;
|
|
94
|
-
}
|
|
95
|
-
if (key === 'home') {
|
|
96
|
-
this.selectedIndex = 0;
|
|
97
|
-
this.markDirty();
|
|
98
|
-
return true;
|
|
99
|
-
}
|
|
100
|
-
if (key === 'end') {
|
|
101
|
-
this.selectedIndex = Math.max(0, browseCount - 1);
|
|
102
|
-
this.markDirty();
|
|
103
|
-
return true;
|
|
104
|
-
}
|
|
105
|
-
return false;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
private getActiveConnections() {
|
|
109
|
-
return this.readModel?.getSnapshot().acp.activeConnections ?? [];
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
public render(width: number, height: number): Line[] {
|
|
113
|
-
this.needsRender = false;
|
|
114
|
-
const intro = 'Remote worker, bridge, and review-artifact posture for delegated work.';
|
|
115
|
-
|
|
116
|
-
if (!this.readModel) {
|
|
117
|
-
const sectionLines = buildEmptyState(
|
|
118
|
-
width,
|
|
119
|
-
' Runtime store not wired into this panel yet.',
|
|
120
|
-
'The remote review workspace needs the shell read model so it can display worker state and review artifacts.',
|
|
121
|
-
[
|
|
122
|
-
{ command: '/remote setup', summary: 'review bootstrap, env, tunnel, and bridge guidance' },
|
|
123
|
-
{ command: '/remote panel', summary: 'reopen the panel from the shell-owned runtime' },
|
|
124
|
-
],
|
|
125
|
-
C,
|
|
126
|
-
);
|
|
127
|
-
const lines = buildPanelWorkspace(width, height, {
|
|
128
|
-
title: 'Remote Work Review',
|
|
129
|
-
intro,
|
|
130
|
-
sections: [{ lines: sectionLines }],
|
|
131
|
-
palette: C,
|
|
132
|
-
});
|
|
133
|
-
while (lines.length < height) lines.push(createEmptyLine(width));
|
|
134
|
-
return lines;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const snapshot = this.readModel.getSnapshot();
|
|
138
|
-
const daemon = snapshot.daemon;
|
|
139
|
-
const acp = snapshot.acp;
|
|
140
|
-
const activeConnections = this.getActiveConnections();
|
|
141
|
-
const artifactCount = snapshot.artifacts.length;
|
|
142
|
-
const pools = snapshot.pools;
|
|
143
|
-
const contracts = snapshot.contracts;
|
|
144
|
-
const supervisor = snapshot.supervisor;
|
|
145
|
-
const distributed = {
|
|
146
|
-
pairRequests: { pending: snapshot.distributed.pairRequests.length },
|
|
147
|
-
peers: {
|
|
148
|
-
total: snapshot.distributed.peers.length,
|
|
149
|
-
connected: snapshot.distributed.peers.filter((peer) => peer.status === 'connected').length,
|
|
150
|
-
nodes: snapshot.distributed.peers.filter((peer) => peer.kind === 'node').length,
|
|
151
|
-
devices: snapshot.distributed.peers.filter((peer) => peer.kind === 'device').length,
|
|
152
|
-
},
|
|
153
|
-
work: {
|
|
154
|
-
queued: snapshot.distributed.work.filter((item) => item.status === 'queued').length,
|
|
155
|
-
claimed: snapshot.distributed.work.filter((item) => item.status === 'claimed').length,
|
|
156
|
-
},
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
const postureLines: Line[] = [
|
|
160
|
-
buildPanelLine(width, [
|
|
161
|
-
[' runtime ', C.label],
|
|
162
|
-
[daemon.transportState.toUpperCase(), stateColor(daemon.transportState)],
|
|
163
|
-
[' running ', C.label],
|
|
164
|
-
[daemon.isRunning ? 'yes' : 'no', daemon.isRunning ? C.ok : C.dim],
|
|
165
|
-
[' reconnects ', C.label],
|
|
166
|
-
[String(daemon.reconnectAttempts), daemon.reconnectAttempts > 0 ? C.warn : C.ok],
|
|
167
|
-
[' jobs ', C.label],
|
|
168
|
-
[String(daemon.runningJobCount), daemon.runningJobCount > 0 ? C.info : C.dim],
|
|
169
|
-
]),
|
|
170
|
-
buildPanelLine(width, [
|
|
171
|
-
[' ACP manager ', C.label],
|
|
172
|
-
[acp.transportState.toUpperCase(), stateColor(acp.transportState)],
|
|
173
|
-
[' active connections ', C.label],
|
|
174
|
-
[String(acp.activeConnections.length), acp.activeConnections.length > 0 ? C.info : C.dim],
|
|
175
|
-
[' total messages ', C.label],
|
|
176
|
-
[String(acp.totalMessages), acp.totalMessages > 0 ? C.value : C.dim],
|
|
177
|
-
]),
|
|
178
|
-
buildPanelLine(width, [
|
|
179
|
-
[' worker contracts ', C.label],
|
|
180
|
-
[String(contracts.length), C.info],
|
|
181
|
-
[' pools ', C.label],
|
|
182
|
-
[String(pools.length), pools.length > 0 ? C.info : C.dim],
|
|
183
|
-
[' review artifacts ', C.label],
|
|
184
|
-
[String(artifactCount), artifactCount > 0 ? C.ok : C.dim],
|
|
185
|
-
]),
|
|
186
|
-
buildPanelLine(width, [
|
|
187
|
-
[' supervisor ', C.label],
|
|
188
|
-
[String(supervisor.sessions.length), C.info],
|
|
189
|
-
[' degraded ', C.label],
|
|
190
|
-
[String(supervisor.degradedConnections), supervisor.degradedConnections > 0 ? C.warn : C.ok],
|
|
191
|
-
[' distributed peers ', C.label],
|
|
192
|
-
[String(distributed.peers?.total ?? 0), C.info],
|
|
193
|
-
[' connected ', C.label],
|
|
194
|
-
[String(distributed.peers?.connected ?? 0), (distributed.peers?.connected ?? 0) > 0 ? C.ok : C.dim],
|
|
195
|
-
[' queued work ', C.label],
|
|
196
|
-
[String(distributed.work?.queued ?? 0), (distributed.work?.queued ?? 0) > 0 ? C.info : C.dim],
|
|
197
|
-
]),
|
|
198
|
-
];
|
|
199
|
-
|
|
200
|
-
if (daemon.lastError) {
|
|
201
|
-
postureLines.push(buildPanelLine(width, [
|
|
202
|
-
[' runtime error ', C.label],
|
|
203
|
-
[daemon.lastError.slice(0, Math.max(0, width - 14)), C.error],
|
|
204
|
-
]));
|
|
205
|
-
}
|
|
206
|
-
postureLines.push(
|
|
207
|
-
buildGuidanceLine(width, '/remote recover', 'inspect remote state with worker support and disconnect recovery hints', C),
|
|
208
|
-
buildGuidanceLine(width, '/remote support', 'inspect transport support before routing remote work or reattaching a session', C),
|
|
209
|
-
);
|
|
210
|
-
|
|
211
|
-
const footerLines = [
|
|
212
|
-
buildGuidanceLine(width, '/remote setup', 'review bridge, tunnel, env, and bootstrap flows for self-hosted remote work', C),
|
|
213
|
-
buildPanelLine(width, [[` focus=${this.browseMode} Up/Down move Tab switch connections/contracts`, C.dim]]),
|
|
214
|
-
] as const;
|
|
215
|
-
|
|
216
|
-
if (activeConnections.length === 0 && contracts.length === 0) {
|
|
217
|
-
const idleLines = [
|
|
218
|
-
...postureLines,
|
|
219
|
-
...buildEmptyState(
|
|
220
|
-
width,
|
|
221
|
-
' No active ACP or remote subagent connections.',
|
|
222
|
-
'Remote review is healthy but idle. Worker contracts, session bundles, and bridge pools will appear here once delegated work exists.',
|
|
223
|
-
[
|
|
224
|
-
{ command: '/remote setup', summary: 'review remote bootstrap and environment export' },
|
|
225
|
-
{ command: '/remote env', summary: 'emit a reusable remote shell snippet' },
|
|
226
|
-
{ command: '/bridge status', summary: 'inspect worker pools and existing remote artifacts' },
|
|
227
|
-
],
|
|
228
|
-
C,
|
|
229
|
-
),
|
|
230
|
-
];
|
|
231
|
-
const lines = buildPanelWorkspace(width, height, {
|
|
232
|
-
title: 'Remote Work Review',
|
|
233
|
-
intro,
|
|
234
|
-
sections: [{ lines: buildSummaryBlock(width, 'Remote posture', idleLines, C) }],
|
|
235
|
-
footerLines,
|
|
236
|
-
palette: C,
|
|
237
|
-
});
|
|
238
|
-
while (lines.length < height) lines.push(createEmptyLine(width));
|
|
239
|
-
return lines.slice(0, height);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
const viewingConnections = this.browseMode === 'connections' && activeConnections.length > 0;
|
|
243
|
-
this.selectedIndex = Math.min(
|
|
244
|
-
this.selectedIndex,
|
|
245
|
-
Math.max(0, (viewingConnections ? activeConnections.length : contracts.length) - 1),
|
|
246
|
-
);
|
|
247
|
-
const browseCount = viewingConnections ? activeConnections.length : contracts.length;
|
|
248
|
-
const selected = viewingConnections ? activeConnections[this.selectedIndex] ?? null : null;
|
|
249
|
-
const selectedContract = !viewingConnections ? contracts[this.selectedIndex] ?? null : null;
|
|
250
|
-
const detailRows: Line[] = [];
|
|
251
|
-
|
|
252
|
-
if (selected) {
|
|
253
|
-
detailRows.push(buildPanelLine(width, [
|
|
254
|
-
[' Agent: ', C.label],
|
|
255
|
-
[selected.agentId, C.value],
|
|
256
|
-
[' State: ', C.label],
|
|
257
|
-
[selected.transportState, stateColor(selected.transportState)],
|
|
258
|
-
[' Completing: ', C.label],
|
|
259
|
-
[selected.completing ? 'yes' : 'no', selected.completing ? C.warn : C.dim],
|
|
260
|
-
]));
|
|
261
|
-
detailRows.push(buildPanelLine(width, [
|
|
262
|
-
[' Connected: ', C.label],
|
|
263
|
-
[formatTimestamp(selected.connectedAt), C.dim],
|
|
264
|
-
[' Messages: ', C.label],
|
|
265
|
-
[String(selected.messageCount), selected.messageCount > 0 ? C.info : C.dim],
|
|
266
|
-
[' Errors: ', C.label],
|
|
267
|
-
[String(selected.errorCount), selected.errorCount > 0 ? C.warn : C.dim],
|
|
268
|
-
]));
|
|
269
|
-
if (selected.lastError) {
|
|
270
|
-
detailRows.push(buildPanelLine(width, [
|
|
271
|
-
[' Last error: ', C.label],
|
|
272
|
-
[selected.lastError.slice(0, Math.max(0, width - 13)), C.error],
|
|
273
|
-
]));
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
const contract = contracts.find((entry) => entry.runnerId === selected.agentId);
|
|
277
|
-
if (contract) {
|
|
278
|
-
detailRows.push(buildPanelLine(width, [
|
|
279
|
-
[' Contract: ', C.label],
|
|
280
|
-
[`${contract.template} / ${contract.trustClass}`, C.info],
|
|
281
|
-
[' Pool: ', C.label],
|
|
282
|
-
[contract.poolId ?? '(none)', contract.poolId ? C.info : C.dim],
|
|
283
|
-
]));
|
|
284
|
-
detailRows.push(buildPanelLine(width, [
|
|
285
|
-
[' Depth: ', C.label],
|
|
286
|
-
[String(contract.capabilityCeiling.orchestrationDepth), C.value],
|
|
287
|
-
[' Pools: ', C.label],
|
|
288
|
-
[String(pools.length), pools.length > 0 ? C.info : C.dim],
|
|
289
|
-
]));
|
|
290
|
-
detailRows.push(buildPanelLine(width, [
|
|
291
|
-
[' Protocol: ', C.label],
|
|
292
|
-
[contract.capabilityCeiling.executionProtocol, C.value],
|
|
293
|
-
[' Review: ', C.label],
|
|
294
|
-
[contract.capabilityCeiling.reviewMode, C.value],
|
|
295
|
-
[' Lane: ', C.label],
|
|
296
|
-
[contract.capabilityCeiling.communicationLane, C.value],
|
|
297
|
-
]));
|
|
298
|
-
detailRows.push(buildPanelLine(width, [
|
|
299
|
-
[' Tools: ', C.label],
|
|
300
|
-
[truncate(contract.capabilityCeiling.allowedTools.join(', ') || '(none)', Math.max(0, width - 10)), C.dim],
|
|
301
|
-
]));
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
if (selected.taskId) {
|
|
305
|
-
detailRows.push(buildPanelLine(width, [
|
|
306
|
-
[' Task: ', C.label],
|
|
307
|
-
[selected.taskId, C.value],
|
|
308
|
-
]));
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
const supervisorEntry = supervisor.sessions.find((entry) => entry.runnerId === selected.agentId);
|
|
312
|
-
if (supervisorEntry) {
|
|
313
|
-
detailRows.push(buildPanelLine(width, [
|
|
314
|
-
[' Heartbeat: ', C.label],
|
|
315
|
-
[supervisorEntry.heartbeat.status, supervisorEntry.heartbeat.status === 'fresh' ? C.ok : supervisorEntry.heartbeat.status === 'stale' ? C.warn : C.error],
|
|
316
|
-
[' Protocol: ', C.label],
|
|
317
|
-
[supervisorEntry.negotiation.executionProtocol, C.value],
|
|
318
|
-
[' Review: ', C.label],
|
|
319
|
-
[supervisorEntry.negotiation.reviewMode, supervisorEntry.negotiation.reviewMode === 'wrfc' ? C.ok : C.dim],
|
|
320
|
-
]));
|
|
321
|
-
detailRows.push(buildPanelLine(width, [[` ${supervisorEntry.heartbeat.detail}`.slice(0, width), C.dim]]));
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
const recentArtifact = snapshot.artifacts.find((artifact) => artifact.runnerId === selected.agentId);
|
|
325
|
-
if (recentArtifact) {
|
|
326
|
-
detailRows.push(buildPanelLine(width, [[' Recent Review Artifact', C.label]]));
|
|
327
|
-
detailRows.push(buildPanelLine(width, [
|
|
328
|
-
[' Artifact: ', C.label],
|
|
329
|
-
[recentArtifact.id, C.value],
|
|
330
|
-
[' Status: ', C.label],
|
|
331
|
-
[recentArtifact.task.status, stateColor(recentArtifact.evidence.transportState)],
|
|
332
|
-
]));
|
|
333
|
-
detailRows.push(buildPanelLine(width, [
|
|
334
|
-
[' Summary: ', C.label],
|
|
335
|
-
[truncate(recentArtifact.task.summary, Math.max(0, width - 12)), C.dim],
|
|
336
|
-
]));
|
|
337
|
-
}
|
|
338
|
-
detailRows.push(buildPanelLine(width, [
|
|
339
|
-
[' Tip: ', C.label],
|
|
340
|
-
['Use Up/Down or j/k to inspect another connection.', C.dim],
|
|
341
|
-
]));
|
|
342
|
-
} else if (selectedContract) {
|
|
343
|
-
detailRows.push(buildPanelLine(width, [
|
|
344
|
-
[' Worker: ', C.label],
|
|
345
|
-
[selectedContract.runnerId, C.value],
|
|
346
|
-
[' Template: ', C.label],
|
|
347
|
-
[selectedContract.template, C.info],
|
|
348
|
-
[' Trust: ', C.label],
|
|
349
|
-
[selectedContract.trustClass, C.value],
|
|
350
|
-
]));
|
|
351
|
-
detailRows.push(buildPanelLine(width, [
|
|
352
|
-
[' Pool: ', C.label],
|
|
353
|
-
[selectedContract.poolId ?? '(none)', selectedContract.poolId ? C.info : C.dim],
|
|
354
|
-
[' Transport: ', C.label],
|
|
355
|
-
[selectedContract.transport.state, stateColor(selectedContract.transport.state)],
|
|
356
|
-
]));
|
|
357
|
-
detailRows.push(buildPanelLine(width, [
|
|
358
|
-
[' Protocol: ', C.label],
|
|
359
|
-
[selectedContract.capabilityCeiling.executionProtocol, C.value],
|
|
360
|
-
[' Review: ', C.label],
|
|
361
|
-
[selectedContract.capabilityCeiling.reviewMode, C.value],
|
|
362
|
-
[' Lane: ', C.label],
|
|
363
|
-
[selectedContract.capabilityCeiling.communicationLane, C.value],
|
|
364
|
-
]));
|
|
365
|
-
detailRows.push(buildPanelLine(width, [
|
|
366
|
-
[' Tools: ', C.label],
|
|
367
|
-
[truncate(selectedContract.capabilityCeiling.allowedTools.join(', ') || '(none)', Math.max(0, width - 10)), C.dim],
|
|
368
|
-
]));
|
|
369
|
-
const supervisorEntry = supervisor.sessions.find((entry) => entry.runnerId === selectedContract.runnerId);
|
|
370
|
-
if (supervisorEntry) {
|
|
371
|
-
detailRows.push(buildPanelLine(width, [
|
|
372
|
-
[' Heartbeat: ', C.label],
|
|
373
|
-
[supervisorEntry.heartbeat.status, supervisorEntry.heartbeat.status === 'fresh' ? C.ok : supervisorEntry.heartbeat.status === 'stale' ? C.warn : C.error],
|
|
374
|
-
[' Lane: ', C.label],
|
|
375
|
-
[supervisorEntry.negotiation.communicationLane, C.info],
|
|
376
|
-
]));
|
|
377
|
-
for (const action of supervisorEntry.recovery.slice(0, 2)) {
|
|
378
|
-
detailRows.push(buildGuidanceLine(width, action.command, action.reason, C));
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
const recentArtifact = snapshot.artifacts.find((artifact) => artifact.runnerId === selectedContract.runnerId);
|
|
382
|
-
if (recentArtifact) {
|
|
383
|
-
detailRows.push(buildPanelLine(width, [
|
|
384
|
-
[' Recent artifact: ', C.label],
|
|
385
|
-
[recentArtifact.id, C.ok],
|
|
386
|
-
[' Status: ', C.label],
|
|
387
|
-
[recentArtifact.task.status, stateColor(recentArtifact.evidence.transportState)],
|
|
388
|
-
]));
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
const postureSection: PanelWorkspaceSection = { lines: buildSummaryBlock(width, 'Remote posture', postureLines, C) };
|
|
392
|
-
const detailSection: PanelWorkspaceSection = {
|
|
393
|
-
lines: buildDetailBlock(width, selected ? 'Selected connection' : 'Selected contract', detailRows, C),
|
|
394
|
-
};
|
|
395
|
-
const browseTitle = viewingConnections ? 'Active Connections' : 'Registered Remote Worker Contracts';
|
|
396
|
-
const rawBrowseLines: Line[] = viewingConnections
|
|
397
|
-
? activeConnections.map((connection, absolute) => {
|
|
398
|
-
return buildPanelListRow(width, [
|
|
399
|
-
{ text: connection.agentId.padEnd(18), fg: C.value },
|
|
400
|
-
{ text: ` ${connection.transportState.padEnd(18)}`, fg: stateColor(connection.transportState) },
|
|
401
|
-
{ text: ` msgs=${String(connection.messageCount).padEnd(6)}`, fg: C.info },
|
|
402
|
-
{ text: ` errs=${String(connection.errorCount).padEnd(4)}`, fg: connection.errorCount > 0 ? C.warn : C.dim },
|
|
403
|
-
{ text: ` ${connection.label}`.slice(0, Math.max(0, width - 56)), fg: C.dim },
|
|
404
|
-
], C, { selected: absolute === this.selectedIndex, selectedBg: C.headerBg });
|
|
405
|
-
})
|
|
406
|
-
: [
|
|
407
|
-
...contracts.map((contract, absolute) => {
|
|
408
|
-
return buildPanelListRow(width, [
|
|
409
|
-
{ text: contract.runnerId.padEnd(18), fg: C.value },
|
|
410
|
-
{ text: ` ${contract.transport.state.padEnd(18)}`, fg: stateColor(contract.transport.state) },
|
|
411
|
-
{ text: ` ${contract.template}`.slice(0, Math.max(0, width - 42)), fg: C.dim },
|
|
412
|
-
], C, { selected: absolute === this.selectedIndex, selectedBg: C.headerBg });
|
|
413
|
-
}),
|
|
414
|
-
buildPanelLine(width, [[' No active connection is currently attached to these contracts.', C.dim]]),
|
|
415
|
-
];
|
|
416
|
-
const resolvedBrowseSection = resolvePrimaryScrollableSection(width, height, {
|
|
417
|
-
intro,
|
|
418
|
-
footerLines,
|
|
419
|
-
palette: C,
|
|
420
|
-
beforeSections: [postureSection],
|
|
421
|
-
section: {
|
|
422
|
-
title: browseTitle,
|
|
423
|
-
scrollableLines: rawBrowseLines,
|
|
424
|
-
selectedIndex: this.selectedIndex,
|
|
425
|
-
scrollOffset: this.scrollOffset,
|
|
426
|
-
guardRows: 1,
|
|
427
|
-
minRows: 4,
|
|
428
|
-
appendWindowSummary: viewingConnections ? { dimColor: C.dim } : undefined,
|
|
429
|
-
},
|
|
430
|
-
afterSections: [detailSection],
|
|
431
|
-
});
|
|
432
|
-
this.scrollOffset = resolvedBrowseSection.scrollOffset;
|
|
433
|
-
|
|
434
|
-
const sections: PanelWorkspaceSection[] = [
|
|
435
|
-
postureSection,
|
|
436
|
-
resolvedBrowseSection.section,
|
|
437
|
-
detailSection,
|
|
438
|
-
];
|
|
439
|
-
const lines = buildPanelWorkspace(width, height, {
|
|
440
|
-
title: 'Remote Work Review',
|
|
441
|
-
intro,
|
|
442
|
-
sections,
|
|
443
|
-
footerLines,
|
|
444
|
-
palette: C,
|
|
445
|
-
});
|
|
446
|
-
while (lines.length < height) lines.push(createEmptyLine(width));
|
|
447
|
-
return lines.slice(0, height);
|
|
448
|
-
}
|
|
449
|
-
}
|
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
import type { Line } from '../types/grid.ts';
|
|
2
|
-
import { createEmptyLine } from '../types/grid.ts';
|
|
3
|
-
import { ScrollableListPanel } from './scrollable-list-panel.ts';
|
|
4
|
-
import type { UiReadModel, UiRoutesSnapshot } from '../runtime/ui-read-models.ts';
|
|
5
|
-
import { truncateDisplay } from '../utils/terminal-width.ts';
|
|
6
|
-
import {
|
|
7
|
-
buildEmptyState,
|
|
8
|
-
buildGuidanceLine,
|
|
9
|
-
buildKeyValueLine,
|
|
10
|
-
buildPanelLine,
|
|
11
|
-
buildPanelWorkspace,
|
|
12
|
-
buildStatusPill,
|
|
13
|
-
DEFAULT_PANEL_PALETTE,
|
|
14
|
-
type PanelPalette,
|
|
15
|
-
} from './polish.ts';
|
|
16
|
-
|
|
17
|
-
const C = {
|
|
18
|
-
...DEFAULT_PANEL_PALETTE,
|
|
19
|
-
header: '#94a3b8',
|
|
20
|
-
headerBg: '#1e293b',
|
|
21
|
-
ok: '#22c55e',
|
|
22
|
-
warn: '#eab308',
|
|
23
|
-
error: '#ef4444',
|
|
24
|
-
info: '#38bdf8',
|
|
25
|
-
selectBg: '#0f172a',
|
|
26
|
-
} as const;
|
|
27
|
-
|
|
28
|
-
function formatTime(value?: number): string {
|
|
29
|
-
if (!value) return 'n/a';
|
|
30
|
-
return new Date(value).toLocaleString();
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
type RouteBinding = UiRoutesSnapshot['bindings'][number];
|
|
34
|
-
|
|
35
|
-
export class RoutesPanel extends ScrollableListPanel<RouteBinding> {
|
|
36
|
-
private readonly readModel?: UiReadModel<UiRoutesSnapshot>;
|
|
37
|
-
private readonly unsub: (() => void) | null;
|
|
38
|
-
|
|
39
|
-
public constructor(readModel?: UiReadModel<UiRoutesSnapshot>) {
|
|
40
|
-
super('routes', 'Routes', 'R', 'monitoring');
|
|
41
|
-
this.showSelectionGutter = true; // I5: non-color selection affordance
|
|
42
|
-
this.readModel = readModel;
|
|
43
|
-
this.unsub = readModel ? readModel.subscribe(() => this.markDirty()) : null;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
public override onDestroy(): void {
|
|
47
|
-
this.unsub?.();
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
protected override getPalette(): PanelPalette {
|
|
51
|
-
return C;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
protected getItems(): readonly RouteBinding[] {
|
|
55
|
-
if (!this.readModel) return [];
|
|
56
|
-
return this.readModel.getSnapshot().bindings;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
protected renderItem(binding: RouteBinding, _index: number, selected: boolean, width: number): Line {
|
|
60
|
-
const bg = selected ? C.selectBg : undefined;
|
|
61
|
-
return buildPanelLine(width, [
|
|
62
|
-
[' ', C.label, bg],
|
|
63
|
-
[binding.surfaceKind.padEnd(9), C.info, bg],
|
|
64
|
-
[` ${truncateDisplay(binding.title ?? binding.externalId, 22).padEnd(22)}`, C.value, bg],
|
|
65
|
-
...buildStatusPill(binding.sessionId ? 'good' : 'warn', ` ${truncateDisplay(binding.sessionId ?? binding.runId ?? 'unbound', 18).padEnd(18)}`, { bg }),
|
|
66
|
-
[` ${truncateDisplay(formatTime(binding.lastSeenAt), Math.max(0, width - 54))}`, C.dim, bg],
|
|
67
|
-
]);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
protected override getEmptyStateMessage(): string {
|
|
71
|
-
return ' No route bindings recorded.';
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
protected override getEmptyStateActions(): Array<{ command: string; summary: string }> {
|
|
75
|
-
return [
|
|
76
|
-
{ command: '/schedule list', summary: 'run jobs and triggers that create route bindings' },
|
|
77
|
-
{ command: '/communication', summary: 'inspect routed communication once a surface is active' },
|
|
78
|
-
];
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
public render(width: number, height: number): Line[] {
|
|
82
|
-
const intro = 'External route bindings that preserve thread, session, and reply context across Slack, Discord, ntfy, webhook, web, and TUI surfaces.';
|
|
83
|
-
|
|
84
|
-
if (!this.readModel) {
|
|
85
|
-
const workspace = buildPanelWorkspace(width, height, {
|
|
86
|
-
title: 'Route Bindings',
|
|
87
|
-
intro,
|
|
88
|
-
sections: [{
|
|
89
|
-
lines: buildEmptyState(
|
|
90
|
-
width,
|
|
91
|
-
' Runtime store not wired.',
|
|
92
|
-
'This panel needs the shared runtime store to inspect omnichannel route bindings.',
|
|
93
|
-
[{ command: '/communication', summary: 'review communication posture while route state is unavailable' }],
|
|
94
|
-
C,
|
|
95
|
-
),
|
|
96
|
-
}],
|
|
97
|
-
palette: C,
|
|
98
|
-
});
|
|
99
|
-
while (workspace.length < height) workspace.push(createEmptyLine(width));
|
|
100
|
-
return workspace;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const snapshot = this.readModel.getSnapshot();
|
|
104
|
-
const bindings = this.getItems();
|
|
105
|
-
const surfaceEntries = Object.entries(snapshot.bindingIdsBySurface)
|
|
106
|
-
.filter(([, ids]) => ids.length > 0)
|
|
107
|
-
.sort((a, b) => b[1].length - a[1].length || a[0].localeCompare(b[0]));
|
|
108
|
-
|
|
109
|
-
const headerLines: Line[] = [
|
|
110
|
-
buildKeyValueLine(width, [
|
|
111
|
-
{ label: 'bindings', value: String(snapshot.totalBindings), valueColor: snapshot.totalBindings > 0 ? C.info : C.dim },
|
|
112
|
-
{ label: 'active', value: String(snapshot.activeBindingIds.length), valueColor: snapshot.activeBindingIds.length > 0 ? C.ok : C.dim },
|
|
113
|
-
{ label: 'resolved', value: String(snapshot.totalResolved), valueColor: snapshot.totalResolved > 0 ? C.ok : C.dim },
|
|
114
|
-
{ label: 'failures', value: String(snapshot.totalFailures), valueColor: snapshot.totalFailures > 0 ? C.error : C.dim },
|
|
115
|
-
], C),
|
|
116
|
-
buildGuidanceLine(width, '/communication', 'inspect routed message flow and delivery behavior across bound surfaces', C),
|
|
117
|
-
];
|
|
118
|
-
|
|
119
|
-
if (bindings.length === 0) {
|
|
120
|
-
return this.renderList(width, height, {
|
|
121
|
-
title: 'Route Bindings',
|
|
122
|
-
header: headerLines,
|
|
123
|
-
emptyMessage: ' No route bindings recorded.',
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
this.clampSelection();
|
|
128
|
-
const selected = bindings[this.selectedIndex]!;
|
|
129
|
-
|
|
130
|
-
const footerLines: Line[] = [
|
|
131
|
-
buildPanelLine(width, [
|
|
132
|
-
[' Binding: ', C.label],
|
|
133
|
-
[selected.id, C.value],
|
|
134
|
-
[' Surface: ', C.label],
|
|
135
|
-
[selected.surfaceKind, C.info],
|
|
136
|
-
]),
|
|
137
|
-
buildPanelLine(width, [
|
|
138
|
-
[' External: ', C.label],
|
|
139
|
-
[truncateDisplay(selected.externalId, 28), C.value],
|
|
140
|
-
[' Kind: ', C.label],
|
|
141
|
-
[selected.kind, C.dim],
|
|
142
|
-
]),
|
|
143
|
-
buildPanelLine(width, [
|
|
144
|
-
[' Session: ', C.label],
|
|
145
|
-
[selected.sessionId ?? 'n/a', C.value],
|
|
146
|
-
[' Run: ', C.label],
|
|
147
|
-
[selected.runId ?? 'n/a', C.dim],
|
|
148
|
-
]),
|
|
149
|
-
buildPanelLine(width, [
|
|
150
|
-
[' Channel: ', C.label],
|
|
151
|
-
[selected.channelId ?? 'n/a', C.dim],
|
|
152
|
-
[' Thread: ', C.label],
|
|
153
|
-
[selected.threadId ?? 'n/a', C.dim],
|
|
154
|
-
]),
|
|
155
|
-
buildPanelLine(width, [
|
|
156
|
-
[' Last seen: ', C.label],
|
|
157
|
-
[formatTime(selected.lastSeenAt), C.dim],
|
|
158
|
-
]),
|
|
159
|
-
];
|
|
160
|
-
|
|
161
|
-
if (surfaceEntries.length > 0) {
|
|
162
|
-
footerLines.push(
|
|
163
|
-
...surfaceEntries.slice(0, 6).map(([surface, ids]) => buildPanelLine(width, [
|
|
164
|
-
[' ', C.label],
|
|
165
|
-
[surface.padEnd(10), C.info],
|
|
166
|
-
[` ${String(ids.length)} binding(s)`, C.value],
|
|
167
|
-
])),
|
|
168
|
-
);
|
|
169
|
-
}
|
|
170
|
-
footerLines.push(buildPanelLine(width, [[' Up/Down move through route bindings', C.dim]]));
|
|
171
|
-
|
|
172
|
-
return this.renderList(width, height, {
|
|
173
|
-
title: 'Route Bindings',
|
|
174
|
-
header: headerLines,
|
|
175
|
-
footer: footerLines,
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
}
|