@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.
- package/CHANGELOG.md +12 -0
- package/README.md +3 -3
- package/docs/README.md +2 -2
- package/docs/getting-started.md +1 -1
- package/docs/runtime-connection.md +37 -0
- package/package.json +43 -2
- package/src/agent/skill-discovery.ts +119 -0
- package/src/cli/config-overrides.ts +1 -5
- package/src/cli/entrypoint.ts +0 -6
- package/src/cli/help.ts +0 -43
- package/src/cli/index.ts +0 -2
- package/src/cli/management-commands.ts +1 -109
- package/src/cli/management.ts +1 -32
- package/src/cli/package-verification.ts +12 -4
- package/src/cli/parser.ts +0 -16
- package/src/cli/status.ts +1 -1
- package/src/cli/types.ts +0 -8
- 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/confirm-state.ts +1 -1
- package/src/panels/index.ts +0 -11
- package/src/version.ts +1 -1
- package/docs/deployment-and-services.md +0 -52
- package/src/cli/service-command.ts +0 -26
- package/src/cli/surface-command.ts +0 -247
- 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,271 +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 { requireSecretsManager, requireShellPaths } from './runtime-services.ts';
|
|
5
|
-
import { requireYesFlag, stripYesFlag } from './confirmation.ts';
|
|
6
|
-
|
|
7
|
-
interface SecureStorageBundle {
|
|
8
|
-
readonly version: 1;
|
|
9
|
-
readonly exportedAt: number;
|
|
10
|
-
readonly storedKeys: readonly string[];
|
|
11
|
-
readonly envBackedKeys: readonly string[];
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
interface DeepLinkBundle {
|
|
15
|
-
readonly version: 1;
|
|
16
|
-
readonly exportedAt: number;
|
|
17
|
-
readonly links: readonly string[];
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
interface IntegrationHelperBundle {
|
|
21
|
-
readonly version: 1;
|
|
22
|
-
readonly exportedAt: number;
|
|
23
|
-
readonly apiFamilies: readonly string[];
|
|
24
|
-
readonly routes: readonly string[];
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function buildSetupLink(surface: string, target?: string): string {
|
|
28
|
-
const params = target ? `?target=${encodeURIComponent(target)}` : '';
|
|
29
|
-
return `goodvibes://open/${surface}${params}`;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function inspectStorageBundle(bundle: SecureStorageBundle): string {
|
|
33
|
-
return [
|
|
34
|
-
'Secure Storage Bundle Review',
|
|
35
|
-
` exportedAt: ${new Date(bundle.exportedAt).toISOString()}`,
|
|
36
|
-
` storedKeys: ${bundle.storedKeys.length}`,
|
|
37
|
-
` envBackedKeys: ${bundle.envBackedKeys.length}`,
|
|
38
|
-
].join('\n');
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function inspectDeepLinkBundle(bundle: DeepLinkBundle): string {
|
|
42
|
-
return [
|
|
43
|
-
'Deep Link Bundle Review',
|
|
44
|
-
` exportedAt: ${new Date(bundle.exportedAt).toISOString()}`,
|
|
45
|
-
` links: ${bundle.links.length}`,
|
|
46
|
-
].join('\n');
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function inspectIntegrationHelperBundle(bundle: IntegrationHelperBundle): string {
|
|
50
|
-
return [
|
|
51
|
-
'Integration Helper Review',
|
|
52
|
-
` exportedAt: ${new Date(bundle.exportedAt).toISOString()}`,
|
|
53
|
-
` apiFamilies: ${bundle.apiFamilies.length}`,
|
|
54
|
-
` routes: ${bundle.routes.length}`,
|
|
55
|
-
].join('\n');
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function registerPlatformServicesRuntimeCommands(registry: CommandRegistry): void {
|
|
59
|
-
registry.register({
|
|
60
|
-
name: 'storage',
|
|
61
|
-
description: 'Review secure storage posture and export portable storage metadata bundles',
|
|
62
|
-
usage: '[review|list|delete <key> --yes|bundle export <path> --yes|bundle inspect <path>]',
|
|
63
|
-
async handler(args, ctx) {
|
|
64
|
-
const parsed = stripYesFlag(args);
|
|
65
|
-
const commandArgs = [...parsed.rest];
|
|
66
|
-
const shellPaths = requireShellPaths(ctx);
|
|
67
|
-
const manager = requireSecretsManager(ctx);
|
|
68
|
-
const sub = commandArgs[0] ?? 'review';
|
|
69
|
-
const review = await manager.inspect();
|
|
70
|
-
const storedKeys = await manager.list();
|
|
71
|
-
const detailedKeys = await manager.listDetailed();
|
|
72
|
-
const envBackedKeys = [...new Set(detailedKeys.filter((record) => record.source === 'env').map((record) => record.key))];
|
|
73
|
-
|
|
74
|
-
if (sub === 'review') {
|
|
75
|
-
ctx.print([
|
|
76
|
-
'Secure Storage Review',
|
|
77
|
-
` policy: ${review.policy}`,
|
|
78
|
-
` stored keys: ${storedKeys.length}`,
|
|
79
|
-
` env-backed keys: ${envBackedKeys.length}`,
|
|
80
|
-
` secure keys: ${review.secureKeys}`,
|
|
81
|
-
` plaintext keys: ${review.plaintextKeys}`,
|
|
82
|
-
...review.locations.map((location) => ` ${location.source}: ${location.exists ? 'present' : 'absent'} (${location.path})`),
|
|
83
|
-
...(review.warnings.length > 0 ? review.warnings.map((warning) => ` warning: ${warning}`) : []),
|
|
84
|
-
].join('\n'));
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
if (sub === 'list') {
|
|
88
|
-
ctx.print(detailedKeys.filter((record) => record.source !== 'env').length > 0
|
|
89
|
-
? ['Secure Storage Keys', ...detailedKeys.filter((record) => record.source !== 'env').map((record) => ` ${record.key} (${record.source}${record.refSource ? `, ref:${record.refSource}` : ''}${record.overriddenByEnv ? ', env override' : ''})`)].join('\n')
|
|
90
|
-
: 'Secure Storage Keys\n No stored secrets yet.');
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
if (sub === 'delete') {
|
|
94
|
-
const key = commandArgs[1];
|
|
95
|
-
if (!key) {
|
|
96
|
-
ctx.print('Usage: /storage delete <key> --yes');
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
if (!parsed.yes) {
|
|
100
|
-
requireYesFlag(ctx, `delete secure storage key ${key}`, '/storage delete <key> --yes');
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
await manager.delete(key);
|
|
104
|
-
ctx.print(`Deleted secure storage key ${key}.`);
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
if (sub === 'bundle') {
|
|
108
|
-
const mode = commandArgs[1];
|
|
109
|
-
const pathArg = commandArgs[2];
|
|
110
|
-
if ((mode === 'export' || mode === 'inspect') && !pathArg) {
|
|
111
|
-
ctx.print(`Usage: /storage bundle ${mode} <path>${mode === 'export' ? ' --yes' : ''}`);
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
if (mode === 'export') {
|
|
115
|
-
if (!parsed.yes) {
|
|
116
|
-
requireYesFlag(ctx, `export secure storage metadata bundle to ${pathArg}`, '/storage bundle export <path> --yes');
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
const targetPath = shellPaths.resolveWorkspacePath(pathArg!);
|
|
120
|
-
const bundle: SecureStorageBundle = {
|
|
121
|
-
version: 1,
|
|
122
|
-
exportedAt: Date.now(),
|
|
123
|
-
storedKeys,
|
|
124
|
-
envBackedKeys,
|
|
125
|
-
};
|
|
126
|
-
mkdirSync(dirname(targetPath), { recursive: true });
|
|
127
|
-
writeFileSync(targetPath, `${JSON.stringify(bundle, null, 2)}\n`, 'utf-8');
|
|
128
|
-
ctx.print(`Secure storage bundle exported to ${targetPath}`);
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
if (mode === 'inspect') {
|
|
132
|
-
const targetPath = shellPaths.resolveWorkspacePath(pathArg!);
|
|
133
|
-
const bundle = JSON.parse(readFileSync(targetPath, 'utf-8')) as SecureStorageBundle;
|
|
134
|
-
ctx.print(inspectStorageBundle(bundle));
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
ctx.print('Usage: /storage [review|list|delete <key> --yes|bundle export <path> --yes|bundle inspect <path>]');
|
|
139
|
-
},
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
registry.register({
|
|
143
|
-
name: 'helpers',
|
|
144
|
-
aliases: ['integration-api'],
|
|
145
|
-
description: 'Review local integration helper API surfaces for remote clients and future web frontends',
|
|
146
|
-
usage: '[review|bundle export <path> --yes|bundle inspect <path>]',
|
|
147
|
-
handler(args, ctx) {
|
|
148
|
-
const parsed = stripYesFlag(args);
|
|
149
|
-
const commandArgs = [...parsed.rest];
|
|
150
|
-
const shellPaths = requireShellPaths(ctx);
|
|
151
|
-
const sub = commandArgs[0] ?? 'review';
|
|
152
|
-
const review = ctx.extensions.integrationHelpers?.buildReview();
|
|
153
|
-
if (!review) {
|
|
154
|
-
ctx.print('Integration helper service unavailable in this runtime.');
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
if (sub === 'review') {
|
|
158
|
-
ctx.print([
|
|
159
|
-
'Integration Helper Review',
|
|
160
|
-
` sessions: ${review.sessions}`,
|
|
161
|
-
` tasks: ${review.tasks}`,
|
|
162
|
-
` pending approvals: ${review.pendingApprovals}`,
|
|
163
|
-
` remote contracts: ${review.remoteContracts}`,
|
|
164
|
-
` registered panels: ${review.panels}`,
|
|
165
|
-
' api families:',
|
|
166
|
-
...review.apiFamilies.map((family) => ` - ${family}`),
|
|
167
|
-
' routes:',
|
|
168
|
-
...review.routes.map((route) => ` - ${route}`),
|
|
169
|
-
].join('\n'));
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
if (sub === 'bundle') {
|
|
173
|
-
const mode = commandArgs[1];
|
|
174
|
-
const pathArg = commandArgs[2];
|
|
175
|
-
if ((mode === 'export' || mode === 'inspect') && !pathArg) {
|
|
176
|
-
ctx.print(`Usage: /helpers bundle ${mode} <path>${mode === 'export' ? ' --yes' : ''}`);
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
if (mode === 'export') {
|
|
180
|
-
if (!parsed.yes) {
|
|
181
|
-
requireYesFlag(ctx, `export integration helper bundle to ${pathArg}`, '/helpers bundle export <path> --yes');
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
const targetPath = shellPaths.resolveWorkspacePath(pathArg!);
|
|
185
|
-
const bundle: IntegrationHelperBundle = {
|
|
186
|
-
version: 1,
|
|
187
|
-
exportedAt: Date.now(),
|
|
188
|
-
apiFamilies: review.apiFamilies,
|
|
189
|
-
routes: review.routes,
|
|
190
|
-
};
|
|
191
|
-
mkdirSync(dirname(targetPath), { recursive: true });
|
|
192
|
-
writeFileSync(targetPath, `${JSON.stringify(bundle, null, 2)}\n`, 'utf-8');
|
|
193
|
-
ctx.print(`Integration helper bundle exported to ${targetPath}`);
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
if (mode === 'inspect') {
|
|
197
|
-
const targetPath = shellPaths.resolveWorkspacePath(pathArg!);
|
|
198
|
-
const bundle = JSON.parse(readFileSync(targetPath, 'utf-8')) as IntegrationHelperBundle;
|
|
199
|
-
ctx.print(inspectIntegrationHelperBundle(bundle));
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
ctx.print('Usage: /helpers [review|bundle export <path> --yes|bundle inspect <path>]');
|
|
204
|
-
},
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
registry.register({
|
|
208
|
-
name: 'deeplink',
|
|
209
|
-
aliases: ['link'],
|
|
210
|
-
description: 'Review and package deep-link entrypoints for setup and operator surfaces',
|
|
211
|
-
usage: '[review|open <surface> [target]|bundle export <path> --yes|bundle inspect <path>]',
|
|
212
|
-
handler(args, ctx) {
|
|
213
|
-
const parsed = stripYesFlag(args);
|
|
214
|
-
const commandArgs = [...parsed.rest];
|
|
215
|
-
const shellPaths = requireShellPaths(ctx);
|
|
216
|
-
const sub = commandArgs[0] ?? 'review';
|
|
217
|
-
const links = [
|
|
218
|
-
buildSetupLink('cockpit'),
|
|
219
|
-
buildSetupLink('security'),
|
|
220
|
-
buildSetupLink('remote'),
|
|
221
|
-
buildSetupLink('knowledge'),
|
|
222
|
-
buildSetupLink('marketplace'),
|
|
223
|
-
];
|
|
224
|
-
if (sub === 'review') {
|
|
225
|
-
ctx.print(['Deep Link Review', ...links.map((link) => ` ${link}`)].join('\n'));
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
if (sub === 'open') {
|
|
229
|
-
const surface = commandArgs[1];
|
|
230
|
-
const target = commandArgs[2];
|
|
231
|
-
if (!surface) {
|
|
232
|
-
ctx.print('Usage: /deeplink open <surface> [target]');
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
ctx.print(buildSetupLink(surface, target));
|
|
236
|
-
return;
|
|
237
|
-
}
|
|
238
|
-
if (sub === 'bundle') {
|
|
239
|
-
const mode = commandArgs[1];
|
|
240
|
-
const pathArg = commandArgs[2];
|
|
241
|
-
if ((mode === 'export' || mode === 'inspect') && !pathArg) {
|
|
242
|
-
ctx.print(`Usage: /deeplink bundle ${mode} <path>${mode === 'export' ? ' --yes' : ''}`);
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
245
|
-
if (mode === 'export') {
|
|
246
|
-
if (!parsed.yes) {
|
|
247
|
-
requireYesFlag(ctx, `export deep link bundle to ${pathArg}`, '/deeplink bundle export <path> --yes');
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
const targetPath = shellPaths.resolveWorkspacePath(pathArg!);
|
|
251
|
-
const bundle: DeepLinkBundle = {
|
|
252
|
-
version: 1,
|
|
253
|
-
exportedAt: Date.now(),
|
|
254
|
-
links,
|
|
255
|
-
};
|
|
256
|
-
mkdirSync(dirname(targetPath), { recursive: true });
|
|
257
|
-
writeFileSync(targetPath, `${JSON.stringify(bundle, null, 2)}\n`, 'utf-8');
|
|
258
|
-
ctx.print(`Deep link bundle exported to ${targetPath}`);
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
if (mode === 'inspect') {
|
|
262
|
-
const targetPath = shellPaths.resolveWorkspacePath(pathArg!);
|
|
263
|
-
const bundle = JSON.parse(readFileSync(targetPath, 'utf-8')) as DeepLinkBundle;
|
|
264
|
-
ctx.print(inspectDeepLinkBundle(bundle));
|
|
265
|
-
return;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
ctx.print('Usage: /deeplink [review|open <surface> [target]|bundle export <path> --yes|bundle inspect <path>]');
|
|
269
|
-
},
|
|
270
|
-
});
|
|
271
|
-
}
|
|
@@ -1,110 +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 type { ProfileBundleEntry, ProfileSyncBundle } from '@/runtime/index.ts';
|
|
5
|
-
import { recordSettingsSyncEvent, recordSettingsSyncFailure } from '@/runtime/index.ts';
|
|
6
|
-
import { requireProfileManager, requireShellPaths } from './runtime-services.ts';
|
|
7
|
-
import { requireYesFlag, stripYesFlag } from './confirmation.ts';
|
|
8
|
-
|
|
9
|
-
function inspectProfileSyncBundle(bundle: ProfileSyncBundle): string {
|
|
10
|
-
return [
|
|
11
|
-
'Profile Sync Bundle Review',
|
|
12
|
-
` exportedAt: ${new Date(bundle.exportedAt).toISOString()}`,
|
|
13
|
-
` profiles: ${bundle.profiles.length}`,
|
|
14
|
-
` activeProfile: ${bundle.activeProfile ?? '(none)'}`,
|
|
15
|
-
].join('\n');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function registerProfileSyncRuntimeCommands(registry: CommandRegistry): void {
|
|
19
|
-
registry.register({
|
|
20
|
-
name: 'profilesync',
|
|
21
|
-
description: 'Export, import, and inspect profile sync bundles',
|
|
22
|
-
usage: '[list|export <path> --yes|inspect <path>|import <path> [prefix] --yes]',
|
|
23
|
-
handler(args, ctx) {
|
|
24
|
-
const parsed = stripYesFlag(args);
|
|
25
|
-
const commandArgs = [...parsed.rest];
|
|
26
|
-
const shellPaths = requireShellPaths(ctx);
|
|
27
|
-
const controlPlaneConfigDir = ctx.platform.configManager.getControlPlaneConfigDir();
|
|
28
|
-
const sub = commandArgs[0] ?? 'list';
|
|
29
|
-
const pm = requireProfileManager(ctx);
|
|
30
|
-
if (sub === 'list') {
|
|
31
|
-
const profiles = pm.list();
|
|
32
|
-
ctx.print(
|
|
33
|
-
profiles.length > 0
|
|
34
|
-
? ['Profile Sync', ...profiles.map((profile) => ` ${profile.name} ${new Date(profile.timestamp).toISOString()}`)].join('\n')
|
|
35
|
-
: 'Profile Sync\n No profiles saved yet.',
|
|
36
|
-
);
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const pathArg = commandArgs[1];
|
|
41
|
-
if (!pathArg) {
|
|
42
|
-
ctx.print(`Usage: /profilesync ${sub} <path>${sub === 'import' ? ' [prefix]' : ''}${sub === 'export' || sub === 'import' ? ' --yes' : ''}`);
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
const targetPath = shellPaths.resolveWorkspacePath(pathArg);
|
|
46
|
-
|
|
47
|
-
if (sub === 'export') {
|
|
48
|
-
if (!parsed.yes) {
|
|
49
|
-
requireYesFlag(ctx, `export profile sync bundle to ${pathArg}`, '/profilesync export <path> --yes');
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
const profiles = pm.list().map((profile) => {
|
|
53
|
-
const loaded = pm.load(profile.name);
|
|
54
|
-
return {
|
|
55
|
-
name: profile.name,
|
|
56
|
-
timestamp: loaded.timestamp,
|
|
57
|
-
data: loaded.data,
|
|
58
|
-
} satisfies ProfileBundleEntry;
|
|
59
|
-
});
|
|
60
|
-
const bundle: ProfileSyncBundle = {
|
|
61
|
-
version: 1,
|
|
62
|
-
exportedAt: Date.now(),
|
|
63
|
-
profiles,
|
|
64
|
-
};
|
|
65
|
-
mkdirSync(dirname(targetPath), { recursive: true });
|
|
66
|
-
writeFileSync(targetPath, JSON.stringify(bundle, null, 2) + '\n', 'utf-8');
|
|
67
|
-
recordSettingsSyncEvent({
|
|
68
|
-
surface: 'profiles',
|
|
69
|
-
direction: 'export',
|
|
70
|
-
path: targetPath,
|
|
71
|
-
timestamp: Date.now(),
|
|
72
|
-
detail: `${profiles.length} profiles exported`,
|
|
73
|
-
}, controlPlaneConfigDir);
|
|
74
|
-
ctx.print(`Profile sync bundle exported to ${targetPath}`);
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (sub === 'inspect') {
|
|
79
|
-
const bundle = JSON.parse(readFileSync(targetPath, 'utf-8')) as ProfileSyncBundle;
|
|
80
|
-
ctx.print(inspectProfileSyncBundle(bundle));
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (sub === 'import') {
|
|
85
|
-
if (!parsed.yes) {
|
|
86
|
-
requireYesFlag(ctx, `import profile sync bundle from ${pathArg}`, '/profilesync import <path> [prefix] --yes');
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
const bundle = JSON.parse(readFileSync(targetPath, 'utf-8')) as ProfileSyncBundle;
|
|
90
|
-
const prefix = commandArgs[2]?.trim() ?? '';
|
|
91
|
-
for (const entry of bundle.profiles) {
|
|
92
|
-
const name = prefix ? `${prefix}-${entry.name}` : entry.name;
|
|
93
|
-
pm.save(name, entry.data);
|
|
94
|
-
}
|
|
95
|
-
recordSettingsSyncEvent({
|
|
96
|
-
surface: 'profiles',
|
|
97
|
-
direction: 'import',
|
|
98
|
-
path: targetPath,
|
|
99
|
-
timestamp: Date.now(),
|
|
100
|
-
detail: `${bundle.profiles.length} profiles imported${prefix ? ` with prefix ${prefix}` : ''}`,
|
|
101
|
-
}, controlPlaneConfigDir);
|
|
102
|
-
ctx.print(`Profile sync bundle imported from ${targetPath}`);
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
recordSettingsSyncFailure('profiles', `unsupported subcommand: ${sub}`, controlPlaneConfigDir);
|
|
107
|
-
ctx.print('Usage: /profilesync [list|export <path> --yes|inspect <path>|import <path> [prefix] --yes]');
|
|
108
|
-
},
|
|
109
|
-
});
|
|
110
|
-
}
|