@pellux/goodvibes-agent 0.1.70 → 0.1.72

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 (78) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +3 -3
  3. package/docs/README.md +2 -2
  4. package/docs/getting-started.md +1 -1
  5. package/docs/runtime-connection.md +37 -0
  6. package/package.json +43 -2
  7. package/src/agent/skill-discovery.ts +119 -0
  8. package/src/cli/config-overrides.ts +1 -5
  9. package/src/cli/entrypoint.ts +0 -6
  10. package/src/cli/help.ts +0 -43
  11. package/src/cli/index.ts +0 -2
  12. package/src/cli/management-commands.ts +1 -109
  13. package/src/cli/management.ts +1 -32
  14. package/src/cli/package-verification.ts +12 -4
  15. package/src/cli/parser.ts +0 -16
  16. package/src/cli/status.ts +1 -1
  17. package/src/cli/types.ts +0 -8
  18. package/src/input/commands/delegation-runtime.ts +0 -8
  19. package/src/input/commands/experience-runtime.ts +0 -177
  20. package/src/input/commands/guidance-runtime.ts +0 -69
  21. package/src/input/commands/local-runtime.ts +1 -57
  22. package/src/input/commands/local-setup-review.ts +1 -1
  23. package/src/input/commands/operator-runtime.ts +1 -145
  24. package/src/input/commands/platform-access-runtime.ts +2 -195
  25. package/src/input/commands/product-runtime.ts +0 -116
  26. package/src/input/commands/security-runtime.ts +88 -0
  27. package/src/input/commands/session-content.ts +0 -97
  28. package/src/input/commands/shell-core.ts +0 -13
  29. package/src/input/commands.ts +2 -95
  30. package/src/panels/builtin/operations.ts +3 -184
  31. package/src/panels/confirm-state.ts +1 -1
  32. package/src/panels/index.ts +0 -11
  33. package/src/version.ts +1 -1
  34. package/docs/deployment-and-services.md +0 -52
  35. package/src/cli/service-command.ts +0 -26
  36. package/src/cli/surface-command.ts +0 -247
  37. package/src/input/commands/branch-runtime.ts +0 -72
  38. package/src/input/commands/control-room-runtime.ts +0 -234
  39. package/src/input/commands/discovery-runtime.ts +0 -61
  40. package/src/input/commands/hooks-runtime.ts +0 -207
  41. package/src/input/commands/incident-runtime.ts +0 -106
  42. package/src/input/commands/integration-runtime.ts +0 -437
  43. package/src/input/commands/local-setup.ts +0 -288
  44. package/src/input/commands/managed-runtime.ts +0 -240
  45. package/src/input/commands/marketplace-runtime.ts +0 -305
  46. package/src/input/commands/memory-product-runtime.ts +0 -148
  47. package/src/input/commands/operator-panel-runtime.ts +0 -146
  48. package/src/input/commands/platform-services-runtime.ts +0 -271
  49. package/src/input/commands/profile-sync-runtime.ts +0 -110
  50. package/src/input/commands/provider.ts +0 -363
  51. package/src/input/commands/remote-runtime-pool.ts +0 -89
  52. package/src/input/commands/remote-runtime-setup.ts +0 -226
  53. package/src/input/commands/remote-runtime.ts +0 -432
  54. package/src/input/commands/replay-runtime.ts +0 -25
  55. package/src/input/commands/services-runtime.ts +0 -220
  56. package/src/input/commands/settings-sync-runtime.ts +0 -197
  57. package/src/input/commands/share-runtime.ts +0 -127
  58. package/src/input/commands/skills-runtime.ts +0 -226
  59. package/src/input/commands/teleport-runtime.ts +0 -68
  60. package/src/panels/cockpit-panel.ts +0 -183
  61. package/src/panels/communication-panel.ts +0 -153
  62. package/src/panels/control-plane-panel.ts +0 -211
  63. package/src/panels/forensics-panel.ts +0 -364
  64. package/src/panels/hooks-panel.ts +0 -239
  65. package/src/panels/incident-review-panel.ts +0 -197
  66. package/src/panels/marketplace-panel.ts +0 -212
  67. package/src/panels/ops-control-panel.ts +0 -150
  68. package/src/panels/ops-strategy-panel.ts +0 -235
  69. package/src/panels/orchestration-panel.ts +0 -272
  70. package/src/panels/plugins-panel.ts +0 -178
  71. package/src/panels/remote-panel.ts +0 -449
  72. package/src/panels/routes-panel.ts +0 -178
  73. package/src/panels/services-panel.ts +0 -231
  74. package/src/panels/settings-sync-panel.ts +0 -120
  75. package/src/panels/skills-panel.ts +0 -431
  76. package/src/panels/watchers-panel.ts +0 -193
  77. package/src/verification/live-verifier.ts +0 -588
  78. 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
- }