@pellux/goodvibes-agent 0.1.9 → 0.1.11
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 +41 -0
- package/README.md +1 -1
- package/docs/getting-started.md +1 -1
- package/docs/release-and-publishing.md +2 -2
- package/package.json +4 -1
- package/src/cli/agent-knowledge-command.ts +46 -20
- package/src/cli/help.ts +15 -2
- package/src/cli/management-commands.ts +3 -3
- package/src/cli/management.ts +7 -1
- package/src/cli/parser.ts +3 -0
- package/src/cli/service-posture.ts +6 -6
- package/src/cli/status.ts +9 -9
- package/src/cli/surface-command.ts +3 -3
- package/src/cli/types.ts +2 -0
- package/src/input/commands/cloudflare-runtime.ts +20 -5
- package/src/input/commands/confirmation.ts +24 -0
- package/src/input/commands/discovery-runtime.ts +16 -7
- package/src/input/commands/eval.ts +27 -14
- package/src/input/commands/experience-runtime.ts +66 -27
- package/src/input/commands/health-runtime.ts +1 -1
- package/src/input/commands/hooks-runtime.ts +79 -20
- package/src/input/commands/incident-runtime.ts +17 -6
- package/src/input/commands/integration-runtime.ts +93 -50
- package/src/input/commands/knowledge.ts +38 -12
- package/src/input/commands/local-auth-runtime.ts +36 -13
- package/src/input/commands/local-provider-runtime.ts +22 -11
- package/src/input/commands/local-runtime.ts +21 -11
- package/src/input/commands/local-setup.ts +35 -16
- package/src/input/commands/managed-runtime.ts +51 -20
- package/src/input/commands/marketplace-runtime.ts +31 -16
- package/src/input/commands/mcp-runtime.ts +65 -34
- package/src/input/commands/memory-product-runtime.ts +72 -35
- package/src/input/commands/memory.ts +9 -9
- package/src/input/commands/notify-runtime.ts +27 -8
- package/src/input/commands/operator-runtime.ts +85 -17
- package/src/input/commands/planning-runtime.ts +14 -2
- package/src/input/commands/platform-access-runtime.ts +88 -45
- package/src/input/commands/platform-services-runtime.ts +51 -25
- package/src/input/commands/product-runtime.ts +54 -27
- package/src/input/commands/profile-sync-runtime.ts +17 -6
- package/src/input/commands/recall-bundle.ts +38 -17
- package/src/input/commands/recall-query.ts +15 -4
- package/src/input/commands/recall-review.ts +9 -3
- package/src/input/commands/remote-runtime-setup.ts +45 -18
- package/src/input/commands/remote-runtime.ts +25 -9
- package/src/input/commands/replay-runtime.ts +9 -2
- package/src/input/commands/services-runtime.ts +21 -10
- package/src/input/commands/session-content.ts +53 -51
- package/src/input/commands/session-workflow.ts +10 -4
- package/src/input/commands/session.ts +1 -1
- package/src/input/commands/settings-sync-runtime.ts +40 -17
- package/src/input/commands/share-runtime.ts +12 -4
- package/src/input/commands/shell-core.ts +3 -3
- package/src/input/commands/subscription-runtime.ts +35 -20
- package/src/input/commands/teleport-runtime.ts +16 -5
- package/src/input/commands/work-plan-runtime.ts +23 -12
- package/src/input/handler-content-actions.ts +11 -62
- package/src/input/handler-interactions.ts +1 -1
- package/src/input/handler-onboarding-cloudflare.ts +48 -117
- package/src/input/handler.ts +1 -0
- package/src/input/keybindings.ts +1 -1
- package/src/input/mcp-workspace.ts +25 -49
- package/src/input/onboarding/onboarding-runtime-status.ts +8 -8
- package/src/input/onboarding/onboarding-wizard-apply.ts +13 -53
- package/src/input/onboarding/onboarding-wizard-cloudflare-step.ts +12 -12
- package/src/input/onboarding/onboarding-wizard-cloudflare.ts +2 -7
- package/src/input/onboarding/onboarding-wizard-constants.ts +7 -7
- package/src/input/onboarding/onboarding-wizard-external-surface-extra-specs.ts +4 -4
- package/src/input/onboarding/onboarding-wizard-steps.ts +13 -13
- package/src/input/profile-picker-modal.ts +13 -31
- package/src/input/session-picker-modal.ts +4 -30
- package/src/input/settings-modal-agent-policy.ts +18 -0
- package/src/input/settings-modal-subscriptions.ts +3 -3
- package/src/input/settings-modal-types.ts +17 -0
- package/src/input/settings-modal.ts +30 -29
- package/src/main.ts +3 -26
- package/src/panels/incident-review-panel.ts +1 -1
- package/src/panels/local-auth-panel.ts +4 -4
- package/src/panels/provider-account-snapshot.ts +1 -1
- package/src/panels/provider-health-domains.ts +2 -2
- package/src/panels/settings-sync-panel.ts +2 -2
- package/src/panels/subscription-panel.ts +7 -7
- package/src/renderer/block-actions.ts +1 -1
- package/src/renderer/help-overlay.ts +2 -2
- package/src/renderer/mcp-workspace.ts +12 -12
- package/src/renderer/process-modal.ts +17 -8
- package/src/renderer/profile-picker-modal.ts +3 -11
- package/src/renderer/session-picker-modal.ts +2 -10
- package/src/renderer/settings-modal.ts +12 -8
- package/src/renderer/ui-factory.ts +4 -32
- package/src/runtime/bootstrap-shell.ts +0 -13
- package/src/runtime/bootstrap.ts +0 -10
- package/src/runtime/onboarding/derivation.ts +6 -6
- package/src/verification/live-verifier.ts +148 -13
- package/src/version.ts +10 -3
- package/src/input/commands/quit-shared.ts +0 -162
- package/src/renderer/git-status.ts +0 -89
|
@@ -8,6 +8,7 @@ import { resolveAndValidatePath } from '@pellux/goodvibes-sdk/platform/utils';
|
|
|
8
8
|
import { BUILTIN_SECRET_PROVIDER_SOURCES, describeSecretRef, isSecretRefInput, resolveSecretRef } from '@pellux/goodvibes-sdk/platform/config';
|
|
9
9
|
import { openCommandPanel, requireBookmarkManager, requireProviderApi, requireSecretsManager } from './runtime-services.ts';
|
|
10
10
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
11
|
+
import { requireYesFlag, stripYesFlag } from './confirmation.ts';
|
|
11
12
|
|
|
12
13
|
function isGoodVibesSecretRefInput(value: string): boolean {
|
|
13
14
|
const normalized = value.trim();
|
|
@@ -143,16 +144,17 @@ export function registerLocalRuntimeCommands(registry: CommandRegistry): void {
|
|
|
143
144
|
registry.register({
|
|
144
145
|
name: 'secrets',
|
|
145
146
|
description: 'Manage hierarchy-aware secrets, external secret refs, and secure/plaintext storage policy controls',
|
|
146
|
-
usage: 'set <KEY> <value> [--user|--project] [--secure|--plaintext] | link <KEY> <secret-ref> [--user|--project] [--secure|--plaintext] | get <KEY> | test <secret-ref> | providers | list | delete <KEY> [--user|--project] [--secure|--plaintext]',
|
|
147
|
+
usage: 'set <KEY> <value> [--user|--project] [--secure|--plaintext] --yes | link <KEY> <secret-ref> [--user|--project] [--secure|--plaintext] --yes | get <KEY> | test <secret-ref> | providers | list | delete <KEY> [--user|--project] [--secure|--plaintext] --yes',
|
|
147
148
|
argsHint: '<set|link|get|test|providers|list|delete> [KEY]',
|
|
148
149
|
async handler(args, ctx) {
|
|
149
150
|
const mgr = requireSecretsManager(ctx);
|
|
150
|
-
const
|
|
151
|
+
const parsed = stripYesFlag(args);
|
|
152
|
+
const [sub, ...rest] = parsed.rest;
|
|
151
153
|
if (!sub || sub === 'list') {
|
|
152
154
|
const records = await mgr.listDetailed();
|
|
153
155
|
const storedRecords = records.filter((record) => record.source !== 'env');
|
|
154
156
|
ctx.print(storedRecords.length === 0
|
|
155
|
-
? '[secrets] No secrets stored. Use: /secrets set <KEY> <value>'
|
|
157
|
+
? '[secrets] No secrets stored. Use: /secrets set <KEY> <value> --yes'
|
|
156
158
|
: [
|
|
157
159
|
'[secrets] Stored keys:',
|
|
158
160
|
...storedRecords.map((record) => ` ${record.key} (${record.source}${record.refSource ? `, ref:${record.refSource}` : ''}${record.overriddenByEnv ? ', env override' : ''})`),
|
|
@@ -165,11 +167,11 @@ export function registerLocalRuntimeCommands(registry: CommandRegistry): void {
|
|
|
165
167
|
...BUILTIN_SECRET_PROVIDER_SOURCES.map((source) => ` ${source}`),
|
|
166
168
|
'',
|
|
167
169
|
'Examples:',
|
|
168
|
-
' /secrets link OPENAI_API_KEY goodvibes://secrets/env/OPENAI_API_KEY',
|
|
169
|
-
' /secrets link SLACK_BOT_TOKEN goodvibes://secrets/bitwarden?item=GoodVibes%20Slack&field=password&sessionEnv=BW_SESSION',
|
|
170
|
-
' /secrets link SLACK_BOT_TOKEN goodvibes://secrets/vaultwarden?item=GoodVibes%20Slack&field=password&server=https%3A%2F%2Fvault.example.test',
|
|
171
|
-
' /secrets link STRIPE_TOKEN goodvibes://secrets/bws/00000000-0000-0000-0000-000000000000?field=value&accessTokenEnv=BWS_ACCESS_TOKEN',
|
|
172
|
-
' /secrets link OPENAI_API_KEY goodvibes://secrets/1password?vault=Private&item=GoodVibes%20OpenAI&field=API%20Key',
|
|
170
|
+
' /secrets link OPENAI_API_KEY goodvibes://secrets/env/OPENAI_API_KEY --yes',
|
|
171
|
+
' /secrets link SLACK_BOT_TOKEN goodvibes://secrets/bitwarden?item=GoodVibes%20Slack&field=password&sessionEnv=BW_SESSION --yes',
|
|
172
|
+
' /secrets link SLACK_BOT_TOKEN goodvibes://secrets/vaultwarden?item=GoodVibes%20Slack&field=password&server=https%3A%2F%2Fvault.example.test --yes',
|
|
173
|
+
' /secrets link STRIPE_TOKEN goodvibes://secrets/bws/00000000-0000-0000-0000-000000000000?field=value&accessTokenEnv=BWS_ACCESS_TOKEN --yes',
|
|
174
|
+
' /secrets link OPENAI_API_KEY goodvibes://secrets/1password?vault=Private&item=GoodVibes%20OpenAI&field=API%20Key --yes',
|
|
173
175
|
].join('\n'));
|
|
174
176
|
return;
|
|
175
177
|
}
|
|
@@ -196,10 +198,14 @@ export function registerLocalRuntimeCommands(registry: CommandRegistry): void {
|
|
|
196
198
|
const valueParts = rest.filter((value) => !value.startsWith('--'));
|
|
197
199
|
const [key, ...rawValueParts] = valueParts;
|
|
198
200
|
if (!key || valueParts.length === 0) {
|
|
199
|
-
ctx.print(`[secrets] Usage: /secrets ${sub} <KEY> <${sub === 'link' ? 'secret-ref' : 'value'}> [--user|--project] [--secure|--plaintext]`);
|
|
201
|
+
ctx.print(`[secrets] Usage: /secrets ${sub} <KEY> <${sub === 'link' ? 'secret-ref' : 'value'}> [--user|--project] [--secure|--plaintext] --yes`);
|
|
200
202
|
return;
|
|
201
203
|
}
|
|
202
204
|
const value = rawValueParts.join(' ');
|
|
205
|
+
if (!parsed.yes) {
|
|
206
|
+
requireYesFlag(ctx, `${sub === 'link' ? 'link secret reference for' : 'store secret value for'} ${key}`, `/secrets ${sub} <KEY> <${sub === 'link' ? 'secret-ref' : 'value'}> [--user|--project] [--secure|--plaintext] --yes`);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
203
209
|
if (sub === 'link' && !isGoodVibesSecretRefInput(value)) {
|
|
204
210
|
ctx.print('[secrets] Invalid secret reference. Use /secrets providers for examples.');
|
|
205
211
|
return;
|
|
@@ -230,7 +236,11 @@ export function registerLocalRuntimeCommands(registry: CommandRegistry): void {
|
|
|
230
236
|
const flags = new Set(rest.filter((value) => value.startsWith('--')));
|
|
231
237
|
const [key] = rest.filter((value) => !value.startsWith('--'));
|
|
232
238
|
if (!key) {
|
|
233
|
-
ctx.print('[secrets] Usage: /secrets delete <KEY> [--user|--project] [--secure|--plaintext]');
|
|
239
|
+
ctx.print('[secrets] Usage: /secrets delete <KEY> [--user|--project] [--secure|--plaintext] --yes');
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (!parsed.yes) {
|
|
243
|
+
requireYesFlag(ctx, `delete secret ${key}`, '/secrets delete <KEY> [--user|--project] [--secure|--plaintext] --yes');
|
|
234
244
|
return;
|
|
235
245
|
}
|
|
236
246
|
await mgr.delete(key, {
|
|
@@ -240,7 +250,7 @@ export function registerLocalRuntimeCommands(registry: CommandRegistry): void {
|
|
|
240
250
|
ctx.print(`[secrets] Deleted: ${key}`);
|
|
241
251
|
return;
|
|
242
252
|
}
|
|
243
|
-
ctx.print('[secrets] Usage: /secrets set <KEY> <value> [--user|--project] [--secure|--plaintext] | link <KEY> <secret-ref> [--user|--project] [--secure|--plaintext] | get <KEY> | test <secret-ref> | providers | list | delete <KEY> [--user|--project] [--secure|--plaintext]');
|
|
253
|
+
ctx.print('[secrets] Usage: /secrets set <KEY> <value> [--user|--project] [--secure|--plaintext] --yes | link <KEY> <secret-ref> [--user|--project] [--secure|--plaintext] --yes | get <KEY> | test <secret-ref> | providers | list | delete <KEY> [--user|--project] [--secure|--plaintext] --yes');
|
|
244
254
|
},
|
|
245
255
|
});
|
|
246
256
|
|
|
@@ -16,6 +16,7 @@ import { buildSetupReviewSnapshot, exportSetupSupportBundle } from './local-setu
|
|
|
16
16
|
import { openOnboardingWizard, requirePanelManager, requireShellPaths } from './runtime-services.ts';
|
|
17
17
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
18
18
|
import { GOODVIBES_AGENT_SURFACE_ROOT } from '../../config/surface.ts';
|
|
19
|
+
import { requireYesFlag, stripYesFlag } from './confirmation.ts';
|
|
19
20
|
|
|
20
21
|
type SetupSnapshot = Awaited<ReturnType<typeof buildSetupReviewSnapshot>>;
|
|
21
22
|
|
|
@@ -24,9 +25,11 @@ export function registerLocalSetupCommands(registry: CommandRegistry): void {
|
|
|
24
25
|
name: 'setup',
|
|
25
26
|
aliases: ['startup'],
|
|
26
27
|
description: 'Launch the onboarding wizard and review Agent startup readiness',
|
|
27
|
-
usage: '[review|doctor|services|hooks|remote|sandbox|onboarding|support-bundle <dir
|
|
28
|
+
usage: '[review|doctor|services|hooks|remote|sandbox|onboarding|support-bundle <dir> --yes|export <path> --yes|transfer <export|inspect|import> <path> [--yes]|link <surface> [target]|open-link <uri>]',
|
|
28
29
|
async handler(args, ctx) {
|
|
29
|
-
const
|
|
30
|
+
const parsed = stripYesFlag(args);
|
|
31
|
+
const commandArgs = [...parsed.rest];
|
|
32
|
+
const sub = commandArgs[0] ?? 'review';
|
|
30
33
|
let shellPaths: ReturnType<typeof requireShellPaths> | null = null;
|
|
31
34
|
let snapshotPromise: Promise<SetupSnapshot> | null = null;
|
|
32
35
|
const getShellPaths = () => (shellPaths ??= requireShellPaths(ctx));
|
|
@@ -134,12 +137,16 @@ export function registerLocalSetupCommands(registry: CommandRegistry): void {
|
|
|
134
137
|
}
|
|
135
138
|
|
|
136
139
|
if (sub === 'support-bundle') {
|
|
137
|
-
const
|
|
138
|
-
const dirArg = args[1];
|
|
140
|
+
const dirArg = commandArgs[1];
|
|
139
141
|
if (!dirArg) {
|
|
140
|
-
ctx.print('Usage: /setup support-bundle <dir>');
|
|
142
|
+
ctx.print('Usage: /setup support-bundle <dir> --yes');
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (!parsed.yes) {
|
|
146
|
+
requireYesFlag(ctx, `export setup support bundle to ${dirArg}`, '/setup support-bundle <dir> --yes');
|
|
141
147
|
return;
|
|
142
148
|
}
|
|
149
|
+
const snapshot = await getSnapshot();
|
|
143
150
|
const targetDir = exportSetupSupportBundle(dirArg, snapshot, ctx);
|
|
144
151
|
writeFileSync(join(targetDir, 'remote-summary.json'), JSON.stringify({
|
|
145
152
|
runners: ctx.ops.remoteRuntime?.listContracts() ?? [],
|
|
@@ -155,12 +162,16 @@ export function registerLocalSetupCommands(registry: CommandRegistry): void {
|
|
|
155
162
|
}
|
|
156
163
|
|
|
157
164
|
if (sub === 'export') {
|
|
158
|
-
const
|
|
159
|
-
const pathArg = args[1];
|
|
165
|
+
const pathArg = commandArgs[1];
|
|
160
166
|
if (!pathArg) {
|
|
161
|
-
ctx.print('Usage: /setup export <path>');
|
|
167
|
+
ctx.print('Usage: /setup export <path> --yes');
|
|
162
168
|
return;
|
|
163
169
|
}
|
|
170
|
+
if (!parsed.yes) {
|
|
171
|
+
requireYesFlag(ctx, `export startup review to ${pathArg}`, '/setup export <path> --yes');
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
const snapshot = await getSnapshot();
|
|
164
175
|
const targetPath = getShellPaths().resolveWorkspacePath(pathArg);
|
|
165
176
|
mkdirSync(dirname(targetPath), { recursive: true });
|
|
166
177
|
writeFileSync(targetPath, JSON.stringify(snapshot, null, 2) + '\n', 'utf-8');
|
|
@@ -169,14 +180,18 @@ export function registerLocalSetupCommands(registry: CommandRegistry): void {
|
|
|
169
180
|
}
|
|
170
181
|
|
|
171
182
|
if (sub === 'transfer') {
|
|
172
|
-
const mode =
|
|
173
|
-
const pathArg =
|
|
183
|
+
const mode = commandArgs[1]?.toLowerCase();
|
|
184
|
+
const pathArg = commandArgs[2];
|
|
174
185
|
if (!mode || !pathArg) {
|
|
175
|
-
ctx.print('Usage: /setup transfer <export|inspect|import> <path>');
|
|
186
|
+
ctx.print('Usage: /setup transfer <export|inspect|import> <path> [--yes]');
|
|
176
187
|
return;
|
|
177
188
|
}
|
|
178
189
|
const targetPath = getShellPaths().resolveWorkspacePath(pathArg);
|
|
179
190
|
if (mode === 'export') {
|
|
191
|
+
if (!parsed.yes) {
|
|
192
|
+
requireYesFlag(ctx, `export setup transfer bundle to ${pathArg}`, '/setup transfer export <path> --yes');
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
180
195
|
const snapshot = await getSnapshot();
|
|
181
196
|
const bundle = buildSetupTransferBundle(ctx, snapshot);
|
|
182
197
|
ctx.print(`Exported setup transfer bundle to ${exportSetupTransferBundle(ctx, pathArg, bundle)}`);
|
|
@@ -192,6 +207,10 @@ export function registerLocalSetupCommands(registry: CommandRegistry): void {
|
|
|
192
207
|
return;
|
|
193
208
|
}
|
|
194
209
|
if (mode === 'import') {
|
|
210
|
+
if (!parsed.yes) {
|
|
211
|
+
requireYesFlag(ctx, `import setup transfer bundle from ${pathArg}`, '/setup transfer import <path> --yes');
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
195
214
|
try {
|
|
196
215
|
const bundle = JSON.parse(readFileSync(targetPath, 'utf-8')) as SetupTransferBundle;
|
|
197
216
|
for (const entry of CONFIG_SCHEMA) {
|
|
@@ -220,13 +239,13 @@ export function registerLocalSetupCommands(registry: CommandRegistry): void {
|
|
|
220
239
|
}
|
|
221
240
|
return;
|
|
222
241
|
}
|
|
223
|
-
ctx.print('Usage: /setup transfer <export|inspect|import> <path>');
|
|
242
|
+
ctx.print('Usage: /setup transfer <export|inspect|import> <path> [--yes]');
|
|
224
243
|
return;
|
|
225
244
|
}
|
|
226
245
|
|
|
227
246
|
if (sub === 'link') {
|
|
228
|
-
const surface =
|
|
229
|
-
const target =
|
|
247
|
+
const surface = commandArgs[1];
|
|
248
|
+
const target = commandArgs[2];
|
|
230
249
|
if (!surface) {
|
|
231
250
|
ctx.print('Usage: /setup link <cockpit|security|remote|knowledge|incident|hooks|orchestration|tasks> [target]');
|
|
232
251
|
return;
|
|
@@ -236,7 +255,7 @@ export function registerLocalSetupCommands(registry: CommandRegistry): void {
|
|
|
236
255
|
}
|
|
237
256
|
|
|
238
257
|
if (sub === 'open-link') {
|
|
239
|
-
const link =
|
|
258
|
+
const link = commandArgs[1];
|
|
240
259
|
if (!link) {
|
|
241
260
|
ctx.print('Usage: /setup open-link <goodvibes://...>');
|
|
242
261
|
return;
|
|
@@ -276,7 +295,7 @@ export function registerLocalSetupCommands(registry: CommandRegistry): void {
|
|
|
276
295
|
return;
|
|
277
296
|
}
|
|
278
297
|
|
|
279
|
-
ctx.print('Usage: /setup [review|doctor|services|hooks|remote|sandbox|onboarding|support-bundle <dir
|
|
298
|
+
ctx.print('Usage: /setup [review|doctor|services|hooks|remote|sandbox|onboarding|support-bundle <dir> --yes|export <path> --yes|transfer <export|inspect|import> <path> [--yes]|link <surface> [target]|open-link <uri>]');
|
|
280
299
|
},
|
|
281
300
|
});
|
|
282
301
|
}
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
} from '@/runtime/index.ts';
|
|
21
21
|
import { requireProfileManager, requireShellPaths } from './runtime-services.ts';
|
|
22
22
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
23
|
+
import { requireYesFlag, stripYesFlag } from './confirmation.ts';
|
|
23
24
|
|
|
24
25
|
function buildConfigSnapshot(
|
|
25
26
|
manager: { get: (key: ConfigKey) => unknown },
|
|
@@ -39,11 +40,13 @@ export function registerManagedRuntimeCommands(registry: CommandRegistry): void
|
|
|
39
40
|
registry.register({
|
|
40
41
|
name: 'managed',
|
|
41
42
|
description: 'Export, inspect, and apply managed settings bundles',
|
|
42
|
-
usage: '[review|staged|rollback-history|export <profile> <path
|
|
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]',
|
|
43
44
|
handler(args, ctx) {
|
|
45
|
+
const parsed = stripYesFlag(args);
|
|
46
|
+
const commandArgs = [...parsed.rest];
|
|
44
47
|
const shellPaths = requireShellPaths(ctx);
|
|
45
48
|
const controlPlaneConfigDir = ctx.platform.configManager.getControlPlaneConfigDir();
|
|
46
|
-
const sub =
|
|
49
|
+
const sub = commandArgs[0] ?? 'review';
|
|
47
50
|
const pm = requireProfileManager(ctx);
|
|
48
51
|
if (sub === 'review') {
|
|
49
52
|
const profiles = pm.list();
|
|
@@ -78,11 +81,15 @@ export function registerManagedRuntimeCommands(registry: CommandRegistry): void
|
|
|
78
81
|
}
|
|
79
82
|
|
|
80
83
|
if (sub === 'lock') {
|
|
81
|
-
const key =
|
|
82
|
-
const source =
|
|
83
|
-
const reason =
|
|
84
|
+
const key = commandArgs[1] as ConfigKey | undefined;
|
|
85
|
+
const source = commandArgs[2];
|
|
86
|
+
const reason = commandArgs.slice(3).join(' ').trim();
|
|
84
87
|
if (!key || !source || !reason) {
|
|
85
|
-
ctx.print('Usage: /managed lock <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');
|
|
86
93
|
return;
|
|
87
94
|
}
|
|
88
95
|
setManagedSettingLock(key, source, reason, controlPlaneConfigDir);
|
|
@@ -91,9 +98,13 @@ export function registerManagedRuntimeCommands(registry: CommandRegistry): void
|
|
|
91
98
|
}
|
|
92
99
|
|
|
93
100
|
if (sub === 'unlock') {
|
|
94
|
-
const key =
|
|
101
|
+
const key = commandArgs[1] as ConfigKey | undefined;
|
|
95
102
|
if (!key) {
|
|
96
|
-
ctx.print('Usage: /managed unlock <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');
|
|
97
108
|
return;
|
|
98
109
|
}
|
|
99
110
|
ctx.print(clearManagedSettingLock(key, controlPlaneConfigDir) ? `Managed lock cleared for ${key}.` : `No managed lock found for ${key}.`);
|
|
@@ -101,10 +112,14 @@ export function registerManagedRuntimeCommands(registry: CommandRegistry): void
|
|
|
101
112
|
}
|
|
102
113
|
|
|
103
114
|
if (sub === 'export') {
|
|
104
|
-
const profileName =
|
|
105
|
-
const pathArg =
|
|
115
|
+
const profileName = commandArgs[1];
|
|
116
|
+
const pathArg = commandArgs[2];
|
|
106
117
|
if (!profileName || !pathArg) {
|
|
107
|
-
ctx.print('Usage: /managed export <profile> <path>');
|
|
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');
|
|
108
123
|
return;
|
|
109
124
|
}
|
|
110
125
|
const loaded = pm.load(profileName);
|
|
@@ -129,12 +144,16 @@ export function registerManagedRuntimeCommands(registry: CommandRegistry): void
|
|
|
129
144
|
}
|
|
130
145
|
|
|
131
146
|
if (sub === 'apply-staged') {
|
|
132
|
-
const requestedKeys =
|
|
133
|
-
const invalidKeys =
|
|
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));
|
|
134
149
|
if (invalidKeys.length > 0) {
|
|
135
150
|
ctx.print(`Unknown config key(s): ${invalidKeys.join(', ')}`);
|
|
136
151
|
return;
|
|
137
152
|
}
|
|
153
|
+
if (!parsed.yes) {
|
|
154
|
+
requireYesFlag(ctx, 'apply staged managed settings', '/managed apply-staged [key ...] --yes');
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
138
157
|
try {
|
|
139
158
|
const result = applyStagedManagedBundle(ctx.platform.configManager, requestedKeys);
|
|
140
159
|
ctx.session.runtime.model = String(ctx.platform.configManager.get('provider.model'));
|
|
@@ -149,9 +168,13 @@ export function registerManagedRuntimeCommands(registry: CommandRegistry): void
|
|
|
149
168
|
}
|
|
150
169
|
|
|
151
170
|
if (sub === 'rollback') {
|
|
152
|
-
const token =
|
|
171
|
+
const token = commandArgs[1];
|
|
153
172
|
if (!token) {
|
|
154
|
-
ctx.print('Usage: /managed rollback <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');
|
|
155
178
|
return;
|
|
156
179
|
}
|
|
157
180
|
try {
|
|
@@ -167,9 +190,9 @@ export function registerManagedRuntimeCommands(registry: CommandRegistry): void
|
|
|
167
190
|
return;
|
|
168
191
|
}
|
|
169
192
|
|
|
170
|
-
const pathArg =
|
|
193
|
+
const pathArg = commandArgs[1];
|
|
171
194
|
if (!pathArg) {
|
|
172
|
-
ctx.print(`Usage: /managed ${sub} <path
|
|
195
|
+
ctx.print(`Usage: /managed ${sub} <path>${sub === 'stage' || sub === 'apply' ? ' --yes' : ''}`);
|
|
173
196
|
return;
|
|
174
197
|
}
|
|
175
198
|
const sourcePath = shellPaths.resolveWorkspacePath(pathArg);
|
|
@@ -181,18 +204,26 @@ export function registerManagedRuntimeCommands(registry: CommandRegistry): void
|
|
|
181
204
|
}
|
|
182
205
|
|
|
183
206
|
if (sub === 'stage') {
|
|
207
|
+
if (!parsed.yes) {
|
|
208
|
+
requireYesFlag(ctx, `stage managed settings bundle from ${pathArg}`, '/managed stage <path> --yes');
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
184
211
|
const stage = stageManagedSettingsBundle(ctx.platform.configManager, bundle, sourcePath);
|
|
185
212
|
ctx.print(`Managed settings bundle staged from ${sourcePath} (${stage.changeCount} changes, risk=${stage.risk}).`);
|
|
186
213
|
return;
|
|
187
214
|
}
|
|
188
215
|
|
|
189
216
|
if (sub === 'apply') {
|
|
190
|
-
const requestedKeys =
|
|
191
|
-
const invalidKeys =
|
|
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));
|
|
192
219
|
if (invalidKeys.length > 0) {
|
|
193
220
|
ctx.print(`Unknown config key(s): ${invalidKeys.join(', ')}`);
|
|
194
221
|
return;
|
|
195
222
|
}
|
|
223
|
+
if (!parsed.yes) {
|
|
224
|
+
requireYesFlag(ctx, `apply managed settings bundle from ${pathArg}`, '/managed apply <path> [key ...] --yes');
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
196
227
|
stageManagedSettingsBundle(ctx.platform.configManager, bundle, sourcePath);
|
|
197
228
|
const result = applyStagedManagedBundle(ctx.platform.configManager, requestedKeys);
|
|
198
229
|
ctx.session.runtime.model = String(ctx.platform.configManager.get('provider.model'));
|
|
@@ -203,7 +234,7 @@ export function registerManagedRuntimeCommands(registry: CommandRegistry): void
|
|
|
203
234
|
}
|
|
204
235
|
|
|
205
236
|
recordSettingsSyncFailure('managed', `unsupported subcommand: ${sub}`, controlPlaneConfigDir);
|
|
206
|
-
ctx.print('Usage: /managed [review|staged|rollback-history|export <profile> <path
|
|
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]');
|
|
207
238
|
},
|
|
208
239
|
});
|
|
209
240
|
}
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
type EcosystemEntryKind,
|
|
21
21
|
} from '@/runtime/index.ts';
|
|
22
22
|
import { openCommandPanel, requireEcosystemCatalogPaths, requireReadModels, requireShellPaths } from './runtime-services.ts';
|
|
23
|
+
import { requireYesFlag, stripYesFlag } from './confirmation.ts';
|
|
23
24
|
|
|
24
25
|
function resolveMarketplaceEntry(
|
|
25
26
|
kind: EcosystemEntryKind,
|
|
@@ -39,11 +40,13 @@ export function registerMarketplaceRuntimeCommands(registry: CommandRegistry): v
|
|
|
39
40
|
name: 'marketplace',
|
|
40
41
|
aliases: ['catalog'],
|
|
41
42
|
description: 'Browse the unified plugin and skill marketplace',
|
|
42
|
-
usage: '[open|overview|recommend|browse [query]|review <plugin|skill|hook-pack|policy-pack> <id>|provenance <plugin|skill|hook-pack|policy-pack> <id>|install-hint <plugin|skill|hook-pack|policy-pack> <id>|install <plugin|skill|hook-pack|policy-pack> <id> [project|user]|update <plugin|skill|hook-pack|policy-pack> <id> [project|user]|rollback <plugin|skill|hook-pack|policy-pack> <id> [project|user] [backupId]|history <plugin|skill|hook-pack|policy-pack> <id> [project|user]|uninstall <plugin|skill|hook-pack|policy-pack> <id> [project|user]|receipt <plugin|skill|hook-pack|policy-pack> <id> [project|user]|bundle export <path> [project|user]|bundle inspect <path>|bundle import <path> [project|user]|installed]',
|
|
43
|
+
usage: '[open|overview|recommend|browse [query]|review <plugin|skill|hook-pack|policy-pack> <id>|provenance <plugin|skill|hook-pack|policy-pack> <id>|install-hint <plugin|skill|hook-pack|policy-pack> <id>|install <plugin|skill|hook-pack|policy-pack> <id> [project|user] --yes|update <plugin|skill|hook-pack|policy-pack> <id> [project|user] --yes|rollback <plugin|skill|hook-pack|policy-pack> <id> [project|user] [backupId] --yes|history <plugin|skill|hook-pack|policy-pack> <id> [project|user]|uninstall <plugin|skill|hook-pack|policy-pack> <id> [project|user] --yes|receipt <plugin|skill|hook-pack|policy-pack> <id> [project|user]|bundle export <path> [project|user] --yes|bundle inspect <path>|bundle import <path> [project|user] --yes|installed]',
|
|
43
44
|
handler(args, ctx) {
|
|
45
|
+
const parsed = stripYesFlag(args);
|
|
46
|
+
const commandArgs = [...parsed.rest];
|
|
44
47
|
const shellPaths = requireShellPaths(ctx);
|
|
45
48
|
const ecosystemPaths = requireEcosystemCatalogPaths(ctx);
|
|
46
|
-
const sub =
|
|
49
|
+
const sub = commandArgs[0] ?? 'open';
|
|
47
50
|
if (sub === 'open' || sub === 'panel') {
|
|
48
51
|
openCommandPanel(ctx, 'marketplace');
|
|
49
52
|
return;
|
|
@@ -71,7 +74,7 @@ export function registerMarketplaceRuntimeCommands(registry: CommandRegistry): v
|
|
|
71
74
|
return;
|
|
72
75
|
}
|
|
73
76
|
if (sub === 'browse') {
|
|
74
|
-
const query =
|
|
77
|
+
const query = commandArgs.slice(1).join(' ');
|
|
75
78
|
const pluginEntries = query ? searchEcosystemCatalog('plugin', query, ecosystemPaths) : loadEcosystemCatalog('plugin', ecosystemPaths);
|
|
76
79
|
const skillEntries = query ? searchEcosystemCatalog('skill', query, ecosystemPaths) : loadEcosystemCatalog('skill', ecosystemPaths);
|
|
77
80
|
const hookPackEntries = query ? searchEcosystemCatalog('hook-pack', query, ecosystemPaths) : loadEcosystemCatalog('hook-pack', ecosystemPaths);
|
|
@@ -113,14 +116,18 @@ export function registerMarketplaceRuntimeCommands(registry: CommandRegistry): v
|
|
|
113
116
|
return;
|
|
114
117
|
}
|
|
115
118
|
if (sub === 'bundle') {
|
|
116
|
-
const mode =
|
|
117
|
-
const target =
|
|
118
|
-
const scope =
|
|
119
|
+
const mode = commandArgs[1];
|
|
120
|
+
const target = commandArgs[2];
|
|
121
|
+
const scope = commandArgs[3] === 'user' ? 'user' : 'project';
|
|
119
122
|
if ((mode === 'export' || mode === 'inspect' || mode === 'import') && !target) {
|
|
120
|
-
ctx.print(`Usage: /marketplace bundle ${mode} <path>${mode === 'export' || mode === 'import' ? ' [project|user]' : ''}`);
|
|
123
|
+
ctx.print(`Usage: /marketplace bundle ${mode} <path>${mode === 'export' || mode === 'import' ? ' [project|user] --yes' : ''}`);
|
|
121
124
|
return;
|
|
122
125
|
}
|
|
123
126
|
if (mode === 'export') {
|
|
127
|
+
if (!parsed.yes) {
|
|
128
|
+
requireYesFlag(ctx, `export marketplace bundle to ${target}`, '/marketplace bundle export <path> [project|user] --yes');
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
124
131
|
const bundle = exportEcosystemCatalogBundle(scope, ecosystemPaths);
|
|
125
132
|
const targetPath = shellPaths.resolveWorkspacePath(target!);
|
|
126
133
|
mkdirSync(dirname(targetPath), { recursive: true });
|
|
@@ -141,6 +148,10 @@ export function registerMarketplaceRuntimeCommands(registry: CommandRegistry): v
|
|
|
141
148
|
return;
|
|
142
149
|
}
|
|
143
150
|
if (mode === 'import') {
|
|
151
|
+
if (!parsed.yes) {
|
|
152
|
+
requireYesFlag(ctx, `import marketplace bundle from ${target}`, '/marketplace bundle import <path> [project|user] --yes');
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
144
155
|
const bundle = JSON.parse(readFileSync(shellPaths.resolveWorkspacePath(target!), 'utf-8')) as EcosystemCatalogBundle;
|
|
145
156
|
const result = importEcosystemCatalogBundle(bundle, { ...ecosystemPaths, scope });
|
|
146
157
|
ctx.print([
|
|
@@ -150,14 +161,14 @@ export function registerMarketplaceRuntimeCommands(registry: CommandRegistry): v
|
|
|
150
161
|
].join('\n'));
|
|
151
162
|
return;
|
|
152
163
|
}
|
|
153
|
-
ctx.print('Usage: /marketplace bundle <export|inspect|import> <path> [project|user]');
|
|
164
|
+
ctx.print('Usage: /marketplace bundle <export|inspect|import> <path> [project|user] --yes');
|
|
154
165
|
return;
|
|
155
166
|
}
|
|
156
167
|
|
|
157
|
-
const kind =
|
|
158
|
-
const entryId =
|
|
168
|
+
const kind = commandArgs[1] as EcosystemEntryKind | undefined;
|
|
169
|
+
const entryId = commandArgs[2];
|
|
159
170
|
if ((sub === 'review' || sub === 'provenance' || sub === 'install-hint' || sub === 'install' || sub === 'update' || sub === 'rollback' || sub === 'history' || sub === 'uninstall' || sub === 'receipt') && (!kind || !entryId || !['plugin', 'skill', 'hook-pack', 'policy-pack'].includes(kind))) {
|
|
160
|
-
ctx.print(`Usage: /marketplace ${sub} <plugin|skill|hook-pack|policy-pack> <id>${sub === 'install' || sub === 'update' || sub === 'rollback' || sub === 'history' || sub === 'uninstall' || sub === 'receipt' ? ' [project|user]' : ''}`);
|
|
171
|
+
ctx.print(`Usage: /marketplace ${sub} <plugin|skill|hook-pack|policy-pack> <id>${sub === 'install' || sub === 'update' || sub === 'rollback' || sub === 'history' || sub === 'uninstall' || sub === 'receipt' ? ' [project|user]' : ''}${sub === 'install' || sub === 'update' || sub === 'rollback' || sub === 'uninstall' ? ' --yes' : ''}`);
|
|
161
172
|
return;
|
|
162
173
|
}
|
|
163
174
|
if (sub === 'review') {
|
|
@@ -216,7 +227,7 @@ export function registerMarketplaceRuntimeCommands(registry: CommandRegistry): v
|
|
|
216
227
|
return;
|
|
217
228
|
}
|
|
218
229
|
if (sub === 'receipt') {
|
|
219
|
-
const scope =
|
|
230
|
+
const scope = commandArgs[3] === 'user' ? 'user' : 'project';
|
|
220
231
|
const result = inspectInstalledEcosystemEntry(kind!, entryId!, { ...ecosystemPaths, scope });
|
|
221
232
|
if (!result.ok) {
|
|
222
233
|
ctx.print(`Error: ${result.error}`);
|
|
@@ -236,7 +247,7 @@ export function registerMarketplaceRuntimeCommands(registry: CommandRegistry): v
|
|
|
236
247
|
return;
|
|
237
248
|
}
|
|
238
249
|
if (sub === 'history') {
|
|
239
|
-
const scope =
|
|
250
|
+
const scope = commandArgs[3] === 'user' ? 'user' : 'project';
|
|
240
251
|
const backups = listEcosystemInstallBackups(kind!, entryId!, { ...ecosystemPaths, scope });
|
|
241
252
|
ctx.print(backups.length > 0
|
|
242
253
|
? [
|
|
@@ -247,7 +258,11 @@ export function registerMarketplaceRuntimeCommands(registry: CommandRegistry): v
|
|
|
247
258
|
return;
|
|
248
259
|
}
|
|
249
260
|
if (sub === 'install' || sub === 'update' || sub === 'rollback' || sub === 'uninstall') {
|
|
250
|
-
const scope =
|
|
261
|
+
const scope = commandArgs[3] === 'user' ? 'user' : 'project';
|
|
262
|
+
if (!parsed.yes) {
|
|
263
|
+
requireYesFlag(ctx, `${sub} curated ${kind} ${entryId}`, `/marketplace ${sub} <plugin|skill|hook-pack|policy-pack> <id> [project|user]${sub === 'rollback' ? ' [backupId]' : ''} --yes`);
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
251
266
|
if (sub === 'install') {
|
|
252
267
|
const result = installEcosystemCatalogEntry(kind!, entryId!, { ...ecosystemPaths, scope });
|
|
253
268
|
if (!result.ok) {
|
|
@@ -267,7 +282,7 @@ export function registerMarketplaceRuntimeCommands(registry: CommandRegistry): v
|
|
|
267
282
|
return;
|
|
268
283
|
}
|
|
269
284
|
if (sub === 'rollback') {
|
|
270
|
-
const backupId =
|
|
285
|
+
const backupId = commandArgs[4];
|
|
271
286
|
const result = rollbackInstalledEcosystemEntry(kind!, entryId!, { ...ecosystemPaths, scope, backupId });
|
|
272
287
|
if (!result.ok) {
|
|
273
288
|
ctx.print(`Error: ${result.error}`);
|
|
@@ -284,7 +299,7 @@ export function registerMarketplaceRuntimeCommands(registry: CommandRegistry): v
|
|
|
284
299
|
ctx.print(`Uninstalled curated ${kind} ${entryId} from ${result.removedPath}`);
|
|
285
300
|
return;
|
|
286
301
|
}
|
|
287
|
-
ctx.print('Usage: /marketplace [open|overview|recommend|browse [query]|review <plugin|skill|hook-pack|policy-pack> <id>|provenance <plugin|skill|hook-pack|policy-pack> <id>|install-hint <plugin|skill|hook-pack|policy-pack> <id>|install <plugin|skill|hook-pack|policy-pack> <id> [project|user]|update <plugin|skill|hook-pack|policy-pack> <id> [project|user]|rollback <plugin|skill|hook-pack|policy-pack> <id> [project|user] [backupId]|history <plugin|skill|hook-pack|policy-pack> <id> [project|user]|uninstall <plugin|skill|hook-pack|policy-pack> <id> [project|user]|receipt <plugin|skill|hook-pack|policy-pack> <id> [project|user]|bundle export <path> [project|user]|bundle inspect <path>|bundle import <path> [project|user]|installed]');
|
|
302
|
+
ctx.print('Usage: /marketplace [open|overview|recommend|browse [query]|review <plugin|skill|hook-pack|policy-pack> <id>|provenance <plugin|skill|hook-pack|policy-pack> <id>|install-hint <plugin|skill|hook-pack|policy-pack> <id>|install <plugin|skill|hook-pack|policy-pack> <id> [project|user] --yes|update <plugin|skill|hook-pack|policy-pack> <id> [project|user] --yes|rollback <plugin|skill|hook-pack|policy-pack> <id> [project|user] [backupId] --yes|history <plugin|skill|hook-pack|policy-pack> <id> [project|user]|uninstall <plugin|skill|hook-pack|policy-pack> <id> [project|user] --yes|receipt <plugin|skill|hook-pack|policy-pack> <id> [project|user]|bundle export <path> [project|user] --yes|bundle inspect <path>|bundle import <path> [project|user] --yes|installed]');
|
|
288
303
|
},
|
|
289
304
|
});
|
|
290
305
|
}
|