@pellux/goodvibes-tui 1.27.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +116 -2
- package/README.md +7 -9
- package/bin/goodvibes +2 -2
- package/bin/launcher-support.js +8 -4
- package/docs/README.md +11 -2
- package/docs/foundation-artifacts/operator-contract.json +6403 -2347
- package/package.json +14 -25
- package/scripts/postinstall.js +35 -35
- package/src/audio/spoken-turn-model-routing.ts +1 -1
- package/src/cli/bundle-command.ts +5 -5
- package/src/cli/completions/generate.ts +1 -1
- package/src/cli/doctor.ts +3 -3
- package/src/cli/entrypoint.ts +12 -7
- package/src/cli/help.ts +4 -5
- package/src/cli/hooks-command.ts +1 -1
- package/src/cli/index.ts +1 -4
- package/src/cli/launch-auto-update.ts +59 -7
- package/src/cli/management-commands.ts +6 -6
- package/src/cli/management-utils.ts +5 -5
- package/src/cli/management.ts +7 -7
- package/src/cli/network-posture.ts +1 -1
- package/src/cli/package-verification.ts +7 -6
- package/src/cli/plugin-bundles-command.ts +1 -1
- package/src/cli/plugin-command.ts +2 -2
- package/src/cli/service-command.ts +2 -2
- package/src/cli/service-posture.ts +4 -4
- package/src/cli/status.ts +9 -8
- package/src/cli/surface-command.ts +4 -4
- package/src/cli/tui-startup.ts +3 -3
- package/src/cli-flags.ts +3 -3
- package/src/config/goodvibes-home-audit.ts +0 -2
- package/src/config/secret-config.ts +37 -5
- package/src/config/tui-extension-settings.ts +1 -1
- package/src/core/approval-alert.ts +2 -2
- package/src/core/budget-breach-notifier.ts +3 -3
- package/src/core/conversation-compaction-render.ts +1 -1
- package/src/core/conversation-line-cache.ts +1 -1
- package/src/core/conversation-render-context.ts +1 -1
- package/src/core/conversation-rendering.ts +2 -2
- package/src/core/conversation.ts +10 -10
- package/src/core/long-task-notifier.ts +2 -2
- package/src/core/memory-diagnostics-gateway.ts +1 -1
- package/src/core/scriptable-statusline.ts +1 -1
- package/src/core/session-auto-titler.ts +1 -1
- package/src/core/session-resume-core.ts +2 -2
- package/src/core/stream-event-wiring.ts +24 -44
- package/src/core/terminal-notifier.ts +3 -3
- package/src/core/turn-cancellation.ts +1 -1
- package/src/core/turn-event-wiring.ts +5 -5
- package/src/input/command-registry.ts +6 -6
- package/src/input/commands/channel-pairing.ts +1 -1
- package/src/input/commands/cluster-runtime.ts +1 -1
- package/src/input/commands/codebase-runtime.ts +1 -1
- package/src/input/commands/devices-runtime.ts +1 -1
- package/src/input/commands/hosted-runtime.ts +330 -0
- package/src/input/commands/local-setup-review.ts +1 -1
- package/src/input/commands/local-setup-transfer.ts +2 -2
- package/src/input/commands/local-setup.ts +3 -3
- package/src/input/commands/managed-runtime.ts +2 -2
- package/src/input/commands/operator-rpc.ts +24 -110
- package/src/input/commands/payment-card-intake.ts +1 -1
- package/src/input/commands/permissions-provenance.ts +2 -2
- package/src/input/commands/plugin-runtime.ts +1 -1
- package/src/input/commands/provider.ts +1 -1
- package/src/input/commands/relay-runtime.ts +18 -9
- package/src/input/commands/rewind-runtime.ts +1 -1
- package/src/input/commands/runtime-services.ts +1 -1
- package/src/input/commands/services-runtime.ts +1 -1
- package/src/input/commands/session-workflow.ts +1 -1
- package/src/input/commands/settings-sync-runtime.ts +2 -2
- package/src/input/commands/share-runtime.ts +1 -1
- package/src/input/commands/tasks-runtime.ts +145 -55
- package/src/input/commands/update-runtime.ts +31 -28
- package/src/input/commands/work-plan-runtime.ts +2 -2
- package/src/input/commands/workstream-attempts.ts +1 -1
- package/src/input/commands/workstream-runtime.ts +2 -2
- package/src/input/commands.ts +2 -0
- package/src/input/feature-unit-layout.ts +1 -1
- package/src/input/feed-context-factory.ts +3 -3
- package/src/input/handler-content-actions.ts +2 -2
- package/src/input/handler-feed-routes.ts +1 -1
- package/src/input/handler-feed.ts +3 -3
- package/src/input/handler-modal-token-routes.ts +1 -1
- package/src/input/handler-onboarding-daemon-adopt.ts +3 -2
- package/src/input/handler-onboarding.ts +1 -1
- package/src/input/handler-types.ts +2 -2
- package/src/input/handler-ui-state.ts +1 -1
- package/src/input/handler.ts +4 -4
- package/src/input/input-history.ts +1 -1
- package/src/input/onboarding/onboarding-feature-units.ts +2 -2
- package/src/input/onboarding/onboarding-runtime-status.ts +15 -1
- package/src/input/onboarding/onboarding-wizard-apply.ts +17 -8
- package/src/input/onboarding/onboarding-wizard-cloudflare-step.ts +5 -3
- package/src/input/onboarding/onboarding-wizard-external-surfaces.ts +1 -1
- package/src/input/onboarding/onboarding-wizard-types.ts +1 -1
- package/src/input/payments-config.ts +6 -6
- package/src/input/search.ts +1 -1
- package/src/input/session-picker-modal.ts +46 -0
- package/src/input/settings-modal-activation.ts +1 -1
- package/src/input/settings-modal-behavior.ts +4 -4
- package/src/input/settings-modal-commit.ts +11 -0
- package/src/input/settings-modal-data.ts +13 -80
- package/src/input/settings-modal-mutations.ts +33 -0
- package/src/input/settings-modal-secrets.ts +34 -0
- package/src/input/settings-modal-subscriptions.ts +1 -1
- package/src/input/settings-modal-types.ts +15 -1
- package/src/input/settings-modal.ts +28 -3
- package/src/input/worktree-setup-config.ts +1 -1
- package/src/main.ts +11 -9
- package/src/panels/agent-inspector-shared.ts +2 -2
- package/src/panels/base-panel.ts +2 -2
- package/src/panels/builtin/operations.ts +6 -8
- package/src/panels/builtin/session.ts +17 -0
- package/src/panels/builtin/shared.ts +5 -5
- package/src/panels/builtin-modals.ts +3 -3
- package/src/panels/confirm-state.ts +1 -1
- package/src/panels/cost-tracker-panel.ts +4 -4
- package/src/panels/diff-panel.ts +2 -2
- package/src/panels/diff-review-panel.ts +2 -2
- package/src/panels/expandable-list-panel.ts +2 -2
- package/src/panels/fleet-acts.ts +1 -1
- package/src/panels/fleet-observed-render.ts +1 -1
- package/src/panels/fleet-panel-format.ts +1 -1
- package/src/panels/fleet-panel.ts +1 -1
- package/src/panels/fleet-spawn.ts +1 -1
- package/src/panels/fleet-steer.ts +1 -1
- package/src/panels/fleet-transcript.ts +1 -1
- package/src/panels/git-panel.ts +1 -1
- package/src/panels/hosted-session-feed.ts +352 -0
- package/src/panels/hosted-session-panel.ts +221 -0
- package/src/panels/local-auth-panel.ts +3 -3
- package/src/panels/modals/devices-modal.ts +1 -1
- package/src/panels/modals/local-auth-modal.ts +1 -1
- package/src/panels/modals/pairing-modal.ts +1 -1
- package/src/panels/modals/services-modal.ts +1 -1
- package/src/panels/modals/settings-sync-modal.ts +1 -1
- package/src/panels/modals/subscription-modal.ts +1 -1
- package/src/panels/modals/work-plan-modal.ts +1 -1
- package/src/panels/notifications-panel.ts +1 -1
- package/src/panels/panel-confirm-overlay.ts +1 -1
- package/src/panels/plugins-panel.ts +2 -2
- package/src/panels/polish-core.ts +2 -2
- package/src/panels/polish-tables.ts +1 -1
- package/src/panels/polish.ts +2 -2
- package/src/panels/scrollable-list-panel.ts +3 -3
- package/src/panels/skills-panel.ts +3 -3
- package/src/panels/token-budget-panel.ts +5 -5
- package/src/panels/types.ts +1 -2
- package/src/permissions/prompt.ts +1 -1
- package/src/renderer/autocomplete-overlay.ts +1 -1
- package/src/renderer/block-actions-overlay.ts +1 -1
- package/src/renderer/bookmark-modal.ts +1 -1
- package/src/renderer/buffer.ts +1 -1
- package/src/renderer/code-block.ts +1 -1
- package/src/renderer/composer-fragments.ts +1 -1
- package/src/renderer/compositor.ts +2 -2
- package/src/renderer/config-modal.ts +1 -1
- package/src/renderer/context-inspector.ts +1 -1
- package/src/renderer/conversation-layout.ts +2 -2
- package/src/renderer/conversation-overlays.ts +1 -1
- package/src/renderer/conversation-surface.ts +2 -2
- package/src/renderer/diff-view.ts +1 -1
- package/src/renderer/diff.ts +1 -1
- package/src/renderer/file-picker-overlay.ts +1 -1
- package/src/renderer/fleet-tab-strip.ts +1 -1
- package/src/renderer/fullscreen-primitives.ts +2 -2
- package/src/renderer/fullscreen-workspace.ts +1 -1
- package/src/renderer/header-line.ts +1 -1
- package/src/renderer/help-overlay.ts +1 -1
- package/src/renderer/history-search-overlay.ts +2 -2
- package/src/renderer/layout.ts +5 -8
- package/src/renderer/markdown-inline.ts +1 -1
- package/src/renderer/markdown-table.ts +1 -1
- package/src/renderer/markdown.ts +1 -1
- package/src/renderer/mcp-workspace.ts +1 -1
- package/src/renderer/modal-factory.ts +2 -2
- package/src/renderer/model-workspace.ts +2 -2
- package/src/renderer/onboarding/onboarding-wizard.ts +1 -1
- package/src/renderer/overlay-box.ts +1 -1
- package/src/renderer/panel-composite.ts +1 -1
- package/src/renderer/panel-workspace-bar.ts +1 -1
- package/src/renderer/process-indicator.ts +1 -1
- package/src/renderer/profile-picker-modal.ts +1 -1
- package/src/renderer/search-overlay.ts +2 -2
- package/src/renderer/selection-modal-overlay.ts +1 -1
- package/src/renderer/session-picker-modal.ts +124 -3
- package/src/renderer/settings-modal-helpers.ts +2 -0
- package/src/renderer/settings-modal.ts +2 -2
- package/src/renderer/shell-surface.ts +2 -2
- package/src/renderer/system-message.ts +1 -1
- package/src/renderer/tab-strip.ts +1 -1
- package/src/renderer/theme-mode-config.ts +1 -1
- package/src/renderer/theme.ts +11 -12
- package/src/renderer/thinking.ts +1 -1
- package/src/renderer/tool-call.ts +2 -2
- package/src/renderer/tool-result-expanded-lines.ts +1 -1
- package/src/renderer/turn-injection.ts +7 -8
- package/src/renderer/ui-factory.ts +3 -3
- package/src/runtime/bootstrap-command-context.ts +4 -4
- package/src/runtime/bootstrap-command-parts.ts +5 -5
- package/src/runtime/bootstrap-core.ts +30 -30
- package/src/runtime/bootstrap-shell.ts +4 -4
- package/src/runtime/bootstrap.ts +128 -157
- package/src/runtime/client/approval-updates.ts +82 -0
- package/src/runtime/client/build-floors.ts +167 -0
- package/src/runtime/client/daemon-attach-handshake.ts +115 -0
- package/src/runtime/client/fleet-union.ts +147 -0
- package/src/runtime/client/host-service-status.ts +71 -0
- package/src/runtime/client/hosted-exit.ts +86 -0
- package/src/runtime/client/hosted-roster.ts +110 -0
- package/src/runtime/client/hosted-session-stream.ts +178 -0
- package/src/runtime/client/hosted-sessions.ts +187 -0
- package/src/runtime/client/operator-endpoint.ts +213 -0
- package/src/runtime/client/spine-adoption.ts +99 -0
- package/src/runtime/cloudflare-control-plane.ts +1 -1
- package/src/runtime/context-accounting-source.ts +1 -1
- package/src/runtime/context.ts +2 -2
- package/src/runtime/conversation-rewind-port.ts +42 -14
- package/src/runtime/daemon-attach-notices.ts +55 -19
- package/src/runtime/diagnostics/panels/ops.ts +1 -1
- package/src/runtime/disposal-wiring.ts +4 -24
- package/src/runtime/index.ts +119 -105
- package/src/runtime/install-self-check-startup.ts +2 -2
- package/src/runtime/interaction-seams.ts +1 -1
- package/src/runtime/knowledge-services.ts +8 -2
- package/src/runtime/legacy-daemon-migration.ts +4 -3
- package/src/runtime/legacy-daemon-reconcile.ts +3 -2
- package/src/runtime/memory-spine-transport.ts +37 -267
- package/src/runtime/notification-dispatch.ts +2 -2
- package/src/runtime/onboarding/apply.ts +2 -2
- package/src/runtime/onboarding/derivation.ts +2 -2
- package/src/runtime/onboarding/markers.ts +2 -2
- package/src/runtime/onboarding/progress.ts +2 -2
- package/src/runtime/onboarding/snapshot.ts +1 -1
- package/src/runtime/onboarding/state.ts +2 -2
- package/src/runtime/onboarding/types.ts +2 -2
- package/src/runtime/orchestrator-core-services.ts +1 -1
- package/src/runtime/path-shadow-startup.ts +28 -221
- package/src/runtime/power-keepawake-remote.ts +3 -2
- package/src/runtime/process-lifecycle.ts +2 -2
- package/src/runtime/provider-fallback.ts +10 -66
- package/src/runtime/recovery-autosave.ts +1 -1
- package/src/runtime/recovery-offer-wiring.ts +39 -23
- package/src/runtime/recovery-prompt.ts +31 -17
- package/src/runtime/relay-reachability-bridge.ts +53 -42
- package/src/runtime/release-artifacts.ts +2 -1
- package/src/runtime/runtime-services-types.ts +82 -35
- package/src/runtime/services.ts +305 -300
- package/src/runtime/session-resume-liveness-confirm.ts +1 -1
- package/src/runtime/ui-services.ts +7 -6
- package/src/runtime/update-check.ts +23 -43
- package/src/runtime/workspace-checkpointing.ts +1 -1
- package/src/runtime/wrfc-persistence.ts +2 -2
- package/src/shell/ui-openers.ts +30 -7
- package/src/utils/clipboard.ts +1 -1
- package/src/verification/verification-ledger.ts +10 -8
- package/src/version.ts +1 -1
- package/bin/goodvibes-daemon +0 -90
- package/src/cli/config-overrides.ts +0 -267
- package/src/cli/endpoints.ts +0 -109
- package/src/cli/parser.ts +0 -395
- package/src/cli/redaction.ts +0 -170
- package/src/cli/types.ts +0 -84
- package/src/cluster/commands.ts +0 -382
- package/src/cluster/remote-daemon-target.ts +0 -225
- package/src/cluster/render.ts +0 -268
- package/src/config/atomic-write.ts +0 -70
- package/src/config/credential-availability.ts +0 -122
- package/src/config/goodvibes-home.ts +0 -149
- package/src/config/index.ts +0 -88
- package/src/config/provider-model.ts +0 -23
- package/src/config/read-versioned.ts +0 -228
- package/src/core/alert-gating.ts +0 -67
- package/src/core/consolidation-receipt.ts +0 -41
- package/src/core/focus-tracker.ts +0 -41
- package/src/core/history.ts +0 -45
- package/src/core/memory-status.ts +0 -118
- package/src/core/orchestrator.ts +0 -7
- package/src/core/pairing-devices.ts +0 -49
- package/src/core/pairing-handoff.ts +0 -101
- package/src/core/pairing-offers.ts +0 -83
- package/src/core/pairing-origin.ts +0 -78
- package/src/core/rewind-turn-anchors.ts +0 -311
- package/src/core/session-recovery.ts +0 -210
- package/src/core/stable-host.ts +0 -171
- package/src/core/transcript-journal.ts +0 -503
- package/src/daemon/cli.ts +0 -693
- package/src/daemon/fatal-boot-report.ts +0 -111
- package/src/daemon/handlers/context.ts +0 -29
- package/src/daemon/handlers/contracts.ts +0 -77
- package/src/daemon/handlers/credentials.ts +0 -139
- package/src/daemon/handlers/drafts/draft-store.ts +0 -427
- package/src/daemon/handlers/drafts/index.ts +0 -17
- package/src/daemon/handlers/drafts/register.ts +0 -331
- package/src/daemon/handlers/errors.ts +0 -18
- package/src/daemon/handlers/inbox/cursor-store.ts +0 -457
- package/src/daemon/handlers/inbox/index.ts +0 -284
- package/src/daemon/handlers/inbox/mapping.ts +0 -192
- package/src/daemon/handlers/inbox/poller.ts +0 -212
- package/src/daemon/handlers/inbox/provider-adapter.ts +0 -156
- package/src/daemon/handlers/inbox/providers/discord.ts +0 -252
- package/src/daemon/handlers/inbox/providers/email.ts +0 -152
- package/src/daemon/handlers/inbox/providers/imap-client.ts +0 -300
- package/src/daemon/handlers/inbox/providers/route-util.ts +0 -24
- package/src/daemon/handlers/inbox/providers/slack.ts +0 -263
- package/src/daemon/handlers/index.ts +0 -117
- package/src/daemon/handlers/register.ts +0 -168
- package/src/daemon/handlers/remote/backends/cloud-terminal.ts +0 -142
- package/src/daemon/handlers/remote/backends/docker.ts +0 -79
- package/src/daemon/handlers/remote/backends/index.ts +0 -40
- package/src/daemon/handlers/remote/backends/local-process.ts +0 -113
- package/src/daemon/handlers/remote/backends/process-runner.ts +0 -127
- package/src/daemon/handlers/remote/backends/ssh.ts +0 -125
- package/src/daemon/handlers/remote/backends/types.ts +0 -97
- package/src/daemon/handlers/remote/dispatcher.ts +0 -181
- package/src/daemon/handlers/remote/index.ts +0 -119
- package/src/daemon/handlers/remote/peer-registry.ts +0 -357
- package/src/daemon/handlers/remote/service.ts +0 -191
- package/src/daemon/handlers/routing/inbox-bridge.ts +0 -71
- package/src/daemon/handlers/routing/index.ts +0 -261
- package/src/daemon/handlers/routing/route-store.ts +0 -319
- package/src/daemon/handlers/routing/routing-resolver.ts +0 -75
- package/src/daemon/handlers/sqlite-store.ts +0 -278
- package/src/daemon/handlers/triage/index.ts +0 -57
- package/src/daemon/handlers/triage/integration.ts +0 -213
- package/src/daemon/handlers/triage/pipeline.ts +0 -274
- package/src/daemon/handlers/triage/scorer.ts +0 -287
- package/src/daemon/handlers/triage/tagger/discord.ts +0 -187
- package/src/daemon/handlers/triage/tagger/imap.ts +0 -384
- package/src/daemon/handlers/triage/tagger/index.ts +0 -184
- package/src/daemon/handlers/triage/tagger/shared.ts +0 -70
- package/src/daemon/handlers/triage/tagger/slack.ts +0 -69
- package/src/daemon/handlers/triage/types.ts +0 -50
- package/src/daemon/lifecycle.ts +0 -41
- package/src/daemon/provision-wake-model.ts +0 -81
- package/src/daemon/safe-serve.ts +0 -61
- package/src/daemon/send/channels.ts +0 -200
- package/src/daemon/send/command.ts +0 -332
- package/src/daemon/send/composition.ts +0 -99
- package/src/daemon/send/failure-text.ts +0 -93
- package/src/daemon/send/inert-text.ts +0 -225
- package/src/daemon/send/stdin.ts +0 -24
- package/src/daemon/service-commands.ts +0 -329
- package/src/export/cost-utils.ts +0 -319
- package/src/input/selection.ts +0 -135
- package/src/plugins/loader.ts +0 -15
- package/src/renderer/bottom-bar.ts +0 -58
- package/src/renderer/conversation-tree.ts +0 -228
- package/src/runtime/agent-graph-composition.ts +0 -78
- package/src/runtime/channel-composition.ts +0 -46
- package/src/runtime/cluster-composition.ts +0 -124
- package/src/runtime/cluster-group-composition.ts +0 -284
- package/src/runtime/code-index-services.ts +0 -167
- package/src/runtime/daemon-handler-composition.ts +0 -76
- package/src/runtime/device-posture-composition.ts +0 -110
- package/src/runtime/durability-housekeeping.ts +0 -183
- package/src/runtime/durability-services.ts +0 -155
- package/src/runtime/embedded-service-factories.ts +0 -74
- package/src/runtime/feature-settings.ts +0 -154
- package/src/runtime/fleet-needs-input-push.ts +0 -70
- package/src/runtime/fleet-services.ts +0 -41
- package/src/runtime/idle-power-services.ts +0 -85
- package/src/runtime/mail-composition.ts +0 -65
- package/src/runtime/memory-fold.ts +0 -55
- package/src/runtime/operator-token-cleanup.ts +0 -109
- package/src/runtime/path-shadow.ts +0 -418
- package/src/runtime/perf/panel-health-monitor.ts +0 -18
- package/src/runtime/provider-optimizer-wiring.ts +0 -55
- package/src/runtime/reachability-notice.ts +0 -100
- package/src/runtime/remote-execution-composition.ts +0 -62
- package/src/runtime/session-liveness-marker.ts +0 -257
- package/src/runtime/session-pointer-surface.ts +0 -22
- package/src/runtime/session-storage-services.ts +0 -41
- package/src/runtime/surface-feature-flags.ts +0 -96
- package/src/runtime/terminal-output-guard.ts +0 -235
- package/src/runtime/trust/workspace-registration.ts +0 -192
- package/src/runtime/trust/workspace-trust.ts +0 -229
- package/src/runtime/ui/index.ts +0 -39
- package/src/runtime/ui/model-picker/data-provider.ts +0 -182
- package/src/runtime/ui/model-picker/health-enrichment.ts +0 -231
- package/src/runtime/ui/model-picker/index.ts +0 -59
- package/src/runtime/ui/model-picker/types.ts +0 -153
- package/src/runtime/ui/provider-health/data-provider.ts +0 -244
- package/src/runtime/ui/provider-health/fallback-visualizer.ts +0 -71
- package/src/runtime/ui/provider-health/index.ts +0 -46
- package/src/runtime/ui/provider-health/types.ts +0 -146
- package/src/runtime/ui-events.ts +0 -1
- package/src/runtime/ui-read-model-helpers.ts +0 -1
- package/src/runtime/ui-read-models-observability-maintenance.ts +0 -1
- package/src/runtime/ui-read-models-observability-options.ts +0 -1
- package/src/runtime/ui-read-models-observability-remote.ts +0 -1
- package/src/runtime/ui-read-models-observability-security.ts +0 -1
- package/src/runtime/ui-read-models-observability-system.ts +0 -1
- package/src/runtime/ui-read-models-observability.ts +0 -1
- package/src/runtime/ui-service-queries.ts +0 -1
- package/src/runtime/voice-setup-services.ts +0 -98
- package/src/runtime/workstream-attempts-validation.ts +0 -77
- package/src/runtime/workstream-draft-edits.ts +0 -95
- package/src/runtime/workstream-draft-store.ts +0 -274
- package/src/runtime/workstream-draft-types.ts +0 -42
- package/src/runtime/workstream-services.ts +0 -373
- package/src/tools/index.ts +0 -1
- package/src/types/grid.ts +0 -48
- package/src/types/sql-js.d.ts +0 -15
- package/src/work-plans/work-plan-store.ts +0 -702
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hosted-runtime.ts — `/hosted`, the front door to a conversation the DAEMON
|
|
3
|
+
* runs.
|
|
4
|
+
*
|
|
5
|
+
* Local sessions are unchanged and remain the default experience: a turn typed
|
|
6
|
+
* into the composer runs in this process, exactly as it always has. `/hosted`
|
|
7
|
+
* is the opt-in — it asks the daemon to compose the same loop on its side, so
|
|
8
|
+
* the conversation does not depend on this window staying open.
|
|
9
|
+
*
|
|
10
|
+
* ── The subcommands, and why they are these ───────────────────────────────
|
|
11
|
+
*
|
|
12
|
+
* /hosted what this terminal is attached to, and what
|
|
13
|
+
* leaving would do
|
|
14
|
+
* /hosted new [prompt…] create one here and attach; `--survive` /
|
|
15
|
+
* `--kill` set this session's own detach override
|
|
16
|
+
* /hosted list [--all] every hosted session the daemon has
|
|
17
|
+
* /hosted attach <id|n> join one and backfill its transcript
|
|
18
|
+
* /hosted say <text…> steer the attached session (ordinary verb)
|
|
19
|
+
* /hosted later <text…> queue a follow-up (ordinary verb)
|
|
20
|
+
* /hosted cancel [callId] cancel one in-flight tool call (ordinary verb)
|
|
21
|
+
* /hosted detach leave — the policy the record names then applies
|
|
22
|
+
* /hosted kill [id] end it regardless of policy
|
|
23
|
+
*
|
|
24
|
+
* There is no `/hosted steer` beside `say` and no hosted-only cancel: those
|
|
25
|
+
* operations are `sessions.steer`, `sessions.followUp` and
|
|
26
|
+
* `sessions.toolCalls.cancel`, which resolve a hosted id daemon-side. This
|
|
27
|
+
* command spells each one once.
|
|
28
|
+
*/
|
|
29
|
+
import type { CommandContext, CommandRegistry } from '../command-registry.ts';
|
|
30
|
+
import { describeOperatorRpcError } from './operator-rpc.ts';
|
|
31
|
+
import { requireShellPaths } from './runtime-services.ts';
|
|
32
|
+
import {
|
|
33
|
+
createDaemonVerbCaller,
|
|
34
|
+
resolveControlPlaneBaseUrl,
|
|
35
|
+
resolveDaemonStateDirectory,
|
|
36
|
+
} from '../../runtime/client/operator-endpoint.ts';
|
|
37
|
+
import {
|
|
38
|
+
createHostedSessionsClient,
|
|
39
|
+
describeDetachEffect,
|
|
40
|
+
hostedSessionLabel,
|
|
41
|
+
type HostedDetachPolicy,
|
|
42
|
+
type HostedSessionRecord,
|
|
43
|
+
type HostedSessionsClient,
|
|
44
|
+
} from '../../runtime/client/hosted-sessions.ts';
|
|
45
|
+
import { watchHostedSession, type HostedSessionSubscription } from '../../runtime/client/hosted-session-stream.ts';
|
|
46
|
+
import { getOrCreateCompanionToken } from '@pellux/goodvibes-sdk/platform/pairing';
|
|
47
|
+
import { getSharedHostedSessionFeed, type HostedSessionFeed } from '../../panels/hosted-session-feed.ts';
|
|
48
|
+
import { getSharedHostedSessionRoster } from '../../runtime/client/hosted-roster.ts';
|
|
49
|
+
|
|
50
|
+
/** The last list this terminal printed, so `attach 2` means what the user just read. */
|
|
51
|
+
let lastListedIds: readonly string[] = [];
|
|
52
|
+
|
|
53
|
+
/** One row of `/hosted list`, in the record's own words. */
|
|
54
|
+
export function renderHostedRecordLine(record: HostedSessionRecord, index: number): string {
|
|
55
|
+
const attached = record.attachedClients.length > 0 ? record.attachedClients.join(', ') : 'nobody';
|
|
56
|
+
const ended = record.status === 'terminated'
|
|
57
|
+
? ` — ended: ${record.terminatedReason ?? 'no reason recorded'}`
|
|
58
|
+
: '';
|
|
59
|
+
return [
|
|
60
|
+
` ${index + 1}. ${hostedSessionLabel(record)} [${record.status}]${ended}`,
|
|
61
|
+
` id ${record.id}`,
|
|
62
|
+
` ${describeDetachEffect(record)}`,
|
|
63
|
+
` ${record.turnCount} turn(s), ${record.messageCount} message(s); attached: ${attached}`,
|
|
64
|
+
].join('\n');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** The status block `/hosted` with no arguments prints. */
|
|
68
|
+
export function renderHostedStatus(feed: HostedSessionFeed): string {
|
|
69
|
+
const state = feed.getState();
|
|
70
|
+
if (!state.record) {
|
|
71
|
+
return [
|
|
72
|
+
'No hosted session is attached in this terminal.',
|
|
73
|
+
' A hosted session runs inside the daemon, so it can outlive this window.',
|
|
74
|
+
' /hosted new [prompt] start one and attach',
|
|
75
|
+
' /hosted list see what already exists',
|
|
76
|
+
].join('\n');
|
|
77
|
+
}
|
|
78
|
+
const record = state.record;
|
|
79
|
+
return [
|
|
80
|
+
`Attached to ${hostedSessionLabel(record)} [${record.status}]`,
|
|
81
|
+
` id ${record.id}`,
|
|
82
|
+
` workspace ${record.workspaceRoot}`,
|
|
83
|
+
` ${describeDetachEffect(record)}`,
|
|
84
|
+
` ${record.turnCount} turn(s), ${record.messageCount} message(s)`,
|
|
85
|
+
state.streaming
|
|
86
|
+
? ' live event stream open'
|
|
87
|
+
: ` no live stream — ${state.streamNote ?? 'not subscribed'}`,
|
|
88
|
+
state.runningToolCalls.length > 0
|
|
89
|
+
? ` running: ${state.runningToolCalls.map((call) => `${call.tool} (${call.callId})`).join(', ')}`
|
|
90
|
+
: ' no tool call is running',
|
|
91
|
+
].join('\n');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Read `--survive` / `--kill` out of the arguments.
|
|
96
|
+
*
|
|
97
|
+
* Absent means "follow the `hostedSessions.detachPolicy` setting", which is a
|
|
98
|
+
* different thing from either value and is what most sessions want: the record
|
|
99
|
+
* then reports the setting's current answer rather than a snapshot of it.
|
|
100
|
+
*/
|
|
101
|
+
export function readDetachOverride(args: readonly string[]): {
|
|
102
|
+
readonly policy: HostedDetachPolicy | undefined;
|
|
103
|
+
readonly rest: string[];
|
|
104
|
+
} {
|
|
105
|
+
const rest: string[] = [];
|
|
106
|
+
let policy: HostedDetachPolicy | undefined;
|
|
107
|
+
for (const arg of args) {
|
|
108
|
+
if (arg === '--survive') { policy = 'survive'; continue; }
|
|
109
|
+
if (arg === '--kill') { policy = 'kill'; continue; }
|
|
110
|
+
rest.push(arg);
|
|
111
|
+
}
|
|
112
|
+
return { policy, rest };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
interface HostedCommandSeams {
|
|
116
|
+
readonly client: HostedSessionsClient;
|
|
117
|
+
readonly baseUrl: string | null;
|
|
118
|
+
readonly authToken: string | null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Resolve everything a `/hosted` subcommand needs, or refuse honestly.
|
|
123
|
+
*
|
|
124
|
+
* The verbs go through `createDaemonVerbCaller` because every
|
|
125
|
+
* `sessions.hosted.*` verb is ws-declared with no REST path; the stream needs
|
|
126
|
+
* the base URL and bearer directly, which is the same resolution the verb
|
|
127
|
+
* caller performs internally.
|
|
128
|
+
*/
|
|
129
|
+
function resolveSeams(context: CommandContext): HostedCommandSeams | null {
|
|
130
|
+
const configManager = context.platform.configManager;
|
|
131
|
+
const homeDirectory = (): string => requireShellPaths(context).homeDirectory;
|
|
132
|
+
const client = createHostedSessionsClient(createDaemonVerbCaller({ configManager, homeDirectory }));
|
|
133
|
+
const baseUrl = resolveControlPlaneBaseUrl(configManager);
|
|
134
|
+
if (!baseUrl) return { client, baseUrl: null, authToken: null };
|
|
135
|
+
let authToken: string | null = null;
|
|
136
|
+
try {
|
|
137
|
+
authToken = getOrCreateCompanionToken('tui', {
|
|
138
|
+
daemonHomeDir: resolveDaemonStateDirectory(homeDirectory()),
|
|
139
|
+
}).token;
|
|
140
|
+
} catch {
|
|
141
|
+
// A token this terminal cannot mint is a stream it cannot open; the verbs
|
|
142
|
+
// resolve their own and still work, so this is not a refusal of the command.
|
|
143
|
+
authToken = null;
|
|
144
|
+
}
|
|
145
|
+
return { client, baseUrl, authToken };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Open the live stream for the attached session, and say so in the feed either way. */
|
|
149
|
+
async function openStream(
|
|
150
|
+
seams: HostedCommandSeams,
|
|
151
|
+
sessionId: string,
|
|
152
|
+
feed: HostedSessionFeed,
|
|
153
|
+
): Promise<void> {
|
|
154
|
+
// The feed owns the subscription handle: the exit path has to be able to
|
|
155
|
+
// close it too, and it has no way to reach a variable in this module.
|
|
156
|
+
feed.closeStream();
|
|
157
|
+
if (!seams.baseUrl) {
|
|
158
|
+
feed.setStreaming(false, 'no control-plane base URL is configured, so nothing can be watched live');
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (!seams.authToken) {
|
|
162
|
+
feed.setStreaming(false, 'no bearer token could be read for the event stream');
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const token = seams.authToken;
|
|
166
|
+
const subscription: HostedSessionSubscription | null = await watchHostedSession({
|
|
167
|
+
baseUrl: seams.baseUrl,
|
|
168
|
+
sessionId,
|
|
169
|
+
getAuthToken: () => token,
|
|
170
|
+
onEvent: (event) => feed.apply(event),
|
|
171
|
+
onLifecycle: (update) => feed.applyLifecycle(update),
|
|
172
|
+
onTerminate: (error) => {
|
|
173
|
+
feed.setStreaming(false, `the event stream ended: ${describeOperatorRpcError(error)}`);
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
feed.bindStream(subscription ? () => subscription.close() : null);
|
|
177
|
+
feed.setStreaming(
|
|
178
|
+
subscription !== null,
|
|
179
|
+
subscription === null ? 'the daemon would not open an event stream' : null,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** Attach to a session by id, backfill it, and start watching it. */
|
|
184
|
+
async function attachSession(
|
|
185
|
+
seams: HostedCommandSeams,
|
|
186
|
+
sessionId: string,
|
|
187
|
+
feed: HostedSessionFeed,
|
|
188
|
+
context: CommandContext,
|
|
189
|
+
): Promise<void> {
|
|
190
|
+
const attachment = await seams.client.attach(sessionId);
|
|
191
|
+
feed.attach(attachment.session, attachment.history);
|
|
192
|
+
await openStream(seams, attachment.session.id, feed);
|
|
193
|
+
context.print([
|
|
194
|
+
`[hosted] attached to ${hostedSessionLabel(attachment.session)} (${attachment.history.length} message(s) backfilled)`,
|
|
195
|
+
` ${describeDetachEffect(attachment.session)}`,
|
|
196
|
+
].join('\n'));
|
|
197
|
+
context.showPanel?.('hosted');
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** Turn `attach 2` into the id the user just read off `/hosted list`. */
|
|
201
|
+
function resolveIdArgument(argument: string): string {
|
|
202
|
+
const index = Number.parseInt(argument, 10);
|
|
203
|
+
if (Number.isInteger(index) && index >= 1 && index <= lastListedIds.length) {
|
|
204
|
+
return lastListedIds[index - 1] as string;
|
|
205
|
+
}
|
|
206
|
+
return argument;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function registerHostedRuntimeCommands(registry: CommandRegistry): void {
|
|
210
|
+
registry.register({
|
|
211
|
+
name: 'hosted',
|
|
212
|
+
description: 'Daemon-hosted sessions: start, join, steer and leave a conversation whose loop runs in the daemon',
|
|
213
|
+
usage: '[new|list|attach|say|later|cancel|detach|kill] [args]',
|
|
214
|
+
argsHint: '[new|list|attach|say|later|cancel|detach|kill]',
|
|
215
|
+
async handler(args, ctx) {
|
|
216
|
+
const feed = getSharedHostedSessionFeed();
|
|
217
|
+
const sub = (args[0] ?? '').toLowerCase();
|
|
218
|
+
|
|
219
|
+
if (sub === '' || sub === 'status') {
|
|
220
|
+
ctx.print(renderHostedStatus(feed));
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const seams = resolveSeams(ctx);
|
|
225
|
+
if (!seams) return;
|
|
226
|
+
const attachedId = feed.getState().record?.id ?? null;
|
|
227
|
+
|
|
228
|
+
try {
|
|
229
|
+
switch (sub) {
|
|
230
|
+
case 'new': {
|
|
231
|
+
const { policy, rest } = readDetachOverride(args.slice(1));
|
|
232
|
+
const workspaceRoot = requireShellPaths(ctx).workingDirectory;
|
|
233
|
+
const prompt = rest.join(' ').trim();
|
|
234
|
+
const record = await seams.client.create({
|
|
235
|
+
workspaceRoot,
|
|
236
|
+
...(prompt ? { initialPrompt: prompt } : {}),
|
|
237
|
+
...(policy ? { detachPolicy: policy } : {}),
|
|
238
|
+
});
|
|
239
|
+
ctx.print([
|
|
240
|
+
`[hosted] created ${hostedSessionLabel(record)} in ${record.workspaceRoot}`,
|
|
241
|
+
` ${describeDetachEffect(record)}`,
|
|
242
|
+
].join('\n'));
|
|
243
|
+
await attachSession(seams, record.id, feed, ctx);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
case 'list': {
|
|
247
|
+
const includeTerminated = args.includes('--all');
|
|
248
|
+
const records = await seams.client.list({ includeTerminated });
|
|
249
|
+
lastListedIds = records.map((record) => record.id);
|
|
250
|
+
// The session picker reads the same roster, so a list typed here is
|
|
251
|
+
// the list it shows — one answer, not two that drift.
|
|
252
|
+
if (!includeTerminated) getSharedHostedSessionRoster().accept(records);
|
|
253
|
+
if (records.length === 0) {
|
|
254
|
+
ctx.print(includeTerminated
|
|
255
|
+
? '[hosted] the daemon is hosting no sessions, and has no retained terminated ones.'
|
|
256
|
+
: '[hosted] the daemon is hosting no sessions. Add --all to include ones that have ended.');
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
ctx.print([
|
|
260
|
+
`Hosted sessions (${records.length}${includeTerminated ? ', including ended' : ''}):`,
|
|
261
|
+
...records.map(renderHostedRecordLine),
|
|
262
|
+
'',
|
|
263
|
+
' /hosted attach <id or number>',
|
|
264
|
+
].join('\n'));
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
case 'attach': {
|
|
268
|
+
const target = args[1];
|
|
269
|
+
if (!target) { ctx.print('Usage: /hosted attach <id or list number>'); return; }
|
|
270
|
+
await attachSession(seams, resolveIdArgument(target), feed, ctx);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
case 'say':
|
|
274
|
+
case 'later': {
|
|
275
|
+
if (!attachedId) { ctx.print('[hosted] no hosted session is attached — /hosted attach <id> first.'); return; }
|
|
276
|
+
const body = args.slice(1).join(' ').trim();
|
|
277
|
+
if (!body) { ctx.print(`Usage: /hosted ${sub} <text>`); return; }
|
|
278
|
+
if (sub === 'say') await seams.client.steer(attachedId, body);
|
|
279
|
+
else await seams.client.followUp(attachedId, body);
|
|
280
|
+
ctx.print(sub === 'say'
|
|
281
|
+
? '[hosted] sent — its output arrives on the Hosted Session panel.'
|
|
282
|
+
: '[hosted] queued — it runs after the current turn.');
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
case 'cancel': {
|
|
286
|
+
if (!attachedId) { ctx.print('[hosted] no hosted session is attached.'); return; }
|
|
287
|
+
const running = feed.getState().runningToolCalls;
|
|
288
|
+
const callId = args[1] ?? running[0]?.callId;
|
|
289
|
+
if (!callId) { ctx.print('[hosted] no tool call is running in this session. Pass a callId to cancel a specific one.'); return; }
|
|
290
|
+
const cancelled = await seams.client.cancelToolCall(attachedId, callId);
|
|
291
|
+
ctx.print(cancelled
|
|
292
|
+
? `[hosted] cancelled tool call ${callId}.`
|
|
293
|
+
: `[hosted] the daemon did not cancel ${callId} — it may have already settled.`);
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
case 'detach': {
|
|
297
|
+
if (!attachedId) { ctx.print('[hosted] no hosted session is attached.'); return; }
|
|
298
|
+
const record = await seams.client.detach(attachedId);
|
|
299
|
+
feed.clear();
|
|
300
|
+
ctx.print(record.status === 'terminated'
|
|
301
|
+
? `[hosted] detached — the session ended (${record.terminatedReason ?? 'no reason recorded'}), which is what its detach policy said would happen.`
|
|
302
|
+
: '[hosted] detached — the session is still running in the daemon and can be reattached.');
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
case 'kill': {
|
|
306
|
+
const target = args[1] ? resolveIdArgument(args[1]) : attachedId;
|
|
307
|
+
if (!target) { ctx.print('Usage: /hosted kill <id> (or attach one first)'); return; }
|
|
308
|
+
const record = await seams.client.kill(target);
|
|
309
|
+
if (target === attachedId) feed.clear();
|
|
310
|
+
ctx.print(`[hosted] ended ${hostedSessionLabel(record)} — ${record.terminatedReason ?? 'no reason recorded'}.`);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
default:
|
|
314
|
+
ctx.print('Usage: /hosted [status|new|list|attach|say|later|cancel|detach|kill]');
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
} catch (error) {
|
|
318
|
+
ctx.print(`[hosted] ${describeOperatorRpcError(error)}`);
|
|
319
|
+
}
|
|
320
|
+
},
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Tests drive the module-level list memory (`/hosted attach 2`); this resets it
|
|
326
|
+
* between them. The live stream is the feed's, and is reset with the feed.
|
|
327
|
+
*/
|
|
328
|
+
export function resetHostedCommandState(): void {
|
|
329
|
+
lastListedIds = [];
|
|
330
|
+
}
|
|
@@ -4,7 +4,7 @@ import type { CommandContext } from '../command-registry.ts';
|
|
|
4
4
|
import { discoverSkills } from '../../panels/skills-panel.ts';
|
|
5
5
|
import { buildSandboxReview, isRunningInWsl } from '@/runtime/index.ts';
|
|
6
6
|
import { renderQemuWrapperTemplate } from '@/runtime/index.ts';
|
|
7
|
-
import { getPluginDirectories } from '
|
|
7
|
+
import { getPluginDirectories } from '@pellux/goodvibes-sdk/platform/plugins';
|
|
8
8
|
import { listBuiltinSubscriptionProviders } from '@pellux/goodvibes-sdk/platform/config';
|
|
9
9
|
import type { SetupReviewSnapshot } from './local-setup-transfer.ts';
|
|
10
10
|
import { requireProviderApi, requireReadModels, requireServiceRegistry, requireShellPaths, requireSubscriptionManager } from './runtime-services.ts';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { dirname, join, resolve } from 'node:path';
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import type { CommandContext } from '../command-registry.ts';
|
|
4
|
-
import type { ConfigKey } from '
|
|
5
|
-
import { CONFIG_SCHEMA } from '
|
|
4
|
+
import type { ConfigKey } from '@pellux/goodvibes-sdk/platform/config';
|
|
5
|
+
import { CONFIG_SCHEMA } from '@pellux/goodvibes-sdk/platform/config';
|
|
6
6
|
import { requireShellPaths } from './runtime-services.ts';
|
|
7
7
|
|
|
8
8
|
export interface SetupReviewSnapshot {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { dirname, join } from 'path';
|
|
2
2
|
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
-
import { atomicWriteFileSync } from '
|
|
3
|
+
import { atomicWriteFileSync } from '@pellux/goodvibes-sdk/platform/config';
|
|
4
4
|
import type { CommandRegistry } from '../command-registry.ts';
|
|
5
|
-
import type { ConfigKey } from '
|
|
6
|
-
import { CONFIG_SCHEMA } from '
|
|
5
|
+
import type { ConfigKey } from '@pellux/goodvibes-sdk/platform/config';
|
|
6
|
+
import { CONFIG_SCHEMA } from '@pellux/goodvibes-sdk/platform/config';
|
|
7
7
|
import { listHookPointContracts } from '@pellux/goodvibes-sdk/platform/hooks';
|
|
8
8
|
import { renderQemuWrapperTemplate } from '@/runtime/index.ts';
|
|
9
9
|
import type { SetupTransferBundle } from './local-setup-transfer.ts';
|
|
@@ -2,8 +2,8 @@ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
|
2
2
|
import { dirname, resolve } from 'node:path';
|
|
3
3
|
import type { CommandRegistry } from '../command-registry.ts';
|
|
4
4
|
import { profileDataToConfigSnapshot } from '@pellux/goodvibes-sdk/platform/profiles';
|
|
5
|
-
import { CONFIG_SCHEMA, type ConfigKey } from '
|
|
6
|
-
import { getProviderIdFromModel } from '
|
|
5
|
+
import { CONFIG_SCHEMA, type ConfigKey } from '@pellux/goodvibes-sdk/platform/config';
|
|
6
|
+
import { getProviderIdFromModel } from '@pellux/goodvibes-sdk/platform/providers';
|
|
7
7
|
import { CONFIG_KEYS } from '@pellux/goodvibes-sdk/platform/config';
|
|
8
8
|
import type { ManagedSettingsBundle } from '@/runtime/index.ts';
|
|
9
9
|
import {
|
|
@@ -1,103 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* operator-rpc.ts
|
|
2
|
+
* operator-rpc.ts — the command layer's handle on the daemon.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* configured control-plane daemon, using `createGoodVibesSdk`'s generic
|
|
12
|
-
* `sdk.operator.invoke(methodId, input)` (typed against the installed
|
|
13
|
-
* contract — see `@pellux/goodvibes-contracts`).
|
|
4
|
+
* The resolution itself (is the daemon enabled, what base URL, what bearer)
|
|
5
|
+
* moved to runtime/client/operator-endpoint.ts when the terminal app became a
|
|
6
|
+
* pure client: the runtime seams — approvals, config, credentials, sessions,
|
|
7
|
+
* fleet, tasks, devices, checkpoints — all need the same answer, and two copies
|
|
8
|
+
* of it is how one call ended up on a DirectTransport while another made a real
|
|
9
|
+
* request. This module keeps the command-context-shaped entry point and
|
|
10
|
+
* re-exports the rest so no command import site had to change.
|
|
14
11
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* calls, not hot-path session traffic. `getOperatorRpc` never throws: it
|
|
18
|
-
* returns `{ available: false, reason }` up front for every case the caller
|
|
19
|
-
* can decide about statically (daemon disabled, no reachable base URL), so
|
|
20
|
-
* commands can print an honest "not available" line instead of guessing.
|
|
21
|
-
* Once a call is made, a non-2xx response (including the 404 a not-yet-wired
|
|
22
|
-
* verb returns) surfaces to the caller as a thrown `GoodVibesSdkError` —
|
|
23
|
-
* callers should catch and render `describeOperatorRpcError` rather than
|
|
24
|
-
* pretend the request succeeded.
|
|
12
|
+
* Every command verb is now a real HTTP round-trip to the configured
|
|
13
|
+
* control-plane daemon; there is no in-process catalog left to answer one.
|
|
25
14
|
*/
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
15
|
+
export {
|
|
16
|
+
describeOperatorRpcError,
|
|
17
|
+
resolveOperatorRpc,
|
|
18
|
+
type OperatorRpc,
|
|
19
|
+
type OperatorRpcAvailable,
|
|
20
|
+
type OperatorRpcUnavailable,
|
|
21
|
+
} from '../../runtime/client/operator-endpoint.ts';
|
|
22
|
+
|
|
23
|
+
import { resolveOperatorRpc } from '../../runtime/client/operator-endpoint.ts';
|
|
24
|
+
import type { OperatorRpc } from '../../runtime/client/operator-endpoint.ts';
|
|
33
25
|
import type { CommandContext } from '../command-registry.ts';
|
|
34
26
|
import { requireShellPaths } from './runtime-services.ts';
|
|
35
27
|
|
|
36
|
-
export interface OperatorRpcUnavailable {
|
|
37
|
-
readonly available: false;
|
|
38
|
-
readonly reason: string;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export interface OperatorRpcAvailable {
|
|
42
|
-
readonly available: true;
|
|
43
|
-
readonly sdk: GoodVibesSdk;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export type OperatorRpc = OperatorRpcUnavailable | OperatorRpcAvailable;
|
|
47
|
-
|
|
48
|
-
function resolveControlPlaneBaseUrl(configManager: ConfigManager): string | null {
|
|
49
|
-
// An explicitly declared external address wins; otherwise the URL is DERIVED
|
|
50
|
-
// from hostMode/host/port/tls.mode. This used to prefer a stored
|
|
51
|
-
// `controlPlane.baseUrl`, which had no writers and so drifted from the bind
|
|
52
|
-
// on port, scheme, and host at once.
|
|
53
|
-
// The cast drops once a published SDK carries this key in ConfigKey; the
|
|
54
|
-
// installed 1.14.0 predates it (same idiom cloudflare-control-plane.ts uses).
|
|
55
|
-
const explicit = String(configManager.get('controlPlane.publicBaseUrl' as never) ?? '').trim();
|
|
56
|
-
if (explicit) return explicit.replace(/\/+$/, '');
|
|
57
|
-
const host = String(configManager.get('controlPlane.host') ?? '').trim();
|
|
58
|
-
const port = configManager.get('controlPlane.port');
|
|
59
|
-
if (host && typeof port === 'number' && port > 0) {
|
|
60
|
-
const scheme = String(configManager.get('controlPlane.tls.mode') ?? 'off') !== 'off' ? 'https' : 'http';
|
|
61
|
-
// A wildcard bind is not a dial target; loopback is the interface it answers on.
|
|
62
|
-
const dialHost = host === '0.0.0.0' || host === '::' ? '127.0.0.1' : host;
|
|
63
|
-
return `${scheme}://${dialHost.includes(':') && !dialHost.startsWith('[') ? `[${dialHost}]` : dialHost}:${port}`;
|
|
64
|
-
}
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
28
|
/**
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* getOperatorRpc) and the Fleet panel's gateway (fleet-gateway.ts) so both
|
|
74
|
-
* reach the same daemon over the same generic invoke path, never two divergent
|
|
75
|
-
* clients. Refusal reasons are surfaced verbatim so callers print them directly.
|
|
76
|
-
*/
|
|
77
|
-
export function resolveOperatorRpc(deps: { readonly configManager: ConfigManager; readonly homeDirectory: string | (() => string) }): OperatorRpc {
|
|
78
|
-
const { configManager } = deps;
|
|
79
|
-
if (!resolveDaemonEnabled(configManager)) {
|
|
80
|
-
return { available: false, reason: 'the daemon is disabled (daemon.enabled=false) — no operator surface to reach. Enable it in /settings, then retry.' };
|
|
81
|
-
}
|
|
82
|
-
const baseUrl = resolveControlPlaneBaseUrl(configManager);
|
|
83
|
-
if (!baseUrl) {
|
|
84
|
-
return { available: false, reason: 'no control-plane base URL is configured (controlPlane.publicBaseUrl / controlPlane.host+port) — cannot reach the operator surface.' };
|
|
85
|
-
}
|
|
86
|
-
// Resolve the home directory only AFTER the static refusals — a caller whose
|
|
87
|
-
// shell paths are not wired (a disabled-daemon path) must still get the honest
|
|
88
|
-
// unavailable reason above, never a "shell paths not wired" throw.
|
|
89
|
-
const homeDirectory = typeof deps.homeDirectory === 'function' ? deps.homeDirectory() : deps.homeDirectory;
|
|
90
|
-
const daemonHomeDir = join(homeDirectory, '.goodvibes', 'daemon');
|
|
91
|
-
const token = getOrCreateCompanionToken('tui', { daemonHomeDir }).token;
|
|
92
|
-
const sdk = createGoodVibesSdk({ baseUrl, authToken: token });
|
|
93
|
-
return { available: true, sdk };
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Resolve (or honestly refuse to resolve) an operator SDK client wired to
|
|
98
|
-
* this workspace's control-plane daemon. Reasons for refusal are surfaced
|
|
99
|
-
* verbatim to the caller so `/ci`, `/checkin`, and `/principals` can print
|
|
100
|
-
* them directly rather than inventing their own wording.
|
|
29
|
+
* Resolve (or honestly refuse to resolve) an operator SDK client wired to this
|
|
30
|
+
* workspace's control-plane daemon. Reasons for refusal are surfaced verbatim
|
|
31
|
+
* to the caller so commands print them directly rather than inventing their own
|
|
32
|
+
* wording.
|
|
101
33
|
*/
|
|
102
34
|
export function getOperatorRpc(context: CommandContext): OperatorRpc {
|
|
103
35
|
return resolveOperatorRpc({
|
|
@@ -107,21 +39,3 @@ export function getOperatorRpc(context: CommandContext): OperatorRpc {
|
|
|
107
39
|
homeDirectory: () => requireShellPaths(context).homeDirectory,
|
|
108
40
|
});
|
|
109
41
|
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Render a thrown operator-rpc error honestly: a 404 means this daemon has
|
|
113
|
-
* not wired a handler for the verb (a live local runtime that simply doesn't
|
|
114
|
-
* implement it yet), never "no result" or a fabricated empty state.
|
|
115
|
-
*/
|
|
116
|
-
export function describeOperatorRpcError(error: unknown): string {
|
|
117
|
-
if (error instanceof GoodVibesSdkError) {
|
|
118
|
-
if (error.status === 404) {
|
|
119
|
-
return 'the connected daemon returned 404 — this operator verb is not wired up on that daemon yet.';
|
|
120
|
-
}
|
|
121
|
-
if (error.status === 401 || error.status === 403) {
|
|
122
|
-
return `the connected daemon rejected the request (${error.status}): ${error.message}`;
|
|
123
|
-
}
|
|
124
|
-
return `operator request failed${error.status ? ` (${error.status})` : ''}: ${error.message}`;
|
|
125
|
-
}
|
|
126
|
-
return error instanceof Error ? error.message : String(error);
|
|
127
|
-
}
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
|
|
60
60
|
import type { CommandRegistry, CommandContext } from '../command-registry.ts';
|
|
61
61
|
import { persistSecretBackedConfigValue } from '../../config/secret-config.ts';
|
|
62
|
-
import type { ConfigKey } from '
|
|
62
|
+
import type { ConfigKey } from '@pellux/goodvibes-sdk/platform/config';
|
|
63
63
|
import { describeCardEntryRefusal, mayOfferCardEntryFlow } from '@pellux/goodvibes-sdk/platform/payments';
|
|
64
64
|
import {
|
|
65
65
|
PAYMENTS_CARD_NUMBER_CONFIG_KEY,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
|
-
import type { ConfigManager, ConfigKey } from '
|
|
3
|
-
import { CONFIG_SCHEMA } from '
|
|
2
|
+
import type { ConfigManager, ConfigKey } from '@pellux/goodvibes-sdk/platform/config';
|
|
3
|
+
import { CONFIG_SCHEMA } from '@pellux/goodvibes-sdk/platform/config';
|
|
4
4
|
import { SHIPPED_CREDENTIAL_READ_RULES } from '@pellux/goodvibes-sdk/platform/permissions';
|
|
5
5
|
|
|
6
6
|
// ---------------------------------------------------------------------------
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { resolve } from 'path';
|
|
2
2
|
import type { PluginStatus } from '@pellux/goodvibes-sdk/platform/plugins';
|
|
3
|
-
import { getPluginDirectories, getUserPluginDirectory } from '
|
|
3
|
+
import { getPluginDirectories, getUserPluginDirectory } from '@pellux/goodvibes-sdk/platform/plugins';
|
|
4
4
|
import type { CommandRegistry } from '../command-registry.ts';
|
|
5
5
|
import {
|
|
6
6
|
installEcosystemCatalogEntry,
|
|
@@ -19,7 +19,7 @@ import type { RouteExplanation } from '@pellux/goodvibes-sdk/platform/providers'
|
|
|
19
19
|
import type { FallbackTestResult, FallbackTransition } from '@pellux/goodvibes-sdk/platform/providers';
|
|
20
20
|
import type { ProviderApiModelRecord } from '@pellux/goodvibes-sdk/platform/providers';
|
|
21
21
|
import { requireProviderApi } from './runtime-services.ts';
|
|
22
|
-
import { featureEnablementWrite } from '
|
|
22
|
+
import { featureEnablementWrite } from '@pellux/goodvibes-terminal-shell';
|
|
23
23
|
|
|
24
24
|
const PROVIDER_OPTIMIZER_FLAG = 'provider-optimizer';
|
|
25
25
|
|
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import type { CommandContext } from '../command-registry.ts';
|
|
2
2
|
import type { CommandRegistry } from '../command-registry.ts';
|
|
3
3
|
import { generateQrMatrix, renderQrToString } from '@pellux/goodvibes-sdk/platform/pairing';
|
|
4
|
+
import { RELAY_STATE_NOT_READABLE_HERE } from '../../runtime/relay-reachability-bridge.ts';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
|
-
* Register the /relay command —
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* Register the /relay command — the relay's configuration, its reachability
|
|
8
|
+
* state, and a QR-encodable pairing payload for the SDK's outbound
|
|
9
|
+
* zero-knowledge relay (relay.* settings, gated by the `relay-connect` feature
|
|
10
|
+
* flag).
|
|
11
|
+
*
|
|
12
|
+
* The CONFIGURATION half is read here, from this terminal's own config. The
|
|
13
|
+
* LIVE half is not: registration state and pairing live in the running daemon's
|
|
14
|
+
* memory (`DaemonServer.getRelayReachability()`), and no verb exposes either to
|
|
15
|
+
* a client — so both read 'unavailable' with the reason said out loud rather
|
|
16
|
+
* than a state this terminal is in no position to know. See
|
|
17
|
+
* relay-reachability-bridge.ts.
|
|
11
18
|
*/
|
|
12
19
|
|
|
13
20
|
function relayConfigured(ctx: CommandContext): { enabled: boolean; url: string; rendezvousId: string; label: string; flagOn: boolean } {
|
|
@@ -32,14 +39,16 @@ async function renderStatus(ctx: CommandContext): Promise<void> {
|
|
|
32
39
|
].join('\n'));
|
|
33
40
|
return;
|
|
34
41
|
}
|
|
35
|
-
const live = ctx.platform.externalServices?.relayStatus() ?? '
|
|
36
|
-
const state = live === 'registered'
|
|
42
|
+
const live = ctx.platform.externalServices?.relayStatus() ?? 'unavailable';
|
|
43
|
+
const state = live === 'registered' || live === 'disabled' || live === 'unavailable' ? live : 'offline';
|
|
37
44
|
ctx.print([
|
|
38
45
|
`Relay: ${state}`,
|
|
39
46
|
` rendezvous id: ${cfg.rendezvousId || '(not yet minted — start the daemon to generate one)'}`,
|
|
40
47
|
` url: ${cfg.url || '(not set)'}`,
|
|
41
48
|
` label: ${cfg.label || '(not set)'}`,
|
|
42
|
-
|
|
49
|
+
state === 'unavailable'
|
|
50
|
+
? ` ${RELAY_STATE_NOT_READABLE_HERE}`
|
|
51
|
+
: ` live connection state: ${live}`,
|
|
43
52
|
' The relay operator sees only ciphertext and connection metadata — self-host your own relay for full control.',
|
|
44
53
|
].join('\n'));
|
|
45
54
|
}
|
|
@@ -57,7 +66,7 @@ async function renderPair(ctx: CommandContext): Promise<void> {
|
|
|
57
66
|
}
|
|
58
67
|
const minted = await externalServices.mintRelayPairing();
|
|
59
68
|
if (!minted) {
|
|
60
|
-
ctx.print(
|
|
69
|
+
ctx.print(`Relay pairing cannot be minted from here. ${RELAY_STATE_NOT_READABLE_HERE}`);
|
|
61
70
|
return;
|
|
62
71
|
}
|
|
63
72
|
const qrMatrix = generateQrMatrix(minted.encoded);
|
|
@@ -42,7 +42,7 @@ import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
|
42
42
|
import type { WorkspaceCheckpoint, WorkspaceCheckpointManager } from '@pellux/goodvibes-sdk/platform/workspace';
|
|
43
43
|
import type { CommandContext, CommandRegistry } from '../command-registry.ts';
|
|
44
44
|
import { buildRewindReceiptBlock } from '../../core/rewind-receipt.ts';
|
|
45
|
-
import { getTurnAnchors, type TurnAnchor } from '
|
|
45
|
+
import { getTurnAnchors, type TurnAnchor } from '@pellux/goodvibes-sdk/platform/rewind';
|
|
46
46
|
import { createConversationRewindPort, type ConversationRewindPort } from '../../runtime/conversation-rewind-port.ts';
|
|
47
47
|
import { requirePanelManager } from './runtime-services.ts';
|
|
48
48
|
import { shortId } from './checkpoint-runtime.ts';
|
|
@@ -14,7 +14,7 @@ import type { CompactionQualityScore } from '../../renderer/compaction-quality.t
|
|
|
14
14
|
import type { UiReadModels } from '../../runtime/ui-read-models.ts';
|
|
15
15
|
import type { ShellPathService, SessionSurface } from '@/runtime/index.ts';
|
|
16
16
|
import type { EcosystemCatalogPathOptions } from '@/runtime/index.ts';
|
|
17
|
-
import type { PluginPathOptions } from '
|
|
17
|
+
import type { PluginPathOptions } from '@pellux/goodvibes-sdk/platform/plugins';
|
|
18
18
|
import type { DirectTransport } from '@/runtime/index.ts';
|
|
19
19
|
import type { KnowledgeApi } from '@pellux/goodvibes-sdk/platform/knowledge';
|
|
20
20
|
import type { HookApi } from '@pellux/goodvibes-sdk/platform/hooks';
|