@vellumai/assistant 0.4.45 → 0.4.48
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/ARCHITECTURE.md +6 -6
- package/docs/architecture/memory.md +1 -1
- package/docs/architecture/scheduling.md +2 -3
- package/docs/architecture/security.md +5 -5
- package/docs/trusted-contact-access.md +5 -6
- package/package.json +4 -1
- package/src/__tests__/avatar-e2e.test.ts +18 -219
- package/src/__tests__/avatar-generator.test.ts +5 -57
- package/src/__tests__/browser-fill-credential.test.ts +5 -2
- package/src/__tests__/bundled-skill-retrieval-guard.test.ts +2 -1
- package/src/__tests__/channel-readiness-routes.test.ts +20 -19
- package/src/__tests__/cli.test.ts +23 -0
- package/src/__tests__/credential-broker-browser-fill.test.ts +23 -22
- package/src/__tests__/credential-broker-server-use.test.ts +22 -21
- package/src/__tests__/credential-broker.test.ts +2 -1
- package/src/__tests__/credential-metadata-store.test.ts +240 -18
- package/src/__tests__/credential-resolve.test.ts +5 -4
- package/src/__tests__/credential-security-e2e.test.ts +8 -8
- package/src/__tests__/credential-security-invariants.test.ts +104 -7
- package/src/__tests__/credential-vault-unit.test.ts +22 -20
- package/src/__tests__/credential-vault.test.ts +284 -12
- package/src/__tests__/credentials-cli.test.ts +11 -6
- package/src/__tests__/gateway-only-enforcement.test.ts +4 -2
- package/src/__tests__/gemini-image-service.test.ts +75 -45
- package/src/__tests__/gemini-provider.test.ts +9 -6
- package/src/__tests__/guardian-action-conversation-turn.test.ts +1 -33
- package/src/__tests__/guardian-action-copy-generator.test.ts +0 -20
- package/src/__tests__/guardian-action-followup-executor.test.ts +1 -28
- package/src/__tests__/guardian-action-followup-store.test.ts +1 -1
- package/src/__tests__/guardian-grant-minting.test.ts +35 -0
- package/src/__tests__/integration-status.test.ts +53 -21
- package/src/__tests__/managed-proxy-context.test.ts +5 -3
- package/src/__tests__/media-generate-image.test.ts +63 -2
- package/src/__tests__/media-reuse-story.e2e.test.ts +7 -3
- package/src/__tests__/messaging-send-tool.test.ts +4 -6
- package/src/__tests__/provider-fail-open-selection.test.ts +3 -1
- package/src/__tests__/provider-managed-proxy-integration.test.ts +70 -6
- package/src/__tests__/schedule-store.test.ts +1 -1
- package/src/__tests__/schema-transforms.test.ts +226 -0
- package/src/__tests__/script-proxy-injection-runtime.test.ts +23 -13
- package/src/__tests__/script-proxy-policy-runtime.test.ts +1 -1
- package/src/__tests__/script-proxy-session-manager.test.ts +1 -1
- package/src/__tests__/secret-onetime-send.test.ts +5 -3
- package/src/__tests__/session-messaging-secret-redirect.test.ts +5 -4
- package/src/__tests__/skills-uninstall.test.ts +2 -2
- package/src/__tests__/skills.test.ts +0 -9
- package/src/__tests__/slack-channel-config.test.ts +9 -8
- package/src/__tests__/slack-share-routes.test.ts +11 -6
- package/src/__tests__/telegram-bot-username-resolution.test.ts +3 -0
- package/src/__tests__/twilio-config.test.ts +2 -1
- package/src/__tests__/twilio-provider.test.ts +4 -2
- package/src/__tests__/twilio-routes.test.ts +5 -4
- package/src/__tests__/verification-control-plane-policy.test.ts +1 -1
- package/src/approvals/AGENTS.md +1 -1
- package/src/calls/call-domain.ts +7 -4
- package/src/calls/twilio-config.ts +2 -1
- package/src/calls/twilio-provider.ts +2 -1
- package/src/calls/twilio-rest.ts +2 -2
- package/src/cli/commands/browser-relay.ts +40 -15
- package/src/cli/commands/credentials.ts +9 -8
- package/src/cli/commands/oauth.ts +1 -1
- package/src/cli.ts +3 -2
- package/src/config/bundled-skills/claude-code/TOOLS.json +0 -4
- package/src/config/bundled-skills/contacts/tools/google-contacts.ts +29 -32
- package/src/config/bundled-skills/gmail/SKILL.md +4 -4
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +54 -61
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +25 -28
- package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +14 -17
- package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +39 -44
- package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +61 -58
- package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +50 -49
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +11 -13
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +148 -146
- package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +4 -7
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +175 -173
- package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +4 -7
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +71 -76
- package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +32 -38
- package/src/config/bundled-skills/google-calendar/SKILL.md +2 -2
- package/src/config/bundled-skills/google-calendar/calendar-client.ts +70 -29
- package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +9 -10
- package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +5 -6
- package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +4 -5
- package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +14 -15
- package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +37 -37
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +4 -9
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +24 -3
- package/src/config/bundled-skills/messaging/SKILL.md +6 -6
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +62 -63
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +15 -16
- package/src/config/bundled-skills/messaging/tools/messaging-auth-test.ts +4 -5
- package/src/config/bundled-skills/messaging/tools/messaging-list-conversations.ts +6 -7
- package/src/config/bundled-skills/messaging/tools/messaging-mark-read.ts +4 -5
- package/src/config/bundled-skills/messaging/tools/messaging-read.ts +14 -15
- package/src/config/bundled-skills/messaging/tools/messaging-search.ts +4 -5
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +128 -128
- package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +33 -34
- package/src/config/bundled-skills/messaging/tools/shared.ts +11 -11
- package/src/config/bundled-skills/notifications/SKILL.md +1 -1
- package/src/config/bundled-skills/phone-calls/SKILL.md +5 -5
- package/src/config/bundled-skills/schedule/SKILL.md +1 -1
- package/src/config/bundled-skills/skill-management/SKILL.md +1 -1
- package/src/config/bundled-skills/slack/tools/shared.ts +4 -10
- package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +4 -5
- package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +15 -16
- package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +4 -5
- package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +4 -5
- package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +4 -5
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +95 -92
- package/src/config/loader.ts +6 -0
- package/src/daemon/computer-use-session.ts +7 -1
- package/src/daemon/guardian-action-generators.ts +4 -5
- package/src/daemon/handlers/config-slack-channel.ts +37 -20
- package/src/daemon/handlers/config-telegram.ts +33 -20
- package/src/daemon/lifecycle.ts +9 -1
- package/src/daemon/message-types/integrations.ts +1 -0
- package/src/daemon/ride-shotgun-handler.ts +3 -1
- package/src/daemon/session-messaging.ts +3 -1
- package/src/daemon/session-tool-setup.ts +18 -2
- package/src/daemon/session.ts +1 -1
- package/src/email/providers/index.ts +2 -1
- package/src/instrument.ts +15 -1
- package/src/media/app-icon-generator.ts +30 -4
- package/src/media/avatar-router.ts +28 -62
- package/src/media/gemini-image-service.ts +28 -2
- package/src/memory/canonical-guardian-store.ts +1 -1
- package/src/memory/guardian-action-store.ts +1 -1
- package/src/memory/schema/guardian.ts +1 -1
- package/src/messaging/provider.ts +16 -10
- package/src/messaging/providers/gmail/adapter.ts +40 -23
- package/src/messaging/providers/gmail/client.ts +203 -122
- package/src/messaging/providers/gmail/people-client.ts +26 -18
- package/src/messaging/providers/slack/adapter.ts +29 -19
- package/src/messaging/providers/slack/client.ts +265 -78
- package/src/messaging/providers/telegram-bot/adapter.ts +5 -4
- package/src/messaging/providers/whatsapp/adapter.ts +6 -3
- package/src/messaging/registry.ts +2 -1
- package/src/oauth/byo-connection.test.ts +436 -0
- package/src/oauth/byo-connection.ts +112 -0
- package/src/oauth/connect-orchestrator.ts +27 -0
- package/src/oauth/connection-resolver.ts +34 -0
- package/src/oauth/connection.ts +38 -0
- package/src/oauth/platform-connection.test.ts +163 -0
- package/src/oauth/platform-connection.ts +110 -0
- package/src/oauth/provider-base-urls.ts +21 -0
- package/src/oauth/provider-profiles.ts +1 -1
- package/src/oauth/token-persistence.ts +20 -20
- package/src/permissions/checker.ts +6 -1
- package/src/prompts/system-prompt.ts +52 -15
- package/src/prompts/templates/BOOTSTRAP.md +1 -1
- package/src/providers/gemini/client.ts +15 -6
- package/src/providers/managed-proxy/constants.ts +2 -2
- package/src/providers/managed-proxy/context.ts +5 -1
- package/src/providers/ratelimit.ts +17 -0
- package/src/providers/registry.ts +2 -2
- package/src/runtime/AGENTS.md +18 -1
- package/src/runtime/auth/route-policy.ts +1 -0
- package/src/runtime/channel-invite-transports/telegram.ts +2 -1
- package/src/runtime/channel-readiness-service.ts +168 -195
- package/src/runtime/channel-readiness-types.ts +4 -0
- package/src/runtime/guardian-action-conversation-turn.ts +1 -3
- package/src/runtime/guardian-action-followup-executor.ts +1 -2
- package/src/runtime/guardian-action-message-composer.ts +3 -23
- package/src/runtime/http-server.ts +9 -4
- package/src/runtime/http-types.ts +0 -1
- package/src/runtime/middleware/rate-limiter.ts +74 -20
- package/src/runtime/middleware/twilio-validation.ts +1 -3
- package/src/runtime/routes/channel-readiness-routes.ts +2 -0
- package/src/runtime/routes/diagnostics-routes.ts +11 -9
- package/src/runtime/routes/guardian-approval-interception.ts +20 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +71 -25
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +12 -5
- package/src/runtime/routes/integrations/slack/share.ts +3 -2
- package/src/runtime/routes/integrations/twilio.ts +6 -5
- package/src/runtime/routes/secret-routes.ts +3 -2
- package/src/runtime/routes/settings-routes.ts +75 -17
- package/src/runtime/telegram-streaming-delivery.test.ts +132 -0
- package/src/runtime/telegram-streaming-delivery.ts +11 -1
- package/src/schedule/integration-status.ts +5 -4
- package/src/security/credential-key.ts +170 -0
- package/src/security/token-manager.ts +36 -7
- package/src/tools/apps/definitions.ts +0 -5
- package/src/tools/assets/materialize.ts +0 -5
- package/src/tools/assets/search.ts +0 -5
- package/src/tools/browser/headless-browser.ts +1 -67
- package/src/tools/claude-code/claude-code.ts +0 -5
- package/src/tools/computer-use/request-computer-control.ts +0 -5
- package/src/tools/credentials/broker.ts +6 -4
- package/src/tools/credentials/metadata-store.ts +72 -20
- package/src/tools/credentials/resolve.ts +2 -1
- package/src/tools/credentials/vault.ts +77 -16
- package/src/tools/filesystem/edit.ts +1 -6
- package/src/tools/filesystem/read.ts +0 -5
- package/src/tools/filesystem/write.ts +1 -6
- package/src/tools/host-filesystem/edit.ts +1 -6
- package/src/tools/host-filesystem/read.ts +1 -6
- package/src/tools/host-filesystem/write.ts +1 -6
- package/src/tools/mcp/mcp-tool-factory.ts +18 -1
- package/src/tools/memory/definitions.ts +0 -5
- package/src/tools/network/web-fetch.ts +0 -5
- package/src/tools/network/web-search.ts +0 -5
- package/src/tools/schema-transforms.ts +99 -0
- package/src/tools/skills/load.ts +0 -5
- package/src/tools/swarm/delegate.ts +0 -5
- package/src/tools/system/avatar-generator.ts +3 -44
- package/src/tools/ui-surface/definitions.ts +0 -15
- package/src/tools/watch/screen-watch.ts +0 -5
- package/src/version.ts +10 -0
- package/src/watcher/providers/github.ts +51 -52
- package/src/watcher/providers/gmail.ts +88 -80
- package/src/watcher/providers/google-calendar.ts +93 -86
- package/src/watcher/providers/linear.ts +87 -93
- package/src/__tests__/avatar-router.test.ts +0 -149
- package/src/__tests__/managed-avatar-client.test.ts +0 -337
- package/src/config/bundled-skills/doordash/SKILL.md +0 -170
- package/src/config/bundled-skills/doordash/__tests__/doordash-client.test.ts +0 -205
- package/src/config/bundled-skills/doordash/__tests__/doordash-session.test.ts +0 -74
- package/src/config/bundled-skills/doordash/doordash-cli.ts +0 -1081
- package/src/config/bundled-skills/doordash/doordash-entry.ts +0 -22
- package/src/config/bundled-skills/doordash/lib/cart-queries.ts +0 -787
- package/src/config/bundled-skills/doordash/lib/client.ts +0 -1069
- package/src/config/bundled-skills/doordash/lib/order-queries.ts +0 -85
- package/src/config/bundled-skills/doordash/lib/queries.ts +0 -28
- package/src/config/bundled-skills/doordash/lib/query-extractor.ts +0 -94
- package/src/config/bundled-skills/doordash/lib/search-queries.ts +0 -203
- package/src/config/bundled-skills/doordash/lib/session.ts +0 -96
- package/src/config/bundled-skills/doordash/lib/shared/errors.ts +0 -61
- package/src/config/bundled-skills/doordash/lib/shared/network-recorder.ts +0 -380
- package/src/config/bundled-skills/doordash/lib/shared/platform.ts +0 -55
- package/src/config/bundled-skills/doordash/lib/shared/recording-store.ts +0 -43
- package/src/config/bundled-skills/doordash/lib/shared/recording-types.ts +0 -49
- package/src/config/bundled-skills/doordash/lib/shared/truncate.ts +0 -6
- package/src/config/bundled-skills/doordash/lib/store-queries.ts +0 -246
- package/src/config/bundled-skills/doordash/lib/types.ts +0 -367
- package/src/media/avatar-types.ts +0 -53
- package/src/media/managed-avatar-client.ts +0 -225
|
@@ -1,380 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CDP Network recorder for capturing browser network traffic.
|
|
3
|
-
* Inlined from assistant/src/tools/browser/network-recorder.ts
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type {
|
|
7
|
-
ExtractedCredential,
|
|
8
|
-
NetworkRecordedEntry,
|
|
9
|
-
NetworkRecordedRequest,
|
|
10
|
-
} from "./recording-types.js";
|
|
11
|
-
|
|
12
|
-
/** Max response body size to capture (64 KB). */
|
|
13
|
-
const MAX_BODY_SIZE = 64 * 1024;
|
|
14
|
-
|
|
15
|
-
/** CDP endpoint to discover targets. */
|
|
16
|
-
const CDP_BASE = "http://localhost:9222";
|
|
17
|
-
|
|
18
|
-
class DirectCDPClient {
|
|
19
|
-
private ws: WebSocket | null = null;
|
|
20
|
-
private nextId = 1;
|
|
21
|
-
private callbacks = new Map<
|
|
22
|
-
number,
|
|
23
|
-
{ resolve: (v: unknown) => void; reject: (e: Error) => void }
|
|
24
|
-
>();
|
|
25
|
-
private eventHandlers = new Map<
|
|
26
|
-
string,
|
|
27
|
-
Array<(params: Record<string, unknown>) => void>
|
|
28
|
-
>();
|
|
29
|
-
|
|
30
|
-
async connect(wsUrl: string): Promise<void> {
|
|
31
|
-
return new Promise((resolve, reject) => {
|
|
32
|
-
const ws = new WebSocket(wsUrl);
|
|
33
|
-
ws.onopen = () => {
|
|
34
|
-
this.ws = ws;
|
|
35
|
-
resolve();
|
|
36
|
-
};
|
|
37
|
-
ws.onerror = (e) => reject(new Error(`CDP WebSocket error: ${e}`));
|
|
38
|
-
ws.onclose = () => {
|
|
39
|
-
this.ws = null;
|
|
40
|
-
};
|
|
41
|
-
ws.onmessage = (event) => {
|
|
42
|
-
try {
|
|
43
|
-
const msg = JSON.parse(
|
|
44
|
-
typeof event.data === "string" ? event.data : "",
|
|
45
|
-
);
|
|
46
|
-
if (msg.id != null) {
|
|
47
|
-
const cb = this.callbacks.get(msg.id);
|
|
48
|
-
if (cb) {
|
|
49
|
-
this.callbacks.delete(msg.id);
|
|
50
|
-
if (msg.error) {
|
|
51
|
-
cb.reject(new Error(msg.error.message));
|
|
52
|
-
} else {
|
|
53
|
-
cb.resolve(msg.result);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
} else if (msg.method) {
|
|
57
|
-
const handlers = this.eventHandlers.get(msg.method);
|
|
58
|
-
if (handlers) {
|
|
59
|
-
for (const h of handlers) h(msg.params ?? {});
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
} catch {
|
|
63
|
-
/* ignore parse errors */
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
async send(
|
|
70
|
-
method: string,
|
|
71
|
-
params?: Record<string, unknown>,
|
|
72
|
-
): Promise<unknown> {
|
|
73
|
-
if (!this.ws) throw new Error("Not connected");
|
|
74
|
-
const id = this.nextId++;
|
|
75
|
-
return new Promise((resolve, reject) => {
|
|
76
|
-
this.callbacks.set(id, { resolve, reject });
|
|
77
|
-
this.ws!.send(JSON.stringify({ id, method, params }));
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
on(event: string, handler: (params: Record<string, unknown>) => void): void {
|
|
82
|
-
let handlers = this.eventHandlers.get(event);
|
|
83
|
-
if (!handlers) {
|
|
84
|
-
handlers = [];
|
|
85
|
-
this.eventHandlers.set(event, handlers);
|
|
86
|
-
}
|
|
87
|
-
handlers.push(handler);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
close(): void {
|
|
91
|
-
if (this.ws) {
|
|
92
|
-
this.ws.close();
|
|
93
|
-
this.ws = null;
|
|
94
|
-
}
|
|
95
|
-
for (const cb of this.callbacks.values()) {
|
|
96
|
-
cb.reject(new Error("CDP client closed"));
|
|
97
|
-
}
|
|
98
|
-
this.callbacks.clear();
|
|
99
|
-
this.eventHandlers.clear();
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export class NetworkRecorder {
|
|
104
|
-
private cdp: DirectCDPClient | null = null;
|
|
105
|
-
private entries = new Map<string, NetworkRecordedEntry>();
|
|
106
|
-
private targetDomain?: string;
|
|
107
|
-
private running = false;
|
|
108
|
-
private cdpBaseUrl = CDP_BASE;
|
|
109
|
-
private attachedTargetIds = new Set<string>();
|
|
110
|
-
private targetPollTimer?: ReturnType<typeof setInterval>;
|
|
111
|
-
|
|
112
|
-
onLoginDetected?: () => void;
|
|
113
|
-
loginSignals: string[] = [];
|
|
114
|
-
|
|
115
|
-
get entryCount(): number {
|
|
116
|
-
return this.entries.size;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
constructor(targetDomain?: string) {
|
|
120
|
-
this.targetDomain = targetDomain;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
async startDirect(cdpBaseUrl: string = CDP_BASE): Promise<void> {
|
|
124
|
-
if (this.running) return;
|
|
125
|
-
this.cdpBaseUrl = cdpBaseUrl;
|
|
126
|
-
|
|
127
|
-
const versionRes = await fetch(`${cdpBaseUrl}/json/version`);
|
|
128
|
-
const version = (await versionRes.json()) as {
|
|
129
|
-
webSocketDebuggerUrl: string;
|
|
130
|
-
};
|
|
131
|
-
const wsUrl = version.webSocketDebuggerUrl;
|
|
132
|
-
|
|
133
|
-
if (!wsUrl) {
|
|
134
|
-
throw new Error("Chrome CDP: no webSocketDebuggerUrl found");
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
this.cdp = new DirectCDPClient();
|
|
138
|
-
await this.cdp.connect(wsUrl);
|
|
139
|
-
this.running = true;
|
|
140
|
-
|
|
141
|
-
await this.discoverAndAttachTargets();
|
|
142
|
-
|
|
143
|
-
this.targetPollTimer = setInterval(() => {
|
|
144
|
-
this.discoverAndAttachTargets().catch(() => {});
|
|
145
|
-
}, 2000);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
private async discoverAndAttachTargets(): Promise<void> {
|
|
149
|
-
if (!this.running) return;
|
|
150
|
-
try {
|
|
151
|
-
const res = await fetch(`${this.cdpBaseUrl}/json`);
|
|
152
|
-
const pages = (await res.json()) as Array<{
|
|
153
|
-
id: string;
|
|
154
|
-
type: string;
|
|
155
|
-
webSocketDebuggerUrl: string;
|
|
156
|
-
}>;
|
|
157
|
-
|
|
158
|
-
for (const page of pages) {
|
|
159
|
-
if (
|
|
160
|
-
page.type === "page" &&
|
|
161
|
-
page.webSocketDebuggerUrl &&
|
|
162
|
-
!this.attachedTargetIds.has(page.id)
|
|
163
|
-
) {
|
|
164
|
-
try {
|
|
165
|
-
this.attachedTargetIds.add(page.id);
|
|
166
|
-
await this.attachToTarget(page.webSocketDebuggerUrl);
|
|
167
|
-
} catch {
|
|
168
|
-
this.attachedTargetIds.delete(page.id);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
} catch {
|
|
173
|
-
// CDP endpoint may be temporarily unavailable
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
private pageClients: DirectCDPClient[] = [];
|
|
178
|
-
|
|
179
|
-
private async attachToTarget(wsUrl: string): Promise<void> {
|
|
180
|
-
const client = new DirectCDPClient();
|
|
181
|
-
await client.connect(wsUrl);
|
|
182
|
-
|
|
183
|
-
client.on("Network.requestWillBeSent", (params) =>
|
|
184
|
-
this.handleRequestWillBeSent(params),
|
|
185
|
-
);
|
|
186
|
-
client.on("Network.responseReceived", (params) =>
|
|
187
|
-
this.handleResponseReceived(params),
|
|
188
|
-
);
|
|
189
|
-
client.on("Network.loadingFinished", (params) =>
|
|
190
|
-
this.handleLoadingFinished(params, client),
|
|
191
|
-
);
|
|
192
|
-
|
|
193
|
-
await client.send("Network.enable");
|
|
194
|
-
this.pageClients.push(client);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
async stop(): Promise<NetworkRecordedEntry[]> {
|
|
198
|
-
if (!this.running) return [];
|
|
199
|
-
this.running = false;
|
|
200
|
-
|
|
201
|
-
if (this.targetPollTimer) {
|
|
202
|
-
clearInterval(this.targetPollTimer);
|
|
203
|
-
this.targetPollTimer = undefined;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
for (const client of this.pageClients) {
|
|
207
|
-
try {
|
|
208
|
-
await client.send("Network.disable");
|
|
209
|
-
} catch {
|
|
210
|
-
/* ignore */
|
|
211
|
-
}
|
|
212
|
-
client.close();
|
|
213
|
-
}
|
|
214
|
-
this.pageClients = [];
|
|
215
|
-
|
|
216
|
-
if (this.cdp) {
|
|
217
|
-
this.cdp.close();
|
|
218
|
-
this.cdp = null;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
const result = Array.from(this.entries.values());
|
|
222
|
-
this.entries.clear();
|
|
223
|
-
this.attachedTargetIds.clear();
|
|
224
|
-
this.loginDetectedFired = false;
|
|
225
|
-
return result;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
async extractCookies(domain?: string): Promise<ExtractedCredential[]> {
|
|
229
|
-
const client = this.pageClients[0];
|
|
230
|
-
if (!client) return [];
|
|
231
|
-
try {
|
|
232
|
-
const result = (await client.send("Network.getAllCookies")) as {
|
|
233
|
-
cookies: Array<{
|
|
234
|
-
name: string;
|
|
235
|
-
value: string;
|
|
236
|
-
domain: string;
|
|
237
|
-
path: string;
|
|
238
|
-
httpOnly: boolean;
|
|
239
|
-
secure: boolean;
|
|
240
|
-
expires: number;
|
|
241
|
-
}>;
|
|
242
|
-
};
|
|
243
|
-
let cookies = result.cookies ?? [];
|
|
244
|
-
if (domain) {
|
|
245
|
-
cookies = cookies.filter(
|
|
246
|
-
(c) =>
|
|
247
|
-
c.domain === domain ||
|
|
248
|
-
c.domain === `.${domain}` ||
|
|
249
|
-
c.domain.endsWith(`.${domain}`),
|
|
250
|
-
);
|
|
251
|
-
}
|
|
252
|
-
return cookies.map((c) => ({
|
|
253
|
-
name: c.name,
|
|
254
|
-
value: c.value,
|
|
255
|
-
domain: c.domain,
|
|
256
|
-
path: c.path,
|
|
257
|
-
httpOnly: c.httpOnly,
|
|
258
|
-
secure: c.secure,
|
|
259
|
-
expires: c.expires > 0 ? c.expires : undefined,
|
|
260
|
-
}));
|
|
261
|
-
} catch {
|
|
262
|
-
return [];
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
getEntries(): NetworkRecordedEntry[] {
|
|
267
|
-
return Array.from(this.entries.values());
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
private matchesDomain(url: string): boolean {
|
|
271
|
-
if (!this.targetDomain) return true;
|
|
272
|
-
try {
|
|
273
|
-
const hostname = new URL(url).hostname;
|
|
274
|
-
return (
|
|
275
|
-
hostname === this.targetDomain ||
|
|
276
|
-
hostname.endsWith(`.${this.targetDomain}`)
|
|
277
|
-
);
|
|
278
|
-
} catch {
|
|
279
|
-
return false;
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
private loginDetectedFired = false;
|
|
284
|
-
|
|
285
|
-
private handleRequestWillBeSent(params: Record<string, unknown>): void {
|
|
286
|
-
const resourceType = params.type as string;
|
|
287
|
-
if (resourceType !== "XHR" && resourceType !== "Fetch") return;
|
|
288
|
-
|
|
289
|
-
const request = params.request as Record<string, unknown>;
|
|
290
|
-
const url = request.url as string;
|
|
291
|
-
if (!this.matchesDomain(url)) return;
|
|
292
|
-
|
|
293
|
-
const requestId = params.requestId as string;
|
|
294
|
-
const headers = (request.headers as Record<string, string>) ?? {};
|
|
295
|
-
const method = (request.method as string) ?? "GET";
|
|
296
|
-
const postData = request.postData as string | undefined;
|
|
297
|
-
|
|
298
|
-
const recordedRequest: NetworkRecordedRequest = {
|
|
299
|
-
method,
|
|
300
|
-
url,
|
|
301
|
-
headers,
|
|
302
|
-
postData,
|
|
303
|
-
};
|
|
304
|
-
const entry: NetworkRecordedEntry = {
|
|
305
|
-
requestId,
|
|
306
|
-
resourceType,
|
|
307
|
-
timestamp: (params.timestamp as number) ?? Date.now() / 1000,
|
|
308
|
-
request: recordedRequest,
|
|
309
|
-
};
|
|
310
|
-
this.entries.set(requestId, entry);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
private handleResponseReceived(params: Record<string, unknown>): void {
|
|
314
|
-
const requestId = params.requestId as string;
|
|
315
|
-
const entry = this.entries.get(requestId);
|
|
316
|
-
if (!entry) return;
|
|
317
|
-
|
|
318
|
-
const response = params.response as Record<string, unknown>;
|
|
319
|
-
const status = (response.status as number) ?? 0;
|
|
320
|
-
entry.response = {
|
|
321
|
-
status,
|
|
322
|
-
headers: (response.headers as Record<string, string>) ?? {},
|
|
323
|
-
mimeType: (response.mimeType as string) ?? "",
|
|
324
|
-
};
|
|
325
|
-
|
|
326
|
-
if (
|
|
327
|
-
status === 200 &&
|
|
328
|
-
this.onLoginDetected &&
|
|
329
|
-
!this.loginDetectedFired &&
|
|
330
|
-
this.loginSignals.length > 0 &&
|
|
331
|
-
this.loginSignals.some((sig) => entry.request.url.includes(sig))
|
|
332
|
-
) {
|
|
333
|
-
this.loginDetectedFired = true;
|
|
334
|
-
setTimeout(() => this.onLoginDetected?.(), 5000);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
private handleLoadingFinished(
|
|
339
|
-
params: Record<string, unknown>,
|
|
340
|
-
client: DirectCDPClient,
|
|
341
|
-
): void {
|
|
342
|
-
const requestId = params.requestId as string;
|
|
343
|
-
const entry = this.entries.get(requestId);
|
|
344
|
-
if (!entry || !entry.response) return;
|
|
345
|
-
|
|
346
|
-
const mimeType = entry.response.mimeType;
|
|
347
|
-
if (!mimeType.includes("json") && !mimeType.includes("text")) return;
|
|
348
|
-
|
|
349
|
-
this.fetchResponseBody(requestId, entry, client);
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
private async fetchResponseBody(
|
|
353
|
-
requestId: string,
|
|
354
|
-
entry: NetworkRecordedEntry,
|
|
355
|
-
client: DirectCDPClient,
|
|
356
|
-
): Promise<void> {
|
|
357
|
-
if (!this.running) return;
|
|
358
|
-
try {
|
|
359
|
-
const result = (await client.send("Network.getResponseBody", {
|
|
360
|
-
requestId,
|
|
361
|
-
})) as {
|
|
362
|
-
body: string;
|
|
363
|
-
base64Encoded: boolean;
|
|
364
|
-
};
|
|
365
|
-
|
|
366
|
-
if (result.body && entry.response) {
|
|
367
|
-
const body = result.base64Encoded
|
|
368
|
-
? Buffer.from(result.body, "base64").toString("utf-8")
|
|
369
|
-
: result.body;
|
|
370
|
-
|
|
371
|
-
entry.response.body =
|
|
372
|
-
body.length > MAX_BODY_SIZE
|
|
373
|
-
? body.slice(0, MAX_BODY_SIZE) + "...[truncated]"
|
|
374
|
-
: body;
|
|
375
|
-
}
|
|
376
|
-
} catch {
|
|
377
|
-
// Response body may not be available
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Inlined platform utilities used by the DoorDash skill.
|
|
3
|
-
* Subset of assistant/src/util/platform.ts — kept minimal.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { readFileSync } from "node:fs";
|
|
7
|
-
import { homedir } from "node:os";
|
|
8
|
-
import { join } from "node:path";
|
|
9
|
-
|
|
10
|
-
function getRootDir(): string {
|
|
11
|
-
const base = process.env.BASE_DATA_DIR?.trim();
|
|
12
|
-
return join(base || homedir(), ".vellum");
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function getDataDir(): string {
|
|
16
|
-
return join(getRootDir(), "workspace", "data");
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/** Default daemon HTTP port — matches cli/src/lib/constants.ts. */
|
|
20
|
-
const DEFAULT_DAEMON_PORT = 7821;
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Resolve the daemon HTTP port from RUNTIME_HTTP_PORT env var or default.
|
|
24
|
-
*/
|
|
25
|
-
export function getHttpPort(): number {
|
|
26
|
-
const envPort = process.env.RUNTIME_HTTP_PORT;
|
|
27
|
-
if (envPort) {
|
|
28
|
-
const parsed = parseInt(envPort, 10);
|
|
29
|
-
if (!isNaN(parsed)) return parsed;
|
|
30
|
-
}
|
|
31
|
-
return DEFAULT_DAEMON_PORT;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Build the base URL for the daemon HTTP server.
|
|
36
|
-
*/
|
|
37
|
-
export function buildDaemonUrl(port?: number): string {
|
|
38
|
-
return `http://127.0.0.1:${port ?? getHttpPort()}`;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Read the HTTP bearer token from `<rootDir>/http-token`.
|
|
43
|
-
* Returns null if the token file doesn't exist or is empty.
|
|
44
|
-
*/
|
|
45
|
-
export function readHttpToken(): string | null {
|
|
46
|
-
try {
|
|
47
|
-
const token = readFileSync(
|
|
48
|
-
join(getRootDir(), "http-token"),
|
|
49
|
-
"utf-8",
|
|
50
|
-
).trim();
|
|
51
|
-
return token || null;
|
|
52
|
-
} catch {
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Simple read/write for session recording JSON files.
|
|
3
|
-
* Inlined from assistant/src/tools/browser/recording-store.ts
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
7
|
-
import { join, resolve } from "node:path";
|
|
8
|
-
|
|
9
|
-
import { getDataDir } from "./platform.js";
|
|
10
|
-
import type { SessionRecording } from "./recording-types.js";
|
|
11
|
-
|
|
12
|
-
function getRecordingsDir(): string {
|
|
13
|
-
return join(getDataDir(), "recordings");
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function saveRecording(recording: SessionRecording): string {
|
|
17
|
-
const dir = getRecordingsDir();
|
|
18
|
-
mkdirSync(dir, { recursive: true });
|
|
19
|
-
|
|
20
|
-
const filePath = resolve(dir, `${recording.id}.json`);
|
|
21
|
-
if (!filePath.startsWith(resolve(dir) + "/")) {
|
|
22
|
-
throw new Error(`Invalid recording ID: ${recording.id}`);
|
|
23
|
-
}
|
|
24
|
-
writeFileSync(filePath, JSON.stringify(recording, null, 2), "utf-8");
|
|
25
|
-
return filePath;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function loadRecording(recordingId: string): SessionRecording | null {
|
|
29
|
-
const dir = getRecordingsDir();
|
|
30
|
-
const filePath = resolve(dir, `${recordingId}.json`);
|
|
31
|
-
if (!filePath.startsWith(resolve(dir) + "/")) {
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
if (!existsSync(filePath)) {
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
try {
|
|
38
|
-
const data = readFileSync(filePath, "utf-8");
|
|
39
|
-
return JSON.parse(data) as SessionRecording;
|
|
40
|
-
} catch {
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
/** Types for CDP network recording. Inlined from assistant/src/tools/browser/network-recording-types.ts */
|
|
2
|
-
|
|
3
|
-
export interface NetworkRecordedRequest {
|
|
4
|
-
method: string;
|
|
5
|
-
url: string;
|
|
6
|
-
headers: Record<string, string>;
|
|
7
|
-
postData?: string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface NetworkRecordedResponse {
|
|
11
|
-
status: number;
|
|
12
|
-
headers: Record<string, string>;
|
|
13
|
-
mimeType: string;
|
|
14
|
-
body?: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface NetworkRecordedEntry {
|
|
18
|
-
requestId: string;
|
|
19
|
-
resourceType: string;
|
|
20
|
-
timestamp: number;
|
|
21
|
-
request: NetworkRecordedRequest;
|
|
22
|
-
response?: NetworkRecordedResponse;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface ExtractedCredential {
|
|
26
|
-
name: string;
|
|
27
|
-
value: string;
|
|
28
|
-
domain: string;
|
|
29
|
-
path: string;
|
|
30
|
-
httpOnly: boolean;
|
|
31
|
-
secure: boolean;
|
|
32
|
-
expires?: number;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface SessionRecording {
|
|
36
|
-
id: string;
|
|
37
|
-
startedAt: number;
|
|
38
|
-
endedAt: number;
|
|
39
|
-
targetDomain?: string;
|
|
40
|
-
networkEntries: NetworkRecordedEntry[];
|
|
41
|
-
cookies: ExtractedCredential[];
|
|
42
|
-
observations: Array<{
|
|
43
|
-
ocrText: string;
|
|
44
|
-
appName?: string;
|
|
45
|
-
windowTitle?: string;
|
|
46
|
-
timestamp: number;
|
|
47
|
-
captureIndex: number;
|
|
48
|
-
}>;
|
|
49
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/** Truncate a string to `maxLen` characters, appending `suffix` if truncated. */
|
|
2
|
-
export function truncate(str: string, maxLen: number, suffix = "..."): string {
|
|
3
|
-
if (str.length <= maxLen) return str;
|
|
4
|
-
if (maxLen < suffix.length) return str.slice(0, maxLen);
|
|
5
|
-
return str.slice(0, maxLen - suffix.length) + suffix;
|
|
6
|
-
}
|