vellum 0.2.1 → 0.2.2
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 +5 -2
- package/package.json +4 -2
- 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 +299 -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-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 +62 -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-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-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-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/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-provider.ts +10 -6
- package/src/calls/twilio-routes.ts +90 -76
- 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 +246 -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 +33 -0
- package/src/config/loader.ts +4 -1
- package/src/config/schema.ts +161 -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/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 +163 -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 +28 -4
- package/src/daemon/ipc-contract.ts +133 -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 +74 -10
- package/src/daemon/server.ts +143 -26
- package/src/daemon/session-agent-loop.ts +887 -0
- package/src/daemon/session-attachments.ts +28 -5
- 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/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/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/http-server.ts +108 -20
- 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 +5 -10
- 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 +220 -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 +77 -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 +242 -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
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract a JPEG thumbnail from a video file using ffmpeg.
|
|
3
|
+
*
|
|
4
|
+
* Writes the video to a temp file, runs ffmpeg to extract the first frame
|
|
5
|
+
* as a JPEG, and returns the result as a base64 string.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { tmpdir } from 'node:os';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import { writeFile, readFile, unlink } from 'node:fs/promises';
|
|
11
|
+
import { randomUUID } from 'node:crypto';
|
|
12
|
+
import { getLogger } from '../util/logger.js';
|
|
13
|
+
|
|
14
|
+
const log = getLogger('video-thumbnail');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Generate a JPEG thumbnail from base64-encoded video data.
|
|
18
|
+
* Returns null if ffmpeg is unavailable or extraction fails.
|
|
19
|
+
*/
|
|
20
|
+
export async function generateVideoThumbnail(dataBase64: string): Promise<string | null> {
|
|
21
|
+
const id = randomUUID();
|
|
22
|
+
const inputPath = join(tmpdir(), `vellum-thumb-in-${id}`);
|
|
23
|
+
const outputPath = join(tmpdir(), `vellum-thumb-out-${id}.jpg`);
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const videoBuffer = Buffer.from(dataBase64, 'base64');
|
|
27
|
+
await writeFile(inputPath, videoBuffer);
|
|
28
|
+
|
|
29
|
+
const proc = Bun.spawn([
|
|
30
|
+
'ffmpeg', '-y',
|
|
31
|
+
'-i', inputPath,
|
|
32
|
+
'-vframes', '1',
|
|
33
|
+
'-vf', 'scale=720:-2',
|
|
34
|
+
'-q:v', '5',
|
|
35
|
+
outputPath,
|
|
36
|
+
], { stderr: 'pipe' });
|
|
37
|
+
|
|
38
|
+
// Race against a 10s timeout to avoid hanging on slow/stuck ffmpeg
|
|
39
|
+
const exitCode = await Promise.race([
|
|
40
|
+
proc.exited,
|
|
41
|
+
new Promise<never>((_, reject) =>
|
|
42
|
+
setTimeout(() => { proc.kill(); reject(new Error('ffmpeg timed out')); }, 10_000)
|
|
43
|
+
),
|
|
44
|
+
]);
|
|
45
|
+
|
|
46
|
+
if (exitCode !== 0) {
|
|
47
|
+
log.warn({ exitCode }, 'ffmpeg thumbnail extraction failed');
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const jpegData = await readFile(outputPath);
|
|
52
|
+
return jpegData.toString('base64');
|
|
53
|
+
} catch (err) {
|
|
54
|
+
log.warn({ error: (err as Error).message }, 'Video thumbnail generation failed');
|
|
55
|
+
return null;
|
|
56
|
+
} finally {
|
|
57
|
+
try { await unlink(inputPath); } catch { /* ignore */ }
|
|
58
|
+
try { await unlink(outputPath); } catch { /* ignore */ }
|
|
59
|
+
}
|
|
60
|
+
}
|
package/src/doordash/client.ts
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
UPDATE_CART_ITEM_QUERY,
|
|
25
25
|
} from './queries.js';
|
|
26
26
|
import { loadCapturedQueries } from './query-extractor.js';
|
|
27
|
+
import { truncate } from '../util/truncate.js';
|
|
27
28
|
|
|
28
29
|
const GRAPHQL_BASE = 'https://www.doordash.com/graphql';
|
|
29
30
|
const CDP_BASE = 'http://localhost:9222';
|
|
@@ -50,6 +51,14 @@ export class SessionExpiredError extends Error {
|
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
53
|
|
|
54
|
+
/** Thrown when DoorDash returns HTTP 403 (rate limited). */
|
|
55
|
+
export class RateLimitError extends Error {
|
|
56
|
+
constructor(reason: string) {
|
|
57
|
+
super(reason);
|
|
58
|
+
this.name = 'RateLimitError';
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
53
62
|
function requireSession(): DoorDashSession {
|
|
54
63
|
const session = loadSession();
|
|
55
64
|
if (!session) {
|
|
@@ -152,8 +161,10 @@ async function cdpFetch(wsUrl: string, url: string, body: string): Promise<unkno
|
|
|
152
161
|
|
|
153
162
|
const parsed = typeof value === 'string' ? JSON.parse(value) : value;
|
|
154
163
|
if (parsed.__error) {
|
|
155
|
-
if (parsed.__status ===
|
|
164
|
+
if (parsed.__status === 401) {
|
|
156
165
|
reject(new SessionExpiredError('DoorDash session has expired.'));
|
|
166
|
+
} else if (parsed.__status === 403) {
|
|
167
|
+
reject(new RateLimitError('DoorDash rate limit hit (HTTP 403).'));
|
|
157
168
|
} else {
|
|
158
169
|
reject(new Error(parsed.__message ?? `HTTP ${parsed.__status}: ${parsed.__body ?? ''}`));
|
|
159
170
|
}
|
|
@@ -175,28 +186,54 @@ async function cdpFetch(wsUrl: string, url: string, body: string): Promise<unkno
|
|
|
175
186
|
});
|
|
176
187
|
}
|
|
177
188
|
|
|
189
|
+
let lastRequestTime = 0;
|
|
190
|
+
|
|
178
191
|
async function graphql<T = unknown>(
|
|
179
192
|
operationName: string,
|
|
180
193
|
query: string,
|
|
181
194
|
variables: Record<string, unknown>,
|
|
182
195
|
_session?: DoorDashSession,
|
|
183
196
|
): Promise<T> {
|
|
184
|
-
// Still require a session to exist (proves we've logged in)
|
|
185
197
|
if (!_session) requireSession();
|
|
186
198
|
|
|
187
199
|
const wsUrl = await findDoordashTab();
|
|
188
200
|
const url = `${GRAPHQL_BASE}/${operationName}?operation=${operationName}`;
|
|
189
201
|
const body = JSON.stringify({ operationName, variables, query });
|
|
190
202
|
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
203
|
+
const backoffSchedule = [5000, 10000, 20000];
|
|
204
|
+
|
|
205
|
+
for (let attempt = 0; ; attempt++) {
|
|
206
|
+
// Inter-request delay
|
|
207
|
+
const now = Date.now();
|
|
208
|
+
const elapsed = now - lastRequestTime;
|
|
209
|
+
if (lastRequestTime > 0 && elapsed < 2000) {
|
|
210
|
+
await new Promise(r => setTimeout(r, 2000 - elapsed));
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
try {
|
|
214
|
+
lastRequestTime = Date.now();
|
|
215
|
+
const json = (await cdpFetch(wsUrl, url, body)) as GraphQLResponse<T>;
|
|
216
|
+
|
|
217
|
+
if (json.errors?.length) {
|
|
218
|
+
const msgs = json.errors.map(e => e.message || JSON.stringify(e)).join('; ');
|
|
219
|
+
throw new Error(`Unexpected response from DoorDash API: ${msgs}`);
|
|
220
|
+
}
|
|
221
|
+
if (!json.data) {
|
|
222
|
+
throw new Error('Unexpected response format from DoorDash API');
|
|
223
|
+
}
|
|
224
|
+
return json.data;
|
|
225
|
+
} catch (err) {
|
|
226
|
+
if (err instanceof RateLimitError && attempt < backoffSchedule.length) {
|
|
227
|
+
const delay = backoffSchedule[attempt];
|
|
228
|
+
process.stderr.write(
|
|
229
|
+
`[doordash] Rate limited, retrying in ${delay / 1000}s... (attempt ${attempt + 1}/${backoffSchedule.length})\n`,
|
|
230
|
+
);
|
|
231
|
+
await new Promise(r => setTimeout(r, delay));
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
throw err;
|
|
235
|
+
}
|
|
198
236
|
}
|
|
199
|
-
return json.data;
|
|
200
237
|
}
|
|
201
238
|
|
|
202
239
|
// ---------------------------------------------------------------------------
|
|
@@ -240,7 +277,7 @@ export async function searchItems(query: string, opts?: { debug?: boolean }): Pr
|
|
|
240
277
|
);
|
|
241
278
|
if (opts?.debug) {
|
|
242
279
|
process.stderr.write(
|
|
243
|
-
`[debug] homePageFacetFeed raw: ${JSON.stringify(data.homePageFacetFeed)
|
|
280
|
+
`[debug] homePageFacetFeed raw: ${truncate(JSON.stringify(data.homePageFacetFeed), 3000, '')}\n`,
|
|
244
281
|
);
|
|
245
282
|
}
|
|
246
283
|
return extractSearchResults(data.homePageFacetFeed);
|
|
@@ -360,7 +397,7 @@ export async function getStoreMenu(
|
|
|
360
397
|
process.stderr.write(
|
|
361
398
|
`[debug] storepageFeed keys: ${Object.keys(feed).join(', ')}\n` +
|
|
362
399
|
`[debug] itemLists count: ${rawItemLists.length}, carousels count: ${rawCarousels.length}\n` +
|
|
363
|
-
`[debug] menuBook: ${JSON.stringify(menuBook)
|
|
400
|
+
`[debug] menuBook: ${truncate(JSON.stringify(menuBook), 2000, '')}\n`,
|
|
364
401
|
);
|
|
365
402
|
}
|
|
366
403
|
|
|
@@ -404,8 +441,8 @@ export async function getRetailStoreMenu(
|
|
|
404
441
|
process.stderr.write(
|
|
405
442
|
`[debug] retailStorePageFeed keys: ${Object.keys(feed).join(', ')}\n` +
|
|
406
443
|
`[debug] l1Categories count: ${l1Cats.length}, collections count: ${collections.length}\n` +
|
|
407
|
-
`[debug] page: ${JSON.stringify(page)
|
|
408
|
-
`[debug] collections sample: ${JSON.stringify(collections.slice(0, 2))
|
|
444
|
+
`[debug] page: ${truncate(JSON.stringify(page), 500, '')}\n` +
|
|
445
|
+
`[debug] collections sample: ${truncate(JSON.stringify(collections.slice(0, 2)), 2000, '')}\n`,
|
|
409
446
|
);
|
|
410
447
|
}
|
|
411
448
|
return extractRetailStoreInfo(data.retailStorePageFeed);
|
|
@@ -424,12 +461,31 @@ export interface ItemDetails {
|
|
|
424
461
|
id: string;
|
|
425
462
|
name: string;
|
|
426
463
|
required: boolean;
|
|
464
|
+
minSelections?: number;
|
|
465
|
+
maxSelections?: number;
|
|
427
466
|
choices: Array<{
|
|
428
467
|
id: string;
|
|
429
468
|
name: string;
|
|
430
469
|
price?: string;
|
|
470
|
+
unitAmount?: number;
|
|
471
|
+
defaultQuantity?: number;
|
|
472
|
+
nestedOptions?: Array<{
|
|
473
|
+
id: string;
|
|
474
|
+
name: string;
|
|
475
|
+
required: boolean;
|
|
476
|
+
choices: Array<{
|
|
477
|
+
id: string;
|
|
478
|
+
name: string;
|
|
479
|
+
price?: string;
|
|
480
|
+
}>;
|
|
481
|
+
}>;
|
|
431
482
|
}>;
|
|
432
483
|
}>;
|
|
484
|
+
specialInstructionsConfig?: {
|
|
485
|
+
maxLength: number;
|
|
486
|
+
placeholderText?: string;
|
|
487
|
+
isEnabled: boolean;
|
|
488
|
+
};
|
|
433
489
|
}
|
|
434
490
|
|
|
435
491
|
export async function getItemDetails(
|
|
@@ -595,7 +651,6 @@ export interface PaymentMethod {
|
|
|
595
651
|
id: string;
|
|
596
652
|
type: string;
|
|
597
653
|
last4: string;
|
|
598
|
-
cardBrand: string;
|
|
599
654
|
isDefault: boolean;
|
|
600
655
|
uuid: string;
|
|
601
656
|
}
|
|
@@ -614,7 +669,6 @@ export async function getPaymentMethods(): Promise<PaymentMethod[]> {
|
|
|
614
669
|
id: String(p.id ?? ''),
|
|
615
670
|
type: String(p.type ?? ''),
|
|
616
671
|
last4: String(p.last4 ?? ''),
|
|
617
|
-
cardBrand: String(p.cardBrand ?? ''),
|
|
618
672
|
isDefault: Boolean(p.isDefault),
|
|
619
673
|
uuid: String(p.paymentMethodUuid ?? p.uuid ?? ''),
|
|
620
674
|
}));
|
|
@@ -800,11 +854,25 @@ function extractStoreInfo(feed: Record<string, unknown>): StoreInfo {
|
|
|
800
854
|
};
|
|
801
855
|
}
|
|
802
856
|
|
|
857
|
+
function extractNestedOptions(extrasList: Array<Record<string, unknown>>): ItemDetails['options'][number]['choices'][number]['nestedOptions'] {
|
|
858
|
+
return extrasList.map(nested => ({
|
|
859
|
+
id: String(nested.id),
|
|
860
|
+
name: String(nested.name ?? ''),
|
|
861
|
+
required: !nested.isOptional,
|
|
862
|
+
choices: ((nested.options ?? []) as Array<Record<string, unknown>>).map(o => ({
|
|
863
|
+
id: String(o.id),
|
|
864
|
+
name: String(o.name ?? ''),
|
|
865
|
+
price: o.displayString as string | undefined,
|
|
866
|
+
})),
|
|
867
|
+
}));
|
|
868
|
+
}
|
|
869
|
+
|
|
803
870
|
function extractItemDetails(page: Record<string, unknown>): ItemDetails {
|
|
804
871
|
const header = (page.itemHeader ?? {}) as Record<string, unknown>;
|
|
805
872
|
const optionLists = (page.optionLists ?? []) as Array<Record<string, unknown>>;
|
|
873
|
+
const itemPreferences = page.itemPreferences as Record<string, unknown> | undefined;
|
|
806
874
|
|
|
807
|
-
|
|
875
|
+
const result: ItemDetails = {
|
|
808
876
|
id: String(header.id ?? ''),
|
|
809
877
|
name: String(header.name ?? ''),
|
|
810
878
|
description: header.description as string | undefined,
|
|
@@ -813,17 +881,43 @@ function extractItemDetails(page: Record<string, unknown>): ItemDetails {
|
|
|
813
881
|
currency: header.currency as string | undefined,
|
|
814
882
|
imageUrl: header.imgUrl as string | undefined,
|
|
815
883
|
menuId: header.menuId as string | undefined,
|
|
816
|
-
options: optionLists.map(ol =>
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
884
|
+
options: optionLists.map(ol => {
|
|
885
|
+
const choices = ((ol.options ?? []) as Array<Record<string, unknown>>).map(o => {
|
|
886
|
+
const choice: ItemDetails['options'][number]['choices'][number] = {
|
|
887
|
+
id: String(o.id),
|
|
888
|
+
name: String(o.name ?? ''),
|
|
889
|
+
price: o.displayString as string | undefined,
|
|
890
|
+
unitAmount: o.unitAmount as number | undefined,
|
|
891
|
+
defaultQuantity: o.defaultQuantity as number | undefined,
|
|
892
|
+
};
|
|
893
|
+
const nestedExtrasList = (o.nestedExtrasList ?? []) as Array<Record<string, unknown>>;
|
|
894
|
+
if (nestedExtrasList.length > 0) {
|
|
895
|
+
choice.nestedOptions = extractNestedOptions(nestedExtrasList);
|
|
896
|
+
}
|
|
897
|
+
return choice;
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
return {
|
|
901
|
+
id: String(ol.id),
|
|
902
|
+
name: String(ol.name ?? ''),
|
|
903
|
+
required: !ol.isOptional,
|
|
904
|
+
minSelections: ol.minNumOptions as number | undefined,
|
|
905
|
+
maxSelections: ol.maxNumOptions as number | undefined,
|
|
906
|
+
choices,
|
|
907
|
+
};
|
|
908
|
+
}),
|
|
826
909
|
};
|
|
910
|
+
|
|
911
|
+
if (itemPreferences) {
|
|
912
|
+
const specialInstructions = (itemPreferences.specialInstructions ?? {}) as Record<string, unknown>;
|
|
913
|
+
result.specialInstructionsConfig = {
|
|
914
|
+
maxLength: Number(specialInstructions.characterMaxLength ?? 500),
|
|
915
|
+
placeholderText: specialInstructions.placeholderText as string | undefined,
|
|
916
|
+
isEnabled: specialInstructions.isEnabled !== false,
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
return result;
|
|
827
921
|
}
|
|
828
922
|
|
|
829
923
|
function extractRetailStoreInfo(feed: Record<string, unknown>): StoreInfo {
|
package/src/doordash/queries.ts
CHANGED
package/src/export/formatter.ts
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* Conversation export formatters for markdown and JSON.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import { truncate } from '../util/truncate.js';
|
|
6
|
+
|
|
5
7
|
interface ContentBlock {
|
|
6
8
|
type: string;
|
|
7
9
|
text?: string;
|
|
@@ -44,7 +46,7 @@ function extractText(blocks: ContentBlock[]): string {
|
|
|
44
46
|
if (block.is_error) {
|
|
45
47
|
parts.push(`[Error: ${block.content ?? ''}]`);
|
|
46
48
|
} else {
|
|
47
|
-
parts.push(`[Result: ${(block.content ?? ''
|
|
49
|
+
parts.push(`[Result: ${truncate(block.content ?? '', 500)}]`);
|
|
48
50
|
}
|
|
49
51
|
break;
|
|
50
52
|
}
|
|
@@ -7,6 +7,7 @@ import type { FollowUp, FollowUpCreateInput, FollowUpStatus } from './types.js';
|
|
|
7
7
|
// ── Helpers ──────────────────────────────────────────────────────────
|
|
8
8
|
|
|
9
9
|
function parseFollowUp(row: typeof followups.$inferSelect): FollowUp {
|
|
10
|
+
const scheduleId = row.reminderCronId;
|
|
10
11
|
return {
|
|
11
12
|
id: row.id,
|
|
12
13
|
channel: row.channel,
|
|
@@ -15,7 +16,8 @@ function parseFollowUp(row: typeof followups.$inferSelect): FollowUp {
|
|
|
15
16
|
sentAt: row.sentAt,
|
|
16
17
|
expectedResponseBy: row.expectedResponseBy,
|
|
17
18
|
status: row.status as FollowUpStatus,
|
|
18
|
-
|
|
19
|
+
reminderScheduleId: scheduleId,
|
|
20
|
+
reminderCronId: scheduleId,
|
|
19
21
|
createdAt: row.createdAt,
|
|
20
22
|
updatedAt: row.updatedAt,
|
|
21
23
|
};
|
|
@@ -36,7 +38,7 @@ export function createFollowUp(input: FollowUpCreateInput): FollowUp {
|
|
|
36
38
|
sentAt: input.sentAt ?? now,
|
|
37
39
|
expectedResponseBy: input.expectedResponseBy ?? null,
|
|
38
40
|
status: 'pending',
|
|
39
|
-
reminderCronId: input.reminderCronId ?? null,
|
|
41
|
+
reminderCronId: input.reminderScheduleId ?? input.reminderCronId ?? null,
|
|
40
42
|
createdAt: now,
|
|
41
43
|
updatedAt: now,
|
|
42
44
|
}).run();
|
package/src/followups/types.ts
CHANGED
|
@@ -8,6 +8,9 @@ export interface FollowUp {
|
|
|
8
8
|
sentAt: number;
|
|
9
9
|
expectedResponseBy: number | null;
|
|
10
10
|
status: FollowUpStatus;
|
|
11
|
+
/** Canonical field — the recurrence schedule ID linked to this follow-up. */
|
|
12
|
+
reminderScheduleId: string | null;
|
|
13
|
+
/** @deprecated Use {@link reminderScheduleId}. Kept for migration compatibility. */
|
|
11
14
|
reminderCronId: string | null;
|
|
12
15
|
createdAt: number;
|
|
13
16
|
updatedAt: number;
|
|
@@ -19,5 +22,8 @@ export interface FollowUpCreateInput {
|
|
|
19
22
|
contactId?: string | null;
|
|
20
23
|
sentAt?: number;
|
|
21
24
|
expectedResponseBy?: number | null;
|
|
25
|
+
/** Canonical field — the recurrence schedule ID to link. */
|
|
26
|
+
reminderScheduleId?: string | null;
|
|
27
|
+
/** @deprecated Use {@link reminderScheduleId}. Kept for migration compatibility. */
|
|
22
28
|
reminderCronId?: string | null;
|
|
23
29
|
}
|
package/src/hooks/templates.ts
CHANGED
|
@@ -45,7 +45,7 @@ export function installTemplates(): void {
|
|
|
45
45
|
log.info({ hook: entry.name }, 'Installed hook template (disabled by default)');
|
|
46
46
|
} catch (err) {
|
|
47
47
|
// Clean up partially-copied directory so the next restart can retry
|
|
48
|
-
try { rmSync(targetDir, { recursive: true, force: true }); } catch {}
|
|
48
|
+
try { rmSync(targetDir, { recursive: true, force: true }); } catch (e) { log.debug({ err: e }, 'Cleanup of partial hook template directory failed'); }
|
|
49
49
|
log.warn({ err, hook: entry.name }, 'Failed to install hook template');
|
|
50
50
|
}
|
|
51
51
|
}
|