vellum 0.2.1 → 0.2.7
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.md +15 -2
- package/bun.lock +71 -100
- package/package.json +5 -3
- package/scripts/capture-x-graphql.ts +562 -0
- package/scripts/ipc/check-swift-decoder-drift.ts +2 -1
- package/scripts/test.sh +5 -0
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +133 -34
- package/src/__tests__/account-registry.test.ts +2 -1
- package/src/__tests__/agent-heartbeat-service.test.ts +250 -0
- package/src/__tests__/asset-materialize-tool.test.ts +16 -15
- package/src/__tests__/asset-search-tool.test.ts +23 -22
- package/src/__tests__/attachments-store.test.ts +56 -127
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +5 -4
- package/src/__tests__/browser-skill-endstate.test.ts +4 -3
- package/src/__tests__/call-bridge.test.ts +385 -0
- package/src/__tests__/call-constants.test.ts +40 -0
- package/src/__tests__/call-orchestrator.test.ts +130 -4
- package/src/__tests__/call-recovery.test.ts +518 -0
- package/src/__tests__/call-routes-http.test.ts +459 -0
- package/src/__tests__/call-state-machine.test.ts +143 -0
- package/src/__tests__/call-store.test.ts +216 -1
- package/src/__tests__/cli-discover.test.ts +1 -1
- package/src/__tests__/commit-message-enrichment-service.test.ts +148 -7
- package/src/__tests__/compaction.benchmark.test.ts +176 -0
- package/src/__tests__/computer-use-tools.test.ts +250 -0
- package/src/__tests__/config-schema.test.ts +305 -3
- package/src/__tests__/conflict-store.test.ts +2 -1
- package/src/__tests__/contacts-tools.test.ts +331 -0
- package/src/__tests__/conversation-store.test.ts +30 -32
- package/src/__tests__/credential-security-invariants.test.ts +4 -0
- package/src/__tests__/date-context.test.ts +373 -0
- package/src/__tests__/db-schedule-syntax-migration.test.ts +129 -0
- package/src/__tests__/fixtures/media-reuse-fixtures.ts +3 -3
- package/src/__tests__/followup-tools.test.ts +303 -0
- package/src/__tests__/handlers-twilio-config.test.ts +221 -0
- package/src/__tests__/handlers-twitter-config.test.ts +718 -0
- package/src/__tests__/intent-routing.test.ts +64 -57
- package/src/__tests__/ipc-roundtrip.benchmark.test.ts +237 -0
- package/src/__tests__/ipc-snapshot.test.ts +71 -28
- package/src/__tests__/llm-usage-store.test.ts +3 -8
- package/src/__tests__/media-generate-image.test.ts +1 -1
- package/src/__tests__/media-reuse-story.e2e.test.ts +7 -7
- package/src/__tests__/memory-regressions.test.ts +100 -2
- package/src/__tests__/memory-retrieval.benchmark.test.ts +430 -0
- package/src/__tests__/parallel-tool.benchmark.test.ts +294 -0
- package/src/__tests__/playbook-tools.test.ts +342 -0
- package/src/__tests__/profile-compiler.test.ts +2 -1
- package/src/__tests__/provider-commit-message-generator.test.ts +303 -0
- package/src/__tests__/provider-streaming.benchmark.test.ts +773 -0
- package/src/__tests__/recurrence-engine-rruleset.test.ts +78 -0
- package/src/__tests__/recurrence-engine.test.ts +69 -0
- package/src/__tests__/recurrence-types.test.ts +71 -0
- package/src/__tests__/registry.test.ts +5 -3
- package/src/__tests__/relay-server.test.ts +633 -0
- package/src/__tests__/reminder-store.test.ts +6 -3
- package/src/__tests__/reminder.test.ts +43 -77
- package/src/__tests__/run-orchestrator-assistant-events.test.ts +8 -4
- package/src/__tests__/run-orchestrator.test.ts +4 -4
- package/src/__tests__/runtime-attachment-metadata.test.ts +7 -6
- package/src/__tests__/runtime-runs-http.test.ts +4 -4
- package/src/__tests__/runtime-runs.test.ts +4 -4
- package/src/__tests__/schedule-store.test.ts +482 -0
- package/src/__tests__/schedule-tools.test.ts +700 -0
- package/src/__tests__/scheduler-recurrence.test.ts +329 -0
- package/src/__tests__/server-history-render.test.ts +14 -13
- package/src/__tests__/session-conflict-gate.test.ts +28 -25
- package/src/__tests__/session-error.test.ts +28 -0
- package/src/__tests__/session-init.benchmark.test.ts +462 -0
- package/src/__tests__/session-queue.test.ts +71 -48
- package/src/__tests__/session-runtime-assembly.test.ts +161 -0
- package/src/__tests__/session-surfaces-task-progress.test.ts +104 -0
- package/src/__tests__/signup-e2e.test.ts +2 -1
- package/src/__tests__/skill-projection.benchmark.test.ts +328 -0
- package/src/__tests__/skill-script-runner.test.ts +159 -0
- package/src/__tests__/speaker-identification.test.ts +52 -0
- package/src/__tests__/subagent-manager-notify.test.ts +42 -10
- package/src/__tests__/subagent-tools.test.ts +141 -41
- package/src/__tests__/task-compiler.test.ts +2 -1
- package/src/__tests__/task-runner.test.ts +2 -1
- package/src/__tests__/task-scheduler.test.ts +2 -1
- package/src/__tests__/task-tools.test.ts +49 -56
- package/src/__tests__/tool-audit-listener.test.ts +1 -0
- package/src/__tests__/tool-domain-event-publisher.test.ts +2 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +500 -0
- package/src/__tests__/tool-executor.test.ts +13 -17
- package/src/__tests__/turn-commit.test.ts +218 -3
- package/src/__tests__/twilio-provider.test.ts +143 -0
- package/src/__tests__/twilio-routes.test.ts +789 -0
- package/src/__tests__/twitter-auth-handler.test.ts +581 -0
- package/src/__tests__/view-image-tool.test.ts +217 -0
- package/src/__tests__/workspace-git-service.test.ts +186 -0
- package/src/__tests__/workspace-heartbeat-service.test.ts +13 -3
- package/src/agent-heartbeat/agent-heartbeat-service.ts +155 -0
- package/src/bundler/app-bundler.ts +12 -8
- package/src/calls/__tests__/twilio-webhook-urls.test.ts +162 -0
- package/src/calls/call-bridge.ts +95 -0
- package/src/calls/call-constants.ts +43 -5
- package/src/calls/call-domain.ts +276 -0
- package/src/calls/call-orchestrator.ts +43 -17
- package/src/calls/call-recovery.ts +207 -0
- package/src/calls/call-state-machine.ts +68 -0
- package/src/calls/call-store.ts +192 -5
- package/src/calls/relay-server.ts +41 -4
- package/src/calls/speaker-identification.ts +213 -0
- package/src/calls/twilio-config.ts +8 -8
- package/src/calls/twilio-provider.ts +13 -9
- package/src/calls/twilio-routes.ts +90 -76
- package/src/calls/twilio-webhook-urls.ts +50 -0
- package/src/calls/types.ts +1 -1
- package/src/cli/config-commands.ts +334 -0
- package/src/cli/core-commands.ts +776 -0
- package/src/cli/doordash.ts +251 -1
- package/src/cli/ipc-client.ts +82 -0
- package/src/cli/map.ts +270 -0
- package/src/cli/twitter.ts +575 -0
- package/src/cli.ts +7 -5
- package/src/commands/__tests__/cc-command-registry.test.ts +319 -0
- package/src/commands/cc-command-registry.ts +209 -0
- package/src/config/bundled-skills/contacts/SKILL.md +39 -0
- package/src/config/bundled-skills/contacts/TOOLS.json +122 -0
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +9 -0
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +9 -0
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +9 -0
- package/src/config/bundled-skills/document/SKILL.md +18 -0
- package/src/config/bundled-skills/document/TOOLS.json +53 -0
- package/src/config/bundled-skills/document/tools/document-create.ts +9 -0
- package/src/config/bundled-skills/document/tools/document-update.ts +9 -0
- package/src/config/bundled-skills/doordash/SKILL.md +82 -23
- package/src/config/bundled-skills/followups/SKILL.md +32 -0
- package/src/config/bundled-skills/followups/TOOLS.json +100 -0
- package/src/config/bundled-skills/followups/tools/followup-create.ts +9 -0
- package/src/config/bundled-skills/followups/tools/followup-list.ts +9 -0
- package/src/config/bundled-skills/followups/tools/followup-resolve.ts +9 -0
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +1 -23
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -1
- package/src/config/bundled-skills/playbooks/SKILL.md +31 -0
- package/src/config/bundled-skills/playbooks/TOOLS.json +126 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +9 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +9 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +9 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +9 -0
- package/src/config/bundled-skills/reminder/SKILL.md +20 -0
- package/src/config/bundled-skills/reminder/TOOLS.json +67 -0
- package/src/config/bundled-skills/reminder/tools/reminder-cancel.ts +9 -0
- package/src/config/bundled-skills/reminder/tools/reminder-create.ts +9 -0
- package/src/config/bundled-skills/reminder/tools/reminder-list.ts +9 -0
- package/src/config/bundled-skills/schedule/SKILL.md +74 -0
- package/src/config/bundled-skills/schedule/TOOLS.json +135 -0
- package/src/config/bundled-skills/schedule/tools/schedule-create.ts +9 -0
- package/src/config/bundled-skills/schedule/tools/schedule-delete.ts +9 -0
- package/src/config/bundled-skills/schedule/tools/schedule-list.ts +9 -0
- package/src/config/bundled-skills/schedule/tools/schedule-update.ts +9 -0
- package/src/config/bundled-skills/subagent/SKILL.md +25 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +107 -0
- package/src/config/bundled-skills/subagent/tools/subagent-abort.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-message.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-read.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-spawn.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-status.ts +9 -0
- package/src/config/bundled-skills/tasks/SKILL.md +28 -0
- package/src/config/bundled-skills/tasks/TOOLS.json +256 -0
- package/src/config/bundled-skills/tasks/tools/task-delete.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-add.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-show.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-update.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-run.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-save.ts +9 -0
- package/src/config/bundled-skills/twitter/SKILL.md +134 -0
- package/src/config/bundled-skills/watcher/SKILL.md +27 -0
- package/src/config/bundled-skills/watcher/TOOLS.json +147 -0
- package/src/config/bundled-skills/watcher/tools/watcher-create.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-list.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-update.ts +9 -0
- package/src/config/defaults.ts +34 -0
- package/src/config/loader.ts +4 -1
- package/src/config/schema.ts +165 -1
- package/src/config/system-prompt.ts +61 -16
- package/src/config/templates/IDENTITY.md +7 -0
- package/src/config/types.ts +4 -0
- package/src/config/vellum-skills/telegram-setup/SKILL.md +1 -5
- package/src/contacts/contact-store.ts +4 -4
- package/src/daemon/assistant-attachments.ts +10 -0
- package/src/daemon/classifier.ts +3 -1
- package/src/daemon/computer-use-session.ts +3 -1
- package/src/daemon/date-context.ts +136 -0
- package/src/daemon/handlers/apps.ts +16 -1
- package/src/daemon/handlers/browser.ts +54 -0
- package/src/daemon/handlers/computer-use.ts +7 -1
- package/src/daemon/handlers/config.ts +205 -5
- package/src/daemon/handlers/diagnostics.ts +5 -1
- package/src/daemon/handlers/documents.ts +18 -29
- package/src/daemon/handlers/home-base.ts +5 -1
- package/src/daemon/handlers/index.ts +40 -277
- package/src/daemon/handlers/misc.ts +9 -1
- package/src/daemon/handlers/publish.ts +6 -1
- package/src/daemon/handlers/sessions.ts +65 -12
- package/src/daemon/handlers/shared.ts +36 -1
- package/src/daemon/handlers/signing.ts +37 -0
- package/src/daemon/handlers/skills.ts +20 -6
- package/src/daemon/handlers/subagents.ts +8 -3
- package/src/daemon/handlers/twitter-auth.ts +169 -0
- package/src/daemon/handlers/work-items.ts +384 -68
- package/src/daemon/ipc-contract-inventory.json +32 -4
- package/src/daemon/ipc-contract.ts +156 -37
- package/src/daemon/ipc-protocol.ts +7 -2
- package/src/daemon/lifecycle.ts +21 -0
- package/src/daemon/main.ts +10 -4
- package/src/daemon/ride-shotgun-handler.ts +75 -10
- package/src/daemon/server.ts +143 -26
- package/src/daemon/session-agent-loop.ts +922 -0
- package/src/daemon/session-attachments.ts +28 -5
- package/src/daemon/session-conflict-gate.ts +18 -109
- package/src/daemon/session-error.ts +24 -3
- package/src/daemon/session-lifecycle.ts +147 -0
- package/src/daemon/session-media-retry.ts +147 -0
- package/src/daemon/session-messaging.ts +145 -0
- package/src/daemon/session-notifiers.ts +164 -0
- package/src/daemon/session-process.ts +2 -2
- package/src/daemon/session-queue-manager.ts +1 -0
- package/src/daemon/session-runtime-assembly.ts +52 -0
- package/src/daemon/session-skill-tools.ts +124 -5
- package/src/daemon/session-slash.ts +3 -0
- package/src/daemon/session-surfaces.ts +77 -2
- package/src/daemon/session-tool-setup.ts +216 -2
- package/src/daemon/session-usage.ts +0 -2
- package/src/daemon/session.ts +114 -1404
- package/src/daemon/video-thumbnail.ts +60 -0
- package/src/doordash/client.ts +121 -27
- package/src/doordash/queries.ts +1 -2
- package/src/export/formatter.ts +3 -1
- package/src/followups/followup-store.ts +4 -2
- package/src/followups/types.ts +6 -0
- package/src/hooks/templates.ts +1 -1
- package/src/index.ts +32 -1153
- package/src/memory/attachments-store.ts +28 -83
- package/src/memory/channel-delivery-store.ts +7 -21
- package/src/memory/clarification-resolver.ts +6 -5
- package/src/memory/conflict-intent.ts +114 -0
- package/src/memory/contradiction-checker.ts +3 -2
- package/src/memory/conversation-key-store.ts +10 -29
- package/src/memory/conversation-store.ts +2 -1
- package/src/memory/db.ts +96 -2
- package/src/memory/entity-extractor.ts +6 -3
- package/src/memory/items-extractor.ts +5 -4
- package/src/memory/job-handlers/conflict.ts +23 -1
- package/src/memory/jobs-store.ts +3 -2
- package/src/memory/llm-usage-store.ts +1 -2
- package/src/memory/runs-store.ts +1 -2
- package/src/memory/schema.ts +23 -2
- package/src/messaging/style-analyzer.ts +3 -2
- package/src/messaging/thread-summarizer.ts +8 -12
- package/src/messaging/triage-engine.ts +4 -2
- package/src/providers/openrouter/client.ts +20 -0
- package/src/providers/registry.ts +8 -0
- package/src/runtime/gateway-client.ts +36 -0
- package/src/runtime/http-server.ts +166 -22
- package/src/runtime/routes/attachment-routes.ts +2 -3
- package/src/runtime/routes/call-routes.ts +140 -0
- package/src/runtime/routes/channel-routes.ts +125 -88
- package/src/runtime/routes/conversation-routes.ts +5 -5
- package/src/runtime/routes/run-routes.ts +2 -2
- package/src/runtime/run-orchestrator.ts +9 -3
- package/src/schedule/recurrence-engine.ts +138 -0
- package/src/schedule/recurrence-types.ts +67 -0
- package/src/schedule/schedule-store.ts +102 -57
- package/src/schedule/scheduler.ts +9 -6
- package/src/security/oauth2.ts +29 -4
- package/src/security/secret-allowlist.ts +46 -0
- package/src/skills/clawhub.ts +1 -1
- package/src/subagent/manager.ts +40 -8
- package/src/swarm/backend-claude-code.ts +64 -9
- package/src/swarm/worker-prompts.ts +2 -1
- package/src/tasks/SPEC.md +34 -28
- package/src/tasks/ephemeral-permissions.ts +16 -7
- package/src/tasks/task-compiler.ts +5 -4
- package/src/tasks/task-runner.ts +10 -5
- package/src/tasks/task-scheduler.ts +1 -1
- package/src/tasks/tool-sanitizer.ts +36 -0
- package/src/tools/assets/search.ts +4 -4
- package/src/tools/browser/api-map.ts +293 -0
- package/src/tools/browser/auto-navigate.ts +270 -0
- package/src/tools/browser/browser-execution.ts +2 -1
- package/src/tools/browser/browser-manager.ts +2 -2
- package/src/tools/browser/network-recorder.ts +5 -4
- package/src/tools/browser/x-auto-navigate.ts +207 -0
- package/src/tools/calls/call-end.ts +17 -67
- package/src/tools/calls/call-start.ts +24 -85
- package/src/tools/calls/call-status.ts +35 -51
- package/src/tools/claude-code/claude-code.ts +207 -11
- package/src/tools/contacts/contact-merge.ts +46 -78
- package/src/tools/contacts/contact-search.ts +35 -79
- package/src/tools/contacts/contact-upsert.ts +35 -108
- package/src/tools/credentials/vault.ts +20 -4
- package/src/tools/document/document-tool.ts +71 -144
- package/src/tools/executor.ts +129 -10
- package/src/tools/followups/followup_create.ts +46 -88
- package/src/tools/followups/followup_list.ts +34 -74
- package/src/tools/followups/followup_resolve.ts +31 -66
- package/src/tools/host-terminal/cli-discover.ts +2 -1
- package/src/tools/host-terminal/host-shell.ts +10 -0
- package/src/tools/memory/handlers.ts +5 -4
- package/src/tools/network/__tests__/web-search.test.ts +427 -0
- package/src/tools/network/script-proxy/__tests__/logging.test.ts +248 -0
- package/src/tools/network/script-proxy/__tests__/policy.test.ts +234 -0
- package/src/tools/network/script-proxy/__tests__/router.test.ts +76 -0
- package/src/tools/network/web-fetch.ts +18 -6
- package/src/tools/playbooks/index.ts +4 -5
- package/src/tools/playbooks/playbook-create.ts +3 -47
- package/src/tools/playbooks/playbook-delete.ts +1 -25
- package/src/tools/playbooks/playbook-list.ts +1 -28
- package/src/tools/playbooks/playbook-update.ts +3 -51
- package/src/tools/reminder/reminder.ts +5 -78
- package/src/tools/schedule/create.ts +69 -74
- package/src/tools/schedule/delete.ts +21 -47
- package/src/tools/schedule/list.ts +55 -74
- package/src/tools/schedule/update.ts +77 -84
- package/src/tools/subagent/abort.ts +29 -58
- package/src/tools/subagent/message.ts +30 -63
- package/src/tools/subagent/read.ts +53 -84
- package/src/tools/subagent/spawn.ts +43 -82
- package/src/tools/subagent/status.ts +42 -71
- package/src/tools/swarm/delegate.ts +2 -1
- package/src/tools/tasks/index.ts +8 -8
- package/src/tools/tasks/task-delete.ts +60 -88
- package/src/tools/tasks/task-list.ts +31 -52
- package/src/tools/tasks/task-run.ts +72 -108
- package/src/tools/tasks/task-save.ts +33 -65
- package/src/tools/tasks/work-item-enqueue.ts +183 -215
- package/src/tools/tasks/work-item-list.ts +33 -63
- package/src/tools/tasks/work-item-remove.ts +45 -97
- package/src/tools/tasks/work-item-update.ts +91 -163
- package/src/tools/terminal/backends/native.ts +3 -1
- package/src/tools/tool-manifest.ts +0 -62
- package/src/tools/types.ts +6 -0
- package/src/tools/ui-surface/definitions.ts +3 -1
- package/src/tools/watch/screen-watch.ts +3 -1
- package/src/tools/watcher/create.ts +52 -98
- package/src/tools/watcher/delete.ts +20 -46
- package/src/tools/watcher/digest.ts +36 -70
- package/src/tools/watcher/list.ts +49 -79
- package/src/tools/watcher/update.ts +45 -91
- package/src/twitter/client.ts +690 -0
- package/src/twitter/session.ts +91 -0
- package/src/usage/types.ts +0 -1
- package/src/util/truncate.ts +6 -0
- package/src/watcher/providers/slack.ts +2 -1
- package/src/watcher/watcher-store.ts +3 -2
- package/src/work-items/work-item-store.ts +27 -2
- package/src/workspace/commit-message-enrichment-service.ts +31 -7
- package/src/workspace/git-service.ts +87 -22
- package/src/workspace/provider-commit-message-generator.ts +269 -0
- package/src/workspace/turn-commit.ts +62 -3
- package/src/tools/contacts/index.ts +0 -4
- package/src/tools/document/index.ts +0 -5
- package/src/tools/followups/index.ts +0 -3
- package/src/tools/subagent/index.ts +0 -5
- /package/src/__tests__/{memory-context-benchmark.test.ts → memory-context-benchmark.benchmark.test.ts} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vellum",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"vellum": "./src/index.ts"
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"typecheck": "bunx tsc --noEmit",
|
|
19
19
|
"test": "bash scripts/test.sh",
|
|
20
20
|
"test:stable": "EXCLUDE_EXPERIMENTAL=true bash scripts/test.sh",
|
|
21
|
+
"test:bench": "find src/__tests__ -maxdepth 1 -type f -name '*.benchmark.test.ts' -print0 | xargs -0 -P 1 -I {} bun test {}",
|
|
21
22
|
"test:filesystem-tools": "bash scripts/test-filesystem-tools.sh"
|
|
22
23
|
},
|
|
23
24
|
"dependencies": {
|
|
@@ -27,8 +28,8 @@
|
|
|
27
28
|
"@huggingface/transformers": "^3.8.1",
|
|
28
29
|
"@qdrant/js-client-rest": "^1.16.2",
|
|
29
30
|
"@sentry/node": "^10.38.0",
|
|
30
|
-
"@vellumai/cli": "0.1.
|
|
31
|
-
"@vellumai/vellum-gateway": "
|
|
31
|
+
"@vellumai/cli": "0.1.8",
|
|
32
|
+
"@vellumai/vellum-gateway": "0.1.9",
|
|
32
33
|
"agentmail": "^0.1.0",
|
|
33
34
|
"archiver": "^7.0.1",
|
|
34
35
|
"commander": "^13.1.0",
|
|
@@ -44,6 +45,7 @@
|
|
|
44
45
|
"playwright": "^1.58.2",
|
|
45
46
|
"postgres": "^3.4.8",
|
|
46
47
|
"react": "^19.2.4",
|
|
48
|
+
"rrule": "^2.8.1",
|
|
47
49
|
"tldts": "^7.0.23",
|
|
48
50
|
"tree-sitter-bash": "0.25.1",
|
|
49
51
|
"uuid": "^11.1.0",
|
|
@@ -0,0 +1,562 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Capture X.com GraphQL API calls via Chrome CDP.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* 1. Make sure Chrome is running with CDP (vellum x refresh will do this)
|
|
7
|
+
* 2. Run: bun run scripts/capture-x-graphql.ts [--auto] [--all]
|
|
8
|
+
* 3. In --auto mode, Chrome is navigated automatically. Otherwise browse X manually.
|
|
9
|
+
* 4. Press Ctrl+C to stop (or wait for --auto to finish).
|
|
10
|
+
*
|
|
11
|
+
* Flags:
|
|
12
|
+
* --auto Automatically navigate Chrome through X.com pages via CDP
|
|
13
|
+
* --all Capture ALL GraphQL queries (skip relevance filter)
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { mkdirSync, existsSync } from 'node:fs';
|
|
17
|
+
|
|
18
|
+
const CDP_BASE = 'http://localhost:9222';
|
|
19
|
+
const CAPTURE_DIR = '/tmp/x-graphql-capture';
|
|
20
|
+
|
|
21
|
+
// ─── Relevance filter ────────────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
const RELEVANT_QUERIES = new Set([
|
|
24
|
+
// Reads
|
|
25
|
+
'UserByScreenName', 'UserTweets', 'TweetDetail', 'TweetResultByRestId',
|
|
26
|
+
'SearchTimeline', 'Bookmarks', 'Likes', 'Favoriters',
|
|
27
|
+
'Followers', 'Following', 'HomeTimeline', 'HomeLatestTimeline',
|
|
28
|
+
'NotificationsTimeline', 'ListTimeline',
|
|
29
|
+
'UserMedia',
|
|
30
|
+
// Writes
|
|
31
|
+
'CreateTweet', 'DeleteTweet', 'FavoriteTweet',
|
|
32
|
+
'UnfavoriteTweet', 'CreateRetweet', 'DeleteRetweet', 'CreateBookmark',
|
|
33
|
+
'DeleteBookmark',
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
// ─── Types ───────────────────────────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
interface CapturedQuery {
|
|
39
|
+
queryName: string;
|
|
40
|
+
queryId: string;
|
|
41
|
+
method: string;
|
|
42
|
+
variables: unknown;
|
|
43
|
+
features?: unknown;
|
|
44
|
+
response?: unknown;
|
|
45
|
+
timestamp: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ─── Minimal CDP WebSocket client ────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
class CDPClient {
|
|
51
|
+
private ws: WebSocket | null = null;
|
|
52
|
+
private nextId = 1;
|
|
53
|
+
private callbacks = new Map<number, { resolve: (v: unknown) => void; reject: (e: Error) => void }>();
|
|
54
|
+
private eventHandlers = new Map<string, Array<(params: Record<string, unknown>) => void>>();
|
|
55
|
+
|
|
56
|
+
async connect(wsUrl: string): Promise<void> {
|
|
57
|
+
return new Promise((resolve, reject) => {
|
|
58
|
+
const ws = new WebSocket(wsUrl);
|
|
59
|
+
ws.onopen = () => { this.ws = ws; resolve(); };
|
|
60
|
+
ws.onerror = (e) => reject(new Error(`CDP error: ${e}`));
|
|
61
|
+
ws.onclose = () => { this.ws = null; };
|
|
62
|
+
ws.onmessage = (event) => {
|
|
63
|
+
const msg = JSON.parse(String(event.data));
|
|
64
|
+
if (msg.id != null) {
|
|
65
|
+
const cb = this.callbacks.get(msg.id);
|
|
66
|
+
if (cb) { this.callbacks.delete(msg.id); msg.error ? cb.reject(new Error(msg.error.message)) : cb.resolve(msg.result); }
|
|
67
|
+
} else if (msg.method) {
|
|
68
|
+
for (const h of this.eventHandlers.get(msg.method) ?? []) h(msg.params ?? {});
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async send(method: string, params?: Record<string, unknown>): Promise<unknown> {
|
|
75
|
+
const id = this.nextId++;
|
|
76
|
+
return new Promise((resolve, reject) => {
|
|
77
|
+
this.callbacks.set(id, { resolve, reject });
|
|
78
|
+
this.ws!.send(JSON.stringify({ id, method, params }));
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
on(event: string, handler: (params: Record<string, unknown>) => void) {
|
|
83
|
+
const list = this.eventHandlers.get(event) ?? [];
|
|
84
|
+
list.push(handler);
|
|
85
|
+
this.eventHandlers.set(event, list);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
close() { this.ws?.close(); }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ─── State ───────────────────────────────────────────────────────────────────
|
|
92
|
+
|
|
93
|
+
const args = process.argv.slice(2);
|
|
94
|
+
const autoMode = args.includes('--auto');
|
|
95
|
+
const captureAll = args.includes('--all');
|
|
96
|
+
|
|
97
|
+
const captured: CapturedQuery[] = [];
|
|
98
|
+
const seenQueries = new Set<string>();
|
|
99
|
+
|
|
100
|
+
// Ensure capture directory exists
|
|
101
|
+
if (!existsSync(CAPTURE_DIR)) mkdirSync(CAPTURE_DIR, { recursive: true });
|
|
102
|
+
|
|
103
|
+
// ─── Auto-navigation steps ───────────────────────────────────────────────────
|
|
104
|
+
|
|
105
|
+
interface GuideStep {
|
|
106
|
+
label: string;
|
|
107
|
+
url?: string;
|
|
108
|
+
clickSelector?: string;
|
|
109
|
+
expectedQueries: string[];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Resolve the logged-in user's screen name for profile-based URLs
|
|
113
|
+
async function getScreenName(): Promise<string | null> {
|
|
114
|
+
if (!navigationClient) return null;
|
|
115
|
+
try {
|
|
116
|
+
const result = await navigationClient.send('Runtime.evaluate', {
|
|
117
|
+
expression: `
|
|
118
|
+
(function() {
|
|
119
|
+
const link = document.querySelector('a[data-testid="AppTabBar_Profile_Link"]');
|
|
120
|
+
if (link) return link.getAttribute('href')?.replace('/', '') ?? null;
|
|
121
|
+
return null;
|
|
122
|
+
})()
|
|
123
|
+
`,
|
|
124
|
+
awaitPromise: false,
|
|
125
|
+
returnByValue: true,
|
|
126
|
+
}) as { result?: { value?: string | null } };
|
|
127
|
+
return result?.result?.value ?? null;
|
|
128
|
+
} catch {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const GUIDE_STEPS: GuideStep[] = [
|
|
134
|
+
{
|
|
135
|
+
label: 'Home timeline',
|
|
136
|
+
url: 'https://x.com/home',
|
|
137
|
+
expectedQueries: ['HomeTimeline', 'HomeLatestTimeline'],
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
label: 'Profile',
|
|
141
|
+
// URL set dynamically in runAutoMode after resolving screen name
|
|
142
|
+
clickSelector: 'a[data-testid="AppTabBar_Profile_Link"]',
|
|
143
|
+
expectedQueries: ['UserByScreenName', 'UserTweets'],
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
label: 'Tweet detail',
|
|
147
|
+
clickSelector: 'article[data-testid="tweet"] a[href*="/status/"]',
|
|
148
|
+
expectedQueries: ['TweetDetail'],
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
label: 'Search',
|
|
152
|
+
url: 'https://x.com/search?q=hello&src=typed_query',
|
|
153
|
+
expectedQueries: ['SearchTimeline'],
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
label: 'Bookmarks',
|
|
157
|
+
url: 'https://x.com/i/bookmarks',
|
|
158
|
+
expectedQueries: ['Bookmarks'],
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
label: 'Notifications',
|
|
162
|
+
url: 'https://x.com/notifications',
|
|
163
|
+
expectedQueries: ['NotificationsTimeline'],
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
label: 'Likes',
|
|
167
|
+
// URL set dynamically
|
|
168
|
+
expectedQueries: ['Likes'],
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
label: 'Followers',
|
|
172
|
+
// URL set dynamically
|
|
173
|
+
expectedQueries: ['Followers'],
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
label: 'Following',
|
|
177
|
+
// URL set dynamically
|
|
178
|
+
expectedQueries: ['Following'],
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
label: 'Media',
|
|
182
|
+
// URL set dynamically
|
|
183
|
+
expectedQueries: ['UserMedia'],
|
|
184
|
+
},
|
|
185
|
+
];
|
|
186
|
+
|
|
187
|
+
// ─── Discover Chrome tabs ────────────────────────────────────────────────────
|
|
188
|
+
|
|
189
|
+
const res = await fetch(`${CDP_BASE}/json/list`);
|
|
190
|
+
if (!res.ok) {
|
|
191
|
+
console.error('Chrome CDP not available. Run `vellum x refresh` first.');
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}
|
|
194
|
+
const targets = (await res.json()) as Array<{ type: string; url: string; webSocketDebuggerUrl: string }>;
|
|
195
|
+
const pages = targets.filter(t => t.type === 'page');
|
|
196
|
+
|
|
197
|
+
if (pages.length === 0) {
|
|
198
|
+
console.error('No pages found in Chrome.');
|
|
199
|
+
process.exit(1);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
console.log(`Found ${pages.length} tab(s). Attaching to all...`);
|
|
203
|
+
|
|
204
|
+
// ─── Pending request tracking ────────────────────────────────────────────────
|
|
205
|
+
|
|
206
|
+
const pendingRequests = new Map<string, { url: string; queryName: string }>();
|
|
207
|
+
// Resolve callbacks for --auto mode: queryName → resolve function
|
|
208
|
+
const queryWaiters = new Map<string, () => void>();
|
|
209
|
+
|
|
210
|
+
function notifyQuerySeen(queryName: string) {
|
|
211
|
+
seenQueries.add(queryName);
|
|
212
|
+
const waiter = queryWaiters.get(queryName);
|
|
213
|
+
if (waiter) {
|
|
214
|
+
queryWaiters.delete(queryName);
|
|
215
|
+
waiter();
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function waitForQuery(queryName: string, timeoutMs = 15000): Promise<boolean> {
|
|
220
|
+
if (seenQueries.has(queryName)) return Promise.resolve(true);
|
|
221
|
+
return new Promise(resolve => {
|
|
222
|
+
const timer = setTimeout(() => {
|
|
223
|
+
queryWaiters.delete(queryName);
|
|
224
|
+
resolve(false);
|
|
225
|
+
}, timeoutMs);
|
|
226
|
+
queryWaiters.set(queryName, () => {
|
|
227
|
+
clearTimeout(timer);
|
|
228
|
+
resolve(true);
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function waitForAnyQuery(queryNames: string[], timeoutMs = 15000): Promise<boolean> {
|
|
234
|
+
if (queryNames.some(q => seenQueries.has(q))) return Promise.resolve(true);
|
|
235
|
+
return new Promise(resolve => {
|
|
236
|
+
const timer = setTimeout(() => {
|
|
237
|
+
for (const q of queryNames) queryWaiters.delete(q);
|
|
238
|
+
resolve(false);
|
|
239
|
+
}, timeoutMs);
|
|
240
|
+
for (const q of queryNames) {
|
|
241
|
+
queryWaiters.set(q, () => {
|
|
242
|
+
clearTimeout(timer);
|
|
243
|
+
for (const q2 of queryNames) queryWaiters.delete(q2);
|
|
244
|
+
resolve(true);
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// ─── Attach to all tabs ──────────────────────────────────────────────────────
|
|
251
|
+
|
|
252
|
+
// We'll keep a reference to one client that's on an x.com tab for navigation
|
|
253
|
+
let navigationClient: CDPClient | null = null;
|
|
254
|
+
let navigationWsUrl: string | null = null;
|
|
255
|
+
|
|
256
|
+
for (const page of pages) {
|
|
257
|
+
const client = new CDPClient();
|
|
258
|
+
await client.connect(page.webSocketDebuggerUrl);
|
|
259
|
+
await client.send('Network.enable');
|
|
260
|
+
|
|
261
|
+
// Track which client is on an x.com tab for navigation
|
|
262
|
+
if (page.url.includes('x.com') || page.url.includes('twitter.com')) {
|
|
263
|
+
navigationClient = client;
|
|
264
|
+
navigationWsUrl = page.webSocketDebuggerUrl;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
client.on('Network.requestWillBeSent', (params) => {
|
|
268
|
+
const req = params.request as Record<string, unknown> | undefined;
|
|
269
|
+
const url = (req?.url ?? params.url) as string | undefined;
|
|
270
|
+
if (!url?.includes('/i/api/graphql/')) return;
|
|
271
|
+
|
|
272
|
+
const match = url.match(/\/graphql\/([^/]+)\/([^?]+)/);
|
|
273
|
+
const queryId = match?.[1] ?? 'unknown';
|
|
274
|
+
const queryName = match?.[2] ?? 'unknown';
|
|
275
|
+
const method = (req?.method as string) ?? 'GET';
|
|
276
|
+
|
|
277
|
+
// Apply relevance filter
|
|
278
|
+
if (!captureAll && !RELEVANT_QUERIES.has(queryName)) return;
|
|
279
|
+
|
|
280
|
+
let variables: unknown = undefined;
|
|
281
|
+
let features: unknown = undefined;
|
|
282
|
+
|
|
283
|
+
if (method === 'POST' && req?.postData) {
|
|
284
|
+
try {
|
|
285
|
+
const body = JSON.parse(req.postData as string);
|
|
286
|
+
variables = body.variables;
|
|
287
|
+
features = body.features;
|
|
288
|
+
} catch { /* ignore */ }
|
|
289
|
+
} else if (method === 'GET') {
|
|
290
|
+
try {
|
|
291
|
+
const u = new URL(url);
|
|
292
|
+
const v = u.searchParams.get('variables');
|
|
293
|
+
if (v) variables = JSON.parse(v);
|
|
294
|
+
const f = u.searchParams.get('features');
|
|
295
|
+
if (f) features = JSON.parse(f);
|
|
296
|
+
} catch { /* ignore */ }
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
console.log(`\n>>> ${method} ${queryName} (${queryId})`);
|
|
300
|
+
if (variables) console.log(` variables: ${JSON.stringify(variables).slice(0, 200)}`);
|
|
301
|
+
|
|
302
|
+
pendingRequests.set(params.requestId as string, { url, queryName });
|
|
303
|
+
captured.push({ queryName, queryId, method, variables, features, timestamp: Date.now() });
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
client.on('Network.responseReceived', (params) => {
|
|
307
|
+
const requestId = params.requestId as string;
|
|
308
|
+
const pending = pendingRequests.get(requestId);
|
|
309
|
+
if (!pending) return;
|
|
310
|
+
|
|
311
|
+
const response = params.response as Record<string, unknown>;
|
|
312
|
+
const status = response.status as number;
|
|
313
|
+
console.log(` <<< ${status}`);
|
|
314
|
+
|
|
315
|
+
// Get full response body
|
|
316
|
+
client.send('Network.getResponseBody', { requestId }).then((result) => {
|
|
317
|
+
const body = (result as Record<string, unknown>).body as string;
|
|
318
|
+
try {
|
|
319
|
+
const json = JSON.parse(body);
|
|
320
|
+
// Attach full response to the captured entry
|
|
321
|
+
const entry = [...captured].reverse().find(e => e.queryName === pending.queryName && !e.response);
|
|
322
|
+
if (entry) {
|
|
323
|
+
entry.response = json;
|
|
324
|
+
|
|
325
|
+
// Write individual file
|
|
326
|
+
const filename = `${pending.queryName}-${entry.timestamp}.json`;
|
|
327
|
+
Bun.write(`${CAPTURE_DIR}/${filename}`, JSON.stringify({
|
|
328
|
+
queryName: entry.queryName,
|
|
329
|
+
queryId: entry.queryId,
|
|
330
|
+
method: entry.method,
|
|
331
|
+
variables: entry.variables,
|
|
332
|
+
features: entry.features,
|
|
333
|
+
response: json,
|
|
334
|
+
}, null, 2));
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Notify waiters
|
|
338
|
+
notifyQuerySeen(pending.queryName);
|
|
339
|
+
} catch { /* ignore */ }
|
|
340
|
+
}).catch(() => { /* body not available */ });
|
|
341
|
+
|
|
342
|
+
pendingRequests.delete(requestId);
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// If no x.com tab found, use the first page for navigation
|
|
347
|
+
if (!navigationClient && pages.length > 0) {
|
|
348
|
+
navigationClient = new CDPClient();
|
|
349
|
+
await navigationClient.connect(pages[0].webSocketDebuggerUrl);
|
|
350
|
+
navigationWsUrl = pages[0].webSocketDebuggerUrl;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// ─── CDP navigation helpers ──────────────────────────────────────────────────
|
|
354
|
+
|
|
355
|
+
async function navigateTo(url: string): Promise<void> {
|
|
356
|
+
if (!navigationClient) return;
|
|
357
|
+
await navigationClient.send('Page.navigate', { url });
|
|
358
|
+
// Wait for page to load
|
|
359
|
+
await new Promise(r => setTimeout(r, 3000));
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
async function clickElement(selector: string): Promise<boolean> {
|
|
363
|
+
if (!navigationClient) return false;
|
|
364
|
+
try {
|
|
365
|
+
const result = await navigationClient.send('Runtime.evaluate', {
|
|
366
|
+
expression: `
|
|
367
|
+
(function() {
|
|
368
|
+
const el = document.querySelector(${JSON.stringify(selector)});
|
|
369
|
+
if (!el) return false;
|
|
370
|
+
el.scrollIntoView({ block: 'center' });
|
|
371
|
+
el.click();
|
|
372
|
+
return true;
|
|
373
|
+
})()
|
|
374
|
+
`,
|
|
375
|
+
awaitPromise: false,
|
|
376
|
+
returnByValue: true,
|
|
377
|
+
}) as { result?: { value?: boolean } };
|
|
378
|
+
return result?.result?.value === true;
|
|
379
|
+
} catch {
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
async function scrollDown(): Promise<void> {
|
|
385
|
+
if (!navigationClient) return;
|
|
386
|
+
try {
|
|
387
|
+
await navigationClient.send('Runtime.evaluate', {
|
|
388
|
+
expression: 'window.scrollBy(0, 800)',
|
|
389
|
+
awaitPromise: false,
|
|
390
|
+
});
|
|
391
|
+
} catch { /* ignore */ }
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// ─── Auto mode ───────────────────────────────────────────────────────────────
|
|
395
|
+
|
|
396
|
+
async function runAutoMode() {
|
|
397
|
+
console.log('\n🚗 Auto mode: navigating Chrome through X.com...\n');
|
|
398
|
+
|
|
399
|
+
// Enable Page domain for navigation
|
|
400
|
+
if (navigationClient) {
|
|
401
|
+
await navigationClient.send('Page.enable').catch(() => {});
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Navigate to home first to discover the screen name
|
|
405
|
+
await navigateTo('https://x.com/home');
|
|
406
|
+
const screenName = await getScreenName();
|
|
407
|
+
if (screenName) {
|
|
408
|
+
console.log(` Detected user: @${screenName}\n`);
|
|
409
|
+
// Fill in profile-based URLs
|
|
410
|
+
for (const step of GUIDE_STEPS) {
|
|
411
|
+
if (step.label === 'Likes') step.url = `https://x.com/${screenName}/likes`;
|
|
412
|
+
if (step.label === 'Followers') step.url = `https://x.com/${screenName}/followers`;
|
|
413
|
+
if (step.label === 'Following') step.url = `https://x.com/${screenName}/following`;
|
|
414
|
+
if (step.label === 'Media') step.url = `https://x.com/${screenName}/media`;
|
|
415
|
+
}
|
|
416
|
+
} else {
|
|
417
|
+
console.log(' Could not detect screen name — some steps will use click navigation\n');
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const completedSteps: string[] = [];
|
|
421
|
+
const failedSteps: string[] = [];
|
|
422
|
+
|
|
423
|
+
for (const step of GUIDE_STEPS) {
|
|
424
|
+
const alreadySeen = step.expectedQueries.some(q => seenQueries.has(q));
|
|
425
|
+
if (alreadySeen) {
|
|
426
|
+
console.log(` ✓ ${step.label} (already captured)`);
|
|
427
|
+
completedSteps.push(step.label);
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
process.stdout.write(` ⏳ ${step.label}...`);
|
|
432
|
+
|
|
433
|
+
// Navigate if URL provided
|
|
434
|
+
if (step.url) {
|
|
435
|
+
await navigateTo(step.url);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Click if selector provided
|
|
439
|
+
if (step.clickSelector) {
|
|
440
|
+
await new Promise(r => setTimeout(r, 1500)); // wait for page to settle
|
|
441
|
+
const clicked = await clickElement(step.clickSelector);
|
|
442
|
+
if (!clicked) {
|
|
443
|
+
// Try scrolling and clicking again
|
|
444
|
+
await scrollDown();
|
|
445
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
446
|
+
await clickElement(step.clickSelector);
|
|
447
|
+
}
|
|
448
|
+
await new Promise(r => setTimeout(r, 2000)); // wait for navigation
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Scroll to trigger lazy-loaded content
|
|
452
|
+
await scrollDown();
|
|
453
|
+
|
|
454
|
+
// Wait for any expected query
|
|
455
|
+
const seen = await waitForAnyQuery(step.expectedQueries, 10000);
|
|
456
|
+
|
|
457
|
+
if (seen) {
|
|
458
|
+
const captured = step.expectedQueries.filter(q => seenQueries.has(q));
|
|
459
|
+
console.log(`\r ✅ ${step.label} → ${captured.join(', ')}`);
|
|
460
|
+
completedSteps.push(step.label);
|
|
461
|
+
} else {
|
|
462
|
+
console.log(`\r ⚠️ ${step.label} → no queries captured (page may need manual interaction)`);
|
|
463
|
+
failedSteps.push(step.label);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
console.log(`\n🏁 Auto navigation complete: ${completedSteps.length}/${GUIDE_STEPS.length} steps succeeded`);
|
|
468
|
+
if (failedSteps.length > 0) {
|
|
469
|
+
console.log(` Missed: ${failedSteps.join(', ')}`);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// Finish
|
|
473
|
+
printSummary();
|
|
474
|
+
process.exit(0);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// ─── Summary ─────────────────────────────────────────────────────────────────
|
|
478
|
+
|
|
479
|
+
function printSummary() {
|
|
480
|
+
console.log(`\n\n${'='.repeat(60)}`);
|
|
481
|
+
console.log(` Captured ${captured.length} GraphQL requests`);
|
|
482
|
+
console.log(`${'='.repeat(60)}\n`);
|
|
483
|
+
|
|
484
|
+
// Dedupe by queryName
|
|
485
|
+
const seen = new Set<string>();
|
|
486
|
+
const unique = captured.filter(q => {
|
|
487
|
+
if (seen.has(q.queryName)) return false;
|
|
488
|
+
seen.add(q.queryName);
|
|
489
|
+
return true;
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
// Print table
|
|
493
|
+
const nameWidth = Math.max(25, ...unique.map(q => q.queryName.length));
|
|
494
|
+
const idWidth = 22;
|
|
495
|
+
const methodWidth = 6;
|
|
496
|
+
|
|
497
|
+
console.log(
|
|
498
|
+
` ${'Query'.padEnd(nameWidth)} ${'QueryID'.padEnd(idWidth)} ${'Method'.padEnd(methodWidth)} Variables`,
|
|
499
|
+
);
|
|
500
|
+
console.log(` ${'─'.repeat(nameWidth)} ${'─'.repeat(idWidth)} ${'─'.repeat(methodWidth)} ${'─'.repeat(30)}`);
|
|
501
|
+
|
|
502
|
+
for (const q of unique) {
|
|
503
|
+
const varKeys = q.variables && typeof q.variables === 'object'
|
|
504
|
+
? Object.keys(q.variables as Record<string, unknown>).join(', ')
|
|
505
|
+
: '—';
|
|
506
|
+
console.log(
|
|
507
|
+
` ${q.queryName.padEnd(nameWidth)} ${q.queryId.padEnd(idWidth)} ${q.method.padEnd(methodWidth)} ${varKeys}`,
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// Gap analysis
|
|
512
|
+
if (!captureAll) {
|
|
513
|
+
const notSeen = [...RELEVANT_QUERIES].filter(q => !seenQueries.has(q));
|
|
514
|
+
if (notSeen.length > 0) {
|
|
515
|
+
console.log(`\n ⚠️ Not captured (${notSeen.length}):`);
|
|
516
|
+
for (const q of notSeen) {
|
|
517
|
+
console.log(` • ${q}`);
|
|
518
|
+
}
|
|
519
|
+
} else {
|
|
520
|
+
console.log('\n ✅ All relevant queries captured!');
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// Save summary
|
|
525
|
+
const summaryPath = `${CAPTURE_DIR}/summary.json`;
|
|
526
|
+
const summary = {
|
|
527
|
+
capturedAt: new Date().toISOString(),
|
|
528
|
+
totalRequests: captured.length,
|
|
529
|
+
uniqueQueries: unique.map(q => ({
|
|
530
|
+
queryName: q.queryName,
|
|
531
|
+
queryId: q.queryId,
|
|
532
|
+
method: q.method,
|
|
533
|
+
variableKeys: q.variables && typeof q.variables === 'object'
|
|
534
|
+
? Object.keys(q.variables as Record<string, unknown>)
|
|
535
|
+
: [],
|
|
536
|
+
})),
|
|
537
|
+
notCaptured: captureAll ? [] : [...RELEVANT_QUERIES].filter(q => !seenQueries.has(q)),
|
|
538
|
+
};
|
|
539
|
+
Bun.write(summaryPath, JSON.stringify(summary, null, 2));
|
|
540
|
+
console.log(`\n Summary saved to ${summaryPath}`);
|
|
541
|
+
console.log(` Individual captures in ${CAPTURE_DIR}/`);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// ─── Main ────────────────────────────────────────────────────────────────────
|
|
545
|
+
|
|
546
|
+
if (autoMode) {
|
|
547
|
+
console.log('\nRecording X.com GraphQL requests (auto mode)...');
|
|
548
|
+
console.log(`Filter: ${captureAll ? 'ALL queries' : `${RELEVANT_QUERIES.size} relevant queries`}`);
|
|
549
|
+
// Give network listeners a moment to settle, then start auto-navigation
|
|
550
|
+
setTimeout(() => runAutoMode(), 1000);
|
|
551
|
+
} else {
|
|
552
|
+
console.log('\nRecording X.com GraphQL requests...');
|
|
553
|
+
console.log(`Filter: ${captureAll ? 'ALL queries' : `${RELEVANT_QUERIES.size} relevant queries`}`);
|
|
554
|
+
console.log('Browse X in Chrome — visit a profile, scroll tweets, search.');
|
|
555
|
+
console.log('Press Ctrl+C to stop and dump results.\n');
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Ctrl+C handler
|
|
559
|
+
process.on('SIGINT', () => {
|
|
560
|
+
printSummary();
|
|
561
|
+
process.exit(0);
|
|
562
|
+
});
|
|
@@ -47,10 +47,11 @@ const SWIFT_OMIT_ALLOWLIST = new Set<string>([
|
|
|
47
47
|
// Watcher messages — not yet consumed by the macOS client
|
|
48
48
|
'watcher_escalation',
|
|
49
49
|
'watcher_notification',
|
|
50
|
+
// Agent heartbeat alerts — not yet consumed by the macOS client
|
|
51
|
+
'agent_heartbeat_alert',
|
|
50
52
|
// Browser handoff — not yet consumed by the macOS client
|
|
51
53
|
'browser_handoff_request',
|
|
52
54
|
// Work item messages — not yet consumed by the macOS client
|
|
53
|
-
'work_item_create_response',
|
|
54
55
|
'work_item_get_response',
|
|
55
56
|
'work_item_run_task_response',
|
|
56
57
|
'work_item_status_changed',
|
package/scripts/test.sh
CHANGED
|
@@ -36,6 +36,11 @@ while IFS= read -r test_file; do
|
|
|
36
36
|
continue
|
|
37
37
|
fi
|
|
38
38
|
fi
|
|
39
|
+
# Always exclude benchmark files — run them with `bun run test:bench` instead
|
|
40
|
+
if [[ "$(basename "${test_file}")" == *.benchmark.test.ts ]]; then
|
|
41
|
+
continue
|
|
42
|
+
fi
|
|
43
|
+
|
|
39
44
|
test_files+=("${test_file}")
|
|
40
45
|
done < <(find src/__tests__ -maxdepth 1 -type f -name '*.test.ts' | sort)
|
|
41
46
|
|