onadit 0.0.1 → 1.0.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/README.dev.md +124 -0
- package/README.md +194 -2
- package/dist/src/auth/keychain.d.ts +42 -0
- package/dist/src/auth/keychain.d.ts.map +1 -0
- package/dist/src/auth/keychain.js +177 -0
- package/dist/src/auth/keychain.js.map +1 -0
- package/dist/src/auth/middleware.d.ts +21 -0
- package/dist/src/auth/middleware.d.ts.map +1 -0
- package/dist/src/auth/middleware.js +93 -0
- package/dist/src/auth/middleware.js.map +1 -0
- package/dist/src/auth/rate-limit.d.ts +16 -0
- package/dist/src/auth/rate-limit.d.ts.map +1 -0
- package/dist/src/auth/rate-limit.js +38 -0
- package/dist/src/auth/rate-limit.js.map +1 -0
- package/dist/src/auth/rotation.d.ts +67 -0
- package/dist/src/auth/rotation.d.ts.map +1 -0
- package/dist/src/auth/rotation.js +190 -0
- package/dist/src/auth/rotation.js.map +1 -0
- package/dist/src/backfill/project-context-batch-constructor.d.ts +127 -0
- package/dist/src/backfill/project-context-batch-constructor.d.ts.map +1 -0
- package/dist/src/backfill/project-context-batch-constructor.js +210 -0
- package/dist/src/backfill/project-context-batch-constructor.js.map +1 -0
- package/dist/src/capture/assistant-text-lookup.d.ts +31 -0
- package/dist/src/capture/assistant-text-lookup.d.ts.map +1 -0
- package/dist/src/capture/assistant-text-lookup.js +222 -0
- package/dist/src/capture/assistant-text-lookup.js.map +1 -0
- package/dist/src/capture/auto-capture.d.ts +322 -0
- package/dist/src/capture/auto-capture.d.ts.map +1 -0
- package/dist/src/capture/auto-capture.js +1167 -0
- package/dist/src/capture/auto-capture.js.map +1 -0
- package/dist/src/capture/auto-repair.d.ts +125 -0
- package/dist/src/capture/auto-repair.d.ts.map +1 -0
- package/dist/src/capture/auto-repair.js +280 -0
- package/dist/src/capture/auto-repair.js.map +1 -0
- package/dist/src/capture/batch-budget.d.ts +90 -0
- package/dist/src/capture/batch-budget.d.ts.map +1 -0
- package/dist/src/capture/batch-budget.js +148 -0
- package/dist/src/capture/batch-budget.js.map +1 -0
- package/dist/src/capture/claude-cli-triage-provider.d.ts +28 -0
- package/dist/src/capture/claude-cli-triage-provider.d.ts.map +1 -0
- package/dist/src/capture/claude-cli-triage-provider.js +101 -0
- package/dist/src/capture/claude-cli-triage-provider.js.map +1 -0
- package/dist/src/capture/codex-cli-triage-provider.d.ts +17 -0
- package/dist/src/capture/codex-cli-triage-provider.d.ts.map +1 -0
- package/dist/src/capture/codex-cli-triage-provider.js +91 -0
- package/dist/src/capture/codex-cli-triage-provider.js.map +1 -0
- package/dist/src/capture/cursor-capture.d.ts +89 -0
- package/dist/src/capture/cursor-capture.d.ts.map +1 -0
- package/dist/src/capture/cursor-capture.js +121 -0
- package/dist/src/capture/cursor-capture.js.map +1 -0
- package/dist/src/capture/observability.d.ts +131 -0
- package/dist/src/capture/observability.d.ts.map +1 -0
- package/dist/src/capture/observability.js +181 -0
- package/dist/src/capture/observability.js.map +1 -0
- package/dist/src/capture/openai-triage-provider.d.ts +92 -0
- package/dist/src/capture/openai-triage-provider.d.ts.map +1 -0
- package/dist/src/capture/openai-triage-provider.js +267 -0
- package/dist/src/capture/openai-triage-provider.js.map +1 -0
- package/dist/src/capture/recover-quarantine.d.ts +273 -0
- package/dist/src/capture/recover-quarantine.d.ts.map +1 -0
- package/dist/src/capture/recover-quarantine.js +581 -0
- package/dist/src/capture/recover-quarantine.js.map +1 -0
- package/dist/src/capture/review-queue-index.d.ts +51 -0
- package/dist/src/capture/review-queue-index.d.ts.map +1 -0
- package/dist/src/capture/review-queue-index.js +204 -0
- package/dist/src/capture/review-queue-index.js.map +1 -0
- package/dist/src/capture/review-queue.d.ts +47 -0
- package/dist/src/capture/review-queue.d.ts.map +1 -0
- package/dist/src/capture/review-queue.js +116 -0
- package/dist/src/capture/review-queue.js.map +1 -0
- package/dist/src/capture/sources.d.ts +53 -0
- package/dist/src/capture/sources.d.ts.map +1 -0
- package/dist/src/capture/sources.js +55 -0
- package/dist/src/capture/sources.js.map +1 -0
- package/dist/src/capture/stored-statements.d.ts +9 -0
- package/dist/src/capture/stored-statements.d.ts.map +1 -0
- package/dist/src/capture/stored-statements.js +63 -0
- package/dist/src/capture/stored-statements.js.map +1 -0
- package/dist/src/capture/triage-classification.d.ts +92 -0
- package/dist/src/capture/triage-classification.d.ts.map +1 -0
- package/dist/src/capture/triage-classification.js +94 -0
- package/dist/src/capture/triage-classification.js.map +1 -0
- package/dist/src/capture/triage-lane.d.ts +39 -0
- package/dist/src/capture/triage-lane.d.ts.map +1 -0
- package/dist/src/capture/triage-lane.js +217 -0
- package/dist/src/capture/triage-lane.js.map +1 -0
- package/dist/src/capture/triage-provider-factory.d.ts +36 -0
- package/dist/src/capture/triage-provider-factory.d.ts.map +1 -0
- package/dist/src/capture/triage-provider-factory.js +55 -0
- package/dist/src/capture/triage-provider-factory.js.map +1 -0
- package/dist/src/capture/triage-provider.d.ts +82 -0
- package/dist/src/capture/triage-provider.d.ts.map +1 -0
- package/dist/src/capture/triage-provider.js +126 -0
- package/dist/src/capture/triage-provider.js.map +1 -0
- package/dist/src/capture/triage.d.ts +30 -0
- package/dist/src/capture/triage.d.ts.map +1 -0
- package/dist/src/capture/triage.js +50 -0
- package/dist/src/capture/triage.js.map +1 -0
- package/dist/src/capture/user-statements-recheck.d.ts +22 -0
- package/dist/src/capture/user-statements-recheck.d.ts.map +1 -0
- package/dist/src/capture/user-statements-recheck.js +181 -0
- package/dist/src/capture/user-statements-recheck.js.map +1 -0
- package/dist/src/capture/user-statements.d.ts +84 -0
- package/dist/src/capture/user-statements.d.ts.map +1 -0
- package/dist/src/capture/user-statements.js +617 -0
- package/dist/src/capture/user-statements.js.map +1 -0
- package/dist/src/cli/agent-homes.d.ts +15 -0
- package/dist/src/cli/agent-homes.d.ts.map +1 -0
- package/dist/src/cli/agent-homes.js +45 -0
- package/dist/src/cli/agent-homes.js.map +1 -0
- package/dist/src/cli/commands/backfill.d.ts +3 -0
- package/dist/src/cli/commands/backfill.d.ts.map +1 -0
- package/dist/src/cli/commands/backfill.js +1379 -0
- package/dist/src/cli/commands/backfill.js.map +1 -0
- package/dist/src/cli/commands/brief.d.ts +12 -0
- package/dist/src/cli/commands/brief.d.ts.map +1 -0
- package/dist/src/cli/commands/brief.js +131 -0
- package/dist/src/cli/commands/brief.js.map +1 -0
- package/dist/src/cli/commands/bulk-ingest.d.ts +3 -0
- package/dist/src/cli/commands/bulk-ingest.d.ts.map +1 -0
- package/dist/src/cli/commands/bulk-ingest.js +126 -0
- package/dist/src/cli/commands/bulk-ingest.js.map +1 -0
- package/dist/src/cli/commands/capture-recover.d.ts +40 -0
- package/dist/src/cli/commands/capture-recover.d.ts.map +1 -0
- package/dist/src/cli/commands/capture-recover.js +194 -0
- package/dist/src/cli/commands/capture-recover.js.map +1 -0
- package/dist/src/cli/commands/capture.d.ts +12 -0
- package/dist/src/cli/commands/capture.d.ts.map +1 -0
- package/dist/src/cli/commands/capture.js +304 -0
- package/dist/src/cli/commands/capture.js.map +1 -0
- package/dist/src/cli/commands/chunk-backfill.d.ts +13 -0
- package/dist/src/cli/commands/chunk-backfill.d.ts.map +1 -0
- package/dist/src/cli/commands/chunk-backfill.js +157 -0
- package/dist/src/cli/commands/chunk-backfill.js.map +1 -0
- package/dist/src/cli/commands/compact.d.ts +3 -0
- package/dist/src/cli/commands/compact.d.ts.map +1 -0
- package/dist/src/cli/commands/compact.js +70 -0
- package/dist/src/cli/commands/compact.js.map +1 -0
- package/dist/src/cli/commands/cursor-probe.d.ts +20 -0
- package/dist/src/cli/commands/cursor-probe.d.ts.map +1 -0
- package/dist/src/cli/commands/cursor-probe.js +162 -0
- package/dist/src/cli/commands/cursor-probe.js.map +1 -0
- package/dist/src/cli/commands/doctor.d.ts +6 -0
- package/dist/src/cli/commands/doctor.d.ts.map +1 -0
- package/dist/src/cli/commands/doctor.js +259 -0
- package/dist/src/cli/commands/doctor.js.map +1 -0
- package/dist/src/cli/commands/feedback.d.ts +34 -0
- package/dist/src/cli/commands/feedback.d.ts.map +1 -0
- package/dist/src/cli/commands/feedback.js +214 -0
- package/dist/src/cli/commands/feedback.js.map +1 -0
- package/dist/src/cli/commands/hooks-install.d.ts +66 -0
- package/dist/src/cli/commands/hooks-install.d.ts.map +1 -0
- package/dist/src/cli/commands/hooks-install.js +228 -0
- package/dist/src/cli/commands/hooks-install.js.map +1 -0
- package/dist/src/cli/commands/import.d.ts +19 -0
- package/dist/src/cli/commands/import.d.ts.map +1 -0
- package/dist/src/cli/commands/import.js +258 -0
- package/dist/src/cli/commands/import.js.map +1 -0
- package/dist/src/cli/commands/ingest.d.ts +3 -0
- package/dist/src/cli/commands/ingest.d.ts.map +1 -0
- package/dist/src/cli/commands/ingest.js +80 -0
- package/dist/src/cli/commands/ingest.js.map +1 -0
- package/dist/src/cli/commands/mcp-install.d.ts +65 -0
- package/dist/src/cli/commands/mcp-install.d.ts.map +1 -0
- package/dist/src/cli/commands/mcp-install.js +306 -0
- package/dist/src/cli/commands/mcp-install.js.map +1 -0
- package/dist/src/cli/commands/menubar.d.ts +127 -0
- package/dist/src/cli/commands/menubar.d.ts.map +1 -0
- package/dist/src/cli/commands/menubar.js +585 -0
- package/dist/src/cli/commands/menubar.js.map +1 -0
- package/dist/src/cli/commands/onboard-first-run.d.ts +5 -0
- package/dist/src/cli/commands/onboard-first-run.d.ts.map +1 -0
- package/dist/src/cli/commands/onboard-first-run.js +156 -0
- package/dist/src/cli/commands/onboard-first-run.js.map +1 -0
- package/dist/src/cli/commands/onboard.d.ts +336 -0
- package/dist/src/cli/commands/onboard.d.ts.map +1 -0
- package/dist/src/cli/commands/onboard.js +1873 -0
- package/dist/src/cli/commands/onboard.js.map +1 -0
- package/dist/src/cli/commands/rebuild.d.ts +12 -0
- package/dist/src/cli/commands/rebuild.d.ts.map +1 -0
- package/dist/src/cli/commands/rebuild.js +167 -0
- package/dist/src/cli/commands/rebuild.js.map +1 -0
- package/dist/src/cli/commands/reconcile.d.ts +3 -0
- package/dist/src/cli/commands/reconcile.d.ts.map +1 -0
- package/dist/src/cli/commands/reconcile.js +68 -0
- package/dist/src/cli/commands/reconcile.js.map +1 -0
- package/dist/src/cli/commands/reindex.d.ts +3 -0
- package/dist/src/cli/commands/reindex.d.ts.map +1 -0
- package/dist/src/cli/commands/reindex.js +66 -0
- package/dist/src/cli/commands/reindex.js.map +1 -0
- package/dist/src/cli/commands/review.d.ts +13 -0
- package/dist/src/cli/commands/review.d.ts.map +1 -0
- package/dist/src/cli/commands/review.js +401 -0
- package/dist/src/cli/commands/review.js.map +1 -0
- package/dist/src/cli/commands/save.d.ts +3 -0
- package/dist/src/cli/commands/save.d.ts.map +1 -0
- package/dist/src/cli/commands/save.js +111 -0
- package/dist/src/cli/commands/save.js.map +1 -0
- package/dist/src/cli/commands/search.d.ts +37 -0
- package/dist/src/cli/commands/search.d.ts.map +1 -0
- package/dist/src/cli/commands/search.js +118 -0
- package/dist/src/cli/commands/search.js.map +1 -0
- package/dist/src/cli/commands/setup.d.ts +30 -0
- package/dist/src/cli/commands/setup.d.ts.map +1 -0
- package/dist/src/cli/commands/setup.js +122 -0
- package/dist/src/cli/commands/setup.js.map +1 -0
- package/dist/src/cli/commands/stats.d.ts +3 -0
- package/dist/src/cli/commands/stats.d.ts.map +1 -0
- package/dist/src/cli/commands/stats.js +42 -0
- package/dist/src/cli/commands/stats.js.map +1 -0
- package/dist/src/cli/commands/status.d.ts +17 -0
- package/dist/src/cli/commands/status.d.ts.map +1 -0
- package/dist/src/cli/commands/status.js +206 -0
- package/dist/src/cli/commands/status.js.map +1 -0
- package/dist/src/cli/commands/token-issue.d.ts +3 -0
- package/dist/src/cli/commands/token-issue.d.ts.map +1 -0
- package/dist/src/cli/commands/token-issue.js +33 -0
- package/dist/src/cli/commands/token-issue.js.map +1 -0
- package/dist/src/cli/commands/triage.d.ts +3 -0
- package/dist/src/cli/commands/triage.d.ts.map +1 -0
- package/dist/src/cli/commands/triage.js +127 -0
- package/dist/src/cli/commands/triage.js.map +1 -0
- package/dist/src/cli/commands/uninstall.d.ts +85 -0
- package/dist/src/cli/commands/uninstall.d.ts.map +1 -0
- package/dist/src/cli/commands/uninstall.js +512 -0
- package/dist/src/cli/commands/uninstall.js.map +1 -0
- package/dist/src/cli/commands/update.d.ts +105 -0
- package/dist/src/cli/commands/update.d.ts.map +1 -0
- package/dist/src/cli/commands/update.js +559 -0
- package/dist/src/cli/commands/update.js.map +1 -0
- package/dist/src/cli/default-config-path.d.ts +15 -0
- package/dist/src/cli/default-config-path.d.ts.map +1 -0
- package/dist/src/cli/default-config-path.js +28 -0
- package/dist/src/cli/default-config-path.js.map +1 -0
- package/dist/src/cli/feedback/feedback-config.d.ts +67 -0
- package/dist/src/cli/feedback/feedback-config.d.ts.map +1 -0
- package/dist/src/cli/feedback/feedback-config.js +169 -0
- package/dist/src/cli/feedback/feedback-config.js.map +1 -0
- package/dist/src/cli/feedback/feedback-history.d.ts +11 -0
- package/dist/src/cli/feedback/feedback-history.d.ts.map +1 -0
- package/dist/src/cli/feedback/feedback-history.js +145 -0
- package/dist/src/cli/feedback/feedback-history.js.map +1 -0
- package/dist/src/cli/feedback/feedback-payload.d.ts +74 -0
- package/dist/src/cli/feedback/feedback-payload.d.ts.map +1 -0
- package/dist/src/cli/feedback/feedback-payload.js +10 -0
- package/dist/src/cli/feedback/feedback-payload.js.map +1 -0
- package/dist/src/cli/feedback/feedback-relay.d.ts +15 -0
- package/dist/src/cli/feedback/feedback-relay.d.ts.map +1 -0
- package/dist/src/cli/feedback/feedback-relay.js +47 -0
- package/dist/src/cli/feedback/feedback-relay.js.map +1 -0
- package/dist/src/cli/feedback/feedback-status.d.ts +11 -0
- package/dist/src/cli/feedback/feedback-status.d.ts.map +1 -0
- package/dist/src/cli/feedback/feedback-status.js +122 -0
- package/dist/src/cli/feedback/feedback-status.js.map +1 -0
- package/dist/src/cli/feedback/invite.d.ts +17 -0
- package/dist/src/cli/feedback/invite.d.ts.map +1 -0
- package/dist/src/cli/feedback/invite.js +73 -0
- package/dist/src/cli/feedback/invite.js.map +1 -0
- package/dist/src/cli/feedback/relay-secret-store.d.ts +32 -0
- package/dist/src/cli/feedback/relay-secret-store.d.ts.map +1 -0
- package/dist/src/cli/feedback/relay-secret-store.js +137 -0
- package/dist/src/cli/feedback/relay-secret-store.js.map +1 -0
- package/dist/src/cli/hooks-writers/claude-code-policy-script.d.ts +35 -0
- package/dist/src/cli/hooks-writers/claude-code-policy-script.d.ts.map +1 -0
- package/dist/src/cli/hooks-writers/claude-code-policy-script.js +122 -0
- package/dist/src/cli/hooks-writers/claude-code-policy-script.js.map +1 -0
- package/dist/src/cli/hooks-writers/claude-code.d.ts +63 -0
- package/dist/src/cli/hooks-writers/claude-code.d.ts.map +1 -0
- package/dist/src/cli/hooks-writers/claude-code.js +289 -0
- package/dist/src/cli/hooks-writers/claude-code.js.map +1 -0
- package/dist/src/cli/hooks-writers/codex.d.ts +16 -0
- package/dist/src/cli/hooks-writers/codex.d.ts.map +1 -0
- package/dist/src/cli/hooks-writers/codex.js +17 -0
- package/dist/src/cli/hooks-writers/codex.js.map +1 -0
- package/dist/src/cli/hooks-writers/errors.d.ts +16 -0
- package/dist/src/cli/hooks-writers/errors.d.ts.map +1 -0
- package/dist/src/cli/hooks-writers/errors.js +24 -0
- package/dist/src/cli/hooks-writers/errors.js.map +1 -0
- package/dist/src/cli/hooks-writers/index.d.ts +23 -0
- package/dist/src/cli/hooks-writers/index.d.ts.map +1 -0
- package/dist/src/cli/hooks-writers/index.js +34 -0
- package/dist/src/cli/hooks-writers/index.js.map +1 -0
- package/dist/src/cli/hooks-writers/state.d.ts +43 -0
- package/dist/src/cli/hooks-writers/state.d.ts.map +1 -0
- package/dist/src/cli/hooks-writers/state.js +225 -0
- package/dist/src/cli/hooks-writers/state.js.map +1 -0
- package/dist/src/cli/hooks-writers/types.d.ts +28 -0
- package/dist/src/cli/hooks-writers/types.d.ts.map +1 -0
- package/dist/src/cli/hooks-writers/types.js +8 -0
- package/dist/src/cli/hooks-writers/types.js.map +1 -0
- package/dist/src/cli/http-client.d.ts +128 -0
- package/dist/src/cli/http-client.d.ts.map +1 -0
- package/dist/src/cli/http-client.js +401 -0
- package/dist/src/cli/http-client.js.map +1 -0
- package/dist/src/cli/index.d.ts +4 -0
- package/dist/src/cli/index.d.ts.map +1 -0
- package/dist/src/cli/index.js +99 -0
- package/dist/src/cli/index.js.map +1 -0
- package/dist/src/cli/job-poller.d.ts +13 -0
- package/dist/src/cli/job-poller.d.ts.map +1 -0
- package/dist/src/cli/job-poller.js +29 -0
- package/dist/src/cli/job-poller.js.map +1 -0
- package/dist/src/cli/mcp-config-writers/codex-toml.d.ts +20 -0
- package/dist/src/cli/mcp-config-writers/codex-toml.d.ts.map +1 -0
- package/dist/src/cli/mcp-config-writers/codex-toml.js +918 -0
- package/dist/src/cli/mcp-config-writers/codex-toml.js.map +1 -0
- package/dist/src/cli/mcp-config-writers/earlier-name-restrictions.d.ts +11 -0
- package/dist/src/cli/mcp-config-writers/earlier-name-restrictions.d.ts.map +1 -0
- package/dist/src/cli/mcp-config-writers/earlier-name-restrictions.js +152 -0
- package/dist/src/cli/mcp-config-writers/earlier-name-restrictions.js.map +1 -0
- package/dist/src/cli/mcp-config-writers/errors.d.ts +17 -0
- package/dist/src/cli/mcp-config-writers/errors.d.ts.map +1 -0
- package/dist/src/cli/mcp-config-writers/errors.js +13 -0
- package/dist/src/cli/mcp-config-writers/errors.js.map +1 -0
- package/dist/src/cli/mcp-config-writers/index.d.ts +28 -0
- package/dist/src/cli/mcp-config-writers/index.d.ts.map +1 -0
- package/dist/src/cli/mcp-config-writers/index.js +70 -0
- package/dist/src/cli/mcp-config-writers/index.js.map +1 -0
- package/dist/src/cli/mcp-config-writers/json-config.d.ts +20 -0
- package/dist/src/cli/mcp-config-writers/json-config.d.ts.map +1 -0
- package/dist/src/cli/mcp-config-writers/json-config.js +301 -0
- package/dist/src/cli/mcp-config-writers/json-config.js.map +1 -0
- package/dist/src/cli/mcp-config-writers/left-in-folder.d.ts +18 -0
- package/dist/src/cli/mcp-config-writers/left-in-folder.d.ts.map +1 -0
- package/dist/src/cli/mcp-config-writers/left-in-folder.js +140 -0
- package/dist/src/cli/mcp-config-writers/left-in-folder.js.map +1 -0
- package/dist/src/cli/mcp-config-writers/redact.d.ts +28 -0
- package/dist/src/cli/mcp-config-writers/redact.d.ts.map +1 -0
- package/dist/src/cli/mcp-config-writers/redact.js +48 -0
- package/dist/src/cli/mcp-config-writers/redact.js.map +1 -0
- package/dist/src/cli/mcp-config-writers/types.d.ts +46 -0
- package/dist/src/cli/mcp-config-writers/types.d.ts.map +1 -0
- package/dist/src/cli/mcp-config-writers/types.js +5 -0
- package/dist/src/cli/mcp-config-writers/types.js.map +1 -0
- package/dist/src/cli/output.d.ts +8 -0
- package/dist/src/cli/output.d.ts.map +1 -0
- package/dist/src/cli/output.js +34 -0
- package/dist/src/cli/output.js.map +1 -0
- package/dist/src/cli/postinstall-menubar.d.ts +38 -0
- package/dist/src/cli/postinstall-menubar.d.ts.map +1 -0
- package/dist/src/cli/postinstall-menubar.js +139 -0
- package/dist/src/cli/postinstall-menubar.js.map +1 -0
- package/dist/src/cli/status/friend-header.d.ts +61 -0
- package/dist/src/cli/status/friend-header.d.ts.map +1 -0
- package/dist/src/cli/status/friend-header.js +727 -0
- package/dist/src/cli/status/friend-header.js.map +1 -0
- package/dist/src/cli/status/local-signals.d.ts +89 -0
- package/dist/src/cli/status/local-signals.d.ts.map +1 -0
- package/dist/src/cli/status/local-signals.js +194 -0
- package/dist/src/cli/status/local-signals.js.map +1 -0
- package/dist/src/cli/token.d.ts +37 -0
- package/dist/src/cli/token.d.ts.map +1 -0
- package/dist/src/cli/token.js +105 -0
- package/dist/src/cli/token.js.map +1 -0
- package/dist/src/cli/ui.d.ts +47 -0
- package/dist/src/cli/ui.d.ts.map +1 -0
- package/dist/src/cli/ui.js +166 -0
- package/dist/src/cli/ui.js.map +1 -0
- package/dist/src/cli/uninstall/mcp-uninstall.d.ts +33 -0
- package/dist/src/cli/uninstall/mcp-uninstall.d.ts.map +1 -0
- package/dist/src/cli/uninstall/mcp-uninstall.js +116 -0
- package/dist/src/cli/uninstall/mcp-uninstall.js.map +1 -0
- package/dist/src/config/loader.d.ts +9 -0
- package/dist/src/config/loader.d.ts.map +1 -0
- package/dist/src/config/loader.js +73 -0
- package/dist/src/config/loader.js.map +1 -0
- package/dist/src/config/schema.d.ts +956 -0
- package/dist/src/config/schema.d.ts.map +1 -0
- package/dist/src/config/schema.js +339 -0
- package/dist/src/config/schema.js.map +1 -0
- package/dist/src/diagnostics/claude-preflight.d.ts +35 -0
- package/dist/src/diagnostics/claude-preflight.d.ts.map +1 -0
- package/dist/src/diagnostics/claude-preflight.js +90 -0
- package/dist/src/diagnostics/claude-preflight.js.map +1 -0
- package/dist/src/diagnostics/codex-preflight.d.ts +43 -0
- package/dist/src/diagnostics/codex-preflight.d.ts.map +1 -0
- package/dist/src/diagnostics/codex-preflight.js +128 -0
- package/dist/src/diagnostics/codex-preflight.js.map +1 -0
- package/dist/src/diagnostics/doctor.d.ts +125 -0
- package/dist/src/diagnostics/doctor.d.ts.map +1 -0
- package/dist/src/diagnostics/doctor.js +450 -0
- package/dist/src/diagnostics/doctor.js.map +1 -0
- package/dist/src/diagnostics/install-layout.d.ts +40 -0
- package/dist/src/diagnostics/install-layout.d.ts.map +1 -0
- package/dist/src/diagnostics/install-layout.js +55 -0
- package/dist/src/diagnostics/install-layout.js.map +1 -0
- package/dist/src/diagnostics/memory-coverage.d.ts +54 -0
- package/dist/src/diagnostics/memory-coverage.d.ts.map +1 -0
- package/dist/src/diagnostics/memory-coverage.js +272 -0
- package/dist/src/diagnostics/memory-coverage.js.map +1 -0
- package/dist/src/diagnostics/notify.d.ts +107 -0
- package/dist/src/diagnostics/notify.d.ts.map +1 -0
- package/dist/src/diagnostics/notify.js +217 -0
- package/dist/src/diagnostics/notify.js.map +1 -0
- package/dist/src/diagnostics/repair-prompt.d.ts +49 -0
- package/dist/src/diagnostics/repair-prompt.d.ts.map +1 -0
- package/dist/src/diagnostics/repair-prompt.js +223 -0
- package/dist/src/diagnostics/repair-prompt.js.map +1 -0
- package/dist/src/ingestion/bulk-ingest.d.ts +11 -0
- package/dist/src/ingestion/bulk-ingest.d.ts.map +1 -0
- package/dist/src/ingestion/bulk-ingest.js +11 -0
- package/dist/src/ingestion/bulk-ingest.js.map +1 -0
- package/dist/src/ingestion/chunk-meta.d.ts +85 -0
- package/dist/src/ingestion/chunk-meta.d.ts.map +1 -0
- package/dist/src/ingestion/chunk-meta.js +167 -0
- package/dist/src/ingestion/chunk-meta.js.map +1 -0
- package/dist/src/ingestion/chunk-text.d.ts +39 -0
- package/dist/src/ingestion/chunk-text.d.ts.map +1 -0
- package/dist/src/ingestion/chunk-text.js +114 -0
- package/dist/src/ingestion/chunk-text.js.map +1 -0
- package/dist/src/ingestion/cursor/cursor-store.d.ts +177 -0
- package/dist/src/ingestion/cursor/cursor-store.d.ts.map +1 -0
- package/dist/src/ingestion/cursor/cursor-store.js +242 -0
- package/dist/src/ingestion/cursor/cursor-store.js.map +1 -0
- package/dist/src/ingestion/cursor/enrich-roots.d.ts +16 -0
- package/dist/src/ingestion/cursor/enrich-roots.d.ts.map +1 -0
- package/dist/src/ingestion/cursor/enrich-roots.js +22 -0
- package/dist/src/ingestion/cursor/enrich-roots.js.map +1 -0
- package/dist/src/ingestion/cursor/vscdb-reader.d.ts +32 -0
- package/dist/src/ingestion/cursor/vscdb-reader.d.ts.map +1 -0
- package/dist/src/ingestion/cursor/vscdb-reader.js +113 -0
- package/dist/src/ingestion/cursor/vscdb-reader.js.map +1 -0
- package/dist/src/ingestion/cursor/workspace-root.d.ts +96 -0
- package/dist/src/ingestion/cursor/workspace-root.d.ts.map +1 -0
- package/dist/src/ingestion/cursor/workspace-root.js +187 -0
- package/dist/src/ingestion/cursor/workspace-root.js.map +1 -0
- package/dist/src/ingestion/extractor.d.ts +16 -0
- package/dist/src/ingestion/extractor.d.ts.map +1 -0
- package/dist/src/ingestion/extractor.js +85 -0
- package/dist/src/ingestion/extractor.js.map +1 -0
- package/dist/src/ingestion/extractors/docx.d.ts +3 -0
- package/dist/src/ingestion/extractors/docx.d.ts.map +1 -0
- package/dist/src/ingestion/extractors/docx.js +20 -0
- package/dist/src/ingestion/extractors/docx.js.map +1 -0
- package/dist/src/ingestion/extractors/pdf.d.ts +3 -0
- package/dist/src/ingestion/extractors/pdf.d.ts.map +1 -0
- package/dist/src/ingestion/extractors/pdf.js +32 -0
- package/dist/src/ingestion/extractors/pdf.js.map +1 -0
- package/dist/src/ingestion/historical-campaign.d.ts +340 -0
- package/dist/src/ingestion/historical-campaign.d.ts.map +1 -0
- package/dist/src/ingestion/historical-campaign.js +1010 -0
- package/dist/src/ingestion/historical-campaign.js.map +1 -0
- package/dist/src/ingestion/ignored-paths.d.ts +20 -0
- package/dist/src/ingestion/ignored-paths.d.ts.map +1 -0
- package/dist/src/ingestion/ignored-paths.js +45 -0
- package/dist/src/ingestion/ignored-paths.js.map +1 -0
- package/dist/src/ingestion/inbox-core/conversation-fingerprint.d.ts +2 -0
- package/dist/src/ingestion/inbox-core/conversation-fingerprint.d.ts.map +1 -0
- package/dist/src/ingestion/inbox-core/conversation-fingerprint.js +27 -0
- package/dist/src/ingestion/inbox-core/conversation-fingerprint.js.map +1 -0
- package/dist/src/ingestion/inbox-core/conversation-key.d.ts +2 -0
- package/dist/src/ingestion/inbox-core/conversation-key.d.ts.map +1 -0
- package/dist/src/ingestion/inbox-core/conversation-key.js +31 -0
- package/dist/src/ingestion/inbox-core/conversation-key.js.map +1 -0
- package/dist/src/ingestion/inbox-core/extensions.d.ts +3 -0
- package/dist/src/ingestion/inbox-core/extensions.d.ts.map +1 -0
- package/dist/src/ingestion/inbox-core/extensions.js +16 -0
- package/dist/src/ingestion/inbox-core/extensions.js.map +1 -0
- package/dist/src/ingestion/inbox-core/idempotency.d.ts +2 -0
- package/dist/src/ingestion/inbox-core/idempotency.d.ts.map +1 -0
- package/dist/src/ingestion/inbox-core/idempotency.js +22 -0
- package/dist/src/ingestion/inbox-core/idempotency.js.map +1 -0
- package/dist/src/ingestion/inbox-core/index.d.ts +20 -0
- package/dist/src/ingestion/inbox-core/index.d.ts.map +1 -0
- package/dist/src/ingestion/inbox-core/index.js +20 -0
- package/dist/src/ingestion/inbox-core/index.js.map +1 -0
- package/dist/src/ingestion/inbox-core/source-detection.d.ts +2 -0
- package/dist/src/ingestion/inbox-core/source-detection.d.ts.map +1 -0
- package/dist/src/ingestion/inbox-core/source-detection.js +23 -0
- package/dist/src/ingestion/inbox-core/source-detection.js.map +1 -0
- package/dist/src/ingestion/inbox-core/source-sniffer.d.ts +11 -0
- package/dist/src/ingestion/inbox-core/source-sniffer.d.ts.map +1 -0
- package/dist/src/ingestion/inbox-core/source-sniffer.js +69 -0
- package/dist/src/ingestion/inbox-core/source-sniffer.js.map +1 -0
- package/dist/src/ingestion/inbox-core/zip-sniffer.d.ts +70 -0
- package/dist/src/ingestion/inbox-core/zip-sniffer.d.ts.map +1 -0
- package/dist/src/ingestion/inbox-core/zip-sniffer.js +161 -0
- package/dist/src/ingestion/inbox-core/zip-sniffer.js.map +1 -0
- package/dist/src/ingestion/inbox-watcher.d.ts +12 -0
- package/dist/src/ingestion/inbox-watcher.d.ts.map +1 -0
- package/dist/src/ingestion/inbox-watcher.js +83 -0
- package/dist/src/ingestion/inbox-watcher.js.map +1 -0
- package/dist/src/ingestion/indexer.d.ts +39 -0
- package/dist/src/ingestion/indexer.d.ts.map +1 -0
- package/dist/src/ingestion/indexer.js +111 -0
- package/dist/src/ingestion/indexer.js.map +1 -0
- package/dist/src/ingestion/metadata-extraction.d.ts +56 -0
- package/dist/src/ingestion/metadata-extraction.d.ts.map +1 -0
- package/dist/src/ingestion/metadata-extraction.js +151 -0
- package/dist/src/ingestion/metadata-extraction.js.map +1 -0
- package/dist/src/ingestion/parsers/chatgpt-web.d.ts +29 -0
- package/dist/src/ingestion/parsers/chatgpt-web.d.ts.map +1 -0
- package/dist/src/ingestion/parsers/chatgpt-web.js +100 -0
- package/dist/src/ingestion/parsers/chatgpt-web.js.map +1 -0
- package/dist/src/ingestion/parsers/claude-code-jsonl.d.ts +26 -0
- package/dist/src/ingestion/parsers/claude-code-jsonl.d.ts.map +1 -0
- package/dist/src/ingestion/parsers/claude-code-jsonl.js +125 -0
- package/dist/src/ingestion/parsers/claude-code-jsonl.js.map +1 -0
- package/dist/src/ingestion/parsers/claude-desktop-sessions.d.ts +17 -0
- package/dist/src/ingestion/parsers/claude-desktop-sessions.d.ts.map +1 -0
- package/dist/src/ingestion/parsers/claude-desktop-sessions.js +112 -0
- package/dist/src/ingestion/parsers/claude-desktop-sessions.js.map +1 -0
- package/dist/src/ingestion/parsers/claude-web.d.ts +24 -0
- package/dist/src/ingestion/parsers/claude-web.d.ts.map +1 -0
- package/dist/src/ingestion/parsers/claude-web.js +75 -0
- package/dist/src/ingestion/parsers/claude-web.js.map +1 -0
- package/dist/src/ingestion/parsers/codex-jsonl.d.ts +18 -0
- package/dist/src/ingestion/parsers/codex-jsonl.d.ts.map +1 -0
- package/dist/src/ingestion/parsers/codex-jsonl.js +150 -0
- package/dist/src/ingestion/parsers/codex-jsonl.js.map +1 -0
- package/dist/src/ingestion/parsers/gemini-web.d.ts +16 -0
- package/dist/src/ingestion/parsers/gemini-web.d.ts.map +1 -0
- package/dist/src/ingestion/parsers/gemini-web.js +170 -0
- package/dist/src/ingestion/parsers/gemini-web.js.map +1 -0
- package/dist/src/ingestion/parsers/grok-cli.d.ts +19 -0
- package/dist/src/ingestion/parsers/grok-cli.d.ts.map +1 -0
- package/dist/src/ingestion/parsers/grok-cli.js +126 -0
- package/dist/src/ingestion/parsers/grok-cli.js.map +1 -0
- package/dist/src/ingestion/parsers/grok-web.d.ts +40 -0
- package/dist/src/ingestion/parsers/grok-web.d.ts.map +1 -0
- package/dist/src/ingestion/parsers/grok-web.js +67 -0
- package/dist/src/ingestion/parsers/grok-web.js.map +1 -0
- package/dist/src/ingestion/parsers/turns.d.ts +36 -0
- package/dist/src/ingestion/parsers/turns.d.ts.map +1 -0
- package/dist/src/ingestion/parsers/turns.js +25 -0
- package/dist/src/ingestion/parsers/turns.js.map +1 -0
- package/dist/src/ingestion/parsers/types.d.ts +47 -0
- package/dist/src/ingestion/parsers/types.d.ts.map +1 -0
- package/dist/src/ingestion/parsers/types.js +26 -0
- package/dist/src/ingestion/parsers/types.js.map +1 -0
- package/dist/src/ingestion/report-cards.d.ts +75 -0
- package/dist/src/ingestion/report-cards.d.ts.map +1 -0
- package/dist/src/ingestion/report-cards.js +224 -0
- package/dist/src/ingestion/report-cards.js.map +1 -0
- package/dist/src/ingestion/scanner.d.ts +48 -0
- package/dist/src/ingestion/scanner.d.ts.map +1 -0
- package/dist/src/ingestion/scanner.js +131 -0
- package/dist/src/ingestion/scanner.js.map +1 -0
- package/dist/src/ingestion/skip-quarantine.d.ts +10 -0
- package/dist/src/ingestion/skip-quarantine.d.ts.map +1 -0
- package/dist/src/ingestion/skip-quarantine.js +35 -0
- package/dist/src/ingestion/skip-quarantine.js.map +1 -0
- package/dist/src/ingestion/staging.d.ts +109 -0
- package/dist/src/ingestion/staging.d.ts.map +1 -0
- package/dist/src/ingestion/staging.js +411 -0
- package/dist/src/ingestion/staging.js.map +1 -0
- package/dist/src/ingestion/watcher.d.ts +65 -0
- package/dist/src/ingestion/watcher.d.ts.map +1 -0
- package/dist/src/ingestion/watcher.js +182 -0
- package/dist/src/ingestion/watcher.js.map +1 -0
- package/dist/src/jobs/codex-override-handler.d.ts +3 -0
- package/dist/src/jobs/codex-override-handler.d.ts.map +1 -0
- package/dist/src/jobs/codex-override-handler.js +16 -0
- package/dist/src/jobs/codex-override-handler.js.map +1 -0
- package/dist/src/jobs/handlers/compact.d.ts +30 -0
- package/dist/src/jobs/handlers/compact.d.ts.map +1 -0
- package/dist/src/jobs/handlers/compact.js +360 -0
- package/dist/src/jobs/handlers/compact.js.map +1 -0
- package/dist/src/jobs/handlers/dedupe-conversations.d.ts +134 -0
- package/dist/src/jobs/handlers/dedupe-conversations.d.ts.map +1 -0
- package/dist/src/jobs/handlers/dedupe-conversations.js +371 -0
- package/dist/src/jobs/handlers/dedupe-conversations.js.map +1 -0
- package/dist/src/jobs/handlers/ingest.d.ts +13 -0
- package/dist/src/jobs/handlers/ingest.d.ts.map +1 -0
- package/dist/src/jobs/handlers/ingest.js +557 -0
- package/dist/src/jobs/handlers/ingest.js.map +1 -0
- package/dist/src/jobs/handlers/optimize.d.ts +11 -0
- package/dist/src/jobs/handlers/optimize.d.ts.map +1 -0
- package/dist/src/jobs/handlers/optimize.js +12 -0
- package/dist/src/jobs/handlers/optimize.js.map +1 -0
- package/dist/src/jobs/handlers/reconcile.d.ts +57 -0
- package/dist/src/jobs/handlers/reconcile.d.ts.map +1 -0
- package/dist/src/jobs/handlers/reconcile.js +685 -0
- package/dist/src/jobs/handlers/reconcile.js.map +1 -0
- package/dist/src/jobs/handlers/reindex.d.ts +38 -0
- package/dist/src/jobs/handlers/reindex.d.ts.map +1 -0
- package/dist/src/jobs/handlers/reindex.js +63 -0
- package/dist/src/jobs/handlers/reindex.js.map +1 -0
- package/dist/src/jobs/handlers/save.d.ts +10 -0
- package/dist/src/jobs/handlers/save.d.ts.map +1 -0
- package/dist/src/jobs/handlers/save.js +316 -0
- package/dist/src/jobs/handlers/save.js.map +1 -0
- package/dist/src/jobs/handlers/triage.d.ts +47 -0
- package/dist/src/jobs/handlers/triage.d.ts.map +1 -0
- package/dist/src/jobs/handlers/triage.js +95 -0
- package/dist/src/jobs/handlers/triage.js.map +1 -0
- package/dist/src/jobs/maintenance-scheduler.d.ts +30 -0
- package/dist/src/jobs/maintenance-scheduler.d.ts.map +1 -0
- package/dist/src/jobs/maintenance-scheduler.js +41 -0
- package/dist/src/jobs/maintenance-scheduler.js.map +1 -0
- package/dist/src/jobs/queue.d.ts +157 -0
- package/dist/src/jobs/queue.d.ts.map +1 -0
- package/dist/src/jobs/queue.js +759 -0
- package/dist/src/jobs/queue.js.map +1 -0
- package/dist/src/jobs/types.d.ts +53 -0
- package/dist/src/jobs/types.d.ts.map +1 -0
- package/dist/src/jobs/types.js +33 -0
- package/dist/src/jobs/types.js.map +1 -0
- package/dist/src/jobs/worker-entry.d.ts +10 -0
- package/dist/src/jobs/worker-entry.d.ts.map +1 -0
- package/dist/src/jobs/worker-entry.js +252 -0
- package/dist/src/jobs/worker-entry.js.map +1 -0
- package/dist/src/jobs/worker-process.d.ts +67 -0
- package/dist/src/jobs/worker-process.d.ts.map +1 -0
- package/dist/src/jobs/worker-process.js +227 -0
- package/dist/src/jobs/worker-process.js.map +1 -0
- package/dist/src/jobs/worker.d.ts +11 -0
- package/dist/src/jobs/worker.d.ts.map +1 -0
- package/dist/src/jobs/worker.js +14 -0
- package/dist/src/jobs/worker.js.map +1 -0
- package/dist/src/main.d.ts +2 -0
- package/dist/src/main.d.ts.map +1 -0
- package/dist/src/main.js +683 -0
- package/dist/src/main.js.map +1 -0
- package/dist/src/mcp/capture-diagnostics.d.ts +51 -0
- package/dist/src/mcp/capture-diagnostics.d.ts.map +1 -0
- package/dist/src/mcp/capture-diagnostics.js +127 -0
- package/dist/src/mcp/capture-diagnostics.js.map +1 -0
- package/dist/src/mcp/errors.d.ts +8 -0
- package/dist/src/mcp/errors.d.ts.map +1 -0
- package/dist/src/mcp/errors.js +69 -0
- package/dist/src/mcp/errors.js.map +1 -0
- package/dist/src/mcp/memory-diagnostics.d.ts +6 -0
- package/dist/src/mcp/memory-diagnostics.d.ts.map +1 -0
- package/dist/src/mcp/memory-diagnostics.js +51 -0
- package/dist/src/mcp/memory-diagnostics.js.map +1 -0
- package/dist/src/mcp/names.d.ts +48 -0
- package/dist/src/mcp/names.d.ts.map +1 -0
- package/dist/src/mcp/names.js +77 -0
- package/dist/src/mcp/names.js.map +1 -0
- package/dist/src/mcp/server.d.ts +19 -0
- package/dist/src/mcp/server.d.ts.map +1 -0
- package/dist/src/mcp/server.js +184 -0
- package/dist/src/mcp/server.js.map +1 -0
- package/dist/src/mcp/stdio.d.ts +2 -0
- package/dist/src/mcp/stdio.d.ts.map +1 -0
- package/dist/src/mcp/stdio.js +8 -0
- package/dist/src/mcp/stdio.js.map +1 -0
- package/dist/src/mcp/tools/context-pack.d.ts +35 -0
- package/dist/src/mcp/tools/context-pack.d.ts.map +1 -0
- package/dist/src/mcp/tools/context-pack.js +279 -0
- package/dist/src/mcp/tools/context-pack.js.map +1 -0
- package/dist/src/mcp/tools/conversations-search.d.ts +38 -0
- package/dist/src/mcp/tools/conversations-search.d.ts.map +1 -0
- package/dist/src/mcp/tools/conversations-search.js +79 -0
- package/dist/src/mcp/tools/conversations-search.js.map +1 -0
- package/dist/src/mcp/tools/evidence-feedback.d.ts +60 -0
- package/dist/src/mcp/tools/evidence-feedback.d.ts.map +1 -0
- package/dist/src/mcp/tools/evidence-feedback.js +62 -0
- package/dist/src/mcp/tools/evidence-feedback.js.map +1 -0
- package/dist/src/mcp/tools/log-outcome.d.ts +72 -0
- package/dist/src/mcp/tools/log-outcome.d.ts.map +1 -0
- package/dist/src/mcp/tools/log-outcome.js +59 -0
- package/dist/src/mcp/tools/log-outcome.js.map +1 -0
- package/dist/src/mcp/tools/open-evidence.d.ts +37 -0
- package/dist/src/mcp/tools/open-evidence.d.ts.map +1 -0
- package/dist/src/mcp/tools/open-evidence.js +72 -0
- package/dist/src/mcp/tools/open-evidence.js.map +1 -0
- package/dist/src/mcp/tools/save.d.ts +37 -0
- package/dist/src/mcp/tools/save.d.ts.map +1 -0
- package/dist/src/mcp/tools/save.js +65 -0
- package/dist/src/mcp/tools/save.js.map +1 -0
- package/dist/src/mcp/tools/search.d.ts +37 -0
- package/dist/src/mcp/tools/search.d.ts.map +1 -0
- package/dist/src/mcp/tools/search.js +95 -0
- package/dist/src/mcp/tools/search.js.map +1 -0
- package/dist/src/mcp/tools/status.d.ts +31 -0
- package/dist/src/mcp/tools/status.d.ts.map +1 -0
- package/dist/src/mcp/tools/status.js +63 -0
- package/dist/src/mcp/tools/status.js.map +1 -0
- package/dist/src/observability/coverage.d.ts +100 -0
- package/dist/src/observability/coverage.d.ts.map +1 -0
- package/dist/src/observability/coverage.js +178 -0
- package/dist/src/observability/coverage.js.map +1 -0
- package/dist/src/observability/embedding-events.d.ts +52 -0
- package/dist/src/observability/embedding-events.d.ts.map +1 -0
- package/dist/src/observability/embedding-events.js +149 -0
- package/dist/src/observability/embedding-events.js.map +1 -0
- package/dist/src/observability/index-events.d.ts +70 -0
- package/dist/src/observability/index-events.d.ts.map +1 -0
- package/dist/src/observability/index-events.js +148 -0
- package/dist/src/observability/index-events.js.map +1 -0
- package/dist/src/observability/onboarding-metric.d.ts +131 -0
- package/dist/src/observability/onboarding-metric.d.ts.map +1 -0
- package/dist/src/observability/onboarding-metric.js +352 -0
- package/dist/src/observability/onboarding-metric.js.map +1 -0
- package/dist/src/observability/retrieval-feedback.d.ts +82 -0
- package/dist/src/observability/retrieval-feedback.d.ts.map +1 -0
- package/dist/src/observability/retrieval-feedback.js +231 -0
- package/dist/src/observability/retrieval-feedback.js.map +1 -0
- package/dist/src/observability/rift-context.d.ts +53 -0
- package/dist/src/observability/rift-context.d.ts.map +1 -0
- package/dist/src/observability/rift-context.js +141 -0
- package/dist/src/observability/rift-context.js.map +1 -0
- package/dist/src/observability/staleness.d.ts +43 -0
- package/dist/src/observability/staleness.d.ts.map +1 -0
- package/dist/src/observability/staleness.js +74 -0
- package/dist/src/observability/staleness.js.map +1 -0
- package/dist/src/observability/tool-usage-stats.d.ts +139 -0
- package/dist/src/observability/tool-usage-stats.d.ts.map +1 -0
- package/dist/src/observability/tool-usage-stats.js +245 -0
- package/dist/src/observability/tool-usage-stats.js.map +1 -0
- package/dist/src/observability/tool-usage.d.ts +231 -0
- package/dist/src/observability/tool-usage.d.ts.map +1 -0
- package/dist/src/observability/tool-usage.js +489 -0
- package/dist/src/observability/tool-usage.js.map +1 -0
- package/dist/src/observability/version-check.d.ts +75 -0
- package/dist/src/observability/version-check.d.ts.map +1 -0
- package/dist/src/observability/version-check.js +198 -0
- package/dist/src/observability/version-check.js.map +1 -0
- package/dist/src/onboarding/capture-switch.d.ts +20 -0
- package/dist/src/onboarding/capture-switch.d.ts.map +1 -0
- package/dist/src/onboarding/capture-switch.js +52 -0
- package/dist/src/onboarding/capture-switch.js.map +1 -0
- package/dist/src/onboarding/daemon-control.d.ts +33 -0
- package/dist/src/onboarding/daemon-control.d.ts.map +1 -0
- package/dist/src/onboarding/daemon-control.js +92 -0
- package/dist/src/onboarding/daemon-control.js.map +1 -0
- package/dist/src/onboarding/env-file.d.ts +18 -0
- package/dist/src/onboarding/env-file.d.ts.map +1 -0
- package/dist/src/onboarding/env-file.js +89 -0
- package/dist/src/onboarding/env-file.js.map +1 -0
- package/dist/src/onboarding/search-by-meaning.d.ts +35 -0
- package/dist/src/onboarding/search-by-meaning.d.ts.map +1 -0
- package/dist/src/onboarding/search-by-meaning.js +163 -0
- package/dist/src/onboarding/search-by-meaning.js.map +1 -0
- package/dist/src/onboarding/setup-page.d.ts +7 -0
- package/dist/src/onboarding/setup-page.d.ts.map +1 -0
- package/dist/src/onboarding/setup-page.js +823 -0
- package/dist/src/onboarding/setup-page.js.map +1 -0
- package/dist/src/onboarding/setup-session.d.ts +22 -0
- package/dist/src/onboarding/setup-session.d.ts.map +1 -0
- package/dist/src/onboarding/setup-session.js +80 -0
- package/dist/src/onboarding/setup-session.js.map +1 -0
- package/dist/src/onboarding/setup-state.d.ts +117 -0
- package/dist/src/onboarding/setup-state.d.ts.map +1 -0
- package/dist/src/onboarding/setup-state.js +267 -0
- package/dist/src/onboarding/setup-state.js.map +1 -0
- package/dist/src/onboarding/voyage-validate.d.ts +22 -0
- package/dist/src/onboarding/voyage-validate.d.ts.map +1 -0
- package/dist/src/onboarding/voyage-validate.js +96 -0
- package/dist/src/onboarding/voyage-validate.js.map +1 -0
- package/dist/src/providers/anthropic-digest.d.ts +23 -0
- package/dist/src/providers/anthropic-digest.d.ts.map +1 -0
- package/dist/src/providers/anthropic-digest.js +91 -0
- package/dist/src/providers/anthropic-digest.js.map +1 -0
- package/dist/src/providers/basic-metadata-extraction.d.ts +60 -0
- package/dist/src/providers/basic-metadata-extraction.d.ts.map +1 -0
- package/dist/src/providers/basic-metadata-extraction.js +114 -0
- package/dist/src/providers/basic-metadata-extraction.js.map +1 -0
- package/dist/src/providers/claude-cli-metadata-extraction.d.ts +47 -0
- package/dist/src/providers/claude-cli-metadata-extraction.d.ts.map +1 -0
- package/dist/src/providers/claude-cli-metadata-extraction.js +130 -0
- package/dist/src/providers/claude-cli-metadata-extraction.js.map +1 -0
- package/dist/src/providers/claude-cli-runner.d.ts +92 -0
- package/dist/src/providers/claude-cli-runner.d.ts.map +1 -0
- package/dist/src/providers/claude-cli-runner.js +598 -0
- package/dist/src/providers/claude-cli-runner.js.map +1 -0
- package/dist/src/providers/claude-input-limit.d.ts +32 -0
- package/dist/src/providers/claude-input-limit.d.ts.map +1 -0
- package/dist/src/providers/claude-input-limit.js +32 -0
- package/dist/src/providers/claude-input-limit.js.map +1 -0
- package/dist/src/providers/codex-cli-digest.d.ts +12 -0
- package/dist/src/providers/codex-cli-digest.d.ts.map +1 -0
- package/dist/src/providers/codex-cli-digest.js +70 -0
- package/dist/src/providers/codex-cli-digest.js.map +1 -0
- package/dist/src/providers/codex-cli-metadata-extraction.d.ts +15 -0
- package/dist/src/providers/codex-cli-metadata-extraction.d.ts.map +1 -0
- package/dist/src/providers/codex-cli-metadata-extraction.js +78 -0
- package/dist/src/providers/codex-cli-metadata-extraction.js.map +1 -0
- package/dist/src/providers/codex-cli-model.d.ts +110 -0
- package/dist/src/providers/codex-cli-model.d.ts.map +1 -0
- package/dist/src/providers/codex-cli-model.js +338 -0
- package/dist/src/providers/codex-cli-model.js.map +1 -0
- package/dist/src/providers/codex-cli-runner.d.ts +70 -0
- package/dist/src/providers/codex-cli-runner.d.ts.map +1 -0
- package/dist/src/providers/codex-cli-runner.js +745 -0
- package/dist/src/providers/codex-cli-runner.js.map +1 -0
- package/dist/src/providers/codex-input-limit.d.ts +43 -0
- package/dist/src/providers/codex-input-limit.d.ts.map +1 -0
- package/dist/src/providers/codex-input-limit.js +70 -0
- package/dist/src/providers/codex-input-limit.js.map +1 -0
- package/dist/src/providers/conversation-generation.d.ts +21 -0
- package/dist/src/providers/conversation-generation.d.ts.map +1 -0
- package/dist/src/providers/conversation-generation.js +132 -0
- package/dist/src/providers/conversation-generation.js.map +1 -0
- package/dist/src/providers/ollama-embed.d.ts +23 -0
- package/dist/src/providers/ollama-embed.d.ts.map +1 -0
- package/dist/src/providers/ollama-embed.js +137 -0
- package/dist/src/providers/ollama-embed.js.map +1 -0
- package/dist/src/providers/ollama.d.ts +42 -0
- package/dist/src/providers/ollama.d.ts.map +1 -0
- package/dist/src/providers/ollama.js +169 -0
- package/dist/src/providers/ollama.js.map +1 -0
- package/dist/src/providers/openai-metadata-extraction.d.ts +120 -0
- package/dist/src/providers/openai-metadata-extraction.d.ts.map +1 -0
- package/dist/src/providers/openai-metadata-extraction.js +225 -0
- package/dist/src/providers/openai-metadata-extraction.js.map +1 -0
- package/dist/src/providers/operator-overrides.d.ts +24 -0
- package/dist/src/providers/operator-overrides.d.ts.map +1 -0
- package/dist/src/providers/operator-overrides.js +84 -0
- package/dist/src/providers/operator-overrides.js.map +1 -0
- package/dist/src/providers/placeholder-embed.d.ts +56 -0
- package/dist/src/providers/placeholder-embed.d.ts.map +1 -0
- package/dist/src/providers/placeholder-embed.js +64 -0
- package/dist/src/providers/placeholder-embed.js.map +1 -0
- package/dist/src/providers/shared-request.d.ts +22 -0
- package/dist/src/providers/shared-request.d.ts.map +1 -0
- package/dist/src/providers/shared-request.js +63 -0
- package/dist/src/providers/shared-request.js.map +1 -0
- package/dist/src/providers/stub.d.ts +19 -0
- package/dist/src/providers/stub.d.ts.map +1 -0
- package/dist/src/providers/stub.js +74 -0
- package/dist/src/providers/stub.js.map +1 -0
- package/dist/src/providers/types.d.ts +100 -0
- package/dist/src/providers/types.d.ts.map +1 -0
- package/dist/src/providers/types.js +63 -0
- package/dist/src/providers/types.js.map +1 -0
- package/dist/src/providers/voyage.d.ts +55 -0
- package/dist/src/providers/voyage.d.ts.map +1 -0
- package/dist/src/providers/voyage.js +179 -0
- package/dist/src/providers/voyage.js.map +1 -0
- package/dist/src/retrieval/brief.d.ts +26 -0
- package/dist/src/retrieval/brief.d.ts.map +1 -0
- package/dist/src/retrieval/brief.js +268 -0
- package/dist/src/retrieval/brief.js.map +1 -0
- package/dist/src/retrieval/canonical-files.d.ts +79 -0
- package/dist/src/retrieval/canonical-files.d.ts.map +1 -0
- package/dist/src/retrieval/canonical-files.js +432 -0
- package/dist/src/retrieval/canonical-files.js.map +1 -0
- package/dist/src/retrieval/compact.d.ts +306 -0
- package/dist/src/retrieval/compact.d.ts.map +1 -0
- package/dist/src/retrieval/compact.js +810 -0
- package/dist/src/retrieval/compact.js.map +1 -0
- package/dist/src/retrieval/context-pack.d.ts +344 -0
- package/dist/src/retrieval/context-pack.d.ts.map +1 -0
- package/dist/src/retrieval/context-pack.js +1483 -0
- package/dist/src/retrieval/context-pack.js.map +1 -0
- package/dist/src/retrieval/conversation-dedup.d.ts +54 -0
- package/dist/src/retrieval/conversation-dedup.d.ts.map +1 -0
- package/dist/src/retrieval/conversation-dedup.js +162 -0
- package/dist/src/retrieval/conversation-dedup.js.map +1 -0
- package/dist/src/retrieval/current-truth.d.ts +360 -0
- package/dist/src/retrieval/current-truth.d.ts.map +1 -0
- package/dist/src/retrieval/current-truth.js +766 -0
- package/dist/src/retrieval/current-truth.js.map +1 -0
- package/dist/src/retrieval/cwd.d.ts +25 -0
- package/dist/src/retrieval/cwd.d.ts.map +1 -0
- package/dist/src/retrieval/cwd.js +48 -0
- package/dist/src/retrieval/cwd.js.map +1 -0
- package/dist/src/retrieval/degraded.d.ts +20 -0
- package/dist/src/retrieval/degraded.d.ts.map +1 -0
- package/dist/src/retrieval/degraded.js +43 -0
- package/dist/src/retrieval/degraded.js.map +1 -0
- package/dist/src/retrieval/evidence-key.d.ts +48 -0
- package/dist/src/retrieval/evidence-key.d.ts.map +1 -0
- package/dist/src/retrieval/evidence-key.js +131 -0
- package/dist/src/retrieval/evidence-key.js.map +1 -0
- package/dist/src/retrieval/feedback-ranking.d.ts +49 -0
- package/dist/src/retrieval/feedback-ranking.d.ts.map +1 -0
- package/dist/src/retrieval/feedback-ranking.js +138 -0
- package/dist/src/retrieval/feedback-ranking.js.map +1 -0
- package/dist/src/retrieval/git-state.d.ts +62 -0
- package/dist/src/retrieval/git-state.d.ts.map +1 -0
- package/dist/src/retrieval/git-state.js +192 -0
- package/dist/src/retrieval/git-state.js.map +1 -0
- package/dist/src/retrieval/group-by-parent.d.ts +38 -0
- package/dist/src/retrieval/group-by-parent.d.ts.map +1 -0
- package/dist/src/retrieval/group-by-parent.js +40 -0
- package/dist/src/retrieval/group-by-parent.js.map +1 -0
- package/dist/src/retrieval/hybrid.d.ts +75 -0
- package/dist/src/retrieval/hybrid.d.ts.map +1 -0
- package/dist/src/retrieval/hybrid.js +151 -0
- package/dist/src/retrieval/hybrid.js.map +1 -0
- package/dist/src/retrieval/lexical.d.ts +33 -0
- package/dist/src/retrieval/lexical.d.ts.map +1 -0
- package/dist/src/retrieval/lexical.js +379 -0
- package/dist/src/retrieval/lexical.js.map +1 -0
- package/dist/src/retrieval/locator-boost.d.ts +37 -0
- package/dist/src/retrieval/locator-boost.d.ts.map +1 -0
- package/dist/src/retrieval/locator-boost.js +129 -0
- package/dist/src/retrieval/locator-boost.js.map +1 -0
- package/dist/src/retrieval/passages.d.ts +24 -0
- package/dist/src/retrieval/passages.d.ts.map +1 -0
- package/dist/src/retrieval/passages.js +160 -0
- package/dist/src/retrieval/passages.js.map +1 -0
- package/dist/src/retrieval/post-filter.d.ts +32 -0
- package/dist/src/retrieval/post-filter.d.ts.map +1 -0
- package/dist/src/retrieval/post-filter.js +57 -0
- package/dist/src/retrieval/post-filter.js.map +1 -0
- package/dist/src/retrieval/project-index.d.ts +71 -0
- package/dist/src/retrieval/project-index.d.ts.map +1 -0
- package/dist/src/retrieval/project-index.js +348 -0
- package/dist/src/retrieval/project-index.js.map +1 -0
- package/dist/src/retrieval/recall-hint.d.ts +22 -0
- package/dist/src/retrieval/recall-hint.d.ts.map +1 -0
- package/dist/src/retrieval/recall-hint.js +33 -0
- package/dist/src/retrieval/recall-hint.js.map +1 -0
- package/dist/src/retrieval/receipt.d.ts +57 -0
- package/dist/src/retrieval/receipt.d.ts.map +1 -0
- package/dist/src/retrieval/receipt.js +122 -0
- package/dist/src/retrieval/receipt.js.map +1 -0
- package/dist/src/retrieval/report-demotion.d.ts +46 -0
- package/dist/src/retrieval/report-demotion.d.ts.map +1 -0
- package/dist/src/retrieval/report-demotion.js +169 -0
- package/dist/src/retrieval/report-demotion.js.map +1 -0
- package/dist/src/retrieval/reranker.d.ts +119 -0
- package/dist/src/retrieval/reranker.d.ts.map +1 -0
- package/dist/src/retrieval/reranker.js +220 -0
- package/dist/src/retrieval/reranker.js.map +1 -0
- package/dist/src/retrieval/stitch-chunks.d.ts +76 -0
- package/dist/src/retrieval/stitch-chunks.d.ts.map +1 -0
- package/dist/src/retrieval/stitch-chunks.js +113 -0
- package/dist/src/retrieval/stitch-chunks.js.map +1 -0
- package/dist/src/retrieval/vector.d.ts +71 -0
- package/dist/src/retrieval/vector.d.ts.map +1 -0
- package/dist/src/retrieval/vector.js +147 -0
- package/dist/src/retrieval/vector.js.map +1 -0
- package/dist/src/runtime/legacy-migration.d.ts +27 -0
- package/dist/src/runtime/legacy-migration.d.ts.map +1 -0
- package/dist/src/runtime/legacy-migration.js +140 -0
- package/dist/src/runtime/legacy-migration.js.map +1 -0
- package/dist/src/runtime/legacy-name-guard.d.ts +35 -0
- package/dist/src/runtime/legacy-name-guard.d.ts.map +1 -0
- package/dist/src/runtime/legacy-name-guard.js +58 -0
- package/dist/src/runtime/legacy-name-guard.js.map +1 -0
- package/dist/src/runtime/rift-env.d.ts +14 -0
- package/dist/src/runtime/rift-env.d.ts.map +1 -0
- package/dist/src/runtime/rift-env.js +79 -0
- package/dist/src/runtime/rift-env.js.map +1 -0
- package/dist/src/runtime/voyage-key-source.d.ts +46 -0
- package/dist/src/runtime/voyage-key-source.d.ts.map +1 -0
- package/dist/src/runtime/voyage-key-source.js +97 -0
- package/dist/src/runtime/voyage-key-source.js.map +1 -0
- package/dist/src/runtime/watcher-startup.d.ts +2 -0
- package/dist/src/runtime/watcher-startup.d.ts.map +1 -0
- package/dist/src/runtime/watcher-startup.js +4 -0
- package/dist/src/runtime/watcher-startup.js.map +1 -0
- package/dist/src/security/archive.d.ts +23 -0
- package/dist/src/security/archive.d.ts.map +1 -0
- package/dist/src/security/archive.js +163 -0
- package/dist/src/security/archive.js.map +1 -0
- package/dist/src/security/paths.d.ts +21 -0
- package/dist/src/security/paths.d.ts.map +1 -0
- package/dist/src/security/paths.js +67 -0
- package/dist/src/security/paths.js.map +1 -0
- package/dist/src/server/app.d.ts +29 -0
- package/dist/src/server/app.d.ts.map +1 -0
- package/dist/src/server/app.js +356 -0
- package/dist/src/server/app.js.map +1 -0
- package/dist/src/server/build-info.d.ts +8 -0
- package/dist/src/server/build-info.d.ts.map +1 -0
- package/dist/src/server/build-info.js +61 -0
- package/dist/src/server/build-info.js.map +1 -0
- package/dist/src/server/lifecycle.d.ts +30 -0
- package/dist/src/server/lifecycle.d.ts.map +1 -0
- package/dist/src/server/lifecycle.js +59 -0
- package/dist/src/server/lifecycle.js.map +1 -0
- package/dist/src/server/middleware/multipart.d.ts +51 -0
- package/dist/src/server/middleware/multipart.d.ts.map +1 -0
- package/dist/src/server/middleware/multipart.js +86 -0
- package/dist/src/server/middleware/multipart.js.map +1 -0
- package/dist/src/server/routes/compact.d.ts +37 -0
- package/dist/src/server/routes/compact.d.ts.map +1 -0
- package/dist/src/server/routes/compact.js +80 -0
- package/dist/src/server/routes/compact.js.map +1 -0
- package/dist/src/server/routes/context.d.ts +12 -0
- package/dist/src/server/routes/context.d.ts.map +1 -0
- package/dist/src/server/routes/context.js +73 -0
- package/dist/src/server/routes/context.js.map +1 -0
- package/dist/src/server/routes/conversations-search.d.ts +4 -0
- package/dist/src/server/routes/conversations-search.d.ts.map +1 -0
- package/dist/src/server/routes/conversations-search.js +287 -0
- package/dist/src/server/routes/conversations-search.js.map +1 -0
- package/dist/src/server/routes/enqueue.d.ts +11 -0
- package/dist/src/server/routes/enqueue.d.ts.map +1 -0
- package/dist/src/server/routes/enqueue.js +17 -0
- package/dist/src/server/routes/enqueue.js.map +1 -0
- package/dist/src/server/routes/friend-status.d.ts +481 -0
- package/dist/src/server/routes/friend-status.d.ts.map +1 -0
- package/dist/src/server/routes/friend-status.js +675 -0
- package/dist/src/server/routes/friend-status.js.map +1 -0
- package/dist/src/server/routes/ingest.d.ts +15 -0
- package/dist/src/server/routes/ingest.d.ts.map +1 -0
- package/dist/src/server/routes/ingest.js +142 -0
- package/dist/src/server/routes/ingest.js.map +1 -0
- package/dist/src/server/routes/jobs.d.ts +10 -0
- package/dist/src/server/routes/jobs.d.ts.map +1 -0
- package/dist/src/server/routes/jobs.js +29 -0
- package/dist/src/server/routes/jobs.js.map +1 -0
- package/dist/src/server/routes/mcp-usage.d.ts +17 -0
- package/dist/src/server/routes/mcp-usage.d.ts.map +1 -0
- package/dist/src/server/routes/mcp-usage.js +17 -0
- package/dist/src/server/routes/mcp-usage.js.map +1 -0
- package/dist/src/server/routes/reconcile.d.ts +4 -0
- package/dist/src/server/routes/reconcile.d.ts.map +1 -0
- package/dist/src/server/routes/reconcile.js +62 -0
- package/dist/src/server/routes/reconcile.js.map +1 -0
- package/dist/src/server/routes/reindex.d.ts +4 -0
- package/dist/src/server/routes/reindex.d.ts.map +1 -0
- package/dist/src/server/routes/reindex.js +77 -0
- package/dist/src/server/routes/reindex.js.map +1 -0
- package/dist/src/server/routes/retrieval-feedback.d.ts +3 -0
- package/dist/src/server/routes/retrieval-feedback.d.ts.map +1 -0
- package/dist/src/server/routes/retrieval-feedback.js +290 -0
- package/dist/src/server/routes/retrieval-feedback.js.map +1 -0
- package/dist/src/server/routes/save.d.ts +52 -0
- package/dist/src/server/routes/save.d.ts.map +1 -0
- package/dist/src/server/routes/save.js +127 -0
- package/dist/src/server/routes/save.js.map +1 -0
- package/dist/src/server/routes/search.d.ts +5 -0
- package/dist/src/server/routes/search.d.ts.map +1 -0
- package/dist/src/server/routes/search.js +689 -0
- package/dist/src/server/routes/search.js.map +1 -0
- package/dist/src/server/routes/setup.d.ts +46 -0
- package/dist/src/server/routes/setup.d.ts.map +1 -0
- package/dist/src/server/routes/setup.js +322 -0
- package/dist/src/server/routes/setup.js.map +1 -0
- package/dist/src/server/routes/stats.d.ts +10 -0
- package/dist/src/server/routes/stats.d.ts.map +1 -0
- package/dist/src/server/routes/stats.js +15 -0
- package/dist/src/server/routes/stats.js.map +1 -0
- package/dist/src/server/routes/status.d.ts +20 -0
- package/dist/src/server/routes/status.d.ts.map +1 -0
- package/dist/src/server/routes/status.js +31 -0
- package/dist/src/server/routes/status.js.map +1 -0
- package/dist/src/server/routes/triage.d.ts +4 -0
- package/dist/src/server/routes/triage.d.ts.map +1 -0
- package/dist/src/server/routes/triage.js +97 -0
- package/dist/src/server/routes/triage.js.map +1 -0
- package/dist/src/server/save-quality.d.ts +21 -0
- package/dist/src/server/save-quality.d.ts.map +1 -0
- package/dist/src/server/save-quality.js +51 -0
- package/dist/src/server/save-quality.js.map +1 -0
- package/dist/src/server/serving-marker.d.ts +85 -0
- package/dist/src/server/serving-marker.d.ts.map +1 -0
- package/dist/src/server/serving-marker.js +226 -0
- package/dist/src/server/serving-marker.js.map +1 -0
- package/dist/src/storage/atomic.d.ts +8 -0
- package/dist/src/storage/atomic.d.ts.map +1 -0
- package/dist/src/storage/atomic.js +22 -0
- package/dist/src/storage/atomic.js.map +1 -0
- package/dist/src/storage/chunk-backfill.d.ts +39 -0
- package/dist/src/storage/chunk-backfill.d.ts.map +1 -0
- package/dist/src/storage/chunk-backfill.js +295 -0
- package/dist/src/storage/chunk-backfill.js.map +1 -0
- package/dist/src/storage/db.d.ts +15 -0
- package/dist/src/storage/db.d.ts.map +1 -0
- package/dist/src/storage/db.js +47 -0
- package/dist/src/storage/db.js.map +1 -0
- package/dist/src/storage/filter.d.ts +42 -0
- package/dist/src/storage/filter.d.ts.map +1 -0
- package/dist/src/storage/filter.js +70 -0
- package/dist/src/storage/filter.js.map +1 -0
- package/dist/src/storage/fts.d.ts +47 -0
- package/dist/src/storage/fts.d.ts.map +1 -0
- package/dist/src/storage/fts.js +86 -0
- package/dist/src/storage/fts.js.map +1 -0
- package/dist/src/storage/integrity.d.ts +11 -0
- package/dist/src/storage/integrity.d.ts.map +1 -0
- package/dist/src/storage/integrity.js +66 -0
- package/dist/src/storage/integrity.js.map +1 -0
- package/dist/src/storage/maintenance.d.ts +27 -0
- package/dist/src/storage/maintenance.d.ts.map +1 -0
- package/dist/src/storage/maintenance.js +66 -0
- package/dist/src/storage/maintenance.js.map +1 -0
- package/dist/src/storage/rebuild.d.ts +71 -0
- package/dist/src/storage/rebuild.d.ts.map +1 -0
- package/dist/src/storage/rebuild.js +605 -0
- package/dist/src/storage/rebuild.js.map +1 -0
- package/dist/src/storage/report-card-backfill.d.ts +20 -0
- package/dist/src/storage/report-card-backfill.d.ts.map +1 -0
- package/dist/src/storage/report-card-backfill.js +114 -0
- package/dist/src/storage/report-card-backfill.js.map +1 -0
- package/dist/src/storage/shadow-swap.d.ts +20 -0
- package/dist/src/storage/shadow-swap.d.ts.map +1 -0
- package/dist/src/storage/shadow-swap.js +163 -0
- package/dist/src/storage/shadow-swap.js.map +1 -0
- package/dist/src/storage/tables.d.ts +147 -0
- package/dist/src/storage/tables.d.ts.map +1 -0
- package/dist/src/storage/tables.js +290 -0
- package/dist/src/storage/tables.js.map +1 -0
- package/operator/swiftbar/render-menu.py +475 -0
- package/operator/swiftbar/rift.10s.sh +201 -0
- package/package.json +57 -3
|
@@ -0,0 +1,1873 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `onadit onboard` — first-run wizard.
|
|
3
|
+
*
|
|
4
|
+
* Walks a friend from "fresh install + Voyage key in hand" to "first
|
|
5
|
+
* useful recall against their freshly-imported data". Step ordering
|
|
6
|
+
* matches Slice 4 in the friend-beta plan; each step is idempotent and
|
|
7
|
+
* `--yes` accepts every prompt's default.
|
|
8
|
+
*
|
|
9
|
+
* Flags worth pointing out:
|
|
10
|
+
* --voyage-key <key> non-interactive key entry
|
|
11
|
+
* --voyage-label <label> operator-supplied display label (no inference)
|
|
12
|
+
* --enable-feedback-relay <url>
|
|
13
|
+
* opt into the Clem-side relay non-interactively
|
|
14
|
+
* --import-export <path> import an export inline; `--no-import-export`
|
|
15
|
+
* skips the prompt entirely
|
|
16
|
+
* --reconfigure-voyage Slice 6 recovery flow — skip everything
|
|
17
|
+
* except validate + write key + kickstart + smoke
|
|
18
|
+
* --yes accept all defaults (used by install scripts)
|
|
19
|
+
* --skip-capture suppress the post-onboarding capture pass
|
|
20
|
+
* (useful in tests / CI dry-runs)
|
|
21
|
+
*/
|
|
22
|
+
import crypto from "node:crypto";
|
|
23
|
+
import fs from "node:fs";
|
|
24
|
+
import os from "node:os";
|
|
25
|
+
import path from "node:path";
|
|
26
|
+
import readline from "node:readline";
|
|
27
|
+
import { Command, Option } from "commander";
|
|
28
|
+
import { CliError, createHttpClient, readToken, resolveBaseUrl, } from "../http-client.js";
|
|
29
|
+
import { issueToken } from "../token.js";
|
|
30
|
+
import { loadConfig } from "../../config/loader.js";
|
|
31
|
+
import { defaultRiftEnvPath, loadRiftEnv, } from "../../runtime/rift-env.js";
|
|
32
|
+
import { resolveVoyageKeySource, readSavedVoyageKey, voyageKeySourceLines, } from "../../runtime/voyage-key-source.js";
|
|
33
|
+
import { runLegacyMigration } from "../../runtime/legacy-migration.js";
|
|
34
|
+
import { sanitizeProjectLabel } from "../../runtime/legacy-name-guard.js";
|
|
35
|
+
import { validateVoyageKey } from "../../onboarding/voyage-validate.js";
|
|
36
|
+
import { useVoyageOwnEndpoint } from "../../onboarding/search-by-meaning.js";
|
|
37
|
+
import { writeCaptureEnabled } from "../../onboarding/capture-switch.js";
|
|
38
|
+
import { writeEnvFile } from "../../onboarding/env-file.js";
|
|
39
|
+
import { kickstartDaemon, waitForHealth, } from "../../onboarding/daemon-control.js";
|
|
40
|
+
import { runAutoCapture, resolveCodexCaptureDeps, resolveCursorCaptureDeps, } from "../../capture/auto-capture.js";
|
|
41
|
+
import { appendAutoCaptureRunRecord, buildAutoCaptureRunRecord, } from "../../capture/observability.js";
|
|
42
|
+
import { DEFAULT_AUTO_CAPTURE_SOURCES, CLAUDE_DESKTOP_CAPTURE_SOURCE, CURSOR_CAPTURE_SOURCE, } from "../../capture/sources.js";
|
|
43
|
+
import { createTriageProvider, resolveTriageProviderName, setConfiguredTriageProviderName, triageWorkerLabel, } from "../../capture/triage-provider-factory.js";
|
|
44
|
+
import { discoverClaudeCodeSessions } from "../../ingestion/parsers/claude-code-jsonl.js";
|
|
45
|
+
import { discoverCodexSessions } from "../../ingestion/parsers/codex-jsonl.js";
|
|
46
|
+
import { sniffInboxSource } from "../../ingestion/inbox-core/source-sniffer.js";
|
|
47
|
+
import { writeHookConfig } from "../hooks-writers/index.js";
|
|
48
|
+
import { HooksParseFailedError } from "../hooks-writers/index.js";
|
|
49
|
+
import { pollJob } from "../job-poller.js";
|
|
50
|
+
import { isJobFailure } from "../output.js";
|
|
51
|
+
import * as ui from "../ui.js";
|
|
52
|
+
import { getBuildInfo } from "../../server/build-info.js";
|
|
53
|
+
import { postFeedback } from "../feedback/feedback-relay.js";
|
|
54
|
+
import { parseInvite } from "../feedback/invite.js";
|
|
55
|
+
import { macRelaySecretStore, } from "../feedback/relay-secret-store.js";
|
|
56
|
+
import { LockedKeychainError } from "../../auth/keychain.js";
|
|
57
|
+
/**
|
|
58
|
+
* Resolve a `--no-<flag>` to a single boolean ("yes, skip this thing").
|
|
59
|
+
* Commander populates the affirmative key with `false`; programmatic
|
|
60
|
+
* callers may pass the `no*` key with `true`. Either form wins.
|
|
61
|
+
*/
|
|
62
|
+
export function isOff(opts, affirmative, negative) {
|
|
63
|
+
return opts[affirmative] === false || opts[negative] === true;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* True when this `onadit onboard` invocation asks to CHANGE something — set a key,
|
|
67
|
+
* enable a lane, import, opt into feedback, install a hook. Absence marks a
|
|
68
|
+
* "passive" re-run (the user just ran the command again), which on an
|
|
69
|
+
* already-onboarded install should report state instead of redoing first-time
|
|
70
|
+
* setup or gating on the stored Voyage key. `--yes`, `--skip-capture`, and the
|
|
71
|
+
* `--no-*` declines are NOT mutations; `--reconfigure-voyage` has its own flow
|
|
72
|
+
* and is handled before this is consulted.
|
|
73
|
+
*
|
|
74
|
+
* Exported for testing — it gates the idempotent short-circuit in `runOnboard`.
|
|
75
|
+
*/
|
|
76
|
+
export function hasOnboardMutationIntent(opts) {
|
|
77
|
+
return ((typeof opts.voyageKey === "string" && opts.voyageKey.trim().length > 0) ||
|
|
78
|
+
opts.voyageLabel !== undefined ||
|
|
79
|
+
opts.enableCapture === true ||
|
|
80
|
+
opts.enableCoworkCapture === true || opts.enableGrokCapture === true ||
|
|
81
|
+
opts.enableCodexEnrichment === true ||
|
|
82
|
+
opts.enableClaudeEnrichment === true ||
|
|
83
|
+
(typeof opts.importExport === "string" && opts.importExport.length > 0) ||
|
|
84
|
+
opts.invite !== undefined ||
|
|
85
|
+
opts.enableFeedbackRelay !== undefined ||
|
|
86
|
+
opts.email !== undefined ||
|
|
87
|
+
opts.withClaudeHook === true);
|
|
88
|
+
}
|
|
89
|
+
const SUPPORTED_INGEST_EXTENSIONS = new Set([".json", ".zip"]);
|
|
90
|
+
const DEFAULT_DATA_DIR = path.join(os.homedir(), "Library", "Application Support", "Rift", // rename:keep data folder name
|
|
91
|
+
"data");
|
|
92
|
+
// Inline import is intentionally narrow until Slice 3 lands the real
|
|
93
|
+
// `onadit import` flow with structural sniffing per source. ChatGPT exports
|
|
94
|
+
// are the most common friend handoff and we route them straight through.
|
|
95
|
+
const ONBOARD_INLINE_IMPORT_SOURCE = "chatgpt_web";
|
|
96
|
+
/** Only explicit feature opt-ins open the longer configuration flow. */
|
|
97
|
+
export function usesAdvancedOnboarding(opts) {
|
|
98
|
+
return opts.advanced === true || opts.reconfigureVoyage === true ||
|
|
99
|
+
(typeof opts.voyageKey === "string" && !!opts.voyageKey.trim()) ||
|
|
100
|
+
opts.voyageLabel !== undefined || opts.enableCapture === true ||
|
|
101
|
+
opts.enableCoworkCapture === true || opts.enableGrokCapture === true || opts.enableCodexEnrichment === true ||
|
|
102
|
+
opts.enableClaudeEnrichment === true || opts.invite !== undefined ||
|
|
103
|
+
opts.enableFeedbackRelay !== undefined || opts.email !== undefined ||
|
|
104
|
+
opts.withClaudeHook === true;
|
|
105
|
+
}
|
|
106
|
+
export function makeOnboardCommand() {
|
|
107
|
+
return new Command("onboard")
|
|
108
|
+
.description("Add an archive, find a memory, connect your AI tool")
|
|
109
|
+
.option("--query <words>", "Find a real topic after import")
|
|
110
|
+
.addOption(new Option("--source <name>", "Archive source (detected when possible)").choices(["chatgpt_web", "claude_web", "gemini_web", "grok_web"]))
|
|
111
|
+
.addOption(new Option("--client <name>", "Connect this AI tool after recall").choices(["claude-desktop", "claude-code", "codex", "cursor"]))
|
|
112
|
+
.option("--advanced", "Configure optional cloud search, capture, and feedback", false)
|
|
113
|
+
.option("--voyage-key <key>", "Voyage API key (skips paste prompt)")
|
|
114
|
+
.option("--no-voyage-key", "Finish without a Voyage key — keyword-only (lexical) search; add a key later")
|
|
115
|
+
.option("--voyage-label <label>", "Operator-supplied display label for the Voyage project")
|
|
116
|
+
.option("--invite <code>", "Accept a feedback invite from Clem (or omit it to paste when asked)")
|
|
117
|
+
.option("--enable-feedback-relay <url>", "Opt into the Onadit feedback relay non-interactively (URL required)")
|
|
118
|
+
.addOption(
|
|
119
|
+
// Hidden: back-compat operator flag. The secret never appears in
|
|
120
|
+
// user-facing help; friends use --invite (or the interactive paste).
|
|
121
|
+
new Option("--relay-secret <secret>", "Back-compat HMAC secret paired with --enable-feedback-relay (stored in the Keychain)").hideHelp())
|
|
122
|
+
.addOption(
|
|
123
|
+
// Hidden: dev/operator escape hatch for unsigned local receivers.
|
|
124
|
+
new Option("--allow-unsigned-feedback-relay", "Dev-only: allow --enable-feedback-relay without a secret (receiver rejects in prod)")
|
|
125
|
+
.hideHelp()
|
|
126
|
+
.default(false))
|
|
127
|
+
.option("--no-feedback-relay", "Decline the feedback relay (local-only)")
|
|
128
|
+
.option("--email <address>", "Opt into product updates + feedback non-interactively (skips the prompt)")
|
|
129
|
+
.option("--import-export <path>", "Import an export inline (.json or .zip)")
|
|
130
|
+
.option("--no-import-export", "Skip the import-now prompt")
|
|
131
|
+
.option("--reconfigure-voyage", "Recovery flow: replace the Voyage key only", false)
|
|
132
|
+
.option("--yes", "Accept all defaults (non-interactive)", false)
|
|
133
|
+
.option("--skip-capture", "Skip the post-setup capture pass (test-only)", false)
|
|
134
|
+
.option("--no-codex-capture", "Skip the triage-worker preflight + disable the capture pass for this run")
|
|
135
|
+
.option("--with-claude-hook", "Install the Onadit policy hook into Claude Code without prompting", false)
|
|
136
|
+
.option("--no-claude-hook", "Skip the Claude Code policy-hook prompt entirely")
|
|
137
|
+
.option("--enable-codex-enrichment", "Opt into Codex AI metadata enrichment (default: AI-free import + keyword search)", false)
|
|
138
|
+
.option("--enable-claude-enrichment", "Opt into Claude Code AI metadata enrichment (digests/compaction stay off until Claude digest support ships; mutually exclusive with --enable-codex-enrichment)", false)
|
|
139
|
+
.option("--enable-capture", "Opt into scheduled chat capture + its triage-worker preflight (Codex by default, or Claude Code via capture.triage.provider; default: capture off, zero AI-worker calls)", false)
|
|
140
|
+
.option("--enable-grok-capture", "Opt into existing and future Grok CLI sessions (adds grok_cli to capture.sources and enables scheduled capture)")
|
|
141
|
+
.option("--enable-cowork-capture", "Opt into Claude Desktop agent/Cowork capture (adds claude_desktop_sessions to capture.sources and enables scheduled capture)", false)
|
|
142
|
+
.action(async (opts, cmd) => {
|
|
143
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
144
|
+
try {
|
|
145
|
+
if (usesAdvancedOnboarding(opts))
|
|
146
|
+
await runOnboard(opts, globalOpts);
|
|
147
|
+
else {
|
|
148
|
+
const { runFirstRun } = await import("./onboard-first-run.js");
|
|
149
|
+
await runFirstRun(opts, globalOpts);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
catch (err) {
|
|
153
|
+
const msg = err instanceof CliError
|
|
154
|
+
? err.message
|
|
155
|
+
: err instanceof Error
|
|
156
|
+
? err.message
|
|
157
|
+
: String(err);
|
|
158
|
+
process.stderr.write(`onboard failed: ${msg}\n`);
|
|
159
|
+
process.exitCode = 1;
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
// Exported for orchestrator-level tests (e.g. the capture-without-key gate);
|
|
164
|
+
// `makeOnboardCommand().action` is the only production caller.
|
|
165
|
+
export async function runOnboard(opts, globalOpts) {
|
|
166
|
+
const rl = readline.createInterface({
|
|
167
|
+
input: process.stdin,
|
|
168
|
+
output: process.stdout,
|
|
169
|
+
terminal: !!process.stdin.isTTY,
|
|
170
|
+
});
|
|
171
|
+
try {
|
|
172
|
+
// Fail fast, before any config write: the two enrichment opt-ins pick
|
|
173
|
+
// different metadata workers (Codex vs Claude), so passing both is an
|
|
174
|
+
// ambiguous request, not a mergeable one. Reject it up front rather than
|
|
175
|
+
// letting last-writer-wins silently decide which provider you got.
|
|
176
|
+
if (opts.enableCodexEnrichment === true && opts.enableClaudeEnrichment === true) {
|
|
177
|
+
throw new CliError("Pass only one of --enable-codex-enrichment or --enable-claude-enrichment — they select different metadata workers.", "validation");
|
|
178
|
+
}
|
|
179
|
+
if (opts.reconfigureVoyage) {
|
|
180
|
+
await reconfigureVoyageFlow(opts, globalOpts, rl);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
// Idempotent re-run. An already-onboarded install + a passive invocation
|
|
184
|
+
// (no flag asking to change anything) + a Voyage key already on disk must
|
|
185
|
+
// NOT redo first-time setup or GATE on that stored key. Re-validating the
|
|
186
|
+
// saved key as a first-time gate is exactly the beta.25 bug: a re-run on an
|
|
187
|
+
// expired key ended with "onboard failed". Instead, report "already set up"
|
|
188
|
+
// with a NON-FATAL key health check and exit clean. A keyword-only install
|
|
189
|
+
// (no key) falls through so the normal flow can still offer to add one.
|
|
190
|
+
if (!hasOnboardMutationIntent(opts)) {
|
|
191
|
+
const absoluteConfig = path.resolve(globalOpts.config);
|
|
192
|
+
const onboarded = readOnboardedState(absoluteConfig);
|
|
193
|
+
const keyRes = resolveOnboardVoyageKeySource(absoluteConfig);
|
|
194
|
+
if (onboarded && keyRes.key) {
|
|
195
|
+
await reportAlreadyOnboarded(keyRes);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
ui.banner();
|
|
200
|
+
// Step 1 — legacy migration (idempotent).
|
|
201
|
+
const dataDir = await ensureConfigAndDataDir(globalOpts.config, opts, rl);
|
|
202
|
+
const migration = await runLegacyMigration({ dataDir });
|
|
203
|
+
if (migration.migratedCount > 0) {
|
|
204
|
+
ui.step("ok", "Existing data", `carried over ${migration.migratedCount} item(s)`);
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
ui.step("ok", "Existing data", "nothing to carry over");
|
|
208
|
+
}
|
|
209
|
+
// Privacy contract — shown BEFORE the first outbound choice (the
|
|
210
|
+
// Voyage key, whose snippets leave the machine for embedding). A beta
|
|
211
|
+
// user should understand what stays local and what leaves before they
|
|
212
|
+
// paste anything.
|
|
213
|
+
ui.step("info", "Privacy", "");
|
|
214
|
+
sayPrivacyContract();
|
|
215
|
+
// Step 2 — Voyage key + validate + persist env. The daemon kickstart +
|
|
216
|
+
// smoke is deferred to Step 2e (after the opt-in writes below) so the
|
|
217
|
+
// respawned daemon boots with the final config.json.
|
|
218
|
+
//
|
|
219
|
+
// A key this install already has stays as it is: the user's own, or the
|
|
220
|
+
// one Onadit provided (Search by meaning). Asking for a key again made a
|
|
221
|
+
// lane opt-in (`--enable-capture`) depend on pasting a second one, and a
|
|
222
|
+
// user on an Onadit-provided key has none to paste. Changing the key is
|
|
223
|
+
// `--voyage-key` or `--reconfigure-voyage`.
|
|
224
|
+
const savedLast4 = savedVoyageKeyToKeep(opts, path.resolve(globalOpts.config));
|
|
225
|
+
if (savedLast4 !== null) {
|
|
226
|
+
ui.step("ok", "Search index", `using the key already set up · ····${savedLast4}`);
|
|
227
|
+
}
|
|
228
|
+
const last4 = savedLast4 ?? (await collectAndPersistVoyageKey(opts, rl));
|
|
229
|
+
// A pasted key is the user's own Voyage key: stop pointing at the
|
|
230
|
+
// endpoint of a key Onadit provided earlier (Search by meaning).
|
|
231
|
+
if (savedLast4 === null && last4 !== null && useVoyageOwnEndpoint(path.resolve(globalOpts.config))) {
|
|
232
|
+
say("Search index now uses your own Voyage key instead of the one Onadit provided.");
|
|
233
|
+
}
|
|
234
|
+
// Step 2b — sanitize + persist optional --voyage-label to config.json
|
|
235
|
+
// (backup first). Invalid labels are dropped without echoing the raw
|
|
236
|
+
// value, so a key-shaped, path-shaped, or legacy-name-shaped label
|
|
237
|
+
// can never leak through stdout or config.json.
|
|
238
|
+
const safeLabel = applyVoyageLabel(opts.voyageLabel, globalOpts.config, say);
|
|
239
|
+
// Step 2c — optional opt-in to Codex AI metadata enrichment. Default is
|
|
240
|
+
// AI-free import + keyword search (zero Codex calls). Only persisted when
|
|
241
|
+
// the user explicitly passed --enable-codex-enrichment.
|
|
242
|
+
applyCodexEnrichmentOptIn(opts.enableCodexEnrichment, globalOpts.config, say);
|
|
243
|
+
applyClaudeEnrichmentOptIn(opts.enableClaudeEnrichment, globalOpts.config, say);
|
|
244
|
+
// Step 2d — optional opt-in to scheduled chat capture. Default off, so a
|
|
245
|
+
// fresh install makes zero Codex calls. Auto-capture embeds each saved
|
|
246
|
+
// conversation, so it requires a Voyage key; opting in without one would
|
|
247
|
+
// persist `capture.enabled = true` that the daemon can't honor (it would
|
|
248
|
+
// wake hourly, find no embedding provider, and save nothing). So we only
|
|
249
|
+
// persist the opt-in when a key was provided, and warn otherwise.
|
|
250
|
+
// `captureEnabled` is the single source of truth for the capture lane:
|
|
251
|
+
// requested AND a key is present. It gates BOTH the persistence here and
|
|
252
|
+
// the Codex preflight in Step 3 — refusing the opt-in must also skip the
|
|
253
|
+
// preflight's Codex triage call, or `--enable-capture` without a key would
|
|
254
|
+
// still leak a Codex call.
|
|
255
|
+
const captureRequested = opts.enableCapture === true || opts.enableCoworkCapture === true || opts.enableGrokCapture === true;
|
|
256
|
+
const captureEnabled = captureRequested && last4 !== null;
|
|
257
|
+
if (captureRequested && last4 === null) {
|
|
258
|
+
const ignoredFlag = opts.enableCoworkCapture === true && opts.enableCapture !== true
|
|
259
|
+
? "--enable-cowork-capture"
|
|
260
|
+
: opts.enableGrokCapture === true && opts.enableCapture !== true
|
|
261
|
+
? "--enable-grok-capture" : "--enable-capture";
|
|
262
|
+
ui.note(`${ignoredFlag} ignored: capture embeds saved conversations and needs a Voyage key. Re-run \`onadit onboard\` with a key to enable it.`);
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
applyCaptureOptIn(captureRequested, globalOpts.config, say);
|
|
266
|
+
applyCoworkCaptureOptIn(opts.enableCoworkCapture, globalOpts.config, say);
|
|
267
|
+
applyGrokCaptureOptIn(opts.enableGrokCapture, globalOpts.config, say);
|
|
268
|
+
}
|
|
269
|
+
// Step 2e — (re)start the daemon AFTER all config writes so it boots with
|
|
270
|
+
// the final config.json. The daemon builds its capture loop and metadata
|
|
271
|
+
// extractor once at boot (src/main.ts), so persisting the opt-ins above
|
|
272
|
+
// BEFORE this kickstart is what makes --enable-capture /
|
|
273
|
+
// --enable-codex-enrichment take effect immediately instead of only after
|
|
274
|
+
// a later restart. Key path only: the smoke asserts voyage_key_present,
|
|
275
|
+
// and the keyword-only path has no daemon smoke to run (the daemon was
|
|
276
|
+
// bootstrapped by install.sh and picks up config on its next start).
|
|
277
|
+
// `not_configured` / `agent_not_loaded` stay recoverable — the daemon
|
|
278
|
+
// simply isn't running yet on a fresh-Mac install before install.sh
|
|
279
|
+
// bootstraps the plist.
|
|
280
|
+
if (last4 !== null) {
|
|
281
|
+
const refresh = await daemonRefreshFlow(globalOpts);
|
|
282
|
+
if (!refresh.ok &&
|
|
283
|
+
refresh.kind !== "not_configured" &&
|
|
284
|
+
refresh.kind !== "agent_not_loaded") {
|
|
285
|
+
throw new CliError(`Voyage smoke failed after daemon kickstart: ${refresh.reason}`, "server_error");
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
// Step 3 — triage-worker preflight (capture lane only).
|
|
289
|
+
//
|
|
290
|
+
// Capture is OFF by default (trust-first): a fresh install makes zero
|
|
291
|
+
// Codex calls. The preflight below performs a real Codex triage call,
|
|
292
|
+
// so it ONLY runs when capture is actually enabled — i.e. `--enable-capture`
|
|
293
|
+
// AND a Voyage key (see `captureEnabled` above). Without the flag, or with
|
|
294
|
+
// the flag but no key, the capture lane — and its Codex preflight — is
|
|
295
|
+
// skipped entirely, and import + keyword search below still work with no
|
|
296
|
+
// Codex dependency.
|
|
297
|
+
//
|
|
298
|
+
// When capture IS enabled, a failed preflight is a warning, not fatal —
|
|
299
|
+
// onboarding continues and the capture pass is skipped for this run.
|
|
300
|
+
// `--no-codex-capture` skips the preflight even when capture was enabled
|
|
301
|
+
// (e.g. enable the daemon schedule but skip the one-shot).
|
|
302
|
+
let captureDisabled = false;
|
|
303
|
+
// `codexUnavailable` is set ONLY when capture was enabled and the live
|
|
304
|
+
// Codex preflight actually failed — i.e. scheduled capture is configured
|
|
305
|
+
// on but cannot run until Codex is fixed. `--no-codex-capture` is NOT this
|
|
306
|
+
// case: it only skips the onboarding one-shot, leaving the daemon's
|
|
307
|
+
// scheduled capture to run normally. Keeping the two apart is what lets the
|
|
308
|
+
// import-prompt and summary copy stay honest instead of either over- or
|
|
309
|
+
// under-promising automatic capture.
|
|
310
|
+
let codexUnavailable = false;
|
|
311
|
+
// Resolve the configured triage worker so the preflight probes (and labels)
|
|
312
|
+
// the worker the daemon will actually use, and record it process-wide so
|
|
313
|
+
// the first-capture pass below inherits the same selection. Unreadable
|
|
314
|
+
// config falls back to codex_cli — byte-for-byte the prior default.
|
|
315
|
+
let triageWorkerName = "codex_cli";
|
|
316
|
+
try {
|
|
317
|
+
triageWorkerName = resolveTriageProviderName(loadConfig(path.resolve(globalOpts.config)));
|
|
318
|
+
}
|
|
319
|
+
catch {
|
|
320
|
+
triageWorkerName = "codex_cli";
|
|
321
|
+
}
|
|
322
|
+
setConfiguredTriageProviderName(triageWorkerName);
|
|
323
|
+
const workerLabel = triageWorkerLabel(triageWorkerName);
|
|
324
|
+
if (!captureEnabled) {
|
|
325
|
+
ui.step("skip", "Chat access", captureRequested
|
|
326
|
+
? "capture needs a Voyage key — skipped"
|
|
327
|
+
: "capture off (default) · enable with --enable-capture");
|
|
328
|
+
captureDisabled = true;
|
|
329
|
+
}
|
|
330
|
+
else if (isOff(opts, "codexCapture", "noCodexCapture")) {
|
|
331
|
+
ui.step("skip", "Chat access", "skipped (--no-codex-capture) · auto-import off");
|
|
332
|
+
captureDisabled = true;
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
const preflightSpin = new ui.Spinner("Chat access").start();
|
|
336
|
+
const preflightOk = await triagePreflight(triageWorkerName);
|
|
337
|
+
if (preflightOk) {
|
|
338
|
+
preflightSpin.succeed("Chat access", `ready · ${workerLabel}`);
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
preflightSpin.fail("Chat access", `not ready · ${workerLabel} — auto-import off`);
|
|
342
|
+
ui.note(triageWorkerName === "claude_code"
|
|
343
|
+
? "To enable later: run `claude` once and sign in, then re-run `onadit onboard`."
|
|
344
|
+
: "To enable later: run `codex login`, then re-run `onadit onboard`.");
|
|
345
|
+
captureDisabled = true;
|
|
346
|
+
codexUnavailable = true;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
// Will the daemon's scheduled auto-capture actually run going forward?
|
|
350
|
+
// Configured on (requested + Voyage key) AND Codex available. This is the
|
|
351
|
+
// signal the import prompt and the summary line must use — NOT bare
|
|
352
|
+
// `captureEnabled`, which is only "configured on" and stays true even after
|
|
353
|
+
// a failed Codex preflight already told the user auto-import is off.
|
|
354
|
+
const autoCaptureWillRun = captureEnabled && !codexUnavailable;
|
|
355
|
+
// Step 4 — discover sessions.
|
|
356
|
+
const claudeSessions = safeDiscover(() => discoverClaudeCodeSessions(path.join(os.homedir(), ".claude")));
|
|
357
|
+
const codexSessions = safeDiscover(() => discoverCodexSessions());
|
|
358
|
+
ui.step("ok", "Chat history", `${claudeSessions} Claude Code · ${codexSessions} Codex CLI`);
|
|
359
|
+
// Set the expectation honestly up front: auto-capture covers the CLI agents,
|
|
360
|
+
// not every AI app on the Mac. The Claude/ChatGPT desktop + web apps keep
|
|
361
|
+
// their history server-side, so those chats arrive via export → onadit import.
|
|
362
|
+
// `ui.detail` (legible, not dimmed) because this is trust-critical copy.
|
|
363
|
+
//
|
|
364
|
+
// The CLI-agent line is gated on `autoCaptureWillRun`: "captured
|
|
365
|
+
// automatically" is only true once capture is enabled AND the worker
|
|
366
|
+
// preflight passed. On a default/off or not-ready install (we just printed
|
|
367
|
+
// "capture off"/"not ready" above) we describe ELIGIBILITY instead, so the
|
|
368
|
+
// block never contradicts the gate it sits under.
|
|
369
|
+
const cliAgentsLine = autoCaptureWillRun
|
|
370
|
+
? " • Claude Code + Codex CLI — captured automatically\n"
|
|
371
|
+
: " • Claude Code + Codex CLI — eligible for auto-capture once capture is enabled and chat access is ready\n";
|
|
372
|
+
ui.detail("Auto-capture covers your terminal AI agents — not every AI app on your Mac:\n" +
|
|
373
|
+
cliAgentsLine +
|
|
374
|
+
" • Cursor — only if you turn it on (add cursor_composer to capture.sources)\n" +
|
|
375
|
+
" • Grok CLI sessions — opt in with: onadit onboard --enable-grok-capture (existing and future sessions)\n" +
|
|
376
|
+
" • Claude Desktop Cowork / agent sessions — opt in with: onadit onboard --enable-cowork-capture\n" +
|
|
377
|
+
" • Claude Desktop normal chats / ChatGPT / web chats — NOT automatic; export them, then:\n" +
|
|
378
|
+
" onadit import <file> --source claude_web (or chatgpt_web)");
|
|
379
|
+
// Step 5 — beta opt-in (stay connected: news, pricing, feedback).
|
|
380
|
+
const feedback = await collectFeedbackPreference(opts, dataDir, rl);
|
|
381
|
+
if (feedback.email) {
|
|
382
|
+
ui.step("ok", "Stay connected", `${feedback.email} — opted in (news + feedback)`);
|
|
383
|
+
}
|
|
384
|
+
else if (feedback.enabled) {
|
|
385
|
+
ui.step("ok", "Stay connected", `feedback relay on (installation_id: ${feedback.installation_id})`);
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
ui.step("ok", "Stay connected", "skipped — nothing shared, feedback stays local");
|
|
389
|
+
}
|
|
390
|
+
// Step 5b — optional Claude Code policy hook.
|
|
391
|
+
await maybeInstallClaudeCodeHook(opts, rl);
|
|
392
|
+
// Step 6 + 7 — watermark current sessions + run one capture pass.
|
|
393
|
+
let captureSaved = 0;
|
|
394
|
+
let captureRan = false;
|
|
395
|
+
if (opts.skipCapture) {
|
|
396
|
+
ui.step("skip", "Chat import", "skipped (--skip-capture)");
|
|
397
|
+
}
|
|
398
|
+
else if (last4 === null) {
|
|
399
|
+
// Auto-capture saves embed each conversation, so it needs a real
|
|
400
|
+
// embedding provider. In keyword-only mode it is skipped; import +
|
|
401
|
+
// search below still work.
|
|
402
|
+
ui.step("skip", "Chat import", "skipped (no embedding key — capture needs one)");
|
|
403
|
+
}
|
|
404
|
+
else if (captureDisabled) {
|
|
405
|
+
// Capture lane is off — either the default (no --enable-capture) or a
|
|
406
|
+
// failed/declined Codex preflight. The "Chat access" step above already
|
|
407
|
+
// printed the specific reason.
|
|
408
|
+
ui.step("skip", "Chat import", "skipped (capture not enabled)");
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
captureRan = true;
|
|
412
|
+
const captureResult = await runFirstCapturePass(globalOpts.config, dataDir);
|
|
413
|
+
captureSaved = captureResult.saved;
|
|
414
|
+
// Per A-1.1/C-1.3: a token-issuance failure during capture-pass
|
|
415
|
+
// is fatal. The wizard cannot meaningfully continue without auth
|
|
416
|
+
// (import + recall both need a token), and silently advancing
|
|
417
|
+
// past it was the original bug.
|
|
418
|
+
if (captureResult.fatal) {
|
|
419
|
+
rl.close();
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
// Step 8 — optional export import.
|
|
424
|
+
let importSucceeded = false;
|
|
425
|
+
let importAttempted = false;
|
|
426
|
+
if (isOff(opts, "importExport", "noImportExport")) {
|
|
427
|
+
ui.step("skip", "File import", "skipped (--no-import-export)");
|
|
428
|
+
}
|
|
429
|
+
else {
|
|
430
|
+
const importPath = await collectImportPath(opts, rl, {
|
|
431
|
+
willRun: autoCaptureWillRun,
|
|
432
|
+
configured: captureEnabled,
|
|
433
|
+
workerLabel,
|
|
434
|
+
});
|
|
435
|
+
if (importPath) {
|
|
436
|
+
importAttempted = true;
|
|
437
|
+
const outcome = await runImport(importPath, globalOpts.config);
|
|
438
|
+
if (outcome.kind === "imported" || outcome.kind === "duplicate") {
|
|
439
|
+
importSucceeded = true;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
else {
|
|
443
|
+
ui.step("skip", "File import", "none — run `onadit import <path> --source <name>` later");
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
// Step 9 — first-recall verification.
|
|
447
|
+
// A recall check only makes sense once this run actually landed user data.
|
|
448
|
+
// When nothing was imported or captured (the default, deliberate path), we
|
|
449
|
+
// skip the search check rather than start a spinner and FAIL it — failing a
|
|
450
|
+
// check the user opted out of is what made a clean setup feel broken.
|
|
451
|
+
const ingestedAny = captureSaved > 0 || importSucceeded;
|
|
452
|
+
const ingestAttempted = captureRan || importAttempted;
|
|
453
|
+
let recall;
|
|
454
|
+
if (!ingestedAny) {
|
|
455
|
+
ui.step("skip", "Search check", "nothing imported yet — skipped");
|
|
456
|
+
recall = { ok: false, reason: "nothing imported yet" };
|
|
457
|
+
}
|
|
458
|
+
else {
|
|
459
|
+
const recallSpin = new ui.Spinner("Search check").start();
|
|
460
|
+
recall = await firstRecallCheck(globalOpts.config);
|
|
461
|
+
if (recall.ok && recall.hits > 0) {
|
|
462
|
+
recallSpin.succeed("Search check", `found ${recall.hits} result(s)`);
|
|
463
|
+
}
|
|
464
|
+
else if (recall.ok) {
|
|
465
|
+
recallSpin.warn("Search check", "no results yet — index is fresh, try a query shortly");
|
|
466
|
+
}
|
|
467
|
+
else {
|
|
468
|
+
recallSpin.fail("Search check", recall.reason);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
// Step 10 — next-action card.
|
|
472
|
+
const card = decideOnboardOutcome({ ingestedAny, recall, ingestAttempted });
|
|
473
|
+
card.push(ui.pc.dim(last4 !== null
|
|
474
|
+
? `Voyage: key ${savedLast4 !== null ? "already set up" : "valid"} (last 4 …${last4})${safeLabel ? ` · label ${safeLabel}` : ""}`
|
|
475
|
+
: "Search: keyword-only (no embedding key) — add one later for semantic search"));
|
|
476
|
+
// One-line "what's on/off" so the friend leaves knowing whether Onadit keeps
|
|
477
|
+
// itself fed. Three honest states: ready (configured + worker available),
|
|
478
|
+
// configured-but-not-ready (preflight failed — don't claim it's running),
|
|
479
|
+
// and off (the default). Capture is off by default (zero-egress default).
|
|
480
|
+
card.push(ui.pc.dim(autoCaptureWillRun
|
|
481
|
+
? "Auto-capture: on — new chats are captured on a schedule."
|
|
482
|
+
: captureEnabled
|
|
483
|
+
? `Auto-capture: configured on, but not ready — ${workerLabel} isn't available (${triageWorkerName === "claude_code"
|
|
484
|
+
? "run `claude` and sign in"
|
|
485
|
+
: "run `codex login`"}).`
|
|
486
|
+
: "Auto-capture: off — enable with: onadit onboard --enable-capture"));
|
|
487
|
+
ui.box(card);
|
|
488
|
+
ui.line("");
|
|
489
|
+
}
|
|
490
|
+
finally {
|
|
491
|
+
rl.close();
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
// ----- Idempotent re-run: "already set up" report -----
|
|
495
|
+
/**
|
|
496
|
+
* An install is "already onboarded" once its config.json exists and the data
|
|
497
|
+
* dir it points at is present. Returns the data dir, or null when this looks
|
|
498
|
+
* like a fresh install (no config, unreadable config, or a missing data dir) —
|
|
499
|
+
* in which case the caller runs the normal first-time flow.
|
|
500
|
+
*/
|
|
501
|
+
function readOnboardedState(absoluteConfig) {
|
|
502
|
+
if (!fs.existsSync(absoluteConfig))
|
|
503
|
+
return null;
|
|
504
|
+
try {
|
|
505
|
+
const cfg = loadConfig(absoluteConfig);
|
|
506
|
+
const dataDir = cfg.data_paths.data_dir;
|
|
507
|
+
return fs.existsSync(dataDir) ? { dataDir } : null;
|
|
508
|
+
}
|
|
509
|
+
catch {
|
|
510
|
+
return null;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Resolve the key an installed Onadit CLI would use for an already-onboarded
|
|
515
|
+
* report — AND where it came from. The shell `$VOYAGE_API_KEY` wins (it
|
|
516
|
+
* overrides in-process checks); the saved `~/.rift.env` is read only when the
|
|
517
|
+
* config path is the canonical installed Onadit config under that same home, so
|
|
518
|
+
* custom/test `--config` runs never read the developer machine's real env file.
|
|
519
|
+
* When both exist and differ, the resolution flags the shadow so we can warn.
|
|
520
|
+
*/
|
|
521
|
+
function resolveOnboardVoyageKeySource(absoluteConfig) {
|
|
522
|
+
const shellKey = (process.env["VOYAGE_API_KEY"] ?? "").trim() || null;
|
|
523
|
+
const homeDir = homeDirForInstalledRiftConfig(absoluteConfig);
|
|
524
|
+
const fileKey = homeDir ? readSavedVoyageKey(defaultRiftEnvPath(homeDir)) : null;
|
|
525
|
+
return resolveVoyageKeySource(shellKey, fileKey);
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Last-4 of the Voyage key already saved for this install, when this run
|
|
529
|
+
* should keep using it: no key was passed to replace it, and the shell does
|
|
530
|
+
* not export a different one. Null otherwise, and the key step runs as usual.
|
|
531
|
+
* The key is not re-validated here: the engine's own health check after the
|
|
532
|
+
* restart says whether it loaded one.
|
|
533
|
+
*/
|
|
534
|
+
function savedVoyageKeyToKeep(opts, absoluteConfig) {
|
|
535
|
+
if (typeof opts.voyageKey === "string" && opts.voyageKey.trim())
|
|
536
|
+
return null;
|
|
537
|
+
const homeDir = homeDirForInstalledRiftConfig(absoluteConfig);
|
|
538
|
+
const saved = homeDir ? readSavedVoyageKey(defaultRiftEnvPath(homeDir)) : null;
|
|
539
|
+
if (!saved)
|
|
540
|
+
return null;
|
|
541
|
+
const shell = (process.env["VOYAGE_API_KEY"] ?? "").trim();
|
|
542
|
+
if (shell && shell !== saved)
|
|
543
|
+
return null;
|
|
544
|
+
return resolveVoyageKeySource(null, saved).last4;
|
|
545
|
+
}
|
|
546
|
+
function homeDirForInstalledRiftConfig(absoluteConfig) {
|
|
547
|
+
const suffix = path.join("Library", "Application Support", "Rift", // rename:keep data folder name
|
|
548
|
+
"data", "config.json");
|
|
549
|
+
const marker = `${path.sep}${suffix}`;
|
|
550
|
+
if (!absoluteConfig.endsWith(marker))
|
|
551
|
+
return null;
|
|
552
|
+
const homeDir = absoluteConfig.slice(0, -marker.length);
|
|
553
|
+
return homeDir.length > 0 ? homeDir : null;
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Passive-re-run summary for an already-onboarded install. Validates the stored
|
|
557
|
+
* Voyage key ONLY to report its health — never to gate. The three outcomes are
|
|
558
|
+
* all clean exits (this function never throws, which is the whole point):
|
|
559
|
+
* - valid → "you're all set"
|
|
560
|
+
* - rejected → "key expired/revoked, replace it" (degraded, not broken)
|
|
561
|
+
* - probe error (offline/timeout) → "couldn't check, run doctor if search is off"
|
|
562
|
+
*
|
|
563
|
+
* Throwing here is the beta.25 bug: a re-run on an expired key ended the whole
|
|
564
|
+
* command with "onboard failed" even though the install was fine.
|
|
565
|
+
*
|
|
566
|
+
* Exported for testing; `runOnboard` is the only production caller.
|
|
567
|
+
*/
|
|
568
|
+
export async function reportAlreadyOnboarded(keyRes, deps = {}) {
|
|
569
|
+
const validate = deps.validate ?? validateVoyageKey;
|
|
570
|
+
ui.step("ok", "Onadit", "already set up");
|
|
571
|
+
// Be explicit about WHICH key this check used (shell env vs ~/.rift.env) and
|
|
572
|
+
// warn when a shell value shadows the saved key — so a stale shell export
|
|
573
|
+
// can't make a healthy install look broken without explanation.
|
|
574
|
+
for (const line of voyageKeySourceLines(keyRes))
|
|
575
|
+
ui.note(line);
|
|
576
|
+
const storedKey = keyRes.key ?? "";
|
|
577
|
+
const spin = new ui.Spinner("Search index").start();
|
|
578
|
+
let result;
|
|
579
|
+
try {
|
|
580
|
+
result = await validate({ apiKey: storedKey });
|
|
581
|
+
}
|
|
582
|
+
catch {
|
|
583
|
+
// A probe error (offline, timeout) is not a setup failure — the key may be
|
|
584
|
+
// fine. Don't fail the command; point at doctor for a deeper check.
|
|
585
|
+
spin.warn("Search index", "couldn't reach Voyage to check the key");
|
|
586
|
+
ui.note("Your install is set up. If semantic search seems off, run `onadit doctor`.");
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
if (result.ok) {
|
|
590
|
+
spin.succeed("Search index", `Voyage key valid · ····${result.last4}`);
|
|
591
|
+
ui.note('You\'re all set. Search: `onadit search "<query>"`. Re-check anytime: `onadit doctor`.');
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
// Key present but rejected (expired/revoked): semantic search is degraded, not
|
|
595
|
+
// broken. Point to repair; never throw.
|
|
596
|
+
spin.warn("Search index", "stored Voyage key was rejected");
|
|
597
|
+
ui.note("Your install is set up, but Voyage rejected the saved key (expired or revoked). " +
|
|
598
|
+
"Semantic search is degraded until you replace it:\n" +
|
|
599
|
+
" • Replace the key: onadit onboard --reconfigure-voyage\n" +
|
|
600
|
+
" • Diagnose search: onadit doctor");
|
|
601
|
+
}
|
|
602
|
+
// ----- Step 1: config.json + data dir -----
|
|
603
|
+
export async function ensureConfigAndDataDir(configPath, opts, rl) {
|
|
604
|
+
const absoluteConfig = path.resolve(configPath);
|
|
605
|
+
const shownConfig = absoluteConfig.replace(os.homedir(), "~");
|
|
606
|
+
if (fs.existsSync(absoluteConfig)) {
|
|
607
|
+
const config = loadConfig(absoluteConfig);
|
|
608
|
+
ui.step("ok", "Settings", shownConfig);
|
|
609
|
+
ui.note(`data dir: ${config.data_paths.data_dir}`);
|
|
610
|
+
return config.data_paths.data_dir;
|
|
611
|
+
}
|
|
612
|
+
const defaultDataDir = DEFAULT_DATA_DIR;
|
|
613
|
+
const dataDir = opts.yes
|
|
614
|
+
? defaultDataDir
|
|
615
|
+
: (await ask(rl, `Data dir [${defaultDataDir}]: `, defaultDataDir)).trim() ||
|
|
616
|
+
defaultDataDir;
|
|
617
|
+
fs.mkdirSync(dataDir, { recursive: true });
|
|
618
|
+
const inboxDir = path.join(dataDir, "inbox");
|
|
619
|
+
fs.mkdirSync(inboxDir, { recursive: true });
|
|
620
|
+
const config = {
|
|
621
|
+
sources: [
|
|
622
|
+
{
|
|
623
|
+
path: inboxDir,
|
|
624
|
+
extraction: "cloud",
|
|
625
|
+
mode: "watch",
|
|
626
|
+
scope: "project",
|
|
627
|
+
},
|
|
628
|
+
],
|
|
629
|
+
embedding: { provider: "voyage", model: "voyage-3-lite" },
|
|
630
|
+
data_paths: { data_dir: dataDir, jobs_dir: path.join(dataDir, "..", "jobs") },
|
|
631
|
+
rate_limit: { window_ms: 60_000, max_requests: 100 },
|
|
632
|
+
// Trust-first default: scheduled chat capture is OFF. The capture lane
|
|
633
|
+
// runs a Codex CLI triage call, so a fresh install sends no conversation
|
|
634
|
+
// content off-device, runs no Voyage embedding, and makes no Codex model
|
|
635
|
+
// call until the user opts in with `--enable-capture` (persisted below).
|
|
636
|
+
// (The daemon does make a metadata-only npm version check — no content, no
|
|
637
|
+
// key, no machine info.) The daemon gates auto-capture on `capture.enabled`.
|
|
638
|
+
capture: { enabled: false, interval_seconds: 3600 },
|
|
639
|
+
// Trust-first default: AI metadata enrichment is OFF. Import + keyword
|
|
640
|
+
// search run AI-free (BasicMetadataExtractor) and make no Codex calls,
|
|
641
|
+
// even on a machine with Codex installed. `--enable-codex-enrichment`
|
|
642
|
+
// (persisted below) flips this on. The `embedding` block above is a
|
|
643
|
+
// routing target only — without a Voyage key, search stays lexical.
|
|
644
|
+
enrichment: { ai_metadata: false },
|
|
645
|
+
};
|
|
646
|
+
fs.mkdirSync(path.dirname(absoluteConfig), { recursive: true });
|
|
647
|
+
fs.writeFileSync(absoluteConfig, JSON.stringify(config, null, 2) + "\n", {
|
|
648
|
+
encoding: "utf8",
|
|
649
|
+
mode: 0o644,
|
|
650
|
+
});
|
|
651
|
+
ui.step("ok", "Settings", `wrote default · ${shownConfig}`);
|
|
652
|
+
ui.note(`data dir: ${dataDir}`);
|
|
653
|
+
// Trigger ensureDirectories.
|
|
654
|
+
loadConfig(absoluteConfig);
|
|
655
|
+
return dataDir;
|
|
656
|
+
}
|
|
657
|
+
// ----- Step 2a: persist --voyage-label to config.json -----
|
|
658
|
+
/**
|
|
659
|
+
* Sanitize a raw `--voyage-label`, persist it when valid, and return the
|
|
660
|
+
* string the caller should use for display. Invalid labels are dropped
|
|
661
|
+
* with a generic notice that never echoes the raw value — so a
|
|
662
|
+
* key-shaped, path-shaped, or legacy-name-shaped label cannot leak
|
|
663
|
+
* through stdout or `config.json`. Returns `null` when the input was
|
|
664
|
+
* absent or rejected.
|
|
665
|
+
*
|
|
666
|
+
* Exported for orchestrator-level tests; `runOnboard` is the only
|
|
667
|
+
* production caller.
|
|
668
|
+
*/
|
|
669
|
+
export function applyVoyageLabel(rawLabel, configPath, emit) {
|
|
670
|
+
if (rawLabel === undefined)
|
|
671
|
+
return null;
|
|
672
|
+
const sanitized = sanitizeProjectLabel(rawLabel);
|
|
673
|
+
if (!sanitized) {
|
|
674
|
+
// Generic message: never echo the raw value. Anything we say here
|
|
675
|
+
// also has to land in the friend's terminal, so it must not name
|
|
676
|
+
// the rejected token.
|
|
677
|
+
emit("Ignoring --voyage-label: must be 1–32 chars of letters, digits, '-' or '_' " +
|
|
678
|
+
"(no paths, secrets, or legacy product names).");
|
|
679
|
+
return null;
|
|
680
|
+
}
|
|
681
|
+
persistVoyageLabel(configPath, sanitized);
|
|
682
|
+
emit(`Stored Voyage label: ${sanitized}`);
|
|
683
|
+
return sanitized;
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Merge `voyage.project_label` into the config file. The file is
|
|
687
|
+
* backed up to `<file>.bak.<iso>` first so an interrupted write or a
|
|
688
|
+
* future operator typo can be undone. Display only — never sent to the
|
|
689
|
+
* relay, never inferred from the key bytes.
|
|
690
|
+
*
|
|
691
|
+
* Defensively re-runs `sanitizeProjectLabel` even though the standard
|
|
692
|
+
* `applyVoyageLabel` caller has already validated: a future programmer
|
|
693
|
+
* who reaches for this export directly still cannot land an unsafe
|
|
694
|
+
* string in the file.
|
|
695
|
+
*/
|
|
696
|
+
export function persistVoyageLabel(configPath, label) {
|
|
697
|
+
const sanitized = sanitizeProjectLabel(label);
|
|
698
|
+
if (!sanitized) {
|
|
699
|
+
throw new CliError("Refusing to persist Voyage label: not a valid label " +
|
|
700
|
+
"(1–32 chars of letters, digits, '-' or '_'; no paths, secrets, or legacy names).", "validation");
|
|
701
|
+
}
|
|
702
|
+
const absolute = path.resolve(configPath);
|
|
703
|
+
if (!fs.existsSync(absolute)) {
|
|
704
|
+
// ensureConfigAndDataDir always writes config.json before this runs;
|
|
705
|
+
// a missing file here means the caller bypassed step 1.
|
|
706
|
+
throw new CliError(`Cannot persist Voyage label: config not found at ${absolute}`, "validation");
|
|
707
|
+
}
|
|
708
|
+
const raw = fs.readFileSync(absolute, "utf8");
|
|
709
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
710
|
+
fs.writeFileSync(`${absolute}.bak.${stamp}`, raw, { encoding: "utf8", mode: 0o600 });
|
|
711
|
+
const parsed = JSON.parse(raw);
|
|
712
|
+
const voyage = parsed["voyage"] ?? {};
|
|
713
|
+
voyage["project_label"] = sanitized;
|
|
714
|
+
parsed["voyage"] = voyage;
|
|
715
|
+
const tmp = `${absolute}.tmp.${process.pid}`;
|
|
716
|
+
fs.writeFileSync(tmp, JSON.stringify(parsed, null, 2) + "\n", {
|
|
717
|
+
encoding: "utf8",
|
|
718
|
+
mode: 0o644,
|
|
719
|
+
});
|
|
720
|
+
fs.renameSync(tmp, absolute);
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* Persist the Codex AI-metadata-enrichment opt-in into config.json. Default
|
|
724
|
+
* onboarding leaves `enrichment.ai_metadata = false` (AI-free import + keyword
|
|
725
|
+
* search, zero Codex calls). When the user passes `--enable-codex-enrichment`,
|
|
726
|
+
* this flips it to true AND pins `enrichment.metadata.provider = "codex_cli"` so
|
|
727
|
+
* the daemon/worker select the Codex-backed extractor — overriding any prior
|
|
728
|
+
* `claude_code` pin rather than silently leaving Claude selected. Backs the file
|
|
729
|
+
* up first, mirroring `persistVoyageLabel`.
|
|
730
|
+
*
|
|
731
|
+
* Exported for orchestrator-level tests; `runOnboard` is the only production
|
|
732
|
+
* caller. Returns true when the flag was set and persisted, false otherwise.
|
|
733
|
+
*/
|
|
734
|
+
export function applyCodexEnrichmentOptIn(enable, configPath, emit) {
|
|
735
|
+
if (enable !== true)
|
|
736
|
+
return false;
|
|
737
|
+
const absolute = path.resolve(configPath);
|
|
738
|
+
if (!fs.existsSync(absolute)) {
|
|
739
|
+
throw new CliError(`Cannot persist enrichment opt-in: config not found at ${absolute}`, "validation");
|
|
740
|
+
}
|
|
741
|
+
const raw = fs.readFileSync(absolute, "utf8");
|
|
742
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
743
|
+
fs.writeFileSync(`${absolute}.bak.${stamp}`, raw, { encoding: "utf8", mode: 0o600 });
|
|
744
|
+
const parsed = JSON.parse(raw);
|
|
745
|
+
const enrichment = parsed["enrichment"] ?? {};
|
|
746
|
+
enrichment["ai_metadata"] = true;
|
|
747
|
+
// Pin the metadata worker to Codex explicitly. Setting only `ai_metadata`
|
|
748
|
+
// would leave a pre-existing `metadata.provider = "claude_code"` in place, so
|
|
749
|
+
// "use Codex" would silently keep routing metadata through Claude (and keep
|
|
750
|
+
// digests off). Codex opt-in must win the provider selection, not just the gate.
|
|
751
|
+
const metadata = enrichment["metadata"] ?? {};
|
|
752
|
+
metadata["provider"] = "codex_cli";
|
|
753
|
+
enrichment["metadata"] = metadata;
|
|
754
|
+
parsed["enrichment"] = enrichment;
|
|
755
|
+
const tmp = `${absolute}.tmp.${process.pid}`;
|
|
756
|
+
fs.writeFileSync(tmp, JSON.stringify(parsed, null, 2) + "\n", {
|
|
757
|
+
encoding: "utf8",
|
|
758
|
+
mode: 0o644,
|
|
759
|
+
});
|
|
760
|
+
fs.renameSync(tmp, absolute);
|
|
761
|
+
emit("Codex AI metadata enrichment enabled (enrichment.ai_metadata = true, metadata.provider = codex_cli).");
|
|
762
|
+
return true;
|
|
763
|
+
}
|
|
764
|
+
/**
|
|
765
|
+
* Persist the Claude-Code AI-metadata-enrichment opt-in into config.json. Like
|
|
766
|
+
* `applyCodexEnrichmentOptIn` it flips `enrichment.ai_metadata = true`, but it
|
|
767
|
+
* ALSO pins `enrichment.metadata.provider = "claude_code"` so the daemon/worker
|
|
768
|
+
* route metadata through the `claude` CLI instead of Codex. Backs the file up
|
|
769
|
+
* first, mirroring the Codex opt-in. Callers must reject the combination of
|
|
770
|
+
* both enrichment flags before reaching here (see `runOnboard`).
|
|
771
|
+
*
|
|
772
|
+
* Discloses the one capability this does NOT turn on: there is no Claude digest
|
|
773
|
+
* worker yet and we will not silently borrow Codex for it, so digests/
|
|
774
|
+
* compaction stay off until a dedicated digest-provider slice ships. Saying so
|
|
775
|
+
* here prevents a silent capability loss for a user who switches to Claude.
|
|
776
|
+
*
|
|
777
|
+
* Exported for orchestrator-level tests; `runOnboard` is the only production
|
|
778
|
+
* caller. Returns true when the flag was set and persisted, false otherwise.
|
|
779
|
+
*/
|
|
780
|
+
export function applyClaudeEnrichmentOptIn(enable, configPath, emit) {
|
|
781
|
+
if (enable !== true)
|
|
782
|
+
return false;
|
|
783
|
+
const absolute = path.resolve(configPath);
|
|
784
|
+
if (!fs.existsSync(absolute)) {
|
|
785
|
+
throw new CliError(`Cannot persist enrichment opt-in: config not found at ${absolute}`, "validation");
|
|
786
|
+
}
|
|
787
|
+
const raw = fs.readFileSync(absolute, "utf8");
|
|
788
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
789
|
+
fs.writeFileSync(`${absolute}.bak.${stamp}`, raw, { encoding: "utf8", mode: 0o600 });
|
|
790
|
+
const parsed = JSON.parse(raw);
|
|
791
|
+
const enrichment = parsed["enrichment"] ?? {};
|
|
792
|
+
enrichment["ai_metadata"] = true;
|
|
793
|
+
const metadata = enrichment["metadata"] ?? {};
|
|
794
|
+
metadata["provider"] = "claude_code";
|
|
795
|
+
enrichment["metadata"] = metadata;
|
|
796
|
+
parsed["enrichment"] = enrichment;
|
|
797
|
+
const tmp = `${absolute}.tmp.${process.pid}`;
|
|
798
|
+
fs.writeFileSync(tmp, JSON.stringify(parsed, null, 2) + "\n", {
|
|
799
|
+
encoding: "utf8",
|
|
800
|
+
mode: 0o644,
|
|
801
|
+
});
|
|
802
|
+
fs.renameSync(tmp, absolute);
|
|
803
|
+
emit("Claude metadata enrichment enabled. Digests/compaction stay off until Claude digest support ships.");
|
|
804
|
+
return true;
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* Persist the scheduled-capture opt-in. Default install writes
|
|
808
|
+
* `capture.enabled = false` (trust-first: zero Codex calls on a fresh
|
|
809
|
+
* machine). When the user passes `--enable-capture`, this flips it to true
|
|
810
|
+
* so the daemon runs auto-capture on its interval. Backs the file up first,
|
|
811
|
+
* mirroring `applyCodexEnrichmentOptIn`.
|
|
812
|
+
*
|
|
813
|
+
* Exported for orchestrator-level tests; `runOnboard` is the only production
|
|
814
|
+
* caller. Returns true when the flag was set and persisted, false otherwise.
|
|
815
|
+
*/
|
|
816
|
+
export function applyCaptureOptIn(enable, configPath, emit) {
|
|
817
|
+
if (enable !== true)
|
|
818
|
+
return false;
|
|
819
|
+
const absolute = path.resolve(configPath);
|
|
820
|
+
if (!fs.existsSync(absolute)) {
|
|
821
|
+
throw new CliError(`Cannot persist capture opt-in: config not found at ${absolute}`, "validation");
|
|
822
|
+
}
|
|
823
|
+
writeCaptureEnabled(absolute, true);
|
|
824
|
+
emit("Scheduled chat capture enabled (capture.enabled = true).");
|
|
825
|
+
return true;
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* Persist the Claude Desktop agent/Cowork capture opt-in into config.json.
|
|
829
|
+
* This adds `claude_desktop_sessions` to `capture.sources` without making it a
|
|
830
|
+
* default for fresh installs. Existing Cowork history is still privacy-safe:
|
|
831
|
+
* the first capture run bootstraps/watermarks already-present sessions instead
|
|
832
|
+
* of importing them as new.
|
|
833
|
+
*
|
|
834
|
+
* Exported for focused tests; `runOnboard` is the production caller.
|
|
835
|
+
*/
|
|
836
|
+
export function applyCoworkCaptureOptIn(enable, configPath, emit) {
|
|
837
|
+
if (enable !== true)
|
|
838
|
+
return false;
|
|
839
|
+
const absolute = path.resolve(configPath);
|
|
840
|
+
if (!fs.existsSync(absolute)) {
|
|
841
|
+
throw new CliError(`Cannot persist Cowork capture opt-in: config not found at ${absolute}`, "validation");
|
|
842
|
+
}
|
|
843
|
+
const raw = fs.readFileSync(absolute, "utf8");
|
|
844
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
845
|
+
fs.writeFileSync(`${absolute}.bak.${stamp}`, raw, { encoding: "utf8", mode: 0o600 });
|
|
846
|
+
const parsed = JSON.parse(raw);
|
|
847
|
+
const capture = parsed["capture"] ?? {};
|
|
848
|
+
const currentSources = Array.isArray(capture["sources"])
|
|
849
|
+
? capture["sources"].filter((s) => typeof s === "string" &&
|
|
850
|
+
(s === "claude_code" ||
|
|
851
|
+
s === "codex_cli" ||
|
|
852
|
+
s === CURSOR_CAPTURE_SOURCE ||
|
|
853
|
+
s === CLAUDE_DESKTOP_CAPTURE_SOURCE || s === "grok_cli"))
|
|
854
|
+
: [...DEFAULT_AUTO_CAPTURE_SOURCES];
|
|
855
|
+
if (!currentSources.includes(CLAUDE_DESKTOP_CAPTURE_SOURCE)) {
|
|
856
|
+
currentSources.push(CLAUDE_DESKTOP_CAPTURE_SOURCE);
|
|
857
|
+
}
|
|
858
|
+
capture["sources"] = currentSources;
|
|
859
|
+
parsed["capture"] = capture;
|
|
860
|
+
const tmp = `${absolute}.tmp.${process.pid}`;
|
|
861
|
+
fs.writeFileSync(tmp, JSON.stringify(parsed, null, 2) + "\n", {
|
|
862
|
+
encoding: "utf8",
|
|
863
|
+
mode: 0o644,
|
|
864
|
+
});
|
|
865
|
+
fs.renameSync(tmp, absolute);
|
|
866
|
+
emit("Claude Desktop Cowork capture enabled (capture.sources includes claude_desktop_sessions). Existing Cowork sessions are baselined on first run; new sessions are captured after that.");
|
|
867
|
+
return true;
|
|
868
|
+
}
|
|
869
|
+
export function applyGrokCaptureOptIn(enable, configPath, emit) {
|
|
870
|
+
if (enable !== true)
|
|
871
|
+
return false;
|
|
872
|
+
const absolute = path.resolve(configPath);
|
|
873
|
+
if (!fs.existsSync(absolute)) {
|
|
874
|
+
throw new CliError(`Cannot persist Grok CLI capture opt-in: config not found at ${absolute}`, "validation");
|
|
875
|
+
}
|
|
876
|
+
const raw = fs.readFileSync(absolute, "utf8");
|
|
877
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
878
|
+
fs.writeFileSync(`${absolute}.bak.${stamp}`, raw, { encoding: "utf8", mode: 0o600 });
|
|
879
|
+
const parsed = JSON.parse(raw);
|
|
880
|
+
const capture = parsed["capture"] ?? {};
|
|
881
|
+
const currentSources = Array.isArray(capture["sources"])
|
|
882
|
+
? capture["sources"].filter((s) => typeof s === "string" &&
|
|
883
|
+
(s === "claude_code" ||
|
|
884
|
+
s === "codex_cli" ||
|
|
885
|
+
s === CURSOR_CAPTURE_SOURCE ||
|
|
886
|
+
s === CLAUDE_DESKTOP_CAPTURE_SOURCE || s === "grok_cli"))
|
|
887
|
+
: [...DEFAULT_AUTO_CAPTURE_SOURCES];
|
|
888
|
+
if (!currentSources.includes("grok_cli")) {
|
|
889
|
+
currentSources.push("grok_cli");
|
|
890
|
+
}
|
|
891
|
+
capture["sources"] = currentSources;
|
|
892
|
+
parsed["capture"] = capture;
|
|
893
|
+
const tmp = `${absolute}.tmp.${process.pid}`;
|
|
894
|
+
fs.writeFileSync(tmp, JSON.stringify(parsed, null, 2) + "\n", {
|
|
895
|
+
encoding: "utf8",
|
|
896
|
+
mode: 0o644,
|
|
897
|
+
});
|
|
898
|
+
fs.renameSync(tmp, absolute);
|
|
899
|
+
emit("Grok CLI capture enabled (capture.sources includes grok_cli). Existing and future local CLI sessions are captured. Grok Bot is not captured.");
|
|
900
|
+
return true;
|
|
901
|
+
}
|
|
902
|
+
// ----- Step 2: Voyage key flow -----
|
|
903
|
+
async function collectAndPersistVoyageKey(opts, rl) {
|
|
904
|
+
const key = await collectVoyageKey(opts, rl);
|
|
905
|
+
if (!key) {
|
|
906
|
+
// No key → keyword-only (lexical) mode. Not a failure; import + search
|
|
907
|
+
// still work. Skip validation, the env write, and the Voyage smoke.
|
|
908
|
+
ui.step("info", "Search index", "no key — keyword search (semantic off)");
|
|
909
|
+
ui.note("Add semantic search later: run `onadit onboard` and paste a Voyage key.");
|
|
910
|
+
return null;
|
|
911
|
+
}
|
|
912
|
+
const validateSpin = new ui.Spinner("Search index").start();
|
|
913
|
+
const validation = await validateVoyageKey({ apiKey: key });
|
|
914
|
+
if (!validation.ok) {
|
|
915
|
+
validateSpin.fail("Search index", "validation failed");
|
|
916
|
+
throw new CliError(`Voyage validation failed: ${validation.reason}`, "validation");
|
|
917
|
+
}
|
|
918
|
+
validateSpin.succeed("Search index", `connected · Voyage · ····${validation.last4}`);
|
|
919
|
+
const envPath = defaultRiftEnvPath();
|
|
920
|
+
const writeResult = writeEnvFile({ filePath: envPath, key: "VOYAGE_API_KEY", value: key });
|
|
921
|
+
ui.note(writeResult.backedUp
|
|
922
|
+
? "wrote ~/.rift.env (existing file backed up)"
|
|
923
|
+
: "wrote ~/.rift.env (mode 0600)");
|
|
924
|
+
// Refresh process.env so any subsequent in-process Voyage call sees it.
|
|
925
|
+
loadRiftEnv({ filePath: envPath });
|
|
926
|
+
// NOTE: the daemon kickstart + smoke deliberately does NOT happen here.
|
|
927
|
+
// It runs in `runOnboard` AFTER the opt-in writes (label/enrichment/
|
|
928
|
+
// capture) land in config.json, so the respawned daemon boots with the
|
|
929
|
+
// final config. The daemon builds its capture loop and metadata extractor
|
|
930
|
+
// once at boot (src/main.ts), so kickstarting before those writes would
|
|
931
|
+
// leave `--enable-capture` / `--enable-codex-enrichment` inert until a
|
|
932
|
+
// later restart.
|
|
933
|
+
return validation.last4;
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
936
|
+
* Resolve the Voyage key, or `""` to mean "finish without a key" (keyword-only
|
|
937
|
+
* lexical mode). The key is no longer mandatory: a friend can import and search
|
|
938
|
+
* first, then add a key later for semantic ranking. Returns `""` on explicit
|
|
939
|
+
* opt-out (`--no-voyage-key`), on `--yes` with no key available, or when the
|
|
940
|
+
* interactive prompt is left blank.
|
|
941
|
+
*/
|
|
942
|
+
export async function collectVoyageKey(opts, rl) {
|
|
943
|
+
if (opts.voyageKey === false || opts.noVoyageKey === true)
|
|
944
|
+
return "";
|
|
945
|
+
if (typeof opts.voyageKey === "string" && opts.voyageKey.trim()) {
|
|
946
|
+
return opts.voyageKey.trim();
|
|
947
|
+
}
|
|
948
|
+
if (process.env["VOYAGE_API_KEY"])
|
|
949
|
+
return process.env["VOYAGE_API_KEY"].trim();
|
|
950
|
+
// Non-interactive with no key supplied: finish in lexical mode rather than
|
|
951
|
+
// fail. The friend can run `onadit onboard` again with a key anytime.
|
|
952
|
+
if (opts.yes)
|
|
953
|
+
return "";
|
|
954
|
+
// Explain the key BEFORE asking for it — a beta user should know what
|
|
955
|
+
// they are pasting and why, not be confronted with a bare prompt.
|
|
956
|
+
ui.detail([
|
|
957
|
+
"Search index key (optional)",
|
|
958
|
+
"• What: paste the Voyage key Clem sent you (it won't echo as you type).",
|
|
959
|
+
"• Why: Voyage adds meaning-based (semantic) search on top of keyword search.",
|
|
960
|
+
"• Skip: press Enter to finish now with keyword search — add a key later anytime.",
|
|
961
|
+
"• Privacy: the key is stored locally and only sent to Voyage when Onadit calls Voyage; never sent to Clem.",
|
|
962
|
+
].join("\n"));
|
|
963
|
+
const answer = (await ask(rl, "Paste your Voyage API key (or press Enter to skip): ")).trim();
|
|
964
|
+
return answer; // "" → finish in keyword-only mode
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* Kickstart, wait for /health, confirm the respawned daemon process has
|
|
968
|
+
* VOYAGE_API_KEY loaded. The smoke is fully non-indexing — `/health`
|
|
969
|
+
* exposes `voyage_key_present` so we never write a row to LanceDB just to
|
|
970
|
+
* prove the daemon read its env file. That keeps first-recall results
|
|
971
|
+
* free of an "onboarding probe" hit that would otherwise satisfy the
|
|
972
|
+
* completion check without any real user data being present.
|
|
973
|
+
*/
|
|
974
|
+
async function daemonRefreshFlow(globalOpts) {
|
|
975
|
+
const baseUrl = safeResolveBaseUrl(globalOpts.config);
|
|
976
|
+
if (!baseUrl) {
|
|
977
|
+
ui.step("skip", "Onadit service", "not configured yet — bootstrap via install.sh, then re-run");
|
|
978
|
+
return { ok: false, reason: "daemon not configured", kind: "not_configured" };
|
|
979
|
+
}
|
|
980
|
+
const daemonSpin = new ui.Spinner("Onadit service").start();
|
|
981
|
+
const kick = await kickstartDaemon();
|
|
982
|
+
if (kick.status === "agent_not_loaded") {
|
|
983
|
+
daemonSpin.fail("Onadit service", "launchd agent not loaded");
|
|
984
|
+
ui.note(kick.hint);
|
|
985
|
+
return { ok: false, reason: kick.hint ?? "agent not loaded", kind: "agent_not_loaded" };
|
|
986
|
+
}
|
|
987
|
+
if (kick.status === "failed") {
|
|
988
|
+
daemonSpin.fail("Onadit service", "kickstart failed");
|
|
989
|
+
return {
|
|
990
|
+
ok: false,
|
|
991
|
+
reason: kick.hint ?? "kickstart failed",
|
|
992
|
+
kind: "kickstart_failed",
|
|
993
|
+
};
|
|
994
|
+
}
|
|
995
|
+
const health = await waitForHealth({ baseUrl });
|
|
996
|
+
if (!health.ok) {
|
|
997
|
+
daemonSpin.fail("Onadit service", "health check failed");
|
|
998
|
+
return { ok: false, reason: health.reason, kind: "health_failed" };
|
|
999
|
+
}
|
|
1000
|
+
if (!health.voyageKeyPresent) {
|
|
1001
|
+
daemonSpin.fail("Onadit service", "voyage_key_present=false after kickstart");
|
|
1002
|
+
return {
|
|
1003
|
+
ok: false,
|
|
1004
|
+
reason: "Daemon /health reports voyage_key_present=false after kickstart.",
|
|
1005
|
+
kind: "smoke_failed",
|
|
1006
|
+
};
|
|
1007
|
+
}
|
|
1008
|
+
daemonSpin.succeed("Onadit service", `healthy · daemon · uptime ${health.uptimeSeconds}s`);
|
|
1009
|
+
return { ok: true, kickstarted: true };
|
|
1010
|
+
}
|
|
1011
|
+
export function classifyTokenFailure(err) {
|
|
1012
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1013
|
+
const lower = message.toLowerCase();
|
|
1014
|
+
// A-1.2: the macOS Keychain prints the same string whether the lock
|
|
1015
|
+
// hits a read or a write. Catch on substring; don't try to inspect
|
|
1016
|
+
// SecKeychain error codes from JS.
|
|
1017
|
+
//
|
|
1018
|
+
// Two shapes can land here:
|
|
1019
|
+
// 1. The raw `security` error from a write path (`issueToken`),
|
|
1020
|
+
// whose message contains "User interaction is not allowed."
|
|
1021
|
+
// 2. The friendly `CliError` shape that `readToken` now throws
|
|
1022
|
+
// (post-beta.7) in place of the raw error. Its message starts
|
|
1023
|
+
// with "Login Keychain is locked" and embeds the
|
|
1024
|
+
// `security unlock-keychain` recovery command verbatim.
|
|
1025
|
+
// Both must classify as `keychain_locked` so the typed
|
|
1026
|
+
// recovery branch in `runFirstCapturePass` fires correctly.
|
|
1027
|
+
if (lower.includes("user interaction is not allowed") ||
|
|
1028
|
+
lower.includes("login keychain is locked") ||
|
|
1029
|
+
lower.includes("security unlock-keychain")) {
|
|
1030
|
+
return { reason: "keychain_locked", message };
|
|
1031
|
+
}
|
|
1032
|
+
return { reason: "other", message };
|
|
1033
|
+
}
|
|
1034
|
+
export async function ensureToken(configPath) {
|
|
1035
|
+
// A-1.2: readToken is inside the try — a locked-keychain *read* must
|
|
1036
|
+
// be classified the same as a locked-keychain *write*.
|
|
1037
|
+
try {
|
|
1038
|
+
const existing = await readToken();
|
|
1039
|
+
if (existing)
|
|
1040
|
+
return { ok: true, token: existing, source: "existing" };
|
|
1041
|
+
}
|
|
1042
|
+
catch (err) {
|
|
1043
|
+
const { reason, message } = classifyTokenFailure(err);
|
|
1044
|
+
return { ok: false, reason, message, hint: describeTokenFailure(message) };
|
|
1045
|
+
}
|
|
1046
|
+
try {
|
|
1047
|
+
const { token } = await issueToken(path.resolve(configPath));
|
|
1048
|
+
// Mirror to ~/.rift/token so subsequent CLI calls don't depend on Keychain.
|
|
1049
|
+
const tokenPath = path.join(os.homedir(), ".rift", "token");
|
|
1050
|
+
try {
|
|
1051
|
+
fs.mkdirSync(path.dirname(tokenPath), { recursive: true });
|
|
1052
|
+
fs.writeFileSync(tokenPath, token + "\n", {
|
|
1053
|
+
encoding: "utf8",
|
|
1054
|
+
mode: 0o600,
|
|
1055
|
+
});
|
|
1056
|
+
fs.chmodSync(tokenPath, 0o600);
|
|
1057
|
+
}
|
|
1058
|
+
catch (writeErr) {
|
|
1059
|
+
const reason = writeErr instanceof Error ? writeErr.message : String(writeErr);
|
|
1060
|
+
say(`Token issued but mirror to ${tokenPath} failed: ${reason}. Subsequent CLI calls will rely on Keychain only.`);
|
|
1061
|
+
}
|
|
1062
|
+
return { ok: true, token, source: "issued" };
|
|
1063
|
+
}
|
|
1064
|
+
catch (err) {
|
|
1065
|
+
const { reason, message } = classifyTokenFailure(err);
|
|
1066
|
+
return { ok: false, reason, message, hint: describeTokenFailure(message) };
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
export function describeTokenFailure(reason) {
|
|
1070
|
+
const lower = reason.toLowerCase();
|
|
1071
|
+
// Two SSH-locked shapes can land here — check both before the generic
|
|
1072
|
+
// Keychain branch so the GUI-only "Keychain Access → File → Unlock"
|
|
1073
|
+
// copy never replaces the SSH-specific `security unlock-keychain`
|
|
1074
|
+
// command: (1) the raw `security` error from the write path, and
|
|
1075
|
+
// (2) the friendly CliError that readToken now emits post-beta.7,
|
|
1076
|
+
// whose message starts with "Login Keychain is locked" and embeds
|
|
1077
|
+
// the unlock command verbatim.
|
|
1078
|
+
if (lower.includes("user interaction is not allowed") ||
|
|
1079
|
+
lower.includes("login keychain is locked") ||
|
|
1080
|
+
lower.includes("security unlock-keychain")) {
|
|
1081
|
+
return "the login Keychain is locked (typical over SSH). Run `security unlock-keychain ~/Library/Keychains/login.keychain-db` (it will prompt for your Mac login password), then re-run `onadit onboard`. For local Terminal installs this should not happen.";
|
|
1082
|
+
}
|
|
1083
|
+
if (lower.includes("keychain") || lower.includes("security:")) {
|
|
1084
|
+
return "unlock the login Keychain (Keychain Access → File → Unlock) and re-run, or run from a Terminal session with GUI access (not over SSH).";
|
|
1085
|
+
}
|
|
1086
|
+
if (lower.includes("eacces") || lower.includes("permission denied")) {
|
|
1087
|
+
return "check that `~/.rift/` is writable by your user, then re-run `onadit onboard`.";
|
|
1088
|
+
}
|
|
1089
|
+
if (lower.includes("enoent") ||
|
|
1090
|
+
lower.includes("no such file") ||
|
|
1091
|
+
lower.includes("daemon") ||
|
|
1092
|
+
lower.includes("pid file") ||
|
|
1093
|
+
lower.includes("not running")) {
|
|
1094
|
+
return "start the daemon with `launchctl kickstart -k gui/$UID/com.getrift.daemon`, then re-run `onadit onboard`.";
|
|
1095
|
+
}
|
|
1096
|
+
return "re-run `onadit onboard`. If the failure repeats, run `onadit feedback --kind=broke --with-status` to ship a diagnostic bundle.";
|
|
1097
|
+
}
|
|
1098
|
+
// ----- Step 3: triage-worker preflight -----
|
|
1099
|
+
/**
|
|
1100
|
+
* Live triage probe for the CONFIGURED worker (Codex CLI by default, or Claude
|
|
1101
|
+
* Code when `capture.triage.provider = claude_code`). Routing through the
|
|
1102
|
+
* factory — not a hardcoded CodexCliTriageProvider — is what stops a Claude
|
|
1103
|
+
* Code user who deliberately picked `claude_code` from being failed by a Codex
|
|
1104
|
+
* probe they never asked for.
|
|
1105
|
+
*/
|
|
1106
|
+
async function triagePreflight(name) {
|
|
1107
|
+
try {
|
|
1108
|
+
await createTriageProvider(name, { timeoutMs: 120_000 }).triage({
|
|
1109
|
+
content: "User: Onadit onboarding preflight.\n\nAssistant: Ready.",
|
|
1110
|
+
});
|
|
1111
|
+
return true;
|
|
1112
|
+
}
|
|
1113
|
+
catch {
|
|
1114
|
+
return false;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
/**
|
|
1118
|
+
* Conservative email check — enough to catch obvious typos at the prompt;
|
|
1119
|
+
* the relay endpoint can re-validate. Returns the normalized (trimmed,
|
|
1120
|
+
* lowercased) address, or undefined if it doesn't look like an email.
|
|
1121
|
+
*/
|
|
1122
|
+
export function normalizeEmail(raw) {
|
|
1123
|
+
const s = raw.trim().toLowerCase();
|
|
1124
|
+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(s) ? s : undefined;
|
|
1125
|
+
}
|
|
1126
|
+
/**
|
|
1127
|
+
* Bundled "stay connected" opt-in. One yes/no that, on yes, collects an
|
|
1128
|
+
* email used for product news, pricing, a leaving-beta heads-up, and a
|
|
1129
|
+
* feedback channel. Opt-in only: `--yes` alone never subscribes (consent
|
|
1130
|
+
* must be active), `--no-feedback-relay` declines, and `--email` subscribes
|
|
1131
|
+
* non-interactively. The relay URL stays operator-only infra; when none is
|
|
1132
|
+
* configured the email is captured locally (sidecar + beta-signups.jsonl)
|
|
1133
|
+
* and reaches the operator once an endpoint is wired.
|
|
1134
|
+
*/
|
|
1135
|
+
export async function collectFeedbackPreference(opts, dataDir, rl, store = macRelaySecretStore) {
|
|
1136
|
+
const declined = isOff(opts, "feedbackRelay", "noFeedbackRelay");
|
|
1137
|
+
// Relay URL + signing secret come from EITHER an invite code (preferred:
|
|
1138
|
+
// nothing to paste by hand) OR the back-compat operator flags. The secret
|
|
1139
|
+
// never lands in the sidecar — it goes to the Keychain below.
|
|
1140
|
+
let url;
|
|
1141
|
+
let secret;
|
|
1142
|
+
if (!declined) {
|
|
1143
|
+
if (opts.invite) {
|
|
1144
|
+
const invite = parseInvite(opts.invite); // throws a friendly CliError if bad
|
|
1145
|
+
url = invite.url;
|
|
1146
|
+
secret = invite.secret;
|
|
1147
|
+
}
|
|
1148
|
+
else {
|
|
1149
|
+
url = opts.enableFeedbackRelay;
|
|
1150
|
+
secret = url ? opts.relaySecret : undefined;
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
// Friend-grade interactive path: when no relay flags were passed, offer to
|
|
1154
|
+
// paste an invite so the bearer code never lands in shell history. Skipped
|
|
1155
|
+
// off a TTY and under --yes (automation). A bad paste warns and skips — it
|
|
1156
|
+
// never aborts onboarding.
|
|
1157
|
+
if (!declined &&
|
|
1158
|
+
!url &&
|
|
1159
|
+
!opts.invite &&
|
|
1160
|
+
!opts.enableFeedbackRelay &&
|
|
1161
|
+
!opts.yes &&
|
|
1162
|
+
process.stdin.isTTY) {
|
|
1163
|
+
// Most friends will NOT have an invite code — make that the obvious,
|
|
1164
|
+
// pressure-free default so the bare prompt doesn't read as "something is
|
|
1165
|
+
// missing". An invite only turns on the direct feedback relay; the email
|
|
1166
|
+
// ask below (and `onadit feedback`) work without it.
|
|
1167
|
+
ui.detail([
|
|
1168
|
+
"Feedback channel (optional)",
|
|
1169
|
+
"• Have an invite code from Clem? Paste it to turn on the direct feedback relay.",
|
|
1170
|
+
"• No code? Just press Enter — this is optional, and most people skip it.",
|
|
1171
|
+
"• Either way you can send feedback any time with `onadit feedback`.",
|
|
1172
|
+
].join("\n"));
|
|
1173
|
+
const pasted = (await ask(rl, "Invite code from Clem (or press Enter to skip): ")).trim();
|
|
1174
|
+
if (pasted) {
|
|
1175
|
+
try {
|
|
1176
|
+
const invite = parseInvite(pasted);
|
|
1177
|
+
url = invite.url;
|
|
1178
|
+
secret = invite.secret;
|
|
1179
|
+
}
|
|
1180
|
+
catch {
|
|
1181
|
+
say(" That invite code is not valid — skipping feedback setup.");
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
// Configuring a URL without a signing secret leaves the install "looks
|
|
1186
|
+
// configured, silently dead" — the receiver requires a valid signature.
|
|
1187
|
+
// An invite always carries one; the flag path must pair a secret or
|
|
1188
|
+
// knowingly opt into unsigned mode (dev/local receivers only).
|
|
1189
|
+
if (url && !secret && !opts.allowUnsignedFeedbackRelay) {
|
|
1190
|
+
throw new CliError("Relay needs a signing secret. Use an invite (onadit onboard --invite <code>), " +
|
|
1191
|
+
"or pair --enable-feedback-relay with --relay-secret " +
|
|
1192
|
+
"(or --allow-unsigned-feedback-relay for a dev receiver).", "validation");
|
|
1193
|
+
}
|
|
1194
|
+
// Secure the secret BEFORE enabling relay: a write failure must not leave an
|
|
1195
|
+
// enabled config that can't sign. (3a's `onadit feedback setup` does the same.)
|
|
1196
|
+
if (secret) {
|
|
1197
|
+
try {
|
|
1198
|
+
await store.write(secret);
|
|
1199
|
+
}
|
|
1200
|
+
catch (err) {
|
|
1201
|
+
if (err instanceof LockedKeychainError) {
|
|
1202
|
+
throw new CliError("Your macOS Keychain is locked. Unlock it and retry: " +
|
|
1203
|
+
"security unlock-keychain ~/Library/Keychains/login.keychain-db", "validation");
|
|
1204
|
+
}
|
|
1205
|
+
throw err;
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
let email;
|
|
1209
|
+
if (!declined) {
|
|
1210
|
+
if (opts.email) {
|
|
1211
|
+
email = normalizeEmail(opts.email);
|
|
1212
|
+
if (!email) {
|
|
1213
|
+
throw new CliError(`--email "${opts.email}" is not a valid email address.`, "validation");
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
else if (!opts.yes && process.stdin.isTTY) {
|
|
1217
|
+
email = await promptStayConnected(rl);
|
|
1218
|
+
}
|
|
1219
|
+
// --yes / non-TTY with no --email: do not auto-subscribe.
|
|
1220
|
+
}
|
|
1221
|
+
const optedIn = email !== undefined || url !== undefined;
|
|
1222
|
+
const cfg = optedIn
|
|
1223
|
+
? {
|
|
1224
|
+
enabled: true,
|
|
1225
|
+
installation_id: crypto.randomUUID(),
|
|
1226
|
+
...(email ? { email } : {}),
|
|
1227
|
+
...(url ? { url } : {}),
|
|
1228
|
+
// url && !secret only happens under --allow-unsigned-feedback-relay
|
|
1229
|
+
// (the guard above throws otherwise): mark it an intentional unsigned
|
|
1230
|
+
// relay so maybeRelay may POST without a secret for THIS install only.
|
|
1231
|
+
...(url && !secret ? { unsigned: true } : {}),
|
|
1232
|
+
}
|
|
1233
|
+
: { enabled: false };
|
|
1234
|
+
// The sidecar holds only non-secret fields; the secret is already in the
|
|
1235
|
+
// Keychain (above).
|
|
1236
|
+
fs.mkdirSync(dataDir, { recursive: true });
|
|
1237
|
+
const sidecar = path.join(dataDir, "feedback-config.json");
|
|
1238
|
+
fs.writeFileSync(sidecar, JSON.stringify(cfg, null, 2) + "\n", {
|
|
1239
|
+
encoding: "utf8",
|
|
1240
|
+
mode: 0o600,
|
|
1241
|
+
});
|
|
1242
|
+
fs.chmodSync(sidecar, 0o600);
|
|
1243
|
+
if (cfg.email && cfg.installation_id) {
|
|
1244
|
+
// Sign the signup POST with the in-hand secret (no Keychain round-trip).
|
|
1245
|
+
await emitBetaSignup(cfg.email, cfg.installation_id, cfg.url, secret, dataDir);
|
|
1246
|
+
}
|
|
1247
|
+
return cfg;
|
|
1248
|
+
}
|
|
1249
|
+
/**
|
|
1250
|
+
* Interactive bundled opt-in. Returns the chosen email, or undefined if the
|
|
1251
|
+
* user declines or skips. Re-prompts up to 3× on a malformed address; an
|
|
1252
|
+
* empty line at any point means "skip".
|
|
1253
|
+
*/
|
|
1254
|
+
async function promptStayConnected(rl) {
|
|
1255
|
+
ui.detail([
|
|
1256
|
+
"Stay in the loop (optional)",
|
|
1257
|
+
"• Get product news, and pricing once we set it.",
|
|
1258
|
+
"• Gives you a direct line to send feedback any time via `onadit feedback`.",
|
|
1259
|
+
"• Opt-in. Stored locally, shared only with Clem — never sold, never spammed.",
|
|
1260
|
+
].join("\n"));
|
|
1261
|
+
const yn = (await ask(rl, "Share your email to stay connected? [y/N] ")).trim().toLowerCase();
|
|
1262
|
+
if (yn !== "y" && yn !== "yes")
|
|
1263
|
+
return undefined;
|
|
1264
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
1265
|
+
const raw = (await ask(rl, "Your email (Enter to skip): ")).trim();
|
|
1266
|
+
if (raw.length === 0)
|
|
1267
|
+
return undefined;
|
|
1268
|
+
const norm = normalizeEmail(raw);
|
|
1269
|
+
if (norm)
|
|
1270
|
+
return norm;
|
|
1271
|
+
say(" That doesn't look like an email address — try again, or press Enter to skip.");
|
|
1272
|
+
}
|
|
1273
|
+
return undefined;
|
|
1274
|
+
}
|
|
1275
|
+
/**
|
|
1276
|
+
* Record a one-time beta signup. The local JSONL row is canonical (never
|
|
1277
|
+
* lost); the relay POST is best-effort and only fires when an endpoint URL
|
|
1278
|
+
* is configured. Failures are swallowed — onboarding must never break on a
|
|
1279
|
+
* signup hiccup.
|
|
1280
|
+
*/
|
|
1281
|
+
async function emitBetaSignup(email, installationId, url, hmacSecret, dataDir) {
|
|
1282
|
+
const build = getBuildInfo();
|
|
1283
|
+
const payload = {
|
|
1284
|
+
ts: new Date().toISOString(),
|
|
1285
|
+
event: "beta_signup",
|
|
1286
|
+
email,
|
|
1287
|
+
installation_id: installationId,
|
|
1288
|
+
version: build.version,
|
|
1289
|
+
commit: build.commit,
|
|
1290
|
+
node: build.node,
|
|
1291
|
+
};
|
|
1292
|
+
// Audit invariant: every byte that reaches Clem must exist locally first.
|
|
1293
|
+
// If the canonical row can't be written, we must NOT relay — otherwise a
|
|
1294
|
+
// signup could reach Clem with no local record to audit against.
|
|
1295
|
+
let wroteLocal = false;
|
|
1296
|
+
try {
|
|
1297
|
+
const dir = path.join(dataDir, "observability");
|
|
1298
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
1299
|
+
const file = path.join(dir, "beta-signups.jsonl");
|
|
1300
|
+
fs.appendFileSync(file, `${JSON.stringify(payload)}\n`, {
|
|
1301
|
+
encoding: "utf8",
|
|
1302
|
+
mode: 0o600,
|
|
1303
|
+
});
|
|
1304
|
+
wroteLocal = true; // the canonical row is on disk; chmod below is cosmetic
|
|
1305
|
+
try {
|
|
1306
|
+
fs.chmodSync(file, 0o600);
|
|
1307
|
+
}
|
|
1308
|
+
catch {
|
|
1309
|
+
// best-effort permission fix — does not affect the audit invariant
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
catch {
|
|
1313
|
+
// Local write failed — don't break onboarding over a signup row, and
|
|
1314
|
+
// (below) don't relay something we couldn't record locally.
|
|
1315
|
+
}
|
|
1316
|
+
if (url && wroteLocal) {
|
|
1317
|
+
try {
|
|
1318
|
+
await postFeedback(payload, {
|
|
1319
|
+
url,
|
|
1320
|
+
...(hmacSecret ? { hmacSecret } : {}),
|
|
1321
|
+
});
|
|
1322
|
+
}
|
|
1323
|
+
catch {
|
|
1324
|
+
// Best-effort; the canonical row is already on disk.
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
// ----- Step 5b: Claude Code policy hook (opt-in) -----
|
|
1329
|
+
/**
|
|
1330
|
+
* Offer to install the Onadit policy hook into Claude Code's settings.
|
|
1331
|
+
*
|
|
1332
|
+
* Only runs when `~/.claude/settings.json` looks plausible (the dir
|
|
1333
|
+
* exists). Honors `--with-claude-hook` (force install, no prompt) and
|
|
1334
|
+
* `--no-claude-hook` (skip silently). Default `--yes` declines unless
|
|
1335
|
+
* `--with-claude-hook` is also set, so non-interactive operators never
|
|
1336
|
+
* silently get a global hook installed.
|
|
1337
|
+
*
|
|
1338
|
+
* Failures are non-fatal — onboarding continues. The hook is a quality
|
|
1339
|
+
* guardrail, not a correctness requirement.
|
|
1340
|
+
*/
|
|
1341
|
+
async function maybeInstallClaudeCodeHook(opts, rl) {
|
|
1342
|
+
if (isOff(opts, "claudeHook", "noClaudeHook")) {
|
|
1343
|
+
return;
|
|
1344
|
+
}
|
|
1345
|
+
const homeDir = os.homedir();
|
|
1346
|
+
const claudeDir = path.join(homeDir, ".claude");
|
|
1347
|
+
const claudeCodeDetected = fs.existsSync(claudeDir);
|
|
1348
|
+
if (!claudeCodeDetected) {
|
|
1349
|
+
return;
|
|
1350
|
+
}
|
|
1351
|
+
let install = false;
|
|
1352
|
+
if (opts.withClaudeHook) {
|
|
1353
|
+
install = true;
|
|
1354
|
+
}
|
|
1355
|
+
else if (opts.yes) {
|
|
1356
|
+
// Non-interactive default: skip. Operator must opt in with --with-claude-hook.
|
|
1357
|
+
ui.step("skip", "Claude Code", "skipped (pass --with-claude-hook to install non-interactively)");
|
|
1358
|
+
return;
|
|
1359
|
+
}
|
|
1360
|
+
else {
|
|
1361
|
+
ui.note([
|
|
1362
|
+
"Claude Code detected.",
|
|
1363
|
+
"Optional: install a PreToolUse policy hook so Onadit retrieval starts with onadit_context_pack",
|
|
1364
|
+
"(prevents broad onadit_search dumps from burning context). Disable any time with RIFT_POLICY_DISABLED=1.",
|
|
1365
|
+
].join("\n"));
|
|
1366
|
+
const answer = (await ask(rl, " Install Claude Code policy hook? [y/N]: ", "n"))
|
|
1367
|
+
.trim()
|
|
1368
|
+
.toLowerCase();
|
|
1369
|
+
install = answer === "y" || answer === "yes";
|
|
1370
|
+
}
|
|
1371
|
+
if (!install) {
|
|
1372
|
+
ui.step("skip", "Claude Code", "not installed");
|
|
1373
|
+
return;
|
|
1374
|
+
}
|
|
1375
|
+
try {
|
|
1376
|
+
const outcome = writeHookConfig({
|
|
1377
|
+
client: "claude-code",
|
|
1378
|
+
homeDir,
|
|
1379
|
+
dryRun: false,
|
|
1380
|
+
});
|
|
1381
|
+
if (outcome.hookEntryAdded) {
|
|
1382
|
+
ui.step("ok", "Claude Code", "connected · auto-recall hook added");
|
|
1383
|
+
}
|
|
1384
|
+
else if (outcome.hookEntryReplaced || outcome.scriptUpdated) {
|
|
1385
|
+
ui.step("ok", "Claude Code", "updated");
|
|
1386
|
+
}
|
|
1387
|
+
else {
|
|
1388
|
+
ui.step("ok", "Claude Code", "already up-to-date");
|
|
1389
|
+
}
|
|
1390
|
+
if (outcome.backupId) {
|
|
1391
|
+
ui.note(`backup ID: ${outcome.backupId}`);
|
|
1392
|
+
}
|
|
1393
|
+
ui.note("(Restart Claude Code or open /hooks once for the new entry to load.)");
|
|
1394
|
+
}
|
|
1395
|
+
catch (err) {
|
|
1396
|
+
if (err instanceof HooksParseFailedError) {
|
|
1397
|
+
ui.step("fail", "Claude Code", err.message);
|
|
1398
|
+
}
|
|
1399
|
+
else {
|
|
1400
|
+
ui.step("fail", "Claude Code", `install failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`);
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
// ----- Step 6 + 7: capture pass -----
|
|
1405
|
+
export async function runFirstCapturePass(configPath, dataDir) {
|
|
1406
|
+
const baseUrl = safeResolveBaseUrl(configPath);
|
|
1407
|
+
if (!baseUrl) {
|
|
1408
|
+
ui.step("skip", "Chat import", "daemon not reachable — skipped");
|
|
1409
|
+
return { saved: 0, reviewed: 0 };
|
|
1410
|
+
}
|
|
1411
|
+
// Per A-1.1/C-1.3: token failure here is fatal. The wizard cannot
|
|
1412
|
+
// proceed without a token (capture, import, recall all need auth).
|
|
1413
|
+
// The previous "skipping" path silently broke onboarding.
|
|
1414
|
+
const tokenResult = await ensureToken(configPath);
|
|
1415
|
+
if (!tokenResult.ok) {
|
|
1416
|
+
say("");
|
|
1417
|
+
if (tokenResult.reason === "keychain_locked") {
|
|
1418
|
+
say("Could not issue an auth token: the macOS login keychain is locked.");
|
|
1419
|
+
say("Run this, then re-run `onadit onboard`:");
|
|
1420
|
+
say(" security unlock-keychain ~/Library/Keychains/login.keychain-db");
|
|
1421
|
+
say("Note: the wizard will restart from the top. If your Voyage key is in");
|
|
1422
|
+
say(" VOYAGE_API_KEY it won't re-prompt; otherwise you'll re-paste it.");
|
|
1423
|
+
}
|
|
1424
|
+
else {
|
|
1425
|
+
// Per C-1.4 (round-6 fix): the "other" branch must spell out the
|
|
1426
|
+
// manual recovery so the user knows the exit path is `onadit token
|
|
1427
|
+
// issue` then re-running onboard. Generic hints alone aren't
|
|
1428
|
+
// enough.
|
|
1429
|
+
say(`Could not issue an auth token: ${tokenResult.message}`);
|
|
1430
|
+
say(`Recover: ${tokenResult.hint}`);
|
|
1431
|
+
say("If the failure persists, run `onadit token issue` manually, then re-run `onadit onboard`.");
|
|
1432
|
+
}
|
|
1433
|
+
process.exitCode = 1;
|
|
1434
|
+
return { saved: 0, reviewed: 0, fatal: true };
|
|
1435
|
+
}
|
|
1436
|
+
const token = tokenResult.token;
|
|
1437
|
+
const client = createHttpClient({ baseUrl, token });
|
|
1438
|
+
let codexCaptureDeps = {};
|
|
1439
|
+
let cursorCaptureDeps = {};
|
|
1440
|
+
// Onboarding is a config-driven capture path, so it must honor the Cursor
|
|
1441
|
+
// opt-in exactly like the daemon: if `cursor_composer` is in
|
|
1442
|
+
// `config.capture.sources`, this first pass authorizes Cursor discovery +
|
|
1443
|
+
// persistence WITHOUT the env flags (which exist only for ad-hoc dogfood
|
|
1444
|
+
// runs). Without this, onboarding would record cursor_composer in its ledger
|
|
1445
|
+
// row yet runAutoCapture would treat Cursor as disabled — capturing nothing.
|
|
1446
|
+
let cursorConfigEnabled = false;
|
|
1447
|
+
let enabledSources = DEFAULT_AUTO_CAPTURE_SOURCES;
|
|
1448
|
+
// Worker name for the honest preflight-failed warning below (defaults to the
|
|
1449
|
+
// codex_cli label until config resolves).
|
|
1450
|
+
let captureWorkerLabel = triageWorkerLabel("codex_cli");
|
|
1451
|
+
try {
|
|
1452
|
+
const { loadConfig } = await import("../../config/loader.js");
|
|
1453
|
+
const cfg = loadConfig(configPath);
|
|
1454
|
+
// Route this first capture pass through the configured triage worker —
|
|
1455
|
+
// runAutoCapture builds its provider from the process-wide configured name,
|
|
1456
|
+
// so without this a claude_code user's onboarding pass would silently fall
|
|
1457
|
+
// back to Codex. (Step 3 already set it; re-assert here so this function is
|
|
1458
|
+
// correct independent of call order.)
|
|
1459
|
+
const captureWorkerName = resolveTriageProviderName(cfg);
|
|
1460
|
+
setConfiguredTriageProviderName(captureWorkerName);
|
|
1461
|
+
captureWorkerLabel = triageWorkerLabel(captureWorkerName);
|
|
1462
|
+
codexCaptureDeps = resolveCodexCaptureDeps(cfg);
|
|
1463
|
+
cursorCaptureDeps = resolveCursorCaptureDeps(cfg);
|
|
1464
|
+
cursorConfigEnabled = cfg.capture.sources.includes(CURSOR_CAPTURE_SOURCE);
|
|
1465
|
+
enabledSources = cfg.capture.sources;
|
|
1466
|
+
}
|
|
1467
|
+
catch {
|
|
1468
|
+
codexCaptureDeps = {};
|
|
1469
|
+
cursorCaptureDeps = {};
|
|
1470
|
+
cursorConfigEnabled = false;
|
|
1471
|
+
}
|
|
1472
|
+
const startedAt = new Date();
|
|
1473
|
+
const captureSpin = new ui.Spinner("Chat import").start();
|
|
1474
|
+
try {
|
|
1475
|
+
const report = await runAutoCapture({
|
|
1476
|
+
dataDir,
|
|
1477
|
+
// Capture exactly the sources config enables — the same set the
|
|
1478
|
+
// scheduled daemon loop uses (buildDaemonAutoCaptureDeps passes
|
|
1479
|
+
// cfg.capture.sources) and the same set recorded in the ledger row
|
|
1480
|
+
// below. Omitting this falls back to DEFAULT_AUTO_CAPTURE_SOURCES, which
|
|
1481
|
+
// would let onboarding capture sources it never records — a status
|
|
1482
|
+
// ledger that says one thing while capture did another.
|
|
1483
|
+
sources: enabledSources,
|
|
1484
|
+
...codexCaptureDeps,
|
|
1485
|
+
...cursorCaptureDeps,
|
|
1486
|
+
cursorConfigEnabled,
|
|
1487
|
+
saveFn: async (saveOpts) => {
|
|
1488
|
+
const { data } = await client.post("/save", {
|
|
1489
|
+
source: saveOpts.source,
|
|
1490
|
+
summary: saveOpts.summary,
|
|
1491
|
+
content: saveOpts.content,
|
|
1492
|
+
topics: saveOpts.topics,
|
|
1493
|
+
...(saveOpts.decisions?.length
|
|
1494
|
+
? { decisions: saveOpts.decisions, user_statements_seal: saveOpts.user_statements_seal }
|
|
1495
|
+
: {}),
|
|
1496
|
+
domain: saveOpts.domain,
|
|
1497
|
+
idempotency_key: saveOpts.idempotency_key,
|
|
1498
|
+
...(saveOpts.replace_idempotency_key
|
|
1499
|
+
? { replace_idempotency_key: saveOpts.replace_idempotency_key }
|
|
1500
|
+
: {}),
|
|
1501
|
+
});
|
|
1502
|
+
const resp = data;
|
|
1503
|
+
if (resp.duplicate)
|
|
1504
|
+
return;
|
|
1505
|
+
const result = await pollJob({ get: client.get.bind(client), jobId: resp.job_id });
|
|
1506
|
+
if (isJobFailure(result.job, result.timedOut)) {
|
|
1507
|
+
throw new Error(`Save job failed: ${result.job.error ?? "unknown"}`);
|
|
1508
|
+
}
|
|
1509
|
+
},
|
|
1510
|
+
});
|
|
1511
|
+
// Record this onboard run in the health ledger (best-effort). Without
|
|
1512
|
+
// it, a friend who finishes onboarding and immediately runs `rift
|
|
1513
|
+
// status`/`onadit doctor` sees "no runs yet" — the exact stale-health
|
|
1514
|
+
// trust break this ledger exists to prevent, at the moment it matters
|
|
1515
|
+
// most. Tagged `onboard` so it sets freshness without ever speaking for
|
|
1516
|
+
// the scheduled daemon loop.
|
|
1517
|
+
try {
|
|
1518
|
+
await appendAutoCaptureRunRecord(dataDir, buildAutoCaptureRunRecord({
|
|
1519
|
+
timestamp: startedAt.toISOString(),
|
|
1520
|
+
enabledSources,
|
|
1521
|
+
durationMs: Date.now() - startedAt.getTime(),
|
|
1522
|
+
report,
|
|
1523
|
+
trigger: "onboard",
|
|
1524
|
+
}));
|
|
1525
|
+
}
|
|
1526
|
+
catch (observabilityErr) {
|
|
1527
|
+
process.stderr.write(`Onboard capture run-record append failed: ${observabilityErr instanceof Error
|
|
1528
|
+
? observabilityErr.message
|
|
1529
|
+
: String(observabilityErr)}\n`);
|
|
1530
|
+
}
|
|
1531
|
+
const base = `${report.saved} added · ${report.review} to review`;
|
|
1532
|
+
// A preflight failure (triage-worker health probe) carries 0 per-session
|
|
1533
|
+
// errors, so an `errors > 0` check alone would `succeed()` on a run where
|
|
1534
|
+
// triage never ran — telling a friend everything's fine when it isn't.
|
|
1535
|
+
// Treat a failed probe as a warning too, and surface why.
|
|
1536
|
+
if (!report.preflight_ok) {
|
|
1537
|
+
captureSpin.warn("Chat import", `${base} · ${captureWorkerLabel} preflight failed${report.preflight_error ? ` (${report.preflight_error})` : ""}`);
|
|
1538
|
+
}
|
|
1539
|
+
else if (report.errors > 0) {
|
|
1540
|
+
captureSpin.warn("Chat import", `${base} · ${report.errors} error(s)`);
|
|
1541
|
+
}
|
|
1542
|
+
else {
|
|
1543
|
+
captureSpin.succeed("Chat import", base);
|
|
1544
|
+
}
|
|
1545
|
+
return { saved: report.saved, reviewed: report.review };
|
|
1546
|
+
}
|
|
1547
|
+
catch (err) {
|
|
1548
|
+
captureSpin.fail("Chat import", `error (non-fatal): ${err instanceof Error ? err.message : String(err)}`);
|
|
1549
|
+
return { saved: 0, reviewed: 0 };
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
// ----- Step 8: import -----
|
|
1553
|
+
async function collectImportPath(opts, rl, capture) {
|
|
1554
|
+
// opts.importExport is `string | false | undefined` — Commander writes
|
|
1555
|
+
// `false` when `--no-import-export` was passed, but the caller already
|
|
1556
|
+
// short-circuits in that case. Only a non-empty string is a real path.
|
|
1557
|
+
if (typeof opts.importExport === "string" && opts.importExport.length > 0) {
|
|
1558
|
+
return opts.importExport;
|
|
1559
|
+
}
|
|
1560
|
+
if (opts.yes)
|
|
1561
|
+
return null;
|
|
1562
|
+
// Explain what a path is, where to find the export, why it's optional,
|
|
1563
|
+
// and that ChatGPT is the only inline source — BEFORE the prompt. A beta
|
|
1564
|
+
// user who has never exported a chat archive shouldn't hit a bare
|
|
1565
|
+
// "Drop a path" prompt. The source-sniffer below still fail-fasts a
|
|
1566
|
+
// non-ChatGPT export, but the friend learns the boundary earlier here.
|
|
1567
|
+
//
|
|
1568
|
+
// The "skipping is fine" reassurance MUST match what will actually happen.
|
|
1569
|
+
// Auto-capture is OFF by default, AND can be configured-on-but-not-running
|
|
1570
|
+
// when the triage-worker preflight failed — promising "new chats get captured
|
|
1571
|
+
// automatically" in either case is the exact trust break this flow removes.
|
|
1572
|
+
// Only promise it when capture will genuinely run; otherwise point at the
|
|
1573
|
+
// real next step (fix the triage worker if configured, or enable it if off).
|
|
1574
|
+
const whySkip = capture.willRun
|
|
1575
|
+
? "skipping is fine — new chats get captured automatically."
|
|
1576
|
+
: capture.configured
|
|
1577
|
+
? `skipping is fine — import later with \`onadit import\` (auto-capture is on but won't run until ${capture.workerLabel} is ready — see above).`
|
|
1578
|
+
: "skipping is fine — import later with `onadit import`, or turn on auto-capture with `onadit onboard --enable-capture`.";
|
|
1579
|
+
ui.detail([
|
|
1580
|
+
"Import past chats (optional)",
|
|
1581
|
+
"• What: the file path to a ChatGPT data export (a .zip or .json on your Mac), e.g. ~/Downloads/chatgpt-export.zip.",
|
|
1582
|
+
"• Where: chatgpt.com → Settings → Data controls → Export data, then unzip nothing — just drop the downloaded file's path here.",
|
|
1583
|
+
`• Why: this seeds Onadit with your existing ChatGPT history so search works on day one; ${whySkip}`,
|
|
1584
|
+
"• Other tools: Claude/Grok/Gemini exports aren't imported here. Run them later with: onadit import <path> --source claude_web|grok_web|gemini_web",
|
|
1585
|
+
].join("\n"));
|
|
1586
|
+
const answer = (await ask(rl, "Path to a ChatGPT export to import now (or `skip`): ", "skip")).trim();
|
|
1587
|
+
if (!answer || answer.toLowerCase() === "skip")
|
|
1588
|
+
return null;
|
|
1589
|
+
return answer;
|
|
1590
|
+
}
|
|
1591
|
+
/**
|
|
1592
|
+
* Inline import is intentionally narrow: ChatGPT exports only.
|
|
1593
|
+
* Friends importing Claude / Grok / Gemini exports during onboarding
|
|
1594
|
+
* are pointed at the standalone `onadit import <path> --source <name>`
|
|
1595
|
+
* command (Slice 3), which adds structural sniffing per source.
|
|
1596
|
+
*/
|
|
1597
|
+
export async function runImport(filePath, configPath, source) {
|
|
1598
|
+
const unquoted = filePath.trim().replace(/^(["'])(.*)\1$/, "$2");
|
|
1599
|
+
const absolute = path.resolve(unquoted.startsWith("~/") ? path.join(os.homedir(), unquoted.slice(2)) : unquoted);
|
|
1600
|
+
if (!fs.existsSync(absolute)) {
|
|
1601
|
+
say(`Import file not found: ${absolute}`);
|
|
1602
|
+
return { kind: "skipped", reason: "file not found" };
|
|
1603
|
+
}
|
|
1604
|
+
const ext = path.extname(absolute).toLowerCase();
|
|
1605
|
+
if (!SUPPORTED_INGEST_EXTENSIONS.has(ext)) {
|
|
1606
|
+
say(`Unsupported file extension "${ext}". Allowed: .json, .zip.`);
|
|
1607
|
+
return { kind: "skipped", reason: "unsupported extension" };
|
|
1608
|
+
}
|
|
1609
|
+
const baseUrl = safeResolveBaseUrl(configPath);
|
|
1610
|
+
const tokenResult = await ensureToken(configPath);
|
|
1611
|
+
if (!baseUrl || !tokenResult.ok) {
|
|
1612
|
+
say("Daemon not reachable — cannot import.");
|
|
1613
|
+
return { kind: "skipped", reason: "daemon not reachable" };
|
|
1614
|
+
}
|
|
1615
|
+
const client = createHttpClient({ baseUrl, token: tokenResult.token });
|
|
1616
|
+
const buf = fs.readFileSync(absolute);
|
|
1617
|
+
// Fail-fast for non-ChatGPT exports: route them to `onadit import` with
|
|
1618
|
+
// the right `--source` flag instead of silently feeding them through
|
|
1619
|
+
// the ChatGPT parser. Returns null on "looks like ChatGPT or unknown,"
|
|
1620
|
+
// a concrete provider otherwise.
|
|
1621
|
+
const sniffed = sniffInboxSource(path.basename(absolute), buf);
|
|
1622
|
+
if (sniffed && !source) {
|
|
1623
|
+
say(`Detected a ${sniffed} export — the inline importer is ChatGPT-only.`);
|
|
1624
|
+
say(`Run: onadit import "${absolute}" --source ${sniffed}`);
|
|
1625
|
+
return { kind: "skipped", reason: `non-chatgpt export detected: ${sniffed}` };
|
|
1626
|
+
}
|
|
1627
|
+
const form = new FormData();
|
|
1628
|
+
const importSource = source === "auto" ? sniffed ?? ONBOARD_INLINE_IMPORT_SOURCE : source ?? ONBOARD_INLINE_IMPORT_SOURCE;
|
|
1629
|
+
form.append("source", importSource);
|
|
1630
|
+
form.append("file", new Blob([buf]), path.basename(absolute));
|
|
1631
|
+
say(`Importing ${path.basename(absolute)} as ${importSource}…`);
|
|
1632
|
+
const { data } = await client.postMultipart("/ingest", form);
|
|
1633
|
+
const resp = data;
|
|
1634
|
+
if (resp.duplicate) {
|
|
1635
|
+
say(`Import already submitted (job ${resp.job_id}).`);
|
|
1636
|
+
}
|
|
1637
|
+
const result = await pollJob({ get: client.get.bind(client), jobId: resp.job_id });
|
|
1638
|
+
if (result.timedOut) {
|
|
1639
|
+
return { kind: "failed", reason: `Import is still running. Check its status with: onadit status (job ${resp.job_id}). Do not start another import.` };
|
|
1640
|
+
}
|
|
1641
|
+
if (isJobFailure(result.job, false)) {
|
|
1642
|
+
const reason = result.job.error ?? "unknown";
|
|
1643
|
+
say(`Import failed: ${reason}`);
|
|
1644
|
+
return { kind: "failed", reason };
|
|
1645
|
+
}
|
|
1646
|
+
say(`Import complete (job ${resp.job_id}).`);
|
|
1647
|
+
return { kind: "imported", jobId: resp.job_id, ...(result.job.import_summary ? { summary: result.job.import_summary } : {}) };
|
|
1648
|
+
}
|
|
1649
|
+
/**
|
|
1650
|
+
* Pure outcome decision for the next-action card.
|
|
1651
|
+
*
|
|
1652
|
+
* "Complete + verified" needs both: this run ingested user data AND a recall
|
|
1653
|
+
* query returned a hit (smoke is non-indexing — see daemonRefreshFlow — so a
|
|
1654
|
+
* hit is real user data, not a leftover probe).
|
|
1655
|
+
*
|
|
1656
|
+
* But an EMPTY archive is not a failure. By default capture is OFF (zero-Codex
|
|
1657
|
+
* trust default) and import is optional, so a friend who simply presses Enter
|
|
1658
|
+
* past the import prompt has finished a valid setup — they just have nothing to
|
|
1659
|
+
* recall yet. That case (`ingestAttempted === false`) is reported as DONE, not
|
|
1660
|
+
* "incomplete". We only flag a retry when an ingest was actually attempted and
|
|
1661
|
+
* still added nothing.
|
|
1662
|
+
*/
|
|
1663
|
+
export function decideOnboardOutcome(input) {
|
|
1664
|
+
const { ingestedAny, recall } = input;
|
|
1665
|
+
const ingestAttempted = input.ingestAttempted ?? false;
|
|
1666
|
+
if (ingestedAny && recall.ok && recall.hits > 0) {
|
|
1667
|
+
return [
|
|
1668
|
+
`Setup complete — recall returned ${recall.hits} result(s) from your data.`,
|
|
1669
|
+
"Next: onadit mcp install --client=claude-desktop",
|
|
1670
|
+
];
|
|
1671
|
+
}
|
|
1672
|
+
if (!ingestedAny) {
|
|
1673
|
+
if (!ingestAttempted) {
|
|
1674
|
+
// Deliberate empty setup — a finished, valid state, not a failure.
|
|
1675
|
+
return [
|
|
1676
|
+
"Setup complete — nothing imported yet.",
|
|
1677
|
+
"Import later: onadit import <path> --source <name>",
|
|
1678
|
+
"Next: onadit mcp install --client=claude-desktop",
|
|
1679
|
+
];
|
|
1680
|
+
}
|
|
1681
|
+
// An ingest ran but landed nothing — honest, calm retry guidance.
|
|
1682
|
+
return [
|
|
1683
|
+
"Setup ready — but capture/import added nothing this run.",
|
|
1684
|
+
"Try: onadit import <path> --source <name>",
|
|
1685
|
+
"If it repeats: onadit feedback --kind=broke --with-status",
|
|
1686
|
+
];
|
|
1687
|
+
}
|
|
1688
|
+
if (recall.ok) {
|
|
1689
|
+
return [
|
|
1690
|
+
"Setup ready — capture+import succeeded but the generic recall query returned 0 hits.",
|
|
1691
|
+
"Indexing may still be in flight, or this query just didn't match your archive yet.",
|
|
1692
|
+
"Try: onadit search \"<a topic you remember discussing>\"",
|
|
1693
|
+
"If repeated tries still return nothing, run: onadit feedback --kind=broke --with-status",
|
|
1694
|
+
];
|
|
1695
|
+
}
|
|
1696
|
+
return [
|
|
1697
|
+
`Setup partially complete — recall check failed: ${recall.reason}`,
|
|
1698
|
+
"Next: onadit status (then: onadit feedback --kind=broke --with-status)",
|
|
1699
|
+
];
|
|
1700
|
+
}
|
|
1701
|
+
// ----- Step 9: first-recall verification -----
|
|
1702
|
+
async function firstRecallCheck(configPath) {
|
|
1703
|
+
const baseUrl = safeResolveBaseUrl(configPath);
|
|
1704
|
+
const tokenResult = await ensureToken(configPath);
|
|
1705
|
+
if (!baseUrl || !tokenResult.ok) {
|
|
1706
|
+
return { ok: false, reason: "daemon not reachable" };
|
|
1707
|
+
}
|
|
1708
|
+
const client = createHttpClient({ baseUrl, token: tokenResult.token });
|
|
1709
|
+
// Generic, content-bearing query — likely to land on captured CLI
|
|
1710
|
+
// sessions or imported chat exports without targeting any onboarding
|
|
1711
|
+
// marker. The smoke is non-indexing (see daemonRefreshFlow), so any
|
|
1712
|
+
// hit returned here is real user data, not a leftover probe.
|
|
1713
|
+
//
|
|
1714
|
+
// Small unusual imports (e.g. one short Claude project export) can
|
|
1715
|
+
// return 0 hits for this generic query even when the data was
|
|
1716
|
+
// indexed correctly. Retry briefly so the post-onboard nudge isn't a
|
|
1717
|
+
// false alarm on slow embedders / sparse archives.
|
|
1718
|
+
const RECALL_RETRY_DELAYS_MS = [0, 5_000];
|
|
1719
|
+
let lastErr = null;
|
|
1720
|
+
for (const delay of RECALL_RETRY_DELAYS_MS) {
|
|
1721
|
+
if (delay > 0)
|
|
1722
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
1723
|
+
try {
|
|
1724
|
+
const { data } = await client.post("/search", {
|
|
1725
|
+
query: "recent conversation",
|
|
1726
|
+
scope: "all",
|
|
1727
|
+
top_k: 10,
|
|
1728
|
+
});
|
|
1729
|
+
const body = data;
|
|
1730
|
+
const hits = (body.results?.length ?? body.hits?.length ?? 0);
|
|
1731
|
+
if (hits > 0)
|
|
1732
|
+
return { ok: true, hits };
|
|
1733
|
+
lastErr = null;
|
|
1734
|
+
}
|
|
1735
|
+
catch (err) {
|
|
1736
|
+
lastErr = err instanceof Error ? err.message : String(err);
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
if (lastErr)
|
|
1740
|
+
return { ok: false, reason: lastErr };
|
|
1741
|
+
return { ok: true, hits: 0 };
|
|
1742
|
+
}
|
|
1743
|
+
// ----- Slice 6: --reconfigure-voyage -----
|
|
1744
|
+
/**
|
|
1745
|
+
* Reconfigure the Voyage key. ALL of (kickstart succeeds, /health 200,
|
|
1746
|
+
* smoke `voyage_key_present=true`) must pass; any failure rolls back the
|
|
1747
|
+
* env file AND restores the previous in-process VOYAGE_API_KEY so a
|
|
1748
|
+
* caller is never left with a half-replaced credential. `agent_not_loaded`
|
|
1749
|
+
* counts as a failure here (unlike initial onboard) because reconfigure
|
|
1750
|
+
* is, by definition, called against a daemon that should already be
|
|
1751
|
+
* running — if the launchd label is gone, we don't trust the new key.
|
|
1752
|
+
*/
|
|
1753
|
+
async function reconfigureVoyageFlow(opts, globalOpts, rl) {
|
|
1754
|
+
say("Reconfiguring Voyage key…");
|
|
1755
|
+
const key = await collectVoyageKey(opts, rl);
|
|
1756
|
+
await performVoyageReconfigure({
|
|
1757
|
+
key,
|
|
1758
|
+
envPath: defaultRiftEnvPath(),
|
|
1759
|
+
refresh: () => daemonRefreshFlow(globalOpts),
|
|
1760
|
+
});
|
|
1761
|
+
}
|
|
1762
|
+
/**
|
|
1763
|
+
* Core reconfigure logic, extracted so tests can drive every failure
|
|
1764
|
+
* branch (validate fail, kickstart agent_not_loaded, /health fail,
|
|
1765
|
+
* voyage_key_present=false) without spinning up a real daemon.
|
|
1766
|
+
*
|
|
1767
|
+
* Contract: ALL of {validate ok, daemonRefresh ok} must succeed; any
|
|
1768
|
+
* failure rolls back the env file AND restores the previous in-process
|
|
1769
|
+
* VOYAGE_API_KEY. agent_not_loaded counts as a failure here — the call
|
|
1770
|
+
* site only invokes this when the daemon should already be running.
|
|
1771
|
+
*/
|
|
1772
|
+
export async function performVoyageReconfigure(deps) {
|
|
1773
|
+
const validate = deps.validate ?? validateVoyageKey;
|
|
1774
|
+
const write = deps.write ?? writeEnvFile;
|
|
1775
|
+
const loadEnv = deps.loadEnv ?? loadRiftEnv;
|
|
1776
|
+
const emit = deps.emit ?? say;
|
|
1777
|
+
const validation = await validate({ apiKey: deps.key });
|
|
1778
|
+
if (!validation.ok) {
|
|
1779
|
+
throw new CliError(`Voyage validation failed: ${validation.reason}`, "validation");
|
|
1780
|
+
}
|
|
1781
|
+
emit(`Voyage key valid (last 4 …${validation.last4}).`);
|
|
1782
|
+
const previousInProcessKey = process.env["VOYAGE_API_KEY"];
|
|
1783
|
+
const writeResult = write({
|
|
1784
|
+
filePath: deps.envPath,
|
|
1785
|
+
key: "VOYAGE_API_KEY",
|
|
1786
|
+
value: deps.key,
|
|
1787
|
+
});
|
|
1788
|
+
loadEnv({ filePath: deps.envPath });
|
|
1789
|
+
const refresh = await deps.refresh();
|
|
1790
|
+
if (refresh.ok) {
|
|
1791
|
+
emit(`Voyage reconfigured (last 4 …${validation.last4}).`);
|
|
1792
|
+
return;
|
|
1793
|
+
}
|
|
1794
|
+
rollbackVoyageEnv(deps.envPath, writeResult, previousInProcessKey);
|
|
1795
|
+
throw new CliError(`Reconfigure failed — restored previous Voyage key (reason: ${refresh.reason}).`, "server_error");
|
|
1796
|
+
}
|
|
1797
|
+
/**
|
|
1798
|
+
* Restore a Voyage env file + in-process VOYAGE_API_KEY after a failed
|
|
1799
|
+
* write. Exported for testing — `reconfigureVoyageFlow` is the only
|
|
1800
|
+
* production caller. When `writeResult.backedUp` is true, the prior
|
|
1801
|
+
* env-file contents are copied back at mode 0600; when false (no prior
|
|
1802
|
+
* file existed), the file we just wrote is removed so we don't leave
|
|
1803
|
+
* a key paired with a now-rejected daemon respawn.
|
|
1804
|
+
*/
|
|
1805
|
+
export function rollbackVoyageEnv(envPath, writeResult, previousInProcessKey) {
|
|
1806
|
+
// 1. Env file.
|
|
1807
|
+
if (writeResult.backedUp && writeResult.backupPath) {
|
|
1808
|
+
try {
|
|
1809
|
+
fs.copyFileSync(writeResult.backupPath, envPath);
|
|
1810
|
+
fs.chmodSync(envPath, 0o600);
|
|
1811
|
+
}
|
|
1812
|
+
catch {
|
|
1813
|
+
say("Reconfigure rollback: env-file restore failed — ping Clem for recovery.");
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
else {
|
|
1817
|
+
// No prior env file — best effort: drop the line we just wrote so we
|
|
1818
|
+
// don't leave a key paired with a now-rejected daemon respawn.
|
|
1819
|
+
try {
|
|
1820
|
+
fs.rmSync(envPath, { force: true });
|
|
1821
|
+
}
|
|
1822
|
+
catch {
|
|
1823
|
+
// Best-effort.
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
// 2. In-process VOYAGE_API_KEY — restore prior value or delete.
|
|
1827
|
+
if (typeof previousInProcessKey === "string" && previousInProcessKey.length > 0) {
|
|
1828
|
+
process.env["VOYAGE_API_KEY"] = previousInProcessKey;
|
|
1829
|
+
}
|
|
1830
|
+
else {
|
|
1831
|
+
delete process.env["VOYAGE_API_KEY"];
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
// ----- Helpers -----
|
|
1835
|
+
function ask(rl, question, fallback) {
|
|
1836
|
+
return new Promise((resolve) => {
|
|
1837
|
+
rl.question(question, (answer) => {
|
|
1838
|
+
resolve(answer || fallback || "");
|
|
1839
|
+
});
|
|
1840
|
+
});
|
|
1841
|
+
}
|
|
1842
|
+
function say(line) {
|
|
1843
|
+
process.stdout.write(line + "\n");
|
|
1844
|
+
}
|
|
1845
|
+
function sayPrivacyContract() {
|
|
1846
|
+
ui.detail([
|
|
1847
|
+
"• Conversation content stays local (LanceDB + raw transcripts).",
|
|
1848
|
+
"• Indexed conversation text — and your search queries — leave the machine",
|
|
1849
|
+
" for embedding only when a Voyage key is configured (Voyage AI).",
|
|
1850
|
+
"• The Voyage key sits in ~/.rift.env (mode 0600), is sent only to Voyage,",
|
|
1851
|
+
" and is never logged or sent to Clem.",
|
|
1852
|
+
"• Feedback is stored locally as JSONL. Relay is opt-in: explicit notes only,",
|
|
1853
|
+
" plus daemon health booleans — no paths, no content, no key bytes.",
|
|
1854
|
+
"• Full contract: https://onadit.com/privacy",
|
|
1855
|
+
].join("\n"));
|
|
1856
|
+
}
|
|
1857
|
+
function safeDiscover(fn) {
|
|
1858
|
+
try {
|
|
1859
|
+
return fn().length;
|
|
1860
|
+
}
|
|
1861
|
+
catch {
|
|
1862
|
+
return 0;
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
function safeResolveBaseUrl(configPath) {
|
|
1866
|
+
try {
|
|
1867
|
+
return resolveBaseUrl(configPath);
|
|
1868
|
+
}
|
|
1869
|
+
catch {
|
|
1870
|
+
return null;
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
//# sourceMappingURL=onboard.js.map
|