@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,231 +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 {
|
|
5
|
-
type ServiceConfig,
|
|
6
|
-
type ServiceInspection,
|
|
7
|
-
type ServiceConnectionTestResult,
|
|
8
|
-
} from '@pellux/goodvibes-sdk/platform/config';
|
|
9
|
-
import type { ServiceInspectionQuery, SubscriptionAccessQuery } from '../runtime/ui-service-queries.ts';
|
|
10
|
-
import {
|
|
11
|
-
buildEmptyState,
|
|
12
|
-
buildPanelLine,
|
|
13
|
-
buildPanelWorkspace,
|
|
14
|
-
buildStatusPill,
|
|
15
|
-
DEFAULT_PANEL_PALETTE,
|
|
16
|
-
} from './polish.ts';
|
|
17
|
-
|
|
18
|
-
const C = {
|
|
19
|
-
...DEFAULT_PANEL_PALETTE,
|
|
20
|
-
header: '#94a3b8',
|
|
21
|
-
headerBg: '#1e293b',
|
|
22
|
-
label: '#64748b',
|
|
23
|
-
value: '#e2e8f0',
|
|
24
|
-
dim: '#475569',
|
|
25
|
-
ok: '#22c55e',
|
|
26
|
-
warn: '#eab308',
|
|
27
|
-
error: '#ef4444',
|
|
28
|
-
info: '#38bdf8',
|
|
29
|
-
selectBg: '#0f172a',
|
|
30
|
-
empty: '#334155',
|
|
31
|
-
} as const;
|
|
32
|
-
|
|
33
|
-
interface ServicePanelEntry {
|
|
34
|
-
readonly name: string;
|
|
35
|
-
readonly inspection: ServiceInspection;
|
|
36
|
-
readonly lastTest?: ServiceConnectionTestResult;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function statusLabel(entry: ServicePanelEntry): string {
|
|
40
|
-
if (!entry.inspection.hasPrimaryCredential) return 'UNCONFIGURED';
|
|
41
|
-
if (entry.lastTest?.ok) return 'HEALTHY';
|
|
42
|
-
if (entry.lastTest && !entry.lastTest.ok) return 'ERROR';
|
|
43
|
-
return 'CONFIGURED';
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function statusColor(entry: ServicePanelEntry): string {
|
|
47
|
-
const label = statusLabel(entry);
|
|
48
|
-
if (label === 'HEALTHY') return C.ok;
|
|
49
|
-
if (label === 'ERROR') return C.error;
|
|
50
|
-
if (label === 'CONFIGURED') return C.warn;
|
|
51
|
-
return C.dim;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function authSummary(config: ServiceConfig, manager: SubscriptionAccessQuery): string {
|
|
55
|
-
const provider = config.providerId ?? config.name;
|
|
56
|
-
const hasOverride = manager.getAccessToken(provider) != null;
|
|
57
|
-
switch (config.authType) {
|
|
58
|
-
case 'bearer':
|
|
59
|
-
return hasOverride ? 'bearer+subscription' : 'bearer';
|
|
60
|
-
case 'basic':
|
|
61
|
-
return 'basic';
|
|
62
|
-
case 'api-key':
|
|
63
|
-
return hasOverride
|
|
64
|
-
? 'oauth-override'
|
|
65
|
-
: config.apiKeyHeader ? `api-key:${config.apiKeyHeader}` : 'api-key';
|
|
66
|
-
case 'oauth':
|
|
67
|
-
return manager.get(provider) != null ? 'oauth(active)' : 'oauth';
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export class ServicesPanel extends ScrollableListPanel<ServicePanelEntry> {
|
|
72
|
-
private readonly registry: ServiceInspectionQuery;
|
|
73
|
-
private readonly subscriptionManager: SubscriptionAccessQuery;
|
|
74
|
-
private entries: ServicePanelEntry[] = [];
|
|
75
|
-
private loading = false;
|
|
76
|
-
|
|
77
|
-
public constructor(
|
|
78
|
-
registry: ServiceInspectionQuery,
|
|
79
|
-
subscriptionManager: SubscriptionAccessQuery,
|
|
80
|
-
) {
|
|
81
|
-
super('services', 'Services', 'V', 'monitoring');
|
|
82
|
-
this.showSelectionGutter = true; // I5: non-color selection affordance
|
|
83
|
-
this.registry = registry;
|
|
84
|
-
this.subscriptionManager = subscriptionManager;
|
|
85
|
-
void this.refresh();
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
public override onActivate(): void {
|
|
89
|
-
super.onActivate();
|
|
90
|
-
if (this.entries.length === 0 && !this.loading) {
|
|
91
|
-
void this.refresh();
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
protected override getPalette() { return C; }
|
|
96
|
-
protected override getEmptyStateMessage() { return ' No services configured.'; }
|
|
97
|
-
protected override getEmptyStateActions() {
|
|
98
|
-
return [
|
|
99
|
-
{ command: '/services auth-review', summary: 'inspect service auth posture and registry config' },
|
|
100
|
-
{ command: '/subscription', summary: 'review provider login state and override posture' },
|
|
101
|
-
];
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
protected getItems(): readonly ServicePanelEntry[] {
|
|
105
|
-
return this.entries;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
protected renderItem(entry: ServicePanelEntry, index: number, selected: boolean, width: number): Line {
|
|
109
|
-
const bg = selected ? C.selectBg : undefined;
|
|
110
|
-
return buildPanelLine(width, [
|
|
111
|
-
[' ', C.label, bg],
|
|
112
|
-
[entry.name.padEnd(16), C.value, bg],
|
|
113
|
-
[` ${statusLabel(entry).padEnd(12)}`, statusColor(entry), bg],
|
|
114
|
-
[` ${authSummary(entry.inspection.config, this.subscriptionManager).padEnd(18)}`, C.info, bg],
|
|
115
|
-
[` ${entry.inspection.config.baseUrl ?? '(no baseUrl)'}`, C.dim, bg],
|
|
116
|
-
]);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
public handleInput(key: string): boolean {
|
|
120
|
-
if (key === 'r') {
|
|
121
|
-
void this.refresh();
|
|
122
|
-
return true;
|
|
123
|
-
}
|
|
124
|
-
if (key === 't') {
|
|
125
|
-
void this.testSelected();
|
|
126
|
-
return true;
|
|
127
|
-
}
|
|
128
|
-
return super.handleInput(key);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
private async refresh(): Promise<void> {
|
|
132
|
-
this.loading = true;
|
|
133
|
-
this.markDirty();
|
|
134
|
-
const configs = this.registry.getAll();
|
|
135
|
-
const names = Object.keys(configs).sort((a, b) => a.localeCompare(b));
|
|
136
|
-
const inspections = await Promise.all(
|
|
137
|
-
names.map(async (name) => ({
|
|
138
|
-
name,
|
|
139
|
-
inspection: (await this.registry.inspect(name))!,
|
|
140
|
-
})),
|
|
141
|
-
);
|
|
142
|
-
const previousTests = new Map(this.entries.map((entry) => [entry.name, entry.lastTest] as const));
|
|
143
|
-
this.entries = inspections.map((entry) => ({
|
|
144
|
-
...entry,
|
|
145
|
-
...(previousTests.get(entry.name) ? { lastTest: previousTests.get(entry.name) } : {}),
|
|
146
|
-
}));
|
|
147
|
-
this.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.entries.length - 1));
|
|
148
|
-
this.loading = false;
|
|
149
|
-
this.markDirty();
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
private async testSelected(): Promise<void> {
|
|
153
|
-
const selected = this.entries[this.selectedIndex];
|
|
154
|
-
if (!selected) return;
|
|
155
|
-
const result = await this.registry.testConnection(selected.name);
|
|
156
|
-
this.entries = this.entries.map((entry) => (
|
|
157
|
-
entry.name === selected.name
|
|
158
|
-
? { ...entry, lastTest: result }
|
|
159
|
-
: entry
|
|
160
|
-
));
|
|
161
|
-
this.markDirty();
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
public render(width: number, height: number): Line[] {
|
|
165
|
-
this.clampSelection();
|
|
166
|
-
const intro = 'Credential posture, subscription overrides, and live connection checks for configured services.';
|
|
167
|
-
|
|
168
|
-
if (this.loading && this.entries.length === 0) {
|
|
169
|
-
const lines = buildPanelWorkspace(width, height, {
|
|
170
|
-
title: 'Service Control Room',
|
|
171
|
-
intro,
|
|
172
|
-
sections: [{ lines: [buildPanelLine(width, [[' Loading configured services...', C.info]])] }],
|
|
173
|
-
palette: C,
|
|
174
|
-
});
|
|
175
|
-
while (lines.length < height) lines.push(createEmptyLine(width));
|
|
176
|
-
return lines;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const selected = this.entries[this.selectedIndex];
|
|
180
|
-
const detailLines: Line[] = [];
|
|
181
|
-
if (selected) {
|
|
182
|
-
const inspect = selected.inspection;
|
|
183
|
-
detailLines.push(buildPanelLine(width, [
|
|
184
|
-
[' Service: ', C.label],
|
|
185
|
-
[selected.name, C.value],
|
|
186
|
-
[' State: ', C.label],
|
|
187
|
-
[statusLabel(selected), statusColor(selected)],
|
|
188
|
-
[' Auth: ', C.label],
|
|
189
|
-
[authSummary(inspect.config, this.subscriptionManager), C.info],
|
|
190
|
-
]));
|
|
191
|
-
detailLines.push(buildPanelLine(width, [
|
|
192
|
-
[' Primary credential: ', C.label],
|
|
193
|
-
...buildStatusPill(inspect.hasPrimaryCredential ? 'good' : 'bad', inspect.hasPrimaryCredential ? 'present' : 'missing'),
|
|
194
|
-
[' Webhook URL: ', C.label],
|
|
195
|
-
...buildStatusPill(inspect.hasWebhookUrl ? 'good' : 'info', inspect.hasWebhookUrl ? 'present' : 'missing'),
|
|
196
|
-
[' Signing secret: ', C.label],
|
|
197
|
-
...buildStatusPill(inspect.hasSigningSecret ? 'good' : 'info', inspect.hasSigningSecret ? 'present' : 'missing'),
|
|
198
|
-
[' App token: ', C.label],
|
|
199
|
-
...buildStatusPill(inspect.hasAppToken ? 'good' : 'info', inspect.hasAppToken ? 'present' : 'missing'),
|
|
200
|
-
]));
|
|
201
|
-
if (selected.lastTest) {
|
|
202
|
-
detailLines.push(buildPanelLine(width, [
|
|
203
|
-
[' Last test: ', C.label],
|
|
204
|
-
...buildStatusPill(selected.lastTest.ok ? 'good' : 'bad', selected.lastTest.ok ? 'ok' : 'failed'),
|
|
205
|
-
[' Status: ', C.label],
|
|
206
|
-
[selected.lastTest.status != null ? String(selected.lastTest.status) : 'n/a', C.value],
|
|
207
|
-
[' URL: ', C.label],
|
|
208
|
-
[(selected.lastTest.testedUrl ?? 'n/a').slice(0, Math.max(0, width - 34)), C.dim],
|
|
209
|
-
]));
|
|
210
|
-
if (selected.lastTest.error) {
|
|
211
|
-
detailLines.push(buildPanelLine(width, [
|
|
212
|
-
[' Error: ', C.label],
|
|
213
|
-
[selected.lastTest.error.slice(0, Math.max(0, width - 10)), C.error],
|
|
214
|
-
]));
|
|
215
|
-
}
|
|
216
|
-
} else {
|
|
217
|
-
detailLines.push(buildPanelLine(width, [[' Press t to test the selected service or r to refresh credential status.', C.dim]]));
|
|
218
|
-
}
|
|
219
|
-
detailLines.push(buildPanelLine(width, [[' Services resolve credentials through hierarchy-aware secure storage, plaintext fallback policy, and project-local config.', C.dim]]));
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
return this.renderList(width, height, {
|
|
223
|
-
title: 'Service Control Room',
|
|
224
|
-
footer: [
|
|
225
|
-
...detailLines,
|
|
226
|
-
buildPanelLine(width, [[' Up/Down move t test selected service r refresh inspections', C.dim]]),
|
|
227
|
-
],
|
|
228
|
-
emptyMessage: intro,
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
}
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import type { Line } from '../types/grid.ts';
|
|
2
|
-
import { ScrollableListPanel } from './scrollable-list-panel.ts';
|
|
3
|
-
import {
|
|
4
|
-
buildDetailBlock,
|
|
5
|
-
buildGuidanceLine,
|
|
6
|
-
buildPanelListRow,
|
|
7
|
-
buildPanelLine,
|
|
8
|
-
buildStatusPill,
|
|
9
|
-
buildSummaryBlock,
|
|
10
|
-
DEFAULT_PANEL_PALETTE,
|
|
11
|
-
type PanelPalette,
|
|
12
|
-
} from './polish.ts';
|
|
13
|
-
import { getSettingsControlPlaneSnapshot } from '@/runtime/index.ts';
|
|
14
|
-
import type { ConfigManager } from '../config/index.ts';
|
|
15
|
-
|
|
16
|
-
const C = {
|
|
17
|
-
...DEFAULT_PANEL_PALETTE,
|
|
18
|
-
dim: '#475569',
|
|
19
|
-
info: '#38bdf8',
|
|
20
|
-
ok: '#22c55e',
|
|
21
|
-
warn: '#eab308',
|
|
22
|
-
error: '#ef4444',
|
|
23
|
-
} as const;
|
|
24
|
-
|
|
25
|
-
type ResolvedEntry = ReturnType<typeof getSettingsControlPlaneSnapshot>['resolvedEntries'][number];
|
|
26
|
-
|
|
27
|
-
export class SettingsSyncPanel extends ScrollableListPanel<ResolvedEntry> {
|
|
28
|
-
public constructor(private readonly configManager: ConfigManager) {
|
|
29
|
-
super('settings-sync', 'Settings Sync', 'S', 'monitoring');
|
|
30
|
-
this.showSelectionGutter = true; // I5: non-color selection affordance
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
protected override getPalette(): PanelPalette {
|
|
34
|
-
return C;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
protected getItems(): readonly ResolvedEntry[] {
|
|
38
|
-
return getSettingsControlPlaneSnapshot(this.configManager).resolvedEntries;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
protected renderItem(entry: ResolvedEntry, _index: number, selected: boolean, width: number): Line {
|
|
42
|
-
return buildPanelListRow(width, [
|
|
43
|
-
{ text: entry.key.padEnd(32), fg: C.value },
|
|
44
|
-
{ text: ` ${entry.effectiveSource}`.padEnd(11), fg: entry.effectiveSource === 'managed' ? C.warn : entry.effectiveSource === 'synced' ? C.ok : entry.effectiveSource === 'local' ? C.info : C.dim },
|
|
45
|
-
{ text: `${String(entry.effectiveValue)}`.slice(0, Math.max(0, width - 47)), fg: entry.locked ? C.warn : C.dim },
|
|
46
|
-
], C, { selected });
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
protected override getEmptyStateMessage(): string {
|
|
50
|
-
return ' No resolved settings entries.';
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
public render(width: number, height: number): Line[] {
|
|
54
|
-
const snapshot = getSettingsControlPlaneSnapshot(this.configManager);
|
|
55
|
-
|
|
56
|
-
const postureLines: Line[] = [
|
|
57
|
-
buildPanelLine(width, [[' resolved keys ', C.label], [String(snapshot.resolvedEntries.length), C.value], [' conflicts ', C.label], ...buildStatusPill(snapshot.conflicts.length > 0 ? 'bad' : 'good', String(snapshot.conflicts.length)), [' failures ', C.label], ...buildStatusPill(snapshot.recentFailures.length > 0 ? 'warn' : 'good', String(snapshot.recentFailures.length))]),
|
|
58
|
-
buildPanelLine(width, [[' managed locks ', C.label], [String(snapshot.managedLockCount), snapshot.managedLockCount > 0 ? C.warn : C.dim], [' staged bundle ', C.label], [snapshot.stagedManagedBundle ? snapshot.stagedManagedBundle.profileName : 'none', snapshot.stagedManagedBundle ? C.info : C.dim]]),
|
|
59
|
-
buildGuidanceLine(width, '/settingssync conflicts', 'review conflicting synced values before they silently shape effective configuration', C),
|
|
60
|
-
buildGuidanceLine(width, '/managed review', 'inspect staged managed changes, risk posture, and rollback records', C),
|
|
61
|
-
];
|
|
62
|
-
|
|
63
|
-
const headerLines: Line[] = [
|
|
64
|
-
...buildSummaryBlock(width, 'Settings posture', postureLines, C),
|
|
65
|
-
buildPanelLine(width, [[' local typed config ', C.label], [String(snapshot.liveKeyCount), C.value], [' saved profiles ', C.label], [String(snapshot.profileCount), C.info], [' managed locks ', C.label], [String(snapshot.managedLockCount), snapshot.managedLockCount > 0 ? C.warn : C.dim]]),
|
|
66
|
-
buildPanelLine(width, [[' effective local ', C.label], [String(snapshot.resolvedCounts.local), C.info], [' synced ', C.label], [String(snapshot.resolvedCounts.synced), snapshot.resolvedCounts.synced > 0 ? C.ok : C.dim], [' managed ', C.label], [String(snapshot.resolvedCounts.managed), snapshot.resolvedCounts.managed > 0 ? C.warn : C.dim]]),
|
|
67
|
-
buildPanelLine(width, [[' last sync ', C.label], [snapshot.lastSync ? `${snapshot.lastSync.surface}/${snapshot.lastSync.direction}` : 'none', snapshot.lastSync ? C.ok : C.dim], [' when ', C.label], [snapshot.lastSync ? new Date(snapshot.lastSync.timestamp).toLocaleString() : 'n/a', C.dim]]),
|
|
68
|
-
// Staged Bundle
|
|
69
|
-
...(snapshot.stagedManagedBundle
|
|
70
|
-
? [
|
|
71
|
-
buildPanelLine(width, [[' profile ', C.label], [snapshot.stagedManagedBundle.profileName, C.value], [' risk ', C.label], [snapshot.stagedManagedBundle.risk, snapshot.stagedManagedBundle.risk === 'high' ? C.error : snapshot.stagedManagedBundle.risk === 'medium' ? C.warn : C.ok], [' changes ', C.label], [String(snapshot.stagedManagedBundle.changeCount), C.info]]),
|
|
72
|
-
buildPanelLine(width, [[' path ', C.label], [snapshot.stagedManagedBundle.path.slice(0, Math.max(0, width - 9)), C.dim]]),
|
|
73
|
-
]
|
|
74
|
-
: [buildPanelLine(width, [[' No staged managed settings bundle.', C.dim]])]),
|
|
75
|
-
// Recent Events
|
|
76
|
-
...(snapshot.recentEvents.length > 0
|
|
77
|
-
? snapshot.recentEvents.map((event) => buildPanelLine(width, [[` ${event.surface}/${event.direction}`.padEnd(18), C.info], [` ${event.detail}`.slice(0, Math.max(0, width - 20)), C.dim]]))
|
|
78
|
-
: [buildPanelLine(width, [[' No sync or managed-setting events recorded yet.', C.dim]])]),
|
|
79
|
-
// Managed Locks
|
|
80
|
-
...(snapshot.managedLocks.length > 0
|
|
81
|
-
? snapshot.managedLocks.slice(0, 10).map((lock) => buildPanelLine(width, [[` ${lock.key}`.padEnd(30), C.value], [` source=${lock.source}`.padEnd(24), C.info], [` ${lock.reason}`.slice(0, Math.max(0, width - 56)), C.dim]]))
|
|
82
|
-
: [buildPanelLine(width, [[' No managed locks are currently active.', C.dim]])]),
|
|
83
|
-
// Failures
|
|
84
|
-
...(snapshot.recentFailures.length > 0
|
|
85
|
-
? snapshot.recentFailures.map((failure) => buildPanelLine(width, [[` ${failure.surface}`.padEnd(10), C.error], [` ${failure.message}`.slice(0, Math.max(0, width - 12)), C.dim]]))
|
|
86
|
-
: [buildPanelLine(width, [[' No recent sync or managed-setting failures.', C.dim]])]),
|
|
87
|
-
// Conflicts
|
|
88
|
-
...(snapshot.conflicts.length > 0
|
|
89
|
-
? snapshot.conflicts.map((conflict) => buildPanelLine(width, [[` ${conflict.key}`.padEnd(30), C.value], [` ${conflict.source}`.padEnd(10), C.warn], [` resolve: /settingssync resolve ${conflict.key} local|synced --yes`.slice(0, Math.max(0, width - 42)), C.dim]]))
|
|
90
|
-
: [buildPanelLine(width, [[' No settings conflicts detected.', C.dim]])]),
|
|
91
|
-
// Rollback History
|
|
92
|
-
...(snapshot.rollbackHistory.length > 0
|
|
93
|
-
? snapshot.rollbackHistory.map((entry) => buildPanelLine(width, [[` ${entry.token}`.padEnd(18), C.info], [` ${entry.profileName}`.padEnd(18), C.value], [` restored=${String(entry.restoredKeys.length).padEnd(4)}`, C.warn], [` ${new Date(entry.appliedAt).toLocaleString()}`.slice(0, Math.max(0, width - 46)), C.dim]]))
|
|
94
|
-
: [buildPanelLine(width, [[' No managed rollback records yet.', C.dim]])]),
|
|
95
|
-
];
|
|
96
|
-
|
|
97
|
-
this.clampSelection();
|
|
98
|
-
const selectedEntry = snapshot.resolvedEntries[this.selectedIndex];
|
|
99
|
-
const footerLines: Line[] = [
|
|
100
|
-
...(selectedEntry
|
|
101
|
-
? buildDetailBlock(width, 'Selected setting', [
|
|
102
|
-
buildPanelLine(width, [[' key ', C.label], [selectedEntry.key, C.value], [' category ', C.label], [selectedEntry.category, C.info]]),
|
|
103
|
-
buildPanelLine(width, [[' effective ', C.label], [selectedEntry.effectiveSource, selectedEntry.effectiveSource === 'managed' ? C.warn : selectedEntry.effectiveSource === 'synced' ? C.ok : selectedEntry.effectiveSource === 'local' ? C.info : C.dim], [' locked ', C.label], [selectedEntry.locked ? 'yes' : 'no', selectedEntry.locked ? C.warn : C.dim], [' conflict ', C.label], [selectedEntry.conflict ? 'yes' : 'no', selectedEntry.conflict ? C.error : C.good]]),
|
|
104
|
-
buildPanelLine(width, [[' source ', C.label], [(selectedEntry.sourceLabel ?? 'local/default').slice(0, Math.max(0, width - 10)), C.dim]]),
|
|
105
|
-
buildPanelLine(width, [[' overrides ', C.label], [(selectedEntry.overriddenSources.length > 0 ? selectedEntry.overriddenSources.join(', ') : 'none').slice(0, Math.max(0, width - 13)), C.dim]]),
|
|
106
|
-
buildPanelLine(width, [[' local ', C.label], [String(selectedEntry.localValue).slice(0, Math.max(0, width - 9)), C.dim]]),
|
|
107
|
-
buildPanelLine(width, [[' synced ', C.label], [String(selectedEntry.syncedValue ?? '(unset)').slice(0, Math.max(0, width - 10)), C.ok]]),
|
|
108
|
-
buildPanelLine(width, [[' managed ', C.label], [String(selectedEntry.managedValue ?? '(unset)').slice(0, Math.max(0, width - 11)), C.warn]]),
|
|
109
|
-
], C)
|
|
110
|
-
: []),
|
|
111
|
-
buildPanelLine(width, [[' ↑/↓ browse /settingssync show <key> mutations require --yes: resolve/apply-staged ', C.dim]]),
|
|
112
|
-
];
|
|
113
|
-
|
|
114
|
-
return this.renderList(width, height, {
|
|
115
|
-
title: 'Settings Sync',
|
|
116
|
-
header: headerLines,
|
|
117
|
-
footer: footerLines,
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
}
|