@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,620 @@
|
|
|
1
|
+
import { createOAuthLocalListener } from '@pellux/goodvibes-sdk/platform/config';
|
|
2
|
+
import { beginOpenAICodexLogin, exchangeOpenAICodexCode } from '@pellux/goodvibes-sdk/platform/config';
|
|
3
|
+
import { openExternalUrl } from '@pellux/goodvibes-sdk/platform/utils';
|
|
4
|
+
import { getProviderIdFromModel } from '../config/provider-model.ts';
|
|
5
|
+
import { buildProviderAccountSnapshot } from '@/runtime/index.ts';
|
|
6
|
+
import { OnboardingWizardController, type OnboardingWizardAction, type OnboardingWizardApplyFeedback } from './onboarding/onboarding-wizard.ts';
|
|
7
|
+
import { handleCloudflareOnboardingActionForHandler, maybeProvisionCloudflareOnFinalApplyForHandler } from './handler-onboarding-cloudflare.ts';
|
|
8
|
+
import { applyOnboardingRequest, collectOnboardingSnapshot, verifyOnboardingRequest } from '../runtime/onboarding/index.ts';
|
|
9
|
+
import type { OnboardingApplyRequest, OnboardingVerificationItem } from '../runtime/onboarding/index.ts';
|
|
10
|
+
import type { ModelPickerTarget } from './model-picker.ts';
|
|
11
|
+
import { captureOnboardingWizardSnapshot, restoreOnboardingWizardSnapshot } from './handler-ui-state.ts';
|
|
12
|
+
import type { InputHandler } from './handler.ts';
|
|
13
|
+
import {
|
|
14
|
+
formatRuntimeActiveSuccessMessage,
|
|
15
|
+
getRuntimeEndpointStatus,
|
|
16
|
+
isRuntimeEndpointActive,
|
|
17
|
+
isRuntimeEndpointOccupyingConfiguredPort,
|
|
18
|
+
runtimePortDiagnostic,
|
|
19
|
+
type OnboardingExternalServiceState,
|
|
20
|
+
type OnboardingRuntimeEndpoint,
|
|
21
|
+
} from './onboarding/onboarding-runtime-status.ts';
|
|
22
|
+
|
|
23
|
+
export interface OnboardingRuntimePosture {
|
|
24
|
+
readonly serviceEnabled: boolean;
|
|
25
|
+
readonly serviceAutostart: boolean;
|
|
26
|
+
readonly restartOnFailure: boolean;
|
|
27
|
+
readonly expectedDaemon: boolean;
|
|
28
|
+
readonly expectedHttpListener: boolean;
|
|
29
|
+
readonly serverBacked: boolean;
|
|
30
|
+
readonly remoteExposure: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function extractAuthorizationCode(input: string): string | null {
|
|
34
|
+
const trimmed = input.trim();
|
|
35
|
+
if (!trimmed) return null;
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const url = new URL(trimmed);
|
|
39
|
+
return url.searchParams.get('code');
|
|
40
|
+
} catch {
|
|
41
|
+
return trimmed;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function isLoopbackHostValue(value: string | null | undefined): boolean {
|
|
46
|
+
const normalized = (value ?? '').trim().toLowerCase();
|
|
47
|
+
if (normalized.length === 0) return false;
|
|
48
|
+
return normalized === 'localhost'
|
|
49
|
+
|| normalized === '::1'
|
|
50
|
+
|| normalized === '[::1]'
|
|
51
|
+
|| normalized === '0:0:0:0:0:0:0:1'
|
|
52
|
+
|| /^127(?:\.\d{1,3}){3}$/.test(normalized);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function onboardingVerificationStatusRank(item: OnboardingVerificationItem): number {
|
|
56
|
+
if (item.status === 'fail') return 3;
|
|
57
|
+
if (item.status === 'warn') return 2;
|
|
58
|
+
return 1;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function dedupeOnboardingVerificationItems(
|
|
62
|
+
items: readonly OnboardingVerificationItem[],
|
|
63
|
+
): OnboardingVerificationItem[] {
|
|
64
|
+
const order: string[] = [];
|
|
65
|
+
const byId = new Map<string, OnboardingVerificationItem>();
|
|
66
|
+
for (const item of items) {
|
|
67
|
+
const existing = byId.get(item.id);
|
|
68
|
+
if (!existing) {
|
|
69
|
+
order.push(item.id);
|
|
70
|
+
byId.set(item.id, item);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (onboardingVerificationStatusRank(item) > onboardingVerificationStatusRank(existing)) {
|
|
74
|
+
byId.set(item.id, item);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return order.map((id) => byId.get(id)).filter((item): item is OnboardingVerificationItem => Boolean(item));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function formatOnboardingApplyCompletionMessage(items: readonly OnboardingVerificationItem[]): string {
|
|
81
|
+
const warnings = items.filter((item) => item.status === 'warn');
|
|
82
|
+
if (warnings.length === 0) return `Onboarding applied and verified ${items.length} item(s).`;
|
|
83
|
+
const passed = items.filter((item) => item.status === 'pass').length;
|
|
84
|
+
return [
|
|
85
|
+
`Onboarding settings applied. ${passed} verification item(s) passed; ${warnings.length} warning(s) need attention.`,
|
|
86
|
+
...warnings.map((warning) => ` warning ${warning.id}: ${warning.message}`),
|
|
87
|
+
].join('\n');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function getRuntimeEndpointBinding(
|
|
91
|
+
handler: InputHandler,
|
|
92
|
+
request: OnboardingApplyRequest,
|
|
93
|
+
endpoint: OnboardingRuntimeEndpoint,
|
|
94
|
+
): { readonly label: string; readonly host: string; readonly port: number } {
|
|
95
|
+
const hostKey = endpoint === 'daemon' ? 'controlPlane.host' : 'httpListener.host';
|
|
96
|
+
const portKey = endpoint === 'daemon' ? 'controlPlane.port' : 'httpListener.port';
|
|
97
|
+
const fallbackHost = '127.0.0.1';
|
|
98
|
+
const fallbackPort = endpoint === 'daemon' ? 3421 : 3422;
|
|
99
|
+
const rawHost = handler.getOnboardingConfigValue(request, hostKey);
|
|
100
|
+
const rawPort = handler.getOnboardingConfigValue(request, portKey);
|
|
101
|
+
const parsedPort = typeof rawPort === 'number' ? rawPort : Number(rawPort);
|
|
102
|
+
return {
|
|
103
|
+
label: endpoint === 'daemon' ? 'GoodVibes daemon' : 'HTTP listener',
|
|
104
|
+
host: String(rawHost ?? fallbackHost),
|
|
105
|
+
port: Number.isFinite(parsedPort) ? parsedPort : fallbackPort,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function formatRuntimeActiveFailureMessage(
|
|
110
|
+
handler: InputHandler,
|
|
111
|
+
request: OnboardingApplyRequest,
|
|
112
|
+
endpoint: OnboardingRuntimeEndpoint,
|
|
113
|
+
state: OnboardingExternalServiceState | undefined,
|
|
114
|
+
): string {
|
|
115
|
+
const binding = getRuntimeEndpointBinding(handler, request, endpoint);
|
|
116
|
+
const portInUse = endpoint === 'daemon' ? state?.daemonPortInUse : state?.httpListenerPortInUse;
|
|
117
|
+
const status = getRuntimeEndpointStatus(state, endpoint);
|
|
118
|
+
const impact = endpoint === 'daemon'
|
|
119
|
+
? 'browser, LAN, and service-backed GoodVibes surfaces may be unavailable until the daemon is running there.'
|
|
120
|
+
: 'incoming webhooks and event surfaces will not receive traffic until the listener is running there.';
|
|
121
|
+
return `${binding.label} is enabled for ${binding.host}:${binding.port}, but onboarding could not confirm an embedded or verified external service after restart. ${runtimePortDiagnostic(binding, portInUse, status)} Settings were saved; ${impact}`;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function formatRuntimeStoppedFailureMessage(
|
|
125
|
+
handler: InputHandler,
|
|
126
|
+
request: OnboardingApplyRequest,
|
|
127
|
+
endpoint: OnboardingRuntimeEndpoint,
|
|
128
|
+
state?: OnboardingExternalServiceState,
|
|
129
|
+
): string {
|
|
130
|
+
const binding = getRuntimeEndpointBinding(handler, request, endpoint);
|
|
131
|
+
const status = getRuntimeEndpointStatus(state, endpoint);
|
|
132
|
+
const disabledSurface = endpoint === 'daemon' ? 'server-backed surfaces' : 'incoming event surfaces';
|
|
133
|
+
const statusDetail = status ? ` ${runtimePortDiagnostic(binding, undefined, status)}` : '';
|
|
134
|
+
return `${binding.label} was disabled for ${disabledSurface}, but ${binding.host}:${binding.port} is still occupied. Settings were saved; another GoodVibes process or external service may still be running on that port.${statusDetail}`;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function showOnboardingApplyFeedbackForHandler(handler: InputHandler, feedback: OnboardingWizardApplyFeedback): void {
|
|
138
|
+
handler.onboardingWizard.setApplyFeedback(feedback);
|
|
139
|
+
const reviewIndex = handler.onboardingWizard.steps.findIndex((step) => step.id === 'review');
|
|
140
|
+
if (reviewIndex >= 0) handler.onboardingWizard.setStep(reviewIndex);
|
|
141
|
+
handler.requestRender();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function continueOnboardingSection(handler: InputHandler): void {
|
|
145
|
+
handler.onboardingWizard.commitEdit();
|
|
146
|
+
handler.onboardingWizard.clearApplyFeedback();
|
|
147
|
+
handler.onboardingWizard.nextStep();
|
|
148
|
+
handler.requestRender();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function clearOnboardingPendingModelPickerTargetForHandler(handler: InputHandler): void {
|
|
152
|
+
handler.onboardingWizard.clearPendingModelPickerTarget();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function clearOnboardingModelPickerCancelStateForHandler(handler: InputHandler): void {
|
|
156
|
+
handler.onboardingModelPickerCancelSnapshot = null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function restoreOnboardingModelPickerCancelStateForHandler(handler: InputHandler): void {
|
|
160
|
+
if (!handler.onboardingModelPickerCancelSnapshot) return;
|
|
161
|
+
restoreOnboardingWizardSnapshot(handler.onboardingWizard, handler.onboardingModelPickerCancelSnapshot, {
|
|
162
|
+
active: true,
|
|
163
|
+
});
|
|
164
|
+
handler.onboardingModelPickerCancelSnapshot = null;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function openModelPickerWithTargetForHandler(
|
|
168
|
+
handler: InputHandler,
|
|
169
|
+
target: ModelPickerTarget,
|
|
170
|
+
source: 'settings' | 'onboarding' = 'settings',
|
|
171
|
+
): boolean {
|
|
172
|
+
const openModelPicker = handler.commandContext?.openModelPicker;
|
|
173
|
+
if (!openModelPicker) return false;
|
|
174
|
+
if (source === 'onboarding' && handler.onboardingWizard.active) {
|
|
175
|
+
handler.onboardingModelPickerCancelSnapshot = captureOnboardingWizardSnapshot(handler.onboardingWizard);
|
|
176
|
+
} else {
|
|
177
|
+
handler.clearOnboardingModelPickerCancelState();
|
|
178
|
+
}
|
|
179
|
+
handler.clearOnboardingPendingModelPickerTarget();
|
|
180
|
+
handler.modelPicker.target = target;
|
|
181
|
+
openModelPicker();
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function openProviderModelPickerWithTargetForHandler(
|
|
186
|
+
handler: InputHandler,
|
|
187
|
+
target: ModelPickerTarget,
|
|
188
|
+
source: 'settings' | 'onboarding' = 'settings',
|
|
189
|
+
): boolean {
|
|
190
|
+
const openProviderPicker = handler.commandContext?.openProviderPicker;
|
|
191
|
+
if (!openProviderPicker) return false;
|
|
192
|
+
if (source === 'onboarding' && handler.onboardingWizard.active) {
|
|
193
|
+
handler.onboardingModelPickerCancelSnapshot = captureOnboardingWizardSnapshot(handler.onboardingWizard);
|
|
194
|
+
} else {
|
|
195
|
+
handler.clearOnboardingModelPickerCancelState();
|
|
196
|
+
}
|
|
197
|
+
handler.clearOnboardingPendingModelPickerTarget();
|
|
198
|
+
handler.modelPicker.target = target;
|
|
199
|
+
openProviderPicker();
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function handleModelPickerCommitForHandler(handler: InputHandler): boolean {
|
|
204
|
+
if (handler.onboardingModelPickerCancelSnapshot && handler.onboardingWizard.active) {
|
|
205
|
+
const selected = handler.modelPicker.mode === 'effort'
|
|
206
|
+
? handler.modelPicker.pendingModel
|
|
207
|
+
: handler.modelPicker.mode === 'contextCap'
|
|
208
|
+
? handler.modelPicker.contextCapPendingModel
|
|
209
|
+
: handler.modelPicker.getSelected();
|
|
210
|
+
if (selected) {
|
|
211
|
+
handler.onboardingWizard.applyModelSelection(handler.modelPicker.target, {
|
|
212
|
+
providerId: selected.provider,
|
|
213
|
+
modelId: selected.registryKey ?? selected.id,
|
|
214
|
+
enabled: true,
|
|
215
|
+
});
|
|
216
|
+
if (handler.modelPicker.target === 'main' && handler.modelPicker.mode === 'effort') {
|
|
217
|
+
const effort = handler.modelPicker.effortLevels[handler.modelPicker.selectedIndex];
|
|
218
|
+
if (effort) handler.onboardingWizard.setFieldValue('default-model.reasoning', effort);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
handler.clearOnboardingPendingModelPickerTarget();
|
|
222
|
+
handler.clearOnboardingModelPickerCancelState();
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
handler.clearOnboardingPendingModelPickerTarget();
|
|
226
|
+
handler.clearOnboardingModelPickerCancelState();
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export async function handleOnboardingActionForHandler(handler: InputHandler, action: OnboardingWizardAction): Promise<void> {
|
|
231
|
+
if (action === 'start-openai-subscription') {
|
|
232
|
+
await handler.handleOpenAiSubscriptionStart();
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
if (action === 'finish-openai-subscription') {
|
|
236
|
+
await handler.handleOpenAiSubscriptionFinish();
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
if (action === 'apply-and-continue') {
|
|
240
|
+
if (handler.onboardingApplyPending) return;
|
|
241
|
+
continueOnboardingSection(handler);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
if (action.startsWith('cloudflare-')) {
|
|
245
|
+
await handleCloudflareOnboardingActionForHandler(handler, action as Extract<OnboardingWizardAction,
|
|
246
|
+
| 'cloudflare-token-requirements'
|
|
247
|
+
| 'cloudflare-create-operational-token'
|
|
248
|
+
| 'cloudflare-discover'
|
|
249
|
+
| 'cloudflare-validate'
|
|
250
|
+
| 'cloudflare-provision'
|
|
251
|
+
| 'cloudflare-verify'
|
|
252
|
+
| 'cloudflare-disable'
|
|
253
|
+
>);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (action !== 'apply') return;
|
|
257
|
+
if (handler.onboardingApplyPending) return;
|
|
258
|
+
const blockers = handler.onboardingWizard.getBlockingFieldLabels();
|
|
259
|
+
if (blockers.length > 0) {
|
|
260
|
+
showOnboardingApplyFeedbackForHandler(handler, {
|
|
261
|
+
severity: 'error',
|
|
262
|
+
title: 'Cannot apply yet',
|
|
263
|
+
summary: 'Fix these required or invalid fields, then apply again.',
|
|
264
|
+
messages: blockers,
|
|
265
|
+
});
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const request = handler.onboardingWizard.buildApplyRequest();
|
|
270
|
+
handler.onboardingWizard.clearApplyFeedback();
|
|
271
|
+
const deps = {
|
|
272
|
+
config: handler.uiServices.platform.configManager,
|
|
273
|
+
secrets: handler.uiServices.platform.secretsManager,
|
|
274
|
+
auth: handler.uiServices.platform.localUserAuthManager,
|
|
275
|
+
shellPaths: handler.uiServices.environment.shellPaths,
|
|
276
|
+
acknowledgementScope: 'project' as const,
|
|
277
|
+
};
|
|
278
|
+
let appliedErrors: string[] = [];
|
|
279
|
+
let verificationItems: readonly OnboardingVerificationItem[] = [];
|
|
280
|
+
let runtimeWarnings: readonly OnboardingVerificationItem[] = [];
|
|
281
|
+
handler.onboardingApplyPending = true;
|
|
282
|
+
try {
|
|
283
|
+
const applied = await applyOnboardingRequest(deps, request);
|
|
284
|
+
if (applied.errors.length > 0) {
|
|
285
|
+
appliedErrors = applied.errors.map((error) => `apply ${error.kind}: ${error.message}`);
|
|
286
|
+
} else {
|
|
287
|
+
const verification = await verifyOnboardingRequest(deps, request);
|
|
288
|
+
verificationItems = verification.items;
|
|
289
|
+
appliedErrors = verification.items
|
|
290
|
+
.filter((item) => item.status === 'fail')
|
|
291
|
+
.map((item) => `verify ${item.id}: ${item.message}`);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (appliedErrors.length === 0) {
|
|
295
|
+
const activationVerification = await handler.restartOnboardingExternalServicesIfNeeded(request);
|
|
296
|
+
runtimeWarnings = dedupeOnboardingVerificationItems([...activationVerification, ...handler.verifyOnboardingRuntimePosture(request)]
|
|
297
|
+
.map((item): OnboardingVerificationItem => item.status === 'fail'
|
|
298
|
+
? { ...item, status: 'warn' }
|
|
299
|
+
: item));
|
|
300
|
+
verificationItems = dedupeOnboardingVerificationItems([...verificationItems, ...runtimeWarnings]);
|
|
301
|
+
const cloudflareItems = await maybeProvisionCloudflareOnFinalApplyForHandler(handler);
|
|
302
|
+
verificationItems = dedupeOnboardingVerificationItems([...verificationItems, ...cloudflareItems]);
|
|
303
|
+
}
|
|
304
|
+
} catch (error) {
|
|
305
|
+
showOnboardingApplyFeedbackForHandler(handler, {
|
|
306
|
+
severity: 'error',
|
|
307
|
+
title: 'Apply failed',
|
|
308
|
+
summary: 'The wizard could not persist these settings. No service restart was attempted.',
|
|
309
|
+
messages: [error instanceof Error ? error.message : String(error)],
|
|
310
|
+
});
|
|
311
|
+
return;
|
|
312
|
+
} finally {
|
|
313
|
+
handler.onboardingApplyPending = false;
|
|
314
|
+
handler.requestRender();
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (appliedErrors.length > 0) {
|
|
318
|
+
showOnboardingApplyFeedbackForHandler(handler, {
|
|
319
|
+
severity: 'error',
|
|
320
|
+
title: 'Apply did not complete',
|
|
321
|
+
summary: 'The settings were not fully applied. Review the messages below and try again.',
|
|
322
|
+
messages: appliedErrors,
|
|
323
|
+
});
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
handler.syncRuntimeFromOnboardingRequest(request);
|
|
328
|
+
handler.onboardingWizard.markApplied();
|
|
329
|
+
handler.onboardingWizard.close();
|
|
330
|
+
for (let index = handler.modalStack.length - 1; index >= 0; index -= 1) {
|
|
331
|
+
if (handler.modalStack[index] === 'onboarding') handler.modalStack.splice(index, 1);
|
|
332
|
+
}
|
|
333
|
+
if (handler.modalStack.length === 0) {
|
|
334
|
+
const returnFocus = handler.modalReturnFocus;
|
|
335
|
+
handler.panelFocused = returnFocus === 'panel';
|
|
336
|
+
handler.indicatorFocused = returnFocus === 'indicator';
|
|
337
|
+
handler.modalReturnFocus = 'prompt';
|
|
338
|
+
}
|
|
339
|
+
handler.commandContext?.print?.(formatOnboardingApplyCompletionMessage(verificationItems));
|
|
340
|
+
handler.requestRender();
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
export async function refreshOnboardingHydrationForHandler(handler: InputHandler, options: {
|
|
344
|
+
readonly preserveValues?: boolean;
|
|
345
|
+
readonly targetStepId?: string;
|
|
346
|
+
} = {}): Promise<void> {
|
|
347
|
+
const hydrationSerial = ++handler.onboardingHydrationSerial;
|
|
348
|
+
handler.onboardingWizard.beginRuntimeHydration();
|
|
349
|
+
handler.requestRender();
|
|
350
|
+
try {
|
|
351
|
+
const snapshot = await collectOnboardingSnapshot({
|
|
352
|
+
config: handler.uiServices.platform.configManager,
|
|
353
|
+
shellPaths: handler.uiServices.environment.shellPaths,
|
|
354
|
+
acknowledgementScope: 'project',
|
|
355
|
+
subscriptions: handler.uiServices.platform.subscriptionManager,
|
|
356
|
+
secrets: handler.uiServices.platform.secretsManager,
|
|
357
|
+
auth: handler.uiServices.platform.localUserAuthManager,
|
|
358
|
+
services: handler.uiServices.platform.serviceRegistry,
|
|
359
|
+
surfaces: {
|
|
360
|
+
list: () => handler.uiServices.platform.surfaceRegistry.syncConfiguredSurfaces(),
|
|
361
|
+
},
|
|
362
|
+
providerAccounts: {
|
|
363
|
+
loadSnapshot: () => buildProviderAccountSnapshot({
|
|
364
|
+
providerRegistry: handler.uiServices.providers.providerRegistry,
|
|
365
|
+
serviceRegistry: handler.uiServices.platform.serviceRegistry,
|
|
366
|
+
subscriptionManager: handler.uiServices.platform.subscriptionManager,
|
|
367
|
+
secretsManager: handler.uiServices.platform.secretsManager,
|
|
368
|
+
}),
|
|
369
|
+
},
|
|
370
|
+
});
|
|
371
|
+
if (!handler.onboardingWizard.active || hydrationSerial !== handler.onboardingHydrationSerial) return;
|
|
372
|
+
handler.onboardingWizard.hydrateRuntimeState({ snapshot }, { resetValues: !(options.preserveValues ?? false) });
|
|
373
|
+
if (options.targetStepId) {
|
|
374
|
+
const targetIndex = handler.onboardingWizard.steps.findIndex((step) => step.id === options.targetStepId);
|
|
375
|
+
if (targetIndex >= 0) handler.onboardingWizard.setStep(targetIndex);
|
|
376
|
+
}
|
|
377
|
+
handler.requestRender();
|
|
378
|
+
} catch (error) {
|
|
379
|
+
if (!handler.onboardingWizard.active || hydrationSerial !== handler.onboardingHydrationSerial) return;
|
|
380
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
381
|
+
handler.onboardingWizard.failRuntimeHydration(message);
|
|
382
|
+
handler.requestRender();
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export async function handleOpenAiSubscriptionStartForHandler(handler: InputHandler): Promise<void> {
|
|
387
|
+
if (handler.onboardingApplyPending) return;
|
|
388
|
+
handler.onboardingApplyPending = true;
|
|
389
|
+
let listener: Awaited<ReturnType<typeof createOAuthLocalListener>> | null = null;
|
|
390
|
+
try {
|
|
391
|
+
const started = await beginOpenAICodexLogin();
|
|
392
|
+
handler.uiServices.platform.subscriptionManager.savePending({
|
|
393
|
+
provider: 'openai',
|
|
394
|
+
state: started.state,
|
|
395
|
+
verifier: started.verifier,
|
|
396
|
+
redirectUri: started.redirectUri,
|
|
397
|
+
createdAt: Date.now(),
|
|
398
|
+
});
|
|
399
|
+
listener = await createOAuthLocalListener({
|
|
400
|
+
expectedState: started.state,
|
|
401
|
+
host: '127.0.0.1',
|
|
402
|
+
port: 1455,
|
|
403
|
+
path: '/auth/callback',
|
|
404
|
+
}).catch(() => null);
|
|
405
|
+
const browserOpened = await openExternalUrl(started.authorizationUrl);
|
|
406
|
+
await handler.refreshOnboardingHydration({ preserveValues: true, targetStepId: 'provider-access' });
|
|
407
|
+
handler.onboardingWizard.setFieldValue('providers.openai-authorization-url', started.authorizationUrl);
|
|
408
|
+
const providerIndex = handler.onboardingWizard.steps.findIndex((step) => step.id === 'provider-access');
|
|
409
|
+
if (providerIndex >= 0) handler.onboardingWizard.setStep(providerIndex);
|
|
410
|
+
handler.requestRender();
|
|
411
|
+
|
|
412
|
+
if (listener && browserOpened) {
|
|
413
|
+
const serial = ++handler.onboardingOpenAiListenerSerial;
|
|
414
|
+
handler.commandContext?.print?.([
|
|
415
|
+
'OpenAI subscription sign-in started from onboarding.',
|
|
416
|
+
' callback listener: waiting on 127.0.0.1:1455',
|
|
417
|
+
' authorizationUrl: shown in the wizard provider step',
|
|
418
|
+
'You can also paste the callback code or URL into the OpenAI callback field.',
|
|
419
|
+
].join('\n'));
|
|
420
|
+
void handler.completeOpenAiSubscriptionFromListener(listener, started.verifier, serial);
|
|
421
|
+
listener = null;
|
|
422
|
+
handler.requestRender();
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
listener?.close();
|
|
427
|
+
handler.commandContext?.print?.([
|
|
428
|
+
'OpenAI subscription sign-in started from onboarding.',
|
|
429
|
+
` browser: ${browserOpened ? 'opened' : 'open failed'}`,
|
|
430
|
+
` callback listener: ${listener ? 'ready' : 'unavailable'}`,
|
|
431
|
+
' authorizationUrl: shown in the wizard provider step',
|
|
432
|
+
'Paste the callback code or URL into the OpenAI callback field after sign-in.',
|
|
433
|
+
].join('\n'));
|
|
434
|
+
handler.requestRender();
|
|
435
|
+
} catch (error) {
|
|
436
|
+
listener?.close();
|
|
437
|
+
handler.commandContext?.print?.([
|
|
438
|
+
'OpenAI subscription sign-in could not start.',
|
|
439
|
+
` ${error instanceof Error ? error.message : String(error)}`,
|
|
440
|
+
].join('\n'));
|
|
441
|
+
handler.requestRender();
|
|
442
|
+
} finally {
|
|
443
|
+
handler.onboardingApplyPending = false;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
export async function completeOpenAiSubscriptionFromListenerForHandler(
|
|
448
|
+
handler: InputHandler,
|
|
449
|
+
listener: Awaited<ReturnType<typeof createOAuthLocalListener>>,
|
|
450
|
+
verifier: string,
|
|
451
|
+
serial: number,
|
|
452
|
+
): Promise<void> {
|
|
453
|
+
try {
|
|
454
|
+
const callback = await listener.waitForCode();
|
|
455
|
+
const pending = handler.uiServices.platform.subscriptionManager.getPending('openai');
|
|
456
|
+
if (!pending || pending.verifier !== verifier || serial !== handler.onboardingOpenAiListenerSerial) return;
|
|
457
|
+
const token = await exchangeOpenAICodexCode(callback.code, verifier);
|
|
458
|
+
const now = Date.now();
|
|
459
|
+
const existing = handler.uiServices.platform.subscriptionManager.get('openai');
|
|
460
|
+
handler.uiServices.platform.subscriptionManager.saveSubscription({
|
|
461
|
+
provider: 'openai',
|
|
462
|
+
accessToken: token.accessToken,
|
|
463
|
+
refreshToken: token.refreshToken,
|
|
464
|
+
tokenType: token.tokenType,
|
|
465
|
+
expiresAt: token.expiresAt,
|
|
466
|
+
...(token.scopes ? { scopes: token.scopes } : {}),
|
|
467
|
+
authMode: 'oauth',
|
|
468
|
+
overrideAmbientApiKeys: false,
|
|
469
|
+
createdAt: existing?.createdAt ?? now,
|
|
470
|
+
updatedAt: now,
|
|
471
|
+
});
|
|
472
|
+
handler.uiServices.platform.subscriptionManager.clearPending('openai');
|
|
473
|
+
handler.onboardingOpenAiListenerSerial += 1;
|
|
474
|
+
handler.commandContext?.print?.([
|
|
475
|
+
'OpenAI subscription sign-in completed from onboarding.',
|
|
476
|
+
` tokenType: ${token.tokenType}`,
|
|
477
|
+
` expiresAt: ${token.expiresAt ? new Date(token.expiresAt).toISOString() : 'n/a'}`,
|
|
478
|
+
].join('\n'));
|
|
479
|
+
await handler.refreshOnboardingHydration({ preserveValues: true, targetStepId: 'provider-access' });
|
|
480
|
+
} catch (error) {
|
|
481
|
+
handler.commandContext?.print?.([
|
|
482
|
+
'OpenAI subscription listener could not complete automatically.',
|
|
483
|
+
` listener: ${error instanceof Error ? error.message : String(error)}`,
|
|
484
|
+
'Paste the callback code or URL into the OpenAI callback field to finish in onboarding.',
|
|
485
|
+
].join('\n'));
|
|
486
|
+
handler.requestRender();
|
|
487
|
+
} finally {
|
|
488
|
+
listener.close();
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
export async function handleOpenAiSubscriptionFinishForHandler(handler: InputHandler): Promise<void> {
|
|
493
|
+
if (handler.onboardingApplyPending) return;
|
|
494
|
+
const code = extractAuthorizationCode(handler.onboardingWizard.getTextFieldValue('providers.openai-callback-code'));
|
|
495
|
+
if (!code) {
|
|
496
|
+
handler.commandContext?.print?.('OpenAI subscription sign-in needs a callback code or URL.');
|
|
497
|
+
handler.requestRender();
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
handler.onboardingApplyPending = true;
|
|
502
|
+
try {
|
|
503
|
+
const pending = handler.uiServices.platform.subscriptionManager.getPending('openai');
|
|
504
|
+
if (!pending) {
|
|
505
|
+
handler.commandContext?.print?.('No pending OpenAI subscription sign-in exists in onboarding.');
|
|
506
|
+
handler.requestRender();
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
const token = await exchangeOpenAICodexCode(code, pending.verifier);
|
|
511
|
+
const now = Date.now();
|
|
512
|
+
const existing = handler.uiServices.platform.subscriptionManager.get('openai');
|
|
513
|
+
handler.uiServices.platform.subscriptionManager.saveSubscription({
|
|
514
|
+
provider: 'openai',
|
|
515
|
+
accessToken: token.accessToken,
|
|
516
|
+
refreshToken: token.refreshToken,
|
|
517
|
+
tokenType: token.tokenType,
|
|
518
|
+
expiresAt: token.expiresAt,
|
|
519
|
+
...(token.scopes ? { scopes: token.scopes } : {}),
|
|
520
|
+
authMode: 'oauth',
|
|
521
|
+
overrideAmbientApiKeys: false,
|
|
522
|
+
createdAt: existing?.createdAt ?? now,
|
|
523
|
+
updatedAt: now,
|
|
524
|
+
});
|
|
525
|
+
handler.uiServices.platform.subscriptionManager.clearPending('openai');
|
|
526
|
+
handler.commandContext?.print?.([
|
|
527
|
+
'OpenAI subscription sign-in completed from onboarding.',
|
|
528
|
+
` tokenType: ${token.tokenType}`,
|
|
529
|
+
` expiresAt: ${token.expiresAt ? new Date(token.expiresAt).toISOString() : 'n/a'}`,
|
|
530
|
+
].join('\n'));
|
|
531
|
+
await handler.refreshOnboardingHydration({ preserveValues: true, targetStepId: 'provider-access' });
|
|
532
|
+
} catch (error) {
|
|
533
|
+
handler.commandContext?.print?.([
|
|
534
|
+
'OpenAI subscription sign-in could not finish.',
|
|
535
|
+
` ${error instanceof Error ? error.message : String(error)}`,
|
|
536
|
+
].join('\n'));
|
|
537
|
+
handler.requestRender();
|
|
538
|
+
} finally {
|
|
539
|
+
handler.onboardingApplyPending = false;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
export function syncRuntimeFromOnboardingRequestForHandler(handler: InputHandler, request: ReturnType<OnboardingWizardController['buildApplyRequest']>): void {
|
|
544
|
+
const runtime = handler.commandContext?.session.runtime;
|
|
545
|
+
if (!runtime) return;
|
|
546
|
+
|
|
547
|
+
for (const operation of request.operations) {
|
|
548
|
+
if (operation.kind !== 'set-config') continue;
|
|
549
|
+
if (operation.key === 'provider.model' && typeof operation.value === 'string') {
|
|
550
|
+
runtime.model = operation.value;
|
|
551
|
+
runtime.provider = getProviderIdFromModel(operation.value);
|
|
552
|
+
}
|
|
553
|
+
if (operation.key === 'provider.reasoningEffort' && typeof operation.value === 'string') runtime.reasoningEffort = operation.value;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
export function getOnboardingConfigValueForHandler(handler: InputHandler, request: OnboardingApplyRequest, key: string): unknown {
|
|
558
|
+
const config = handler.uiServices.platform.configManager;
|
|
559
|
+
for (let index = request.operations.length - 1; index >= 0; index -= 1) {
|
|
560
|
+
const operation = request.operations[index];
|
|
561
|
+
if (operation?.kind === 'set-config' && operation.key === key) return operation.value;
|
|
562
|
+
}
|
|
563
|
+
return config.get(key as never);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
export function getOnboardingRuntimePostureForHandler(handler: InputHandler, request: OnboardingApplyRequest): OnboardingRuntimePosture {
|
|
567
|
+
const getConfigValue = (key: string): unknown => handler.getOnboardingConfigValue(request, key);
|
|
568
|
+
const serviceEnabled = getConfigValue('service.enabled') === true;
|
|
569
|
+
const serviceAutostart = getConfigValue('service.autostart') === true;
|
|
570
|
+
const restartOnFailure = getConfigValue('service.restartOnFailure') === true;
|
|
571
|
+
const daemonEnabled = getConfigValue('danger.daemon') === true || getConfigValue('controlPlane.enabled') === true;
|
|
572
|
+
const listenerEnabled = getConfigValue('danger.httpListener') === true;
|
|
573
|
+
const webEnabled = getConfigValue('web.enabled') === true;
|
|
574
|
+
const controlPlaneRemote = getConfigValue('controlPlane.hostMode') === 'network'
|
|
575
|
+
|| (getConfigValue('controlPlane.hostMode') === 'custom'
|
|
576
|
+
&& !isLoopbackHostValue(String(getConfigValue('controlPlane.host') ?? '')))
|
|
577
|
+
|| getConfigValue('controlPlane.allowRemote') === true;
|
|
578
|
+
const listenerRemote = getConfigValue('httpListener.hostMode') === 'network'
|
|
579
|
+
|| (getConfigValue('httpListener.hostMode') === 'custom'
|
|
580
|
+
&& !isLoopbackHostValue(String(getConfigValue('httpListener.host') ?? '')));
|
|
581
|
+
const webRemote = getConfigValue('web.hostMode') === 'network'
|
|
582
|
+
|| (getConfigValue('web.hostMode') === 'custom'
|
|
583
|
+
&& !isLoopbackHostValue(String(getConfigValue('web.host') ?? '')));
|
|
584
|
+
const remoteExposure = controlPlaneRemote || listenerRemote || webRemote;
|
|
585
|
+
|
|
586
|
+
return {
|
|
587
|
+
serviceEnabled,
|
|
588
|
+
serviceAutostart,
|
|
589
|
+
restartOnFailure,
|
|
590
|
+
expectedDaemon: daemonEnabled || webEnabled,
|
|
591
|
+
expectedHttpListener: listenerEnabled,
|
|
592
|
+
serverBacked: serviceEnabled || daemonEnabled || listenerEnabled || webEnabled,
|
|
593
|
+
remoteExposure,
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
export async function restartOnboardingExternalServicesIfNeededForHandler(handler: InputHandler, request: OnboardingApplyRequest): Promise<OnboardingVerificationItem[]> {
|
|
598
|
+
const externalServices = handler.uiServices.platform.externalServices;
|
|
599
|
+
const state = externalServices?.inspect();
|
|
600
|
+
const daemonStatus = state?.daemonStatus?.reason ?? (state?.daemonRunning ? 'external daemon appears active' : 'external daemon is not verified from this shell');
|
|
601
|
+
return [{
|
|
602
|
+
id: 'runtime:external-daemon-owned',
|
|
603
|
+
status: 'pass',
|
|
604
|
+
message: `GoodVibes Agent did not start, stop, or restart daemon/listener services; daemon lifecycle is external. ${daemonStatus}`,
|
|
605
|
+
target: 'service',
|
|
606
|
+
}];
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
export function verifyOnboardingRuntimePostureForHandler(handler: InputHandler, request: OnboardingApplyRequest): OnboardingVerificationItem[] {
|
|
610
|
+
const externalServices = handler.uiServices.platform.externalServices;
|
|
611
|
+
const externalState = externalServices?.inspect();
|
|
612
|
+
return [{
|
|
613
|
+
id: 'runtime:external-daemon-owned',
|
|
614
|
+
status: 'pass',
|
|
615
|
+
message: externalState
|
|
616
|
+
? 'Daemon/listener lifecycle is externally managed; Agent onboarding did not request service shutdown, startup, restart, or bind changes.'
|
|
617
|
+
: 'Daemon/listener lifecycle is externally managed; no local service controller is required for Agent onboarding.',
|
|
618
|
+
target: 'service',
|
|
619
|
+
}];
|
|
620
|
+
}
|