@pellux/goodvibes-agent 0.1.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/.goodvibes/GOODVIBES.md +35 -0
- package/.goodvibes/agents/reviewer.md +48 -0
- package/.goodvibes/skills/add-provider/SKILL.md +199 -0
- package/CHANGELOG.md +25 -0
- package/README.md +74 -0
- package/bin/goodvibes-agent.ts +2 -0
- package/docs/README.md +23 -0
- package/docs/deployment-and-services.md +57 -0
- package/docs/getting-started.md +53 -0
- package/docs/release-and-publishing.md +46 -0
- package/package.json +134 -0
- package/scripts/check-bun.sh +20 -0
- package/src/audio/player.ts +156 -0
- package/src/audio/spoken-turn-controller.ts +203 -0
- package/src/audio/spoken-turn-model-routing.ts +117 -0
- package/src/audio/spoken-turn-wiring.ts +44 -0
- package/src/audio/text-chunker.ts +110 -0
- package/src/cli/bundle-command.ts +227 -0
- package/src/cli/completion.ts +90 -0
- package/src/cli/config-overrides.ts +159 -0
- package/src/cli/endpoints.ts +63 -0
- package/src/cli/entrypoint.ts +172 -0
- package/src/cli/help.ts +299 -0
- package/src/cli/index.ts +11 -0
- package/src/cli/management-commands.ts +426 -0
- package/src/cli/management.ts +744 -0
- package/src/cli/network-posture.ts +46 -0
- package/src/cli/package-verification.ts +123 -0
- package/src/cli/parser.ts +369 -0
- package/src/cli/provider-auth-routes.ts +22 -0
- package/src/cli/provider-classification.ts +107 -0
- package/src/cli/redaction.ts +105 -0
- package/src/cli/service-command.ts +26 -0
- package/src/cli/service-posture.ts +482 -0
- package/src/cli/status.ts +383 -0
- package/src/cli/surface-command.ts +247 -0
- package/src/cli/tui-startup.ts +32 -0
- package/src/cli/types.ts +69 -0
- package/src/cli-flags.ts +21 -0
- package/src/config/goodvibes-home-audit.ts +465 -0
- package/src/config/index.ts +57 -0
- package/src/config/provider-model.ts +23 -0
- package/src/config/secret-config.ts +119 -0
- package/src/config/secrets.ts +71 -0
- package/src/config/surface.ts +1 -0
- package/src/core/composer-state.ts +61 -0
- package/src/core/conversation-rendering.ts +359 -0
- package/src/core/conversation.ts +551 -0
- package/src/core/history.ts +45 -0
- package/src/core/orchestrator.ts +7 -0
- package/src/core/system-message-router.ts +171 -0
- package/src/daemon/cli.ts +55 -0
- package/src/daemon/safe-serve.ts +61 -0
- package/src/input/agent-workspace.ts +428 -0
- package/src/input/autocomplete.ts +96 -0
- package/src/input/bookmark-modal.ts +115 -0
- package/src/input/command-args-hint.ts +36 -0
- package/src/input/command-registry.ts +329 -0
- package/src/input/commands/agent-externalized-tui.ts +73 -0
- package/src/input/commands/agent-workspace-runtime.ts +17 -0
- package/src/input/commands/branch-runtime.ts +72 -0
- package/src/input/commands/cloudflare-runtime.ts +370 -0
- package/src/input/commands/config.ts +18 -0
- package/src/input/commands/control-room-runtime.ts +255 -0
- package/src/input/commands/conversation-runtime.ts +207 -0
- package/src/input/commands/discovery-runtime.ts +52 -0
- package/src/input/commands/eval.ts +204 -0
- package/src/input/commands/experience-runtime.ts +278 -0
- package/src/input/commands/guidance-runtime.ts +106 -0
- package/src/input/commands/health-runtime.ts +434 -0
- package/src/input/commands/hooks-runtime.ts +148 -0
- package/src/input/commands/incident-runtime.ts +95 -0
- package/src/input/commands/integration-runtime.ts +394 -0
- package/src/input/commands/intelligence-runtime.ts +223 -0
- package/src/input/commands/knowledge.ts +531 -0
- package/src/input/commands/local-auth-runtime.ts +105 -0
- package/src/input/commands/local-provider-runtime.ts +170 -0
- package/src/input/commands/local-runtime.ts +392 -0
- package/src/input/commands/local-setup-review.ts +199 -0
- package/src/input/commands/local-setup-transfer.ts +135 -0
- package/src/input/commands/local-setup.ts +282 -0
- package/src/input/commands/managed-runtime.ts +209 -0
- package/src/input/commands/marketplace-runtime.ts +290 -0
- package/src/input/commands/mcp-runtime.ts +432 -0
- package/src/input/commands/memory-product-runtime.ts +111 -0
- package/src/input/commands/memory.ts +151 -0
- package/src/input/commands/notify-runtime.ts +83 -0
- package/src/input/commands/onboarding-runtime.ts +14 -0
- package/src/input/commands/operator-panel-runtime.ts +146 -0
- package/src/input/commands/operator-runtime.ts +392 -0
- package/src/input/commands/planning-runtime.ts +205 -0
- package/src/input/commands/platform-access-runtime.ts +422 -0
- package/src/input/commands/platform-services-runtime.ts +246 -0
- package/src/input/commands/policy-dispatch.ts +339 -0
- package/src/input/commands/policy.ts +17 -0
- package/src/input/commands/product-runtime.ts +351 -0
- package/src/input/commands/profile-sync-runtime.ts +99 -0
- package/src/input/commands/provider-accounts-runtime.ts +113 -0
- package/src/input/commands/provider.ts +363 -0
- package/src/input/commands/qrcode-runtime.ts +20 -0
- package/src/input/commands/quit-shared.ts +162 -0
- package/src/input/commands/recall-bundle.ts +132 -0
- package/src/input/commands/recall-capture.ts +152 -0
- package/src/input/commands/recall-query.ts +229 -0
- package/src/input/commands/recall-review.ts +98 -0
- package/src/input/commands/recall-shared.ts +22 -0
- package/src/input/commands/remote-runtime-pool.ts +106 -0
- package/src/input/commands/remote-runtime-setup.ts +199 -0
- package/src/input/commands/remote-runtime.ts +431 -0
- package/src/input/commands/replay-runtime.ts +18 -0
- package/src/input/commands/runtime-services.ts +291 -0
- package/src/input/commands/schedule-runtime.ts +91 -0
- package/src/input/commands/services-runtime.ts +209 -0
- package/src/input/commands/session-content.ts +408 -0
- package/src/input/commands/session-workflow.ts +464 -0
- package/src/input/commands/session.ts +375 -0
- package/src/input/commands/settings-sync-runtime.ts +174 -0
- package/src/input/commands/share-runtime.ts +119 -0
- package/src/input/commands/shell-core.ts +307 -0
- package/src/input/commands/skills-runtime.ts +221 -0
- package/src/input/commands/subscription-runtime.ts +434 -0
- package/src/input/commands/tasks-runtime.ts +230 -0
- package/src/input/commands/teamwork-runtime.ts +339 -0
- package/src/input/commands/teleport-runtime.ts +57 -0
- package/src/input/commands/tts-runtime.ts +29 -0
- package/src/input/commands/work-plan-runtime.ts +169 -0
- package/src/input/commands.ts +131 -0
- package/src/input/feed-context-factory.ts +254 -0
- package/src/input/file-picker.ts +192 -0
- package/src/input/handler-command-route.ts +180 -0
- package/src/input/handler-content-actions.ts +497 -0
- package/src/input/handler-feed-routes.ts +648 -0
- package/src/input/handler-feed.ts +452 -0
- package/src/input/handler-interactions.ts +281 -0
- package/src/input/handler-modal-routes.ts +418 -0
- package/src/input/handler-modal-stack.ts +263 -0
- package/src/input/handler-modal-token-routes.ts +329 -0
- package/src/input/handler-onboarding-cloudflare.ts +391 -0
- package/src/input/handler-onboarding.ts +620 -0
- package/src/input/handler-picker-routes.ts +472 -0
- package/src/input/handler-prompt-buffer.ts +320 -0
- package/src/input/handler-shortcuts.ts +213 -0
- package/src/input/handler-ui-state.ts +372 -0
- package/src/input/handler.ts +729 -0
- package/src/input/input-history.ts +297 -0
- package/src/input/keybindings.ts +292 -0
- package/src/input/mcp-workspace.ts +554 -0
- package/src/input/model-picker-provider-filter.ts +28 -0
- package/src/input/model-picker-types.ts +137 -0
- package/src/input/model-picker.ts +797 -0
- package/src/input/onboarding/handler-onboarding-routes.ts +125 -0
- package/src/input/onboarding/onboarding-runtime-status.ts +87 -0
- package/src/input/onboarding/onboarding-wizard-apply.ts +277 -0
- package/src/input/onboarding/onboarding-wizard-cloudflare-step.ts +494 -0
- package/src/input/onboarding/onboarding-wizard-cloudflare.ts +204 -0
- package/src/input/onboarding/onboarding-wizard-constants.ts +158 -0
- package/src/input/onboarding/onboarding-wizard-external-surface-extra-specs.ts +130 -0
- package/src/input/onboarding/onboarding-wizard-external-surfaces.ts +762 -0
- package/src/input/onboarding/onboarding-wizard-helpers.ts +167 -0
- package/src/input/onboarding/onboarding-wizard-rules.ts +256 -0
- package/src/input/onboarding/onboarding-wizard-state.ts +365 -0
- package/src/input/onboarding/onboarding-wizard-steps.ts +798 -0
- package/src/input/onboarding/onboarding-wizard-types.ts +195 -0
- package/src/input/onboarding/onboarding-wizard.ts +711 -0
- package/src/input/panel-integration-actions.ts +78 -0
- package/src/input/profile-picker-modal.ts +222 -0
- package/src/input/search.ts +100 -0
- package/src/input/selection-modal.ts +163 -0
- package/src/input/selection.ts +135 -0
- package/src/input/session-picker-modal.ts +136 -0
- package/src/input/settings-modal-behavior.ts +37 -0
- package/src/input/settings-modal-secrets.ts +41 -0
- package/src/input/settings-modal-subscriptions.ts +95 -0
- package/src/input/settings-modal-types.ts +91 -0
- package/src/input/settings-modal.ts +793 -0
- package/src/input/submission-intent.ts +17 -0
- package/src/input/submission-router.ts +59 -0
- package/src/input/tts-settings-actions.ts +100 -0
- package/src/main.ts +792 -0
- package/src/mcp/runtime-reload.ts +81 -0
- package/src/panels/agent-inspector-panel.ts +521 -0
- package/src/panels/agent-inspector-shared.ts +94 -0
- package/src/panels/agent-logs-panel.ts +559 -0
- package/src/panels/agent-logs-shared.ts +129 -0
- package/src/panels/approval-panel.ts +150 -0
- package/src/panels/automation-control-panel.ts +212 -0
- package/src/panels/base-panel.ts +254 -0
- package/src/panels/builtin/agent.ts +117 -0
- package/src/panels/builtin/development.ts +31 -0
- package/src/panels/builtin/knowledge.ts +26 -0
- package/src/panels/builtin/operations.ts +349 -0
- package/src/panels/builtin/session.ts +129 -0
- package/src/panels/builtin/shared.ts +274 -0
- package/src/panels/builtin-panels.ts +23 -0
- package/src/panels/cockpit-panel.ts +183 -0
- package/src/panels/communication-panel.ts +153 -0
- package/src/panels/confirm-state.ts +61 -0
- package/src/panels/context-visualizer-panel.ts +204 -0
- package/src/panels/control-plane-panel.ts +211 -0
- package/src/panels/cost-tracker-panel.ts +444 -0
- package/src/panels/debug-panel.ts +432 -0
- package/src/panels/diff-panel.ts +520 -0
- package/src/panels/docs-panel.ts +283 -0
- package/src/panels/eval-panel.ts +399 -0
- package/src/panels/file-explorer-panel.ts +584 -0
- package/src/panels/file-preview-panel.ts +434 -0
- package/src/panels/forensics-panel.ts +364 -0
- package/src/panels/git-panel.ts +638 -0
- package/src/panels/hooks-panel.ts +239 -0
- package/src/panels/incident-review-panel.ts +197 -0
- package/src/panels/index.ts +46 -0
- package/src/panels/intelligence-panel.ts +176 -0
- package/src/panels/knowledge-panel.ts +345 -0
- package/src/panels/local-auth-panel.ts +130 -0
- package/src/panels/marketplace-panel.ts +212 -0
- package/src/panels/memory-panel.ts +225 -0
- package/src/panels/ops-control-panel.ts +150 -0
- package/src/panels/ops-strategy-panel.ts +235 -0
- package/src/panels/orchestration-panel.ts +273 -0
- package/src/panels/panel-list-panel.ts +509 -0
- package/src/panels/panel-manager.ts +570 -0
- package/src/panels/panel-picker.ts +106 -0
- package/src/panels/plan-dashboard-panel.ts +274 -0
- package/src/panels/plugins-panel.ts +178 -0
- package/src/panels/policy-panel.ts +308 -0
- package/src/panels/polish.ts +717 -0
- package/src/panels/project-planning-panel.ts +711 -0
- package/src/panels/provider-account-snapshot.ts +259 -0
- package/src/panels/provider-accounts-panel.ts +218 -0
- package/src/panels/provider-health-domains.ts +215 -0
- package/src/panels/provider-health-panel.ts +727 -0
- package/src/panels/provider-health-tracker.ts +115 -0
- package/src/panels/provider-stats-panel.ts +366 -0
- package/src/panels/qr-panel.ts +182 -0
- package/src/panels/remote-panel.ts +449 -0
- package/src/panels/routes-panel.ts +178 -0
- package/src/panels/sandbox-panel.ts +283 -0
- package/src/panels/schedule-panel.ts +329 -0
- package/src/panels/scrollable-list-panel.ts +491 -0
- package/src/panels/search-focus.ts +32 -0
- package/src/panels/security-panel.ts +295 -0
- package/src/panels/services-panel.ts +231 -0
- package/src/panels/session-browser-panel.ts +400 -0
- package/src/panels/session-maintenance.ts +125 -0
- package/src/panels/settings-sync-panel.ts +120 -0
- package/src/panels/skills-panel.ts +431 -0
- package/src/panels/subscription-panel.ts +263 -0
- package/src/panels/symbol-outline-panel.ts +486 -0
- package/src/panels/system-messages-panel.ts +230 -0
- package/src/panels/tasks-panel.ts +399 -0
- package/src/panels/thinking-panel.ts +304 -0
- package/src/panels/token-budget-panel.ts +475 -0
- package/src/panels/tool-inspector-panel.ts +429 -0
- package/src/panels/types.ts +54 -0
- package/src/panels/watchers-panel.ts +193 -0
- package/src/panels/work-plan-panel.ts +175 -0
- package/src/panels/worktree-panel.ts +182 -0
- package/src/panels/wrfc-panel.ts +609 -0
- package/src/permissions/prompt.ts +165 -0
- package/src/planning/project-planning-coordinator.ts +543 -0
- package/src/plugins/loader.ts +15 -0
- package/src/renderer/agent-detail-modal.ts +331 -0
- package/src/renderer/agent-workspace.ts +238 -0
- package/src/renderer/ansi-sanitize.ts +76 -0
- package/src/renderer/autocomplete-overlay.ts +154 -0
- package/src/renderer/block-actions.ts +76 -0
- package/src/renderer/bookmark-modal.ts +101 -0
- package/src/renderer/bottom-bar.ts +58 -0
- package/src/renderer/buffer.ts +113 -0
- package/src/renderer/code-block.ts +373 -0
- package/src/renderer/compositor.ts +283 -0
- package/src/renderer/context-inspector.ts +219 -0
- package/src/renderer/conversation-layout.ts +67 -0
- package/src/renderer/conversation-overlays.ts +140 -0
- package/src/renderer/conversation-surface.ts +260 -0
- package/src/renderer/diff-view.ts +132 -0
- package/src/renderer/diff.ts +130 -0
- package/src/renderer/file-picker-overlay.ts +101 -0
- package/src/renderer/file-tree.ts +153 -0
- package/src/renderer/fullscreen-primitives.ts +130 -0
- package/src/renderer/fullscreen-workspace.ts +199 -0
- package/src/renderer/git-status.ts +89 -0
- package/src/renderer/help-overlay.ts +267 -0
- package/src/renderer/history-search-overlay.ts +73 -0
- package/src/renderer/layout-engine.ts +97 -0
- package/src/renderer/layout.ts +32 -0
- package/src/renderer/live-tail-modal.ts +156 -0
- package/src/renderer/markdown.ts +635 -0
- package/src/renderer/mcp-workspace.ts +237 -0
- package/src/renderer/modal-factory.ts +467 -0
- package/src/renderer/modal-utils.ts +24 -0
- package/src/renderer/model-picker-overlay.ts +473 -0
- package/src/renderer/model-workspace.ts +488 -0
- package/src/renderer/onboarding/onboarding-wizard.ts +615 -0
- package/src/renderer/overlay-box.ts +146 -0
- package/src/renderer/overlay-viewport.ts +104 -0
- package/src/renderer/panel-composite.ts +158 -0
- package/src/renderer/panel-picker-overlay.ts +202 -0
- package/src/renderer/panel-tab-bar.ts +69 -0
- package/src/renderer/panel-workspace-bar.ts +42 -0
- package/src/renderer/process-indicator.ts +96 -0
- package/src/renderer/process-modal.ts +656 -0
- package/src/renderer/process-summary.ts +67 -0
- package/src/renderer/profile-picker-modal.ts +129 -0
- package/src/renderer/progress.ts +98 -0
- package/src/renderer/qr-renderer.ts +120 -0
- package/src/renderer/search-overlay.ts +54 -0
- package/src/renderer/selection-modal-overlay.ts +214 -0
- package/src/renderer/semantic-diff.ts +369 -0
- package/src/renderer/session-picker-modal.ts +127 -0
- package/src/renderer/settings-modal-helpers.ts +193 -0
- package/src/renderer/settings-modal.ts +537 -0
- package/src/renderer/shell-surface.ts +88 -0
- package/src/renderer/status-glyphs.ts +21 -0
- package/src/renderer/status-token.ts +67 -0
- package/src/renderer/surface-layout.ts +101 -0
- package/src/renderer/syntax-highlighter.ts +542 -0
- package/src/renderer/system-message.ts +83 -0
- package/src/renderer/tab-strip.ts +108 -0
- package/src/renderer/text-layout.ts +31 -0
- package/src/renderer/thinking.ts +17 -0
- package/src/renderer/tool-call.ts +234 -0
- package/src/renderer/ui-factory.ts +524 -0
- package/src/renderer/ui-primitives.ts +96 -0
- package/src/runtime/bootstrap-command-context.ts +278 -0
- package/src/runtime/bootstrap-command-parts.ts +386 -0
- package/src/runtime/bootstrap-core.ts +540 -0
- package/src/runtime/bootstrap-hook-bridge.ts +112 -0
- package/src/runtime/bootstrap-shell.ts +283 -0
- package/src/runtime/bootstrap.ts +575 -0
- package/src/runtime/cloudflare-control-plane.ts +349 -0
- package/src/runtime/context.ts +142 -0
- package/src/runtime/diagnostics/panels/index.ts +24 -0
- package/src/runtime/diagnostics/panels/ops.ts +156 -0
- package/src/runtime/diagnostics/panels/panel-resources.ts +118 -0
- package/src/runtime/diagnostics/panels/policy.ts +177 -0
- package/src/runtime/index.ts +662 -0
- package/src/runtime/onboarding/apply.ts +642 -0
- package/src/runtime/onboarding/derivation.ts +534 -0
- package/src/runtime/onboarding/index.ts +7 -0
- package/src/runtime/onboarding/markers.ts +148 -0
- package/src/runtime/onboarding/snapshot.ts +406 -0
- package/src/runtime/onboarding/state.ts +141 -0
- package/src/runtime/onboarding/types.ts +404 -0
- package/src/runtime/onboarding/verify.ts +171 -0
- package/src/runtime/operator-token-cleanup.ts +27 -0
- package/src/runtime/perf/panel-contracts.ts +32 -0
- package/src/runtime/perf/panel-health-monitor.ts +18 -0
- package/src/runtime/sandbox-public-gaps.ts +358 -0
- package/src/runtime/services.ts +670 -0
- package/src/runtime/store/domains/domain-read-matrix.ts +15 -0
- package/src/runtime/store/domains/index.ts +222 -0
- package/src/runtime/store/domains/panels.ts +117 -0
- package/src/runtime/store/domains/ui-perf.ts +103 -0
- package/src/runtime/store/index.ts +305 -0
- package/src/runtime/store/selectors/index.ts +359 -0
- package/src/runtime/store/state.ts +145 -0
- package/src/runtime/surface-feature-flags.ts +65 -0
- package/src/runtime/terminal-output-guard.ts +228 -0
- package/src/runtime/ui/index.ts +39 -0
- package/src/runtime/ui/model-picker/data-provider.ts +182 -0
- package/src/runtime/ui/model-picker/health-enrichment.ts +228 -0
- package/src/runtime/ui/model-picker/index.ts +59 -0
- package/src/runtime/ui/model-picker/types.ts +149 -0
- package/src/runtime/ui/provider-health/data-provider.ts +244 -0
- package/src/runtime/ui/provider-health/fallback-visualizer.ts +71 -0
- package/src/runtime/ui/provider-health/index.ts +46 -0
- package/src/runtime/ui/provider-health/types.ts +146 -0
- package/src/runtime/ui-events.ts +1 -0
- package/src/runtime/ui-read-model-helpers.ts +1 -0
- package/src/runtime/ui-read-models-observability-maintenance.ts +1 -0
- package/src/runtime/ui-read-models-observability-options.ts +1 -0
- package/src/runtime/ui-read-models-observability-remote.ts +1 -0
- package/src/runtime/ui-read-models-observability-security.ts +1 -0
- package/src/runtime/ui-read-models-observability-system.ts +1 -0
- package/src/runtime/ui-read-models-observability.ts +1 -0
- package/src/runtime/ui-read-models.ts +61 -0
- package/src/runtime/ui-service-queries.ts +1 -0
- package/src/runtime/ui-services.ts +190 -0
- package/src/scripts/process-messages.ts +42 -0
- package/src/shell/blocking-input.ts +98 -0
- package/src/shell/service-settings-sync.ts +273 -0
- package/src/shell/ui-openers.ts +352 -0
- package/src/tools/index.ts +1 -0
- package/src/tools/wrfc-agent-guard.ts +49 -0
- package/src/types/grid.ts +48 -0
- package/src/types/sql-js.d.ts +15 -0
- package/src/utils/clipboard.ts +22 -0
- package/src/utils/splash-lines.ts +46 -0
- package/src/utils/terminal-width.ts +185 -0
- package/src/verification/live-verifier.ts +430 -0
- package/src/verification/verification-ledger.ts +242 -0
- package/src/version.ts +17 -0
- package/src/widget/index.ts +2 -0
- package/src/widget/types.ts +9 -0
- package/src/widget/widget.ts +8 -0
- package/src/work-plans/work-plan-store.ts +374 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { mkdirSync } from 'node:fs';
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import type { ConfigKey } from '@pellux/goodvibes-sdk/platform/config';
|
|
4
|
+
import type { ManagedServiceStatus } from '@pellux/goodvibes-sdk/platform/daemon';
|
|
5
|
+
import { logger, summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
6
|
+
import {
|
|
7
|
+
createPlatformServiceManager,
|
|
8
|
+
getServiceStateRoot,
|
|
9
|
+
type CliServiceRuntime,
|
|
10
|
+
} from '../cli/service-posture.ts';
|
|
11
|
+
|
|
12
|
+
type ManagedServiceAction = 'install' | 'uninstall' | 'start' | 'stop' | 'restart' | 'status';
|
|
13
|
+
|
|
14
|
+
export interface ServiceManagerLike {
|
|
15
|
+
status(): ManagedServiceStatus;
|
|
16
|
+
install(): ManagedServiceStatus;
|
|
17
|
+
uninstall(): ManagedServiceStatus;
|
|
18
|
+
start(): ManagedServiceStatus;
|
|
19
|
+
stop(): ManagedServiceStatus;
|
|
20
|
+
restart(): ManagedServiceStatus;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface CommandResult {
|
|
24
|
+
readonly status: number | null;
|
|
25
|
+
readonly stdout?: string;
|
|
26
|
+
readonly stderr?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ServiceSettingsSyncChange {
|
|
30
|
+
readonly key: ConfigKey;
|
|
31
|
+
readonly previousValue: unknown;
|
|
32
|
+
readonly value: unknown;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface ServiceSettingsSyncResult {
|
|
36
|
+
readonly handled: boolean;
|
|
37
|
+
readonly action?: ManagedServiceAction | 'install-start' | 'disable';
|
|
38
|
+
readonly status?: ManagedServiceStatus;
|
|
39
|
+
readonly message?: string;
|
|
40
|
+
readonly error?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface ServiceSettingsSyncOptions {
|
|
44
|
+
readonly createManager?: (runtime: CliServiceRuntime) => ServiceManagerLike;
|
|
45
|
+
readonly runCommand?: (command: string, args: readonly string[]) => CommandResult;
|
|
46
|
+
readonly mkdir?: typeof mkdirSync;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const SERVICE_DEFINITION_KEYS = new Set<ConfigKey>([
|
|
50
|
+
'service.restartOnFailure',
|
|
51
|
+
'service.platform',
|
|
52
|
+
'service.serviceName',
|
|
53
|
+
'service.logPath',
|
|
54
|
+
] as ConfigKey[]);
|
|
55
|
+
|
|
56
|
+
function runCommand(command: string, args: readonly string[], options: ServiceSettingsSyncOptions): CommandResult {
|
|
57
|
+
if (options.runCommand) return options.runCommand(command, args);
|
|
58
|
+
return spawnSync(command, [...args], { stdio: 'pipe', encoding: 'utf-8' });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function commandError(result: CommandResult): string | null {
|
|
62
|
+
if ((result.status ?? 1) === 0) return null;
|
|
63
|
+
return ((result.stderr ?? '') || (result.stdout ?? '') || `command exited with ${result.status}`).trim();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function serviceName(runtime: CliServiceRuntime, fallback = 'goodvibes'): string {
|
|
67
|
+
return String(runtime.configManager.get('service.serviceName') ?? fallback).trim() || fallback;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function runSystemd(runtime: CliServiceRuntime, args: readonly string[], options: ServiceSettingsSyncOptions): string | null {
|
|
71
|
+
const result = runCommand('systemctl', ['--user', ...args], options);
|
|
72
|
+
return commandError(result);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function reloadSystemdIfNeeded(
|
|
76
|
+
runtime: CliServiceRuntime,
|
|
77
|
+
status: ManagedServiceStatus,
|
|
78
|
+
options: ServiceSettingsSyncOptions,
|
|
79
|
+
): string | null {
|
|
80
|
+
if (status.platform !== 'systemd') return null;
|
|
81
|
+
return runSystemd(runtime, ['daemon-reload'], options);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function disableSystemService(
|
|
85
|
+
runtime: CliServiceRuntime,
|
|
86
|
+
manager: ServiceManagerLike,
|
|
87
|
+
options: ServiceSettingsSyncOptions,
|
|
88
|
+
): ServiceSettingsSyncResult {
|
|
89
|
+
const before = manager.status();
|
|
90
|
+
let disableError: string | null = null;
|
|
91
|
+
if (before.platform === 'systemd') {
|
|
92
|
+
disableError = before.installed || before.running
|
|
93
|
+
? runSystemd(runtime, ['disable', '--now', `${serviceName(runtime)}.service`], options)
|
|
94
|
+
: null;
|
|
95
|
+
if (disableError) {
|
|
96
|
+
logger.warn('Settings service sync: systemd disable failed', { error: disableError });
|
|
97
|
+
}
|
|
98
|
+
} else if (before.running || before.installed) {
|
|
99
|
+
const stopped = manager.stop();
|
|
100
|
+
if (stopped.actionError) {
|
|
101
|
+
return {
|
|
102
|
+
handled: true,
|
|
103
|
+
action: 'stop',
|
|
104
|
+
status: stopped,
|
|
105
|
+
message: `Service disable failed: ${stopped.actionError}`,
|
|
106
|
+
error: stopped.actionError,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const uninstalled = manager.uninstall();
|
|
112
|
+
const reloadError = reloadSystemdIfNeeded(runtime, uninstalled, options);
|
|
113
|
+
const error = uninstalled.actionError ?? reloadError ?? disableError ?? undefined;
|
|
114
|
+
return {
|
|
115
|
+
handled: true,
|
|
116
|
+
action: 'disable',
|
|
117
|
+
status: uninstalled,
|
|
118
|
+
message: error ? `Service disable failed: ${error}` : 'OS service disabled',
|
|
119
|
+
...(error ? { error } : {}),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function installAndStartSystemService(
|
|
124
|
+
runtime: CliServiceRuntime,
|
|
125
|
+
manager: ServiceManagerLike,
|
|
126
|
+
options: ServiceSettingsSyncOptions,
|
|
127
|
+
): ServiceSettingsSyncResult {
|
|
128
|
+
(options.mkdir ?? mkdirSync)(getServiceStateRoot(runtime), { recursive: true });
|
|
129
|
+
const installed = manager.install();
|
|
130
|
+
if (installed.actionError) {
|
|
131
|
+
return {
|
|
132
|
+
handled: true,
|
|
133
|
+
action: 'install',
|
|
134
|
+
status: installed,
|
|
135
|
+
message: `Service install failed: ${installed.actionError}`,
|
|
136
|
+
error: installed.actionError,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const reloadError = reloadSystemdIfNeeded(runtime, installed, options);
|
|
141
|
+
if (reloadError) {
|
|
142
|
+
return {
|
|
143
|
+
handled: true,
|
|
144
|
+
action: 'install',
|
|
145
|
+
status: installed,
|
|
146
|
+
message: `Service install failed: ${reloadError}`,
|
|
147
|
+
error: reloadError,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const started = manager.start();
|
|
152
|
+
const error = started.actionError ?? undefined;
|
|
153
|
+
return {
|
|
154
|
+
handled: true,
|
|
155
|
+
action: 'install-start',
|
|
156
|
+
status: started,
|
|
157
|
+
message: error ? `Service start failed: ${error}` : 'OS service installed and started',
|
|
158
|
+
...(error ? { error } : {}),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function refreshInstalledSystemService(
|
|
163
|
+
runtime: CliServiceRuntime,
|
|
164
|
+
manager: ServiceManagerLike,
|
|
165
|
+
options: ServiceSettingsSyncOptions,
|
|
166
|
+
): ServiceSettingsSyncResult {
|
|
167
|
+
const before = manager.status();
|
|
168
|
+
if (!before.installed && runtime.configManager.get('service.autostart') !== true) {
|
|
169
|
+
return {
|
|
170
|
+
handled: true,
|
|
171
|
+
action: 'status',
|
|
172
|
+
status: before,
|
|
173
|
+
message: 'Service setting saved',
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const installed = manager.install();
|
|
178
|
+
if (installed.actionError) {
|
|
179
|
+
return {
|
|
180
|
+
handled: true,
|
|
181
|
+
action: 'install',
|
|
182
|
+
status: installed,
|
|
183
|
+
message: `Service update failed: ${installed.actionError}`,
|
|
184
|
+
error: installed.actionError,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const reloadError = reloadSystemdIfNeeded(runtime, installed, options);
|
|
189
|
+
if (reloadError) {
|
|
190
|
+
return {
|
|
191
|
+
handled: true,
|
|
192
|
+
action: 'install',
|
|
193
|
+
status: installed,
|
|
194
|
+
message: `Service update failed: ${reloadError}`,
|
|
195
|
+
error: reloadError,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const next = before.running ? manager.restart() : manager.start();
|
|
200
|
+
const error = next.actionError ?? undefined;
|
|
201
|
+
return {
|
|
202
|
+
handled: true,
|
|
203
|
+
action: before.running ? 'restart' : 'start',
|
|
204
|
+
status: next,
|
|
205
|
+
message: error ? `Service update failed: ${error}` : 'OS service updated',
|
|
206
|
+
...(error ? { error } : {}),
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function syncServiceSettingToPlatform(
|
|
211
|
+
runtime: CliServiceRuntime,
|
|
212
|
+
change: ServiceSettingsSyncChange,
|
|
213
|
+
options: ServiceSettingsSyncOptions = {},
|
|
214
|
+
): ServiceSettingsSyncResult {
|
|
215
|
+
if (!String(change.key).startsWith('service.')) return { handled: false };
|
|
216
|
+
if (change.previousValue === change.value) return { handled: true, message: 'Service setting unchanged' };
|
|
217
|
+
|
|
218
|
+
const manager = options.createManager?.(runtime) ?? createPlatformServiceManager(runtime);
|
|
219
|
+
|
|
220
|
+
try {
|
|
221
|
+
if (change.key === 'service.autostart') {
|
|
222
|
+
if (change.value === true) {
|
|
223
|
+
if (runtime.configManager.get('service.enabled') !== true) {
|
|
224
|
+
runtime.configManager.setDynamic('service.enabled', true);
|
|
225
|
+
}
|
|
226
|
+
return installAndStartSystemService(runtime, manager, options);
|
|
227
|
+
}
|
|
228
|
+
return disableSystemService(runtime, manager, options);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (change.key === 'service.enabled') {
|
|
232
|
+
if (change.value === false) {
|
|
233
|
+
if (runtime.configManager.get('service.autostart') === true) {
|
|
234
|
+
runtime.configManager.setDynamic('service.autostart', false);
|
|
235
|
+
}
|
|
236
|
+
return disableSystemService(runtime, manager, options);
|
|
237
|
+
}
|
|
238
|
+
if (runtime.configManager.get('service.autostart') === true) {
|
|
239
|
+
return installAndStartSystemService(runtime, manager, options);
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
handled: true,
|
|
243
|
+
action: 'status',
|
|
244
|
+
status: manager.status(),
|
|
245
|
+
message: 'Service mode saved; enable autostart to install the OS service',
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (SERVICE_DEFINITION_KEYS.has(change.key)) {
|
|
250
|
+
if (runtime.configManager.get('service.enabled') === true && runtime.configManager.get('service.autostart') === true) {
|
|
251
|
+
return refreshInstalledSystemService(runtime, manager, options);
|
|
252
|
+
}
|
|
253
|
+
return {
|
|
254
|
+
handled: true,
|
|
255
|
+
action: 'status',
|
|
256
|
+
status: manager.status(),
|
|
257
|
+
message: 'Service setting saved; enable autostart to install the OS service',
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
} catch (error) {
|
|
261
|
+
const summarized = summarizeError(error);
|
|
262
|
+
logger.error('Settings service sync failed', { key: change.key, error: summarized });
|
|
263
|
+
return {
|
|
264
|
+
handled: true,
|
|
265
|
+
action: 'status',
|
|
266
|
+
status: manager.status(),
|
|
267
|
+
message: `Service sync failed: ${summarized}`,
|
|
268
|
+
error: summarized,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return { handled: false };
|
|
273
|
+
}
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
import type { ConfigManager } from '../config/index.ts';
|
|
2
|
+
import { getProviderIdFromModel } from '../config/provider-model.ts';
|
|
3
|
+
import type { ConversationManager } from '../core/conversation';
|
|
4
|
+
import type { CommandContext } from '../input/command-registry.ts';
|
|
5
|
+
import type { InputHandler } from '../input/handler.ts';
|
|
6
|
+
import type { PanelManager } from '../panels/panel-manager.ts';
|
|
7
|
+
import type { ProviderRegistry } from '@pellux/goodvibes-sdk/platform/providers';
|
|
8
|
+
import type { MutableRuntimeState } from '@/runtime/index.ts';
|
|
9
|
+
import type { FeatureFlagManager } from '@/runtime/index.ts';
|
|
10
|
+
import type { McpRegistry } from '@pellux/goodvibes-sdk/platform/mcp';
|
|
11
|
+
import type { SubscriptionManager } from '@pellux/goodvibes-sdk/platform/config';
|
|
12
|
+
import type { SecretsManager } from '@pellux/goodvibes-sdk/platform/config';
|
|
13
|
+
import type { ServiceInspectionQuery } from '../runtime/ui-service-queries.ts';
|
|
14
|
+
import type { ModelPickerTargetInfo } from '../input/model-picker.ts';
|
|
15
|
+
import { syncServiceSettingToPlatform } from './service-settings-sync.ts';
|
|
16
|
+
|
|
17
|
+
type WireShellUiOpenersOptions = {
|
|
18
|
+
commandContext: CommandContext;
|
|
19
|
+
input: InputHandler;
|
|
20
|
+
panelManager: PanelManager;
|
|
21
|
+
conversation: ConversationManager;
|
|
22
|
+
configManager: ConfigManager;
|
|
23
|
+
providerRegistry: ProviderRegistry;
|
|
24
|
+
runtime: MutableRuntimeState;
|
|
25
|
+
featureFlags: FeatureFlagManager;
|
|
26
|
+
mcpRegistry: McpRegistry;
|
|
27
|
+
subscriptionManager: SubscriptionManager;
|
|
28
|
+
secretsManager?: Pick<SecretsManager, 'delete' | 'get' | 'set'>;
|
|
29
|
+
serviceRegistry: Pick<ServiceInspectionQuery, 'getAll'>;
|
|
30
|
+
workingDirectory: string;
|
|
31
|
+
homeDirectory: string;
|
|
32
|
+
getConfiguredProviderIds: () => string[];
|
|
33
|
+
getPinned: () => Promise<string[]>;
|
|
34
|
+
render: () => void;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Derive the configuredVia tier for a provider.
|
|
39
|
+
* Tier order mirrors SDK provider-routes.ts: env → secrets → subscription → undefined.
|
|
40
|
+
* The preResolvedSecretKeys set is pre-fetched async before the sync picker render cycle.
|
|
41
|
+
*/
|
|
42
|
+
function deriveConfiguredVia(
|
|
43
|
+
providerId: string,
|
|
44
|
+
configuredIds: Set<string>,
|
|
45
|
+
subscriptionManager: SubscriptionManager,
|
|
46
|
+
preResolvedSecretKeys?: ReadonlySet<string>,
|
|
47
|
+
): 'env' | 'secrets' | 'subscription' | 'anonymous' | undefined {
|
|
48
|
+
if (!configuredIds.has(providerId)) return undefined;
|
|
49
|
+
|
|
50
|
+
// Tier 1: subscription check (most specific — subscription overrides env for this provider)
|
|
51
|
+
const subs = subscriptionManager.list();
|
|
52
|
+
if (subs.some((s) => s.provider === providerId)) return 'subscription';
|
|
53
|
+
|
|
54
|
+
// Tier 2: env-var present (process.env check; anonymous providers don't appear in configuredIds)
|
|
55
|
+
// We don't have BUILTIN_PROVIDER_ENV_KEYS here; if env was used the configuredIds path covers it.
|
|
56
|
+
// The presence in configuredIds and no subscription → either env or secrets.
|
|
57
|
+
// Tier 3: secrets-manager backed (pre-resolved async batch)
|
|
58
|
+
if (preResolvedSecretKeys && preResolvedSecretKeys.has(providerId)) return 'secrets';
|
|
59
|
+
|
|
60
|
+
return 'env';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Build a configuredViaMap for the given provider list.
|
|
65
|
+
* Pass preResolvedSecretKeys (from an async SecretsManager batch) to surface the 'secrets' tier.
|
|
66
|
+
*/
|
|
67
|
+
function buildConfiguredViaMap(
|
|
68
|
+
providers: string[],
|
|
69
|
+
configuredIds: Set<string>,
|
|
70
|
+
subscriptionManager: SubscriptionManager,
|
|
71
|
+
preResolvedSecretKeys?: ReadonlySet<string>,
|
|
72
|
+
): Map<string, 'env' | 'secrets' | 'subscription' | 'anonymous'> {
|
|
73
|
+
const map = new Map<string, 'env' | 'secrets' | 'subscription' | 'anonymous'>();
|
|
74
|
+
for (const p of providers) {
|
|
75
|
+
const via = deriveConfiguredVia(p, configuredIds, subscriptionManager, preResolvedSecretKeys);
|
|
76
|
+
if (via !== undefined) map.set(p, via);
|
|
77
|
+
}
|
|
78
|
+
return map;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function wireShellUiOpeners(options: WireShellUiOpenersOptions): void {
|
|
82
|
+
const {
|
|
83
|
+
commandContext,
|
|
84
|
+
input,
|
|
85
|
+
panelManager,
|
|
86
|
+
conversation,
|
|
87
|
+
configManager,
|
|
88
|
+
providerRegistry,
|
|
89
|
+
runtime,
|
|
90
|
+
featureFlags,
|
|
91
|
+
mcpRegistry,
|
|
92
|
+
subscriptionManager,
|
|
93
|
+
secretsManager,
|
|
94
|
+
serviceRegistry,
|
|
95
|
+
workingDirectory,
|
|
96
|
+
homeDirectory,
|
|
97
|
+
getConfiguredProviderIds,
|
|
98
|
+
getPinned,
|
|
99
|
+
render,
|
|
100
|
+
} = options;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Pre-resolve which provider IDs have secrets-manager keys (async batch, SDK tier pattern).
|
|
104
|
+
* Returns a set of provider IDs (not env var names) that are secrets-backed.
|
|
105
|
+
* Falls back to empty set if secretsManager is not provided.
|
|
106
|
+
*/
|
|
107
|
+
async function resolveSecretProviderIds(): Promise<ReadonlySet<string>> {
|
|
108
|
+
if (!secretsManager) return new Set<string>();
|
|
109
|
+
const configuredIds = new Set(getConfiguredProviderIds());
|
|
110
|
+
// For each configured provider, check if secretsManager has a key for it by provider ID.
|
|
111
|
+
// We use provider ID as the lookup key since we don't have BUILTIN_PROVIDER_ENV_KEYS here.
|
|
112
|
+
const results = await Promise.all(
|
|
113
|
+
[...configuredIds].map(async (providerId) => {
|
|
114
|
+
const val = await secretsManager.get(providerId).catch(() => null);
|
|
115
|
+
return val !== null ? providerId : null;
|
|
116
|
+
}),
|
|
117
|
+
);
|
|
118
|
+
return new Set(results.filter((v): v is string => v !== null));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const getCurrentModelForPickerTarget = (): string => {
|
|
122
|
+
const selectedTarget = input.modelPicker.getSelectedTargetInfo();
|
|
123
|
+
const target = selectedTarget?.target ?? input.modelPicker.target;
|
|
124
|
+
if (target === 'helper') return String(configManager.get('helper.globalModel') || runtime.model);
|
|
125
|
+
if (target === 'tool') return String(configManager.get('tools.llmModel') || runtime.model);
|
|
126
|
+
if (target === 'tts') return String(configManager.get('tts.llmModel') || runtime.model);
|
|
127
|
+
return runtime.model;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const getCurrentProviderForPickerTarget = (): string => {
|
|
131
|
+
const selectedTarget = input.modelPicker.getSelectedTargetInfo();
|
|
132
|
+
const target = selectedTarget?.target ?? input.modelPicker.target;
|
|
133
|
+
if (target === 'helper') return String(configManager.get('helper.globalProvider') || runtime.provider);
|
|
134
|
+
if (target === 'tool') return String(configManager.get('tools.llmProvider') || runtime.provider);
|
|
135
|
+
if (target === 'tts') return String(configManager.get('tts.llmProvider') || runtime.provider);
|
|
136
|
+
return runtime.provider;
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const buildModelPickerTargets = (): ModelPickerTargetInfo[] => {
|
|
140
|
+
const mainProvider = getProviderIdFromModel(configManager.get('provider.model') || runtime.provider).trim();
|
|
141
|
+
const mainModel = String(configManager.get('provider.model') || runtime.model || '').trim();
|
|
142
|
+
const helperProvider = String(configManager.get('helper.globalProvider') ?? '').trim();
|
|
143
|
+
const helperModel = String(configManager.get('helper.globalModel') ?? '').trim();
|
|
144
|
+
const toolProvider = String(configManager.get('tools.llmProvider') ?? '').trim();
|
|
145
|
+
const toolModel = String(configManager.get('tools.llmModel') ?? '').trim();
|
|
146
|
+
const ttsProvider = String(configManager.get('tts.llmProvider') ?? '').trim();
|
|
147
|
+
const ttsModel = String(configManager.get('tts.llmModel') ?? '').trim();
|
|
148
|
+
|
|
149
|
+
return [
|
|
150
|
+
{
|
|
151
|
+
target: 'main',
|
|
152
|
+
label: 'Main Chat',
|
|
153
|
+
description: 'Default provider and model for normal chat turns in this TUI session.',
|
|
154
|
+
provider: mainProvider,
|
|
155
|
+
model: mainModel,
|
|
156
|
+
enabled: true,
|
|
157
|
+
inherited: false,
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
target: 'helper',
|
|
161
|
+
label: 'Helper Model',
|
|
162
|
+
description: 'Optional helper route used for supporting work. Empty provider/model values inherit Main Chat.',
|
|
163
|
+
provider: helperProvider || mainProvider,
|
|
164
|
+
model: helperModel || mainModel,
|
|
165
|
+
enabled: Boolean(configManager.get('helper.enabled')),
|
|
166
|
+
inherited: helperProvider.length === 0 && helperModel.length === 0,
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
target: 'tool',
|
|
170
|
+
label: 'Tool LLM',
|
|
171
|
+
description: 'Optional LLM route for tool-specific reasoning. Selecting a model enables the tool LLM route.',
|
|
172
|
+
provider: toolProvider || mainProvider,
|
|
173
|
+
model: toolModel || mainModel,
|
|
174
|
+
enabled: Boolean(configManager.get('tools.llmEnabled')),
|
|
175
|
+
inherited: toolProvider.length === 0 && toolModel.length === 0,
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
target: 'tts',
|
|
179
|
+
label: 'TTS LLM',
|
|
180
|
+
description: 'Optional LLM override for /tts response generation. Empty values use the current chat model.',
|
|
181
|
+
provider: ttsProvider || mainProvider,
|
|
182
|
+
model: ttsModel || mainModel,
|
|
183
|
+
enabled: true,
|
|
184
|
+
inherited: ttsProvider.length === 0 && ttsModel.length === 0,
|
|
185
|
+
},
|
|
186
|
+
];
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
commandContext.openModelPicker = () => {
|
|
190
|
+
void (async () => {
|
|
191
|
+
const models = providerRegistry.getSelectableModels();
|
|
192
|
+
const configuredIds = new Set(getConfiguredProviderIds());
|
|
193
|
+
input.modelPicker.configuredProviders = configuredIds;
|
|
194
|
+
const providerIds = [...new Set(models.map((m) => m.provider))];
|
|
195
|
+
const secretProviderIds = await resolveSecretProviderIds();
|
|
196
|
+
input.modelPicker.configuredViaMap = buildConfiguredViaMap(providerIds, configuredIds, subscriptionManager, secretProviderIds);
|
|
197
|
+
void getPinned().then((pinned) => {
|
|
198
|
+
input.modelPicker.pinnedIds = new Set(pinned);
|
|
199
|
+
});
|
|
200
|
+
void input.modelPicker.loadRecentModels().catch(() => {}); // best-effort: prefetch for UI, failure is non-visible
|
|
201
|
+
input.modalOpened('modelPicker');
|
|
202
|
+
input.modelPicker.setTargetInfos(buildModelPickerTargets());
|
|
203
|
+
input.modelPicker.openAllModels(models, getCurrentModelForPickerTarget());
|
|
204
|
+
render();
|
|
205
|
+
})().catch((error: unknown) => {
|
|
206
|
+
commandContext.print?.(`Model picker failed to open: ${error instanceof Error ? error.message : String(error)}`);
|
|
207
|
+
render();
|
|
208
|
+
});
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
commandContext.openModelPickerWithTarget = (target) => input.openModelPickerWithTarget(target);
|
|
212
|
+
commandContext.openProviderModelPickerWithTarget = (target) => input.openProviderModelPickerWithTarget(target);
|
|
213
|
+
|
|
214
|
+
commandContext.openProviderPicker = () => {
|
|
215
|
+
void (async () => {
|
|
216
|
+
const providers = [...new Set(providerRegistry.listModels().map((model) => model.provider))];
|
|
217
|
+
const configuredIds = new Set(getConfiguredProviderIds());
|
|
218
|
+
input.modelPicker.configuredProviders = configuredIds;
|
|
219
|
+
const secretProviderIds = await resolveSecretProviderIds();
|
|
220
|
+
input.modelPicker.configuredViaMap = buildConfiguredViaMap(providers, configuredIds, subscriptionManager, secretProviderIds);
|
|
221
|
+
input.modalOpened('modelPicker');
|
|
222
|
+
input.modelPicker.setTargetInfos(buildModelPickerTargets());
|
|
223
|
+
input.modelPicker.openProviders(providers, getCurrentProviderForPickerTarget());
|
|
224
|
+
render();
|
|
225
|
+
})().catch((error: unknown) => {
|
|
226
|
+
commandContext.print?.(`Provider picker failed to open: ${error instanceof Error ? error.message : String(error)}`);
|
|
227
|
+
render();
|
|
228
|
+
});
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
commandContext.openSelection = (title, items, opts, callback) => {
|
|
232
|
+
input.openSelection(title, items, opts, callback);
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
commandContext.openOnboardingWizard = (modeOrOptions) => {
|
|
236
|
+
input.openOnboardingWizard(modeOrOptions);
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
commandContext.openContextInspector = () => {
|
|
240
|
+
input.modalOpened('contextInspector');
|
|
241
|
+
input.contextInspectorModal.open();
|
|
242
|
+
render();
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
commandContext.openBookmarkModal = () => {
|
|
246
|
+
input.modalOpened('bookmark');
|
|
247
|
+
input.bookmarkModal.open();
|
|
248
|
+
render();
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
commandContext.openHelpOverlay = () => {
|
|
252
|
+
if (!input.helpOverlayActive) input.modalOpened('help');
|
|
253
|
+
input.helpOverlayActive = !input.helpOverlayActive;
|
|
254
|
+
input.helpScrollOffset = 0;
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
commandContext.openShortcutsOverlay = () => {
|
|
258
|
+
if (!input.shortcutsOverlayActive) input.modalOpened('shortcuts');
|
|
259
|
+
input.shortcutsOverlayActive = !input.shortcutsOverlayActive;
|
|
260
|
+
input.shortcutsScrollOffset = 0;
|
|
261
|
+
render();
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
commandContext.openProfilePicker = () => {
|
|
265
|
+
input.modalOpened('profilePicker');
|
|
266
|
+
input.profilePickerModal.open();
|
|
267
|
+
render();
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
commandContext.openSettingsModal = (target?: string) => {
|
|
271
|
+
input.modalOpened('settings');
|
|
272
|
+
input.settingsModal.open(configManager, featureFlags, subscriptionManager, serviceRegistry, mcpRegistry, secretsManager, {
|
|
273
|
+
onSettingApplied: (change) => syncServiceSettingToPlatform(
|
|
274
|
+
{ configManager, workingDirectory, homeDirectory },
|
|
275
|
+
change,
|
|
276
|
+
),
|
|
277
|
+
});
|
|
278
|
+
input.settingsModal.selectTarget(target);
|
|
279
|
+
render();
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
commandContext.openMcpWorkspace = () => {
|
|
283
|
+
input.openMcpWorkspace(commandContext);
|
|
284
|
+
render();
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
commandContext.openAgentWorkspace = () => {
|
|
288
|
+
input.openAgentWorkspace(commandContext);
|
|
289
|
+
render();
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
commandContext.openSessionPicker = () => {
|
|
293
|
+
input.modalOpened('sessionPicker');
|
|
294
|
+
input.sessionPickerModal.open();
|
|
295
|
+
render();
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
commandContext.openPanelPicker = () => {
|
|
299
|
+
if (!panelManager.isVisible()) {
|
|
300
|
+
if (panelManager.getAllOpen().length === 0) {
|
|
301
|
+
try {
|
|
302
|
+
panelManager.open('panel-list');
|
|
303
|
+
} catch {
|
|
304
|
+
// non-fatal
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
panelManager.show();
|
|
308
|
+
input.panelFocused = true;
|
|
309
|
+
conversation.setSplashSuppressed(true);
|
|
310
|
+
conversation.rebuildHistory();
|
|
311
|
+
} else if (!input.panelFocused) {
|
|
312
|
+
if (panelManager.getAllOpen().length === 0) {
|
|
313
|
+
try {
|
|
314
|
+
panelManager.open('panel-list');
|
|
315
|
+
} catch {
|
|
316
|
+
// non-fatal
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
panelManager.show();
|
|
320
|
+
input.panelFocused = true;
|
|
321
|
+
conversation.setSplashSuppressed(true);
|
|
322
|
+
conversation.rebuildHistory();
|
|
323
|
+
} else {
|
|
324
|
+
panelManager.hide();
|
|
325
|
+
input.panelFocused = false;
|
|
326
|
+
conversation.setSplashSuppressed(false);
|
|
327
|
+
conversation.rebuildHistory();
|
|
328
|
+
}
|
|
329
|
+
render();
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
commandContext.focusPanels = () => {
|
|
333
|
+
if (!panelManager.isVisible() || panelManager.getAllOpen().length === 0) return;
|
|
334
|
+
input.panelFocused = true;
|
|
335
|
+
render();
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
commandContext.focusPrompt = () => {
|
|
339
|
+
input.panelFocused = false;
|
|
340
|
+
input.indicatorFocused = false;
|
|
341
|
+
render();
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
commandContext.showPanel = (panelId, pane) => {
|
|
345
|
+
panelManager.open(panelId, pane);
|
|
346
|
+
panelManager.show();
|
|
347
|
+
input.panelFocused = true;
|
|
348
|
+
conversation.setSplashSuppressed(true);
|
|
349
|
+
conversation.rebuildHistory();
|
|
350
|
+
render();
|
|
351
|
+
};
|
|
352
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { registerAllTools } from '@pellux/goodvibes-sdk/platform/tools';
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { Tool } from '@pellux/goodvibes-sdk/platform/types';
|
|
2
|
+
import type { ToolRegistry } from '@pellux/goodvibes-sdk/platform/tools';
|
|
3
|
+
|
|
4
|
+
type AgentToolArgs = {
|
|
5
|
+
readonly mode?: unknown;
|
|
6
|
+
readonly [key: string]: unknown;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type AgentToolPolicyGuardOptions = {
|
|
10
|
+
readonly getLastUserMessage?: () => string | null;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const LOCAL_AGENT_DENIAL = [
|
|
14
|
+
'GoodVibes Agent does not spawn local Engineer/Reviewer/Tester/Verifier roots or run local WRFC chains.',
|
|
15
|
+
'Keep ordinary assistant work serial in the main conversation.',
|
|
16
|
+
'For explicit build/fix/review work, delegate one request to GoodVibes TUI through the public shared-session/build-delegation contract with the full original user ask.',
|
|
17
|
+
].join(' ');
|
|
18
|
+
|
|
19
|
+
export function installAgentToolPolicyGuard(registry: ToolRegistry, options: AgentToolPolicyGuardOptions = {}): void {
|
|
20
|
+
const agentTool = registry.list().find((tool) => tool.definition.name === 'agent');
|
|
21
|
+
if (!agentTool) throw new Error('Agent tool policy guard could not find the agent tool.');
|
|
22
|
+
wrapAgentToolForAgentPolicy(agentTool, options);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function wrapAgentToolForAgentPolicy(tool: Tool, _options: AgentToolPolicyGuardOptions = {}): void {
|
|
26
|
+
const originalExecute = tool.execute.bind(tool);
|
|
27
|
+
tool.execute = async (args) => {
|
|
28
|
+
const denial = validateAgentToolInvocationForAgentPolicy(args as AgentToolArgs);
|
|
29
|
+
if (denial) return { success: false, error: denial };
|
|
30
|
+
return originalExecute(normalizeAgentToolInvocationForAgentPolicy(args as AgentToolArgs) as Parameters<Tool['execute']>[0]);
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function validateAgentToolInvocationForAgentPolicy(args: AgentToolArgs): string | null {
|
|
35
|
+
if (args.mode === 'spawn' || args.mode === 'batch-spawn') return LOCAL_AGENT_DENIAL;
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function normalizeAgentToolInvocationForAgentPolicy(args: AgentToolArgs): AgentToolArgs {
|
|
40
|
+
return args;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const AGENT_LOCAL_SPAWN_DENIAL_MESSAGE = LOCAL_AGENT_DENIAL;
|
|
44
|
+
|
|
45
|
+
// Compatibility exports for copied TUI tests/imports during the near-fork phase.
|
|
46
|
+
export const installWrfcAgentToolGuard = installAgentToolPolicyGuard;
|
|
47
|
+
export const wrapWrfcAgentTool = wrapAgentToolForAgentPolicy;
|
|
48
|
+
export const validateWrfcAgentToolInvocation = validateAgentToolInvocationForAgentPolicy;
|
|
49
|
+
export const normalizeWrfcAgentToolInvocation = normalizeAgentToolInvocationForAgentPolicy;
|