@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.
Files changed (60) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/package.json +42 -1
  3. package/src/agent/skill-discovery.ts +119 -0
  4. package/src/input/commands/delegation-runtime.ts +0 -8
  5. package/src/input/commands/experience-runtime.ts +0 -177
  6. package/src/input/commands/guidance-runtime.ts +0 -69
  7. package/src/input/commands/local-runtime.ts +1 -57
  8. package/src/input/commands/local-setup-review.ts +1 -1
  9. package/src/input/commands/operator-runtime.ts +1 -145
  10. package/src/input/commands/platform-access-runtime.ts +2 -195
  11. package/src/input/commands/product-runtime.ts +0 -116
  12. package/src/input/commands/security-runtime.ts +88 -0
  13. package/src/input/commands/session-content.ts +0 -97
  14. package/src/input/commands/shell-core.ts +0 -13
  15. package/src/input/commands.ts +2 -95
  16. package/src/panels/builtin/operations.ts +3 -184
  17. package/src/panels/index.ts +0 -11
  18. package/src/version.ts +1 -1
  19. package/src/input/commands/branch-runtime.ts +0 -72
  20. package/src/input/commands/control-room-runtime.ts +0 -234
  21. package/src/input/commands/discovery-runtime.ts +0 -61
  22. package/src/input/commands/hooks-runtime.ts +0 -207
  23. package/src/input/commands/incident-runtime.ts +0 -106
  24. package/src/input/commands/integration-runtime.ts +0 -437
  25. package/src/input/commands/local-setup.ts +0 -288
  26. package/src/input/commands/managed-runtime.ts +0 -240
  27. package/src/input/commands/marketplace-runtime.ts +0 -305
  28. package/src/input/commands/memory-product-runtime.ts +0 -148
  29. package/src/input/commands/operator-panel-runtime.ts +0 -146
  30. package/src/input/commands/platform-services-runtime.ts +0 -271
  31. package/src/input/commands/profile-sync-runtime.ts +0 -110
  32. package/src/input/commands/provider.ts +0 -363
  33. package/src/input/commands/remote-runtime-pool.ts +0 -89
  34. package/src/input/commands/remote-runtime-setup.ts +0 -226
  35. package/src/input/commands/remote-runtime.ts +0 -432
  36. package/src/input/commands/replay-runtime.ts +0 -25
  37. package/src/input/commands/services-runtime.ts +0 -220
  38. package/src/input/commands/settings-sync-runtime.ts +0 -197
  39. package/src/input/commands/share-runtime.ts +0 -127
  40. package/src/input/commands/skills-runtime.ts +0 -226
  41. package/src/input/commands/teleport-runtime.ts +0 -68
  42. package/src/panels/cockpit-panel.ts +0 -183
  43. package/src/panels/communication-panel.ts +0 -153
  44. package/src/panels/control-plane-panel.ts +0 -211
  45. package/src/panels/forensics-panel.ts +0 -364
  46. package/src/panels/hooks-panel.ts +0 -239
  47. package/src/panels/incident-review-panel.ts +0 -197
  48. package/src/panels/marketplace-panel.ts +0 -212
  49. package/src/panels/ops-control-panel.ts +0 -150
  50. package/src/panels/ops-strategy-panel.ts +0 -235
  51. package/src/panels/orchestration-panel.ts +0 -272
  52. package/src/panels/plugins-panel.ts +0 -178
  53. package/src/panels/remote-panel.ts +0 -449
  54. package/src/panels/routes-panel.ts +0 -178
  55. package/src/panels/services-panel.ts +0 -231
  56. package/src/panels/settings-sync-panel.ts +0 -120
  57. package/src/panels/skills-panel.ts +0 -431
  58. package/src/panels/watchers-panel.ts +0 -193
  59. package/src/verification/live-verifier.ts +0 -588
  60. package/src/verification/verification-ledger.ts +0 -239
