@pellux/goodvibes-tui 1.1.0 → 1.9.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 +85 -21
- package/README.md +28 -18
- package/docs/foundation-artifacts/operator-contract.json +4045 -1763
- package/package.json +2 -2
- package/src/audio/spoken-turn-controller.ts +12 -2
- package/src/audio/spoken-turn-wiring.ts +2 -1
- package/src/cli/help.ts +8 -1
- package/src/cli/service-posture.ts +2 -1
- package/src/cli/status.ts +2 -1
- package/src/cli/surface-command.ts +2 -2
- package/src/core/alert-gating.ts +67 -0
- package/src/core/approval-alert.ts +72 -0
- package/src/core/budget-breach-notifier.ts +106 -0
- package/src/core/composer-state.ts +11 -3
- package/src/core/conversation-line-cache.ts +27 -3
- package/src/core/conversation-rendering.ts +90 -14
- package/src/core/conversation.ts +18 -0
- package/src/core/focus-tracker.ts +41 -0
- package/src/core/long-task-notifier.ts +33 -6
- package/src/core/stream-event-wiring.ts +20 -1
- package/src/core/system-message-noise.ts +87 -0
- package/src/core/system-message-router.ts +104 -51
- package/src/core/turn-cancellation.ts +25 -0
- package/src/core/turn-event-wiring.ts +72 -3
- package/src/daemon/cli.ts +29 -2
- package/src/daemon/handlers/register.ts +8 -1
- package/src/daemon/service-commands.ts +329 -0
- package/src/export/cost-utils.ts +36 -0
- package/src/input/autocomplete.ts +27 -1
- package/src/input/command-registry.ts +84 -5
- package/src/input/commands/checkpoint-runtime.ts +280 -0
- package/src/input/commands/codebase-runtime.ts +232 -0
- package/src/input/commands/config.ts +43 -3
- package/src/input/commands/eval.ts +1 -1
- package/src/input/commands/health-runtime.ts +10 -2
- package/src/input/commands/image-runtime.ts +112 -0
- package/src/input/commands/intelligence-runtime.ts +11 -1
- package/src/input/commands/local-auth-runtime.ts +4 -1
- package/src/input/commands/local-runtime.ts +9 -3
- package/src/input/commands/marketplace-runtime.ts +2 -2
- package/src/input/commands/memory.ts +73 -35
- package/src/input/commands/operator-panel-runtime.ts +71 -31
- package/src/input/commands/planning-runtime.ts +116 -4
- package/src/input/commands/platform-sandbox-runtime.ts +1 -6
- package/src/input/commands/plugin-runtime.ts +2 -2
- package/src/input/commands/policy-dispatch.ts +21 -0
- package/src/input/commands/provider-accounts-runtime.ts +1 -1
- package/src/input/commands/qrcode-runtime.ts +26 -6
- package/src/input/commands/recall-review.ts +35 -0
- package/src/input/commands/remote-runtime-setup.ts +5 -3
- package/src/input/commands/remote-runtime.ts +3 -3
- package/src/input/commands/runtime-services.ts +54 -13
- package/src/input/commands/services-runtime.ts +1 -1
- package/src/input/commands/session-content.ts +20 -9
- package/src/input/commands/session-workflow.ts +16 -1
- package/src/input/commands/settings-sync-runtime.ts +16 -4
- package/src/input/commands/shell-core.ts +25 -8
- package/src/input/commands/skills-runtime.ts +2 -8
- package/src/input/commands/subscription-runtime.ts +2 -2
- package/src/input/commands/test-runtime.ts +277 -0
- package/src/input/commands/websearch-runtime.ts +101 -0
- package/src/input/commands/work-plan-runtime.ts +36 -10
- package/src/input/commands/workstream-runtime.ts +488 -0
- package/src/input/commands.ts +12 -0
- package/src/input/config-modal-types.ts +175 -0
- package/src/input/config-modal.ts +592 -0
- package/src/input/feed-context-factory.ts +10 -8
- package/src/input/handler-command-route.ts +37 -20
- package/src/input/handler-content-actions.ts +17 -2
- package/src/input/handler-feed-routes.ts +74 -114
- package/src/input/handler-feed.ts +79 -27
- package/src/input/handler-interactions.ts +1 -3
- package/src/input/handler-modal-routes.ts +136 -6
- package/src/input/handler-modal-stack.ts +14 -7
- package/src/input/handler-modal-token-routes.ts +16 -49
- package/src/input/handler-onboarding-daemon-adopt.ts +149 -0
- package/src/input/handler-onboarding.ts +71 -59
- package/src/input/handler-picker-routes.ts +26 -102
- package/src/input/handler-shortcuts.ts +74 -14
- package/src/input/handler-types.ts +2 -6
- package/src/input/handler-ui-state.ts +10 -22
- package/src/input/handler.ts +12 -29
- package/src/input/keybindings.ts +23 -8
- package/src/input/model-picker-types.ts +24 -6
- package/src/input/model-picker.ts +77 -2
- package/src/input/onboarding/onboarding-runtime-status.ts +10 -1
- package/src/input/onboarding/onboarding-wizard-apply.ts +8 -1
- package/src/input/onboarding/onboarding-wizard-constants.ts +4 -1
- package/src/input/onboarding/onboarding-wizard-network-adopt.ts +136 -0
- package/src/input/onboarding/onboarding-wizard-steps.ts +9 -6
- package/src/input/onboarding/onboarding-wizard-types.ts +3 -1
- package/src/input/panel-integration-actions.ts +9 -170
- package/src/input/panel-mouse-geometry.ts +97 -0
- package/src/input/panel-paste-flood-guard.ts +86 -0
- package/src/input/selection-modal.ts +11 -0
- package/src/input/session-picker-modal.ts +44 -4
- package/src/input/settings-modal-data.ts +146 -2
- package/src/input/settings-modal-mutations.ts +6 -4
- package/src/input/settings-modal-types.ts +4 -2
- package/src/main.ts +49 -51
- package/src/panels/agent-inspector-shared.ts +2 -1
- package/src/panels/base-panel.ts +22 -1
- package/src/panels/builtin/agent.ts +21 -107
- package/src/panels/builtin/development.ts +15 -61
- package/src/panels/builtin/knowledge.ts +8 -32
- package/src/panels/builtin/operations.ts +84 -428
- package/src/panels/builtin/session.ts +21 -112
- package/src/panels/builtin/shared.ts +18 -46
- package/src/panels/builtin-modals.ts +218 -0
- package/src/panels/builtin-panels.ts +5 -0
- package/src/panels/cost-tracker-panel.ts +36 -10
- package/src/panels/diff-panel.ts +12 -43
- package/src/panels/eval-registry.ts +60 -0
- package/src/panels/fleet-deep-link.ts +31 -0
- package/src/panels/fleet-panel-format.ts +62 -0
- package/src/panels/fleet-panel-worktree-detail.ts +48 -0
- package/src/panels/fleet-panel.ts +758 -0
- package/src/panels/fleet-read-model.ts +520 -0
- package/src/panels/fleet-steer.ts +125 -0
- package/src/panels/fleet-stop.ts +153 -0
- package/src/panels/fleet-tabs.ts +230 -0
- package/src/panels/fleet-transcript.ts +356 -0
- package/src/panels/index.ts +7 -31
- package/src/panels/modals/hooks-modal.ts +187 -0
- package/src/panels/modals/keybindings-modal.ts +166 -0
- package/src/panels/modals/knowledge-modal.ts +158 -0
- package/src/panels/modals/local-auth-modal.ts +132 -0
- package/src/panels/modals/marketplace-modal.ts +218 -0
- package/src/panels/modals/memory-modal.ts +180 -0
- package/src/panels/modals/modal-surface-helpers.ts +54 -0
- package/src/panels/modals/modal-theme.ts +42 -0
- package/src/panels/modals/pairing-modal.ts +163 -0
- package/src/panels/modals/planning-modal.ts +304 -0
- package/src/panels/modals/plugins-modal.ts +174 -0
- package/src/panels/modals/policy-modal.ts +256 -0
- package/src/panels/modals/provider-health-modal.ts +136 -0
- package/src/panels/modals/remote-modal.ts +115 -0
- package/src/panels/modals/sandbox-modal.ts +150 -0
- package/src/panels/modals/security-modal.ts +217 -0
- package/src/panels/modals/services-modal.ts +179 -0
- package/src/panels/modals/settings-sync-modal.ts +142 -0
- package/src/panels/modals/skills-modal.ts +189 -0
- package/src/panels/modals/subscription-modal.ts +172 -0
- package/src/panels/modals/work-plan-modal.ts +177 -0
- package/src/panels/panel-confirm-overlay.ts +72 -0
- package/src/panels/panel-manager.ts +123 -9
- package/src/panels/polish-core.ts +38 -25
- package/src/panels/project-planning-answer-actions.ts +8 -11
- package/src/panels/skills-panel.ts +7 -51
- package/src/panels/types.ts +37 -0
- package/src/permissions/hunk-selection.ts +179 -0
- package/src/permissions/prompt.ts +216 -13
- package/src/renderer/autocomplete-overlay.ts +28 -2
- package/src/renderer/compaction-history-modal.ts +19 -3
- package/src/renderer/compaction-preview.ts +13 -2
- package/src/renderer/compaction-quality.ts +100 -0
- package/src/renderer/compositor.ts +2 -3
- package/src/renderer/config-modal.ts +95 -0
- package/src/renderer/conversation-overlays.ts +10 -17
- package/src/renderer/fleet-tab-strip.ts +56 -0
- package/src/renderer/footer-tips.ts +15 -3
- package/src/renderer/fullscreen-primitives.ts +32 -22
- package/src/renderer/git-status.ts +43 -1
- package/src/renderer/help-overlay.ts +2 -2
- package/src/renderer/layout.ts +0 -4
- package/src/renderer/markdown.ts +7 -3
- package/src/renderer/modal-factory.ts +25 -20
- package/src/renderer/model-workspace.ts +62 -4
- package/src/renderer/overlay-box.ts +21 -17
- package/src/renderer/panel-workspace-bar.ts +8 -2
- package/src/renderer/process-indicator.ts +14 -3
- package/src/renderer/selection-modal-overlay.ts +6 -1
- package/src/renderer/session-picker-modal.ts +196 -3
- package/src/renderer/settings-modal-helpers.ts +2 -0
- package/src/renderer/settings-modal.ts +7 -0
- package/src/renderer/shell-surface.ts +8 -1
- package/src/renderer/status-glyphs.ts +14 -15
- package/src/renderer/system-message.ts +15 -3
- package/src/renderer/terminal-bg-probe.ts +339 -0
- package/src/renderer/terminal-escapes.ts +20 -0
- package/src/renderer/theme-mode-config.ts +67 -0
- package/src/renderer/theme.ts +91 -1
- package/src/renderer/thinking.ts +11 -3
- package/src/renderer/tool-call.ts +15 -9
- package/src/renderer/tool-result-summary.ts +148 -0
- package/src/renderer/turn-injection.ts +133 -0
- package/src/renderer/ui-factory.ts +154 -85
- package/src/renderer/ui-primitives.ts +30 -129
- package/src/runtime/bootstrap-command-context.ts +27 -1
- package/src/runtime/bootstrap-command-parts.ts +41 -10
- package/src/runtime/bootstrap-core.ts +47 -15
- package/src/runtime/bootstrap-hook-bridge.ts +7 -0
- package/src/runtime/bootstrap-shell.ts +48 -17
- package/src/runtime/bootstrap.ts +129 -22
- package/src/runtime/code-index-services.ts +135 -0
- package/src/runtime/legacy-daemon-migration.ts +516 -0
- package/src/runtime/memory-fold.ts +26 -0
- package/src/runtime/onboarding/derivation.ts +7 -2
- package/src/runtime/onboarding/snapshot.ts +27 -1
- package/src/runtime/onboarding/types.ts +31 -1
- package/src/runtime/operator-token-cleanup.ts +82 -1
- package/src/runtime/orchestrator-core-services.ts +59 -0
- package/src/runtime/process-lifecycle.ts +3 -1
- package/src/runtime/resume-notice.ts +209 -0
- package/src/runtime/services.ts +98 -46
- package/src/runtime/session-inbound-inputs.ts +252 -0
- package/src/runtime/session-spine-transport.ts +64 -0
- package/src/runtime/terminal-output-guard.ts +15 -8
- package/src/runtime/ui-services.ts +27 -3
- package/src/runtime/workstream-services.ts +327 -0
- package/src/runtime/wrfc-persistence.ts +124 -17
- package/src/shell/blocking-input.ts +68 -4
- package/src/shell/recovery-input-helpers.ts +170 -1
- package/src/shell/ui-openers.ts +171 -26
- package/src/utils/format-duration.ts +8 -18
- package/src/utils/splash-lines.ts +1 -1
- package/src/utils/terminal-width.ts +52 -0
- package/src/version.ts +1 -1
- package/src/panels/agent-inspector-panel.ts +0 -786
- package/src/panels/approval-panel.ts +0 -252
- package/src/panels/automation-control-panel.ts +0 -479
- package/src/panels/cockpit-panel.ts +0 -481
- package/src/panels/cockpit-read-model.ts +0 -238
- package/src/panels/communication-panel.ts +0 -256
- package/src/panels/control-plane-panel.ts +0 -470
- package/src/panels/debug-panel.ts +0 -615
- package/src/panels/docs-panel.ts +0 -384
- package/src/panels/eval-panel.ts +0 -627
- package/src/panels/file-explorer-panel.ts +0 -673
- package/src/panels/file-preview-panel.ts +0 -517
- package/src/panels/hooks-panel.ts +0 -401
- package/src/panels/incident-review-panel.ts +0 -406
- package/src/panels/intelligence-panel.ts +0 -383
- package/src/panels/knowledge-graph-panel.ts +0 -515
- package/src/panels/marketplace-panel.ts +0 -399
- package/src/panels/memory-panel.ts +0 -558
- package/src/panels/ops-control-panel.ts +0 -329
- package/src/panels/ops-strategy-panel.ts +0 -321
- package/src/panels/orchestration-panel.ts +0 -465
- package/src/panels/panel-list-panel.ts +0 -566
- package/src/panels/plan-dashboard-panel.ts +0 -707
- package/src/panels/policy-panel.ts +0 -517
- package/src/panels/project-planning-panel.ts +0 -731
- package/src/panels/provider-health-panel.ts +0 -678
- package/src/panels/provider-health-tracker.ts +0 -310
- package/src/panels/provider-health-views.ts +0 -567
- package/src/panels/qr-panel.ts +0 -280
- package/src/panels/remote-panel.ts +0 -534
- package/src/panels/routes-panel.ts +0 -241
- package/src/panels/sandbox-panel.ts +0 -456
- package/src/panels/security-panel.ts +0 -447
- package/src/panels/services-panel.ts +0 -329
- package/src/panels/session-browser-panel.ts +0 -496
- package/src/panels/settings-sync-panel.ts +0 -398
- package/src/panels/subscription-panel.ts +0 -342
- package/src/panels/symbol-outline-panel.ts +0 -619
- package/src/panels/system-messages-panel.ts +0 -364
- package/src/panels/tasks-panel.ts +0 -608
- package/src/panels/thinking-panel.ts +0 -333
- package/src/panels/tool-inspector-panel.ts +0 -537
- package/src/panels/work-plan-panel.ts +0 -540
- package/src/panels/worktree-panel.ts +0 -360
- package/src/panels/wrfc-panel.ts +0 -790
- package/src/renderer/agent-detail-modal.ts +0 -466
- package/src/renderer/live-tail-modal.ts +0 -156
- package/src/renderer/process-modal.ts +0 -671
- package/src/renderer/qr-renderer.ts +0 -120
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { InputToken } from '@pellux/goodvibes-sdk/platform/core';
|
|
2
2
|
import type { SelectionResult, SelectionAction } from './selection-modal.ts';
|
|
3
3
|
import type { CommandContext } from './command-registry.ts';
|
|
4
|
+
import type { ConfigModal } from './config-modal.ts';
|
|
4
5
|
import { openTtsProviderPicker, openTtsVoicePicker } from './tts-settings-actions.ts';
|
|
5
6
|
import { isTextBackspace } from './delete-key-policy.ts';
|
|
6
7
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
@@ -93,16 +94,26 @@ export function handleSelectionModalToken(state: SelectionRouteState, token: Inp
|
|
|
93
94
|
if (selected) {
|
|
94
95
|
dispatchSelectionAction(action, selected);
|
|
95
96
|
}
|
|
97
|
+
} else if (state.selectionModal.allowSearch) {
|
|
98
|
+
// UX-C instant filter: an unclaimed keystroke arms search AND starts
|
|
99
|
+
// the query immediately, instead of silently doing nothing until the
|
|
100
|
+
// user discovers '/' first (the "help search needs '/' arming while
|
|
101
|
+
// the palette filters instantly" evaluator finding — /help has no
|
|
102
|
+
// customActions, so every letter used to be swallowed here). '/'
|
|
103
|
+
// above still works too; this is additive, not a replacement, and
|
|
104
|
+
// never fires for a claimed hotkey letter (checked first, above).
|
|
105
|
+
state.selectionModal.focusSearch();
|
|
106
|
+
state.selectionModal.setQuery(token.value);
|
|
96
107
|
}
|
|
97
108
|
}
|
|
98
109
|
} else if (token.type === 'key') {
|
|
99
110
|
if (token.logicalName === 'escape') {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
111
|
+
// UX-C: ONE Escape always closes the modal, regardless of search state.
|
|
112
|
+
// Clearing an in-progress query is Backspace's job, not Esc's — the old
|
|
113
|
+
// two-stage contract (1st Esc clears query, 2nd blurs search, 3rd
|
|
114
|
+
// finally closes) was the "help modal took 3 Escapes after searching"
|
|
115
|
+
// evaluator finding (searchFocused is not modal-specific — every
|
|
116
|
+
// SelectionModal-based picker had the same multi-Esc pattern).
|
|
106
117
|
state.handleEscape();
|
|
107
118
|
return true;
|
|
108
119
|
}
|
|
@@ -533,3 +544,122 @@ export function handleProfilePickerToken(state: ProfilePickerRouteState, token:
|
|
|
533
544
|
state.requestRender();
|
|
534
545
|
return true;
|
|
535
546
|
}
|
|
547
|
+
|
|
548
|
+
type ConfigModalRouteState = {
|
|
549
|
+
configModal: ConfigModal;
|
|
550
|
+
commandContext?: CommandContext;
|
|
551
|
+
requestRender: () => void;
|
|
552
|
+
handleEscape: () => void;
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Route a key to the generic config-modal host (W6.1 MIGRATE-TO-MODAL
|
|
557
|
+
* surfaces). The host owns the reserved navigation keys (Esc, up/down/j/k tab
|
|
558
|
+
* left/right, and now '/' — DEBT-5 item 1's type-to-filter, armed the same
|
|
559
|
+
* way as scrollable-list-panel.ts's opt-in filter and SettingsModal's own
|
|
560
|
+
* '/'-armed search); every other key is offered to the active surface's
|
|
561
|
+
* declarative action table (fireAction handles the two-press confirm for
|
|
562
|
+
* destructive actions). Any unrecognised key is absorbed so the modal stays
|
|
563
|
+
* modal — this is the same "active modal captures all keys" shape as the
|
|
564
|
+
* other modal routers.
|
|
565
|
+
*
|
|
566
|
+
* While the filter is armed (`configModal.isFilterActive()`), printable text
|
|
567
|
+
* tokens go to the query instead of nav/action dispatch — this is WHY 'j'/'k'
|
|
568
|
+
* lose their vim-nav meaning mid-filter (you're typing, not navigating) but
|
|
569
|
+
* Escape, Backspace, and the arrow/tab nav keys keep their ordinary meaning
|
|
570
|
+
* (arrows navigate the FILTERED list; Enter still acts on the selected row).
|
|
571
|
+
*/
|
|
572
|
+
export function handleConfigModalToken(state: ConfigModalRouteState, token: InputToken): boolean {
|
|
573
|
+
if (!state.configModal.active) return false;
|
|
574
|
+
|
|
575
|
+
// Every token that reaches the modal is a user interaction — after the
|
|
576
|
+
// first one, structure freezes to interaction boundaries (liveness rule).
|
|
577
|
+
// Before it, renders may sync structure so async onOpen loads appear
|
|
578
|
+
// without a keypress (batch refutation finding 3).
|
|
579
|
+
state.configModal.noteInteraction();
|
|
580
|
+
|
|
581
|
+
const filtering = state.configModal.isFilterActive();
|
|
582
|
+
|
|
583
|
+
if (token.type === 'key') {
|
|
584
|
+
if (token.logicalName === 'escape') {
|
|
585
|
+
// Two-stage Esc, but ONLY while there is a query to clear (the one
|
|
586
|
+
// documented exception to single-Esc-close): clearFilterOrFallthrough
|
|
587
|
+
// returns false on an empty query, so an armed-but-empty filter (or no
|
|
588
|
+
// filter at all) closes the modal in one press, same as every other
|
|
589
|
+
// modal here.
|
|
590
|
+
if (state.configModal.clearFilterOrFallthrough()) {
|
|
591
|
+
state.requestRender();
|
|
592
|
+
return true;
|
|
593
|
+
}
|
|
594
|
+
state.handleEscape();
|
|
595
|
+
return true;
|
|
596
|
+
}
|
|
597
|
+
if (filtering && isTextBackspace(token.logicalName ?? '')) {
|
|
598
|
+
state.configModal.backspaceFilter();
|
|
599
|
+
state.requestRender();
|
|
600
|
+
return true;
|
|
601
|
+
}
|
|
602
|
+
if (!filtering && token.logicalName === '/') {
|
|
603
|
+
state.configModal.activateFilter();
|
|
604
|
+
state.requestRender();
|
|
605
|
+
return true;
|
|
606
|
+
}
|
|
607
|
+
switch (token.logicalName) {
|
|
608
|
+
case 'up':
|
|
609
|
+
state.configModal.moveUp();
|
|
610
|
+
state.requestRender();
|
|
611
|
+
return true;
|
|
612
|
+
case 'down':
|
|
613
|
+
state.configModal.moveDown();
|
|
614
|
+
state.requestRender();
|
|
615
|
+
return true;
|
|
616
|
+
case 'left':
|
|
617
|
+
state.configModal.prevTab();
|
|
618
|
+
state.requestRender();
|
|
619
|
+
return true;
|
|
620
|
+
case 'right':
|
|
621
|
+
case 'tab':
|
|
622
|
+
state.configModal.nextTab();
|
|
623
|
+
state.requestRender();
|
|
624
|
+
return true;
|
|
625
|
+
}
|
|
626
|
+
// Any other 'key' token (e.g. 'enter') falls through to the action table
|
|
627
|
+
// below even while filtering — only printable TEXT tokens are captured
|
|
628
|
+
// by the filter (below), so Enter still fires on the current selection.
|
|
629
|
+
} else if (token.type === 'text') {
|
|
630
|
+
if (!filtering && token.value === '/') {
|
|
631
|
+
state.configModal.activateFilter();
|
|
632
|
+
state.requestRender();
|
|
633
|
+
return true;
|
|
634
|
+
}
|
|
635
|
+
if (filtering) {
|
|
636
|
+
// The WHOLE token value lands in the filter in one call — including a
|
|
637
|
+
// multi-char paste token — never split into per-char nav/action
|
|
638
|
+
// dispatch (the text-capture invariant this item's brief calls out).
|
|
639
|
+
state.configModal.appendFilterText(token.value);
|
|
640
|
+
state.requestRender();
|
|
641
|
+
return true;
|
|
642
|
+
}
|
|
643
|
+
if (token.value === 'j') { state.configModal.moveDown(); state.requestRender(); return true; }
|
|
644
|
+
if (token.value === 'k') { state.configModal.moveUp(); state.requestRender(); return true; }
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
const actionKey = token.type === 'key' ? (token.logicalName ?? '') : token.type === 'text' ? token.value : '';
|
|
648
|
+
const submitInput = state.commandContext?.submitInput;
|
|
649
|
+
const fired = actionKey.length > 0 && state.configModal.fireAction(actionKey, {
|
|
650
|
+
print: (message: string) => state.commandContext?.print(message),
|
|
651
|
+
executeCommand: state.commandContext?.executeCommand,
|
|
652
|
+
openModal: state.commandContext?.openModal,
|
|
653
|
+
...(submitInput ? { submitInput: (text: string) => submitInput(text) } : {}),
|
|
654
|
+
});
|
|
655
|
+
if (fired) {
|
|
656
|
+
state.requestRender();
|
|
657
|
+
return true;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// Unhandled key while a config modal is open: drop any pending confirm and
|
|
661
|
+
// absorb the key (the modal owns the keyboard while it is active).
|
|
662
|
+
state.configModal.clearConfirmOnMiss();
|
|
663
|
+
state.requestRender();
|
|
664
|
+
return true;
|
|
665
|
+
}
|
|
@@ -40,12 +40,12 @@ export type EscapeState = ModalStackState & {
|
|
|
40
40
|
selectionCallback: ((result: SelectionResult | null) => void) | null;
|
|
41
41
|
bookmarkModal: ModalStackState['bookmarkModal'] & { open: () => void };
|
|
42
42
|
contextInspectorModal: ModalStackState['contextInspectorModal'] & { open: () => void };
|
|
43
|
-
processModal: ModalStackState['processModal'] & { open: () => void };
|
|
44
43
|
settingsModal: ModalStackState['settingsModal'] & {
|
|
45
44
|
editingMode: boolean;
|
|
46
45
|
cancelEdit: () => void;
|
|
47
46
|
};
|
|
48
47
|
mcpWorkspace?: ModalStackState['mcpWorkspace'];
|
|
48
|
+
configModal: ModalStackState['configModal'] & { reopen: () => void };
|
|
49
49
|
selectionModal: ModalStackState['selectionModal'];
|
|
50
50
|
autocompleteReset: () => void;
|
|
51
51
|
autocompleteUpdate?: (query: string) => void;
|
|
@@ -120,14 +120,12 @@ export function handleEscape(state: EscapeState): {
|
|
|
120
120
|
shortcutsScrollOffset = 0;
|
|
121
121
|
},
|
|
122
122
|
closeBookmark: () => state.bookmarkModal.close(),
|
|
123
|
-
closeAgentDetail: () => state.agentDetailModal.close(),
|
|
124
|
-
closeLiveTail: () => state.liveTailModal.close(),
|
|
125
123
|
closeSettings: () => state.settingsModal.close(),
|
|
126
124
|
closeMcpWorkspace: () => state.mcpWorkspace?.close(),
|
|
127
125
|
closeSessionPicker: () => state.sessionPickerModal.close(),
|
|
128
126
|
closeProfilePicker: () => state.profilePickerModal.close(),
|
|
127
|
+
closeConfigModal: () => state.configModal.close(),
|
|
129
128
|
closeContextInspector: () => state.contextInspectorModal.close(),
|
|
130
|
-
closeProcess: () => state.processModal.close(),
|
|
131
129
|
closeModelPicker: () => {
|
|
132
130
|
state.modelPicker.close();
|
|
133
131
|
state.restoreOnboardingModelPickerCancelState?.();
|
|
@@ -150,8 +148,17 @@ export function handleEscape(state: EscapeState): {
|
|
|
150
148
|
if (state.modalStack[i] === 'command') state.modalStack.splice(i, 1);
|
|
151
149
|
}
|
|
152
150
|
state.autocompleteReset();
|
|
153
|
-
|
|
154
|
-
|
|
151
|
+
// Esc's palette-dismiss convention: a bare '/' with nothing typed
|
|
152
|
+
// after it IS the palette, so Esc clears it along with the slash
|
|
153
|
+
// (first Esc, one step, done — no lingering '/' to backspace out).
|
|
154
|
+
// Once the user has typed a command name (or anything else) past
|
|
155
|
+
// the slash, Esc's job is only to dismiss the ghost-suggestion
|
|
156
|
+
// overlay; the typed text is real composer content and stays put,
|
|
157
|
+
// just no longer treated as an in-progress command.
|
|
158
|
+
if (prompt === '/') {
|
|
159
|
+
prompt = '';
|
|
160
|
+
cursorPos = 0;
|
|
161
|
+
}
|
|
155
162
|
},
|
|
156
163
|
});
|
|
157
164
|
};
|
|
@@ -161,8 +168,8 @@ export function handleEscape(state: EscapeState): {
|
|
|
161
168
|
openHelp: () => { helpOverlayActive = true; },
|
|
162
169
|
openShortcuts: () => { shortcutsOverlayActive = true; },
|
|
163
170
|
openBookmark: () => state.bookmarkModal.open(),
|
|
164
|
-
openProcess: () => state.processModal.open(),
|
|
165
171
|
openContextInspector: () => state.contextInspectorModal.open(),
|
|
172
|
+
openConfigModal: () => state.configModal.reopen(),
|
|
166
173
|
openMcpWorkspace: () => state.mcpWorkspace?.reopen(),
|
|
167
174
|
openOnboarding: () => state.onboardingWizard?.reopen(),
|
|
168
175
|
openCommandMode: () => {
|
|
@@ -5,13 +5,11 @@ import type { BookmarkModal } from './bookmark-modal.ts';
|
|
|
5
5
|
import type { SettingsModal } from './settings-modal.ts';
|
|
6
6
|
import type { SessionPickerModal } from './session-picker-modal.ts';
|
|
7
7
|
import type { ProfilePickerModal } from './profile-picker-modal.ts';
|
|
8
|
+
import type { ConfigModal } from './config-modal.ts';
|
|
8
9
|
import type { HistorySearch } from './input-history.ts';
|
|
9
10
|
import type { ModelPickerModal } from './model-picker.ts';
|
|
10
11
|
import { handleMcpWorkspaceToken, type McpWorkspace } from './mcp-workspace.ts';
|
|
11
12
|
import type { CommandContext } from './command-registry.ts';
|
|
12
|
-
import type { LiveTailModal } from '../renderer/live-tail-modal.ts';
|
|
13
|
-
import type { ProcessModal } from '../renderer/process-modal.ts';
|
|
14
|
-
import type { AgentDetailModal } from '../renderer/agent-detail-modal.ts';
|
|
15
13
|
import type { ContextInspectorModal } from '../renderer/context-inspector.ts';
|
|
16
14
|
import type { FilePickerModal } from './file-picker.ts';
|
|
17
15
|
import type { BlockActionsMenu, BlockActionId } from '../renderer/block-actions.ts';
|
|
@@ -21,6 +19,7 @@ import { handleHistorySearchToken, handleOverlayToken, handleSearchModeToken } f
|
|
|
21
19
|
import { handleOnboardingWizardToken } from './onboarding/handler-onboarding-routes.ts';
|
|
22
20
|
import {
|
|
23
21
|
handleBookmarkModalToken,
|
|
22
|
+
handleConfigModalToken,
|
|
24
23
|
handleProfilePickerToken,
|
|
25
24
|
handleSelectionModalToken,
|
|
26
25
|
handleSessionPickerToken,
|
|
@@ -30,9 +29,7 @@ import {
|
|
|
30
29
|
handleBlockActionsToken,
|
|
31
30
|
handleEscapeOnlyModalToken,
|
|
32
31
|
handleFilePickerToken,
|
|
33
|
-
handleLiveTailToken,
|
|
34
32
|
handleModelPickerToken,
|
|
35
|
-
handleProcessModalToken,
|
|
36
33
|
} from './handler-picker-routes.ts';
|
|
37
34
|
|
|
38
35
|
export type ModalTokenRouteState = {
|
|
@@ -47,6 +44,7 @@ export type ModalTokenRouteState = {
|
|
|
47
44
|
mcpWorkspace: McpWorkspace;
|
|
48
45
|
sessionPickerModal: SessionPickerModal;
|
|
49
46
|
profilePickerModal: ProfilePickerModal;
|
|
47
|
+
configModal: ConfigModal;
|
|
50
48
|
onboardingWizard: OnboardingWizardController;
|
|
51
49
|
helpOverlayActive: boolean;
|
|
52
50
|
helpScrollOffset: number;
|
|
@@ -61,9 +59,6 @@ export type ModalTokenRouteState = {
|
|
|
61
59
|
getViewportHeight: () => number;
|
|
62
60
|
requestRender: () => void;
|
|
63
61
|
handleEscape: () => void;
|
|
64
|
-
liveTailModal: LiveTailModal;
|
|
65
|
-
processModal: ProcessModal;
|
|
66
|
-
agentDetailModal: AgentDetailModal;
|
|
67
62
|
contextInspectorModal: ContextInspectorModal;
|
|
68
63
|
modalOpened: (name: string) => void;
|
|
69
64
|
filePicker: FilePickerModal;
|
|
@@ -177,6 +172,15 @@ export function handleModalTokenRoutes(state: ModalTokenRouteState, token: Input
|
|
|
177
172
|
return withState(state, true);
|
|
178
173
|
}
|
|
179
174
|
|
|
175
|
+
if (handleConfigModalToken({
|
|
176
|
+
configModal: state.configModal,
|
|
177
|
+
commandContext: state.commandContext,
|
|
178
|
+
requestRender: state.requestRender,
|
|
179
|
+
handleEscape: state.handleEscape,
|
|
180
|
+
}, token)) {
|
|
181
|
+
return withState(state, true);
|
|
182
|
+
}
|
|
183
|
+
|
|
180
184
|
const overlayState = {
|
|
181
185
|
helpOverlayActive: state.helpOverlayActive,
|
|
182
186
|
helpScrollOffset: state.helpScrollOffset,
|
|
@@ -223,36 +227,10 @@ export function handleModalTokenRoutes(state: ModalTokenRouteState, token: Input
|
|
|
223
227
|
return withState(state, true);
|
|
224
228
|
}
|
|
225
229
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
handleEscape: state.handleEscape,
|
|
231
|
-
}, token)) {
|
|
232
|
-
return withState(state, true);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// Agent detail modal: route c + confirm keys before escape-close.
|
|
236
|
-
// handleKey() consumes confirm-flow keys (y, Enter, n, Esc) and the 'c'
|
|
237
|
-
// initiator; unhandled keys (including Esc when no confirm is pending)
|
|
238
|
-
// fall through to escape-close below.
|
|
239
|
-
if (state.agentDetailModal.active) {
|
|
240
|
-
const keyStr: string =
|
|
241
|
-
token.type === 'key' ? (token.logicalName ?? '') :
|
|
242
|
-
token.type === 'text' ? token.value : '';
|
|
243
|
-
if (keyStr && state.agentDetailModal.handleKey(keyStr)) {
|
|
244
|
-
state.requestRender();
|
|
245
|
-
return withState(state, true);
|
|
246
|
-
}
|
|
247
|
-
// 'c' was not consumed (non-cancellable), or any other key.
|
|
248
|
-
// Esc closes the modal; all other keys are absorbed by the active modal.
|
|
249
|
-
if (token.type === 'key' && token.logicalName === 'escape') {
|
|
250
|
-
state.handleEscape();
|
|
251
|
-
return withState(state, true);
|
|
252
|
-
}
|
|
253
|
-
state.requestRender();
|
|
254
|
-
return withState(state, true);
|
|
255
|
-
}
|
|
230
|
+
// W6.1 retirement: the live-tail, agent-detail, and process-modal token
|
|
231
|
+
// routes were removed with those modals (F2 now opens Fleet, which subsumes
|
|
232
|
+
// the live process tree; the three modals were structurally unreachable after
|
|
233
|
+
// the F2 repoint and were deleted).
|
|
256
234
|
|
|
257
235
|
if (handleEscapeOnlyModalToken({
|
|
258
236
|
active: state.contextInspectorModal.active,
|
|
@@ -262,17 +240,6 @@ export function handleModalTokenRoutes(state: ModalTokenRouteState, token: Input
|
|
|
262
240
|
return withState(state, true);
|
|
263
241
|
}
|
|
264
242
|
|
|
265
|
-
if (handleProcessModalToken({
|
|
266
|
-
processModal: state.processModal,
|
|
267
|
-
liveTailModal: state.liveTailModal,
|
|
268
|
-
agentDetailModal: state.agentDetailModal,
|
|
269
|
-
modalOpened: state.modalOpened,
|
|
270
|
-
requestRender: state.requestRender,
|
|
271
|
-
handleEscape: state.handleEscape,
|
|
272
|
-
}, token)) {
|
|
273
|
-
return withState(state, true);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
243
|
const filePickerState = {
|
|
277
244
|
filePicker: state.filePicker,
|
|
278
245
|
prompt: state.prompt,
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* F1 (One-Platform Wave 2 friction fix): the onboarding wizard's "Connect to
|
|
3
|
+
* this daemon now" action, wired from the Network step's `network.daemon-source`
|
|
4
|
+
* = 'adopt' fields. Extracted from handler-onboarding.ts to keep that file
|
|
5
|
+
* under the architecture line cap.
|
|
6
|
+
*/
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
9
|
+
import { resolveDaemonCompanionToken } from '../runtime/operator-token-cleanup.ts';
|
|
10
|
+
import {
|
|
11
|
+
buildManagedDaemonServiceManager,
|
|
12
|
+
detectLegacyUnit,
|
|
13
|
+
resolveInstalledDaemonBinary,
|
|
14
|
+
runLegacyDaemonMigration,
|
|
15
|
+
MANAGED_SERVICE_NAME,
|
|
16
|
+
} from '../runtime/legacy-daemon-migration.ts';
|
|
17
|
+
import type { InputHandlerLike as InputHandler } from './handler-types.ts';
|
|
18
|
+
import {
|
|
19
|
+
formatRuntimeActiveSuccessMessage,
|
|
20
|
+
getRuntimeEndpointStatus,
|
|
21
|
+
isRuntimeEndpointActive,
|
|
22
|
+
runtimePortDiagnostic,
|
|
23
|
+
} from './onboarding/onboarding-runtime-status.ts';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Immediately (not deferred to the final wizard Apply):
|
|
27
|
+
* 1. Installs the pasted token into <homeDirectory>/.goodvibes/daemon/operator-tokens.json
|
|
28
|
+
* via `resolveDaemonCompanionToken` — the same durable-persistence logic
|
|
29
|
+
* the `GOODVIBES_DAEMON_TOKEN` non-interactive override uses.
|
|
30
|
+
* 2. Points `controlPlane.host`/`controlPlane.port` at the pasted host/port.
|
|
31
|
+
* 3. Restarts the external-services controller and reports back, honestly,
|
|
32
|
+
* whether the daemon at that host/port was actually adopted — reusing
|
|
33
|
+
* the same `onboarding-runtime-status.ts` helpers the normal
|
|
34
|
+
* start-a-new-daemon path uses to report success/failure after Apply.
|
|
35
|
+
*
|
|
36
|
+
* Feedback is printed to the conversation feed (matching the existing
|
|
37
|
+
* start-openai-subscription action's pattern) rather than stored as wizard
|
|
38
|
+
* field state, because a 'status' field's value is recomputed fresh from the
|
|
39
|
+
* step builder on every render and is not a place to durably stash an
|
|
40
|
+
* out-of-band result.
|
|
41
|
+
*/
|
|
42
|
+
export async function handleConnectExistingDaemonForHandler(handler: InputHandler): Promise<void> {
|
|
43
|
+
if (handler.onboardingApplyPending) return;
|
|
44
|
+
const host = handler.onboardingWizard.getStringFieldValue('network.adopt-daemon-host', '127.0.0.1').trim() || '127.0.0.1';
|
|
45
|
+
const port = handler.onboardingWizard.getPortFieldValue('network.adopt-daemon-port', 3421);
|
|
46
|
+
const token = handler.onboardingWizard.getStringFieldValue('network.adopt-daemon-token', '').trim();
|
|
47
|
+
if (token.length === 0) {
|
|
48
|
+
handler.commandContext?.print?.('Connect to existing daemon: paste that daemon\'s token first.');
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const externalServices = handler.uiServices.platform.externalServices;
|
|
53
|
+
if (!externalServices) {
|
|
54
|
+
handler.commandContext?.print?.('Connect to existing daemon: background service controller is unavailable in this build.');
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
handler.onboardingApplyPending = true;
|
|
59
|
+
handler.requestRender();
|
|
60
|
+
try {
|
|
61
|
+
const daemonHomeDir = join(handler.uiServices.environment.homeDirectory, '.goodvibes', 'daemon');
|
|
62
|
+
resolveDaemonCompanionToken(daemonHomeDir, token);
|
|
63
|
+
handler.uiServices.platform.configManager.setDynamic('controlPlane.host', host);
|
|
64
|
+
handler.uiServices.platform.configManager.setDynamic('controlPlane.port', port);
|
|
65
|
+
|
|
66
|
+
const state = await externalServices.restart();
|
|
67
|
+
const binding = { label: 'GoodVibes daemon', host, port };
|
|
68
|
+
if (isRuntimeEndpointActive(state, 'daemon')) {
|
|
69
|
+
handler.commandContext?.print?.([
|
|
70
|
+
'Connect to existing daemon: succeeded.',
|
|
71
|
+
` ${formatRuntimeActiveSuccessMessage('daemon', state)}`,
|
|
72
|
+
' This host/port and token are applied now; Apply at the end of the wizard will persist them.',
|
|
73
|
+
].join('\n'));
|
|
74
|
+
} else {
|
|
75
|
+
const status = getRuntimeEndpointStatus(state, 'daemon');
|
|
76
|
+
handler.commandContext?.print?.([
|
|
77
|
+
'Connect to existing daemon: could not verify a connection.',
|
|
78
|
+
` ${runtimePortDiagnostic(binding, undefined, status)}`,
|
|
79
|
+
' Check the host, port, and token, then try again.',
|
|
80
|
+
].join('\n'));
|
|
81
|
+
}
|
|
82
|
+
} catch (error) {
|
|
83
|
+
handler.commandContext?.print?.(`Connect to existing daemon failed: ${summarizeError(error)}`);
|
|
84
|
+
} finally {
|
|
85
|
+
handler.onboardingApplyPending = false;
|
|
86
|
+
handler.requestRender();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* W4-D1: the guided UX entry point for migrating a legacy
|
|
92
|
+
* `goodvibes-daemon.service` unit (Wave 3 shipped detect+disclose only; this
|
|
93
|
+
* closes that inheritance). This is a thin wrapper over
|
|
94
|
+
* `detectLegacyUnit` + `runLegacyDaemonMigration`
|
|
95
|
+
* (`../runtime/legacy-daemon-migration.ts`) — the actual migration mechanics
|
|
96
|
+
* (never auto-migrate, new-up-then-old-down with a health check before the
|
|
97
|
+
* legacy unit is touched, adopt-or-warn for an unrecognized process on the
|
|
98
|
+
* port) live there and are exercised against fakes in
|
|
99
|
+
* `src/test/daemon/service-commands.test.ts`; this function only builds the
|
|
100
|
+
* same `PlatformServiceManager` the daemon CLI's `migrate-service` subcommand
|
|
101
|
+
* builds (via the shared `buildManagedDaemonServiceManager`, so a migration
|
|
102
|
+
* triggered from either surface installs the identical unit) and prints the
|
|
103
|
+
* honest result lines to the conversation feed, mirroring
|
|
104
|
+
* `handleConnectExistingDaemonForHandler`'s pattern above.
|
|
105
|
+
*
|
|
106
|
+
* This lives in `src/input/` rather than importing
|
|
107
|
+
* `../daemon/service-commands.ts` directly because the architecture gate's
|
|
108
|
+
* `input-no-entrypoints` rule forbids `src/input/**` from depending on
|
|
109
|
+
* `src/daemon/**` (input must stay a pure event-handling layer) — the shared
|
|
110
|
+
* engine lives in the entrypoint-agnostic `src/runtime/` layer instead.
|
|
111
|
+
*
|
|
112
|
+
* `confirm` is caller-supplied rather than read from a fixed field name here
|
|
113
|
+
* so this stays reusable from any surface: the onboarding wizard's Network
|
|
114
|
+
* step (`onboarding-wizard-network-adopt.ts`'s `pushLegacyDaemonMigrationFields`,
|
|
115
|
+
* dispatched from `handler-onboarding.ts`'s `migrate-legacy-daemon-service`
|
|
116
|
+
* action) reads its own `network.migrate-legacy-daemon-confirm` checklist
|
|
117
|
+
* toggle and passes it through as this parameter — unchecked previews the
|
|
118
|
+
* dry-run plan (`confirm: false`), checked executes the real migration
|
|
119
|
+
* (`confirm: true`).
|
|
120
|
+
*/
|
|
121
|
+
export async function handleMigrateLegacyDaemonServiceForHandler(handler: InputHandler, confirm: boolean): Promise<void> {
|
|
122
|
+
if (handler.onboardingApplyPending) return;
|
|
123
|
+
|
|
124
|
+
handler.onboardingApplyPending = true;
|
|
125
|
+
handler.requestRender();
|
|
126
|
+
try {
|
|
127
|
+
const homeDirectory = handler.uiServices.environment.homeDirectory;
|
|
128
|
+
const configManager = handler.uiServices.platform.configManager;
|
|
129
|
+
const host = String(configManager.get('controlPlane.host') ?? '127.0.0.1');
|
|
130
|
+
const port = Number(configManager.get('controlPlane.port') ?? 3421);
|
|
131
|
+
const binaryPath = resolveInstalledDaemonBinary({ moduleUrl: import.meta.url });
|
|
132
|
+
|
|
133
|
+
const manager = buildManagedDaemonServiceManager({ binaryPath, homeDir: homeDirectory, host, port });
|
|
134
|
+
const legacy = detectLegacyUnit({ homeDir: homeDirectory });
|
|
135
|
+
const result = await runLegacyDaemonMigration(
|
|
136
|
+
{ host, port, trackedServiceName: MANAGED_SERVICE_NAME, confirmMigration: confirm },
|
|
137
|
+
manager,
|
|
138
|
+
legacy,
|
|
139
|
+
);
|
|
140
|
+
handler.commandContext?.print?.(
|
|
141
|
+
['Migrate legacy daemon service:', ...result.lines.map((line: string) => ` ${line}`)].join('\n'),
|
|
142
|
+
);
|
|
143
|
+
} catch (error) {
|
|
144
|
+
handler.commandContext?.print?.(`Migrate legacy daemon service failed: ${summarizeError(error)}`);
|
|
145
|
+
} finally {
|
|
146
|
+
handler.onboardingApplyPending = false;
|
|
147
|
+
handler.requestRender();
|
|
148
|
+
}
|
|
149
|
+
}
|