@vellumai/assistant 0.3.4 → 0.3.6
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/Dockerfile +2 -0
- package/README.md +88 -2
- package/eslint.config.mjs +31 -0
- package/package.json +1 -1
- package/scripts/ipc/check-swift-decoder-drift.ts +4 -1
- package/scripts/ipc/generate-swift.ts +31 -2
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +438 -1
- package/src/__tests__/approval-conversation-turn.test.ts +214 -0
- package/src/__tests__/approval-hardcoded-copy-guard.test.ts +41 -0
- package/src/__tests__/approval-message-composer.test.ts +253 -0
- package/src/__tests__/browser-manager.test.ts +1 -0
- package/src/__tests__/call-conversation-messages.test.ts +130 -0
- package/src/__tests__/call-domain.test.ts +12 -2
- package/src/__tests__/call-orchestrator.test.ts +799 -249
- package/src/__tests__/call-pointer-messages.test.ts +148 -0
- package/src/__tests__/call-recovery.test.ts +3 -0
- package/src/__tests__/call-routes-http.test.ts +32 -2
- package/src/__tests__/call-store.test.ts +3 -0
- package/src/__tests__/channel-approval-routes.test.ts +1277 -98
- package/src/__tests__/channel-approval.test.ts +37 -0
- package/src/__tests__/channel-approvals.test.ts +36 -50
- package/src/__tests__/channel-guardian.test.ts +630 -22
- package/src/__tests__/channel-readiness-service.test.ts +324 -0
- package/src/__tests__/checker.test.ts +14 -7
- package/src/__tests__/clarification-resolver.test.ts +44 -24
- package/src/__tests__/commit-message-enrichment-service.test.ts +9 -4
- package/src/__tests__/computer-use-session-working-dir.test.ts +8 -0
- package/src/__tests__/config-schema.test.ts +14 -8
- package/src/__tests__/context-window-manager.test.ts +30 -2
- package/src/__tests__/contradiction-checker.test.ts +20 -5
- package/src/__tests__/credential-security-invariants.test.ts +7 -2
- package/src/__tests__/daemon-lifecycle.test.ts +13 -12
- package/src/__tests__/db-migration-rollback.test.ts +752 -0
- package/src/__tests__/dictation-mode-detection.test.ts +63 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -0
- package/src/__tests__/entity-search.test.ts +615 -0
- package/src/__tests__/fuzzy-match-property.test.ts +5 -5
- package/src/__tests__/guardian-action-store.test.ts +123 -0
- package/src/__tests__/guardian-action-sweep.test.ts +277 -0
- package/src/__tests__/guardian-dispatch.test.ts +389 -0
- package/src/__tests__/guardian-question-copy.test.ts +47 -0
- package/src/__tests__/handlers-telegram-config.test.ts +4 -2
- package/src/__tests__/handlers-twilio-config.test.ts +533 -0
- package/src/__tests__/intent-routing.test.ts +2 -0
- package/src/__tests__/ipc-snapshot.test.ts +291 -1
- package/src/__tests__/memory-upsert-concurrency.test.ts +828 -0
- package/src/__tests__/messaging-send-tool.test.ts +65 -0
- package/src/__tests__/model-intents.test.ts +96 -0
- package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +42 -0
- package/src/__tests__/oauth2-gateway-transport.test.ts +130 -0
- package/src/__tests__/onboarding-starter-tasks.test.ts +2 -0
- package/src/__tests__/provider-commit-message-generator.test.ts +89 -13
- package/src/__tests__/provider-error-scenarios.test.ts +621 -0
- package/src/__tests__/provider-fail-open-selection.test.ts +119 -0
- package/src/__tests__/qdrant-manager.test.ts +27 -20
- package/src/__tests__/relay-server.test.ts +779 -40
- package/src/__tests__/run-orchestrator-assistant-events.test.ts +6 -0
- package/src/__tests__/run-orchestrator.test.ts +42 -4
- package/src/__tests__/runtime-runs-http.test.ts +17 -1
- package/src/__tests__/runtime-runs.test.ts +16 -0
- package/src/__tests__/schedule-store.test.ts +18 -4
- package/src/__tests__/scheduler-recurrence.test.ts +13 -4
- package/src/__tests__/session-abort-tool-results.test.ts +6 -0
- package/src/__tests__/session-agent-loop.test.ts +857 -0
- package/src/__tests__/session-conflict-gate.test.ts +6 -0
- package/src/__tests__/session-pre-run-repair.test.ts +6 -0
- package/src/__tests__/session-profile-injection.test.ts +6 -0
- package/src/__tests__/session-provider-retry-repair.test.ts +6 -0
- package/src/__tests__/session-queue.test.ts +6 -0
- package/src/__tests__/session-runtime-assembly.test.ts +321 -13
- package/src/__tests__/session-slash-known.test.ts +6 -0
- package/src/__tests__/session-slash-queue.test.ts +6 -0
- package/src/__tests__/session-slash-unknown.test.ts +6 -0
- package/src/__tests__/session-surfaces-task-progress.test.ts +2 -0
- package/src/__tests__/session-tool-setup-app-refresh.test.ts +1 -0
- package/src/__tests__/session-tool-setup-memory-scope.test.ts +1 -0
- package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +1 -0
- package/src/__tests__/session-workspace-injection.test.ts +6 -0
- package/src/__tests__/session-workspace-tool-tracking.test.ts +6 -0
- package/src/__tests__/skills.test.ts +2 -0
- package/src/__tests__/sms-messaging-provider.test.ts +126 -0
- package/src/__tests__/starter-task-flow.test.ts +2 -0
- package/src/__tests__/swarm-dag-pathological.test.ts +535 -0
- package/src/__tests__/system-prompt.test.ts +2 -0
- package/src/__tests__/task-management-tools.test.ts +2 -2
- package/src/__tests__/task-runner.test.ts +14 -4
- package/src/__tests__/terminal-tools.test.ts +25 -19
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +545 -0
- package/src/__tests__/tool-executor-shell-integration.test.ts +11 -11
- package/src/__tests__/tool-executor.test.ts +23 -24
- package/src/__tests__/trust-store.test.ts +3 -3
- package/src/__tests__/twilio-rest.test.ts +29 -0
- package/src/__tests__/twilio-routes-elevenlabs.test.ts +3 -0
- package/src/__tests__/twilio-routes-twiml.test.ts +11 -0
- package/src/__tests__/twilio-routes.test.ts +167 -11
- package/src/__tests__/twitter-cli-error-shaping.test.ts +2 -2
- package/src/__tests__/user-reference.test.ts +2 -0
- package/src/__tests__/voice-quality.test.ts +222 -0
- package/src/__tests__/web-search.test.ts +46 -30
- package/src/__tests__/work-item-output.test.ts +110 -0
- package/src/agent/loop.ts +1 -1
- package/src/agent-heartbeat/agent-heartbeat-service.ts +2 -10
- package/src/amazon/client.ts +1418 -0
- package/src/amazon/request-extractor.ts +135 -0
- package/src/amazon/session.ts +109 -0
- package/src/autonomy/autonomy-store.ts +5 -5
- package/src/browser-extension-relay/client.ts +124 -0
- package/src/browser-extension-relay/protocol.ts +63 -0
- package/src/browser-extension-relay/server.ts +177 -0
- package/src/bundler/app-bundler.ts +3 -3
- package/src/bundler/bundle-signer.ts +1 -1
- package/src/bundler/signature-verifier.ts +1 -1
- package/src/calls/call-conversation-messages.ts +33 -0
- package/src/calls/call-domain.ts +114 -10
- package/src/calls/call-orchestrator.ts +268 -59
- package/src/calls/call-pointer-messages.ts +53 -0
- package/src/calls/call-recovery.ts +3 -8
- package/src/calls/call-store.ts +69 -87
- package/src/calls/elevenlabs-config.ts +3 -2
- package/src/calls/guardian-action-sweep.ts +105 -0
- package/src/calls/guardian-dispatch.ts +203 -0
- package/src/calls/guardian-question-copy.ts +133 -0
- package/src/calls/relay-server.ts +466 -8
- package/src/calls/speaker-identification.ts +1 -1
- package/src/calls/twilio-config.ts +22 -14
- package/src/calls/twilio-provider.ts +6 -4
- package/src/calls/twilio-rest.ts +308 -7
- package/src/calls/twilio-routes.ts +65 -12
- package/src/calls/types.ts +3 -1
- package/src/channels/types.ts +25 -0
- package/src/cli/amazon.ts +815 -0
- package/src/cli/config-commands.ts +2 -2
- package/src/cli/core-commands.ts +4 -3
- package/src/cli/influencer.ts +244 -0
- package/src/cli/map.ts +89 -6
- package/src/cli.ts +1 -1
- package/src/config/agent-schema.ts +171 -0
- package/src/config/bundled-skills/amazon/SKILL.md +127 -0
- package/src/config/bundled-skills/amazon/icon.svg +13 -0
- package/src/config/bundled-skills/api-mapping/SKILL.md +78 -0
- package/src/config/bundled-skills/browser/SKILL.md +1 -0
- package/src/config/bundled-skills/browser/TOOLS.json +17 -0
- package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +25 -0
- package/src/config/bundled-skills/doordash/SKILL.md +51 -51
- package/src/config/bundled-skills/email-setup/SKILL.md +14 -5
- package/src/config/bundled-skills/google-oauth-setup/SKILL.md +183 -0
- package/src/config/bundled-skills/influencer/SKILL.md +144 -0
- package/src/config/bundled-skills/knowledge-graph/SKILL.md +15 -0
- package/src/config/bundled-skills/knowledge-graph/TOOLS.json +56 -0
- package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +185 -0
- package/src/config/bundled-skills/macos-automation/icon.svg +12 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +176 -0
- package/src/config/bundled-skills/media-processing/TOOLS.json +230 -0
- package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +77 -0
- package/src/config/bundled-skills/media-processing/__tests__/cost-tracker.test.ts +69 -0
- package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +303 -0
- package/src/config/bundled-skills/media-processing/services/concurrency-pool.ts +55 -0
- package/src/config/bundled-skills/media-processing/services/cost-tracker.ts +86 -0
- package/src/config/bundled-skills/media-processing/services/gemini-map.ts +339 -0
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +551 -0
- package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +259 -0
- package/src/config/bundled-skills/media-processing/services/reduce.ts +197 -0
- package/src/config/bundled-skills/media-processing/tools/analyze-keyframes.ts +136 -0
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +59 -0
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +195 -0
- package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +197 -0
- package/src/config/bundled-skills/media-processing/tools/media-diagnostics.ts +143 -0
- package/src/config/bundled-skills/media-processing/tools/media-status.ts +75 -0
- package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +65 -0
- package/src/config/bundled-skills/messaging/SKILL.md +33 -8
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -7
- package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +2 -1
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +5 -1
- package/src/config/bundled-skills/phone-calls/SKILL.md +88 -23
- package/src/config/bundled-skills/twitter/SKILL.md +19 -3
- package/src/config/bundled-skills/twitter/icon.svg +14 -0
- package/src/config/bundled-tool-registry.ts +310 -0
- package/src/config/calls-schema.ts +181 -0
- package/src/config/core-schema.ts +309 -0
- package/src/config/defaults.ts +28 -3
- package/src/config/env-registry.ts +162 -0
- package/src/config/env.ts +175 -0
- package/src/config/loader.ts +6 -6
- package/src/config/memory-schema.ts +528 -0
- package/src/config/sandbox-schema.ts +55 -0
- package/src/config/schema.ts +158 -1133
- package/src/config/skill-state.ts +1 -1
- package/src/config/skills-schema.ts +32 -0
- package/src/config/skills.ts +35 -24
- package/src/config/system-prompt.ts +131 -56
- package/src/config/templates/IDENTITY.md +2 -2
- package/src/config/templates/SOUL.md +1 -1
- package/src/config/types.ts +1 -0
- package/src/config/user-reference.ts +4 -9
- package/src/config/vellum-skills/catalog.json +6 -7
- package/src/config/vellum-skills/chatgpt-import/tools/chatgpt-import.ts +5 -1
- package/src/config/vellum-skills/slack-oauth-setup/SKILL.md +4 -3
- package/src/config/vellum-skills/sms-setup/SKILL.md +216 -0
- package/src/config/vellum-skills/twilio-setup/SKILL.md +40 -8
- package/src/context/window-manager.ts +27 -7
- package/src/daemon/approval-generators.ts +186 -0
- package/src/daemon/approved-devices-store.ts +140 -0
- package/src/daemon/assistant-attachments.ts +1 -1
- package/src/daemon/classifier.ts +35 -32
- package/src/daemon/config-watcher.ts +1 -1
- package/src/daemon/daemon-control.ts +217 -0
- package/src/daemon/handlers/apps.ts +2 -3
- package/src/daemon/handlers/config-channels.ts +158 -0
- package/src/daemon/handlers/config-inbox.ts +540 -0
- package/src/daemon/handlers/config-ingress.ts +231 -0
- package/src/daemon/handlers/config-integrations.ts +258 -0
- package/src/daemon/handlers/config-model.ts +143 -0
- package/src/daemon/handlers/config-parental.ts +163 -0
- package/src/daemon/handlers/config-scheduling.ts +172 -0
- package/src/daemon/handlers/config-slack.ts +92 -0
- package/src/daemon/handlers/config-telegram.ts +301 -0
- package/src/daemon/handlers/config-tools.ts +177 -0
- package/src/daemon/handlers/config-trust.ts +104 -0
- package/src/daemon/handlers/config-twilio.ts +1080 -0
- package/src/daemon/handlers/config.ts +53 -1689
- package/src/daemon/handlers/diagnostics.ts +1 -1
- package/src/daemon/handlers/dictation.ts +180 -0
- package/src/daemon/handlers/documents.ts +18 -32
- package/src/daemon/handlers/identity.ts +14 -23
- package/src/daemon/handlers/index.ts +11 -0
- package/src/daemon/handlers/misc.ts +3 -5
- package/src/daemon/handlers/pairing.ts +98 -0
- package/src/daemon/handlers/sessions.ts +56 -5
- package/src/daemon/handlers/shared.ts +6 -1
- package/src/daemon/handlers/skills.ts +1 -1
- package/src/daemon/handlers/twitter-auth.ts +2 -0
- package/src/daemon/handlers/work-items.ts +17 -9
- package/src/daemon/handlers/workspace-files.ts +4 -3
- package/src/daemon/install-cli-launchers.ts +113 -0
- package/src/daemon/ipc-contract/apps.ts +356 -0
- package/src/daemon/ipc-contract/browser.ts +74 -0
- package/src/daemon/ipc-contract/computer-use.ts +151 -0
- package/src/daemon/ipc-contract/diagnostics.ts +56 -0
- package/src/daemon/ipc-contract/documents.ts +74 -0
- package/src/daemon/ipc-contract/inbox.ts +209 -0
- package/src/daemon/ipc-contract/integrations.ts +284 -0
- package/src/daemon/ipc-contract/memory.ts +48 -0
- package/src/daemon/ipc-contract/messages.ts +211 -0
- package/src/daemon/ipc-contract/pairing.ts +45 -0
- package/src/daemon/ipc-contract/parental-control.ts +95 -0
- package/src/daemon/ipc-contract/schedules.ts +97 -0
- package/src/daemon/ipc-contract/sessions.ts +315 -0
- package/src/daemon/ipc-contract/shared.ts +42 -0
- package/src/daemon/ipc-contract/skills.ts +120 -0
- package/src/daemon/ipc-contract/subagents.ts +58 -0
- package/src/daemon/ipc-contract/surfaces.ts +250 -0
- package/src/daemon/ipc-contract/trust.ts +60 -0
- package/src/daemon/ipc-contract/work-items.ts +225 -0
- package/src/daemon/ipc-contract/workspace.ts +113 -0
- package/src/daemon/ipc-contract-inventory.json +70 -0
- package/src/daemon/ipc-contract-inventory.ts +55 -29
- package/src/daemon/ipc-contract.ts +229 -2426
- package/src/daemon/ipc-protocol.ts +1 -1
- package/src/daemon/ipc-validate.ts +7 -0
- package/src/daemon/lifecycle.ts +97 -377
- package/src/daemon/pairing-store.ts +177 -0
- package/src/daemon/providers-setup.ts +43 -0
- package/src/daemon/ride-shotgun-handler.ts +68 -3
- package/src/daemon/server.ts +66 -46
- package/src/daemon/session-agent-loop-handlers.ts +421 -0
- package/src/daemon/session-agent-loop.ts +117 -275
- package/src/daemon/session-dynamic-profile.ts +1 -1
- package/src/daemon/session-history.ts +1 -1
- package/src/daemon/session-media-retry.ts +1 -1
- package/src/daemon/session-messaging.ts +37 -2
- package/src/daemon/session-notifiers.ts +5 -25
- package/src/daemon/session-process.ts +99 -59
- package/src/daemon/session-queue-manager.ts +96 -4
- package/src/daemon/session-runtime-assembly.ts +199 -10
- package/src/daemon/session-surfaces.ts +19 -4
- package/src/daemon/session-tool-setup.ts +30 -30
- package/src/daemon/session-workspace.ts +1 -1
- package/src/daemon/session.ts +35 -2
- package/src/daemon/shutdown-handlers.ts +122 -0
- package/src/daemon/trace-emitter.ts +1 -1
- package/src/daemon/watch-handler.ts +36 -33
- package/src/doordash/cart-queries.ts +787 -0
- package/src/doordash/client.ts +144 -127
- package/src/doordash/order-queries.ts +85 -0
- package/src/doordash/queries.ts +10 -1308
- package/src/doordash/search-queries.ts +203 -0
- package/src/doordash/session.ts +3 -2
- package/src/doordash/store-queries.ts +246 -0
- package/src/doordash/types.ts +367 -0
- package/src/email/providers/agentmail.ts +2 -1
- package/src/email/providers/index.ts +3 -2
- package/src/email/service.ts +3 -2
- package/src/errors.ts +43 -0
- package/src/home-base/prebuilt/seed.ts +1 -1
- package/src/hooks/cli.ts +6 -5
- package/src/hooks/config.ts +6 -8
- package/src/hooks/discovery.ts +6 -5
- package/src/hooks/manager.ts +4 -3
- package/src/hooks/runner.ts +2 -2
- package/src/hooks/templates.ts +5 -5
- package/src/inbound/public-ingress-urls.ts +6 -4
- package/src/index.ts +4 -2
- package/src/influencer/client.ts +1104 -0
- package/src/instrument.ts +4 -3
- package/src/logfire.ts +4 -3
- package/src/memory/admin.ts +25 -35
- package/src/memory/attachments-store.ts +4 -7
- package/src/memory/channel-delivery-store.ts +30 -1
- package/src/memory/channel-guardian-store.ts +202 -2
- package/src/memory/clarification-resolver.ts +37 -33
- package/src/memory/conflict-store.ts +67 -61
- package/src/memory/contradiction-checker.ts +141 -117
- package/src/memory/conversation-store.ts +335 -51
- package/src/memory/db-connection.ts +27 -4
- package/src/memory/db-init.ts +265 -4
- package/src/memory/db.ts +14 -1
- package/src/memory/embedding-backend.ts +27 -5
- package/src/memory/embedding-ollama.ts +2 -1
- package/src/memory/entity-extractor.ts +38 -35
- package/src/memory/guardian-action-store.ts +430 -0
- package/src/memory/inbox-escalation-projection.ts +59 -0
- package/src/memory/inbox-thread-store.ts +218 -0
- package/src/memory/ingress-invite-store.ts +338 -0
- package/src/memory/ingress-member-store.ts +350 -0
- package/src/memory/items-extractor.ts +91 -97
- package/src/memory/job-handlers/index-maintenance.ts +3 -3
- package/src/memory/job-handlers/media-processing.ts +69 -0
- package/src/memory/job-handlers/summarization.ts +32 -26
- package/src/memory/job-utils.ts +3 -10
- package/src/memory/jobs-store.ts +8 -10
- package/src/memory/jobs-worker.ts +55 -36
- package/src/memory/media-store.ts +759 -0
- package/src/memory/migrations/001-job-deferrals.ts +45 -0
- package/src/memory/migrations/002-tool-invocations-fk.ts +43 -0
- package/src/memory/migrations/003-memory-fts-backfill.ts +24 -0
- package/src/memory/migrations/004-entity-relation-dedup.ts +87 -0
- package/src/memory/migrations/005-fingerprint-scope-unique.ts +80 -0
- package/src/memory/migrations/006-scope-salted-fingerprints.ts +62 -0
- package/src/memory/migrations/007-assistant-id-to-self.ts +254 -0
- package/src/memory/migrations/008-remove-assistant-id-columns.ts +208 -0
- package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +83 -0
- package/src/memory/migrations/010-ext-conv-bindings-channel-chat-unique.ts +56 -0
- package/src/memory/migrations/011-call-sessions-provider-sid-dedup.ts +63 -0
- package/src/memory/migrations/012-call-sessions-add-initiated-from.ts +19 -0
- package/src/memory/migrations/013-guardian-action-tables.ts +68 -0
- package/src/memory/migrations/014-backfill-inbox-thread-state.ts +76 -0
- package/src/memory/migrations/015-drop-active-search-index.ts +27 -0
- package/src/memory/migrations/016-memory-segments-indexes.ts +11 -0
- package/src/memory/migrations/017-memory-items-indexes.ts +10 -0
- package/src/memory/migrations/018-remaining-table-indexes.ts +13 -0
- package/src/memory/migrations/index.ts +24 -0
- package/src/memory/migrations/registry.ts +79 -0
- package/src/memory/migrations/validate-migration-state.ts +69 -0
- package/src/memory/qdrant-manager.ts +49 -8
- package/src/memory/query-builder.ts +1 -1
- package/src/memory/raw-query.ts +119 -0
- package/src/memory/recall-cache.ts +4 -1
- package/src/memory/retriever.ts +165 -47
- package/src/memory/schema-migration.ts +25 -984
- package/src/memory/schema.ts +228 -7
- package/src/memory/search/entity.ts +205 -31
- package/src/memory/search/lexical.ts +81 -52
- package/src/memory/search/ranking.ts +27 -23
- package/src/memory/search/semantic.ts +157 -19
- package/src/memory/search/types.ts +24 -0
- package/src/memory/shared-app-links-store.ts +4 -5
- package/src/memory/validation.ts +19 -0
- package/src/messaging/draft-store.ts +5 -6
- package/src/messaging/provider-types.ts +2 -0
- package/src/messaging/providers/sms/adapter.ts +201 -0
- package/src/messaging/providers/sms/client.ts +93 -0
- package/src/messaging/providers/sms/types.ts +7 -0
- package/src/messaging/providers/telegram-bot/adapter.ts +2 -5
- package/src/messaging/providers/whatsapp/adapter.ts +136 -0
- package/src/messaging/providers/whatsapp/client.ts +67 -0
- package/src/messaging/style-analyzer.ts +5 -4
- package/src/messaging/thread-summarizer.ts +61 -69
- package/src/messaging/triage-engine.ts +62 -71
- package/src/migrations/config-merge.ts +53 -0
- package/src/migrations/data-layout.ts +68 -0
- package/src/migrations/data-merge.ts +33 -0
- package/src/migrations/hooks-merge.ts +90 -0
- package/src/migrations/index.ts +6 -0
- package/src/migrations/log.ts +23 -0
- package/src/migrations/skills-merge.ts +33 -0
- package/src/migrations/workspace-layout.ts +79 -0
- package/src/permissions/checker.ts +133 -11
- package/src/permissions/prompter.ts +14 -0
- package/src/permissions/shell-identity.ts +31 -1
- package/src/permissions/trust-store.ts +21 -1
- package/src/providers/anthropic/client.ts +4 -4
- package/src/providers/failover.ts +2 -2
- package/src/providers/model-intents.ts +70 -0
- package/src/providers/ollama/client.ts +2 -1
- package/src/providers/provider-send-message.ts +176 -0
- package/src/providers/registry.ts +71 -30
- package/src/providers/retry.ts +35 -1
- package/src/providers/types.ts +12 -1
- package/src/runtime/approval-conversation-turn.ts +97 -0
- package/src/runtime/approval-message-composer.ts +253 -0
- package/src/runtime/channel-approval-parser.ts +36 -2
- package/src/runtime/channel-approvals.ts +11 -24
- package/src/runtime/channel-guardian-service.ts +88 -21
- package/src/runtime/channel-readiness-service.ts +418 -0
- package/src/runtime/channel-readiness-types.ts +35 -0
- package/src/runtime/channel-retry-sweep.ts +184 -0
- package/src/runtime/guardian-context-resolver.ts +108 -0
- package/src/runtime/http-server.ts +275 -717
- package/src/runtime/http-types.ts +59 -3
- package/src/runtime/middleware/auth.ts +116 -0
- package/src/runtime/middleware/error-handler.ts +33 -0
- package/src/runtime/middleware/twilio-validation.ts +127 -0
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/call-routes.ts +51 -7
- package/src/runtime/routes/channel-delivery-routes.ts +170 -0
- package/src/runtime/routes/channel-guardian-routes.ts +1191 -0
- package/src/runtime/routes/channel-inbound-routes.ts +1152 -0
- package/src/runtime/routes/channel-route-shared.ts +144 -0
- package/src/runtime/routes/channel-routes.ts +32 -1588
- package/src/runtime/routes/conversation-routes.ts +50 -7
- package/src/runtime/routes/events-routes.ts +2 -2
- package/src/runtime/routes/identity-routes.ts +126 -0
- package/src/runtime/routes/pairing-routes.ts +143 -0
- package/src/runtime/routes/run-routes.ts +15 -1
- package/src/runtime/run-orchestrator.ts +86 -35
- package/src/schedule/schedule-store.ts +36 -32
- package/src/schedule/scheduler.ts +3 -3
- package/src/security/encrypted-store.ts +5 -7
- package/src/security/oauth2.ts +45 -15
- package/src/security/parental-control-store.ts +183 -0
- package/src/security/secret-allowlist.ts +4 -3
- package/src/security/secret-scanner.ts +5 -5
- package/src/security/secure-keys.ts +1 -1
- package/src/security/token-manager.ts +3 -2
- package/src/services/vercel-deploy.ts +6 -2
- package/src/skills/tool-manifest.ts +3 -3
- package/src/skills/vellum-catalog-remote.ts +75 -16
- package/src/slack/slack-webhook.ts +2 -1
- package/src/swarm/orchestrator.ts +92 -1
- package/src/swarm/router-planner.ts +6 -9
- package/src/swarm/worker-prompts.ts +9 -12
- package/src/tasks/task-compiler.ts +19 -28
- package/src/tasks/task-runner.ts +1 -1
- package/src/tools/assets/materialize.ts +2 -2
- package/src/tools/assets/search.ts +15 -14
- package/src/tools/browser/__tests__/auth-detector.test.ts +1 -0
- package/src/tools/browser/auto-navigate.ts +1 -0
- package/src/tools/browser/browser-execution.ts +10 -1
- package/src/tools/browser/browser-manager.ts +119 -4
- package/src/tools/browser/network-recorder.ts +5 -0
- package/src/tools/calls/call-start.ts +1 -0
- package/src/tools/credentials/broker.ts +11 -2
- package/src/tools/credentials/metadata-store.ts +18 -14
- package/src/tools/credentials/post-connect-hooks.ts +61 -0
- package/src/tools/credentials/vault.ts +49 -23
- package/src/tools/execution-target.ts +11 -1
- package/src/tools/executor.ts +68 -9
- package/src/tools/host-terminal/cli-discover.ts +1 -1
- package/src/tools/network/script-proxy/http-forwarder.ts +1 -1
- package/src/tools/network/script-proxy/mitm-handler.ts +1 -1
- package/src/tools/network/script-proxy/server.ts +1 -1
- package/src/tools/network/script-proxy/session-manager.ts +6 -5
- package/src/tools/network/web-fetch.ts +18 -2
- package/src/tools/network/web-search.ts +8 -4
- package/src/tools/reminder/reminder-store.ts +14 -15
- package/src/tools/schedule/create.ts +1 -0
- package/src/tools/schedule/list.ts +2 -1
- package/src/tools/shared/filesystem/file-ops-service.ts +5 -7
- package/src/tools/skills/skill-script-runner.ts +24 -9
- package/src/tools/skills/skill-tool-factory.ts +1 -0
- package/src/tools/tasks/work-item-enqueue.ts +2 -2
- package/src/tools/terminal/evaluate-typescript.ts +21 -12
- package/src/tools/terminal/parser.ts +50 -0
- package/src/tools/types.ts +2 -0
- package/src/tools/watcher/delete.ts +6 -0
- package/src/tools/weather/service.ts +1 -1
- package/src/twitter/client.ts +190 -24
- package/src/twitter/router.ts +1 -1
- package/src/twitter/session.ts +4 -3
- package/src/util/clipboard.ts +1 -1
- package/src/util/errors.ts +65 -8
- package/src/util/fs.ts +40 -0
- package/src/util/json.ts +10 -0
- package/src/util/log-redact.ts +189 -0
- package/src/util/logger.ts +19 -17
- package/src/util/object.ts +3 -0
- package/src/util/platform.ts +105 -363
- package/src/util/pricing.ts +1 -1
- package/src/util/promise-guard.ts +1 -1
- package/src/util/retry.ts +19 -0
- package/src/util/row-mapper.ts +79 -0
- package/src/util/silently.ts +21 -0
- package/src/watcher/engine.ts +5 -1
- package/src/watcher/provider-types.ts +20 -0
- package/src/watcher/providers/github.ts +156 -0
- package/src/watcher/providers/gmail.ts +1 -0
- package/src/watcher/providers/google-calendar.ts +1 -0
- package/src/watcher/providers/linear.ts +460 -0
- package/src/watcher/providers/slack.ts +1 -0
- package/src/work-items/work-item-runner.ts +1 -1
- package/src/workspace/git-service.ts +1 -1
- package/src/workspace/provider-commit-message-generator.ts +51 -22
- package/src/__tests__/call-bridge.test.ts +0 -517
- package/src/__tests__/session-process-bridge.test.ts +0 -244
- package/src/calls/call-bridge.ts +0 -168
- package/src/config/vellum-skills/google-oauth-setup/SKILL.md +0 -199
|
@@ -0,0 +1,815 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI command group: `vellum amazon`
|
|
3
|
+
*
|
|
4
|
+
* Shop on Amazon and Amazon Fresh via the command line.
|
|
5
|
+
* All commands output JSON to stdout. Use --json for machine-readable output.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as net from 'node:net';
|
|
9
|
+
import { Command } from 'commander';
|
|
10
|
+
import {
|
|
11
|
+
loadSession,
|
|
12
|
+
saveSession,
|
|
13
|
+
importFromRecording,
|
|
14
|
+
clearSession,
|
|
15
|
+
} from '../amazon/session.js';
|
|
16
|
+
import {
|
|
17
|
+
extractRequests,
|
|
18
|
+
saveRequests,
|
|
19
|
+
} from '../amazon/request-extractor.js';
|
|
20
|
+
import {
|
|
21
|
+
loadRecording,
|
|
22
|
+
} from '../tools/browser/recording-store.js';
|
|
23
|
+
import {
|
|
24
|
+
search,
|
|
25
|
+
getProductDetails,
|
|
26
|
+
addToCart,
|
|
27
|
+
removeFromCart,
|
|
28
|
+
viewCart,
|
|
29
|
+
getFreshDeliverySlots,
|
|
30
|
+
selectFreshDeliverySlot,
|
|
31
|
+
getPaymentMethods,
|
|
32
|
+
getCheckoutSummary,
|
|
33
|
+
placeOrder,
|
|
34
|
+
SessionExpiredError,
|
|
35
|
+
} from '../amazon/client.js';
|
|
36
|
+
import { getSocketPath, readSessionToken } from '../util/platform.js';
|
|
37
|
+
import {
|
|
38
|
+
serialize,
|
|
39
|
+
createMessageParser,
|
|
40
|
+
} from '../daemon/ipc-protocol.js';
|
|
41
|
+
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Helpers
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
function output(data: unknown, json: boolean): void {
|
|
47
|
+
process.stdout.write(
|
|
48
|
+
json ? JSON.stringify(data) + '\n' : JSON.stringify(data, null, 2) + '\n',
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function outputError(message: string, code = 1): void {
|
|
53
|
+
output({ ok: false, error: message }, true);
|
|
54
|
+
process.exitCode = code;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function getJson(cmd: Command): boolean {
|
|
58
|
+
let c: Command | null = cmd;
|
|
59
|
+
while (c) {
|
|
60
|
+
if ((c.opts() as { json?: boolean }).json) return true;
|
|
61
|
+
c = c.parent;
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const SESSION_EXPIRED_MSG =
|
|
67
|
+
'Your Amazon session has expired. Please sign in to Amazon in Chrome — ' +
|
|
68
|
+
'the assistant will use Ride Shotgun to capture your session automatically.';
|
|
69
|
+
|
|
70
|
+
async function run(cmd: Command, fn: () => Promise<unknown>): Promise<void> {
|
|
71
|
+
try {
|
|
72
|
+
const result = await fn();
|
|
73
|
+
output(
|
|
74
|
+
{ ok: true, ...(result as Record<string, unknown>) },
|
|
75
|
+
getJson(cmd),
|
|
76
|
+
);
|
|
77
|
+
} catch (err) {
|
|
78
|
+
if (err instanceof SessionExpiredError) {
|
|
79
|
+
output(
|
|
80
|
+
{ ok: false, error: 'session_expired', message: SESSION_EXPIRED_MSG },
|
|
81
|
+
getJson(cmd),
|
|
82
|
+
);
|
|
83
|
+
process.exitCode = 1;
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
outputError(err instanceof Error ? err.message : String(err));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
// Command registration
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
|
|
94
|
+
export function registerAmazonCommand(program: Command): void {
|
|
95
|
+
const amz = program
|
|
96
|
+
.command('amazon')
|
|
97
|
+
.description(
|
|
98
|
+
'Shop on Amazon and Amazon Fresh. Requires a session imported from a Ride Shotgun recording.',
|
|
99
|
+
)
|
|
100
|
+
.option('--json', 'Machine-readable JSON output');
|
|
101
|
+
|
|
102
|
+
// =========================================================================
|
|
103
|
+
// login — import session from a recording
|
|
104
|
+
// =========================================================================
|
|
105
|
+
amz.command('login')
|
|
106
|
+
.description('Import an Amazon session from a Ride Shotgun recording')
|
|
107
|
+
.requiredOption(
|
|
108
|
+
'--recording <path>',
|
|
109
|
+
'Path to the recording JSON file',
|
|
110
|
+
)
|
|
111
|
+
.action(async (opts: { recording: string }, cmd: Command) => {
|
|
112
|
+
await run(cmd, async () => {
|
|
113
|
+
const session = importFromRecording(opts.recording);
|
|
114
|
+
return {
|
|
115
|
+
message: 'Session imported successfully',
|
|
116
|
+
cookieCount: session.cookies.length,
|
|
117
|
+
recordingId: session.recordingId,
|
|
118
|
+
};
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// =========================================================================
|
|
123
|
+
// logout — clear saved session
|
|
124
|
+
// =========================================================================
|
|
125
|
+
amz.command('logout')
|
|
126
|
+
.description('Clear the saved Amazon session')
|
|
127
|
+
.action((_opts: unknown, cmd: Command) => {
|
|
128
|
+
clearSession();
|
|
129
|
+
output({ ok: true, message: 'Session cleared' }, getJson(cmd));
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// =========================================================================
|
|
133
|
+
// refresh — start Ride Shotgun learn to capture fresh cookies
|
|
134
|
+
// =========================================================================
|
|
135
|
+
amz.command('refresh')
|
|
136
|
+
.description(
|
|
137
|
+
'Start a Ride Shotgun learn session to capture fresh Amazon cookies. ' +
|
|
138
|
+
'Opens amazon.com in a separate Chrome window — sign in when prompted. ' +
|
|
139
|
+
'Your existing Chrome and tabs are not affected.',
|
|
140
|
+
)
|
|
141
|
+
.option('--duration <seconds>', 'Recording duration in seconds', '180')
|
|
142
|
+
.action(async (opts: { duration: string }, cmd: Command) => {
|
|
143
|
+
const json = getJson(cmd);
|
|
144
|
+
const duration = parseInt(opts.duration, 10);
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
// Restore minimized Chrome window so user can see the login page
|
|
148
|
+
try { await restoreChromeWindow(); } catch { /* best-effort */ }
|
|
149
|
+
|
|
150
|
+
const result = await startLearnSession(duration);
|
|
151
|
+
if (result.recordingPath) {
|
|
152
|
+
const session = importFromRecording(result.recordingPath);
|
|
153
|
+
|
|
154
|
+
// Also extract and save captured request templates for self-healing
|
|
155
|
+
let requestsCaptured = 0;
|
|
156
|
+
try {
|
|
157
|
+
const recording = loadRecording(result.recordingId ?? '');
|
|
158
|
+
if (recording) {
|
|
159
|
+
const requests = extractRequests(recording);
|
|
160
|
+
if (requests.length > 0) {
|
|
161
|
+
saveRequests(requests);
|
|
162
|
+
requestsCaptured = requests.length;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
} catch {
|
|
166
|
+
// Non-fatal: request extraction is best-effort
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Best-effort: minimize Chrome window after capturing session
|
|
170
|
+
try {
|
|
171
|
+
await minimizeChromeWindow();
|
|
172
|
+
process.stderr.write('[amazon] Chrome window minimized\n');
|
|
173
|
+
} catch {
|
|
174
|
+
// Non-fatal: minimizing is best-effort
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
output(
|
|
178
|
+
{
|
|
179
|
+
ok: true,
|
|
180
|
+
message: 'Session refreshed successfully',
|
|
181
|
+
cookieCount: session.cookies.length,
|
|
182
|
+
recordingId: result.recordingId,
|
|
183
|
+
requestsCaptured,
|
|
184
|
+
},
|
|
185
|
+
json,
|
|
186
|
+
);
|
|
187
|
+
} else {
|
|
188
|
+
output(
|
|
189
|
+
{
|
|
190
|
+
ok: false,
|
|
191
|
+
error: 'Recording completed but no recording path returned',
|
|
192
|
+
recordingId: result.recordingId,
|
|
193
|
+
},
|
|
194
|
+
json,
|
|
195
|
+
);
|
|
196
|
+
process.exitCode = 1;
|
|
197
|
+
}
|
|
198
|
+
} catch (err) {
|
|
199
|
+
outputError(err instanceof Error ? err.message : String(err));
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// =========================================================================
|
|
204
|
+
// refresh-headless — refresh session from Chrome's cookie database
|
|
205
|
+
// =========================================================================
|
|
206
|
+
amz.command('refresh-headless')
|
|
207
|
+
.description(
|
|
208
|
+
'Refresh Amazon session by reading cookies directly from Chrome\'s local database. ' +
|
|
209
|
+
'No visible Chrome window needed. Requires Chrome to be signed into Amazon.',
|
|
210
|
+
)
|
|
211
|
+
.action(async (_opts: unknown, cmd: Command) => {
|
|
212
|
+
const json = getJson(cmd);
|
|
213
|
+
try {
|
|
214
|
+
const session = await extractSessionFromChromeCookies();
|
|
215
|
+
saveSession(session);
|
|
216
|
+
output(
|
|
217
|
+
{
|
|
218
|
+
ok: true,
|
|
219
|
+
message: 'Session refreshed from Chrome cookie database (headless)',
|
|
220
|
+
cookieCount: session.cookies.length,
|
|
221
|
+
},
|
|
222
|
+
json,
|
|
223
|
+
);
|
|
224
|
+
} catch (err) {
|
|
225
|
+
outputError(err instanceof Error ? err.message : String(err));
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// =========================================================================
|
|
230
|
+
// status — check session status
|
|
231
|
+
// =========================================================================
|
|
232
|
+
amz.command('status')
|
|
233
|
+
.description('Check if an Amazon session is active')
|
|
234
|
+
.action((_opts: unknown, cmd: Command) => {
|
|
235
|
+
const session = loadSession();
|
|
236
|
+
if (session) {
|
|
237
|
+
output(
|
|
238
|
+
{
|
|
239
|
+
ok: true,
|
|
240
|
+
loggedIn: true,
|
|
241
|
+
cookieCount: session.cookies.length,
|
|
242
|
+
importedAt: session.importedAt,
|
|
243
|
+
recordingId: session.recordingId,
|
|
244
|
+
},
|
|
245
|
+
getJson(cmd),
|
|
246
|
+
);
|
|
247
|
+
} else {
|
|
248
|
+
output({ ok: true, loggedIn: false }, getJson(cmd));
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// =========================================================================
|
|
253
|
+
// search — search for products
|
|
254
|
+
// =========================================================================
|
|
255
|
+
amz.command('search')
|
|
256
|
+
.description('Search for products on Amazon')
|
|
257
|
+
.argument('<query>', 'Search query (e.g. "AA batteries", "milk")')
|
|
258
|
+
.option('--fresh', 'Search Amazon Fresh grocery items')
|
|
259
|
+
.option('--limit <n>', 'Max results', '20')
|
|
260
|
+
.action(async (query: string, opts: { fresh?: boolean; limit: string }, cmd: Command) => {
|
|
261
|
+
await run(cmd, async () => {
|
|
262
|
+
const results = await search(query, {
|
|
263
|
+
isFresh: opts.fresh,
|
|
264
|
+
limit: parseInt(opts.limit, 10),
|
|
265
|
+
});
|
|
266
|
+
return { results, count: results.length };
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
// =========================================================================
|
|
271
|
+
// product — get product details
|
|
272
|
+
// =========================================================================
|
|
273
|
+
amz.command('product')
|
|
274
|
+
.description('Get product details for an ASIN')
|
|
275
|
+
.argument('<asin>', 'Amazon ASIN (e.g. B07XXXXX)')
|
|
276
|
+
.option('--fresh', 'Product is an Amazon Fresh item')
|
|
277
|
+
.action(async (asin: string, opts: { fresh?: boolean }, cmd: Command) => {
|
|
278
|
+
await run(cmd, async () => {
|
|
279
|
+
const product = await getProductDetails(asin, { isFresh: opts.fresh });
|
|
280
|
+
return { product };
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// =========================================================================
|
|
285
|
+
// variations — list product variations (child ASINs)
|
|
286
|
+
// =========================================================================
|
|
287
|
+
amz.command('variations')
|
|
288
|
+
.description('List available variations (sizes, colors, etc.) for a product')
|
|
289
|
+
.argument('<asin>', 'Parent ASIN')
|
|
290
|
+
.action(async (asin: string, _opts: unknown, cmd: Command) => {
|
|
291
|
+
await run(cmd, async () => {
|
|
292
|
+
const product = await getProductDetails(asin);
|
|
293
|
+
return {
|
|
294
|
+
asin,
|
|
295
|
+
title: product.title,
|
|
296
|
+
variations: product.variations,
|
|
297
|
+
count: product.variations.length,
|
|
298
|
+
};
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// =========================================================================
|
|
303
|
+
// cart — cart operations (subcommand group)
|
|
304
|
+
// =========================================================================
|
|
305
|
+
const cart = amz.command('cart').description('Cart operations');
|
|
306
|
+
|
|
307
|
+
// cart view
|
|
308
|
+
cart
|
|
309
|
+
.command('view')
|
|
310
|
+
.description('View cart contents')
|
|
311
|
+
.action(async (_opts: unknown, cmd: Command) => {
|
|
312
|
+
await run(cmd, async () => {
|
|
313
|
+
const result = await viewCart();
|
|
314
|
+
return { cart: result };
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
// cart add
|
|
319
|
+
cart
|
|
320
|
+
.command('add')
|
|
321
|
+
.description('Add a product to the cart')
|
|
322
|
+
.requiredOption('--asin <asin>', 'Product ASIN')
|
|
323
|
+
.option('--quantity <n>', 'Quantity', '1')
|
|
324
|
+
.option('--fresh', 'Amazon Fresh item')
|
|
325
|
+
.option('--verbose', 'Show detailed diagnostics for debugging')
|
|
326
|
+
.action(
|
|
327
|
+
async (opts: { asin: string; quantity: string; fresh?: boolean; verbose?: boolean }, cmd: Command) => {
|
|
328
|
+
await run(cmd, async () => {
|
|
329
|
+
const result = await addToCart({
|
|
330
|
+
asin: opts.asin,
|
|
331
|
+
quantity: parseInt(opts.quantity, 10),
|
|
332
|
+
isFresh: opts.fresh,
|
|
333
|
+
verbose: opts.verbose,
|
|
334
|
+
});
|
|
335
|
+
// Dump verbose diagnostics to stderr so they don't pollute JSON output
|
|
336
|
+
if (opts.verbose) {
|
|
337
|
+
const v = (result as unknown as Record<string, unknown>).__verbose;
|
|
338
|
+
if (v) {
|
|
339
|
+
process.stderr.write('\n[amazon:verbose] ── Cart Add Diagnostics ──\n');
|
|
340
|
+
for (const [k, val] of Object.entries(v as Record<string, unknown>)) {
|
|
341
|
+
const icon = String(val) === 'EMPTY' ? '❌' : '✅';
|
|
342
|
+
process.stderr.write(`[amazon:verbose] ${icon} ${k}: ${val}\n`);
|
|
343
|
+
}
|
|
344
|
+
process.stderr.write('[amazon:verbose] ──────────────────────────\n\n');
|
|
345
|
+
}
|
|
346
|
+
const d = (result as unknown as Record<string, unknown>).__debug as Record<string, unknown> | undefined;
|
|
347
|
+
if (d?.addCartJson) {
|
|
348
|
+
process.stderr.write(`[amazon:verbose] Raw Amazon response: ${d.addCartJson}\n\n`);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
// Strip internal debug fields from JSON output unless verbose
|
|
352
|
+
if (!opts.verbose) {
|
|
353
|
+
delete (result as unknown as Record<string, unknown>).__verbose;
|
|
354
|
+
delete (result as unknown as Record<string, unknown>).__debug;
|
|
355
|
+
}
|
|
356
|
+
return { cart: result };
|
|
357
|
+
});
|
|
358
|
+
},
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
// cart remove
|
|
362
|
+
cart
|
|
363
|
+
.command('remove')
|
|
364
|
+
.description('Remove an item from the cart')
|
|
365
|
+
.requiredOption('--cart-item-id <id>', 'Cart item ID (from cart view)')
|
|
366
|
+
.action(
|
|
367
|
+
async (opts: { cartItemId: string }, cmd: Command) => {
|
|
368
|
+
await run(cmd, async () => {
|
|
369
|
+
const result = await removeFromCart({ cartItemId: opts.cartItemId });
|
|
370
|
+
return { cart: result };
|
|
371
|
+
});
|
|
372
|
+
},
|
|
373
|
+
);
|
|
374
|
+
|
|
375
|
+
// =========================================================================
|
|
376
|
+
// fresh — Amazon Fresh operations (subcommand group)
|
|
377
|
+
// =========================================================================
|
|
378
|
+
const fresh = amz.command('fresh').description('Amazon Fresh grocery delivery operations');
|
|
379
|
+
|
|
380
|
+
// fresh delivery-slots
|
|
381
|
+
fresh
|
|
382
|
+
.command('delivery-slots')
|
|
383
|
+
.description('Get available Amazon Fresh delivery slots')
|
|
384
|
+
.action(async (_opts: unknown, cmd: Command) => {
|
|
385
|
+
await run(cmd, async () => {
|
|
386
|
+
const slots = await getFreshDeliverySlots();
|
|
387
|
+
return { slots, count: slots.length };
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
// fresh select-slot
|
|
392
|
+
fresh
|
|
393
|
+
.command('select-slot')
|
|
394
|
+
.description('Select an Amazon Fresh delivery slot')
|
|
395
|
+
.requiredOption('--slot-id <id>', 'Delivery slot ID (from delivery-slots command)')
|
|
396
|
+
.action(async (opts: { slotId: string }, cmd: Command) => {
|
|
397
|
+
await run(cmd, async () => {
|
|
398
|
+
const result = await selectFreshDeliverySlot(opts.slotId);
|
|
399
|
+
return result;
|
|
400
|
+
});
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
// =========================================================================
|
|
404
|
+
// payment-methods — list saved payment methods
|
|
405
|
+
// =========================================================================
|
|
406
|
+
amz.command('payment-methods')
|
|
407
|
+
.description('List saved payment methods')
|
|
408
|
+
.action(async (_opts: unknown, cmd: Command) => {
|
|
409
|
+
await run(cmd, async () => {
|
|
410
|
+
const methods = await getPaymentMethods();
|
|
411
|
+
return { methods, count: methods.length };
|
|
412
|
+
});
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
// =========================================================================
|
|
416
|
+
// checkout — get checkout summary
|
|
417
|
+
// =========================================================================
|
|
418
|
+
amz.command('checkout')
|
|
419
|
+
.description('Get checkout summary (totals, shipping, payment options)')
|
|
420
|
+
.action(async (_opts: unknown, cmd: Command) => {
|
|
421
|
+
await run(cmd, async () => {
|
|
422
|
+
const summary = await getCheckoutSummary();
|
|
423
|
+
return { summary };
|
|
424
|
+
});
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
// =========================================================================
|
|
428
|
+
// order — order operations (subcommand group)
|
|
429
|
+
// =========================================================================
|
|
430
|
+
const order = amz.command('order').description('Order operations');
|
|
431
|
+
|
|
432
|
+
// order place
|
|
433
|
+
order
|
|
434
|
+
.command('place')
|
|
435
|
+
.description('Place an Amazon order (IRREVERSIBLE — always confirm with user first)')
|
|
436
|
+
.option('--payment-method-id <id>', 'Payment method ID (uses default if omitted)')
|
|
437
|
+
.option('--slot-id <id>', 'Amazon Fresh delivery slot ID')
|
|
438
|
+
.action(
|
|
439
|
+
async (opts: { paymentMethodId?: string; slotId?: string }, cmd: Command) => {
|
|
440
|
+
await run(cmd, async () => {
|
|
441
|
+
const result = await placeOrder({
|
|
442
|
+
paymentMethodId: opts.paymentMethodId,
|
|
443
|
+
deliverySlotId: opts.slotId,
|
|
444
|
+
});
|
|
445
|
+
return { order: result };
|
|
446
|
+
});
|
|
447
|
+
},
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// ---------------------------------------------------------------------------
|
|
452
|
+
// Chrome CDP restart helper
|
|
453
|
+
// ---------------------------------------------------------------------------
|
|
454
|
+
|
|
455
|
+
import { spawn as spawnChild, execSync } from 'node:child_process';
|
|
456
|
+
import { homedir, tmpdir } from 'node:os';
|
|
457
|
+
import { join as pathJoin } from 'node:path';
|
|
458
|
+
import { copyFileSync, unlinkSync as unlinkFileSync, existsSync as fileExists } from 'node:fs';
|
|
459
|
+
import * as crypto from 'node:crypto';
|
|
460
|
+
|
|
461
|
+
const CDP_BASE = 'http://localhost:9222';
|
|
462
|
+
const CHROME_DATA_DIR = pathJoin(
|
|
463
|
+
homedir(),
|
|
464
|
+
'Library/Application Support/Google/Chrome-CDP',
|
|
465
|
+
);
|
|
466
|
+
|
|
467
|
+
async function isCdpReady(): Promise<boolean> {
|
|
468
|
+
try {
|
|
469
|
+
const res = await fetch(`${CDP_BASE}/json/version`);
|
|
470
|
+
return res.ok;
|
|
471
|
+
} catch {
|
|
472
|
+
return false;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
async function ensureChromeWithCDP(): Promise<void> {
|
|
477
|
+
if (await isCdpReady()) return;
|
|
478
|
+
|
|
479
|
+
const chromeApp =
|
|
480
|
+
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
|
|
481
|
+
spawnChild(chromeApp, [
|
|
482
|
+
`--remote-debugging-port=9222`,
|
|
483
|
+
`--force-renderer-accessibility`,
|
|
484
|
+
`--user-data-dir=${CHROME_DATA_DIR}`,
|
|
485
|
+
`https://www.amazon.com/`,
|
|
486
|
+
], {
|
|
487
|
+
detached: true,
|
|
488
|
+
stdio: 'ignore',
|
|
489
|
+
}).unref();
|
|
490
|
+
|
|
491
|
+
for (let i = 0; i < 30; i++) {
|
|
492
|
+
await new Promise(r => setTimeout(r, 500));
|
|
493
|
+
if (await isCdpReady()) return;
|
|
494
|
+
}
|
|
495
|
+
throw new Error('Chrome started but CDP endpoint not responding after 15s');
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
async function minimizeChromeWindow(): Promise<void> {
|
|
499
|
+
const res = await fetch(`${CDP_BASE}/json/list`);
|
|
500
|
+
const targets = (await res.json()) as Array<{ type: string; webSocketDebuggerUrl: string }>;
|
|
501
|
+
const pageTarget = targets.find(t => t.type === 'page');
|
|
502
|
+
if (!pageTarget) return;
|
|
503
|
+
|
|
504
|
+
const ws = new WebSocket(pageTarget.webSocketDebuggerUrl);
|
|
505
|
+
|
|
506
|
+
await new Promise<void>((resolve, reject) => {
|
|
507
|
+
const timeout = setTimeout(() => {
|
|
508
|
+
ws.close();
|
|
509
|
+
reject(new Error('CDP minimize timed out'));
|
|
510
|
+
}, 5000);
|
|
511
|
+
|
|
512
|
+
ws.addEventListener('open', () => {
|
|
513
|
+
ws.send(JSON.stringify({ id: 1, method: 'Browser.getWindowForTarget' }));
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
ws.addEventListener('message', (event) => {
|
|
517
|
+
const msg = JSON.parse(String(event.data)) as { id: number; result?: { windowId: number } };
|
|
518
|
+
if (msg.id === 1 && msg.result) {
|
|
519
|
+
ws.send(JSON.stringify({
|
|
520
|
+
id: 2,
|
|
521
|
+
method: 'Browser.setWindowBounds',
|
|
522
|
+
params: { windowId: msg.result.windowId, bounds: { windowState: 'minimized' } },
|
|
523
|
+
}));
|
|
524
|
+
} else if (msg.id === 1) {
|
|
525
|
+
clearTimeout(timeout);
|
|
526
|
+
ws.close();
|
|
527
|
+
reject(new Error('Browser.getWindowForTarget failed'));
|
|
528
|
+
} else if (msg.id === 2) {
|
|
529
|
+
clearTimeout(timeout);
|
|
530
|
+
ws.close();
|
|
531
|
+
resolve();
|
|
532
|
+
}
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
ws.addEventListener('error', (err) => {
|
|
536
|
+
clearTimeout(timeout);
|
|
537
|
+
reject(err);
|
|
538
|
+
});
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
async function restoreChromeWindow(): Promise<void> {
|
|
543
|
+
const res = await fetch(`${CDP_BASE}/json/list`);
|
|
544
|
+
const targets = (await res.json()) as Array<{ type: string; webSocketDebuggerUrl: string }>;
|
|
545
|
+
const pageTarget = targets.find(t => t.type === 'page');
|
|
546
|
+
if (!pageTarget) return;
|
|
547
|
+
|
|
548
|
+
const ws = new WebSocket(pageTarget.webSocketDebuggerUrl);
|
|
549
|
+
|
|
550
|
+
await new Promise<void>((resolve, reject) => {
|
|
551
|
+
const timeout = setTimeout(() => {
|
|
552
|
+
ws.close();
|
|
553
|
+
reject(new Error('CDP restore timed out'));
|
|
554
|
+
}, 5000);
|
|
555
|
+
|
|
556
|
+
ws.addEventListener('open', () => {
|
|
557
|
+
ws.send(JSON.stringify({ id: 1, method: 'Browser.getWindowForTarget' }));
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
ws.addEventListener('message', (event) => {
|
|
561
|
+
const msg = JSON.parse(String(event.data)) as { id: number; result?: { windowId: number } };
|
|
562
|
+
if (msg.id === 1 && msg.result) {
|
|
563
|
+
ws.send(JSON.stringify({
|
|
564
|
+
id: 2,
|
|
565
|
+
method: 'Browser.setWindowBounds',
|
|
566
|
+
params: { windowId: msg.result.windowId, bounds: { windowState: 'normal' } },
|
|
567
|
+
}));
|
|
568
|
+
} else if (msg.id === 1) {
|
|
569
|
+
clearTimeout(timeout);
|
|
570
|
+
ws.close();
|
|
571
|
+
reject(new Error('Browser.getWindowForTarget failed'));
|
|
572
|
+
} else if (msg.id === 2) {
|
|
573
|
+
clearTimeout(timeout);
|
|
574
|
+
ws.close();
|
|
575
|
+
resolve();
|
|
576
|
+
}
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
ws.addEventListener('error', (err) => {
|
|
580
|
+
clearTimeout(timeout);
|
|
581
|
+
reject(err);
|
|
582
|
+
});
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// ---------------------------------------------------------------------------
|
|
587
|
+
// Headless cookie extraction from Chrome's SQLite database
|
|
588
|
+
// ---------------------------------------------------------------------------
|
|
589
|
+
|
|
590
|
+
const CHROME_COOKIES_DB = pathJoin(
|
|
591
|
+
homedir(),
|
|
592
|
+
'Library/Application Support/Google/Chrome/Default/Cookies',
|
|
593
|
+
);
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Decrypt a Chrome cookie encrypted_value blob on macOS.
|
|
597
|
+
* Chrome uses AES-128-CBC with a key derived from the Keychain password via PBKDF2.
|
|
598
|
+
* The encrypted blob is prefixed with 'v10' (3 bytes).
|
|
599
|
+
*/
|
|
600
|
+
function decryptChromeCookie(encHex: string, derivedKey: Buffer): string | null {
|
|
601
|
+
const buf = Buffer.from(encHex, 'hex');
|
|
602
|
+
if (buf.length < 4 || buf.slice(0, 3).toString() !== 'v10') return null;
|
|
603
|
+
try {
|
|
604
|
+
const iv = Buffer.alloc(16, 0x20); // Chrome uses 16 space characters as IV
|
|
605
|
+
const decipher = crypto.createDecipheriv('aes-128-cbc', derivedKey, iv);
|
|
606
|
+
const decrypted = Buffer.concat([decipher.update(buf.slice(3)), decipher.final()]);
|
|
607
|
+
// Strip leading non-printable bytes (padding artifacts)
|
|
608
|
+
const str = decrypted.toString('utf-8');
|
|
609
|
+
const match = str.match(/[\x20-\x7e]+/);
|
|
610
|
+
return match ? match[0] : null;
|
|
611
|
+
} catch {
|
|
612
|
+
return null;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* Extract Amazon session cookies directly from Chrome's local SQLite cookie database.
|
|
618
|
+
* No visible Chrome window or user interaction required.
|
|
619
|
+
*
|
|
620
|
+
* Requirements:
|
|
621
|
+
* - Chrome must be installed with a Default profile
|
|
622
|
+
* - The user must be signed into Amazon in Chrome
|
|
623
|
+
* - macOS Keychain access for 'Chrome Safe Storage' (will prompt once)
|
|
624
|
+
*/
|
|
625
|
+
async function extractSessionFromChromeCookies(): Promise<import('../amazon/session.js').AmazonSession> {
|
|
626
|
+
// 1. Get Chrome Safe Storage key from macOS Keychain
|
|
627
|
+
let keychainPassword: string;
|
|
628
|
+
try {
|
|
629
|
+
keychainPassword = execSync(
|
|
630
|
+
'security find-generic-password -w -s "Chrome Safe Storage" -a "Chrome"',
|
|
631
|
+
{ encoding: 'utf-8' },
|
|
632
|
+
).trim();
|
|
633
|
+
} catch {
|
|
634
|
+
throw new Error(
|
|
635
|
+
'Could not read Chrome Safe Storage key from macOS Keychain. ' +
|
|
636
|
+
'Make sure Chrome is installed and has been opened at least once.',
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// 2. Derive the AES key using PBKDF2 (same as Chrome's implementation)
|
|
641
|
+
const derivedKey = crypto.pbkdf2Sync(keychainPassword, 'saltysalt', 1003, 16, 'sha1');
|
|
642
|
+
|
|
643
|
+
// 3. Copy the Cookies DB to a temp file, then query the copy.
|
|
644
|
+
// Reading Chrome's live SQLite DB directly can interfere with Chrome's
|
|
645
|
+
// WAL journaling and cause session logouts. Copying first is safe.
|
|
646
|
+
const tmpCookiesDb = pathJoin(tmpdir(), `vellum-chrome-cookies-${Date.now()}.db`);
|
|
647
|
+
let rawOutput: string;
|
|
648
|
+
try {
|
|
649
|
+
copyFileSync(CHROME_COOKIES_DB, tmpCookiesDb);
|
|
650
|
+
// Also copy WAL and SHM files if they exist, so the copy is consistent
|
|
651
|
+
const walPath = CHROME_COOKIES_DB + '-wal';
|
|
652
|
+
const shmPath = CHROME_COOKIES_DB + '-shm';
|
|
653
|
+
if (fileExists(walPath)) copyFileSync(walPath, tmpCookiesDb + '-wal');
|
|
654
|
+
if (fileExists(shmPath)) copyFileSync(shmPath, tmpCookiesDb + '-shm');
|
|
655
|
+
|
|
656
|
+
rawOutput = execSync(
|
|
657
|
+
`sqlite3 "${tmpCookiesDb}" "SELECT name, hex(encrypted_value), host_key, path, is_httponly, is_secure, expires_utc FROM cookies WHERE host_key LIKE '%amazon.com%'"`,
|
|
658
|
+
{ encoding: 'utf-8' },
|
|
659
|
+
).trim();
|
|
660
|
+
} catch {
|
|
661
|
+
throw new Error(
|
|
662
|
+
'Could not read Chrome Cookies database. ' +
|
|
663
|
+
'Make sure Chrome is installed and the Cookies file exists.',
|
|
664
|
+
);
|
|
665
|
+
} finally {
|
|
666
|
+
// Clean up temp files
|
|
667
|
+
try { unlinkFileSync(tmpCookiesDb); } catch {}
|
|
668
|
+
try { unlinkFileSync(tmpCookiesDb + '-wal'); } catch {}
|
|
669
|
+
try { unlinkFileSync(tmpCookiesDb + '-shm'); } catch {}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
if (!rawOutput) {
|
|
673
|
+
throw new Error(
|
|
674
|
+
'No Amazon cookies found in Chrome. ' +
|
|
675
|
+
'Make sure you are signed into Amazon in Chrome.',
|
|
676
|
+
);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// 4. Decrypt each cookie
|
|
680
|
+
const cookies: import('../tools/browser/network-recording-types.js').ExtractedCredential[] = [];
|
|
681
|
+
for (const line of rawOutput.split('\n')) {
|
|
682
|
+
const parts = line.split('|');
|
|
683
|
+
if (parts.length < 7) continue;
|
|
684
|
+
const [name, encHex, domain, path, httpOnly, secure, expiresUtc] = parts;
|
|
685
|
+
if (!encHex) continue;
|
|
686
|
+
|
|
687
|
+
const value = decryptChromeCookie(encHex, derivedKey);
|
|
688
|
+
if (!value) continue;
|
|
689
|
+
|
|
690
|
+
cookies.push({
|
|
691
|
+
name,
|
|
692
|
+
value,
|
|
693
|
+
domain,
|
|
694
|
+
path: path || '/',
|
|
695
|
+
httpOnly: httpOnly === '1',
|
|
696
|
+
secure: secure === '1',
|
|
697
|
+
expires: expiresUtc ? Math.floor(parseInt(expiresUtc, 10) / 1000000 - 11644473600) : undefined,
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// 5. Validate required cookies are present
|
|
702
|
+
const cookieNames = new Set(cookies.map(c => c.name));
|
|
703
|
+
if (!cookieNames.has('session-id')) {
|
|
704
|
+
throw new Error(
|
|
705
|
+
'Chrome cookies are missing required Amazon cookie: session-id. ' +
|
|
706
|
+
'Make sure you are signed into Amazon in Chrome.',
|
|
707
|
+
);
|
|
708
|
+
}
|
|
709
|
+
if (!cookieNames.has('ubid-main')) {
|
|
710
|
+
throw new Error(
|
|
711
|
+
'Chrome cookies are missing required Amazon cookie: ubid-main. ' +
|
|
712
|
+
'Make sure you are signed into Amazon in Chrome.',
|
|
713
|
+
);
|
|
714
|
+
}
|
|
715
|
+
if (!cookieNames.has('at-main') && !cookieNames.has('x-main')) {
|
|
716
|
+
throw new Error(
|
|
717
|
+
'Chrome cookies are missing required Amazon auth cookie (at-main or x-main). ' +
|
|
718
|
+
'Make sure you are fully signed into Amazon in Chrome.',
|
|
719
|
+
);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
return {
|
|
723
|
+
cookies,
|
|
724
|
+
importedAt: new Date().toISOString(),
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
// ---------------------------------------------------------------------------
|
|
729
|
+
// Ride Shotgun learn session helper
|
|
730
|
+
// ---------------------------------------------------------------------------
|
|
731
|
+
|
|
732
|
+
interface LearnResult {
|
|
733
|
+
recordingId?: string;
|
|
734
|
+
recordingPath?: string;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
async function startLearnSession(durationSeconds: number): Promise<LearnResult> {
|
|
738
|
+
await ensureChromeWithCDP();
|
|
739
|
+
|
|
740
|
+
return new Promise((resolve, reject) => {
|
|
741
|
+
const socketPath = getSocketPath();
|
|
742
|
+
const sessionToken = readSessionToken();
|
|
743
|
+
const socket = net.createConnection(socketPath);
|
|
744
|
+
const parser = createMessageParser();
|
|
745
|
+
|
|
746
|
+
socket.on('error', (err) => {
|
|
747
|
+
reject(new Error(`Cannot connect to daemon: ${err.message}. Is the daemon running?`));
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
const timeoutHandle = setTimeout(() => {
|
|
751
|
+
socket.destroy();
|
|
752
|
+
reject(new Error(`Learn session timed out after ${durationSeconds + 30}s`));
|
|
753
|
+
}, (durationSeconds + 30) * 1000);
|
|
754
|
+
timeoutHandle.unref();
|
|
755
|
+
|
|
756
|
+
let authenticated = !sessionToken;
|
|
757
|
+
|
|
758
|
+
const sendStartCommand = () => {
|
|
759
|
+
socket.write(
|
|
760
|
+
serialize({
|
|
761
|
+
type: 'ride_shotgun_start',
|
|
762
|
+
durationSeconds,
|
|
763
|
+
intervalSeconds: 5,
|
|
764
|
+
mode: 'learn',
|
|
765
|
+
targetDomain: 'amazon.com',
|
|
766
|
+
} as unknown as import('../daemon/ipc-protocol.js').ClientMessage),
|
|
767
|
+
);
|
|
768
|
+
};
|
|
769
|
+
|
|
770
|
+
socket.on('data', (chunk) => {
|
|
771
|
+
const messages = parser.feed(chunk.toString('utf-8'));
|
|
772
|
+
for (const msg of messages) {
|
|
773
|
+
const m = msg as unknown as Record<string, unknown>;
|
|
774
|
+
|
|
775
|
+
if (!authenticated && m.type === 'auth_result') {
|
|
776
|
+
if ((m as { success: boolean }).success) {
|
|
777
|
+
authenticated = true;
|
|
778
|
+
sendStartCommand();
|
|
779
|
+
} else {
|
|
780
|
+
clearTimeout(timeoutHandle);
|
|
781
|
+
socket.destroy();
|
|
782
|
+
reject(new Error('Daemon authentication failed'));
|
|
783
|
+
}
|
|
784
|
+
continue;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
if (m.type === 'auth_result') {
|
|
788
|
+
continue;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
if (m.type === 'ride_shotgun_result') {
|
|
792
|
+
clearTimeout(timeoutHandle);
|
|
793
|
+
socket.destroy();
|
|
794
|
+
resolve({
|
|
795
|
+
recordingId: m.recordingId as string | undefined,
|
|
796
|
+
recordingPath: m.recordingPath as string | undefined,
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
});
|
|
801
|
+
|
|
802
|
+
socket.on('connect', () => {
|
|
803
|
+
if (sessionToken) {
|
|
804
|
+
socket.write(
|
|
805
|
+
serialize({
|
|
806
|
+
type: 'auth',
|
|
807
|
+
token: sessionToken,
|
|
808
|
+
} as unknown as import('../daemon/ipc-protocol.js').ClientMessage),
|
|
809
|
+
);
|
|
810
|
+
} else {
|
|
811
|
+
sendStartCommand();
|
|
812
|
+
}
|
|
813
|
+
});
|
|
814
|
+
});
|
|
815
|
+
}
|