@@ -1,288 +0,0 @@
1
- import { dirname, join } from 'path';
2
- import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
3
- import type { CommandRegistry } from '../command-registry.ts';
4
- import type { ConfigKey } from '../../config/index.ts';
5
- import { CONFIG_SCHEMA } from '../../config/index.ts';
6
- import { listHookPointContracts } from '@pellux/goodvibes-sdk/platform/hooks';
7
- import type { SetupTransferBundle } from './local-setup-transfer.ts';
8
- import {
9
- buildSetupTransferBundle,
10
- createSetupLink,
11
- exportSetupTransferBundle,
12
- inspectSetupTransferBundle,
13
- parseSetupLink,
14
- } from './local-setup-transfer.ts';
15
- import { buildSetupReviewSnapshot, exportSetupSupportBundle } from './local-setup-review.ts';
16
- import { openOnboardingWizard, requirePanelManager, requireShellPaths } from './runtime-services.ts';
17
- import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
18
- import { GOODVIBES_AGENT_SURFACE_ROOT } from '../../config/surface.ts';
19
- import { requireYesFlag, stripYesFlag } from './confirmation.ts';
20
-
21
- type SetupSnapshot = Awaited<ReturnType<typeof buildSetupReviewSnapshot>>;
22
-
23
- export function registerLocalSetupCommands(registry: CommandRegistry): void {
24
- registry.register({
25
- name: 'setup',
26
- aliases: ['startup'],
27
- description: 'Launch the onboarding wizard and review Agent startup readiness',
28
- usage: '[review|doctor|services|hooks|remote|onboarding|support-bundle <dir> --yes|export <path> --yes|transfer <export|inspect|import> <path> [--yes]|link <surface> [target]|open-link <uri>]',
29
- async handler(args, ctx) {
30
- const parsed = stripYesFlag(args);
31
- const commandArgs = [...parsed.rest];
32
- const sub = commandArgs[0] ?? 'review';
33
- let shellPaths: ReturnType<typeof requireShellPaths> | null = null;
34
- let snapshotPromise: Promise<SetupSnapshot> | null = null;
35
- const getShellPaths = () => (shellPaths ??= requireShellPaths(ctx));
36
- const getSnapshot = async (): Promise<SetupSnapshot> => {
37
- snapshotPromise ??= buildSetupReviewSnapshot(ctx);
38
- return snapshotPromise;
39
- };
40
-
41
- if (sub === 'review') {
42
- const snapshot = await getSnapshot();
43
- ctx.print([
44
- 'Startup Readiness Review',
45
- ` session: ${snapshot.sessionId}`,
46
- ` providers/models: ${snapshot.providerCount}`,
47
- ` services configured: ${snapshot.serviceCount}`,
48
- ` oauth providers: ${snapshot.oauthProviderCount + snapshot.builtinSubscriptionProviderCount}`,
49
- ` active subscriptions: ${snapshot.activeSubscriptionCount}`,
50
- ` pending subscriptions: ${snapshot.pendingSubscriptionCount}`,
51
- ` skills discovered: ${snapshot.skillCount}`,
52
- ` plugins discovered: ${snapshot.pluginCount}`,
53
- ` quarantined plugins: ${snapshot.quarantinedPluginCount}`,
54
- ` plugin search dirs: ${snapshot.pluginDirectories.length}`,
55
- ` managed hooks: ${snapshot.managedHookCount}`,
56
- ` managed hook chains: ${snapshot.managedHookChainCount}`,
57
- ` mcp servers known: ${snapshot.mcpServerCount}`,
58
- ` mcp quarantined: ${snapshot.quarantinedMcpCount}`,
59
- ` mcp elevated: ${snapshot.elevatedMcpCount}`,
60
- ` remote workers: ${snapshot.remoteRunnerCount}`,
61
- '',
62
- ` service ids: ${snapshot.services.join(', ') || '(none)'}`,
63
- ` plugin dirs: ${snapshot.pluginDirectories.join(', ') || '(none)'}`,
64
- ].join('\n'));
65
- return;
66
- }
67
-
68
- if (sub === 'doctor') {
69
- const snapshot = await getSnapshot();
70
- ctx.print([
71
- 'Startup Doctor',
72
- ...snapshot.issues.map((issue) => ` [${issue.severity.toUpperCase()}] ${issue.area}: ${issue.message}`),
73
- ...(snapshot.serviceIssues.length > 0
74
- ? ['', ' Service issues:', ...snapshot.serviceIssues.map((issue) => ` - ${issue}`)]
75
- : []),
76
- ].join('\n'));
77
- return;
78
- }
79
-
80
- if (sub === 'services') {
81
- const snapshot = await getSnapshot();
82
- ctx.print([
83
- 'Startup Services',
84
- ` configured: ${snapshot.serviceCount}`,
85
- ` oauth providers: ${snapshot.oauthProviderCount + snapshot.builtinSubscriptionProviderCount}`,
86
- ` active subscriptions: ${snapshot.activeSubscriptionCount}`,
87
- ` pending subscriptions: ${snapshot.pendingSubscriptionCount}`,
88
- ` issues: ${snapshot.serviceIssues.length}`,
89
- ...snapshot.services.map((name) => ` ${name}`),
90
- ...(snapshot.serviceIssues.length > 0
91
- ? ['', ...snapshot.serviceIssues.map((issue) => ` issue: ${issue}`)]
92
- : []),
93
- ].join('\n'));
94
- return;
95
- }
96
-
97
- if (sub === 'hooks') {
98
- const snapshot = await getSnapshot();
99
- const contracts = listHookPointContracts();
100
- ctx.print([
101
- 'Startup Hooks',
102
- ` managed hooks: ${snapshot.managedHookCount}`,
103
- ` managed chains: ${snapshot.managedHookChainCount}`,
104
- ` hook contracts: ${contracts.length}`,
105
- ].join('\n'));
106
- return;
107
- }
108
-
109
- if (sub === 'remote') {
110
- const snapshot = await getSnapshot();
111
- const runners = ctx.ops.remoteRuntime?.listContracts() ?? [];
112
- ctx.print([
113
- 'Startup Remote',
114
- ` worker contracts: ${snapshot.remoteRunnerCount}`,
115
- ...runners.map((runner) => ` ${runner.id} [${runner.trustClass}] ${runner.label}`),
116
- ].join('\n'));
117
- return;
118
- }
119
-
120
- if (sub === 'onboarding') {
121
- openOnboardingWizard(ctx, { mode: 'edit', reset: true });
122
- ctx.print('Opening onboarding wizard.');
123
- return;
124
- }
125
-
126
- if (sub === 'support-bundle') {
127
- const dirArg = commandArgs[1];
128
- if (!dirArg) {
129
- ctx.print('Usage: /setup support-bundle <dir> --yes');
130
- return;
131
- }
132
- if (!parsed.yes) {
133
- requireYesFlag(ctx, `export setup support bundle to ${dirArg}`, '/setup support-bundle <dir> --yes');
134
- return;
135
- }
136
- const snapshot = await getSnapshot();
137
- const targetDir = exportSetupSupportBundle(dirArg, snapshot, ctx);
138
- writeFileSync(join(targetDir, 'remote-summary.json'), JSON.stringify({
139
- runners: ctx.ops.remoteRuntime?.listContracts() ?? [],
140
- artifacts: (ctx.ops.remoteRuntime?.listArtifacts() ?? []).map((artifact) => ({
141
- id: artifact.id,
142
- runnerId: artifact.runnerId,
143
- status: artifact.task.status,
144
- createdAt: artifact.createdAt,
145
- })),
146
- }, null, 2) + '\n', 'utf-8');
147
- ctx.print(`Exported support bundle to ${targetDir}`);
148
- return;
149
- }
150
-
151
- if (sub === 'export') {
152
- const pathArg = commandArgs[1];
153
- if (!pathArg) {
154
- ctx.print('Usage: /setup export <path> --yes');
155
- return;
156
- }
157
- if (!parsed.yes) {
158
- requireYesFlag(ctx, `export startup review to ${pathArg}`, '/setup export <path> --yes');
159
- return;
160
- }
161
- const snapshot = await getSnapshot();
162
- const targetPath = getShellPaths().resolveWorkspacePath(pathArg);
163
- mkdirSync(dirname(targetPath), { recursive: true });
164
- writeFileSync(targetPath, JSON.stringify(snapshot, null, 2) + '\n', 'utf-8');
165
- ctx.print(`Exported startup review to ${targetPath}`);
166
- return;
167
- }
168
-
169
- if (sub === 'transfer') {
170
- const mode = commandArgs[1]?.toLowerCase();
171
- const pathArg = commandArgs[2];
172
- if (!mode || !pathArg) {
173
- ctx.print('Usage: /setup transfer <export|inspect|import> <path> [--yes]');
174
- return;
175
- }
176
- const targetPath = getShellPaths().resolveWorkspacePath(pathArg);
177
- if (mode === 'export') {
178
- if (!parsed.yes) {
179
- requireYesFlag(ctx, `export setup transfer bundle to ${pathArg}`, '/setup transfer export <path> --yes');
180
- return;
181
- }
182
- const snapshot = await getSnapshot();
183
- const bundle = buildSetupTransferBundle(ctx, snapshot);
184
- ctx.print(`Exported setup transfer bundle to ${exportSetupTransferBundle(ctx, pathArg, bundle)}`);
185
- return;
186
- }
187
- if (mode === 'inspect') {
188
- try {
189
- const bundle = JSON.parse(readFileSync(targetPath, 'utf-8')) as SetupTransferBundle;
190
- ctx.print(`${inspectSetupTransferBundle(bundle)}\n path: ${targetPath}`);
191
- } catch (error) {
192
- ctx.print(`Failed to inspect setup transfer bundle: ${summarizeError(error)}`);
193
- }
194
- return;
195
- }
196
- if (mode === 'import') {
197
- if (!parsed.yes) {
198
- requireYesFlag(ctx, `import setup transfer bundle from ${pathArg}`, '/setup transfer import <path> --yes');
199
- return;
200
- }
201
- try {
202
- const bundle = JSON.parse(readFileSync(targetPath, 'utf-8')) as SetupTransferBundle;
203
- for (const entry of CONFIG_SCHEMA) {
204
- if (Object.prototype.hasOwnProperty.call(bundle.config, entry.key)) {
205
- ctx.platform.configManager.setDynamic(entry.key as ConfigKey, (bundle.config as Record<string, unknown>)[entry.key]);
206
- }
207
- }
208
- if (bundle.services) {
209
- const servicesPath = getShellPaths().resolveProjectPath(GOODVIBES_AGENT_SURFACE_ROOT, 'services.json');
210
- mkdirSync(dirname(servicesPath), { recursive: true });
211
- writeFileSync(servicesPath, JSON.stringify(bundle.services, null, 2) + '\n', 'utf-8');
212
- }
213
- if (bundle.ecosystem?.plugins) {
214
- const pluginsPath = getShellPaths().resolveProjectPath(GOODVIBES_AGENT_SURFACE_ROOT, 'ecosystem', 'plugins.json');
215
- mkdirSync(dirname(pluginsPath), { recursive: true });
216
- writeFileSync(pluginsPath, JSON.stringify(bundle.ecosystem.plugins, null, 2) + '\n', 'utf-8');
217
- }
218
- if (bundle.ecosystem?.skills) {
219
- const skillsPath = getShellPaths().resolveProjectPath(GOODVIBES_AGENT_SURFACE_ROOT, 'ecosystem', 'skills.json');
220
- mkdirSync(dirname(skillsPath), { recursive: true });
221
- writeFileSync(skillsPath, JSON.stringify(bundle.ecosystem.skills, null, 2) + '\n', 'utf-8');
222
- }
223
- ctx.print(`Imported setup transfer bundle from ${targetPath}`);
224
- } catch (error) {
225
- ctx.print(`Failed to import setup transfer bundle: ${summarizeError(error)}`);
226
- }
227
- return;
228
- }
229
- ctx.print('Usage: /setup transfer <export|inspect|import> <path> [--yes]');
230
- return;
231
- }
232
-
233
- if (sub === 'link') {
234
- const surface = commandArgs[1];
235
- const target = commandArgs[2];
236
- if (!surface) {
237
- ctx.print('Usage: /setup link <cockpit|security|remote|knowledge|incident|hooks|orchestration|tasks> [target]');
238
- return;
239
- }
240
- ctx.print(createSetupLink(surface, target));
241
- return;
242
- }
243
-
244
- if (sub === 'open-link') {
245
- const link = commandArgs[1];
246
- if (!link) {
247
- ctx.print('Usage: /setup open-link <goodvibes://...>');
248
- return;
249
- }
250
- const parsed = parseSetupLink(link);
251
- if (!parsed) {
252
- ctx.print(`Invalid setup link: ${link}`);
253
- return;
254
- }
255
- const panelOpeners: Record<string, (() => void) | undefined> = {
256
- cockpit: ctx.openCockpitPanel,
257
- security: ctx.openSecurityPanel,
258
- remote: ctx.openRemotePanel,
259
- knowledge: ctx.openKnowledgePanel,
260
- incident: ctx.openIncidentPanel,
261
- hooks: ctx.openHooksPanel,
262
- orchestration: ctx.openOrchestrationPanel,
263
- };
264
- if (parsed.surface === 'tasks') {
265
- if (ctx.showPanel) ctx.showPanel('tasks');
266
- else {
267
- const panelManager = requirePanelManager(ctx);
268
- panelManager.open('tasks');
269
- panelManager.show();
270
- ctx.renderRequest();
271
- }
272
- ctx.print(`Opened setup link for tasks${parsed.target ? ` (${parsed.target})` : ''}.`);
273
- return;
274
- }
275
- const openPanel = panelOpeners[parsed.surface];
276
- if (!openPanel) {
277
- ctx.print(`Unsupported setup link surface: ${parsed.surface}`);
278
- return;
279
- }
280
- openPanel();
281
- ctx.print(`Opened setup link for ${parsed.surface}${parsed.target ? ` (${parsed.target})` : ''}.`);
282
- return;
283
- }
284
-
285
- ctx.print('Usage: /setup [review|doctor|services|hooks|remote|onboarding|support-bundle <dir> --yes|export <path> --yes|transfer <export|inspect|import> <path> [--yes]|link <surface> [target]|open-link <uri>]');
286
- },
287
- });
288
- }
@@ -1,240 +0,0 @@
1
- import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
- import { dirname, resolve } from 'node:path';
3
- import type { CommandRegistry } from '../command-registry.ts';
4
- import { profileDataToConfigSnapshot } from '@pellux/goodvibes-sdk/platform/profiles';
5
- import { CONFIG_SCHEMA, type ConfigKey } from '../../config/index.ts';
6
- import { getProviderIdFromModel } from '../../config/provider-model.ts';
7
- import { CONFIG_KEYS } from '@pellux/goodvibes-sdk/platform/config';
8
- import type { ManagedSettingsBundle } from '@/runtime/index.ts';
9
- import {
10
- applyStagedManagedBundle,
11
- clearManagedSettingLock,
12
- formatStagedManagedBundleReview,
13
- getSettingsControlPlaneSnapshot,
14
- inspectManagedSettingsBundle,
15
- recordSettingsSyncEvent,
16
- recordSettingsSyncFailure,
17
- rollbackManagedApply,
18
- setManagedSettingLock,
19
- stageManagedSettingsBundle,
20
- } from '@/runtime/index.ts';
21
- import { requireProfileManager, requireShellPaths } from './runtime-services.ts';
22
- import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
23
- import { requireYesFlag, stripYesFlag } from './confirmation.ts';
24
-
25
- function buildConfigSnapshot(
26
- manager: { get: (key: ConfigKey) => unknown },
27
- ): Record<string, unknown> {
28
- const snapshot: Record<string, unknown> = {};
29
- for (const entry of CONFIG_SCHEMA) {
30
- try {
31
- snapshot[entry.key] = structuredClone(manager.get(entry.key));
32
- } catch {
33
- // ignore unreadable settings
34
- }
35
- }
36
- return snapshot;
37
- }
38
-
39
- export function registerManagedRuntimeCommands(registry: CommandRegistry): void {
40
- registry.register({
41
- name: 'managed',
42
- description: 'Export, inspect, and apply managed settings bundles',
43
- usage: '[review|staged|rollback-history|export <profile> <path> --yes|inspect <path>|stage <path> --yes|apply <path> [key ...] --yes|apply-staged [key ...] --yes|rollback <token> --yes|lock <key> <source> <reason...> --yes|unlock <key> --yes]',
44
- handler(args, ctx) {
45
- const parsed = stripYesFlag(args);
46
- const commandArgs = [...parsed.rest];
47
- const shellPaths = requireShellPaths(ctx);
48
- const controlPlaneConfigDir = ctx.platform.configManager.getControlPlaneConfigDir();
49
- const sub = commandArgs[0] ?? 'review';
50
- const pm = requireProfileManager(ctx);
51
- if (sub === 'review') {
52
- const profiles = pm.list();
53
- const snapshot = getSettingsControlPlaneSnapshot(ctx.platform.configManager);
54
- ctx.print([
55
- 'Managed Settings Review',
56
- ` saved profiles: ${profiles.length}`,
57
- ` live config keys: ${Object.keys(buildConfigSnapshot(ctx.platform.configManager)).length}`,
58
- ` staged bundle: ${snapshot.stagedManagedBundle ? snapshot.stagedManagedBundle.profileName : 'none'}`,
59
- ` active locks: ${snapshot.managedLockCount}`,
60
- ` rollback records: ${snapshot.rollbackHistory.length}`,
61
- ].join('\n'));
62
- return;
63
- }
64
-
65
- if (sub === 'staged') {
66
- ctx.print(formatStagedManagedBundleReview(ctx.platform.configManager));
67
- return;
68
- }
69
-
70
- if (sub === 'rollback-history') {
71
- const snapshot = getSettingsControlPlaneSnapshot(ctx.platform.configManager);
72
- ctx.print(snapshot.rollbackHistory.length > 0
73
- ? [
74
- 'Managed Rollback History',
75
- ...snapshot.rollbackHistory.map((entry) => (
76
- ` ${entry.token} ${entry.profileName} restored=${entry.restoredKeys.length} ${new Date(entry.appliedAt).toLocaleString()}`
77
- )),
78
- ].join('\n')
79
- : 'Managed Rollback History\n No managed rollback records yet.');
80
- return;
81
- }
82
-
83
- if (sub === 'lock') {
84
- const key = commandArgs[1] as ConfigKey | undefined;
85
- const source = commandArgs[2];
86
- const reason = commandArgs.slice(3).join(' ').trim();
87
- if (!key || !source || !reason) {
88
- ctx.print('Usage: /managed lock <key> <source> <reason...> --yes');
89
- return;
90
- }
91
- if (!parsed.yes) {
92
- requireYesFlag(ctx, `lock managed setting ${key}`, '/managed lock <key> <source> <reason...> --yes');
93
- return;
94
- }
95
- setManagedSettingLock(key, source, reason, controlPlaneConfigDir);
96
- ctx.print(`Managed lock recorded for ${key}.`);
97
- return;
98
- }
99
-
100
- if (sub === 'unlock') {
101
- const key = commandArgs[1] as ConfigKey | undefined;
102
- if (!key) {
103
- ctx.print('Usage: /managed unlock <key> --yes');
104
- return;
105
- }
106
- if (!parsed.yes) {
107
- requireYesFlag(ctx, `unlock managed setting ${key}`, '/managed unlock <key> --yes');
108
- return;
109
- }
110
- ctx.print(clearManagedSettingLock(key, controlPlaneConfigDir) ? `Managed lock cleared for ${key}.` : `No managed lock found for ${key}.`);
111
- return;
112
- }
113
-
114
- if (sub === 'export') {
115
- const profileName = commandArgs[1];
116
- const pathArg = commandArgs[2];
117
- if (!profileName || !pathArg) {
118
- ctx.print('Usage: /managed export <profile> <path> --yes');
119
- return;
120
- }
121
- if (!parsed.yes) {
122
- requireYesFlag(ctx, `export managed settings profile ${profileName} to ${pathArg}`, '/managed export <profile> <path> --yes');
123
- return;
124
- }
125
- const loaded = pm.load(profileName);
126
- const bundle: ManagedSettingsBundle = {
127
- version: 1,
128
- exportedAt: Date.now(),
129
- profileName,
130
- settings: profileDataToConfigSnapshot(loaded.data),
131
- };
132
- const targetPath = shellPaths.resolveWorkspacePath(pathArg);
133
- mkdirSync(dirname(targetPath), { recursive: true });
134
- writeFileSync(targetPath, JSON.stringify(bundle, null, 2) + '\n', 'utf-8');
135
- recordSettingsSyncEvent({
136
- surface: 'managed',
137
- direction: 'export',
138
- path: targetPath,
139
- timestamp: Date.now(),
140
- detail: `${Object.keys(bundle.settings).length} settings exported from ${profileName}`,
141
- }, controlPlaneConfigDir);
142
- ctx.print(`Managed settings bundle exported to ${targetPath}`);
143
- return;
144
- }
145
-
146
- if (sub === 'apply-staged') {
147
- const requestedKeys = commandArgs.slice(1).filter((value): value is ConfigKey => CONFIG_KEYS.has(value as ConfigKey));
148
- const invalidKeys = commandArgs.slice(1).filter((value) => !CONFIG_KEYS.has(value as ConfigKey));
149
- if (invalidKeys.length > 0) {
150
- ctx.print(`Unknown config key(s): ${invalidKeys.join(', ')}`);
151
- return;
152
- }
153
- if (!parsed.yes) {
154
- requireYesFlag(ctx, 'apply staged managed settings', '/managed apply-staged [key ...] --yes');
155
- return;
156
- }
157
- try {
158
- const result = applyStagedManagedBundle(ctx.platform.configManager, requestedKeys);
159
- ctx.session.runtime.model = String(ctx.platform.configManager.get('provider.model'));
160
- ctx.session.runtime.provider = getProviderIdFromModel(ctx.platform.configManager.get('provider.model'));
161
- ctx.session.runtime.reasoningEffort = ctx.platform.configManager.get('provider.reasoningEffort') as string;
162
- ctx.print(`Staged managed settings applied (${result.appliedCount} changes, rollback ${result.rollbackToken}${result.remainingCount > 0 ? `, ${result.remainingCount} still staged` : ''}).`);
163
- } catch (error) {
164
- recordSettingsSyncFailure('managed', summarizeError(error), controlPlaneConfigDir);
165
- ctx.print(summarizeError(error));
166
- }
167
- return;
168
- }
169
-
170
- if (sub === 'rollback') {
171
- const token = commandArgs[1];
172
- if (!token) {
173
- ctx.print('Usage: /managed rollback <token> --yes');
174
- return;
175
- }
176
- if (!parsed.yes) {
177
- requireYesFlag(ctx, `rollback managed settings token ${token}`, '/managed rollback <token> --yes');
178
- return;
179
- }
180
- try {
181
- const restored = rollbackManagedApply(ctx.platform.configManager, token);
182
- ctx.session.runtime.model = String(ctx.platform.configManager.get('provider.model'));
183
- ctx.session.runtime.provider = getProviderIdFromModel(ctx.platform.configManager.get('provider.model'));
184
- ctx.session.runtime.reasoningEffort = ctx.platform.configManager.get('provider.reasoningEffort') as string;
185
- ctx.print(`Managed rollback ${token} restored ${restored} setting(s).`);
186
- } catch (error) {
187
- recordSettingsSyncFailure('managed', summarizeError(error), controlPlaneConfigDir);
188
- ctx.print(summarizeError(error));
189
- }
190
- return;
191
- }
192
-
193
- const pathArg = commandArgs[1];
194
- if (!pathArg) {
195
- ctx.print(`Usage: /managed ${sub} <path>${sub === 'stage' || sub === 'apply' ? ' --yes' : ''}`);
196
- return;
197
- }
198
- const sourcePath = shellPaths.resolveWorkspacePath(pathArg);
199
- const bundle = JSON.parse(readFileSync(sourcePath, 'utf-8')) as ManagedSettingsBundle;
200
-
201
- if (sub === 'inspect') {
202
- ctx.print(inspectManagedSettingsBundle(ctx.platform.configManager, bundle, sourcePath));
203
- return;
204
- }
205
-
206
- if (sub === 'stage') {
207
- if (!parsed.yes) {
208
- requireYesFlag(ctx, `stage managed settings bundle from ${pathArg}`, '/managed stage <path> --yes');
209
- return;
210
- }
211
- const stage = stageManagedSettingsBundle(ctx.platform.configManager, bundle, sourcePath);
212
- ctx.print(`Managed settings bundle staged from ${sourcePath} (${stage.changeCount} changes, risk=${stage.risk}).`);
213
- return;
214
- }
215
-
216
- if (sub === 'apply') {
217
- const requestedKeys = commandArgs.slice(2).filter((value): value is ConfigKey => CONFIG_KEYS.has(value as ConfigKey));
218
- const invalidKeys = commandArgs.slice(2).filter((value) => !CONFIG_KEYS.has(value as ConfigKey));
219
- if (invalidKeys.length > 0) {
220
- ctx.print(`Unknown config key(s): ${invalidKeys.join(', ')}`);
221
- return;
222
- }
223
- if (!parsed.yes) {
224
- requireYesFlag(ctx, `apply managed settings bundle from ${pathArg}`, '/managed apply <path> [key ...] --yes');
225
- return;
226
- }
227
- stageManagedSettingsBundle(ctx.platform.configManager, bundle, sourcePath);
228
- const result = applyStagedManagedBundle(ctx.platform.configManager, requestedKeys);
229
- ctx.session.runtime.model = String(ctx.platform.configManager.get('provider.model'));
230
- ctx.session.runtime.provider = getProviderIdFromModel(ctx.platform.configManager.get('provider.model'));
231
- ctx.session.runtime.reasoningEffort = ctx.platform.configManager.get('provider.reasoningEffort') as string;
232
- ctx.print(`Managed settings bundle applied from ${sourcePath} (${result.appliedCount} changes, rollback ${result.rollbackToken}${result.remainingCount > 0 ? `, ${result.remainingCount} still staged` : ''}).`);
233
- return;
234
- }
235
-
236
- recordSettingsSyncFailure('managed', `unsupported subcommand: ${sub}`, controlPlaneConfigDir);
237
- ctx.print('Usage: /managed [review|staged|rollback-history|export <profile> <path> --yes|inspect <path>|stage <path> --yes|apply <path> [key ...] --yes|apply-staged [key ...] --yes|rollback <token> --yes|lock <key> <source> <reason...> --yes|unlock <key> --yes]');
238
- },
239
- });
240
- }