@pellux/goodvibes-agent 0.1.10 → 0.1.12
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 +32 -0
- package/package.json +1 -1
- package/src/cli/agent-knowledge-command.ts +30 -3
- package/src/cli/help.ts +2 -2
- 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 +65 -26
- package/src/input/commands/health-runtime.ts +1 -1
- package/src/input/commands/hooks-runtime.ts +50 -19
- 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/keybindings.ts +1 -1
- package/src/input/mcp-workspace.ts +25 -49
- package/src/input/onboarding/onboarding-wizard-cloudflare-step.ts +8 -8
- package/src/input/onboarding/onboarding-wizard-cloudflare.ts +1 -6
- package/src/input/profile-picker-modal.ts +13 -31
- package/src/input/session-picker-modal.ts +4 -30
- package/src/input/settings-modal-subscriptions.ts +3 -3
- 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/profile-picker-modal.ts +3 -11
- package/src/renderer/session-picker-modal.ts +2 -10
- package/src/verification/live-verifier.ts +100 -68
- package/src/version.ts +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { KnowledgeService } from '@pellux/goodvibes-sdk/platform/knowledge';
|
|
2
2
|
import type { CommandContext, SlashCommand } from '../command-registry.ts';
|
|
3
|
+
import { requireYesFlag, stripYesFlag } from './confirmation.ts';
|
|
3
4
|
|
|
4
5
|
const KNOWLEDGE_REVIEW_ACTIONS = ['accept', 'reject', 'resolve', 'reopen', 'edit', 'forget'] as const;
|
|
5
6
|
|
|
@@ -158,7 +159,7 @@ export const knowledgeCommand: SlashCommand = {
|
|
|
158
159
|
aliases: ['know', 'kb'],
|
|
159
160
|
description: 'Agent Knowledge/Wiki: isolated Agent-owned sources, graph, review queue, and compact prompt packets.',
|
|
160
161
|
usage: '<subcommand> [args]',
|
|
161
|
-
argsHint: 'status|ask|ingest-url|import-bookmarks|
|
|
162
|
+
argsHint: 'status|ask|ingest-url --yes|import-bookmarks --yes|list|search|get|queue|review-issue --yes',
|
|
162
163
|
handler: async (args: string[], context: CommandContext): Promise<void> => {
|
|
163
164
|
const knowledge = requireAgentKnowledgeApi(context);
|
|
164
165
|
if (!knowledge) {
|
|
@@ -169,7 +170,8 @@ export const knowledgeCommand: SlashCommand = {
|
|
|
169
170
|
return;
|
|
170
171
|
}
|
|
171
172
|
const sub = (args[0] ?? 'status').toLowerCase();
|
|
172
|
-
const
|
|
173
|
+
const confirmation = stripYesFlag(args.slice(1));
|
|
174
|
+
const rest = [...confirmation.rest];
|
|
173
175
|
const disallowedScopeFlag = findDisallowedKnowledgeScopeFlag(rest);
|
|
174
176
|
if (disallowedScopeFlag) {
|
|
175
177
|
printScopeFlagRejection(context, disallowedScopeFlag);
|
|
@@ -219,7 +221,11 @@ export const knowledgeCommand: SlashCommand = {
|
|
|
219
221
|
case 'ingest-url': {
|
|
220
222
|
const [url] = positionalArgs(rest, ['--title', '--tags', '--folder']);
|
|
221
223
|
if (!url) {
|
|
222
|
-
context.print('[knowledge] Usage: /knowledge ingest-url <url> [--title <title>] [--tags <a,b>] [--folder <path>]');
|
|
224
|
+
context.print('[knowledge] Usage: /knowledge ingest-url <url> [--title <title>] [--tags <a,b>] [--folder <path>] --yes');
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
if (!confirmation.yes) {
|
|
228
|
+
requireYesFlag(context, `ingest URL into Agent Knowledge ${url}`, '/knowledge ingest-url <url> [--title <title>] [--tags <a,b>] [--folder <path>] --yes');
|
|
223
229
|
return;
|
|
224
230
|
}
|
|
225
231
|
const result = await knowledge.ingest.url({
|
|
@@ -240,7 +246,11 @@ export const knowledgeCommand: SlashCommand = {
|
|
|
240
246
|
case 'import-bookmarks': {
|
|
241
247
|
const [path] = positionalArgs(rest);
|
|
242
248
|
if (!path) {
|
|
243
|
-
context.print('[knowledge] Usage: /knowledge import-bookmarks <path>');
|
|
249
|
+
context.print('[knowledge] Usage: /knowledge import-bookmarks <path> --yes');
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
if (!confirmation.yes) {
|
|
253
|
+
requireYesFlag(context, `import bookmark file into Agent Knowledge ${path}`, '/knowledge import-bookmarks <path> --yes');
|
|
244
254
|
return;
|
|
245
255
|
}
|
|
246
256
|
const result = await knowledge.ingest.bookmarksFile({ path, sessionId: context.session.runtime.sessionId });
|
|
@@ -254,7 +264,11 @@ export const knowledgeCommand: SlashCommand = {
|
|
|
254
264
|
case 'import-urls': {
|
|
255
265
|
const [path] = positionalArgs(rest);
|
|
256
266
|
if (!path) {
|
|
257
|
-
context.print('[knowledge] Usage: /knowledge import-urls <path>');
|
|
267
|
+
context.print('[knowledge] Usage: /knowledge import-urls <path> --yes');
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
if (!confirmation.yes) {
|
|
271
|
+
requireYesFlag(context, `import URL list into Agent Knowledge ${path}`, '/knowledge import-urls <path> --yes');
|
|
258
272
|
return;
|
|
259
273
|
}
|
|
260
274
|
const result = await knowledge.ingest.urlsFile({ path, sessionId: context.session.runtime.sessionId });
|
|
@@ -401,7 +415,11 @@ export const knowledgeCommand: SlashCommand = {
|
|
|
401
415
|
const [issueId, actionValue] = positionalArgs(rest, ['--reviewer', '--value']);
|
|
402
416
|
const action = actionValue?.toLowerCase();
|
|
403
417
|
if (!issueId || !action || !KNOWLEDGE_REVIEW_ACTIONS.includes(action as KnowledgeReviewAction)) {
|
|
404
|
-
context.print('[knowledge] Usage: /knowledge review-issue <issueId> <accept|reject|resolve|reopen|edit|forget> [--reviewer <name>] [--value <json-object>]');
|
|
418
|
+
context.print('[knowledge] Usage: /knowledge review-issue <issueId> <accept|reject|resolve|reopen|edit|forget> [--reviewer <name>] [--value <json-object>] --yes');
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
if (!confirmation.yes) {
|
|
422
|
+
requireYesFlag(context, `review Agent Knowledge issue ${issueId}`, '/knowledge review-issue <issueId> <action> [--reviewer <name>] [--value <json-object>] --yes');
|
|
405
423
|
return;
|
|
406
424
|
}
|
|
407
425
|
const value = readJsonObjectFlag(rest, '--value');
|
|
@@ -513,6 +531,10 @@ export const knowledgeCommand: SlashCommand = {
|
|
|
513
531
|
}
|
|
514
532
|
|
|
515
533
|
case 'reindex': {
|
|
534
|
+
if (!confirmation.yes) {
|
|
535
|
+
requireYesFlag(context, 'reindex Agent Knowledge', '/knowledge reindex --yes');
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
516
538
|
const result = await knowledge.status.reindex();
|
|
517
539
|
context.print([
|
|
518
540
|
'[knowledge] Reindex complete',
|
|
@@ -525,6 +547,10 @@ export const knowledgeCommand: SlashCommand = {
|
|
|
525
547
|
}
|
|
526
548
|
|
|
527
549
|
case 'consolidate': {
|
|
550
|
+
if (!confirmation.yes) {
|
|
551
|
+
requireYesFlag(context, 'run Agent Knowledge consolidation', '/knowledge consolidate [light|deep] --yes');
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
528
554
|
const mode = (positionalArgs(rest)[0] ?? 'light').toLowerCase();
|
|
529
555
|
const jobId = mode === 'deep' ? 'knowledge-deep-consolidation' : 'knowledge-light-consolidation';
|
|
530
556
|
const run = await knowledge.jobs.run(jobId, { mode: 'inline' });
|
|
@@ -537,22 +563,22 @@ export const knowledgeCommand: SlashCommand = {
|
|
|
537
563
|
'Usage: /knowledge <subcommand>',
|
|
538
564
|
' status',
|
|
539
565
|
' ask <query> [--limit <n>] [--mode <concise|standard|detailed>]',
|
|
540
|
-
' ingest-url <url> [--title <title>] [--tags <a,b>] [--folder <path>]',
|
|
541
|
-
' import-bookmarks <path>',
|
|
542
|
-
' import-urls <path>',
|
|
566
|
+
' ingest-url <url> [--title <title>] [--tags <a,b>] [--folder <path>] --yes',
|
|
567
|
+
' import-bookmarks <path> --yes',
|
|
568
|
+
' import-urls <path> --yes',
|
|
543
569
|
' list [--kind <sources|nodes|issues>] [--limit <n>]',
|
|
544
570
|
' search <query> [--limit <n>]',
|
|
545
571
|
' get <id>',
|
|
546
572
|
' queue [limit]',
|
|
547
|
-
' review-issue <issueId> <accept|reject|resolve|reopen|edit|forget> [--reviewer <name>] [--value <json-object>]',
|
|
573
|
+
' review-issue <issueId> <accept|reject|resolve|reopen|edit|forget> [--reviewer <name>] [--value <json-object>] --yes',
|
|
548
574
|
' candidates [limit]',
|
|
549
575
|
' reports [limit]',
|
|
550
576
|
' schedules',
|
|
551
577
|
' lint',
|
|
552
578
|
' packet <task...> [--scope <path> ...]',
|
|
553
579
|
' explain <task...> [--scope <path> ...]',
|
|
554
|
-
' reindex',
|
|
555
|
-
' consolidate [light|deep]',
|
|
580
|
+
' reindex --yes',
|
|
581
|
+
' consolidate [light|deep] --yes',
|
|
556
582
|
].join('\n'));
|
|
557
583
|
}
|
|
558
584
|
},
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import type { CommandContext, CommandRegistry } from '../command-registry.ts';
|
|
2
2
|
import { openCommandPanel, requireLocalUserAuthManager } from './runtime-services.ts';
|
|
3
3
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
4
|
+
import { requireYesFlag, stripYesFlag } from './confirmation.ts';
|
|
4
5
|
|
|
5
6
|
function formatRoles(roles: readonly string[]): string {
|
|
6
7
|
return roles.length > 0 ? roles.join(', ') : '(none)';
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
export function handleLocalAuthCommand(args: string[], ctx: CommandContext): void {
|
|
10
|
-
const
|
|
11
|
+
const parsed = stripYesFlag(args);
|
|
12
|
+
const commandArgs = [...parsed.rest];
|
|
13
|
+
const sub = (commandArgs[0] ?? 'review').toLowerCase();
|
|
11
14
|
const auth = requireLocalUserAuthManager(ctx);
|
|
12
15
|
if (sub === 'panel' || sub === 'open') {
|
|
13
16
|
openCommandPanel(ctx, 'local-auth');
|
|
@@ -15,11 +18,15 @@ export function handleLocalAuthCommand(args: string[], ctx: CommandContext): voi
|
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
if (sub === 'add-user') {
|
|
18
|
-
const username =
|
|
19
|
-
const password =
|
|
20
|
-
const roles =
|
|
21
|
+
const username = commandArgs[1];
|
|
22
|
+
const password = commandArgs[2];
|
|
23
|
+
const roles = commandArgs[3]?.split(',').map((value) => value.trim()).filter(Boolean) ?? ['admin'];
|
|
21
24
|
if (!username || !password) {
|
|
22
|
-
ctx.print('Usage: /auth local add-user <username> <password> [roles]');
|
|
25
|
+
ctx.print('Usage: /auth local add-user <username> <password> [roles] --yes');
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (!parsed.yes) {
|
|
29
|
+
requireYesFlag(ctx, `add local auth user ${username}`, '/auth local add-user <username> <password> [roles] --yes');
|
|
23
30
|
return;
|
|
24
31
|
}
|
|
25
32
|
try {
|
|
@@ -32,9 +39,13 @@ export function handleLocalAuthCommand(args: string[], ctx: CommandContext): voi
|
|
|
32
39
|
}
|
|
33
40
|
|
|
34
41
|
if (sub === 'delete-user') {
|
|
35
|
-
const username =
|
|
42
|
+
const username = commandArgs[1];
|
|
36
43
|
if (!username) {
|
|
37
|
-
ctx.print('Usage: /auth local delete-user <username>');
|
|
44
|
+
ctx.print('Usage: /auth local delete-user <username> --yes');
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (!parsed.yes) {
|
|
48
|
+
requireYesFlag(ctx, `delete local auth user ${username}`, '/auth local delete-user <username> --yes');
|
|
38
49
|
return;
|
|
39
50
|
}
|
|
40
51
|
try {
|
|
@@ -47,10 +58,14 @@ export function handleLocalAuthCommand(args: string[], ctx: CommandContext): voi
|
|
|
47
58
|
}
|
|
48
59
|
|
|
49
60
|
if (sub === 'rotate-password') {
|
|
50
|
-
const username =
|
|
51
|
-
const password =
|
|
61
|
+
const username = commandArgs[1];
|
|
62
|
+
const password = commandArgs[2];
|
|
52
63
|
if (!username || !password) {
|
|
53
|
-
ctx.print('Usage: /auth local rotate-password <username> <password>');
|
|
64
|
+
ctx.print('Usage: /auth local rotate-password <username> <password> --yes');
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (!parsed.yes) {
|
|
68
|
+
requireYesFlag(ctx, `rotate password for local auth user ${username}`, '/auth local rotate-password <username> <password> --yes');
|
|
54
69
|
return;
|
|
55
70
|
}
|
|
56
71
|
try {
|
|
@@ -63,9 +78,13 @@ export function handleLocalAuthCommand(args: string[], ctx: CommandContext): voi
|
|
|
63
78
|
}
|
|
64
79
|
|
|
65
80
|
if (sub === 'revoke-session') {
|
|
66
|
-
const token =
|
|
81
|
+
const token = commandArgs[1];
|
|
67
82
|
if (!token) {
|
|
68
|
-
ctx.print('Usage: /auth local revoke-session <token-or-fingerprint>');
|
|
83
|
+
ctx.print('Usage: /auth local revoke-session <token-or-fingerprint> --yes');
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (!parsed.yes) {
|
|
87
|
+
requireYesFlag(ctx, 'revoke local auth session', '/auth local revoke-session <token-or-fingerprint> --yes');
|
|
69
88
|
return;
|
|
70
89
|
}
|
|
71
90
|
ctx.print(auth.revokeSession(token) ? `Revoked session ${token.slice(0, 12)}…` : `Unknown session token or fingerprint: ${token}`);
|
|
@@ -73,6 +92,10 @@ export function handleLocalAuthCommand(args: string[], ctx: CommandContext): voi
|
|
|
73
92
|
}
|
|
74
93
|
|
|
75
94
|
if (sub === 'clear-bootstrap-file') {
|
|
95
|
+
if (!parsed.yes) {
|
|
96
|
+
requireYesFlag(ctx, 'clear the local auth bootstrap credential file', '/auth local clear-bootstrap-file --yes');
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
76
99
|
ctx.print(auth.clearBootstrapCredentialFile()
|
|
77
100
|
? 'Removed bootstrap credential file.'
|
|
78
101
|
: 'No bootstrap credential file was present.');
|
|
@@ -97,7 +120,7 @@ export function registerLocalAuthRuntimeCommands(registry: CommandRegistry): voi
|
|
|
97
120
|
name: 'local-auth',
|
|
98
121
|
aliases: ['auth-local'],
|
|
99
122
|
description: 'Inspect and manage local daemon/listener auth users, sessions, and bootstrap credentials',
|
|
100
|
-
usage: '[review|panel|add-user <username> <password> [roles]|delete-user <username
|
|
123
|
+
usage: '[review|panel|add-user <username> <password> [roles] --yes|delete-user <username> --yes|rotate-password <username> <password> --yes|revoke-session <token-or-fingerprint> --yes|clear-bootstrap-file --yes]',
|
|
101
124
|
handler(args, ctx) {
|
|
102
125
|
handleLocalAuthCommand(args, ctx);
|
|
103
126
|
},
|
|
@@ -7,6 +7,7 @@ import type { CustomProviderConfig } from '@pellux/goodvibes-sdk/platform/provid
|
|
|
7
7
|
import { requireProviderApi, requireShellPaths } from './runtime-services.ts';
|
|
8
8
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
9
9
|
import { GOODVIBES_AGENT_SURFACE_ROOT } from '../../config/surface.ts';
|
|
10
|
+
import { requireYesFlag, stripYesFlag } from './confirmation.ts';
|
|
10
11
|
|
|
11
12
|
function isValidProviderName(name: string): boolean {
|
|
12
13
|
return /^[a-zA-Z0-9_-]+$/.test(name);
|
|
@@ -17,17 +18,23 @@ export function registerLocalProviderRuntimeCommands(registry: CommandRegistry):
|
|
|
17
18
|
name: 'provider',
|
|
18
19
|
aliases: ['p'],
|
|
19
20
|
description: 'Switch provider or manage custom providers (add/remove)',
|
|
20
|
-
usage: '[add <name> <baseURL> [apiKey] | remove <name> | <provider-name>]',
|
|
21
|
-
argsHint: '[add|remove
|
|
21
|
+
usage: '[add <name> <baseURL> [apiKey] --yes | remove <name> --yes | <provider-name>]',
|
|
22
|
+
argsHint: '[name|add --yes|remove --yes]',
|
|
22
23
|
async handler(args, ctx) {
|
|
24
|
+
const parsed = stripYesFlag(args);
|
|
25
|
+
const commandArgs = [...parsed.rest];
|
|
23
26
|
const shellPaths = requireShellPaths(ctx);
|
|
24
|
-
if (
|
|
25
|
-
const addArgs =
|
|
27
|
+
if (commandArgs[0] === 'add') {
|
|
28
|
+
const addArgs = commandArgs.slice(1);
|
|
26
29
|
if (addArgs.length < 2) {
|
|
27
|
-
ctx.print('Usage: /provider add <name> <baseURL> [apiKey]\nExample: /provider add my-server http://192.168.0.85:8001/v1');
|
|
30
|
+
ctx.print('Usage: /provider add <name> <baseURL> [apiKey] --yes\nExample: /provider add my-server http://192.168.0.85:8001/v1 --yes');
|
|
28
31
|
return;
|
|
29
32
|
}
|
|
30
33
|
const [name, baseURL, apiKey] = addArgs;
|
|
34
|
+
if (!parsed.yes) {
|
|
35
|
+
requireYesFlag(ctx, `add custom provider ${name}`, '/provider add <name> <baseURL> [apiKey] --yes');
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
31
38
|
if (!isValidProviderName(name)) {
|
|
32
39
|
ctx.print('Error: Provider name must contain only letters, numbers, hyphens, and underscores.');
|
|
33
40
|
return;
|
|
@@ -42,7 +49,7 @@ export function registerLocalProviderRuntimeCommands(registry: CommandRegistry):
|
|
|
42
49
|
const providersDir = shellPaths.resolveUserPath(GOODVIBES_AGENT_SURFACE_ROOT, 'providers');
|
|
43
50
|
const providerFile = join(providersDir, `${name}.json`);
|
|
44
51
|
if (existsSync(providerFile)) {
|
|
45
|
-
ctx.print(`Error: Provider '${name}' already exists at ${providerFile}\nRemove it first with: /provider remove ${name}`);
|
|
52
|
+
ctx.print(`Error: Provider '${name}' already exists at ${providerFile}\nRemove it first with: /provider remove ${name} --yes`);
|
|
46
53
|
return;
|
|
47
54
|
}
|
|
48
55
|
|
|
@@ -111,10 +118,14 @@ export function registerLocalProviderRuntimeCommands(registry: CommandRegistry):
|
|
|
111
118
|
return;
|
|
112
119
|
}
|
|
113
120
|
|
|
114
|
-
if (
|
|
115
|
-
const name =
|
|
121
|
+
if (commandArgs[0] === 'remove' || commandArgs[0] === 'rm') {
|
|
122
|
+
const name = commandArgs[1];
|
|
116
123
|
if (!name) {
|
|
117
|
-
ctx.print('Usage: /provider remove <name>');
|
|
124
|
+
ctx.print('Usage: /provider remove <name> --yes');
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (!parsed.yes) {
|
|
128
|
+
requireYesFlag(ctx, `remove custom provider ${name}`, '/provider remove <name> --yes');
|
|
118
129
|
return;
|
|
119
130
|
}
|
|
120
131
|
if (!isValidProviderName(name)) {
|
|
@@ -135,7 +146,7 @@ export function registerLocalProviderRuntimeCommands(registry: CommandRegistry):
|
|
|
135
146
|
return;
|
|
136
147
|
}
|
|
137
148
|
|
|
138
|
-
if (
|
|
149
|
+
if (commandArgs.length === 0) {
|
|
139
150
|
if (ctx.openProviderPicker) {
|
|
140
151
|
ctx.openProviderPicker();
|
|
141
152
|
return;
|
|
@@ -145,7 +156,7 @@ export function registerLocalProviderRuntimeCommands(registry: CommandRegistry):
|
|
|
145
156
|
return;
|
|
146
157
|
}
|
|
147
158
|
|
|
148
|
-
const providerName =
|
|
159
|
+
const providerName = commandArgs[0];
|
|
149
160
|
const providerApi = requireProviderApi(ctx);
|
|
150
161
|
const selectable = await providerApi.listModels({
|
|
151
162
|
providerId: providerName,
|
|
@@ -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
|
}
|