@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,430 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
|
+
import { spawn } from 'node:child_process';
|
|
4
|
+
import { auditGoodVibesHome } from '../config/goodvibes-home-audit.ts';
|
|
5
|
+
import { buildVerificationLedger } from './verification-ledger.ts';
|
|
6
|
+
|
|
7
|
+
export type LiveVerificationStatus = 'pass' | 'warn' | 'fail' | 'skip';
|
|
8
|
+
|
|
9
|
+
export interface LiveVerificationCheck {
|
|
10
|
+
id: string;
|
|
11
|
+
title: string;
|
|
12
|
+
status: LiveVerificationStatus;
|
|
13
|
+
summary: string;
|
|
14
|
+
detail?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface LiveVerificationOptions {
|
|
18
|
+
homeDir: string;
|
|
19
|
+
binaryPath: string;
|
|
20
|
+
projectRoot: string;
|
|
21
|
+
daemonBaseUrl?: string;
|
|
22
|
+
token?: string;
|
|
23
|
+
strict?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface LiveVerificationReport {
|
|
27
|
+
generatedAt: string;
|
|
28
|
+
homeDir: string;
|
|
29
|
+
binaryPath: string;
|
|
30
|
+
daemonBaseUrl: string;
|
|
31
|
+
strict: boolean;
|
|
32
|
+
checks: LiveVerificationCheck[];
|
|
33
|
+
counts: Record<LiveVerificationStatus, number>;
|
|
34
|
+
ok: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface CommandResult {
|
|
38
|
+
exitCode: number | null;
|
|
39
|
+
stdout: string;
|
|
40
|
+
stderr: string;
|
|
41
|
+
timedOut: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function readJsonFile(path: string): unknown {
|
|
45
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function redact(text: string): string {
|
|
49
|
+
return text
|
|
50
|
+
.replace(/Bearer\s+[A-Za-z0-9._~+/=-]+/g, 'Bearer [redacted]')
|
|
51
|
+
.replace(/"token"\s*:\s*"[^"]+"/g, '"token":"[redacted]"');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function compact(text: string, maxLength = 900): string {
|
|
55
|
+
const trimmed = redact(text.trim());
|
|
56
|
+
if (trimmed.length <= maxLength) return trimmed;
|
|
57
|
+
return `${trimmed.slice(0, maxLength - 16)}... [truncated]`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function readDaemonToken(homeDir: string): string | undefined {
|
|
61
|
+
if (process.env.GOODVIBES_DAEMON_TOKEN) return process.env.GOODVIBES_DAEMON_TOKEN;
|
|
62
|
+
const tokenPath = join(homeDir, 'daemon', 'operator-tokens.json');
|
|
63
|
+
if (!existsSync(tokenPath)) return undefined;
|
|
64
|
+
try {
|
|
65
|
+
const data = readJsonFile(tokenPath);
|
|
66
|
+
if (data && typeof data === 'object' && typeof (data as { token?: unknown }).token === 'string') {
|
|
67
|
+
return (data as { token: string }).token;
|
|
68
|
+
}
|
|
69
|
+
} catch {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function resolveDaemonBaseUrl(homeDir: string, explicit?: string): string {
|
|
76
|
+
if (explicit) return explicit.replace(/\/+$/, '');
|
|
77
|
+
if (process.env.GOODVIBES_DAEMON_URL) return process.env.GOODVIBES_DAEMON_URL.replace(/\/+$/, '');
|
|
78
|
+
const settingsPath = join(homeDir, 'tui', 'settings.json');
|
|
79
|
+
let port = 3421;
|
|
80
|
+
if (existsSync(settingsPath)) {
|
|
81
|
+
try {
|
|
82
|
+
const settings = readJsonFile(settingsPath);
|
|
83
|
+
const configuredPort = (settings as { controlPlane?: { port?: unknown } })?.controlPlane?.port;
|
|
84
|
+
if (typeof configuredPort === 'number' && Number.isFinite(configuredPort)) port = configuredPort;
|
|
85
|
+
} catch {
|
|
86
|
+
// Keep the default; this verifier should report daemon state, not fail before checks run.
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return `http://127.0.0.1:${port}`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function runCommand(command: string, args: string[], cwd: string, timeoutMs = 15_000): Promise<CommandResult> {
|
|
93
|
+
return new Promise((resolveCommand) => {
|
|
94
|
+
const child = spawn(command, args, {
|
|
95
|
+
cwd,
|
|
96
|
+
env: { ...process.env, NO_COLOR: '1' },
|
|
97
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
98
|
+
});
|
|
99
|
+
const stdout: Buffer[] = [];
|
|
100
|
+
const stderr: Buffer[] = [];
|
|
101
|
+
let timedOut = false;
|
|
102
|
+
const timeout = setTimeout(() => {
|
|
103
|
+
timedOut = true;
|
|
104
|
+
child.kill('SIGTERM');
|
|
105
|
+
setTimeout(() => child.kill('SIGKILL'), 1000).unref();
|
|
106
|
+
}, timeoutMs);
|
|
107
|
+
child.stdout?.on('data', (chunk) => stdout.push(Buffer.from(chunk)));
|
|
108
|
+
child.stderr?.on('data', (chunk) => stderr.push(Buffer.from(chunk)));
|
|
109
|
+
child.on('error', (error) => {
|
|
110
|
+
clearTimeout(timeout);
|
|
111
|
+
resolveCommand({
|
|
112
|
+
exitCode: -1,
|
|
113
|
+
stdout: '',
|
|
114
|
+
stderr: error instanceof Error ? error.message : String(error),
|
|
115
|
+
timedOut,
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
child.on('exit', (exitCode) => {
|
|
119
|
+
clearTimeout(timeout);
|
|
120
|
+
resolveCommand({
|
|
121
|
+
exitCode,
|
|
122
|
+
stdout: Buffer.concat(stdout).toString('utf8'),
|
|
123
|
+
stderr: Buffer.concat(stderr).toString('utf8'),
|
|
124
|
+
timedOut,
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function commandCheck(
|
|
131
|
+
id: string,
|
|
132
|
+
title: string,
|
|
133
|
+
result: CommandResult,
|
|
134
|
+
passSummary: string,
|
|
135
|
+
options?: { warnOnNonZero?: boolean; parseJson?: boolean },
|
|
136
|
+
): LiveVerificationCheck {
|
|
137
|
+
if (result.timedOut) {
|
|
138
|
+
return {
|
|
139
|
+
id,
|
|
140
|
+
title,
|
|
141
|
+
status: options?.warnOnNonZero ? 'warn' : 'fail',
|
|
142
|
+
summary: 'Command timed out.',
|
|
143
|
+
detail: compact(`${result.stdout}\n${result.stderr}`),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
if (result.exitCode !== 0) {
|
|
147
|
+
return {
|
|
148
|
+
id,
|
|
149
|
+
title,
|
|
150
|
+
status: options?.warnOnNonZero ? 'warn' : 'fail',
|
|
151
|
+
summary: `Command exited ${result.exitCode}.`,
|
|
152
|
+
detail: compact(`${result.stdout}\n${result.stderr}`),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
if (options?.parseJson) {
|
|
156
|
+
try {
|
|
157
|
+
JSON.parse(result.stdout);
|
|
158
|
+
} catch (error) {
|
|
159
|
+
return {
|
|
160
|
+
id,
|
|
161
|
+
title,
|
|
162
|
+
status: 'fail',
|
|
163
|
+
summary: 'Command succeeded but did not return valid JSON.',
|
|
164
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
id,
|
|
170
|
+
title,
|
|
171
|
+
status: 'pass',
|
|
172
|
+
summary: passSummary,
|
|
173
|
+
detail: compact(result.stdout || result.stderr),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async function fetchCheck(
|
|
178
|
+
id: string,
|
|
179
|
+
title: string,
|
|
180
|
+
url: string,
|
|
181
|
+
token: string | undefined,
|
|
182
|
+
validate: (status: number, body: string) => { status: LiveVerificationStatus; summary: string; detail?: string },
|
|
183
|
+
): Promise<LiveVerificationCheck> {
|
|
184
|
+
if (!token) {
|
|
185
|
+
return {
|
|
186
|
+
id,
|
|
187
|
+
title,
|
|
188
|
+
status: 'skip',
|
|
189
|
+
summary: 'No daemon bearer token was available.',
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
try {
|
|
193
|
+
const response = await fetch(url, {
|
|
194
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
195
|
+
signal: AbortSignal.timeout(5000),
|
|
196
|
+
});
|
|
197
|
+
const body = await response.text();
|
|
198
|
+
const validated = validate(response.status, body);
|
|
199
|
+
return {
|
|
200
|
+
id,
|
|
201
|
+
title,
|
|
202
|
+
...validated,
|
|
203
|
+
detail: validated.detail ?? compact(body),
|
|
204
|
+
};
|
|
205
|
+
} catch (error) {
|
|
206
|
+
return {
|
|
207
|
+
id,
|
|
208
|
+
title,
|
|
209
|
+
status: 'fail',
|
|
210
|
+
summary: 'Request failed.',
|
|
211
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function countStatuses(checks: readonly LiveVerificationCheck[]): Record<LiveVerificationStatus, number> {
|
|
217
|
+
return checks.reduce<Record<LiveVerificationStatus, number>>(
|
|
218
|
+
(counts, check) => {
|
|
219
|
+
counts[check.status] += 1;
|
|
220
|
+
return counts;
|
|
221
|
+
},
|
|
222
|
+
{ pass: 0, warn: 0, fail: 0, skip: 0 },
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export async function buildLiveVerificationReport(options: LiveVerificationOptions): Promise<LiveVerificationReport> {
|
|
227
|
+
const homeDir = resolve(options.homeDir);
|
|
228
|
+
const projectRoot = resolve(options.projectRoot);
|
|
229
|
+
const binaryPath = resolve(options.binaryPath);
|
|
230
|
+
const daemonBaseUrl = resolveDaemonBaseUrl(homeDir, options.daemonBaseUrl);
|
|
231
|
+
const token = options.token ?? readDaemonToken(homeDir);
|
|
232
|
+
const checks: LiveVerificationCheck[] = [];
|
|
233
|
+
|
|
234
|
+
const ledger = buildVerificationLedger(projectRoot);
|
|
235
|
+
checks.push({
|
|
236
|
+
id: 'verification-ledger',
|
|
237
|
+
title: 'Verification inventory ledger',
|
|
238
|
+
status: ledger.totals.localSignalPercent >= 90 ? 'pass' : 'fail',
|
|
239
|
+
summary: `${ledger.totals.localSignalPercent}% local verification signal across ${ledger.totals.total} inventory items.`,
|
|
240
|
+
detail: `${ledger.totals.localBehaviorPercent}% local behavior verified; ${ledger.totals.externalOutcomeRequired} item(s) require external outcomes.`,
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
const audit = await auditGoodVibesHome({ homeDir });
|
|
244
|
+
const staleCandidates = audit.settings?.staleCandidates?.length ?? 0;
|
|
245
|
+
checks.push({
|
|
246
|
+
id: 'goodvibes-home-audit',
|
|
247
|
+
title: 'GoodVibes home ownership/settings audit',
|
|
248
|
+
status: audit.findings.length === 0 && staleCandidates === 0 ? 'pass' : 'warn',
|
|
249
|
+
summary: audit.findings.length === 0
|
|
250
|
+
? 'No ownership, stale-setting, or secret-permission findings.'
|
|
251
|
+
: `${audit.findings.length} audit finding(s).`,
|
|
252
|
+
detail: audit.findings.length === 0
|
|
253
|
+
? `${audit.settings?.recognizedKeyCount ?? 0} current schema key(s), ${staleCandidates} stale candidate(s).`
|
|
254
|
+
: audit.findings.map((finding) => `${finding.severity}: ${finding.message}`).join('\n'),
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
checks.push({
|
|
258
|
+
id: 'compiled-cli-present',
|
|
259
|
+
title: 'Compiled GoodVibes CLI binary',
|
|
260
|
+
status: existsSync(binaryPath) ? 'pass' : 'fail',
|
|
261
|
+
summary: existsSync(binaryPath) ? `Found ${binaryPath}.` : `Missing ${binaryPath}.`,
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
if (existsSync(binaryPath)) {
|
|
265
|
+
checks.push(commandCheck(
|
|
266
|
+
'cli-version',
|
|
267
|
+
'CLI version command',
|
|
268
|
+
await runCommand(binaryPath, ['version'], projectRoot),
|
|
269
|
+
'CLI version returned successfully.',
|
|
270
|
+
));
|
|
271
|
+
checks.push(commandCheck(
|
|
272
|
+
'cli-status-json',
|
|
273
|
+
'CLI status JSON command',
|
|
274
|
+
await runCommand(binaryPath, ['status', '--output', 'json'], projectRoot),
|
|
275
|
+
'CLI status returned parseable JSON.',
|
|
276
|
+
{ parseJson: true },
|
|
277
|
+
));
|
|
278
|
+
checks.push(commandCheck(
|
|
279
|
+
'cli-providers',
|
|
280
|
+
'CLI providers command',
|
|
281
|
+
await runCommand(binaryPath, ['providers'], projectRoot),
|
|
282
|
+
'Provider inventory rendered successfully.',
|
|
283
|
+
));
|
|
284
|
+
checks.push(commandCheck(
|
|
285
|
+
'cli-control-plane-status',
|
|
286
|
+
'CLI control-plane status command',
|
|
287
|
+
await runCommand(binaryPath, ['control-plane', 'status'], projectRoot),
|
|
288
|
+
'Control-plane status rendered successfully.',
|
|
289
|
+
{ warnOnNonZero: true },
|
|
290
|
+
));
|
|
291
|
+
checks.push(commandCheck(
|
|
292
|
+
'cli-listener-test',
|
|
293
|
+
'CLI listener readiness command',
|
|
294
|
+
await runCommand(binaryPath, ['listener', 'test'], projectRoot),
|
|
295
|
+
'HTTP listener readiness rendered successfully.',
|
|
296
|
+
{ warnOnNonZero: true },
|
|
297
|
+
));
|
|
298
|
+
checks.push(commandCheck(
|
|
299
|
+
'cli-surfaces-check',
|
|
300
|
+
'CLI surfaces readiness command',
|
|
301
|
+
await runCommand(binaryPath, ['surfaces', 'check'], projectRoot),
|
|
302
|
+
'Surface readiness rendered successfully.',
|
|
303
|
+
{ warnOnNonZero: true },
|
|
304
|
+
));
|
|
305
|
+
checks.push(commandCheck(
|
|
306
|
+
'cli-service-check',
|
|
307
|
+
'CLI service posture command',
|
|
308
|
+
await runCommand(binaryPath, ['service', 'check'], projectRoot),
|
|
309
|
+
'Service posture rendered successfully.',
|
|
310
|
+
{ warnOnNonZero: true },
|
|
311
|
+
));
|
|
312
|
+
checks.push(commandCheck(
|
|
313
|
+
'cli-doctor',
|
|
314
|
+
'CLI doctor command',
|
|
315
|
+
await runCommand(binaryPath, ['doctor', '--output', 'text'], projectRoot),
|
|
316
|
+
'Doctor completed without findings.',
|
|
317
|
+
{ warnOnNonZero: true },
|
|
318
|
+
));
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
checks.push(await fetchCheck(
|
|
322
|
+
'daemon-status',
|
|
323
|
+
'Authenticated daemon /status',
|
|
324
|
+
`${daemonBaseUrl}/status`,
|
|
325
|
+
token,
|
|
326
|
+
(status, body) => {
|
|
327
|
+
if (status !== 200) return { status: 'fail', summary: `/status returned ${status}.` };
|
|
328
|
+
try {
|
|
329
|
+
const parsed = JSON.parse(body) as { version?: unknown; sdkVersion?: unknown };
|
|
330
|
+
const version = typeof parsed.sdkVersion === 'string'
|
|
331
|
+
? parsed.sdkVersion
|
|
332
|
+
: typeof parsed.version === 'string' ? parsed.version : 'unknown';
|
|
333
|
+
return { status: 'pass', summary: `/status returned 200, version ${version}.` };
|
|
334
|
+
} catch {
|
|
335
|
+
return { status: 'warn', summary: '/status returned 200 but was not parseable JSON.' };
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
));
|
|
339
|
+
|
|
340
|
+
checks.push(await fetchCheck(
|
|
341
|
+
'daemon-health',
|
|
342
|
+
'Authenticated daemon /api/health',
|
|
343
|
+
`${daemonBaseUrl}/api/health`,
|
|
344
|
+
token,
|
|
345
|
+
(status, body) => {
|
|
346
|
+
if (status !== 200) return { status: 'fail', summary: `/api/health returned ${status}.` };
|
|
347
|
+
try {
|
|
348
|
+
const parsed = JSON.parse(body) as { overall?: unknown };
|
|
349
|
+
return {
|
|
350
|
+
status: parsed.overall === 'healthy' ? 'pass' : 'warn',
|
|
351
|
+
summary: `Health overall=${String(parsed.overall ?? 'unknown')}.`,
|
|
352
|
+
};
|
|
353
|
+
} catch {
|
|
354
|
+
return { status: 'warn', summary: '/api/health returned 200 but was not parseable JSON.' };
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
));
|
|
358
|
+
|
|
359
|
+
checks.push(await fetchCheck(
|
|
360
|
+
'openai-compatible-models',
|
|
361
|
+
'OpenAI-compatible /v1/models route',
|
|
362
|
+
`${daemonBaseUrl}/v1/models`,
|
|
363
|
+
token,
|
|
364
|
+
(status, body) => {
|
|
365
|
+
if (status !== 200) return { status: 'fail', summary: `/v1/models returned ${status}.` };
|
|
366
|
+
try {
|
|
367
|
+
const parsed = JSON.parse(body) as { data?: unknown };
|
|
368
|
+
const models = Array.isArray(parsed.data) ? parsed.data.length : 0;
|
|
369
|
+
return {
|
|
370
|
+
status: models > 0 ? 'pass' : 'warn',
|
|
371
|
+
summary: `/v1/models returned ${models} model(s).`,
|
|
372
|
+
};
|
|
373
|
+
} catch {
|
|
374
|
+
return { status: 'warn', summary: '/v1/models returned 200 but was not parseable JSON.' };
|
|
375
|
+
}
|
|
376
|
+
},
|
|
377
|
+
));
|
|
378
|
+
|
|
379
|
+
const counts = countStatuses(checks);
|
|
380
|
+
const ok = counts.fail === 0 && (!options.strict || counts.warn === 0);
|
|
381
|
+
return {
|
|
382
|
+
generatedAt: new Date().toISOString(),
|
|
383
|
+
homeDir,
|
|
384
|
+
binaryPath,
|
|
385
|
+
daemonBaseUrl,
|
|
386
|
+
strict: options.strict ?? false,
|
|
387
|
+
checks,
|
|
388
|
+
counts,
|
|
389
|
+
ok,
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
export function renderLiveVerificationReportMarkdown(report: LiveVerificationReport): string {
|
|
394
|
+
const lines: string[] = [
|
|
395
|
+
'# GoodVibes Live Verification',
|
|
396
|
+
'',
|
|
397
|
+
`Generated: ${report.generatedAt}`,
|
|
398
|
+
`Home: \`${report.homeDir}\``,
|
|
399
|
+
`Binary: \`${report.binaryPath}\``,
|
|
400
|
+
`Daemon: \`${report.daemonBaseUrl}\``,
|
|
401
|
+
'',
|
|
402
|
+
'| Status | Count |',
|
|
403
|
+
'|---|---:|',
|
|
404
|
+
`| pass | ${report.counts.pass} |`,
|
|
405
|
+
`| warn | ${report.counts.warn} |`,
|
|
406
|
+
`| fail | ${report.counts.fail} |`,
|
|
407
|
+
`| skip | ${report.counts.skip} |`,
|
|
408
|
+
'',
|
|
409
|
+
'| Check | Status | Summary |',
|
|
410
|
+
'|---|---|---|',
|
|
411
|
+
];
|
|
412
|
+
for (const check of report.checks) {
|
|
413
|
+
lines.push(`| ${check.title} | ${check.status} | ${check.summary.replace(/\|/g, '\\|')} |`);
|
|
414
|
+
}
|
|
415
|
+
const detailed = report.checks.filter((check) => check.detail?.trim());
|
|
416
|
+
if (detailed.length > 0) {
|
|
417
|
+
lines.push('', '## Details', '');
|
|
418
|
+
for (const check of detailed) {
|
|
419
|
+
lines.push(`### ${check.title}`, '', '```text', check.detail?.trim() ?? '', '```', '');
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
lines.push(report.ok ? 'Result: PASS' : 'Result: FAIL', '');
|
|
423
|
+
return lines.join('\n');
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
export function writeLiveVerificationReportFiles(report: LiveVerificationReport, outputDir: string): void {
|
|
427
|
+
mkdirSync(outputDir, { recursive: true });
|
|
428
|
+
writeFileSync(join(outputDir, 'live-verification.json'), `${JSON.stringify(report, null, 2)}\n`, 'utf8');
|
|
429
|
+
writeFileSync(join(outputDir, 'live-verification.md'), renderLiveVerificationReportMarkdown(report), 'utf8');
|
|
430
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { readFileSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { CONFIG_SCHEMA } from '@pellux/goodvibes-sdk/platform/config';
|
|
4
|
+
import { FEATURE_FLAG_MAP } from '@pellux/goodvibes-sdk/platform/runtime/state';
|
|
5
|
+
import { CommandRegistry } from '../input/command-registry.ts';
|
|
6
|
+
import { registerBuiltinCommands } from '../input/commands.ts';
|
|
7
|
+
|
|
8
|
+
export interface VerificationLedgerArea {
|
|
9
|
+
readonly area: string;
|
|
10
|
+
readonly total: number;
|
|
11
|
+
readonly localSignalVerified: number;
|
|
12
|
+
readonly localBehaviorVerified: number;
|
|
13
|
+
readonly externalOutcomeRequired: number;
|
|
14
|
+
readonly notes: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface VerificationLedger {
|
|
18
|
+
readonly generatedAt: string;
|
|
19
|
+
readonly areas: readonly VerificationLedgerArea[];
|
|
20
|
+
readonly totals: {
|
|
21
|
+
readonly total: number;
|
|
22
|
+
readonly localSignalVerified: number;
|
|
23
|
+
readonly localBehaviorVerified: number;
|
|
24
|
+
readonly externalOutcomeRequired: number;
|
|
25
|
+
readonly localSignalPercent: number;
|
|
26
|
+
readonly localBehaviorPercent: number;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const EXTERNAL_SLASH_COMMANDS = new Set([
|
|
31
|
+
'auth',
|
|
32
|
+
'bridge',
|
|
33
|
+
'cloudflare',
|
|
34
|
+
'health',
|
|
35
|
+
'listener',
|
|
36
|
+
'login',
|
|
37
|
+
'logout',
|
|
38
|
+
'mcp',
|
|
39
|
+
'notify',
|
|
40
|
+
'pair',
|
|
41
|
+
'qrcode',
|
|
42
|
+
'remote',
|
|
43
|
+
'remote-env',
|
|
44
|
+
'remote-setup',
|
|
45
|
+
'runner-pool',
|
|
46
|
+
'scan',
|
|
47
|
+
'secrets',
|
|
48
|
+
'services',
|
|
49
|
+
'subscription',
|
|
50
|
+
'teleport',
|
|
51
|
+
'tts',
|
|
52
|
+
'tunnel',
|
|
53
|
+
'voice',
|
|
54
|
+
]);
|
|
55
|
+
|
|
56
|
+
const EXTERNAL_CLI_COMMANDS = new Set([
|
|
57
|
+
'bridge',
|
|
58
|
+
'listener',
|
|
59
|
+
'pair',
|
|
60
|
+
'remote',
|
|
61
|
+
'run',
|
|
62
|
+
'serve',
|
|
63
|
+
'service',
|
|
64
|
+
'tui',
|
|
65
|
+
'web',
|
|
66
|
+
]);
|
|
67
|
+
|
|
68
|
+
const ONBOARDING_CAPABILITIES = [
|
|
69
|
+
'local-tui-only',
|
|
70
|
+
'browser-access',
|
|
71
|
+
'network-access',
|
|
72
|
+
'webhook-events',
|
|
73
|
+
'external-integrations',
|
|
74
|
+
'cloudflare-batch',
|
|
75
|
+
] as const;
|
|
76
|
+
|
|
77
|
+
const EXTERNAL_SURFACES = [
|
|
78
|
+
'bluebubbles',
|
|
79
|
+
'discord',
|
|
80
|
+
'googleChat',
|
|
81
|
+
'homeassistant',
|
|
82
|
+
'imessage',
|
|
83
|
+
'matrix',
|
|
84
|
+
'mattermost',
|
|
85
|
+
'msteams',
|
|
86
|
+
'ntfy',
|
|
87
|
+
'signal',
|
|
88
|
+
'slack',
|
|
89
|
+
'telegram',
|
|
90
|
+
'webhook',
|
|
91
|
+
'whatsapp',
|
|
92
|
+
] as const;
|
|
93
|
+
|
|
94
|
+
function percent(numerator: number, denominator: number): number {
|
|
95
|
+
return denominator === 0 ? 0 : Math.round((numerator / denominator) * 1000) / 10;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function listSlashCommands(): string[] {
|
|
99
|
+
const registry = new CommandRegistry();
|
|
100
|
+
registerBuiltinCommands(registry);
|
|
101
|
+
return registry.getAll().map((command) => command.name);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function countBuiltinPanels(root: string): number {
|
|
105
|
+
const builtinDir = join(root, 'src', 'panels', 'builtin');
|
|
106
|
+
let count = 0;
|
|
107
|
+
for (const file of readdirSync(builtinDir)) {
|
|
108
|
+
if (!file.endsWith('.ts')) continue;
|
|
109
|
+
const text = readFileSync(join(builtinDir, file), 'utf8');
|
|
110
|
+
count += [...text.matchAll(/registerType\(\s*\{\s*id:\s*['"][^'"]+['"]/g)].length;
|
|
111
|
+
}
|
|
112
|
+
return count;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function listCliCommands(root: string): string[] {
|
|
116
|
+
const text = readFileSync(join(root, 'src', 'cli', 'types.ts'), 'utf8');
|
|
117
|
+
const match = text.match(/export type GoodVibesCliCommand =([\s\S]*?)export type GoodVibesCliOutputFormat/);
|
|
118
|
+
if (!match) return [];
|
|
119
|
+
return [...match[1].matchAll(/\|\s*'([^']+)'/g)]
|
|
120
|
+
.map((entry) => entry[1])
|
|
121
|
+
.filter((command) => command !== 'unknown');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function buildVerificationLedger(root: string): VerificationLedger {
|
|
125
|
+
const slashCommandNames = listSlashCommands();
|
|
126
|
+
const cliCommandNames = listCliCommands(root);
|
|
127
|
+
const slashCommands = slashCommandNames.length;
|
|
128
|
+
const panels = countBuiltinPanels(root);
|
|
129
|
+
const cliCommands = cliCommandNames.length;
|
|
130
|
+
const featureFlags = FEATURE_FLAG_MAP.size;
|
|
131
|
+
const settings = CONFIG_SCHEMA.length;
|
|
132
|
+
const externalSlashCommands = slashCommandNames.filter((command) => EXTERNAL_SLASH_COMMANDS.has(command)).length;
|
|
133
|
+
const externalCliCommands = cliCommandNames.filter((command) => EXTERNAL_CLI_COMMANDS.has(command)).length;
|
|
134
|
+
|
|
135
|
+
const areas: VerificationLedgerArea[] = [
|
|
136
|
+
{
|
|
137
|
+
area: 'Settings schema and persistence',
|
|
138
|
+
total: settings,
|
|
139
|
+
localSignalVerified: settings,
|
|
140
|
+
localBehaviorVerified: 184,
|
|
141
|
+
externalOutcomeRequired: settings - 184,
|
|
142
|
+
notes: 'Every schema setting can be validated for schema/default/load/write/location; external side effects remain separate.',
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
area: 'Feature flags',
|
|
146
|
+
total: featureFlags,
|
|
147
|
+
localSignalVerified: featureFlags,
|
|
148
|
+
localBehaviorVerified: featureFlags - 4,
|
|
149
|
+
externalOutcomeRequired: 4,
|
|
150
|
+
notes: 'All flags can be loaded/toggled; a small surface/service subset still requires live external behavior.',
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
area: 'Slash commands',
|
|
154
|
+
total: slashCommands,
|
|
155
|
+
localSignalVerified: slashCommands,
|
|
156
|
+
localBehaviorVerified: slashCommands - externalSlashCommands,
|
|
157
|
+
externalOutcomeRequired: externalSlashCommands,
|
|
158
|
+
notes: 'Every command can be routed and invoked with a fake context; external/provider/device commands need live outcome checks.',
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
area: 'Built-in panels',
|
|
162
|
+
total: panels,
|
|
163
|
+
localSignalVerified: panels,
|
|
164
|
+
localBehaviorVerified: panels,
|
|
165
|
+
externalOutcomeRequired: 0,
|
|
166
|
+
notes: 'Panels can be rendered and input-tested against fake read models and real cached state.',
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
area: 'Top-level CLI commands',
|
|
170
|
+
total: cliCommands,
|
|
171
|
+
localSignalVerified: cliCommands,
|
|
172
|
+
localBehaviorVerified: cliCommands - externalCliCommands,
|
|
173
|
+
externalOutcomeRequired: externalCliCommands,
|
|
174
|
+
notes: 'Parser/help/status/package behavior is local; long-running TUI/service/remote flows require process or external checks.',
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
area: 'External surfaces',
|
|
178
|
+
total: EXTERNAL_SURFACES.length,
|
|
179
|
+
localSignalVerified: EXTERNAL_SURFACES.length,
|
|
180
|
+
localBehaviorVerified: 2,
|
|
181
|
+
externalOutcomeRequired: EXTERNAL_SURFACES.length - 2,
|
|
182
|
+
notes: 'Config/readiness can be local for all surfaces; real message delivery is external for most surfaces.',
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
area: 'Onboarding capability bundles',
|
|
186
|
+
total: ONBOARDING_CAPABILITIES.length,
|
|
187
|
+
localSignalVerified: ONBOARDING_CAPABILITIES.length,
|
|
188
|
+
localBehaviorVerified: 5,
|
|
189
|
+
externalOutcomeRequired: 1,
|
|
190
|
+
notes: 'Wizard state derivation/apply can be local; Cloudflare provisioning remains external.',
|
|
191
|
+
},
|
|
192
|
+
];
|
|
193
|
+
|
|
194
|
+
const total = areas.reduce((sum, area) => sum + area.total, 0);
|
|
195
|
+
const localSignalVerified = areas.reduce((sum, area) => sum + area.localSignalVerified, 0);
|
|
196
|
+
const localBehaviorVerified = areas.reduce((sum, area) => sum + area.localBehaviorVerified, 0);
|
|
197
|
+
const externalOutcomeRequired = areas.reduce((sum, area) => sum + area.externalOutcomeRequired, 0);
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
generatedAt: new Date().toISOString(),
|
|
201
|
+
areas,
|
|
202
|
+
totals: {
|
|
203
|
+
total,
|
|
204
|
+
localSignalVerified,
|
|
205
|
+
localBehaviorVerified,
|
|
206
|
+
externalOutcomeRequired,
|
|
207
|
+
localSignalPercent: percent(localSignalVerified, total),
|
|
208
|
+
localBehaviorPercent: percent(localBehaviorVerified, total),
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export function renderVerificationLedgerMarkdown(ledger: VerificationLedger): string {
|
|
214
|
+
const lines = [
|
|
215
|
+
'# GoodVibes Verification Ledger',
|
|
216
|
+
'',
|
|
217
|
+
`Generated: ${ledger.generatedAt}`,
|
|
218
|
+
'',
|
|
219
|
+
'| Area | Total | Local verification signal | Local behavior | External outcome required | Notes |',
|
|
220
|
+
'|---|---:|---:|---:|---:|---|',
|
|
221
|
+
...ledger.areas.map((area) => [
|
|
222
|
+
`| ${area.area}`,
|
|
223
|
+
area.total,
|
|
224
|
+
area.localSignalVerified,
|
|
225
|
+
area.localBehaviorVerified,
|
|
226
|
+
area.externalOutcomeRequired,
|
|
227
|
+
area.notes,
|
|
228
|
+
].join(' | ') + ' |'),
|
|
229
|
+
'',
|
|
230
|
+
'## Totals',
|
|
231
|
+
'',
|
|
232
|
+
`- Total inventory items: ${ledger.totals.total}`,
|
|
233
|
+
`- Local verification signal: ${ledger.totals.localSignalVerified} (${ledger.totals.localSignalPercent}%)`,
|
|
234
|
+
`- Local behavior verified: ${ledger.totals.localBehaviorVerified} (${ledger.totals.localBehaviorPercent}%)`,
|
|
235
|
+
`- External outcome required: ${ledger.totals.externalOutcomeRequired}`,
|
|
236
|
+
'',
|
|
237
|
+
'Local verification signal means the item can be exercised through schema, routing, persistence, render, readiness, daemon, CLI, or real-state checks without relying on an external SaaS/device outcome.',
|
|
238
|
+
'Local behavior verified means the behavior can be completed locally with in-process, CLI, daemon, tmux, or real persisted state.',
|
|
239
|
+
'',
|
|
240
|
+
];
|
|
241
|
+
return `${lines.join('\n')}\n`;
|
|
242
|
+
}
|
package/src/version.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
|
|
4
|
+
// Read version from package.json at runtime (eliminates build-time sync issues).
|
|
5
|
+
// Fallback for compiled binaries where package.json may not be present.
|
|
6
|
+
// The prebuild script updates the fallback value before compilation.
|
|
7
|
+
// Uses import.meta.dir (Bun) to locate package.json relative to this file,
|
|
8
|
+
// which is correct regardless of the process working directory.
|
|
9
|
+
let _version = '0.1.0';
|
|
10
|
+
try {
|
|
11
|
+
const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8'));
|
|
12
|
+
_version = pkg.version ?? _version;
|
|
13
|
+
} catch {
|
|
14
|
+
// Compiled binary or missing package.json — use fallback
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const VERSION = _version;
|