@pellux/goodvibes-agent 0.1.117 → 1.0.1
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 +20 -4
- package/README.md +35 -14
- package/bin/goodvibes-agent.ts +16 -2
- package/dist/package/main.js +176093 -171065
- package/docs/README.md +11 -5
- package/docs/channels-remote-and-api.md +50 -0
- package/docs/connected-host.md +3 -3
- package/docs/getting-started.md +29 -15
- package/docs/knowledge-artifacts-and-multimodal.md +91 -0
- package/docs/project-planning.md +79 -0
- package/docs/providers-and-routing.md +46 -0
- package/docs/release-and-publishing.md +44 -9
- package/docs/tools-and-commands.md +123 -0
- package/docs/voice-and-live-tts.md +51 -0
- package/package.json +2 -5
- package/src/agent/channel-delivery.ts +201 -0
- package/src/agent/media-generation.ts +159 -0
- package/src/agent/memory-prompt.ts +0 -1
- package/src/agent/note-registry.ts +329 -0
- package/src/agent/operator-actions.ts +343 -0
- package/src/agent/persona-registry.ts +15 -14
- package/src/agent/record-labels.ts +107 -0
- package/src/agent/reminder-schedule-format.ts +33 -24
- package/src/agent/reminder-schedule.ts +26 -25
- package/src/agent/routine-registry.ts +13 -12
- package/src/agent/routine-schedule-args.ts +2 -1
- package/src/agent/routine-schedule-format.ts +77 -53
- package/src/agent/routine-schedule-promotion.ts +34 -32
- package/src/agent/routine-schedule-receipts.ts +28 -26
- package/src/agent/runtime-profile-starters.ts +2 -2
- package/src/agent/runtime-profile.ts +18 -17
- package/src/agent/skill-registry.ts +25 -24
- package/src/cli/agent-knowledge-args.ts +5 -1
- package/src/cli/agent-knowledge-command.ts +39 -33
- package/src/cli/agent-knowledge-format.ts +80 -67
- package/src/cli/agent-knowledge-methods.ts +1 -1
- package/src/cli/agent-knowledge-runtime.ts +32 -26
- package/src/cli/bundle-command.ts +13 -8
- package/src/cli/config-overrides.ts +37 -36
- package/src/cli/external-runtime.ts +10 -4
- package/src/cli/help.ts +29 -11
- package/src/cli/local-library-command.ts +159 -73
- package/src/cli/management-commands.ts +98 -62
- package/src/cli/management.ts +52 -26
- package/src/cli/memory-command.ts +66 -32
- package/src/cli/parser.ts +37 -24
- package/src/cli/profiles-command.ts +52 -35
- package/src/cli/provider-auth-routes.ts +2 -1
- package/src/cli/routines-command.ts +59 -39
- package/src/cli/service-posture.ts +6 -6
- package/src/cli/status.ts +46 -121
- package/src/core/conversation-message-snapshot.ts +131 -0
- package/src/input/agent-workspace-activation.ts +33 -7
- package/src/input/agent-workspace-basic-command-editor-submission.ts +7 -3
- package/src/input/agent-workspace-basic-command-editors.ts +30 -10
- package/src/input/agent-workspace-categories.ts +276 -64
- package/src/input/agent-workspace-channel-command-editor-submission.ts +34 -0
- package/src/input/agent-workspace-channel-command-editors.ts +23 -5
- package/src/input/agent-workspace-channels.ts +35 -2
- package/src/input/agent-workspace-command-editor.ts +18 -2
- package/src/input/agent-workspace-config-reader.ts +16 -0
- package/src/input/agent-workspace-delegation-editor-submission.ts +1 -1
- package/src/input/agent-workspace-editors.ts +140 -2
- package/src/input/agent-workspace-knowledge-query-editor.ts +1 -1
- package/src/input/agent-workspace-knowledge-url-editor.ts +4 -11
- package/src/input/agent-workspace-learned-behavior.ts +2 -2
- package/src/input/agent-workspace-library-command-editor-submission.ts +1 -1
- package/src/input/agent-workspace-library-command-editors.ts +2 -2
- package/src/input/agent-workspace-local-operations.ts +218 -0
- package/src/input/agent-workspace-local-selection.ts +75 -0
- package/src/input/agent-workspace-media-command-editor-submission.ts +62 -0
- package/src/input/agent-workspace-media-command-editors.ts +27 -0
- package/src/input/agent-workspace-memory-command-editors.ts +1 -1
- package/src/input/agent-workspace-memory-editor.ts +2 -2
- package/src/input/agent-workspace-navigation.ts +38 -2
- package/src/input/agent-workspace-notify-editor-submission.ts +16 -2
- package/src/input/agent-workspace-operations-command-editor-submission.ts +63 -1
- package/src/input/agent-workspace-operations-command-editors.ts +80 -3
- package/src/input/agent-workspace-panel-route.ts +43 -0
- package/src/input/agent-workspace-reminder-schedule-editor.ts +1 -1
- package/src/input/agent-workspace-routine-schedule-editor.ts +2 -2
- package/src/input/agent-workspace-search.ts +169 -0
- package/src/input/agent-workspace-setup.ts +22 -11
- package/src/input/agent-workspace-skill-bundle-command-editor-submission.ts +6 -6
- package/src/input/agent-workspace-skill-bundle-command-editors.ts +10 -10
- package/src/input/agent-workspace-snapshot.ts +112 -13
- package/src/input/agent-workspace-task-command-editors.ts +4 -4
- package/src/input/agent-workspace-token.ts +18 -2
- package/src/input/agent-workspace-types.ts +92 -4
- package/src/input/agent-workspace-voice-media.ts +3 -6
- package/src/input/agent-workspace-web-research-editor.ts +104 -0
- package/src/input/agent-workspace.ts +136 -208
- package/src/input/command-registry.ts +4 -1
- package/src/input/commands/agent-local-library-args.ts +52 -0
- package/src/input/commands/agent-runtime-profile-runtime.ts +45 -41
- package/src/input/commands/agent-skills-runtime.ts +87 -99
- package/src/input/commands/agent-workspace-runtime.ts +23 -7
- package/src/input/commands/brief-runtime.ts +25 -24
- package/src/input/commands/channels-runtime.ts +145 -31
- package/src/input/commands/delegation-runtime.ts +29 -23
- package/src/input/commands/experience-runtime.ts +6 -4
- package/src/input/commands/guidance-runtime.ts +4 -4
- package/src/input/commands/health-runtime.ts +140 -115
- package/src/input/commands/knowledge.ts +57 -56
- package/src/input/commands/local-provider-runtime.ts +36 -18
- package/src/input/commands/local-runtime.ts +138 -16
- package/src/input/commands/local-setup-review.ts +7 -7
- package/src/input/commands/mcp-runtime.ts +118 -50
- package/src/input/commands/notify-runtime.ts +38 -9
- package/src/input/commands/operator-actions-runtime.ts +138 -0
- package/src/input/commands/operator-runtime.ts +6 -17
- package/src/input/commands/personas-runtime.ts +49 -59
- package/src/input/commands/planning-runtime.ts +1 -1
- package/src/input/commands/platform-access-runtime.ts +29 -29
- package/src/input/commands/provider-accounts-runtime.ts +60 -39
- package/src/input/commands/qrcode-runtime.ts +45 -6
- package/src/input/commands/recall-bundle.ts +11 -11
- package/src/input/commands/recall-capture.ts +13 -11
- package/src/input/commands/recall-query.ts +29 -21
- package/src/input/commands/recall-review.ts +2 -1
- package/src/input/commands/routines-runtime.ts +63 -72
- package/src/input/commands/schedule-runtime.ts +33 -20
- package/src/input/commands/security-runtime.ts +4 -4
- package/src/input/commands/session-content.ts +80 -78
- package/src/input/commands/session-workflow.ts +132 -93
- package/src/input/commands/session.ts +3 -174
- package/src/input/commands/shell-core.ts +32 -17
- package/src/input/commands/subscription-runtime.ts +53 -179
- package/src/input/commands/tasks-runtime.ts +20 -20
- package/src/input/commands/work-plan-runtime.ts +33 -8
- package/src/input/commands.ts +2 -2
- package/src/input/feed-context-factory.ts +2 -1
- package/src/input/file-picker.ts +3 -2
- package/src/input/handler-command-route.ts +4 -7
- package/src/input/handler-content-actions.ts +89 -1
- package/src/input/handler-feed-routes.ts +19 -12
- package/src/input/handler-feed.ts +6 -3
- package/src/input/handler-interactions.ts +7 -5
- package/src/input/handler-modal-stack.ts +3 -3
- package/src/input/handler-onboarding.ts +24 -80
- package/src/input/handler-shortcuts.ts +15 -4
- package/src/input/handler.ts +47 -17
- package/src/input/input-history.ts +5 -6
- package/src/input/keybindings.ts +22 -11
- package/src/input/onboarding/onboarding-wizard-apply.ts +13 -0
- package/src/input/onboarding/onboarding-wizard-constants.ts +1 -0
- package/src/input/onboarding/onboarding-wizard-operator-steps.ts +147 -57
- package/src/input/onboarding/onboarding-wizard-steps.ts +46 -29
- package/src/input/onboarding/onboarding-wizard-types.ts +3 -1
- package/src/input/onboarding/onboarding-wizard.ts +68 -0
- package/src/input/profile-picker-modal.ts +31 -12
- package/src/input/session-picker-modal.ts +22 -4
- package/src/input/settings-modal-behavior.ts +0 -3
- package/src/input/settings-modal-subscriptions.ts +3 -3
- package/src/input/settings-modal-types.ts +2 -13
- package/src/input/settings-modal.ts +6 -52
- package/src/input/submission-intent.ts +0 -1
- package/src/input/submission-router.ts +3 -3
- package/src/main.ts +18 -8
- package/src/panels/approval-panel.ts +8 -8
- package/src/panels/automation-control-panel.ts +2 -2
- package/src/panels/base-panel.ts +1 -1
- package/src/panels/builtin/agent.ts +1 -1
- package/src/panels/builtin/operations.ts +1 -10
- package/src/panels/builtin/session.ts +9 -9
- package/src/panels/builtin/shared.ts +2 -2
- package/src/panels/cost-tracker-panel.ts +1 -1
- package/src/panels/docs-panel.ts +5 -3
- package/src/panels/index.ts +0 -1
- package/src/panels/knowledge-panel.ts +6 -5
- package/src/panels/memory-panel.ts +7 -6
- package/src/panels/panel-list-panel.ts +36 -80
- package/src/panels/project-planning-panel.ts +18 -11
- package/src/panels/provider-account-snapshot.ts +51 -25
- package/src/panels/provider-accounts-panel.ts +33 -18
- package/src/panels/provider-health-domains.ts +45 -4
- package/src/panels/provider-health-panel.ts +5 -4
- package/src/panels/qr-panel.ts +1 -1
- package/src/panels/schedule-panel.ts +9 -17
- package/src/panels/security-panel.ts +8 -8
- package/src/panels/session-browser-panel.ts +10 -10
- package/src/panels/subscription-panel.ts +3 -3
- package/src/panels/system-messages-panel.ts +1 -1
- package/src/panels/tasks-panel.ts +20 -13
- package/src/panels/tool-inspector-panel.ts +19 -12
- package/src/panels/work-plan-panel.ts +5 -5
- package/src/planning/project-planning-coordinator.ts +1 -1
- package/src/provider-auth-route-display.ts +9 -0
- package/src/renderer/agent-workspace-style.ts +34 -0
- package/src/renderer/agent-workspace.ts +228 -52
- package/src/renderer/autocomplete-overlay.ts +25 -6
- package/src/renderer/bookmark-modal.ts +19 -4
- package/src/renderer/buffer.ts +4 -2
- package/src/renderer/context-inspector.ts +50 -13
- package/src/renderer/diff.ts +1 -1
- package/src/renderer/file-picker-overlay.ts +19 -6
- package/src/renderer/help-overlay.ts +106 -33
- package/src/renderer/history-search-overlay.ts +19 -4
- package/src/renderer/live-tail-modal.ts +27 -5
- package/src/renderer/mcp-workspace.ts +164 -50
- package/src/renderer/model-picker-overlay.ts +58 -2
- package/src/renderer/model-workspace.ts +104 -20
- package/src/renderer/process-modal.ts +27 -6
- package/src/renderer/profile-picker-modal.ts +20 -4
- package/src/renderer/search-overlay.ts +25 -5
- package/src/renderer/selection-modal-overlay.ts +46 -14
- package/src/renderer/session-picker-modal.ts +18 -1
- package/src/renderer/settings-modal-helpers.ts +2 -40
- package/src/renderer/settings-modal.ts +83 -50
- package/src/renderer/tool-call.ts +20 -11
- package/src/runtime/agent-runtime-events.ts +6 -6
- package/src/runtime/bootstrap-command-context.ts +7 -1
- package/src/runtime/bootstrap-command-parts.ts +9 -7
- package/src/runtime/bootstrap-core.ts +19 -1
- package/src/runtime/bootstrap-hook-bridge.ts +7 -18
- package/src/runtime/bootstrap-shell.ts +4 -4
- package/src/runtime/bootstrap.ts +31 -22
- package/src/runtime/connected-host-auth.ts +7 -2
- package/src/runtime/diagnostics/panels/index.ts +2 -2
- package/src/runtime/diagnostics/panels/policy.ts +7 -7
- package/src/runtime/index.ts +2 -1
- package/src/runtime/onboarding/apply-file-helpers.ts +66 -0
- package/src/runtime/onboarding/apply.ts +80 -79
- package/src/runtime/onboarding/derivation.ts +5 -8
- package/src/runtime/onboarding/snapshot.ts +0 -15
- package/src/runtime/onboarding/types.ts +8 -19
- package/src/runtime/onboarding/verify.ts +32 -10
- package/src/runtime/operator-token-cleanup.ts +1 -1
- package/src/runtime/services.ts +91 -22
- package/src/runtime/store/selectors/index.ts +3 -3
- package/src/runtime/store/state.ts +1 -4
- package/src/runtime/surface-feature-flags.ts +41 -6
- package/src/runtime/ui-read-models.ts +3 -4
- package/src/runtime/ui-services.ts +6 -6
- package/src/shell/blocking-input.ts +2 -1
- package/src/shell/ui-openers.ts +3 -13
- package/src/tools/agent-analysis-registry-policy.ts +0 -1
- package/src/tools/agent-channel-send-tool.ts +133 -0
- package/src/tools/agent-context-policy.ts +1 -1
- package/src/tools/agent-knowledge-ingest-tool.ts +405 -0
- package/src/tools/agent-knowledge-tool.ts +170 -0
- package/src/tools/agent-local-registry-tool.ts +269 -69
- package/src/tools/agent-media-generate-tool.ts +133 -0
- package/src/tools/agent-notify-tool.ts +143 -0
- package/src/tools/agent-operator-action-tool.ts +137 -0
- package/src/tools/agent-operator-briefing-tool.ts +217 -0
- package/src/tools/agent-reminder-schedule-tool.ts +237 -0
- package/src/tools/agent-tool-policy-guard.ts +8 -8
- package/src/tools/agent-work-plan-tool.ts +256 -0
- package/src/version.ts +1 -1
- package/src/input/commands/policy-dispatch.ts +0 -339
- package/src/input/commands/policy.ts +0 -13
- package/src/panels/panel-picker.ts +0 -105
- package/src/panels/policy-panel.ts +0 -308
- package/src/renderer/panel-picker-overlay.ts +0 -202
|
@@ -1,38 +1,14 @@
|
|
|
1
1
|
import { discoverPersonas, type DiscoveredPersonaRecord } from '../../agent/persona-discovery.ts';
|
|
2
2
|
import { AgentPersonaRegistry, type AgentPersonaRecord } from '../../agent/persona-registry.ts';
|
|
3
|
+
import { formatAgentRecordOrigin, formatAgentRecordReviewState } from '../../agent/record-labels.ts';
|
|
3
4
|
import type { CommandContext, CommandRegistry } from '../command-registry.ts';
|
|
5
|
+
import { parseAgentLocalLibraryArgs, type ParsedAgentLocalLibraryArgs } from './agent-local-library-args.ts';
|
|
4
6
|
import { requireShellPaths } from './runtime-services.ts';
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
readonly rest: readonly string[];
|
|
8
|
-
readonly flags: ReadonlyMap<string, string>;
|
|
9
|
-
readonly yes: boolean;
|
|
10
|
-
}
|
|
8
|
+
const PERSONA_VALUE_FLAGS = ['name', 'description', 'body', 'tags', 'triggers'] as const;
|
|
11
9
|
|
|
12
|
-
function parsePersonaArgs(args: readonly string[]):
|
|
13
|
-
|
|
14
|
-
const rest: string[] = [];
|
|
15
|
-
let yes = false;
|
|
16
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
17
|
-
const token = args[index] ?? '';
|
|
18
|
-
if (token === '--yes') {
|
|
19
|
-
yes = true;
|
|
20
|
-
continue;
|
|
21
|
-
}
|
|
22
|
-
if (token.startsWith('--')) {
|
|
23
|
-
const key = token.slice(2);
|
|
24
|
-
const next = args[index + 1];
|
|
25
|
-
if (next !== undefined && !next.startsWith('--')) {
|
|
26
|
-
flags.set(key, next);
|
|
27
|
-
index += 1;
|
|
28
|
-
} else {
|
|
29
|
-
flags.set(key, 'true');
|
|
30
|
-
}
|
|
31
|
-
continue;
|
|
32
|
-
}
|
|
33
|
-
rest.push(token);
|
|
34
|
-
}
|
|
35
|
-
return { rest, flags, yes };
|
|
10
|
+
function parsePersonaArgs(args: readonly string[]): ParsedAgentLocalLibraryArgs {
|
|
11
|
+
return parseAgentLocalLibraryArgs(args, { valueFlags: PERSONA_VALUE_FLAGS });
|
|
36
12
|
}
|
|
37
13
|
|
|
38
14
|
function splitList(value: string | undefined): readonly string[] {
|
|
@@ -44,21 +20,33 @@ function registryFromContext(ctx: CommandContext): AgentPersonaRegistry {
|
|
|
44
20
|
return AgentPersonaRegistry.fromShellPaths(requireShellPaths(ctx));
|
|
45
21
|
}
|
|
46
22
|
|
|
23
|
+
function formatPersonaReceipt(title: string, persona: Pick<AgentPersonaRecord, 'id' | 'name'>, extra: readonly string[] = []): string {
|
|
24
|
+
const activeSuffix = extra.some((line) => line.includes('active yes') || line.includes('active: yes')) ? ' (active)' : '';
|
|
25
|
+
const normalizedExtra = extra.map((line) => line.replace(/^ active /, ' active: '));
|
|
26
|
+
return [
|
|
27
|
+
`${title} ${persona.id}: ${persona.name}${activeSuffix}`,
|
|
28
|
+
` id ${persona.id}`,
|
|
29
|
+
` name ${persona.name}`,
|
|
30
|
+
...normalizedExtra,
|
|
31
|
+
].join('\n');
|
|
32
|
+
}
|
|
33
|
+
|
|
47
34
|
function summarizePersona(persona: AgentPersonaRecord, activePersonaId: string | null): string {
|
|
48
35
|
const active = persona.id === activePersonaId ? 'active' : 'inactive';
|
|
49
|
-
const
|
|
50
|
-
|
|
36
|
+
const review = formatAgentRecordReviewState(persona.reviewState);
|
|
37
|
+
const tags = persona.tags.length > 0 ? ` tags ${persona.tags.join(', ')}` : '';
|
|
38
|
+
return ` ${persona.id} ${active} ${review} ${persona.name} - ${persona.description}${tags}`;
|
|
51
39
|
}
|
|
52
40
|
|
|
53
41
|
function renderList(title: string, registry: AgentPersonaRegistry, personas: readonly AgentPersonaRecord[]): string {
|
|
54
42
|
const snapshot = registry.snapshot();
|
|
55
43
|
if (personas.length === 0) {
|
|
56
|
-
return `${title}\n No local Agent personas yet. Create one with /personas create --name <name> --description <summary> --body <instructions>.`;
|
|
44
|
+
return `${title}\n No local Agent personas yet.\n No Agent-local personas yet. Create one with /personas create --name <name> --description <summary> --body <instructions>.`;
|
|
57
45
|
}
|
|
58
46
|
return [
|
|
59
47
|
`${title} (${personas.length})`,
|
|
60
|
-
` store
|
|
61
|
-
` active
|
|
48
|
+
` store ${snapshot.path}`,
|
|
49
|
+
` active ${snapshot.activePersona?.name ?? '(none)'}`,
|
|
62
50
|
...personas.map((persona) => summarizePersona(persona, snapshot.activePersonaId)),
|
|
63
51
|
].join('\n');
|
|
64
52
|
}
|
|
@@ -66,11 +54,10 @@ function renderList(title: string, registry: AgentPersonaRegistry, personas: rea
|
|
|
66
54
|
function renderPersona(persona: AgentPersonaRecord, activePersonaId: string | null): string {
|
|
67
55
|
return [
|
|
68
56
|
`Persona ${persona.name}`,
|
|
69
|
-
` id
|
|
57
|
+
` id ${persona.id}`,
|
|
70
58
|
` active: ${persona.id === activePersonaId ? 'yes' : 'no'}`,
|
|
71
|
-
` review: ${persona.reviewState}`,
|
|
72
|
-
`
|
|
73
|
-
` provenance: ${persona.provenance}`,
|
|
59
|
+
` review: ${formatAgentRecordReviewState(persona.reviewState)}`,
|
|
60
|
+
` origin: ${formatAgentRecordOrigin(persona.source, persona.provenance)}`,
|
|
74
61
|
` tags: ${persona.tags.join(', ') || '(none)'}`,
|
|
75
62
|
` triggers: ${persona.triggers.join(', ') || '(none)'}`,
|
|
76
63
|
` created: ${persona.createdAt}`,
|
|
@@ -85,7 +72,7 @@ function renderPersona(persona: AgentPersonaRecord, activePersonaId: string | nu
|
|
|
85
72
|
|
|
86
73
|
function summarizeDiscoveredPersona(persona: DiscoveredPersonaRecord): string {
|
|
87
74
|
const description = persona.description ? ` - ${persona.description}` : '';
|
|
88
|
-
return ` ${persona.name} ${persona.origin}${description}\n path
|
|
75
|
+
return ` ${persona.name} ${persona.origin}${description}\n path ${persona.path}`;
|
|
89
76
|
}
|
|
90
77
|
|
|
91
78
|
function renderDiscoveredPersonas(personas: readonly DiscoveredPersonaRecord[]): string {
|
|
@@ -131,18 +118,18 @@ async function importDiscoveredPersona(args: readonly string[], ctx: CommandCont
|
|
|
131
118
|
}
|
|
132
119
|
const discovered = findDiscoveredPersona(await discoverPersonas(requireShellPaths(ctx)), name);
|
|
133
120
|
if (!discovered) {
|
|
134
|
-
ctx.print(`Unknown discovered Agent persona
|
|
121
|
+
ctx.print(`Unknown discovered Agent persona ${name}\nRun /personas discover to inspect available persona files.`);
|
|
135
122
|
return;
|
|
136
123
|
}
|
|
137
124
|
if (!parsed.yes) {
|
|
138
125
|
ctx.print([
|
|
139
126
|
'Agent persona import preview',
|
|
140
|
-
` name
|
|
141
|
-
` origin
|
|
142
|
-
` path
|
|
143
|
-
` description
|
|
144
|
-
` body characters
|
|
145
|
-
' next
|
|
127
|
+
` name ${discovered.name}`,
|
|
128
|
+
` origin ${discovered.origin}`,
|
|
129
|
+
` path ${discovered.path}`,
|
|
130
|
+
` description ${discovered.description || '(none)'}`,
|
|
131
|
+
` body characters ${discovered.body.length}`,
|
|
132
|
+
' next rerun with --yes to import into the Agent-local persona registry',
|
|
146
133
|
].join('\n'));
|
|
147
134
|
return;
|
|
148
135
|
}
|
|
@@ -153,10 +140,10 @@ async function importDiscoveredPersona(args: readonly string[], ctx: CommandCont
|
|
|
153
140
|
tags: frontmatterList(discovered, 'tags'),
|
|
154
141
|
triggers: frontmatterList(discovered, 'triggers'),
|
|
155
142
|
source: 'imported',
|
|
156
|
-
provenance: `
|
|
143
|
+
provenance: `Imported file (${discovered.origin}): ${discovered.path}`,
|
|
157
144
|
});
|
|
158
145
|
if (parsed.flags.get('use') === 'true') personaRegistry.setActive(persona.id);
|
|
159
|
-
ctx.print(
|
|
146
|
+
ctx.print(formatPersonaReceipt('Imported Agent persona', persona, [` active ${parsed.flags.get('use') === 'true' ? 'yes' : 'no'}`]));
|
|
160
147
|
}
|
|
161
148
|
|
|
162
149
|
function requiredFlag(flags: ReadonlyMap<string, string>, key: string): string {
|
|
@@ -166,14 +153,17 @@ function requiredFlag(flags: ReadonlyMap<string, string>, key: string): string {
|
|
|
166
153
|
}
|
|
167
154
|
|
|
168
155
|
function printError(ctx: CommandContext, error: unknown): void {
|
|
169
|
-
ctx.print(
|
|
156
|
+
ctx.print([
|
|
157
|
+
'Error',
|
|
158
|
+
` message ${error instanceof Error ? error.message : String(error)}`,
|
|
159
|
+
].join('\n'));
|
|
170
160
|
}
|
|
171
161
|
|
|
172
162
|
export function registerPersonasRuntimeCommands(registry: CommandRegistry): void {
|
|
173
163
|
registry.register({
|
|
174
164
|
name: 'personas',
|
|
175
165
|
aliases: ['persona'],
|
|
176
|
-
description: 'Manage local
|
|
166
|
+
description: 'Manage Agent-local personas',
|
|
177
167
|
usage: '[list|discover|import-discovered <name> --yes|search <query>|show <id>|create --name <name> --description <summary> --body <instructions>|update <id> [--name ...] [--description ...] [--body ...]|use <id>|active|clear|review <id>|stale <id> <reason...>|delete <id> --yes]',
|
|
178
168
|
async handler(args, ctx) {
|
|
179
169
|
const sub = (args[0] ?? 'list').toLowerCase();
|
|
@@ -204,7 +194,7 @@ export function registerPersonasRuntimeCommands(registry: CommandRegistry): void
|
|
|
204
194
|
}
|
|
205
195
|
const snapshot = registryStore.snapshot();
|
|
206
196
|
const persona = registryStore.get(id);
|
|
207
|
-
ctx.print(persona ? renderPersona(persona, snapshot.activePersonaId) : `Unknown persona
|
|
197
|
+
ctx.print(persona ? renderPersona(persona, snapshot.activePersonaId) : `Unknown Agent persona ${id}`);
|
|
208
198
|
return;
|
|
209
199
|
}
|
|
210
200
|
if (sub === 'create') {
|
|
@@ -217,9 +207,9 @@ export function registerPersonasRuntimeCommands(registry: CommandRegistry): void
|
|
|
217
207
|
tags: splitList(parsed.flags.get('tags')),
|
|
218
208
|
triggers: splitList(parsed.flags.get('triggers')),
|
|
219
209
|
source: 'user',
|
|
220
|
-
provenance: '
|
|
210
|
+
provenance: 'Command',
|
|
221
211
|
});
|
|
222
|
-
ctx.print(
|
|
212
|
+
ctx.print(formatPersonaReceipt('Created Agent persona', persona));
|
|
223
213
|
return;
|
|
224
214
|
}
|
|
225
215
|
if (sub === 'update') {
|
|
@@ -235,9 +225,9 @@ export function registerPersonasRuntimeCommands(registry: CommandRegistry): void
|
|
|
235
225
|
body: parsed.flags.get('body'),
|
|
236
226
|
tags: parsed.flags.has('tags') ? splitList(parsed.flags.get('tags')) : undefined,
|
|
237
227
|
triggers: parsed.flags.has('triggers') ? splitList(parsed.flags.get('triggers')) : undefined,
|
|
238
|
-
provenance: '
|
|
228
|
+
provenance: 'Command',
|
|
239
229
|
});
|
|
240
|
-
ctx.print(
|
|
230
|
+
ctx.print(formatPersonaReceipt('Updated Agent persona', updated));
|
|
241
231
|
return;
|
|
242
232
|
}
|
|
243
233
|
if (sub === 'use') {
|
|
@@ -247,7 +237,7 @@ export function registerPersonasRuntimeCommands(registry: CommandRegistry): void
|
|
|
247
237
|
return;
|
|
248
238
|
}
|
|
249
239
|
const persona = registryStore.setActive(id);
|
|
250
|
-
ctx.print(`Active Agent persona: ${persona.name}
|
|
240
|
+
ctx.print(`Active Agent persona: ${persona.name}\n${formatPersonaReceipt('Active Agent persona', persona)}`);
|
|
251
241
|
return;
|
|
252
242
|
}
|
|
253
243
|
if (sub === 'active') {
|
|
@@ -267,7 +257,7 @@ export function registerPersonasRuntimeCommands(registry: CommandRegistry): void
|
|
|
267
257
|
return;
|
|
268
258
|
}
|
|
269
259
|
const persona = registryStore.markReviewed(id);
|
|
270
|
-
ctx.print(
|
|
260
|
+
ctx.print(formatPersonaReceipt('Reviewed Agent persona', persona));
|
|
271
261
|
return;
|
|
272
262
|
}
|
|
273
263
|
if (sub === 'stale') {
|
|
@@ -277,7 +267,7 @@ export function registerPersonasRuntimeCommands(registry: CommandRegistry): void
|
|
|
277
267
|
return;
|
|
278
268
|
}
|
|
279
269
|
const persona = registryStore.markStale(id, args.slice(2).join(' '));
|
|
280
|
-
ctx.print(
|
|
270
|
+
ctx.print(formatPersonaReceipt('Marked Agent persona stale', persona));
|
|
281
271
|
return;
|
|
282
272
|
}
|
|
283
273
|
if (sub === 'delete' || sub === 'remove') {
|
|
@@ -292,7 +282,7 @@ export function registerPersonasRuntimeCommands(registry: CommandRegistry): void
|
|
|
292
282
|
return;
|
|
293
283
|
}
|
|
294
284
|
const removed = registryStore.deletePersona(id);
|
|
295
|
-
ctx.print(
|
|
285
|
+
ctx.print(formatPersonaReceipt('Deleted Agent persona', removed));
|
|
296
286
|
return;
|
|
297
287
|
}
|
|
298
288
|
ctx.print('Usage: /personas [list|discover|import-discovered|search <query>|show <id>|create|update|use|active|clear|review|stale|delete]');
|
|
@@ -111,7 +111,7 @@ export function registerPlanningRuntimeCommands(registry: CommandRegistry): void
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
if (args[0] === 'panel') {
|
|
114
|
-
ctx.print('
|
|
114
|
+
ctx.print('Use /plan status or /plan list for compact command output. Open Agent Workspace -> Work -> Planning status or Saved plans for the workspace view.');
|
|
115
115
|
return;
|
|
116
116
|
}
|
|
117
117
|
|
|
@@ -3,6 +3,7 @@ import { dirname } from 'node:path';
|
|
|
3
3
|
import type { CommandRegistry } from '../command-registry.ts';
|
|
4
4
|
import { listBuiltinSubscriptionProviders } from '@pellux/goodvibes-sdk/platform/config';
|
|
5
5
|
import { buildAuthInspectionSnapshot, inspectProviderAuth } from '@/runtime/index.ts';
|
|
6
|
+
import { formatAgentRecordSource } from '../../agent/record-labels.ts';
|
|
6
7
|
import { requireSecretsManager, requireServiceRegistry, requireShellPaths, requireSubscriptionManager } from './runtime-services.ts';
|
|
7
8
|
import { requireYesFlag, stripYesFlag } from './confirmation.ts';
|
|
8
9
|
|
|
@@ -18,11 +19,11 @@ interface AuthReviewBundle {
|
|
|
18
19
|
function inspectAuthBundle(bundle: AuthReviewBundle): string {
|
|
19
20
|
return [
|
|
20
21
|
'Auth Review Bundle',
|
|
21
|
-
`
|
|
22
|
-
`
|
|
23
|
-
` stored secrets
|
|
24
|
-
` active subscriptions
|
|
25
|
-
` pending subscriptions
|
|
22
|
+
` exported at ${new Date(bundle.exportedAt).toISOString()}`,
|
|
23
|
+
` connected host auth ${bundle.externalRuntimeAuth}`,
|
|
24
|
+
` stored secrets ${bundle.secretKeys.length}`,
|
|
25
|
+
` active subscriptions ${bundle.activeSubscriptions.length}`,
|
|
26
|
+
` pending subscriptions ${bundle.pendingSubscriptions.length}`,
|
|
26
27
|
].join('\n');
|
|
27
28
|
}
|
|
28
29
|
|
|
@@ -66,12 +67,12 @@ export function registerPlatformAccessRuntimeCommands(registry: CommandRegistry)
|
|
|
66
67
|
const builtinProviders = listBuiltinSubscriptionProviders().map((entry) => entry.provider);
|
|
67
68
|
ctx.print([
|
|
68
69
|
'Auth Review',
|
|
69
|
-
' connected
|
|
70
|
-
` stored secrets
|
|
71
|
-
` built-in providers
|
|
72
|
-
` active subscriptions
|
|
73
|
-
` pending subscriptions
|
|
74
|
-
...snapshot.providers.map((provider) => ` ${provider.provider}
|
|
70
|
+
' connected host auth managed outside goodvibes-agent',
|
|
71
|
+
` stored secrets ${snapshot.secretKeyCount}`,
|
|
72
|
+
` built-in providers ${builtinProviders.length}${builtinProviders.length > 0 ? ` (${builtinProviders.join(', ')})` : ''}`,
|
|
73
|
+
` active subscriptions ${snapshot.activeSubscriptions}${snapshot.activeSubscriptions > 0 ? ` (${snapshot.providers.filter((provider) => provider.activeSubscription).map((provider) => provider.provider).join(', ')})` : ''}`,
|
|
74
|
+
` pending subscriptions ${snapshot.pendingSubscriptions}${snapshot.pendingSubscriptions > 0 ? ` (${snapshot.providers.filter((provider) => provider.pendingLogin).map((provider) => provider.provider).join(', ')})` : ''}`,
|
|
75
|
+
...snapshot.providers.map((provider) => ` ${provider.provider} auth ${provider.freshness} finish manual configured ${provider.configured ? 'yes' : 'no'}`),
|
|
75
76
|
].join('\n'));
|
|
76
77
|
return;
|
|
77
78
|
}
|
|
@@ -89,19 +90,18 @@ export function registerPlatformAccessRuntimeCommands(registry: CommandRegistry)
|
|
|
89
90
|
});
|
|
90
91
|
ctx.print([
|
|
91
92
|
`Auth Provider ${provider}`,
|
|
92
|
-
` configured
|
|
93
|
-
...(inspection.source ? [`
|
|
94
|
-
` freshness
|
|
95
|
-
|
|
96
|
-
...(inspection.redirectUri ? [`
|
|
97
|
-
|
|
98
|
-
`
|
|
99
|
-
`
|
|
100
|
-
`
|
|
101
|
-
...(inspection.
|
|
102
|
-
...
|
|
103
|
-
...inspection.
|
|
104
|
-
...inspection.nextActions.map((action) => ` next: ${action}`),
|
|
93
|
+
` configured ${inspection.configured ? 'yes' : 'no'}`,
|
|
94
|
+
...(inspection.source ? [` origin ${formatAgentRecordSource(inspection.source)}`] : []),
|
|
95
|
+
` freshness ${inspection.freshness}`,
|
|
96
|
+
' finish mode explicit manual',
|
|
97
|
+
...(inspection.redirectUri ? [` redirect URI ${inspection.redirectUri}`] : []),
|
|
98
|
+
` active subscription ${inspection.activeSubscription ? 'yes' : 'no'}`,
|
|
99
|
+
` pending login ${inspection.pendingLogin ? 'yes' : 'no'}`,
|
|
100
|
+
` ambient API key override ${inspection.overrideAmbientApiKeys ? 'yes' : 'no'}`,
|
|
101
|
+
...(inspection.tokenType ? [` token type ${inspection.tokenType}`] : []),
|
|
102
|
+
...(inspection.expiresAt ? [` expires at ${new Date(inspection.expiresAt).toISOString()}`] : []),
|
|
103
|
+
...inspection.issues.map((issue) => ` issue ${issue}`),
|
|
104
|
+
...inspection.nextActions.map((action) => ` next ${action}`),
|
|
105
105
|
].join('\n'));
|
|
106
106
|
return;
|
|
107
107
|
}
|
|
@@ -119,12 +119,12 @@ export function registerPlatformAccessRuntimeCommands(registry: CommandRegistry)
|
|
|
119
119
|
});
|
|
120
120
|
ctx.print([
|
|
121
121
|
`Auth Repair ${provider}`,
|
|
122
|
-
` configured
|
|
123
|
-
` freshness
|
|
124
|
-
|
|
125
|
-
...inspection.issues.map((issue) => ` issue
|
|
122
|
+
` configured ${inspection.configured ? 'yes' : 'no'}`,
|
|
123
|
+
` freshness ${inspection.freshness}`,
|
|
124
|
+
' finish mode explicit manual',
|
|
125
|
+
...inspection.issues.map((issue) => ` issue ${issue}`),
|
|
126
126
|
...(inspection.nextActions.length > 0
|
|
127
|
-
? [' next
|
|
127
|
+
? [' next', ...inspection.nextActions.map((action) => ` ${action}`)]
|
|
128
128
|
: [' No active repair actions suggested.']),
|
|
129
129
|
].join('\n'));
|
|
130
130
|
return;
|
|
@@ -2,13 +2,34 @@ import type { CommandContext, CommandRegistry } from '../command-registry.ts';
|
|
|
2
2
|
import type {
|
|
3
3
|
ProviderAccountRecord,
|
|
4
4
|
ProviderAccountSnapshot,
|
|
5
|
-
|
|
5
|
+
ProviderAuthRoute,
|
|
6
|
+
ProviderRouteRecord,
|
|
7
|
+
} from '../../panels/provider-account-snapshot.ts';
|
|
8
|
+
import { buildProviderAccountSnapshot } from '../../panels/provider-account-snapshot.ts';
|
|
6
9
|
import {
|
|
7
|
-
|
|
10
|
+
requireProvider,
|
|
11
|
+
requireServiceRegistry,
|
|
12
|
+
requireSubscriptionManager,
|
|
8
13
|
} from './runtime-services.ts';
|
|
14
|
+
import { formatProviderAuthRouteId } from '../../provider-auth-route-display.ts';
|
|
15
|
+
|
|
16
|
+
function formatRouteList(routes: readonly ProviderAuthRoute[]): string {
|
|
17
|
+
return routes.map((route) => formatProviderAuthRouteId(route)).join(', ');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function formatRouteRecord(route: ProviderRouteRecord): string {
|
|
21
|
+
return `${formatProviderAuthRouteId(route.route)} ${route.usable ? 'usable' : 'blocked'} auth ${route.freshness} ${route.detail}`;
|
|
22
|
+
}
|
|
9
23
|
|
|
10
24
|
async function loadProviderAccountSnapshot(context: CommandContext): Promise<ProviderAccountSnapshot> {
|
|
11
|
-
return await
|
|
25
|
+
return await buildProviderAccountSnapshot({
|
|
26
|
+
providerModels: requireProvider(context).providerRegistry,
|
|
27
|
+
services: requireServiceRegistry(context),
|
|
28
|
+
subscriptions: requireSubscriptionManager(context),
|
|
29
|
+
environment: {
|
|
30
|
+
hasEnvironmentVariable: (name: string) => Boolean(process.env[name]),
|
|
31
|
+
},
|
|
32
|
+
});
|
|
12
33
|
}
|
|
13
34
|
|
|
14
35
|
function findProviderAccountRecord(
|
|
@@ -28,7 +49,7 @@ export function registerProviderAccountsRuntimeCommands(registry: CommandRegistr
|
|
|
28
49
|
async handler(args, ctx) {
|
|
29
50
|
const sub = (args[0] ?? 'review').toLowerCase();
|
|
30
51
|
if (sub === 'panel' || sub === 'open') {
|
|
31
|
-
ctx.print('
|
|
52
|
+
ctx.print('Open Agent Workspace -> Setup -> Provider accounts for the workspace view, or run /accounts review for compact command output.');
|
|
32
53
|
return;
|
|
33
54
|
}
|
|
34
55
|
const snapshot = await loadProviderAccountSnapshot(ctx);
|
|
@@ -36,16 +57,16 @@ export function registerProviderAccountsRuntimeCommands(registry: CommandRegistr
|
|
|
36
57
|
const providerId = args[1];
|
|
37
58
|
const record = findProviderAccountRecord(snapshot, providerId);
|
|
38
59
|
if (!record) {
|
|
39
|
-
ctx.print(providerId ? `Unknown provider account
|
|
60
|
+
ctx.print(providerId ? `Unknown provider account ${providerId}` : 'Usage: /accounts routes <provider>');
|
|
40
61
|
return;
|
|
41
62
|
}
|
|
42
63
|
ctx.print([
|
|
43
64
|
`Provider Routes ${record.providerId}`,
|
|
44
|
-
` preferred
|
|
45
|
-
` active
|
|
46
|
-
` reason
|
|
47
|
-
...record.routeRecords.map((route) => ` ${route
|
|
48
|
-
...record.routeRecords.flatMap((route) => route.issues.map((issue) => ` issue
|
|
65
|
+
` preferred route ${formatProviderAuthRouteId(record.preferredRoute)}`,
|
|
66
|
+
` active route ${formatProviderAuthRouteId(record.activeRoute)}`,
|
|
67
|
+
` reason ${record.activeRouteReason}`,
|
|
68
|
+
...record.routeRecords.map((route) => ` ${formatRouteRecord(route)}`),
|
|
69
|
+
...record.routeRecords.flatMap((route) => route.issues.map((issue) => ` issue ${issue}`)),
|
|
49
70
|
].join('\n'));
|
|
50
71
|
return;
|
|
51
72
|
}
|
|
@@ -53,17 +74,17 @@ export function registerProviderAccountsRuntimeCommands(registry: CommandRegistr
|
|
|
53
74
|
const providerId = args[1];
|
|
54
75
|
const record = findProviderAccountRecord(snapshot, providerId);
|
|
55
76
|
if (!record) {
|
|
56
|
-
ctx.print(providerId ? `Unknown provider account
|
|
77
|
+
ctx.print(providerId ? `Unknown provider account ${providerId}` : 'Usage: /accounts repair <provider>');
|
|
57
78
|
return;
|
|
58
79
|
}
|
|
59
80
|
ctx.print([
|
|
60
81
|
`Provider Account Repair ${record.providerId}`,
|
|
61
|
-
` active route
|
|
62
|
-
` preferred route
|
|
63
|
-
...(record.fallbackRisk ? [`
|
|
64
|
-
...(record.issues.map((issue) => ` issue
|
|
82
|
+
` active route ${formatProviderAuthRouteId(record.activeRoute)}`,
|
|
83
|
+
` preferred route ${formatProviderAuthRouteId(record.preferredRoute)}`,
|
|
84
|
+
...(record.fallbackRisk ? [` routing risk ${record.fallbackRisk}`] : []),
|
|
85
|
+
...(record.issues.map((issue) => ` issue ${issue}`)),
|
|
65
86
|
...(record.recommendedActions.length > 0
|
|
66
|
-
? [' next
|
|
87
|
+
? [' next', ...record.recommendedActions.map((action) => ` ${action}`)]
|
|
67
88
|
: [' No active repair actions suggested.']),
|
|
68
89
|
].join('\n'));
|
|
69
90
|
return;
|
|
@@ -72,39 +93,39 @@ export function registerProviderAccountsRuntimeCommands(registry: CommandRegistr
|
|
|
72
93
|
const providerId = args[1];
|
|
73
94
|
const record = findProviderAccountRecord(snapshot, providerId);
|
|
74
95
|
if (!record) {
|
|
75
|
-
ctx.print(providerId ? `Unknown provider account
|
|
96
|
+
ctx.print(providerId ? `Unknown provider account ${providerId}` : 'Usage: /accounts show <provider>');
|
|
76
97
|
return;
|
|
77
98
|
}
|
|
78
99
|
ctx.print([
|
|
79
100
|
`Provider Account ${record.providerId}`,
|
|
80
|
-
`
|
|
81
|
-
`
|
|
82
|
-
`
|
|
83
|
-
` configured
|
|
84
|
-
`
|
|
85
|
-
`
|
|
86
|
-
`
|
|
87
|
-
`
|
|
88
|
-
`
|
|
89
|
-
...(record.fallbackRoute ? [`
|
|
90
|
-
...(record.fallbackRisk ? [`
|
|
91
|
-
...(record.expiresAt ? [`
|
|
92
|
-
...record.routeRecords.map((route) => ` route ${route
|
|
93
|
-
...record.routeRecords.flatMap((route) => route.issues.map((issue) => ` issue
|
|
94
|
-
...record.usageWindows.map((entry) => ` window
|
|
95
|
-
...record.issues.map((issue) => ` issue
|
|
96
|
-
...record.notes.map((note) => ` note
|
|
97
|
-
...record.recommendedActions.map((action) => ` next
|
|
101
|
+
` preferred route ${formatProviderAuthRouteId(record.preferredRoute)}`,
|
|
102
|
+
` active route ${formatProviderAuthRouteId(record.activeRoute)}`,
|
|
103
|
+
` auth freshness ${record.authFreshness}`,
|
|
104
|
+
` configured ${record.configured ? 'yes' : 'no'}`,
|
|
105
|
+
` OAuth ready ${record.oauthReady ? 'yes' : 'no'}`,
|
|
106
|
+
` pending login ${record.pendingLogin ? 'yes' : 'no'}`,
|
|
107
|
+
` available routes ${formatRouteList(record.availableRoutes)}`,
|
|
108
|
+
` model count ${record.modelCount}`,
|
|
109
|
+
` route reason ${record.activeRouteReason}`,
|
|
110
|
+
...(record.fallbackRoute ? [` fallback route ${formatProviderAuthRouteId(record.fallbackRoute)}`] : []),
|
|
111
|
+
...(record.fallbackRisk ? [` routing risk ${record.fallbackRisk}`] : []),
|
|
112
|
+
...(record.expiresAt ? [` expires at ${new Date(record.expiresAt).toISOString()}`] : []),
|
|
113
|
+
...record.routeRecords.map((route) => ` route ${formatRouteRecord(route)}`),
|
|
114
|
+
...record.routeRecords.flatMap((route) => route.issues.map((issue) => ` issue ${issue}`)),
|
|
115
|
+
...record.usageWindows.map((entry) => ` window ${entry.label} — ${entry.detail}`),
|
|
116
|
+
...record.issues.map((issue) => ` issue ${issue}`),
|
|
117
|
+
...record.notes.map((note) => ` note ${note}`),
|
|
118
|
+
...record.recommendedActions.map((action) => ` next ${action}`),
|
|
98
119
|
].join('\n'));
|
|
99
120
|
return;
|
|
100
121
|
}
|
|
101
122
|
ctx.print([
|
|
102
123
|
'Provider Account Review',
|
|
103
|
-
` providers
|
|
104
|
-
` configured
|
|
105
|
-
` issues
|
|
124
|
+
` providers ${snapshot.providers.length}`,
|
|
125
|
+
` configured ${snapshot.configuredCount}`,
|
|
126
|
+
` issues ${snapshot.issueCount}`,
|
|
106
127
|
...snapshot.providers.map((record) => (
|
|
107
|
-
` ${record.providerId} active
|
|
128
|
+
` ${record.providerId} active route ${formatProviderAuthRouteId(record.activeRoute)} preferred route ${formatProviderAuthRouteId(record.preferredRoute)} auth ${record.authFreshness} models ${record.modelCount} issues ${record.issues.length}`
|
|
108
129
|
)),
|
|
109
130
|
].join('\n'));
|
|
110
131
|
},
|
|
@@ -2,14 +2,14 @@ import type { CommandRegistry } from '../command-registry.ts';
|
|
|
2
2
|
import { networkInterfaces } from 'node:os';
|
|
3
3
|
import {
|
|
4
4
|
buildCompanionConnectionInfo,
|
|
5
|
+
type CompanionConnectionInfo,
|
|
5
6
|
encodeConnectionPayload,
|
|
6
|
-
formatConnectionBlock,
|
|
7
7
|
generateQrMatrix,
|
|
8
8
|
renderQrToString,
|
|
9
9
|
} from '@pellux/goodvibes-sdk/platform/pairing';
|
|
10
10
|
import { GOODVIBES_AGENT_PAIRING_SURFACE } from '../../config/surface.ts';
|
|
11
11
|
import { resolveRuntimeEndpointBinding } from '../../cli/endpoints.ts';
|
|
12
|
-
import { connectedHostTokenRequiredMessage, readConnectedHostOperatorToken } from '../../runtime/connected-host-auth.ts';
|
|
12
|
+
import { connectedHostOperatorTokenFingerprint, connectedHostTokenRequiredMessage, readConnectedHostOperatorToken } from '../../runtime/connected-host-auth.ts';
|
|
13
13
|
import { requirePlatform, requireShellPaths } from './runtime-services.ts';
|
|
14
14
|
|
|
15
15
|
function getLocalNetworkIp(): string {
|
|
@@ -31,13 +31,42 @@ function urlHostForBindHost(host: string): string {
|
|
|
31
31
|
return host || '127.0.0.1';
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
function formatTokenLine(token: string, showToken: boolean): string {
|
|
35
|
+
if (showToken) return `Token: ${token}`;
|
|
36
|
+
return `Token: present sha256:${connectedHostOperatorTokenFingerprint(token)} (hidden in text; QR contains pairing payload)`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function formatAgentPairingBlock(info: CompanionConnectionInfo, qr: string, showToken: boolean): string {
|
|
40
|
+
return [
|
|
41
|
+
`GoodVibes Agent companion pairing v${info.version}`,
|
|
42
|
+
'━━━━━━━━━━━━━━━━━━━━━━━━',
|
|
43
|
+
`Connected host: ${info.url}`,
|
|
44
|
+
`Surface: ${info.surface}`,
|
|
45
|
+
`User: ${info.username}`,
|
|
46
|
+
formatTokenLine(info.token, showToken),
|
|
47
|
+
'',
|
|
48
|
+
'Scan to connect:',
|
|
49
|
+
'',
|
|
50
|
+
qr,
|
|
51
|
+
'',
|
|
52
|
+
showToken
|
|
53
|
+
? 'Manual token display was explicitly confirmed for this command.'
|
|
54
|
+
: 'For manual setup, rerun /pair --show-token --yes to print the token once.',
|
|
55
|
+
'Agent is waiting for the companion app to connect to the owning GoodVibes host.',
|
|
56
|
+
].join('\n');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function shouldShowToken(args: readonly string[]): boolean {
|
|
60
|
+
return args.includes('--show-token') || args.includes('--manual-token');
|
|
61
|
+
}
|
|
62
|
+
|
|
34
63
|
export function registerQrcodeRuntimeCommands(registry: CommandRegistry): void {
|
|
35
64
|
registry.register({
|
|
36
65
|
name: 'qrcode',
|
|
37
66
|
aliases: ['qr', 'pair'],
|
|
38
67
|
description: 'Print companion pairing details and a QR code',
|
|
39
68
|
usage: '',
|
|
40
|
-
handler(
|
|
69
|
+
handler(args, ctx) {
|
|
41
70
|
const shellPaths = requireShellPaths(ctx);
|
|
42
71
|
const configManager = requirePlatform(ctx).configManager;
|
|
43
72
|
const tokenRecord = readConnectedHostOperatorToken(shellPaths.homeDirectory);
|
|
@@ -46,16 +75,26 @@ export function registerQrcodeRuntimeCommands(registry: CommandRegistry): void {
|
|
|
46
75
|
return;
|
|
47
76
|
}
|
|
48
77
|
const binding = resolveRuntimeEndpointBinding(configManager, 'controlPlane');
|
|
49
|
-
const
|
|
78
|
+
const connectedHostUrl = `http://${urlHostForBindHost(binding.host)}:${binding.port}`;
|
|
50
79
|
const info = buildCompanionConnectionInfo({
|
|
51
|
-
daemonUrl,
|
|
80
|
+
daemonUrl: connectedHostUrl,
|
|
52
81
|
token: tokenRecord.token,
|
|
53
82
|
username: 'admin',
|
|
54
83
|
surface: GOODVIBES_AGENT_PAIRING_SURFACE,
|
|
55
84
|
});
|
|
56
85
|
const payload = encodeConnectionPayload(info);
|
|
57
86
|
const qr = renderQrToString(generateQrMatrix(payload));
|
|
58
|
-
|
|
87
|
+
const showToken = shouldShowToken(args);
|
|
88
|
+
if (showToken && !args.includes('--yes')) {
|
|
89
|
+
ctx.print([
|
|
90
|
+
'Manual companion token display requires confirmation.',
|
|
91
|
+
' rerun: /pair --show-token --yes',
|
|
92
|
+
` token fingerprint: sha256:${connectedHostOperatorTokenFingerprint(tokenRecord.token)}`,
|
|
93
|
+
' QR pairing remains available without printing the raw token.',
|
|
94
|
+
].join('\n'));
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
ctx.print(formatAgentPairingBlock(info, qr, showToken));
|
|
59
98
|
},
|
|
60
99
|
});
|
|
61
100
|
}
|