@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
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { OnboardingAcknowledgementTarget, OnboardingApplyOperation, OnboardingApplyRequest } from '../../runtime/onboarding/index.ts';
|
|
2
2
|
import { formatProviderModel } from '../../config/provider-model.ts';
|
|
3
3
|
import { getServerSurfaceFeatureFlags } from '../../runtime/surface-feature-flags.ts';
|
|
4
|
+
import { featureEnablementWrite } from '../../runtime/feature-settings.ts';
|
|
4
5
|
import {
|
|
5
6
|
buildCloudflareApiTokenRef,
|
|
6
7
|
buildCloudflareOperationalTokenRef,
|
|
@@ -17,13 +18,9 @@ import {
|
|
|
17
18
|
} from './onboarding-wizard-external-surfaces.ts';
|
|
18
19
|
import { applyFeatureUnitOperations } from './onboarding-feature-units.ts';
|
|
19
20
|
import { buildGoodVibesSecretKey, buildGoodVibesSecretRef, isLoopbackAddress, isSecretReferenceValue } from './onboarding-wizard-helpers.ts';
|
|
21
|
+
import { deriveProviderKeyCaptureTargets, providerKeyFieldId } from '../../runtime/onboarding/provider-key-capture.ts';
|
|
20
22
|
import type { OnboardingWizardControllerLike } from './onboarding-wizard-types.ts';
|
|
21
23
|
|
|
22
|
-
/** SDK schema default for behavior.hitlMode (schema-domain-core DEFAULT_CONFIG). */
|
|
23
|
-
const HITL_MODE_SCHEMA_DEFAULT = 'balanced';
|
|
24
|
-
/** Feature flag that gates behavior.hitlMode preset application in ModeManager. */
|
|
25
|
-
const HITL_UX_MODES_FEATURE_FLAG = 'hitl-ux-modes';
|
|
26
|
-
|
|
27
24
|
export function buildOnboardingApplyRequest(controller: OnboardingWizardControllerLike): OnboardingApplyRequest {
|
|
28
25
|
const operations: OnboardingApplyOperation[] = [];
|
|
29
26
|
// Accumulates feature-flag state that onboarding selections require, so the
|
|
@@ -132,21 +129,21 @@ export function buildOnboardingApplyRequest(controller: OnboardingWizardControll
|
|
|
132
129
|
setConfig('provider.reasoningEffort', controller.getStringFieldValue('default-model.reasoning', controller.runtimeSnapshot?.providerRouting.primaryReasoningEffort ?? 'medium'));
|
|
133
130
|
const hitlMode = controller.getStringFieldValue('experience.hitl', controller.runtimeSnapshot?.runtimeDefaults.behavior.hitlMode ?? 'balanced');
|
|
134
131
|
setConfig('behavior.hitlMode', hitlMode);
|
|
135
|
-
// behavior.hitlMode
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
// SDK schema default is 'balanced' (schema-domain-core DEFAULT_CONFIG); a
|
|
141
|
-
// choice equal to the default is left to the flag's own default-off state.
|
|
142
|
-
if (hitlMode !== HITL_MODE_SCHEMA_DEFAULT) {
|
|
143
|
-
featureFlagOverrides.set(HITL_UX_MODES_FEATURE_FLAG, 'enabled');
|
|
144
|
-
}
|
|
132
|
+
// behavior.hitlMode IS the enablement key of the hitl-ux-modes capability
|
|
133
|
+
// (active for quiet/balanced/operator, off only at 'off'), so the write
|
|
134
|
+
// above already carries the enablement — no separate gating write exists,
|
|
135
|
+
// and adding one here would clobber a non-default choice with the stock
|
|
136
|
+
// mode when the batch flushes.
|
|
145
137
|
setConfig('behavior.guidanceMode', controller.getStringFieldValue('experience.guidance', controller.runtimeSnapshot?.runtimeDefaults.behavior.guidanceMode ?? 'minimal'));
|
|
146
138
|
setConfig('permissions.mode', controller.getStringFieldValue('experience.permissions', controller.runtimeSnapshot?.runtimeDefaults.permissionsMode ?? 'prompt'));
|
|
147
139
|
setConfig('storage.secretPolicy', controller.getStringFieldValue('external-services.secret-policy', controller.runtimeSnapshot?.runtimeDefaults.secretStoragePolicy ?? 'preferred_secure'));
|
|
148
140
|
|
|
149
|
-
|
|
141
|
+
// Provider-agnostic key capture: store each entered key under the provider's
|
|
142
|
+
// own declared secret key. Empty fields are skipped (setSecret no-ops), so
|
|
143
|
+
// a blank field keeps any existing key untouched.
|
|
144
|
+
for (const target of deriveProviderKeyCaptureTargets(controller.runtimeSnapshot?.providerAccounts?.providers)) {
|
|
145
|
+
setSecret(target.apiKeyEnvVar, controller.getStringFieldValue(providerKeyFieldId(target.providerId), ''));
|
|
146
|
+
}
|
|
150
147
|
|
|
151
148
|
if (shouldShowCloudflareStep(controller)) {
|
|
152
149
|
addCloudflareOperations(controller, operations, setSecret);
|
|
@@ -190,12 +187,13 @@ export function buildOnboardingApplyRequest(controller: OnboardingWizardControll
|
|
|
190
187
|
}
|
|
191
188
|
}
|
|
192
189
|
// Feature units from the guided feature steps (safety, memory & context,
|
|
193
|
-
// telemetry, automation, provider, advanced): enable/disable each
|
|
194
|
-
// user changed from its default and write its chosen sub-options.
|
|
190
|
+
// telemetry, automation, provider, advanced): enable/disable each feature
|
|
191
|
+
// the user changed from its default and write its chosen sub-options.
|
|
195
192
|
applyFeatureUnitOperations(controller, setConfig, featureFlagOverrides);
|
|
196
193
|
|
|
197
|
-
// Surface/server
|
|
198
|
-
// own — apply them last so they win over any feature-step
|
|
194
|
+
// Surface/server capabilities are authoritative for the network-exposing
|
|
195
|
+
// features they own — apply them last so they win over any feature-step
|
|
196
|
+
// override.
|
|
199
197
|
for (const surfaceFlag of getServerSurfaceFeatureFlags({
|
|
200
198
|
serverBacked: hasServers,
|
|
201
199
|
web: browserAccess,
|
|
@@ -203,8 +201,11 @@ export function buildOnboardingApplyRequest(controller: OnboardingWizardControll
|
|
|
203
201
|
})) {
|
|
204
202
|
featureFlagOverrides.set(surfaceFlag, 'enabled');
|
|
205
203
|
}
|
|
204
|
+
// Each selection lands as a plain domain-settings write on the feature's
|
|
205
|
+
// real enablement key (e.g. sandbox.enabled, controlPlane.gateway).
|
|
206
206
|
for (const flagId of [...featureFlagOverrides.keys()].sort((left, right) => left.localeCompare(right))) {
|
|
207
|
-
|
|
207
|
+
const write = featureEnablementWrite(flagId, featureFlagOverrides.get(flagId)! === 'enabled');
|
|
208
|
+
if (write) setConfig(write.key, write.value);
|
|
208
209
|
}
|
|
209
210
|
|
|
210
211
|
acknowledge('providers', 'providers.reviewed');
|
|
@@ -282,6 +282,23 @@ const SURFACE_OVERLAYS: readonly SurfaceOverlay[] = [
|
|
|
282
282
|
{ configKey: 'surfaces.mattermost.defaultChannelId', label: 'Mattermost default channel ID', hint: 'Fallback Mattermost channel for outbound messages.', placeholder: 'channel id' },
|
|
283
283
|
],
|
|
284
284
|
},
|
|
285
|
+
{
|
|
286
|
+
onboardingId: 'telephony',
|
|
287
|
+
sdkSurface: 'telephony',
|
|
288
|
+
label: 'Telephony surface',
|
|
289
|
+
hint: 'Enable SMS/voice notification delivery — direct Twilio, or a self-hosted bridge.',
|
|
290
|
+
fields: [
|
|
291
|
+
{ configKey: 'surfaces.telephony.provider', label: 'Telephony provider', hint: 'twilio for direct Twilio delivery, bridge for a self-hosted telephony bridge.', placeholder: 'twilio' },
|
|
292
|
+
{ configKey: 'surfaces.telephony.mode', label: 'Telephony mode', hint: 'sms, voice (call with spoken text), or bridge.', placeholder: 'sms' },
|
|
293
|
+
{ configKey: 'surfaces.telephony.accountSid', label: 'Twilio account SID', hint: 'Account SID for direct Twilio delivery.', placeholder: 'AC...' },
|
|
294
|
+
{ configKey: 'surfaces.telephony.authToken', label: 'Twilio auth token', hint: 'Auth token for direct Twilio delivery.', placeholder: 'auth token' },
|
|
295
|
+
{ configKey: 'surfaces.telephony.fromNumber', label: 'From number', hint: 'The E.164 number outbound SMS/calls originate from.', placeholder: '+15551234567' },
|
|
296
|
+
{ configKey: 'surfaces.telephony.defaultRecipient', label: 'Default recipient', hint: 'Fallback E.164 number for outbound delivery.', placeholder: '+15557654321' },
|
|
297
|
+
{ configKey: 'surfaces.telephony.bridgeUrl', label: 'Bridge URL', hint: 'Base URL of the self-hosted telephony bridge (bridge provider only).', placeholder: 'https://telephony-bridge.example.test' },
|
|
298
|
+
{ configKey: 'surfaces.telephony.token', label: 'Bridge token', hint: 'Token used to authenticate with the telephony bridge.', placeholder: 'bridge token' },
|
|
299
|
+
{ configKey: 'surfaces.telephony.webhookSecret', label: 'Webhook secret', hint: 'Secret used to verify inbound telephony webhooks.', placeholder: 'webhook secret' },
|
|
300
|
+
],
|
|
301
|
+
},
|
|
285
302
|
{
|
|
286
303
|
onboardingId: 'matrix',
|
|
287
304
|
sdkSurface: 'matrix',
|
|
@@ -106,7 +106,7 @@ export function pushLegacyDaemonMigrationFields(
|
|
|
106
106
|
fields.push({
|
|
107
107
|
kind: 'status',
|
|
108
108
|
id: 'network.migrate-legacy-daemon-detected',
|
|
109
|
-
label: '
|
|
109
|
+
label: 'Separate install-script daemon service detected',
|
|
110
110
|
// Follow-up: name the unit this host actually resolves (carried on the
|
|
111
111
|
// snapshot from `resolveConfiguredServiceName` at collection time) — the
|
|
112
112
|
// MANAGED_SERVICE_NAME fallback only covers snapshots built without it.
|
|
@@ -126,7 +126,7 @@ export function pushLegacyDaemonMigrationFields(
|
|
|
126
126
|
fields.push({
|
|
127
127
|
kind: 'action',
|
|
128
128
|
id: 'network.migrate-legacy-daemon',
|
|
129
|
-
label: confirmed ? 'Migrate
|
|
129
|
+
label: confirmed ? 'Migrate the install-script daemon service now' : 'Preview migration plan',
|
|
130
130
|
hint: confirmed
|
|
131
131
|
? 'Executes the migration now: new-up-then-old-down, never touching the legacy unit until the new one is verified healthy. Result prints to the feed below.'
|
|
132
132
|
: 'Prints the migration plan without changing anything. Check "Confirm migration execution" above to arm the real migration.',
|
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
} from './onboarding-wizard-external-surfaces.ts';
|
|
11
11
|
import { buildFeatureUnitSteps } from './onboarding-feature-units.ts';
|
|
12
12
|
import { countSelected, modelSelectionLabel, normalizeText } from './onboarding-wizard-helpers.ts';
|
|
13
|
+
import { deriveProviderKeyCaptureTargets } from '../../runtime/onboarding/provider-key-capture.ts';
|
|
14
|
+
import { buildProviderKeyInventoryField, buildProviderKeyMaskedFields, buildSubscriptionPosture } from './onboarding-provider-key-fields.ts';
|
|
13
15
|
import { getDaemonSource, pushDaemonAdoptionFields, pushLegacyDaemonMigrationFields } from './onboarding-wizard-network-adopt.ts';
|
|
14
16
|
import type { OnboardingWizardControllerLike } from './onboarding-wizard-types.ts';
|
|
15
17
|
import type { OnboardingWizardAcknowledgementFieldDefinition, OnboardingWizardActionFieldDefinition, OnboardingWizardChecklistFieldDefinition, OnboardingWizardExternalSurfaceStepId, OnboardingWizardFieldDefinition, OnboardingWizardModelPickerFieldDefinition, OnboardingWizardRadioFieldDefinition, OnboardingWizardRadioOption, OnboardingWizardStepDefinition } from './onboarding-wizard-types.ts';
|
|
@@ -150,10 +152,20 @@ export function buildProvidersStep(controller: OnboardingWizardControllerLike):
|
|
|
150
152
|
const providerAck = controller.runtimeDerived.reopenEditAcknowledgements.providers;
|
|
151
153
|
const activeSubscriptions = controller.runtimeSnapshot?.subscriptions.active ?? [];
|
|
152
154
|
const pendingSubscriptions = controller.runtimeSnapshot?.subscriptions.pending ?? [];
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
const
|
|
155
|
+
// Provider-agnostic subscription posture: one status row per
|
|
156
|
+
// subscription-capable provider (registration truth), plus any provider with
|
|
157
|
+
// a live/pending session, and which provider's in-wizard sign-in applies now.
|
|
158
|
+
const subscription = buildSubscriptionPosture(
|
|
159
|
+
activeSubscriptions.map((entry) => entry.provider),
|
|
160
|
+
pendingSubscriptions.map((entry) => entry.provider),
|
|
161
|
+
controller.runtimeSnapshot?.providerAccounts?.providers,
|
|
162
|
+
);
|
|
163
|
+
const signInPending = subscription.signInPending;
|
|
164
|
+
// Provider-agnostic key capture: one field per provider the registry knows
|
|
165
|
+
// accepts an API key (derived from registration truth — never a hardcoded
|
|
166
|
+
// vendor list). No provider is named as the only option.
|
|
167
|
+
const keyTargets = deriveProviderKeyCaptureTargets(controller.runtimeSnapshot?.providerAccounts?.providers);
|
|
168
|
+
const configuredKeyCount = keyTargets.filter((target) => target.configured).length;
|
|
157
169
|
const providerReviewField: OnboardingWizardAcknowledgementFieldDefinition = {
|
|
158
170
|
kind: 'acknowledgement',
|
|
159
171
|
id: 'providers.reviewed',
|
|
@@ -166,46 +178,21 @@ export function buildProvidersStep(controller: OnboardingWizardControllerLike):
|
|
|
166
178
|
};
|
|
167
179
|
|
|
168
180
|
const fields: OnboardingWizardFieldDefinition[] = [
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
: openAiPending
|
|
176
|
-
? 'An OpenAI subscription login is pending.'
|
|
177
|
-
: 'No OpenAI subscription session was found in the current runtime state.',
|
|
178
|
-
defaultValue: openAiActive ? 'Active' : openAiPending ? 'Pending' : 'Not detected',
|
|
179
|
-
},
|
|
180
|
-
{
|
|
181
|
-
kind: 'status',
|
|
182
|
-
id: 'providers.api-key-inventory',
|
|
183
|
-
label: 'Provider API key inventory',
|
|
184
|
-
hint: providerSecretCount > 0
|
|
185
|
-
? `${providerSecretCount} provider credential reference(s) were found. Values stay masked.`
|
|
186
|
-
: 'No provider API key references were detected in the current runtime state.',
|
|
187
|
-
defaultValue: providerSecretCount > 0 ? `${providerSecretCount} configured` : 'None detected',
|
|
188
|
-
},
|
|
189
|
-
{
|
|
190
|
-
kind: 'masked',
|
|
191
|
-
id: 'providers.openai-api-key',
|
|
192
|
-
label: 'OpenAI API key',
|
|
193
|
-
hint: openAiApiKeyConfigured
|
|
194
|
-
? 'An OpenAI API key is already stored. Leave blank to keep it; enter a new key to replace it through the secret manager.'
|
|
195
|
-
: 'Optional: enter an OpenAI API key now. The value is stored through the secret manager, not in config.',
|
|
196
|
-
placeholder: openAiApiKeyConfigured ? 'already configured' : 'sk-...',
|
|
197
|
-
defaultValue: '',
|
|
198
|
-
},
|
|
199
|
-
...(openAiActive ? [] : [
|
|
181
|
+
...subscription.statusFields,
|
|
182
|
+
buildProviderKeyInventoryField(keyTargets),
|
|
183
|
+
// One masked field per key-accepting provider. Entered values are stored
|
|
184
|
+
// through the secret manager at apply and the provider re-registers live.
|
|
185
|
+
...buildProviderKeyMaskedFields(keyTargets),
|
|
186
|
+
...(!subscription.signInProviderId ? [] : [
|
|
200
187
|
{
|
|
201
188
|
kind: 'action' as const,
|
|
202
189
|
id: 'providers.openai-subscription-start',
|
|
203
190
|
action: 'start-openai-subscription' as const,
|
|
204
|
-
label:
|
|
191
|
+
label: signInPending ? 'Restart OpenAI subscription sign-in' : 'Start OpenAI subscription sign-in',
|
|
205
192
|
hint: 'Opens the OpenAI sign-in flow from the wizard and records pending login state here.',
|
|
206
|
-
defaultValue:
|
|
193
|
+
defaultValue: signInPending ? 'Restart' : 'Start',
|
|
207
194
|
},
|
|
208
|
-
...(
|
|
195
|
+
...(signInPending ? [
|
|
209
196
|
{
|
|
210
197
|
kind: 'text' as const,
|
|
211
198
|
id: 'providers.openai-authorization-url',
|
|
@@ -239,12 +226,13 @@ export function buildProvidersStep(controller: OnboardingWizardControllerLike):
|
|
|
239
226
|
id: 'provider-access',
|
|
240
227
|
title: 'AI provider access',
|
|
241
228
|
shortLabel: 'Providers',
|
|
242
|
-
description: 'Review subscription posture and optionally add an
|
|
229
|
+
description: 'Review subscription posture and optionally add an API key for any provider that accepts one, directly through the wizard.',
|
|
243
230
|
summaryTitle: 'Provider access summary',
|
|
244
231
|
summaryLines: [
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
232
|
+
subscription.summaryLine,
|
|
233
|
+
keyTargets.length === 0
|
|
234
|
+
? 'Provider API keys: no key-accepting providers registered'
|
|
235
|
+
: `Provider API keys: ${configuredKeyCount}/${keyTargets.length} configured`,
|
|
248
236
|
`Review: ${controller.getFieldValueLabel(providerReviewField)}`,
|
|
249
237
|
],
|
|
250
238
|
fields,
|
|
@@ -2,14 +2,14 @@ import type { CommandContext } from './command-registry.ts';
|
|
|
2
2
|
import type { Panel } from '../panels/types.ts';
|
|
3
3
|
import type { PanelManager } from '../panels/panel-manager.ts';
|
|
4
4
|
|
|
5
|
-
//
|
|
5
|
+
// (the purge): every instanceof-routed branch this file used to carry
|
|
6
6
|
// (FileExplorerPanel/FilePreviewPanel/SymbolOutlinePanel — DELETE;
|
|
7
7
|
// ApprovalPanel/TasksPanel/OrchestrationPanel/AgentInspectorPanel —
|
|
8
8
|
// RETIRE-INTO-FLEET) targeted a panel class that no longer exists. Panels
|
|
9
9
|
// migrated onto the formal `Panel.handlePanelIntegrationAction` hook (the
|
|
10
10
|
// preferred seam — see the comment below) are unaffected; this function now
|
|
11
11
|
// only provides that passthrough. See
|
|
12
|
-
// .goodvibes/audit/2026-07-04-wave6-briefs.json
|
|
12
|
+
// .goodvibes/audit/2026-07-04-wave6-briefs.json for the disposition
|
|
13
13
|
// map.
|
|
14
14
|
export function handlePanelIntegrationAction(
|
|
15
15
|
panelManager: PanelManager,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
// Pure mouse-geometry helpers for the panel workspace, extracted from
|
|
5
5
|
// handler-feed-routes.ts so that file stays under the 800-line architecture
|
|
6
|
-
// cap once the
|
|
6
|
+
// cap once the focus verbs and the paste-flood guard both live in
|
|
7
7
|
// it. These map a terminal (row, col) onto the panel/pane/workspace-tab under
|
|
8
8
|
// the cursor. No dependency on the feed-route state types, so no import cycle.
|
|
9
9
|
// ---------------------------------------------------------------------------
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
|
-
// panel-paste-flood-guard.ts —
|
|
2
|
+
// panel-paste-flood-guard.ts — item 5.
|
|
3
3
|
//
|
|
4
4
|
// A terminal WITHOUT bracketed paste delivers a pasted block as a burst of
|
|
5
5
|
// discrete 1-char 'text' tokens (isPasteToken stays false for every one of
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider key intake — capture a provider's API key at the moment the user
|
|
3
|
+
* selects an unconfigured provider or model, store it through the secrets
|
|
4
|
+
* manager (never env-only, never cleartext into config or provider JSON), let
|
|
5
|
+
* the provider re-register live, then complete the user's original selection.
|
|
6
|
+
*
|
|
7
|
+
* The decision of whether a key is needed is read from the provider's own
|
|
8
|
+
* registration-time auth state (describeAuthState / isConfigured) — never a
|
|
9
|
+
* hardcoded vendor list — so this works for any provider the registry knows
|
|
10
|
+
* accepts a key. The secrets-store key name is the provider's declared auth env
|
|
11
|
+
* var; it is used only as the storage key and is never shown to the user.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { LLMProvider } from '@pellux/goodvibes-sdk/platform/providers';
|
|
15
|
+
import type { ConcealedInputRequest } from './concealed-input.ts';
|
|
16
|
+
import type { CommandContext } from './command-registry.ts';
|
|
17
|
+
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
18
|
+
|
|
19
|
+
/** The intake decision derived purely from a provider's registration truth. */
|
|
20
|
+
export interface ProviderKeyIntakeDecision {
|
|
21
|
+
/** True when the provider accepts a key and does not yet have one. */
|
|
22
|
+
readonly needsKey: boolean;
|
|
23
|
+
/** Secrets-store key to write the value under (the provider's auth env var). */
|
|
24
|
+
readonly secretKey?: string;
|
|
25
|
+
/** True when the provider already has a credential (no prompt needed). */
|
|
26
|
+
readonly alreadyConfigured: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Decide whether to prompt for a key, reading only the provider's own auth
|
|
31
|
+
* state. A provider that is already configured, works anonymously, or declares
|
|
32
|
+
* no accepted env var is left alone (proceed straight through). A provider that
|
|
33
|
+
* does not declare an auth state at all is treated per isConfigured(), and an
|
|
34
|
+
* unknown state is assumed configured so a working selection is never nagged.
|
|
35
|
+
*/
|
|
36
|
+
export function decideProviderKeyIntake(provider: LLMProvider | undefined): ProviderKeyIntakeDecision {
|
|
37
|
+
if (!provider) return { needsKey: false, alreadyConfigured: false };
|
|
38
|
+
const state = provider.describeAuthState?.();
|
|
39
|
+
if (state) {
|
|
40
|
+
if (state.configured) return { needsKey: false, alreadyConfigured: true };
|
|
41
|
+
if (state.anonymousReady) return { needsKey: false, alreadyConfigured: false };
|
|
42
|
+
const secretKey = state.authEnvVars[0];
|
|
43
|
+
if (secretKey) return { needsKey: true, secretKey, alreadyConfigured: false };
|
|
44
|
+
return { needsKey: false, alreadyConfigured: false };
|
|
45
|
+
}
|
|
46
|
+
const configured = provider.isConfigured?.() ?? true;
|
|
47
|
+
return { needsKey: false, alreadyConfigured: configured };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Human-facing name for a provider — plain, vendor-neutral, never an env var. */
|
|
51
|
+
export function providerIntakeLabel(providerId: string): string {
|
|
52
|
+
return `the ${providerId} provider`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Dependencies the intake orchestrator needs, decoupled from the command context for testing. */
|
|
56
|
+
export interface ProviderKeyIntakeDeps {
|
|
57
|
+
/** The provider instance whose registration-time auth state drives the decision. */
|
|
58
|
+
readonly provider: LLMProvider | undefined;
|
|
59
|
+
/** Canonical secret store — the same path /secrets set writes through. */
|
|
60
|
+
readonly secretsManager?: {
|
|
61
|
+
set(key: string, value: string, options?: { scope?: 'project' | 'user'; medium?: 'secure' | 'plaintext' }): Promise<void>;
|
|
62
|
+
} | undefined;
|
|
63
|
+
/** Re-resolves credentials and re-registers providers live (no restart). */
|
|
64
|
+
readonly refreshProviderCredentials?: (() => Promise<void>) | undefined;
|
|
65
|
+
/** Begins one line of concealed composer input for the key. */
|
|
66
|
+
readonly beginConcealedInput?: ((request: ConcealedInputRequest) => void) | undefined;
|
|
67
|
+
/** Surfaces short status lines to the transcript. */
|
|
68
|
+
readonly print: (text: string) => void;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* If the selected provider needs a key, open the concealed prompt, store the
|
|
73
|
+
* pasted value through the secrets manager, re-register the provider live, then
|
|
74
|
+
* run `proceed` (the user's original selection). If no key is needed — or the
|
|
75
|
+
* concealed surface is unavailable — `proceed` runs immediately. `proceed`
|
|
76
|
+
* always runs exactly once, including when the user skips (empty/Escape), so a
|
|
77
|
+
* selection is never stranded.
|
|
78
|
+
*/
|
|
79
|
+
export function runProviderKeyIntake(
|
|
80
|
+
providerId: string,
|
|
81
|
+
deps: ProviderKeyIntakeDeps,
|
|
82
|
+
proceed: () => void,
|
|
83
|
+
): void {
|
|
84
|
+
const decision = decideProviderKeyIntake(deps.provider);
|
|
85
|
+
if (!decision.needsKey || !decision.secretKey) {
|
|
86
|
+
proceed();
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const { secretsManager, beginConcealedInput } = deps;
|
|
90
|
+
if (!secretsManager || !beginConcealedInput) {
|
|
91
|
+
// No canonical store or no concealed surface here — keep the selection
|
|
92
|
+
// rather than blocking it; the user can add the key via /secrets set later.
|
|
93
|
+
proceed();
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const secretKey = decision.secretKey;
|
|
97
|
+
const label = providerIntakeLabel(providerId);
|
|
98
|
+
let completed = false;
|
|
99
|
+
const finishOnce = (): void => {
|
|
100
|
+
if (completed) return;
|
|
101
|
+
completed = true;
|
|
102
|
+
proceed();
|
|
103
|
+
};
|
|
104
|
+
deps.print(`Enter the API key for ${label} (input is masked; press Enter to store, Esc to skip).`);
|
|
105
|
+
beginConcealedInput({
|
|
106
|
+
label: providerId,
|
|
107
|
+
onSubmit: (value) => {
|
|
108
|
+
if (value.length === 0) {
|
|
109
|
+
deps.print(`No key entered for ${label}; selection kept, no key stored.`);
|
|
110
|
+
finishOnce();
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
void (async () => {
|
|
114
|
+
try {
|
|
115
|
+
await secretsManager.set(secretKey, value, { scope: 'user', medium: 'secure' });
|
|
116
|
+
await deps.refreshProviderCredentials?.();
|
|
117
|
+
deps.print(`Stored the API key for ${label} and registered it live (value hidden). No restart needed.`);
|
|
118
|
+
} catch (error) {
|
|
119
|
+
deps.print(`Could not store the key for ${label}: ${summarizeError(error)}`);
|
|
120
|
+
}
|
|
121
|
+
finishOnce();
|
|
122
|
+
})();
|
|
123
|
+
},
|
|
124
|
+
onCancel: () => {
|
|
125
|
+
deps.print(`Key entry skipped for ${label}; selection kept, no key stored.`);
|
|
126
|
+
finishOnce();
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Command-context adapter for {@link runProviderKeyIntake}: resolves the live
|
|
133
|
+
* provider, secrets manager, live re-registration hook, and concealed-input
|
|
134
|
+
* surface from the shell command context, then runs the intake before
|
|
135
|
+
* completing the user's original selection (`proceed`).
|
|
136
|
+
*/
|
|
137
|
+
export function ensureProviderKeyThenSelect(
|
|
138
|
+
ctx: CommandContext,
|
|
139
|
+
providerId: string,
|
|
140
|
+
proceed: () => void,
|
|
141
|
+
): void {
|
|
142
|
+
// Resolve the live provider defensively: a command context that routes only
|
|
143
|
+
// through the provider API (no raw registry access) leaves the intake a no-op
|
|
144
|
+
// and the selection completes unchanged.
|
|
145
|
+
let registry: CommandContext['provider']['providerRegistry'] | undefined;
|
|
146
|
+
let provider: ReturnType<CommandContext['provider']['providerRegistry']['tryGet']> | undefined;
|
|
147
|
+
try {
|
|
148
|
+
registry = ctx.provider?.providerRegistry;
|
|
149
|
+
provider = registry?.tryGet?.(providerId);
|
|
150
|
+
} catch {
|
|
151
|
+
registry = undefined;
|
|
152
|
+
provider = undefined;
|
|
153
|
+
}
|
|
154
|
+
runProviderKeyIntake(
|
|
155
|
+
providerId,
|
|
156
|
+
{
|
|
157
|
+
provider,
|
|
158
|
+
secretsManager: ctx.platform?.secretsManager,
|
|
159
|
+
refreshProviderCredentials: registry
|
|
160
|
+
? () => registry!.refreshProviderCredentials()
|
|
161
|
+
: undefined,
|
|
162
|
+
beginConcealedInput: ctx.beginConcealedInput,
|
|
163
|
+
print: (text) => ctx.print(text),
|
|
164
|
+
},
|
|
165
|
+
proceed,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
@@ -59,14 +59,14 @@ export const SANDBOX_EGRESS_ALLOWLIST_SYNTHETIC_SETTING: ConfigSetting = {
|
|
|
59
59
|
key: SANDBOX_EGRESS_ALLOWLIST_CONFIG_KEY,
|
|
60
60
|
type: 'string',
|
|
61
61
|
default: [],
|
|
62
|
-
description: 'Comma-separated command base names (or "*" for all) whose network access is re-enabled inside the per-command exec sandbox boundary, as a named escalation the approval flow surfaces ("wants network"). Empty = the sandbox disables network for every command. Only takes effect when sandbox.enabled is on, the
|
|
62
|
+
description: 'Comma-separated command base names (or "*" for all) whose network access is re-enabled inside the per-command exec sandbox boundary, as a named escalation the approval flow surfaces ("wants network"). Empty = the sandbox disables network for every command. Only takes effect when sandbox.enabled is on, the exec-sandbox setting is on, and bubblewrap is available (Linux only — reports unavailable elsewhere).',
|
|
63
63
|
};
|
|
64
64
|
|
|
65
65
|
export const SANDBOX_WORKSPACE_WRITABLE_SYNTHETIC_SETTING: ConfigSetting = {
|
|
66
66
|
key: SANDBOX_WORKSPACE_WRITABLE_CONFIG_KEY,
|
|
67
67
|
type: 'string',
|
|
68
68
|
default: [],
|
|
69
|
-
description: 'Comma-separated absolute paths outside the workspace bound writable into the per-command exec sandbox boundary, as a named escalation the approval flow surfaces ("wants path outside workspace"). Empty = only the workspace (and an isolated /tmp) is writable. Only takes effect when sandbox.enabled is on, the
|
|
69
|
+
description: 'Comma-separated absolute paths outside the workspace bound writable into the per-command exec sandbox boundary, as a named escalation the approval flow surfaces ("wants path outside workspace"). Empty = only the workspace (and an isolated /tmp) is writable. Only takes effect when sandbox.enabled is on, the exec-sandbox setting is on, and bubblewrap is available (Linux only — reports unavailable elsewhere).',
|
|
70
70
|
};
|
|
71
71
|
|
|
72
72
|
function buildSandboxExecListEntry(configManager: Pick<ConfigManager, 'get'>, setting: ConfigSetting): SettingEntry {
|
|
@@ -37,7 +37,7 @@ export class SelectionModal {
|
|
|
37
37
|
public allowSearch = true;
|
|
38
38
|
public customActions: Map<string, SelectionAction> = new Map();
|
|
39
39
|
/**
|
|
40
|
-
*
|
|
40
|
+
* vocab unification: overrides the default per-primaryAction Enter
|
|
41
41
|
* verb ("Select"/"Toggle"/"Edit"/"Delete") shown in the footer hint. Used by
|
|
42
42
|
* callers whose items are really commands (e.g. /help) so the hint reads
|
|
43
43
|
* "[Enter] Run", matching the slash-command palette's own verb, instead of
|
|
@@ -92,6 +92,11 @@ export function activateSelected(ctx: ActivateSelectedContext): void {
|
|
|
92
92
|
const idx = setting.enumValues.indexOf(entry.currentValue as string);
|
|
93
93
|
const nextIdx = (idx + 1) % setting.enumValues.length;
|
|
94
94
|
ctx.setValue(setting.key as ConfigKey, setting.enumValues[nextIdx]);
|
|
95
|
+
} else if (setting.type === 'object') {
|
|
96
|
+
// Object-typed keys (e.g. pricing.modelPrices) edit as JSON: seed the
|
|
97
|
+
// buffer with the current value's JSON so the edit starts from truth.
|
|
98
|
+
ctx.setEditingMode(true);
|
|
99
|
+
ctx.setEditBuffer(JSON.stringify(entry.currentValue ?? {}));
|
|
95
100
|
} else if (setting.type === 'string' || setting.type === 'number') {
|
|
96
101
|
// Enter inline edit mode
|
|
97
102
|
ctx.setEditingMode(true);
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* settings-modal-adjustment — pure adjustment helpers for SettingsModal.
|
|
3
3
|
*
|
|
4
|
-
* These functions encapsulate the
|
|
4
|
+
* These functions encapsulate the directional-adjustment operation:
|
|
5
5
|
* - adjustSelected: cycle enum/boolean/number values via left/right arrow keys
|
|
6
|
-
* - toggleSelectedFlag: toggle a feature flag between enabled and disabled
|
|
7
6
|
*
|
|
8
7
|
* Each function takes its dependencies as explicit arguments rather than
|
|
9
8
|
* accessing class-level state directly, following the same pattern as
|
|
@@ -13,11 +12,9 @@
|
|
|
13
12
|
import type { ConfigKey } from '@pellux/goodvibes-sdk/platform/config';
|
|
14
13
|
import type { ConfigManager } from '@pellux/goodvibes-sdk/platform/config';
|
|
15
14
|
import type { FeatureFlagManager } from '@/runtime/index.ts';
|
|
16
|
-
import type { FlagState } from '@/runtime/index.ts';
|
|
17
15
|
import type { FlagEntry, McpEntry, SettingEntry } from './settings-modal-types.ts';
|
|
18
16
|
import { buildMcpEntries } from './settings-modal-data.ts';
|
|
19
17
|
import { getNumericAdjustmentMeta, roundToPrecision } from './settings-modal-behavior.ts';
|
|
20
|
-
import { applyFlagState } from './settings-modal-mutations.ts';
|
|
21
18
|
import type { McpRegistry } from '@pellux/goodvibes-sdk/platform/mcp';
|
|
22
19
|
|
|
23
20
|
// ---------------------------------------------------------------------------
|
|
@@ -45,10 +42,9 @@ export function adjustSelected(
|
|
|
45
42
|
): void {
|
|
46
43
|
if (ctx.editingMode) return;
|
|
47
44
|
|
|
48
|
-
// Feature-unit
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
// flag-toggle chokepoint. No category-special-case is needed here anymore.
|
|
45
|
+
// Feature-unit headers are the real config rows for their enablement keys
|
|
46
|
+
// (boolean or enum), so they fall through to the ordinary branches below —
|
|
47
|
+
// a plain config write the settings bridge forwards to the gate manager.
|
|
52
48
|
|
|
53
49
|
if (ctx.currentCategory === 'mcp') {
|
|
54
50
|
const entry = ctx.getSelectedMcp();
|
|
@@ -96,26 +92,3 @@ export function adjustSelected(
|
|
|
96
92
|
ctx.setValue(setting.key as ConfigKey, nextValue);
|
|
97
93
|
}
|
|
98
94
|
}
|
|
99
|
-
|
|
100
|
-
// ---------------------------------------------------------------------------
|
|
101
|
-
// toggleSelectedFlag
|
|
102
|
-
// ---------------------------------------------------------------------------
|
|
103
|
-
|
|
104
|
-
export interface ToggleSelectedFlagContext {
|
|
105
|
-
readonly featureFlagManager: FeatureFlagManager | null;
|
|
106
|
-
readonly configManager: ConfigManager | null;
|
|
107
|
-
getSelectedFlag(): FlagEntry | null;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export function toggleSelectedFlag(ctx: ToggleSelectedFlagContext): void {
|
|
111
|
-
const flagEntry = ctx.getSelectedFlag();
|
|
112
|
-
if (!flagEntry || !ctx.featureFlagManager || !ctx.configManager) return;
|
|
113
|
-
|
|
114
|
-
const { state } = flagEntry;
|
|
115
|
-
|
|
116
|
-
// Killed flags are blocked
|
|
117
|
-
if (state === 'killed') return;
|
|
118
|
-
|
|
119
|
-
const newState: FlagState = state === 'enabled' ? 'disabled' : 'enabled';
|
|
120
|
-
applyFlagState(flagEntry, newState, ctx.featureFlagManager, ctx.configManager);
|
|
121
|
-
}
|