vellum 0.2.0 → 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 +161 -34
- package/src/__tests__/account-registry.test.ts +2 -1
- package/src/__tests__/agent-heartbeat-service.test.ts +250 -0
- package/src/__tests__/app-bundler.test.ts +12 -33
- 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 +5 -8
- 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 +454 -0
- 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-state.test.ts +133 -0
- package/src/__tests__/call-store.test.ts +691 -0
- package/src/__tests__/cli-discover.test.ts +1 -1
- package/src/__tests__/commit-message-enrichment-service.test.ts +550 -0
- 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 +348 -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__/doordash-session.test.ts +9 -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 +96 -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 +17 -10
- 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 +222 -0
- package/src/__tests__/run-orchestrator.test.ts +7 -7
- package/src/__tests__/runtime-attachment-metadata.test.ts +19 -20
- package/src/__tests__/runtime-runs-http.test.ts +5 -23
- package/src/__tests__/runtime-runs.test.ts +11 -11
- 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 +89 -16
- 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 +273 -2
- 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 +403 -0
- package/src/__tests__/workspace-heartbeat-service.test.ts +141 -2
- package/src/agent-heartbeat/agent-heartbeat-service.ts +155 -0
- package/src/bundler/app-bundler.ts +35 -14
- package/src/calls/call-bridge.ts +95 -0
- package/src/calls/call-constants.ts +48 -0
- package/src/calls/call-domain.ts +276 -0
- package/src/calls/call-orchestrator.ts +390 -0
- package/src/calls/call-recovery.ts +207 -0
- package/src/calls/call-state-machine.ts +68 -0
- package/src/calls/call-state.ts +64 -0
- package/src/calls/call-store.ts +416 -0
- package/src/calls/relay-server.ts +335 -0
- package/src/calls/speaker-identification.ts +213 -0
- package/src/calls/twilio-config.ts +34 -0
- package/src/calls/twilio-provider.ts +173 -0
- package/src/calls/twilio-routes.ts +250 -0
- package/src/calls/types.ts +37 -0
- package/src/calls/voice-provider.ts +14 -0
- package/src/cli/config-commands.ts +334 -0
- package/src/cli/core-commands.ts +776 -0
- package/src/cli/doordash.ts +256 -25
- 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 +163 -0
- 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.json +2 -2
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +2 -24
- 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 +44 -0
- package/src/config/loader.ts +4 -1
- package/src/config/schema.ts +218 -1
- package/src/config/system-prompt.ts +100 -6
- package/src/config/templates/IDENTITY.md +7 -0
- package/src/config/types.ts +5 -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 +192 -4
- 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 -271
- 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 +495 -39
- package/src/daemon/ipc-contract-inventory.json +40 -4
- package/src/daemon/ipc-contract.ts +185 -37
- package/src/daemon/ipc-protocol.ts +7 -2
- package/src/daemon/lifecycle.ts +48 -5
- package/src/daemon/main.ts +10 -4
- package/src/daemon/ride-shotgun-handler.ts +74 -10
- package/src/daemon/server.ts +144 -29
- 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 +222 -2
- package/src/daemon/session-usage.ts +0 -2
- package/src/daemon/session.ts +114 -1365
- 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 -1151
- package/src/media/gemini-image-service.ts +1 -1
- 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 +362 -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 +65 -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 +277 -25
- package/src/runtime/http-types.ts +0 -2
- package/src/runtime/routes/attachment-routes.ts +5 -6
- package/src/runtime/routes/call-routes.ts +140 -0
- package/src/runtime/routes/channel-routes.ts +12 -19
- package/src/runtime/routes/conversation-routes.ts +5 -9
- package/src/runtime/routes/run-routes.ts +4 -8
- package/src/runtime/run-orchestrator.ts +39 -6
- 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 +67 -0
- package/src/tools/calls/call-start.ts +73 -0
- package/src/tools/calls/call-status.ts +81 -0
- 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 +21 -5
- 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/registry.ts +2 -4
- 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 -6
- package/src/tools/tasks/task-delete.ts +69 -56
- package/src/tools/tasks/task-list.ts +31 -52
- package/src/tools/tasks/task-run.ts +74 -102
- package/src/tools/tasks/task-save.ts +33 -65
- package/src/tools/tasks/work-item-enqueue.ts +192 -134
- package/src/tools/tasks/work-item-list.ts +33 -78
- package/src/tools/tasks/work-item-remove.ts +60 -0
- package/src/tools/tasks/work-item-update.ts +114 -0
- package/src/tools/terminal/backends/native.ts +3 -1
- package/src/tools/tool-manifest.ts +20 -74
- package/src/tools/types.ts +6 -0
- package/src/tools/ui-surface/definitions.ts +6 -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 +236 -2
- package/src/workspace/commit-message-enrichment-service.ts +284 -0
- package/src/workspace/commit-message-provider.ts +95 -0
- package/src/workspace/git-service.ts +272 -52
- package/src/workspace/heartbeat-service.ts +70 -13
- package/src/workspace/provider-commit-message-generator.ts +242 -0
- package/src/workspace/turn-commit.ts +100 -51
- 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,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recording analyzer that processes NetworkRecordedEntry[] into a deduplicated
|
|
3
|
+
* API map. Collapses ID-like path segments into {id} placeholders so repeated
|
|
4
|
+
* calls to the same endpoint are grouped together.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { mkdirSync, writeFileSync } from 'fs';
|
|
8
|
+
import { join } from 'path';
|
|
9
|
+
import { getDataDir } from '../../util/platform.js';
|
|
10
|
+
import type { NetworkRecordedEntry } from './network-recording-types.js';
|
|
11
|
+
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Types
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
export interface ApiEndpoint {
|
|
17
|
+
method: string;
|
|
18
|
+
urlPattern: string;
|
|
19
|
+
exampleUrl: string;
|
|
20
|
+
queryParams: string[];
|
|
21
|
+
requestBodyKeys: string[];
|
|
22
|
+
responseStatus: number[];
|
|
23
|
+
responseBodyKeys: string[];
|
|
24
|
+
count: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ApiMapResult {
|
|
28
|
+
domain: string;
|
|
29
|
+
analyzedAt: number;
|
|
30
|
+
totalRequests: number;
|
|
31
|
+
endpoints: ApiEndpoint[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// Helpers
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
|
|
38
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
39
|
+
const NUMERIC_RE = /^\d+$/;
|
|
40
|
+
const HEX_HASH_RE = /^[0-9a-f]{8,}$/i;
|
|
41
|
+
|
|
42
|
+
/** Returns true when a path segment looks like a dynamic ID. */
|
|
43
|
+
function isIdSegment(segment: string): boolean {
|
|
44
|
+
if (NUMERIC_RE.test(segment)) return true;
|
|
45
|
+
if (UUID_RE.test(segment)) return true;
|
|
46
|
+
if (HEX_HASH_RE.test(segment)) return true;
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Replace ID-like path segments with `{id}`. */
|
|
51
|
+
function normalizePathSegments(pathname: string): string {
|
|
52
|
+
return pathname
|
|
53
|
+
.split('/')
|
|
54
|
+
.map((seg) => (isIdSegment(seg) ? '{id}' : seg))
|
|
55
|
+
.join('/');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Safely parse JSON, returning undefined on failure. */
|
|
59
|
+
function tryParseJson(text: string | undefined): Record<string, unknown> | undefined {
|
|
60
|
+
if (!text) return undefined;
|
|
61
|
+
try {
|
|
62
|
+
const parsed = JSON.parse(text);
|
|
63
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
64
|
+
return parsed as Record<string, unknown>;
|
|
65
|
+
}
|
|
66
|
+
} catch {
|
|
67
|
+
// not JSON
|
|
68
|
+
}
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
// Core analysis
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
|
|
76
|
+
export function analyzeApiMap(
|
|
77
|
+
entries: NetworkRecordedEntry[],
|
|
78
|
+
domain: string,
|
|
79
|
+
): ApiMapResult {
|
|
80
|
+
const groups = new Map<
|
|
81
|
+
string,
|
|
82
|
+
{
|
|
83
|
+
method: string;
|
|
84
|
+
urlPattern: string;
|
|
85
|
+
exampleUrl: string;
|
|
86
|
+
queryParams: Set<string>;
|
|
87
|
+
requestBodyKeys: Set<string>;
|
|
88
|
+
responseStatus: Set<number>;
|
|
89
|
+
responseBodyKeys: Set<string>;
|
|
90
|
+
count: number;
|
|
91
|
+
}
|
|
92
|
+
>();
|
|
93
|
+
|
|
94
|
+
for (const entry of entries) {
|
|
95
|
+
const { request, response } = entry;
|
|
96
|
+
let parsed: URL;
|
|
97
|
+
try {
|
|
98
|
+
parsed = new URL(request.url);
|
|
99
|
+
} catch {
|
|
100
|
+
continue; // skip malformed URLs
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const method = request.method.toUpperCase();
|
|
104
|
+
const urlPattern = `${parsed.hostname}${normalizePathSegments(parsed.pathname)}`;
|
|
105
|
+
const key = `${method} ${urlPattern}`;
|
|
106
|
+
|
|
107
|
+
let group = groups.get(key);
|
|
108
|
+
if (!group) {
|
|
109
|
+
group = {
|
|
110
|
+
method,
|
|
111
|
+
urlPattern,
|
|
112
|
+
exampleUrl: request.url,
|
|
113
|
+
queryParams: new Set(),
|
|
114
|
+
requestBodyKeys: new Set(),
|
|
115
|
+
responseStatus: new Set(),
|
|
116
|
+
responseBodyKeys: new Set(),
|
|
117
|
+
count: 0,
|
|
118
|
+
};
|
|
119
|
+
groups.set(key, group);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
group.count++;
|
|
123
|
+
|
|
124
|
+
// Collect query param keys
|
|
125
|
+
for (const paramKey of parsed.searchParams.keys()) {
|
|
126
|
+
group.queryParams.add(paramKey);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Request body keys (POST/PUT/PATCH)
|
|
130
|
+
if (['POST', 'PUT', 'PATCH'].includes(method)) {
|
|
131
|
+
const body = tryParseJson(request.postData);
|
|
132
|
+
if (body) {
|
|
133
|
+
for (const k of Object.keys(body)) {
|
|
134
|
+
group.requestBodyKeys.add(k);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Response status
|
|
140
|
+
if (response) {
|
|
141
|
+
group.responseStatus.add(response.status);
|
|
142
|
+
|
|
143
|
+
// Response body keys
|
|
144
|
+
const resBody = tryParseJson(response.body);
|
|
145
|
+
if (resBody) {
|
|
146
|
+
for (const k of Object.keys(resBody)) {
|
|
147
|
+
group.responseBodyKeys.add(k);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const endpoints: ApiEndpoint[] = Array.from(groups.values()).map((g) => ({
|
|
154
|
+
method: g.method,
|
|
155
|
+
urlPattern: g.urlPattern,
|
|
156
|
+
exampleUrl: g.exampleUrl,
|
|
157
|
+
queryParams: Array.from(g.queryParams).sort(),
|
|
158
|
+
requestBodyKeys: Array.from(g.requestBodyKeys).sort(),
|
|
159
|
+
responseStatus: Array.from(g.responseStatus).sort((a, b) => a - b),
|
|
160
|
+
responseBodyKeys: Array.from(g.responseBodyKeys).sort(),
|
|
161
|
+
count: g.count,
|
|
162
|
+
}));
|
|
163
|
+
|
|
164
|
+
// Sort by count descending, then by urlPattern for stability
|
|
165
|
+
endpoints.sort((a, b) => b.count - a.count || a.urlPattern.localeCompare(b.urlPattern));
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
domain,
|
|
169
|
+
analyzedAt: Date.now(),
|
|
170
|
+
totalRequests: entries.length,
|
|
171
|
+
endpoints,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
// Persistence
|
|
177
|
+
// ---------------------------------------------------------------------------
|
|
178
|
+
|
|
179
|
+
export function saveApiMap(domain: string, result: ApiMapResult): string {
|
|
180
|
+
const dir = join(getDataDir(), 'api-maps');
|
|
181
|
+
mkdirSync(dir, { recursive: true });
|
|
182
|
+
|
|
183
|
+
const timestamp = Date.now();
|
|
184
|
+
const filePath = join(dir, `${domain}-${timestamp}.json`);
|
|
185
|
+
writeFileSync(filePath, JSON.stringify(result, null, 2));
|
|
186
|
+
return filePath;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ---------------------------------------------------------------------------
|
|
190
|
+
// Pretty-print
|
|
191
|
+
// ---------------------------------------------------------------------------
|
|
192
|
+
|
|
193
|
+
export function printApiMapTable(result: ApiMapResult): void {
|
|
194
|
+
console.log(`\nAPI Map for ${result.domain} — ${result.totalRequests} total requests, ${result.endpoints.length} unique endpoints\n`);
|
|
195
|
+
|
|
196
|
+
const header = ['Method', 'URL Pattern', 'Count', 'Status', 'Query Params'];
|
|
197
|
+
const rows = result.endpoints.map((ep) => [
|
|
198
|
+
ep.method,
|
|
199
|
+
ep.urlPattern,
|
|
200
|
+
String(ep.count),
|
|
201
|
+
ep.responseStatus.join(',') || '-',
|
|
202
|
+
ep.queryParams.join(',') || '-',
|
|
203
|
+
]);
|
|
204
|
+
|
|
205
|
+
// Calculate column widths
|
|
206
|
+
const widths = header.map((h, i) =>
|
|
207
|
+
Math.max(h.length, ...rows.map((r) => r[i].length)),
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
const sep = widths.map((w) => '-'.repeat(w)).join(' | ');
|
|
211
|
+
const fmt = (row: string[]) =>
|
|
212
|
+
row.map((cell, i) => cell.padEnd(widths[i])).join(' | ');
|
|
213
|
+
|
|
214
|
+
console.log(fmt(header));
|
|
215
|
+
console.log(sep);
|
|
216
|
+
for (const row of rows) {
|
|
217
|
+
console.log(fmt(row));
|
|
218
|
+
}
|
|
219
|
+
console.log();
|
|
220
|
+
}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CDP-based auto-navigation for any domain.
|
|
3
|
+
*
|
|
4
|
+
* Drives Chrome through a domain's pages by discovering internal links,
|
|
5
|
+
* so the NetworkRecorder captures the API surface without manual browsing.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { getLogger } from '../../util/logger.js';
|
|
9
|
+
|
|
10
|
+
const log = getLogger('auto-navigate');
|
|
11
|
+
|
|
12
|
+
const CDP_BASE = 'http://localhost:9222';
|
|
13
|
+
const MAX_PAGES = 15;
|
|
14
|
+
const PAGE_WAIT_MS = 3500;
|
|
15
|
+
const SCROLL_WAIT_MS = 2000;
|
|
16
|
+
|
|
17
|
+
/** Minimal CDP client — connects to one page tab. */
|
|
18
|
+
class MiniCDP {
|
|
19
|
+
private ws: WebSocket | null = null;
|
|
20
|
+
private nextId = 1;
|
|
21
|
+
private callbacks = new Map<number, { resolve: (v: unknown) => void; reject: (e: Error) => void }>();
|
|
22
|
+
|
|
23
|
+
async connect(wsUrl: string): Promise<void> {
|
|
24
|
+
return new Promise((resolve, reject) => {
|
|
25
|
+
const ws = new WebSocket(wsUrl);
|
|
26
|
+
ws.onopen = () => { this.ws = ws; resolve(); };
|
|
27
|
+
ws.onerror = (e) => reject(new Error(`CDP error: ${e}`));
|
|
28
|
+
ws.onclose = () => {
|
|
29
|
+
this.ws = null;
|
|
30
|
+
for (const [, cb] of this.callbacks) {
|
|
31
|
+
cb.reject(new Error('WebSocket closed'));
|
|
32
|
+
}
|
|
33
|
+
this.callbacks.clear();
|
|
34
|
+
};
|
|
35
|
+
ws.onmessage = (event) => {
|
|
36
|
+
const msg = JSON.parse(String(event.data));
|
|
37
|
+
if (msg.id != null) {
|
|
38
|
+
const cb = this.callbacks.get(msg.id);
|
|
39
|
+
if (cb) {
|
|
40
|
+
this.callbacks.delete(msg.id);
|
|
41
|
+
msg.error ? cb.reject(new Error(msg.error.message)) : cb.resolve(msg.result);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async send(method: string, params?: Record<string, unknown>): Promise<unknown> {
|
|
49
|
+
if (!this.ws) throw new Error('Not connected');
|
|
50
|
+
const id = this.nextId++;
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
this.callbacks.set(id, { resolve, reject });
|
|
53
|
+
this.ws!.send(JSON.stringify({ id, method, params }));
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
close() { this.ws?.close(); }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Navigate Chrome through a domain's pages to trigger API calls.
|
|
62
|
+
* Discovers internal links from the DOM and visits up to ~15 unique paths.
|
|
63
|
+
*
|
|
64
|
+
* @param domain The domain to crawl (e.g. "example.com").
|
|
65
|
+
* @param abortSignal Optional signal to stop navigation early.
|
|
66
|
+
* @returns List of visited page URLs.
|
|
67
|
+
*/
|
|
68
|
+
export async function autoNavigate(domain: string, abortSignal?: { aborted: boolean }): Promise<string[]> {
|
|
69
|
+
let wsUrl: string | null = null;
|
|
70
|
+
try {
|
|
71
|
+
const res = await fetch(`${CDP_BASE}/json/list`);
|
|
72
|
+
if (!res.ok) {
|
|
73
|
+
log.warn('CDP not available for auto-navigation');
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
const targets = (await res.json()) as Array<{ type: string; url: string; webSocketDebuggerUrl: string }>;
|
|
77
|
+
const domainTab = targets.find(t => {
|
|
78
|
+
if (t.type !== 'page') return false;
|
|
79
|
+
try {
|
|
80
|
+
const hostname = new URL(t.url).hostname;
|
|
81
|
+
return hostname === domain || hostname.endsWith('.' + domain);
|
|
82
|
+
} catch { return false; }
|
|
83
|
+
});
|
|
84
|
+
wsUrl = domainTab?.webSocketDebuggerUrl ?? targets.find(t => t.type === 'page')?.webSocketDebuggerUrl ?? null;
|
|
85
|
+
} catch (err) {
|
|
86
|
+
log.warn({ err }, 'Failed to discover Chrome tabs');
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!wsUrl) {
|
|
91
|
+
log.warn('No Chrome tab found for auto-navigation');
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const cdp = new MiniCDP();
|
|
96
|
+
try {
|
|
97
|
+
await cdp.connect(wsUrl);
|
|
98
|
+
} catch (err) {
|
|
99
|
+
log.warn({ err }, 'Failed to connect CDP for auto-navigation');
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
await cdp.send('Page.enable').catch(() => {});
|
|
104
|
+
|
|
105
|
+
const rootUrl = `https://${domain}/`;
|
|
106
|
+
const visited = new Set<string>();
|
|
107
|
+
const visitedUrls: string[] = [];
|
|
108
|
+
|
|
109
|
+
// Navigate to the domain root first
|
|
110
|
+
try {
|
|
111
|
+
await cdp.send('Page.navigate', { url: rootUrl });
|
|
112
|
+
await sleep(PAGE_WAIT_MS);
|
|
113
|
+
visited.add('/');
|
|
114
|
+
visitedUrls.push(rootUrl);
|
|
115
|
+
log.info({ url: rootUrl }, 'Visited root page');
|
|
116
|
+
} catch (err) {
|
|
117
|
+
log.warn({ err }, 'Failed to navigate to domain root');
|
|
118
|
+
cdp.close();
|
|
119
|
+
return [];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (abortSignal?.aborted) { cdp.close(); return visitedUrls; }
|
|
123
|
+
|
|
124
|
+
// Scroll the root page to trigger lazy content
|
|
125
|
+
await scrollPage(cdp);
|
|
126
|
+
await sleep(SCROLL_WAIT_MS);
|
|
127
|
+
|
|
128
|
+
// Click common interactive elements on the root page
|
|
129
|
+
await clickInteractiveElements(cdp);
|
|
130
|
+
await sleep(SCROLL_WAIT_MS);
|
|
131
|
+
|
|
132
|
+
// Discover internal links from the current page
|
|
133
|
+
let discoveredLinks = await discoverInternalLinks(cdp, domain);
|
|
134
|
+
log.info({ count: discoveredLinks.length }, 'Discovered internal links from root');
|
|
135
|
+
|
|
136
|
+
// Visit discovered pages
|
|
137
|
+
for (const link of discoveredLinks) {
|
|
138
|
+
if (abortSignal?.aborted) break;
|
|
139
|
+
if (visited.size >= MAX_PAGES) break;
|
|
140
|
+
if (visited.has(link.key)) continue;
|
|
141
|
+
|
|
142
|
+
const url = link.url;
|
|
143
|
+
log.info({ url }, 'Auto-navigate visiting page');
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
await cdp.send('Page.navigate', { url });
|
|
147
|
+
await sleep(PAGE_WAIT_MS);
|
|
148
|
+
visited.add(link.key);
|
|
149
|
+
visitedUrls.push(url);
|
|
150
|
+
|
|
151
|
+
// Scroll to trigger lazy-loaded content
|
|
152
|
+
await scrollPage(cdp);
|
|
153
|
+
await sleep(SCROLL_WAIT_MS);
|
|
154
|
+
|
|
155
|
+
// Click interactive elements to trigger more API calls
|
|
156
|
+
await clickInteractiveElements(cdp);
|
|
157
|
+
await sleep(1500);
|
|
158
|
+
|
|
159
|
+
// Discover more links from this page
|
|
160
|
+
const newLinks = await discoverInternalLinks(cdp, domain);
|
|
161
|
+
for (const nl of newLinks) {
|
|
162
|
+
if (!visited.has(nl.key) && !discoveredLinks.some(l => l.key === nl.key)) {
|
|
163
|
+
discoveredLinks.push(nl);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
log.info({ url }, 'Auto-navigate page completed');
|
|
168
|
+
} catch (err) {
|
|
169
|
+
log.warn({ err, url }, 'Auto-navigate page failed');
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
cdp.close();
|
|
174
|
+
log.info({ visited: visitedUrls.length, total: discoveredLinks.length + 1 }, 'Auto-navigation finished');
|
|
175
|
+
return visitedUrls;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
interface DiscoveredLink {
|
|
179
|
+
/** Full URL to navigate to (preserves subdomain). */
|
|
180
|
+
url: string;
|
|
181
|
+
/** Deduplication key: origin + pathname. */
|
|
182
|
+
key: string;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Extract internal links from the current page DOM, preserving subdomains. */
|
|
186
|
+
async function discoverInternalLinks(cdp: MiniCDP, domain: string): Promise<DiscoveredLink[]> {
|
|
187
|
+
try {
|
|
188
|
+
const result = await cdp.send('Runtime.evaluate', {
|
|
189
|
+
expression: `
|
|
190
|
+
(function() {
|
|
191
|
+
const domain = ${JSON.stringify(domain)};
|
|
192
|
+
const seen = new Set();
|
|
193
|
+
const links = [];
|
|
194
|
+
for (const a of document.querySelectorAll('a[href]')) {
|
|
195
|
+
const href = a.getAttribute('href');
|
|
196
|
+
if (!href) continue;
|
|
197
|
+
try {
|
|
198
|
+
const url = new URL(href, location.origin);
|
|
199
|
+
if (url.hostname !== domain && !url.hostname.endsWith('.' + domain)) continue;
|
|
200
|
+
const path = url.pathname;
|
|
201
|
+
// Skip anchors, query-only links, file downloads, and trivial paths
|
|
202
|
+
if (path === '/' || path === '') continue;
|
|
203
|
+
if (path.match(/\\.(png|jpg|jpeg|gif|svg|css|js|woff|pdf|zip)$/i)) continue;
|
|
204
|
+
const key = url.origin + url.pathname;
|
|
205
|
+
if (!seen.has(key)) {
|
|
206
|
+
seen.add(key);
|
|
207
|
+
links.push({ url: url.origin + url.pathname, key });
|
|
208
|
+
}
|
|
209
|
+
} catch { /* skip malformed URLs */ }
|
|
210
|
+
}
|
|
211
|
+
return links;
|
|
212
|
+
})()
|
|
213
|
+
`,
|
|
214
|
+
awaitPromise: false,
|
|
215
|
+
returnByValue: true,
|
|
216
|
+
}) as { result?: { value?: DiscoveredLink[] } };
|
|
217
|
+
return result?.result?.value ?? [];
|
|
218
|
+
} catch {
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** Scroll the page to trigger lazy-loaded content. */
|
|
224
|
+
async function scrollPage(cdp: MiniCDP): Promise<void> {
|
|
225
|
+
await cdp.send('Runtime.evaluate', {
|
|
226
|
+
expression: 'window.scrollBy(0, 800)',
|
|
227
|
+
awaitPromise: false,
|
|
228
|
+
}).catch(() => {});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** Click common interactive elements (tabs, nav buttons) to trigger API calls. */
|
|
232
|
+
async function clickInteractiveElements(cdp: MiniCDP): Promise<void> {
|
|
233
|
+
const selectors = [
|
|
234
|
+
'nav a:not([href="/"])',
|
|
235
|
+
'[role="tab"]',
|
|
236
|
+
'[role="tablist"] button',
|
|
237
|
+
'button[data-tab]',
|
|
238
|
+
'.tab, .nav-tab, .nav-link',
|
|
239
|
+
];
|
|
240
|
+
|
|
241
|
+
for (const selector of selectors) {
|
|
242
|
+
await clickInPage(cdp, selector);
|
|
243
|
+
await sleep(800);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async function clickInPage(cdp: MiniCDP, selector: string): Promise<boolean> {
|
|
248
|
+
try {
|
|
249
|
+
const result = await cdp.send('Runtime.evaluate', {
|
|
250
|
+
expression: `
|
|
251
|
+
(function() {
|
|
252
|
+
const el = document.querySelector(${JSON.stringify(selector)});
|
|
253
|
+
if (!el) return false;
|
|
254
|
+
el.scrollIntoView({ block: 'center' });
|
|
255
|
+
el.click();
|
|
256
|
+
return true;
|
|
257
|
+
})()
|
|
258
|
+
`,
|
|
259
|
+
awaitPromise: false,
|
|
260
|
+
returnByValue: true,
|
|
261
|
+
}) as { result?: { value?: boolean } };
|
|
262
|
+
return result?.result?.value === true;
|
|
263
|
+
} catch {
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function sleep(ms: number): Promise<void> {
|
|
269
|
+
return new Promise(r => setTimeout(r, ms));
|
|
270
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ToolContext, ToolExecutionResult } from '../types.js';
|
|
2
2
|
import type { ImageContent } from '../../providers/types.js';
|
|
3
3
|
import { getLogger } from '../../util/logger.js';
|
|
4
|
+
import { truncate } from '../../util/truncate.js';
|
|
4
5
|
import {
|
|
5
6
|
parseUrl,
|
|
6
7
|
isPrivateOrLocalHost,
|
|
@@ -826,7 +827,7 @@ export async function executeBrowserWaitFor(
|
|
|
826
827
|
`document.body?.innerText?.includes(${escaped})`,
|
|
827
828
|
{ timeout },
|
|
828
829
|
);
|
|
829
|
-
return { content: `Text "${text
|
|
830
|
+
return { content: `Text "${truncate(text, 80)}" appeared on page.`, isError: false };
|
|
830
831
|
}
|
|
831
832
|
|
|
832
833
|
// duration mode (milliseconds)
|
|
@@ -433,7 +433,7 @@ class BrowserManager {
|
|
|
433
433
|
if (this.browserCdpSession) {
|
|
434
434
|
try {
|
|
435
435
|
await this.browserCdpSession.detach();
|
|
436
|
-
} catch {}
|
|
436
|
+
} catch (e) { log.debug({ err: e }, 'CDP session detach failed during shutdown'); }
|
|
437
437
|
this.browserCdpSession = null;
|
|
438
438
|
this.browserWindowId = null;
|
|
439
439
|
}
|
|
@@ -489,7 +489,7 @@ class BrowserManager {
|
|
|
489
489
|
try {
|
|
490
490
|
await cdp.send('Page.stopScreencast');
|
|
491
491
|
await cdp.detach();
|
|
492
|
-
} catch {}
|
|
492
|
+
} catch (e) { log.debug({ err: e }, 'Screencast stop / CDP detach failed during cleanup'); }
|
|
493
493
|
this.cdpSessions.delete(sessionId);
|
|
494
494
|
this.screencastCallbacks.delete(sessionId);
|
|
495
495
|
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { getLogger } from '../../util/logger.js';
|
|
9
|
+
import { truncate } from '../../util/truncate.js';
|
|
9
10
|
import type { NetworkRecordedEntry, NetworkRecordedRequest, ExtractedCredential } from './network-recording-types.js';
|
|
10
11
|
|
|
11
12
|
const log = getLogger('network-recorder');
|
|
@@ -54,7 +55,7 @@ class DirectCDPClient {
|
|
|
54
55
|
for (const h of handlers) h(msg.params ?? {});
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
|
-
} catch {}
|
|
58
|
+
} catch (e) { log.debug({ err: e }, 'Failed to parse CDP WebSocket message'); }
|
|
58
59
|
};
|
|
59
60
|
});
|
|
60
61
|
}
|
|
@@ -195,7 +196,7 @@ export class NetworkRecorder {
|
|
|
195
196
|
for (const client of this.pageClients) {
|
|
196
197
|
try {
|
|
197
198
|
await client.send('Network.disable');
|
|
198
|
-
} catch {}
|
|
199
|
+
} catch (e) { log.debug({ err: e }, 'Network.disable failed during cleanup'); }
|
|
199
200
|
client.close();
|
|
200
201
|
}
|
|
201
202
|
this.pageClients = [];
|
|
@@ -273,7 +274,7 @@ export class NetworkRecorder {
|
|
|
273
274
|
const method = (request.method as string) ?? 'GET';
|
|
274
275
|
const postData = request.postData as string | undefined;
|
|
275
276
|
|
|
276
|
-
log.debug({ url: url
|
|
277
|
+
log.debug({ url: truncate(url, 120, ''), method, requestId }, 'Request captured');
|
|
277
278
|
|
|
278
279
|
const recordedRequest: NetworkRecordedRequest = { method, url, headers, postData };
|
|
279
280
|
const entry: NetworkRecordedEntry = {
|
|
@@ -307,7 +308,7 @@ export class NetworkRecorder {
|
|
|
307
308
|
this.loginSignals.some(sig => entry.request.url.includes(sig))
|
|
308
309
|
) {
|
|
309
310
|
this.loginDetectedFired = true;
|
|
310
|
-
log.info({ url: entry.request.url
|
|
311
|
+
log.info({ url: truncate(entry.request.url, 120, '') }, 'Login detected — will auto-stop in 5s');
|
|
311
312
|
// Delay to let remaining network requests (cookies, session data) settle
|
|
312
313
|
setTimeout(() => this.onLoginDetected?.(), 5000);
|
|
313
314
|
}
|