@pellux/goodvibes-tui 1.16.1 → 1.18.0
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 +138 -9
- package/README.md +1 -1
- package/docs/foundation-artifacts/operator-contract.json +5490 -1806
- package/package.json +7 -7
- package/src/cli/completions/generate.ts +5 -4
- package/src/cli/config-overrides.ts +51 -11
- package/src/cli/doctor.ts +4 -5
- package/src/cli/entrypoint.ts +13 -8
- package/src/cli/help.ts +7 -6
- package/src/cli/launch-auto-update.ts +214 -0
- package/src/cli/management-commands.ts +82 -15
- package/src/cli/management.ts +0 -2
- package/src/cli/parser.ts +5 -0
- package/src/cli/status.ts +13 -0
- package/src/cli/surface-command.ts +5 -5
- package/src/cli/tui-startup.ts +60 -97
- package/src/cli/types.ts +2 -0
- package/src/config/index.ts +1 -1
- package/src/config/tui-extension-settings.ts +33 -0
- package/src/core/composer-state.ts +1 -1
- package/src/core/consolidation-receipt.ts +41 -0
- package/src/core/conversation-line-cache.ts +3 -3
- package/src/core/conversation-rendering.ts +18 -9
- package/src/core/conversation.ts +2 -2
- package/src/core/long-task-notifier.ts +7 -7
- package/src/core/memory-provenance.ts +67 -0
- package/src/core/pairing-devices.ts +49 -0
- package/src/core/pairing-handoff.ts +101 -0
- package/src/core/pairing-offers.ts +83 -0
- package/src/core/pairing-origin.ts +78 -0
- package/src/core/pairing-tailscale-gateway.ts +56 -0
- package/src/core/power-chip-source.ts +91 -0
- package/src/core/power-status.ts +95 -0
- package/src/core/stable-host.ts +171 -0
- package/src/core/stream-event-wiring.ts +29 -6
- package/src/core/system-message-noise.ts +1 -1
- package/src/core/system-message-router.ts +6 -6
- package/src/core/turn-budget-outcome.ts +60 -0
- package/src/core/turn-cancellation.ts +40 -1
- package/src/core/turn-event-wiring.ts +14 -8
- package/src/daemon/cli.ts +64 -58
- package/src/daemon/lifecycle.ts +41 -0
- package/src/daemon/service-commands.ts +5 -5
- package/src/export/cost-utils.ts +128 -6
- package/src/input/autocomplete.ts +2 -2
- package/src/input/command-registry.ts +55 -9
- package/src/input/commands/acp-runtime.ts +72 -0
- package/src/input/commands/ci-runtime.ts +5 -1
- package/src/input/commands/codebase-runtime.ts +6 -6
- package/src/input/commands/cost-runtime.ts +1 -1
- package/src/input/commands/devices-runtime.ts +109 -0
- package/src/input/commands/git-runtime.ts +28 -4
- package/src/input/commands/graph-runtime.ts +45 -0
- package/src/input/commands/health-runtime.ts +2 -2
- package/src/input/commands/intelligence-runtime.ts +1 -1
- package/src/input/commands/local-auth-runtime.ts +1 -1
- package/src/input/commands/local-provider-runtime.ts +49 -10
- package/src/input/commands/local-runtime.ts +53 -5
- package/src/input/commands/memory.ts +2 -2
- package/src/input/commands/operator-panel-runtime.ts +5 -5
- package/src/input/commands/operator-rpc.ts +32 -11
- package/src/input/commands/permissions-provenance.ts +1 -1
- package/src/input/commands/permissions-runtime.ts +99 -8
- package/src/input/commands/planning-runtime.ts +3 -3
- package/src/input/commands/platform-sandbox-runtime.ts +1 -1
- package/src/input/commands/power-runtime.ts +56 -0
- package/src/input/commands/provider-accounts-runtime.ts +71 -6
- package/src/input/commands/provider.ts +7 -5
- package/src/input/commands/qrcode-runtime.ts +14 -19
- package/src/input/commands/queue-runtime.ts +70 -0
- package/src/input/commands/recall-files-git.ts +13 -51
- package/src/input/commands/recall-files.ts +8 -11
- package/src/input/commands/recall-review.ts +2 -2
- package/src/input/commands/relay-runtime.ts +1 -1
- package/src/input/commands/remote-runtime.ts +1 -1
- package/src/input/commands/runtime-services.ts +4 -4
- package/src/input/commands/schedule-runtime.ts +14 -7
- package/src/input/commands/services-runtime.ts +1 -1
- package/src/input/commands/session-workflow.ts +2 -2
- package/src/input/commands/settings-sync-runtime.ts +2 -2
- package/src/input/commands/share-runtime.ts +1 -1
- package/src/input/commands/shell-core.ts +46 -68
- package/src/input/commands/subscription-runtime.ts +1 -1
- package/src/input/commands/update-runtime.ts +153 -96
- package/src/input/commands/work-plan-runtime.ts +1 -1
- package/src/input/commands/workstream-attempts.ts +6 -1
- package/src/input/commands/workstream-runtime.ts +46 -27
- package/src/input/commands/worktree-runtime.ts +35 -8
- package/src/input/commands.ts +10 -2
- package/src/input/config-modal-types.ts +14 -3
- package/src/input/config-modal.ts +75 -13
- package/src/input/feature-unit-layout.ts +93 -82
- package/src/input/feed-context-factory.ts +2 -2
- package/src/input/handler-command-route.ts +2 -2
- package/src/input/handler-content-actions.ts +1 -1
- package/src/input/handler-feed-routes.ts +7 -7
- package/src/input/handler-feed.ts +4 -4
- package/src/input/handler-interactions.ts +3 -2
- package/src/input/handler-modal-routes.ts +12 -4
- package/src/input/handler-modal-token-routes.ts +1 -1
- package/src/input/handler-onboarding-daemon-adopt.ts +2 -2
- package/src/input/handler-onboarding.ts +3 -2
- package/src/input/handler-picker-routes.ts +26 -13
- package/src/input/handler-shortcuts.ts +29 -6
- package/src/input/handler.ts +2 -2
- package/src/input/keybindings.ts +21 -3
- package/src/input/onboarding/onboarding-feature-units.ts +34 -27
- package/src/input/onboarding/onboarding-provider-key-fields.ts +112 -0
- package/src/input/onboarding/onboarding-wizard-apply.ts +22 -21
- package/src/input/onboarding/onboarding-wizard-external-surfaces.ts +17 -0
- package/src/input/onboarding/onboarding-wizard-network-adopt.ts +2 -2
- package/src/input/onboarding/onboarding-wizard-steps.ts +30 -42
- package/src/input/panel-integration-actions.ts +2 -2
- package/src/input/panel-mouse-geometry.ts +1 -1
- package/src/input/panel-paste-flood-guard.ts +1 -1
- package/src/input/provider-key-intake.ts +167 -0
- package/src/input/sandbox-exec-config.ts +2 -2
- package/src/input/selection-modal.ts +1 -1
- package/src/input/settings-modal-activation.ts +5 -0
- package/src/input/settings-modal-adjustment.ts +4 -31
- package/src/input/settings-modal-data.ts +52 -57
- package/src/input/settings-modal-mutations.ts +12 -68
- package/src/input/settings-modal-types.ts +27 -17
- package/src/input/settings-modal.ts +74 -55
- package/src/main.ts +90 -90
- package/src/panels/agent-inspector-shared.ts +4 -4
- package/src/panels/base-panel.ts +2 -2
- package/src/panels/builtin/agent.ts +4 -4
- package/src/panels/builtin/development.ts +4 -4
- package/src/panels/builtin/knowledge.ts +1 -1
- package/src/panels/builtin/operations.ts +83 -11
- package/src/panels/builtin/session.ts +5 -5
- package/src/panels/builtin/shared.ts +16 -4
- package/src/panels/builtin-modals.ts +59 -42
- package/src/panels/builtin-panels.ts +1 -1
- package/src/panels/cost-tracker-panel.ts +124 -7
- package/src/panels/diff-panel.ts +39 -6
- package/src/panels/eval-registry.ts +2 -2
- package/src/panels/fleet-acts.ts +446 -0
- package/src/panels/fleet-deep-link.ts +1 -1
- package/src/panels/fleet-gateway.ts +117 -0
- package/src/panels/fleet-observed-render.ts +128 -0
- package/src/panels/fleet-panel-format.ts +163 -11
- package/src/panels/fleet-panel-worktree-detail.ts +14 -0
- package/src/panels/fleet-panel.ts +73 -57
- package/src/panels/fleet-read-model.ts +84 -14
- package/src/panels/fleet-spawn.ts +266 -0
- package/src/panels/fleet-steer.ts +2 -2
- package/src/panels/fleet-stop.ts +41 -5
- package/src/panels/fleet-tabs.ts +15 -16
- package/src/panels/fleet-transcript.ts +5 -5
- package/src/panels/git-panel.ts +2 -2
- package/src/panels/index.ts +4 -1
- package/src/panels/local-auth-panel.ts +1 -1
- package/src/panels/memory-consolidation-gateway.ts +94 -0
- package/src/panels/modals/devices-modal.ts +161 -0
- package/src/panels/modals/hooks-modal.ts +1 -1
- package/src/panels/modals/keybindings-modal.ts +2 -2
- package/src/panels/modals/knowledge-modal.ts +1 -1
- package/src/panels/modals/marketplace-modal.ts +4 -4
- package/src/panels/modals/memory-modal.ts +153 -9
- package/src/panels/modals/modal-surface-helpers.ts +1 -1
- package/src/panels/modals/modal-theme.ts +1 -1
- package/src/panels/modals/pairing-modal.ts +198 -68
- package/src/panels/modals/planning-modal.ts +2 -2
- package/src/panels/modals/plugins-modal.ts +1 -1
- package/src/panels/modals/policy-modal.ts +1 -1
- package/src/panels/modals/security-modal.ts +7 -1
- package/src/panels/modals/skills-modal.ts +1 -1
- package/src/panels/modals/work-plan-modal.ts +3 -3
- package/src/panels/notifications-feed.ts +127 -0
- package/src/panels/notifications-panel.ts +160 -0
- package/src/panels/panel-manager.ts +9 -9
- package/src/panels/plugins-panel.ts +4 -4
- package/src/panels/project-planning-answer-actions.ts +1 -1
- package/src/panels/provider-health-routes.ts +2 -2
- package/src/panels/skills-panel.ts +4 -5
- package/src/panels/token-budget-panel.ts +11 -3
- package/src/panels/types.ts +5 -5
- package/src/panels/workstream-graph-render.ts +123 -0
- package/src/permissions/broker-approval-card.ts +201 -0
- package/src/permissions/hunk-selection.ts +28 -4
- package/src/permissions/prompt.ts +244 -29
- package/src/renderer/autocomplete-overlay.ts +107 -54
- package/src/renderer/code-block.ts +2 -2
- package/src/renderer/compaction-history-modal.ts +1 -1
- package/src/renderer/compaction-preview.ts +1 -1
- package/src/renderer/compaction-quality.ts +1 -1
- package/src/renderer/composer-fragments.ts +69 -0
- package/src/renderer/compositor.ts +2 -2
- package/src/renderer/config-modal.ts +1 -1
- package/src/renderer/conversation-overlays.ts +1 -1
- package/src/renderer/footer-tips.ts +3 -3
- package/src/renderer/hint-grammar.ts +1 -1
- package/src/renderer/modal-factory.ts +1 -1
- package/src/renderer/model-picker-overlay.ts +1 -1
- package/src/renderer/model-workspace.ts +7 -2
- package/src/renderer/panel-composite.ts +2 -2
- package/src/renderer/panel-workspace-bar.ts +1 -1
- package/src/renderer/process-indicator.ts +1 -1
- package/src/renderer/selection-modal-overlay.ts +1 -1
- package/src/renderer/session-picker-modal.ts +2 -2
- package/src/renderer/settings-modal-helpers.ts +23 -5
- package/src/renderer/settings-modal.ts +129 -39
- package/src/renderer/shell-surface.ts +6 -0
- package/src/renderer/terminal-bg-probe.ts +1 -1
- package/src/renderer/theme.ts +3 -3
- package/src/renderer/tool-call.ts +13 -7
- package/src/renderer/tool-result-summary.ts +1 -1
- package/src/renderer/turn-injection.ts +7 -6
- package/src/renderer/ui-factory.ts +38 -1
- package/src/runtime/bootstrap-command-context.ts +7 -2
- package/src/runtime/bootstrap-command-parts.ts +9 -5
- package/src/runtime/bootstrap-core.ts +41 -41
- package/src/runtime/bootstrap-hook-bridge.ts +1 -1
- package/src/runtime/bootstrap-shell.ts +13 -5
- package/src/runtime/bootstrap.ts +34 -32
- package/src/runtime/channel-composition.ts +46 -0
- package/src/runtime/code-index-services.ts +3 -4
- package/src/runtime/daemon-attach-notices.ts +120 -0
- package/src/runtime/durability-services.ts +90 -0
- package/src/runtime/embedded-service-factories.ts +40 -0
- package/src/runtime/feature-settings.ts +147 -0
- package/src/runtime/fleet-services.ts +41 -0
- package/src/runtime/idle-power-services.ts +85 -0
- package/src/runtime/index.ts +6 -21
- package/src/runtime/interaction-seams.ts +109 -0
- package/src/runtime/legacy-daemon-migration.ts +29 -19
- package/src/runtime/memory-fold.ts +1 -1
- package/src/runtime/notification-dispatch.ts +110 -0
- package/src/runtime/onboarding/apply.ts +3 -29
- package/src/runtime/onboarding/fast-path.ts +39 -6
- package/src/runtime/onboarding/provider-key-capture.ts +126 -0
- package/src/runtime/onboarding/snapshot.ts +0 -1
- package/src/runtime/onboarding/types.ts +9 -3
- package/src/runtime/power-keepawake-remote.ts +75 -0
- package/src/runtime/process-lifecycle.ts +3 -1
- package/src/runtime/provider-fallback.ts +55 -0
- package/src/runtime/provider-optimizer-wiring.ts +55 -0
- package/src/runtime/release-artifacts.ts +19 -103
- package/src/runtime/resume-notice.ts +1 -1
- package/src/runtime/services.ts +113 -113
- package/src/runtime/session-continuity-hints.ts +38 -0
- package/src/runtime/surface-feature-flags.ts +40 -9
- package/src/runtime/terminal-output-guard.ts +2 -2
- package/src/runtime/trust/workspace-registration.ts +10 -2
- package/src/runtime/trust/workspace-trust.ts +58 -31
- package/src/runtime/ui-services.ts +6 -1
- package/src/runtime/update-check.ts +15 -69
- package/src/runtime/workstream-services.ts +2 -2
- package/src/shell/blocking-input.ts +108 -132
- package/src/shell/recovery-input-helpers.ts +53 -166
- package/src/shell/ui-openers.ts +38 -6
- package/src/utils/format-duration.ts +1 -1
- package/src/verification/verification-ledger.ts +21 -4
- package/src/version.ts +1 -1
- package/src/input/commands/flags-runtime.ts +0 -328
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// acp-runtime.ts — the scripted path for hosting third-party coding agents,
|
|
3
|
+
// over the SAME verbs the Fleet panel's spawn affordance drives (acp.agents.list
|
|
4
|
+
// / acp.sessions.create). `/agents list` shows what the daemon discovered
|
|
5
|
+
// (read-only, quiet when none); `/agents host <agentId> [dir]` spawns one as a
|
|
6
|
+
// long-lived daemon session that shows up as an acp-agent fleet row. A working
|
|
7
|
+
// directory defaults to the current one; a known workspace root can be named
|
|
8
|
+
// instead — but no path is ever required beyond what the operator already has.
|
|
9
|
+
//
|
|
10
|
+
// A structured spawn failure ({binary, stage, message}) is printed verbatim,
|
|
11
|
+
// never left as a hung row: the daemon bounds the handshake and returns a
|
|
12
|
+
// 'failed' record with its three honest fields.
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
|
|
15
|
+
import type { CommandRegistry } from '../command-registry.ts';
|
|
16
|
+
import { requireShellPaths } from './runtime-services.ts';
|
|
17
|
+
import { describeOperatorRpcError, getOperatorRpc } from './operator-rpc.ts';
|
|
18
|
+
|
|
19
|
+
export function registerAcpRuntimeCommands(registry: CommandRegistry): void {
|
|
20
|
+
registry.register({
|
|
21
|
+
name: 'agents',
|
|
22
|
+
aliases: ['acp'],
|
|
23
|
+
description: 'Host third-party coding agents (Claude Code, Codex, opencode) as fleet rows over ACP',
|
|
24
|
+
usage: 'list | host <agentId> [directory]',
|
|
25
|
+
argsHint: '[list|host]',
|
|
26
|
+
async handler(args, ctx) {
|
|
27
|
+
const sub = (args[0] ?? 'list').toLowerCase();
|
|
28
|
+
const rpc = getOperatorRpc(ctx);
|
|
29
|
+
if (!rpc.available) { ctx.print(`[agents] ${rpc.reason}`); return; }
|
|
30
|
+
|
|
31
|
+
if (sub === 'list') {
|
|
32
|
+
try {
|
|
33
|
+
const { agents } = await rpc.sdk.operator.invoke('acp.agents.list', {});
|
|
34
|
+
if (agents.length === 0) {
|
|
35
|
+
ctx.print('[agents] No third-party ACP agents discovered. Install Claude Code, Codex, or opencode on PATH to host one.');
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
ctx.print([
|
|
39
|
+
'Discovered ACP agents (host with /agents host <id> [directory]):',
|
|
40
|
+
...agents.map((a) => ` ${a.id} — ${a.title} (${a.binaryPath})`),
|
|
41
|
+
].join('\n'));
|
|
42
|
+
} catch (error) {
|
|
43
|
+
ctx.print(`[agents list] ${describeOperatorRpcError(error)}`);
|
|
44
|
+
}
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (sub === 'host') {
|
|
49
|
+
const agentId = args[1];
|
|
50
|
+
if (!agentId) {
|
|
51
|
+
ctx.print('Usage: /agents host <agentId> [directory] (agentId from /agents list; directory defaults to the current one)');
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const cwd = args[2] ?? requireShellPaths(ctx).workingDirectory;
|
|
55
|
+
try {
|
|
56
|
+
const { hosted, started } = await rpc.sdk.operator.invoke('acp.sessions.create', { agentId, cwd });
|
|
57
|
+
if (hosted.error) {
|
|
58
|
+
// Structured failure, verbatim — never a hung row.
|
|
59
|
+
ctx.print(`[agents host] Could not host ${hosted.title || agentId}: ${hosted.error.stage} stage failed for ${hosted.error.binary} — ${hosted.error.message}`);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
ctx.print(`[agents host] Hosting ${hosted.title || agentId} in ${cwd}${started ? '' : ' (queued)'} — it appears as an acp-agent row in the fleet; steer and stop it like any agent.`);
|
|
63
|
+
} catch (error) {
|
|
64
|
+
ctx.print(`[agents host] ${describeOperatorRpcError(error)}`);
|
|
65
|
+
}
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
ctx.print('Usage: /agents list | /agents host <agentId> [directory]');
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
}
|
|
@@ -155,8 +155,12 @@ export function registerCiRuntimeCommands(registry: CommandRegistry): void {
|
|
|
155
155
|
const result = await rpc.sdk.operator.invoke('ci.watches.run', { watchId: runId });
|
|
156
156
|
const lines = [renderReport(result.report)];
|
|
157
157
|
lines.push(`notified: ${result.notified ? 'yes' : 'no'}${result.notificationId ? ` (${result.notificationId})` : ''}`);
|
|
158
|
-
lines.push(`fix-session triggered: ${result.fixSessionTriggered ? 'yes' : 'no'}
|
|
158
|
+
lines.push(`fix-session triggered: ${result.fixSessionTriggered ? 'yes' : 'no'}`);
|
|
159
159
|
ctx.print(lines.join('\n'));
|
|
160
|
+
// Route the spawned session through the shared one-key jump affordance
|
|
161
|
+
// instead of printing the id for the user to retype — same attach flow
|
|
162
|
+
// as the accepted fix-this card.
|
|
163
|
+
if (result.fixSessionId) ctx.armFixSessionAttach?.(result.fixSessionId);
|
|
160
164
|
} catch (error) {
|
|
161
165
|
ctx.print(`[ci watches run] ${describeOperatorRpcError(error)}`);
|
|
162
166
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
// Explicit-query + transparency surface over the repo source-tree code index
|
|
5
5
|
// (CodeIndexStore, @pellux/goodvibes-sdk/platform/state, landed on SDK main
|
|
6
|
-
//
|
|
6
|
+
// with explicit-query build/status/search) via the TUI's own wiring in
|
|
7
7
|
// src/runtime/code-index-services.ts, threaded through
|
|
8
8
|
// CommandContext.session.codeIndexStore exactly like wrfcController/
|
|
9
9
|
// workstreamEngine already are (bootstrap-command-context.ts).
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
// node while it runs), status (counts, skips, degradation state, last
|
|
15
15
|
// build), search <query> (explicit retrieval, results labeled
|
|
16
16
|
// 'lexical'|'semantic' honestly — never implied as more precise than they
|
|
17
|
-
// are).
|
|
18
|
-
//
|
|
17
|
+
// are). Passive auto-injection into coding turns and tool-site incremental
|
|
18
|
+
// reindex followed later; `status` surfaces both honestly (the
|
|
19
19
|
// auto-injection flag+setting gate, and the last reindex activity).
|
|
20
20
|
// ---------------------------------------------------------------------------
|
|
21
21
|
|
|
@@ -35,7 +35,7 @@ function formatBytes(bytes: number): string {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
|
-
* Honest one-line auto-injection state
|
|
38
|
+
* Honest one-line auto-injection state: both the (default-off)
|
|
39
39
|
* `agent-passive-code-injection` feature flag AND storage.codeIndexEnabled must
|
|
40
40
|
* be on for passive code injection to run — state which is off, and why.
|
|
41
41
|
*/
|
|
@@ -49,7 +49,7 @@ function renderAutoInjectionState(flagEnabled: boolean, settingEnabled: boolean)
|
|
|
49
49
|
return ` auto-injection: off (${reasons.join('; ')})`;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
/** Honest last-reindex activity line (
|
|
52
|
+
/** Honest last-reindex activity line (tool-site incremental reindex). */
|
|
53
53
|
function renderReindexActivity(activity: CodeIndexReindexActivity | null, now: number): string {
|
|
54
54
|
if (!activity) return ' last reindex: none this session (writes/edits reindex touched files once the index is built)';
|
|
55
55
|
const agoSec = Math.max(0, Math.round((now - activity.at) / 1000));
|
|
@@ -82,7 +82,7 @@ function renderCodeIndexStatus(stats: CodeIndexStats, configManager: Pick<Config
|
|
|
82
82
|
lines.push(
|
|
83
83
|
` auto-build on startup: ${autoStart ? 'on' : 'off'} (storage.codeIndexEnabled, default off — /config to change)`,
|
|
84
84
|
);
|
|
85
|
-
//
|
|
85
|
+
// Passive code auto-injection state (flag + setting) and the last tool-site reindex.
|
|
86
86
|
lines.push(renderAutoInjectionState(extras.flagEnabled, autoStart));
|
|
87
87
|
lines.push(renderReindexActivity(extras.reindexActivity, extras.now ?? Date.now()));
|
|
88
88
|
lines.push(
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
} from './cost-attribution-format.ts';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* /cost budget <usd> — makes the CostTrackerPanel's budget alert real
|
|
14
|
+
* /cost budget <usd> — makes the CostTrackerPanel's budget alert real.
|
|
15
15
|
* Opens the cost panel (creating it via the registered factory if not already
|
|
16
16
|
* open) and sets its budget threshold directly, so the meter+alert already
|
|
17
17
|
* built into CostTrackerPanel.render() (:266-290) actually fires. 0 disables
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type { CommandContext, CommandRegistry } from '../command-registry.ts';
|
|
2
|
+
import { formatDeviceLine, resolveTokenByIdPrefix } from '../../core/pairing-devices.ts';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Register the /devices command — manage the per-device pairing tokens minted by
|
|
6
|
+
* the pairing surfaces. Mirrors the pairing.tokens.* gateway verbs the daemon
|
|
7
|
+
* serves (list/rename/revoke/migrate/revoke-shared) so the terminal has the same
|
|
8
|
+
* capability as the web app's device manager.
|
|
9
|
+
*
|
|
10
|
+
* /devices list paired devices (name · created · last-seen · id)
|
|
11
|
+
* /devices rename <id> <name> rename a device token (id or unambiguous prefix)
|
|
12
|
+
* /devices revoke <id> revoke a device token immediately
|
|
13
|
+
* /devices migrate-shared [name] mint a per-device token off the legacy shared token
|
|
14
|
+
* /devices revoke-shared revoke the legacy shared token (it stops authenticating)
|
|
15
|
+
*/
|
|
16
|
+
export function registerDevicesRuntimeCommands(registry: CommandRegistry): void {
|
|
17
|
+
registry.register({
|
|
18
|
+
name: 'devices',
|
|
19
|
+
aliases: ['device'],
|
|
20
|
+
description: 'Manage paired-device tokens (list, rename, revoke, migrate-shared)',
|
|
21
|
+
usage: '[list|rename <id> <name>|revoke <id>|migrate-shared [name]|revoke-shared]',
|
|
22
|
+
argsHint: '[list|rename|revoke|migrate-shared|revoke-shared]',
|
|
23
|
+
handler(args, ctx) {
|
|
24
|
+
const pairingTokens = ctx.platform.pairingTokens;
|
|
25
|
+
if (!pairingTokens) {
|
|
26
|
+
ctx.print('Device management is unavailable in this runtime (no pairing token service wired).');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const sub = (args[0] ?? 'list').toLowerCase();
|
|
30
|
+
|
|
31
|
+
if (sub === 'list') {
|
|
32
|
+
renderList(ctx, pairingTokens);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (sub === 'rename') {
|
|
37
|
+
const idOrPrefix = args[1];
|
|
38
|
+
const name = args.slice(2).join(' ').trim();
|
|
39
|
+
if (!idOrPrefix || !name) {
|
|
40
|
+
ctx.print('Usage: /devices rename <id> <new name>');
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const resolved = resolveTokenByIdPrefix(pairingTokens.list(), idOrPrefix);
|
|
44
|
+
if (!resolved.ok) {
|
|
45
|
+
ctx.print(resolved.reason === 'ambiguous' ? `Ambiguous device id "${idOrPrefix}" — use more characters.` : `No device matches "${idOrPrefix}".`);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
pairingTokens.rename(resolved.token.id, name);
|
|
49
|
+
ctx.print(`Renamed device to "${name}".`);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (sub === 'revoke') {
|
|
54
|
+
const idOrPrefix = args[1];
|
|
55
|
+
if (!idOrPrefix) {
|
|
56
|
+
ctx.print('Usage: /devices revoke <id>');
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const resolved = resolveTokenByIdPrefix(pairingTokens.list(), idOrPrefix);
|
|
60
|
+
if (!resolved.ok) {
|
|
61
|
+
ctx.print(resolved.reason === 'ambiguous' ? `Ambiguous device id "${idOrPrefix}" — use more characters.` : `No device matches "${idOrPrefix}".`);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const revoked = pairingTokens.revoke(resolved.token.id);
|
|
65
|
+
ctx.print(revoked ? `Revoked "${resolved.token.name}" — its token stops working immediately.` : `Device "${resolved.token.name}" was already absent.`);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (sub === 'migrate-shared') {
|
|
70
|
+
const name = args.slice(1).join(' ').trim() || 'migrated device';
|
|
71
|
+
const minted = pairingTokens.mintForMigration({ name });
|
|
72
|
+
ctx.print(
|
|
73
|
+
`Minted a per-device token "${minted.name}" (shown once): ${minted.token}\n` +
|
|
74
|
+
'Pair this device with it, then run /devices revoke-shared to retire the legacy shared token.',
|
|
75
|
+
);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (sub === 'revoke-shared') {
|
|
80
|
+
if (pairingTokens.isLegacyRevoked()) {
|
|
81
|
+
ctx.print('The legacy shared token is already revoked.');
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
pairingTokens.revokeLegacyShared();
|
|
85
|
+
ctx.print('Revoked the legacy shared token — devices still on it must re-pair with their own token.');
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
ctx.print(`Unknown /devices subcommand: ${sub}. Use: list | rename | revoke | migrate-shared | revoke-shared`);
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function renderList(ctx: CommandContext, pairingTokens: NonNullable<CommandContext['platform']['pairingTokens']>): void {
|
|
95
|
+
const list = pairingTokens.list();
|
|
96
|
+
const lines: string[] = ['Paired devices:'];
|
|
97
|
+
if (list.length === 0) {
|
|
98
|
+
lines.push(' (none yet — pair one with /pair)');
|
|
99
|
+
} else {
|
|
100
|
+
for (const token of list) lines.push(` ${formatDeviceLine(token)}`);
|
|
101
|
+
}
|
|
102
|
+
lines.push(
|
|
103
|
+
'',
|
|
104
|
+
pairingTokens.isLegacyRevoked()
|
|
105
|
+
? 'Legacy shared token: revoked.'
|
|
106
|
+
: 'Legacy shared token: active — run /devices migrate-shared then /devices revoke-shared to retire it.',
|
|
107
|
+
);
|
|
108
|
+
ctx.print(lines.join('\n'));
|
|
109
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { CommandRegistry } from '../command-registry.ts';
|
|
2
2
|
import { GitService } from '@pellux/goodvibes-sdk/platform/git';
|
|
3
|
-
import { requireShellPaths } from './runtime-services.ts';
|
|
3
|
+
import { requireShellPaths, requirePanelManager } from './runtime-services.ts';
|
|
4
4
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
5
5
|
|
|
6
6
|
export function registerGitRuntimeCommands(registry: CommandRegistry): void {
|
|
@@ -64,10 +64,34 @@ export function registerGitRuntimeCommands(registry: CommandRegistry): void {
|
|
|
64
64
|
break;
|
|
65
65
|
}
|
|
66
66
|
case 'diff': {
|
|
67
|
+
// Route the full, uncapped working-tree diff (GitService.diffStructured
|
|
68
|
+
// — parsed per-file/per-hunk with no size limit) into the real diff
|
|
69
|
+
// panel. The old path sliced the raw text at 4,000 chars and printed a
|
|
70
|
+
// stub; a large diff now renders complete.
|
|
67
71
|
try {
|
|
68
|
-
const
|
|
69
|
-
if (
|
|
70
|
-
|
|
72
|
+
const structured = await git.diffStructured();
|
|
73
|
+
if (structured.files.length === 0) {
|
|
74
|
+
ctx.print('No unstaged changes.');
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
const { DiffPanel } = await import('../../panels/diff-panel.ts');
|
|
78
|
+
const pm = requirePanelManager(ctx);
|
|
79
|
+
let panel = pm.getAllOpen().find((p) => p.id === 'diff');
|
|
80
|
+
if (!panel) {
|
|
81
|
+
try {
|
|
82
|
+
panel = pm.open('diff');
|
|
83
|
+
} catch {
|
|
84
|
+
ctx.print('Could not open diff panel.');
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
pm.activateById('diff');
|
|
89
|
+
if (!pm.isVisible()) pm.show();
|
|
90
|
+
ctx.focusPanels?.();
|
|
91
|
+
(panel as InstanceType<typeof DiffPanel>).loadStructuredDiff(structured);
|
|
92
|
+
const fileWord = structured.files.length === 1 ? 'file' : 'files';
|
|
93
|
+
ctx.print(`Diff panel updated: ${structured.files.length} ${fileWord}, +${structured.additions} -${structured.deletions} (complete, uncapped).`);
|
|
94
|
+
ctx.renderRequest();
|
|
71
95
|
} catch (e) {
|
|
72
96
|
ctx.print(`Git diff failed: ${summarizeError(e)}`);
|
|
73
97
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { CommandRegistry } from '../command-registry.ts';
|
|
2
|
+
import { getOperatorRpc, describeOperatorRpcError } from './operator-rpc.ts';
|
|
3
|
+
import { renderWorkstreamGraphLines } from '../../panels/workstream-graph-render.ts';
|
|
4
|
+
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// /graph — a workstream's task graph (fleet.graph.get) rendered legibly.
|
|
7
|
+
//
|
|
8
|
+
// The observability-layer surface: nodes, edges, per-node states (ready /
|
|
9
|
+
// running / blocked-with-"waiting on: X" / stalled / orphaned) and the elastic
|
|
10
|
+
// pool posture ("N ready, M running, at cap (fleet.maxSize=N)") — the shape of
|
|
11
|
+
// the dependency graph WITHOUT opening any transcript. The fix-phase chain's
|
|
12
|
+
// task graph is one such workstream.
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
|
|
15
|
+
function renderWidth(): number {
|
|
16
|
+
const cols = typeof process !== 'undefined' && process.stdout && process.stdout.columns ? process.stdout.columns : 80;
|
|
17
|
+
return Math.max(40, cols - 2);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function registerGraphRuntimeCommands(registry: CommandRegistry): void {
|
|
21
|
+
registry.register({
|
|
22
|
+
name: 'graph',
|
|
23
|
+
description: "Show a workstream's task graph — nodes, edges, states, and pool posture",
|
|
24
|
+
usage: '<workstreamId>',
|
|
25
|
+
argsHint: '<workstreamId>',
|
|
26
|
+
async handler(args, ctx) {
|
|
27
|
+
const workstreamId = (args[0] ?? '').trim();
|
|
28
|
+
if (!workstreamId) {
|
|
29
|
+
ctx.print('Usage: /graph <workstreamId> — renders that workstream\'s task graph (nodes, edges, states, pool).');
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const rpc = getOperatorRpc(ctx);
|
|
33
|
+
if (!rpc.available) {
|
|
34
|
+
ctx.print(`[graph] ${rpc.reason}`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
const snapshot = await rpc.sdk.operator.invoke('fleet.graph.get', { workstreamId });
|
|
39
|
+
ctx.print(renderWorkstreamGraphLines(snapshot, renderWidth()).join('\n'));
|
|
40
|
+
} catch (error) {
|
|
41
|
+
ctx.print(`[graph] ${describeOperatorRpcError(error)}`);
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
}
|
|
@@ -48,7 +48,7 @@ export function registerHealthRuntimeCommands(registry: CommandRegistry): void {
|
|
|
48
48
|
const readModels = requireReadModels(ctx);
|
|
49
49
|
|
|
50
50
|
if (sub === 'open' || sub === 'panel' || sub === 'provider') {
|
|
51
|
-
ctx.openModal?.('providers-modal'); //
|
|
51
|
+
ctx.openModal?.('providers-modal'); // provider-health panel -> config modal
|
|
52
52
|
return;
|
|
53
53
|
}
|
|
54
54
|
|
|
@@ -477,7 +477,7 @@ export function registerHealthRuntimeCommands(registry: CommandRegistry): void {
|
|
|
477
477
|
|
|
478
478
|
ctx.print([
|
|
479
479
|
'Health Review',
|
|
480
|
-
//
|
|
480
|
+
// item 3: /health stays a cross-domain transcript report (it
|
|
481
481
|
// genuinely spans settings/accounts/auth/sandbox/etc — no single
|
|
482
482
|
// modal owns this data), but the providers domain DOES have one, so
|
|
483
483
|
// point at it honestly. "/provider" (no domain) is a DIFFERENT
|
|
@@ -55,7 +55,7 @@ export function registerIntelligenceRuntimeCommands(registry: CommandRegistry):
|
|
|
55
55
|
async handler(args, ctx) {
|
|
56
56
|
const sub = (args[0] ?? 'review').toLowerCase();
|
|
57
57
|
if (sub === 'panel' || sub === 'open') {
|
|
58
|
-
//
|
|
58
|
+
// (the purge): 'intelligence'/IntelligencePanel was
|
|
59
59
|
// DELETE-disposition (no surviving human surface — its read model
|
|
60
60
|
// stays and still backs every subcommand below). There is no alias
|
|
61
61
|
// to resolve through, so this prints an honest notice and opens
|
|
@@ -10,7 +10,7 @@ export function handleLocalAuthCommand(args: string[], ctx: CommandContext): voi
|
|
|
10
10
|
const sub = (args[0] ?? 'review').toLowerCase();
|
|
11
11
|
const auth = requireLocalUserAuthManager(ctx);
|
|
12
12
|
if (sub === 'panel' || sub === 'open') {
|
|
13
|
-
//
|
|
13
|
+
// browse view moved to local-auth-modal. The LocalAuthPanel itself is
|
|
14
14
|
// kept (masked password-entry host) but is no longer the /local-auth panel
|
|
15
15
|
// destination — masked entry is reached via add-user/rotate-password below.
|
|
16
16
|
ctx.openModal?.('local-auth-modal');
|
|
@@ -4,29 +4,35 @@ import { writeFile, unlink } from 'node:fs/promises';
|
|
|
4
4
|
import type { CommandRegistry } from '../command-registry.ts';
|
|
5
5
|
import { fetchModelContextWindows } from '@pellux/goodvibes-sdk/platform/discovery';
|
|
6
6
|
import type { CustomProviderConfig } from '@pellux/goodvibes-sdk/platform/providers';
|
|
7
|
-
import { requireProviderApi, requireShellPaths } from './runtime-services.ts';
|
|
7
|
+
import { requireProviderApi, requireSecretsManager, requireShellPaths } from './runtime-services.ts';
|
|
8
|
+
import { ensureProviderKeyThenSelect } from '../provider-key-intake.ts';
|
|
8
9
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
9
10
|
|
|
10
11
|
function isValidProviderName(name: string): boolean {
|
|
11
12
|
return /^[a-zA-Z0-9_-]+$/.test(name);
|
|
12
13
|
}
|
|
13
14
|
|
|
15
|
+
/** Conventional secrets-store key a custom provider reads its API key from. */
|
|
16
|
+
function customProviderKeyName(providerName: string): string {
|
|
17
|
+
return `${providerName.replace(/[^A-Za-z0-9]/g, '_').toUpperCase()}_API_KEY`;
|
|
18
|
+
}
|
|
19
|
+
|
|
14
20
|
export function registerLocalProviderRuntimeCommands(registry: CommandRegistry): void {
|
|
15
21
|
registry.register({
|
|
16
22
|
name: 'provider',
|
|
17
23
|
aliases: ['p'],
|
|
18
24
|
description: 'Switch provider or manage custom providers (add/remove)',
|
|
19
|
-
usage: '[add <name> <baseURL>
|
|
25
|
+
usage: '[add <name> <baseURL> | remove <name> | <provider-name>]',
|
|
20
26
|
argsHint: '[add|remove|name]',
|
|
21
27
|
async handler(args, ctx) {
|
|
22
28
|
const shellPaths = requireShellPaths(ctx);
|
|
23
29
|
if (args[0] === 'add') {
|
|
24
30
|
const addArgs = args.slice(1);
|
|
25
31
|
if (addArgs.length < 2) {
|
|
26
|
-
ctx.print('Usage: /provider add <name> <baseURL
|
|
32
|
+
ctx.print('Usage: /provider add <name> <baseURL>\nExample: /provider add my-server http://192.168.0.85:8001/v1\nAfter the provider is created you can enter its API key with the keystrokes masked (or skip it for keyless endpoints).');
|
|
27
33
|
return;
|
|
28
34
|
}
|
|
29
|
-
const [name, baseURL
|
|
35
|
+
const [name, baseURL] = addArgs;
|
|
30
36
|
if (!isValidProviderName(name)) {
|
|
31
37
|
ctx.print('Error: Provider name must contain only letters, numbers, hyphens, and underscores.');
|
|
32
38
|
return;
|
|
@@ -51,7 +57,6 @@ export function registerLocalProviderRuntimeCommands(registry: CommandRegistry):
|
|
|
51
57
|
const controller = new AbortController();
|
|
52
58
|
const timeoutId = setTimeout(() => controller.abort(), 5000);
|
|
53
59
|
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
|
|
54
|
-
if (apiKey) headers['Authorization'] = `Bearer ${apiKey}`;
|
|
55
60
|
const res = await fetch(`${baseURL}/models`, { signal: controller.signal, headers });
|
|
56
61
|
clearTimeout(timeoutId);
|
|
57
62
|
if (res.ok) {
|
|
@@ -91,12 +96,16 @@ export function registerLocalProviderRuntimeCommands(registry: CommandRegistry):
|
|
|
91
96
|
contextWindow: contextWindows[id] ?? 8192,
|
|
92
97
|
capabilities: { toolCalling: true, codeEditing: true, reasoning: false, multimodal: false },
|
|
93
98
|
}));
|
|
99
|
+
// The key is never embedded in the provider JSON. The config references
|
|
100
|
+
// it by name (apiKeyEnv) and the actual value lands in the secrets
|
|
101
|
+
// manager below, resolved live at request time.
|
|
102
|
+
const keyName = customProviderKeyName(name);
|
|
94
103
|
const config: CustomProviderConfig = {
|
|
95
104
|
name,
|
|
96
105
|
displayName: name,
|
|
97
106
|
type: 'openai-compat',
|
|
98
107
|
baseURL,
|
|
99
|
-
|
|
108
|
+
apiKeyEnv: keyName,
|
|
100
109
|
models,
|
|
101
110
|
};
|
|
102
111
|
try {
|
|
@@ -107,6 +116,32 @@ export function registerLocalProviderRuntimeCommands(registry: CommandRegistry):
|
|
|
107
116
|
return;
|
|
108
117
|
}
|
|
109
118
|
ctx.print(`Provider '${name}' added with ${models.length} model(s):\n${discoveredModelIds.length > 0 ? discoveredModelIds.map((id) => ` • ${id} (${(contextWindows[id] ?? 8192).toLocaleString()} ctx)`).join('\n') : ` • ${defaultModel} (starter entry)`}\nThe file watcher will auto-register it shortly.`);
|
|
119
|
+
// Capture the key with the keystrokes masked, straight into the secrets
|
|
120
|
+
// manager — never a cleartext argument, never written into the JSON.
|
|
121
|
+
if (ctx.beginConcealedInput) {
|
|
122
|
+
const secretsManager = requireSecretsManager(ctx);
|
|
123
|
+
ctx.print(`Enter the API key for provider '${name}' (input is masked; press Enter to store, Esc to skip if the endpoint needs no key).`);
|
|
124
|
+
ctx.beginConcealedInput({
|
|
125
|
+
label: name,
|
|
126
|
+
onSubmit: (value) => {
|
|
127
|
+
if (value.length === 0) {
|
|
128
|
+
ctx.print(`No key stored for '${name}' (endpoint used without a key).`);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
void (async () => {
|
|
132
|
+
try {
|
|
133
|
+
await secretsManager.set(keyName, value, { scope: 'user', medium: 'secure' });
|
|
134
|
+
ctx.print(`Stored the API key for '${name}' (value hidden). It resolves live for this provider — no restart needed.`);
|
|
135
|
+
} catch (error) {
|
|
136
|
+
ctx.print(`Could not store the key for '${name}': ${summarizeError(error)}`);
|
|
137
|
+
}
|
|
138
|
+
})();
|
|
139
|
+
},
|
|
140
|
+
onCancel: () => {
|
|
141
|
+
ctx.print(`No key stored for '${name}' (endpoint used without a key).`);
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
}
|
|
110
145
|
return;
|
|
111
146
|
}
|
|
112
147
|
|
|
@@ -157,10 +192,14 @@ export function registerLocalProviderRuntimeCommands(registry: CommandRegistry):
|
|
|
157
192
|
}
|
|
158
193
|
try {
|
|
159
194
|
const selected = await providerApi.selectModel(match.registryKey);
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
ctx
|
|
163
|
-
|
|
195
|
+
// Unconfigured provider selected → capture its key inline, then finish
|
|
196
|
+
// the switch so it lands ready to chat.
|
|
197
|
+
ensureProviderKeyThenSelect(ctx, selected.providerId, () => {
|
|
198
|
+
ctx.session.runtime.model = selected.registryKey;
|
|
199
|
+
ctx.session.runtime.provider = selected.providerId;
|
|
200
|
+
ctx.platform.configManager.set('provider.model', selected.registryKey);
|
|
201
|
+
ctx.print(`Switched to provider: ${selected.providerId} (model: ${selected.modelId})`);
|
|
202
|
+
});
|
|
164
203
|
} catch (e) {
|
|
165
204
|
ctx.print(`Error: ${summarizeError(e)}`);
|
|
166
205
|
}
|
|
@@ -4,6 +4,7 @@ import { readFile } from 'node:fs/promises';
|
|
|
4
4
|
import type { CommandRegistry, CommandContext } from '../command-registry.ts';
|
|
5
5
|
import type { SelectionItem } from '../selection-modal.ts';
|
|
6
6
|
import type { ContentPart } from '@pellux/goodvibes-sdk/platform/providers';
|
|
7
|
+
import { formatModelDiscoveryReport } from '@pellux/goodvibes-sdk/platform/providers';
|
|
7
8
|
import { resolveAndValidatePath } from '@pellux/goodvibes-sdk/platform/utils';
|
|
8
9
|
import { BUILTIN_SECRET_PROVIDER_SOURCES, describeSecretRef, isSecretRefInput, resolveSecretRef } from '@pellux/goodvibes-sdk/platform/config';
|
|
9
10
|
import { openCommandPanel, requireBookmarkManager, requireProviderApi, requireSecretsManager } from './runtime-services.ts';
|
|
@@ -70,7 +71,7 @@ export function registerLocalRuntimeCommands(registry: CommandRegistry): void {
|
|
|
70
71
|
handler(args, ctx) {
|
|
71
72
|
const sub = (args[0] ?? '').toLowerCase();
|
|
72
73
|
if (sub === 'panel' || sub === 'review') {
|
|
73
|
-
//
|
|
74
|
+
// (the purge): 'tools'/ToolInspectorPanel was DELETE-disposition
|
|
74
75
|
// (no surviving human surface — tool results render inline in the
|
|
75
76
|
// transcript, plus a per-node tool list in Fleet). There is no alias
|
|
76
77
|
// to resolve through, so this prints an honest notice and opens
|
|
@@ -200,10 +201,42 @@ export function registerLocalRuntimeCommands(registry: CommandRegistry): void {
|
|
|
200
201
|
const flags = new Set(rest.filter((value) => value.startsWith('--')));
|
|
201
202
|
const valueParts = rest.filter((value) => !value.startsWith('--'));
|
|
202
203
|
const [key, ...rawValueParts] = valueParts;
|
|
203
|
-
if (!key
|
|
204
|
+
if (!key) {
|
|
204
205
|
ctx.print(`[secrets] Usage: /secrets ${sub} <KEY> <${sub === 'link' ? 'secret-ref' : 'value'}> [--user|--project] [--secure|--plaintext]`);
|
|
205
206
|
return;
|
|
206
207
|
}
|
|
208
|
+
const scope = flags.has('--user') ? 'user' : 'project';
|
|
209
|
+
const medium = flags.has('--plaintext') ? 'plaintext' : 'secure';
|
|
210
|
+
// /secrets set KEY (no value) drops into the concealed prompt so the
|
|
211
|
+
// value is typed with the keystrokes masked and never enters history.
|
|
212
|
+
if (sub === 'set' && rawValueParts.length === 0) {
|
|
213
|
+
if (!ctx.beginConcealedInput) {
|
|
214
|
+
ctx.print('[secrets] Usage: /secrets set <KEY> <value> [--user|--project] [--secure|--plaintext]');
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
ctx.print(`[secrets] Enter value for ${key} (input is masked; press Enter to store, Esc to cancel).`);
|
|
218
|
+
ctx.beginConcealedInput({
|
|
219
|
+
label: key,
|
|
220
|
+
onSubmit: (value) => {
|
|
221
|
+
if (value.length === 0) {
|
|
222
|
+
ctx.print(`[secrets] ${key} not set (empty value).`);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
void (async () => {
|
|
226
|
+
try {
|
|
227
|
+
await mgr.set(key, value, { scope, medium });
|
|
228
|
+
ctx.print(`[secrets] Stored: ${key} (${scope}, ${medium})`);
|
|
229
|
+
} catch (error) {
|
|
230
|
+
ctx.print(`[secrets] Could not store ${key}: ${summarizeError(error)}`);
|
|
231
|
+
}
|
|
232
|
+
})();
|
|
233
|
+
},
|
|
234
|
+
onCancel: () => {
|
|
235
|
+
ctx.print(`[secrets] ${key} cancelled.`);
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
207
240
|
const value = rawValueParts.join(' ');
|
|
208
241
|
if (sub === 'link' && !isGoodVibesSecretRefInput(value)) {
|
|
209
242
|
ctx.print('[secrets] Invalid secret reference. Use /secrets providers for examples.');
|
|
@@ -213,8 +246,6 @@ export function registerLocalRuntimeCommands(registry: CommandRegistry): void {
|
|
|
213
246
|
ctx.print('[secrets] Invalid secret reference. Use /secrets providers for examples.');
|
|
214
247
|
return;
|
|
215
248
|
}
|
|
216
|
-
const scope = flags.has('--user') ? 'user' : 'project';
|
|
217
|
-
const medium = flags.has('--plaintext') ? 'plaintext' : 'secure';
|
|
218
249
|
await mgr.set(key, value, { scope, medium });
|
|
219
250
|
ctx.print(sub === 'link'
|
|
220
251
|
? `[secrets] Linked: ${key} -> ${describeSecretRef(value)} (${scope}, ${medium})`
|
|
@@ -337,7 +368,24 @@ export function registerLocalRuntimeCommands(registry: CommandRegistry): void {
|
|
|
337
368
|
} catch (e) {
|
|
338
369
|
ctx.print(`Token limits refresh failed: ${summarizeError(e)}`);
|
|
339
370
|
}
|
|
340
|
-
|
|
371
|
+
ctx.print('Refreshing live provider model lists...');
|
|
372
|
+
let discoveryOk = false;
|
|
373
|
+
try {
|
|
374
|
+
// Explicit user refresh → force past each provider's TTL cache and
|
|
375
|
+
// report the delta ("3 new, 1 retired") or the honest fallback reason.
|
|
376
|
+
const reports = await providerApi.refreshLiveModelDiscovery(undefined, { force: true });
|
|
377
|
+
discoveryOk = true;
|
|
378
|
+
if (reports.length === 0) {
|
|
379
|
+
ctx.print('No providers perform live model discovery.');
|
|
380
|
+
} else {
|
|
381
|
+
for (const report of reports) {
|
|
382
|
+
ctx.print(` ${formatModelDiscoveryReport(report.providerId, report)}`);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
} catch (e) {
|
|
386
|
+
ctx.print(`Live model discovery failed: ${summarizeError(e)}`);
|
|
387
|
+
}
|
|
388
|
+
if (!catalogOk || !benchmarksOk || !limitsOk || !discoveryOk) ctx.print('Some refreshes failed — see messages above.');
|
|
341
389
|
},
|
|
342
390
|
});
|
|
343
391
|
|
|
@@ -24,7 +24,7 @@ import { handleRecallFilesApply, handleRecallFilesReview, handleRecallFilesSync
|
|
|
24
24
|
// ── Top-level command ─────────────────────────────────────────────────────────
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
|
-
*
|
|
27
|
+
* item 3 divergence note (historical): the work order that shipped
|
|
28
28
|
* this command named its front door "/memory", but at the time `/memory` was
|
|
29
29
|
* already a distinct, unrelated command (session-pinned sticky notes,
|
|
30
30
|
* src/input/commands/session-content.ts) with no modal surface — so that
|
|
@@ -84,7 +84,7 @@ export const recallCommand: SlashCommand = {
|
|
|
84
84
|
handler: async (args: string[], context: CommandContext): Promise<void> => {
|
|
85
85
|
const [sub, ...rest] = args;
|
|
86
86
|
|
|
87
|
-
//
|
|
87
|
+
// item 3: bare `/recall` opens the memory-modal surface — the old
|
|
88
88
|
// bare/unknown-subcommand usage block moved to an explicit `report`
|
|
89
89
|
// subcommand (scriptability preserved: /recall report).
|
|
90
90
|
if (sub === undefined) {
|