@poolzin/pool-bot 2026.2.23 → 2026.2.25
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/CHANGELOG.md +29 -0
- package/dist/acp/client.js +207 -18
- package/dist/acp/secret-file.js +22 -0
- package/dist/agents/agent-scope.js +10 -0
- package/dist/agents/bash-process-registry.test-helpers.js +29 -0
- package/dist/agents/bash-tools.exec-approval-request.js +20 -0
- package/dist/agents/bash-tools.exec-host-gateway.js +230 -0
- package/dist/agents/bash-tools.exec-host-node.js +235 -0
- package/dist/agents/bash-tools.exec-types.js +1 -0
- package/dist/agents/bash-tools.process.js +224 -218
- package/dist/agents/content-blocks.js +16 -0
- package/dist/agents/model-fallback.js +96 -101
- package/dist/agents/models-config.providers.js +299 -182
- package/dist/agents/pi-embedded-payloads.js +1 -0
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.fixture.js +34 -0
- package/dist/agents/skills.test-helpers.js +13 -0
- package/dist/agents/stable-stringify.js +12 -0
- package/dist/agents/subagent-registry.mocks.shared.js +12 -0
- package/dist/agents/test-helpers/assistant-message-fixtures.js +29 -0
- package/dist/agents/test-helpers/pi-tools-sandbox-context.js +27 -0
- package/dist/agents/tool-policy-shared.js +108 -0
- package/dist/agents/tools/browser-tool.js +160 -54
- package/dist/agents/tools/cron-tool.test-helpers.js +12 -0
- package/dist/agents/tools/discord-actions-moderation-shared.js +27 -0
- package/dist/agents/tools/image-tool.js +214 -99
- package/dist/agents/tools/sessions-history-tool.js +140 -108
- package/dist/agents/workspace.js +222 -46
- package/dist/auto-reply/commands-registry.js +15 -18
- package/dist/auto-reply/fallback-state.js +114 -0
- package/dist/auto-reply/model-runtime.js +68 -0
- package/dist/auto-reply/reply/agent-runner-execution.js +36 -4
- package/dist/auto-reply/reply/agent-runner.js +165 -39
- package/dist/auto-reply/reply/commands-setunset-standard.js +13 -0
- package/dist/browser/config.js +26 -0
- package/dist/browser/navigation-guard.js +31 -0
- package/dist/browser/routes/agent.act.js +431 -424
- package/dist/browser/routes/agent.shared.js +47 -3
- package/dist/browser/routes/agent.snapshot.js +122 -116
- package/dist/browser/routes/agent.storage.js +303 -297
- package/dist/browser/routes/tabs.js +154 -100
- package/dist/browser/server-lifecycle.js +37 -0
- package/dist/build-info.json +3 -3
- package/dist/channels/allow-from.js +25 -0
- package/dist/channels/plugins/account-action-gate.js +13 -0
- package/dist/channels/plugins/message-actions.js +10 -0
- package/dist/channels/telegram/api.js +18 -0
- package/dist/cli/argv.js +84 -21
- package/dist/cli/banner.js +2 -1
- package/dist/cli/exec-approvals-cli.js +92 -124
- package/dist/cli/memory-cli.js +158 -61
- package/dist/cli/nodes-cli/register.push.js +63 -0
- package/dist/cli/nodes-media-utils.js +21 -0
- package/dist/cli/plugins-cli.js +245 -61
- package/dist/cli/program/build-program.js +3 -1
- package/dist/cli/program/command-registry.js +223 -136
- package/dist/cli/program/help.js +43 -12
- package/dist/cli/route.js +1 -1
- package/dist/cli/test-runtime-capture.js +24 -0
- package/dist/commands/agent.js +163 -87
- package/dist/commands/channels.mock-harness.js +23 -0
- package/dist/commands/daemon-install-runtime-warning.js +11 -0
- package/dist/commands/onboard-helpers.js +4 -4
- package/dist/commands/sessions.test-helpers.js +61 -0
- package/dist/compat/legacy-names.js +2 -2
- package/dist/config/commands.js +3 -0
- package/dist/config/config.js +1 -1
- package/dist/config/env-substitution.js +62 -34
- package/dist/config/env-vars.js +9 -0
- package/dist/config/io.js +571 -171
- package/dist/config/merge-patch.js +50 -4
- package/dist/config/redact-snapshot.js +404 -76
- package/dist/config/schema.js +58 -570
- package/dist/config/validation.js +140 -85
- package/dist/config/zod-schema.hooks.js +40 -11
- package/dist/config/zod-schema.installs.js +20 -0
- package/dist/config/zod-schema.js +8 -7
- package/dist/control-ui/assets/{index-HRr1grwl.js → index-Dvkl4Xlx.js} +2 -1
- package/dist/control-ui/assets/{index-HRr1grwl.js.map → index-Dvkl4Xlx.js.map} +1 -1
- package/dist/control-ui/index.html +1 -1
- package/dist/daemon/cmd-argv.js +21 -0
- package/dist/daemon/cmd-set.js +58 -0
- package/dist/daemon/service-types.js +1 -0
- package/dist/discord/monitor/exec-approvals.js +357 -162
- package/dist/gateway/auth.js +38 -3
- package/dist/gateway/call.js +149 -68
- package/dist/gateway/canvas-capability.js +75 -0
- package/dist/gateway/control-plane-audit.js +28 -0
- package/dist/gateway/control-plane-rate-limit.js +53 -0
- package/dist/gateway/events.js +1 -0
- package/dist/gateway/hooks.js +109 -54
- package/dist/gateway/http-common.js +22 -0
- package/dist/gateway/method-scopes.js +169 -0
- package/dist/gateway/net.js +23 -0
- package/dist/gateway/openresponses-http.js +120 -110
- package/dist/gateway/probe-auth.js +2 -0
- package/dist/gateway/protocol/index.js +3 -2
- package/dist/gateway/protocol/schema/protocol-schemas.js +2 -0
- package/dist/gateway/protocol/schema/push.js +18 -0
- package/dist/gateway/protocol/schema.js +1 -0
- package/dist/gateway/server-http.js +236 -52
- package/dist/gateway/server-methods/agent.js +162 -24
- package/dist/gateway/server-methods/chat.js +461 -130
- package/dist/gateway/server-methods/config.js +193 -150
- package/dist/gateway/server-methods/nodes.helpers.js +12 -0
- package/dist/gateway/server-methods/nodes.js +251 -69
- package/dist/gateway/server-methods/push.js +53 -0
- package/dist/gateway/server-reload-handlers.js +2 -3
- package/dist/gateway/server-runtime-config.js +5 -0
- package/dist/gateway/server-runtime-state.js +2 -0
- package/dist/gateway/server-ws-runtime.js +1 -0
- package/dist/gateway/server.impl.js +296 -139
- package/dist/gateway/session-preview.test-helpers.js +11 -0
- package/dist/gateway/startup-auth.js +126 -0
- package/dist/gateway/test-helpers.agent-results.js +15 -0
- package/dist/gateway/test-helpers.mocks.js +37 -14
- package/dist/gateway/test-helpers.server.js +161 -77
- package/dist/hooks/bundled/session-memory/handler.js +165 -34
- package/dist/hooks/gmail-watcher-lifecycle.js +23 -0
- package/dist/infra/archive-path.js +49 -0
- package/dist/infra/device-pairing.js +148 -167
- package/dist/infra/exec-approvals-allowlist.js +19 -70
- package/dist/infra/exec-approvals-analysis.js +44 -17
- package/dist/infra/exec-safe-bin-policy.js +269 -0
- package/dist/infra/fixed-window-rate-limit.js +33 -0
- package/dist/infra/git-root.js +61 -0
- package/dist/infra/heartbeat-active-hours.js +2 -2
- package/dist/infra/heartbeat-reason.js +40 -0
- package/dist/infra/heartbeat-runner.js +72 -32
- package/dist/infra/install-source-utils.js +91 -7
- package/dist/infra/node-pairing.js +50 -105
- package/dist/infra/npm-integrity.js +45 -0
- package/dist/infra/npm-pack-install.js +40 -0
- package/dist/infra/outbound/channel-adapters.js +20 -7
- package/dist/infra/outbound/message-action-runner.js +107 -327
- package/dist/infra/outbound/message.js +59 -36
- package/dist/infra/outbound/outbound-policy.js +52 -25
- package/dist/infra/outbound/outbound-send-service.js +58 -71
- package/dist/infra/pairing-files.js +10 -0
- package/dist/infra/plain-object.js +9 -0
- package/dist/infra/push-apns.js +365 -0
- package/dist/infra/restart-sentinel.js +16 -1
- package/dist/infra/restart.js +229 -26
- package/dist/infra/scp-host.js +54 -0
- package/dist/infra/update-startup.js +86 -9
- package/dist/media/inbound-path-policy.js +114 -0
- package/dist/media/input-files.js +16 -0
- package/dist/memory/test-manager.js +8 -0
- package/dist/plugin-sdk/temp-path.js +47 -0
- package/dist/plugins/discovery.js +217 -23
- package/dist/plugins/hook-runner-global.js +16 -0
- package/dist/plugins/loader.js +192 -26
- package/dist/plugins/logger.js +8 -0
- package/dist/plugins/manifest-registry.js +3 -0
- package/dist/plugins/path-safety.js +34 -0
- package/dist/plugins/registry.js +5 -2
- package/dist/plugins/runtime/index.js +271 -206
- package/dist/providers/github-copilot-models.js +4 -1
- package/dist/security/audit-channel.js +8 -19
- package/dist/security/audit-extra.async.js +354 -182
- package/dist/security/audit-extra.js +11 -1
- package/dist/security/audit-extra.sync.js +340 -33
- package/dist/security/audit-fs.js +31 -13
- package/dist/security/audit.js +145 -371
- package/dist/security/dm-policy-shared.js +24 -0
- package/dist/security/external-content.js +20 -8
- package/dist/security/fix.js +49 -85
- package/dist/security/scan-paths.js +20 -0
- package/dist/security/secret-equal.js +3 -7
- package/dist/security/windows-acl.js +30 -15
- package/dist/shared/node-list-parse.js +13 -0
- package/dist/shared/operator-scope-compat.js +37 -0
- package/dist/shared/text-chunking.js +29 -0
- package/dist/slack/blocks.test-helpers.js +31 -0
- package/dist/slack/monitor/mrkdwn.js +8 -0
- package/dist/telegram/bot-message-dispatch.js +366 -164
- package/dist/telegram/draft-stream.js +30 -7
- package/dist/telegram/reasoning-lane-coordinator.js +128 -0
- package/dist/terminal/prompt-select-styled.js +9 -0
- package/dist/test-utils/command-runner.js +6 -0
- package/dist/test-utils/internal-hook-event-payload.js +10 -0
- package/dist/test-utils/model-auth-mock.js +12 -0
- package/dist/test-utils/provider-usage-fetch.js +14 -0
- package/dist/test-utils/temp-home.js +33 -0
- package/dist/tui/components/chat-log.js +9 -0
- package/dist/tui/tui-command-handlers.js +36 -27
- package/dist/tui/tui-event-handlers.js +122 -32
- package/dist/tui/tui.js +181 -45
- package/dist/utils/mask-api-key.js +10 -0
- package/dist/utils/run-with-concurrency.js +39 -0
- package/dist/web/media.js +4 -0
- package/docs/tools/slash-commands.md +5 -1
- package/extensions/bluebubbles/package.json +1 -1
- package/extensions/copilot-proxy/package.json +1 -1
- package/extensions/diagnostics-otel/package.json +1 -1
- package/extensions/discord/package.json +1 -1
- package/extensions/feishu/package.json +1 -1
- package/extensions/feishu/src/external-keys.ts +19 -0
- package/extensions/google-antigravity-auth/package.json +1 -1
- package/extensions/google-gemini-cli-auth/package.json +1 -1
- package/extensions/googlechat/package.json +1 -1
- package/extensions/imessage/package.json +1 -1
- package/extensions/irc/package.json +1 -1
- package/extensions/line/package.json +1 -1
- package/extensions/llm-task/package.json +1 -1
- package/extensions/lobster/package.json +1 -1
- package/extensions/lobster/src/windows-spawn.ts +193 -0
- package/extensions/matrix/CHANGELOG.md +5 -0
- package/extensions/matrix/package.json +1 -1
- package/extensions/matrix/src/matrix/actions/limits.ts +6 -0
- package/extensions/mattermost/package.json +1 -1
- package/extensions/mattermost/src/mattermost/reactions.test-helpers.ts +83 -0
- package/extensions/memory-core/package.json +1 -1
- package/extensions/memory-lancedb/package.json +1 -1
- package/extensions/minimax-portal-auth/package.json +1 -1
- package/extensions/msteams/CHANGELOG.md +5 -0
- package/extensions/msteams/package.json +1 -1
- package/extensions/nextcloud-talk/package.json +1 -1
- package/extensions/nostr/CHANGELOG.md +5 -0
- package/extensions/nostr/package.json +1 -1
- package/extensions/open-prose/package.json +1 -1
- package/extensions/openai-codex-auth/package.json +1 -1
- package/extensions/signal/package.json +1 -1
- package/extensions/slack/package.json +1 -1
- package/extensions/telegram/package.json +1 -1
- package/extensions/tlon/package.json +1 -1
- package/extensions/twitch/CHANGELOG.md +5 -0
- package/extensions/twitch/package.json +1 -1
- package/extensions/voice-call/CHANGELOG.md +5 -0
- package/extensions/voice-call/package.json +1 -1
- package/extensions/whatsapp/package.json +1 -1
- package/extensions/zalo/CHANGELOG.md +5 -0
- package/extensions/zalo/package.json +1 -1
- package/extensions/zalouser/CHANGELOG.md +5 -0
- package/extensions/zalouser/package.json +1 -1
- package/package.json +1 -1
|
@@ -1,312 +1,325 @@
|
|
|
1
1
|
import { isActKind, parseClickButton, parseClickModifiers, } from "./agent.act.shared.js";
|
|
2
|
-
import {
|
|
2
|
+
import { readBody, resolveTargetIdFromBody, withPlaywrightRouteContext, SELECTOR_UNSUPPORTED_MESSAGE, } from "./agent.shared.js";
|
|
3
|
+
import { DEFAULT_DOWNLOAD_DIR, DEFAULT_UPLOAD_DIR, resolvePathWithinRoot, resolvePathsWithinRoot, } from "./path-output.js";
|
|
3
4
|
import { jsonError, toBoolean, toNumber, toStringArray, toStringOrEmpty } from "./utils.js";
|
|
5
|
+
function resolveDownloadPathOrRespond(res, requestedPath) {
|
|
6
|
+
const downloadPathResult = resolvePathWithinRoot({
|
|
7
|
+
rootDir: DEFAULT_DOWNLOAD_DIR,
|
|
8
|
+
requestedPath,
|
|
9
|
+
scopeLabel: "downloads directory",
|
|
10
|
+
});
|
|
11
|
+
if (!downloadPathResult.ok) {
|
|
12
|
+
res.status(400).json({ error: downloadPathResult.error });
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
return downloadPathResult.path;
|
|
16
|
+
}
|
|
17
|
+
function buildDownloadRequestBase(cdpUrl, targetId, timeoutMs) {
|
|
18
|
+
return {
|
|
19
|
+
cdpUrl,
|
|
20
|
+
targetId,
|
|
21
|
+
timeoutMs: timeoutMs ?? undefined,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function respondWithDownloadResult(res, targetId, result) {
|
|
25
|
+
res.json({ ok: true, targetId, download: result });
|
|
26
|
+
}
|
|
4
27
|
export function registerBrowserAgentActRoutes(app, ctx) {
|
|
5
28
|
app.post("/act", async (req, res) => {
|
|
6
|
-
const profileCtx = resolveProfileContext(req, res, ctx);
|
|
7
|
-
if (!profileCtx) {
|
|
8
|
-
return;
|
|
9
|
-
}
|
|
10
29
|
const body = readBody(req);
|
|
11
30
|
const kindRaw = toStringOrEmpty(body.kind);
|
|
12
31
|
if (!isActKind(kindRaw)) {
|
|
13
32
|
return jsonError(res, 400, "kind is required");
|
|
14
33
|
}
|
|
15
34
|
const kind = kindRaw;
|
|
16
|
-
const targetId =
|
|
35
|
+
const targetId = resolveTargetIdFromBody(body);
|
|
17
36
|
if (Object.hasOwn(body, "selector") && kind !== "wait") {
|
|
18
37
|
return jsonError(res, 400, SELECTOR_UNSUPPORTED_MESSAGE);
|
|
19
38
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
case "type": {
|
|
66
|
-
const ref = toStringOrEmpty(body.ref);
|
|
67
|
-
if (!ref) {
|
|
68
|
-
return jsonError(res, 400, "ref is required");
|
|
39
|
+
await withPlaywrightRouteContext({
|
|
40
|
+
req,
|
|
41
|
+
res,
|
|
42
|
+
ctx,
|
|
43
|
+
targetId,
|
|
44
|
+
feature: `act:${kind}`,
|
|
45
|
+
run: async ({ cdpUrl, tab, pw }) => {
|
|
46
|
+
const evaluateEnabled = ctx.state().resolved.evaluateEnabled;
|
|
47
|
+
switch (kind) {
|
|
48
|
+
case "click": {
|
|
49
|
+
const ref = toStringOrEmpty(body.ref);
|
|
50
|
+
if (!ref) {
|
|
51
|
+
return jsonError(res, 400, "ref is required");
|
|
52
|
+
}
|
|
53
|
+
const doubleClick = toBoolean(body.doubleClick) ?? false;
|
|
54
|
+
const timeoutMs = toNumber(body.timeoutMs);
|
|
55
|
+
const buttonRaw = toStringOrEmpty(body.button) || "";
|
|
56
|
+
const button = buttonRaw ? parseClickButton(buttonRaw) : undefined;
|
|
57
|
+
if (buttonRaw && !button) {
|
|
58
|
+
return jsonError(res, 400, "button must be left|right|middle");
|
|
59
|
+
}
|
|
60
|
+
const modifiersRaw = toStringArray(body.modifiers) ?? [];
|
|
61
|
+
const parsedModifiers = parseClickModifiers(modifiersRaw);
|
|
62
|
+
if (parsedModifiers.error) {
|
|
63
|
+
return jsonError(res, 400, parsedModifiers.error);
|
|
64
|
+
}
|
|
65
|
+
const modifiers = parsedModifiers.modifiers;
|
|
66
|
+
const clickRequest = {
|
|
67
|
+
cdpUrl,
|
|
68
|
+
targetId: tab.targetId,
|
|
69
|
+
ref,
|
|
70
|
+
doubleClick,
|
|
71
|
+
};
|
|
72
|
+
if (button) {
|
|
73
|
+
clickRequest.button = button;
|
|
74
|
+
}
|
|
75
|
+
if (modifiers) {
|
|
76
|
+
clickRequest.modifiers = modifiers;
|
|
77
|
+
}
|
|
78
|
+
if (timeoutMs) {
|
|
79
|
+
clickRequest.timeoutMs = timeoutMs;
|
|
80
|
+
}
|
|
81
|
+
await pw.clickViaPlaywright(clickRequest);
|
|
82
|
+
return res.json({ ok: true, targetId: tab.targetId, url: tab.url });
|
|
69
83
|
}
|
|
70
|
-
|
|
71
|
-
|
|
84
|
+
case "type": {
|
|
85
|
+
const ref = toStringOrEmpty(body.ref);
|
|
86
|
+
if (!ref) {
|
|
87
|
+
return jsonError(res, 400, "ref is required");
|
|
88
|
+
}
|
|
89
|
+
if (typeof body.text !== "string") {
|
|
90
|
+
return jsonError(res, 400, "text is required");
|
|
91
|
+
}
|
|
92
|
+
const text = body.text;
|
|
93
|
+
const submit = toBoolean(body.submit) ?? false;
|
|
94
|
+
const slowly = toBoolean(body.slowly) ?? false;
|
|
95
|
+
const timeoutMs = toNumber(body.timeoutMs);
|
|
96
|
+
const typeRequest = {
|
|
97
|
+
cdpUrl,
|
|
98
|
+
targetId: tab.targetId,
|
|
99
|
+
ref,
|
|
100
|
+
text,
|
|
101
|
+
submit,
|
|
102
|
+
slowly,
|
|
103
|
+
};
|
|
104
|
+
if (timeoutMs) {
|
|
105
|
+
typeRequest.timeoutMs = timeoutMs;
|
|
106
|
+
}
|
|
107
|
+
await pw.typeViaPlaywright(typeRequest);
|
|
108
|
+
return res.json({ ok: true, targetId: tab.targetId });
|
|
72
109
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
typeRequest.timeoutMs = timeoutMs;
|
|
110
|
+
case "press": {
|
|
111
|
+
const key = toStringOrEmpty(body.key);
|
|
112
|
+
if (!key) {
|
|
113
|
+
return jsonError(res, 400, "key is required");
|
|
114
|
+
}
|
|
115
|
+
const delayMs = toNumber(body.delayMs);
|
|
116
|
+
await pw.pressKeyViaPlaywright({
|
|
117
|
+
cdpUrl,
|
|
118
|
+
targetId: tab.targetId,
|
|
119
|
+
key,
|
|
120
|
+
delayMs: delayMs ?? undefined,
|
|
121
|
+
});
|
|
122
|
+
return res.json({ ok: true, targetId: tab.targetId });
|
|
87
123
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
124
|
+
case "hover": {
|
|
125
|
+
const ref = toStringOrEmpty(body.ref);
|
|
126
|
+
if (!ref) {
|
|
127
|
+
return jsonError(res, 400, "ref is required");
|
|
128
|
+
}
|
|
129
|
+
const timeoutMs = toNumber(body.timeoutMs);
|
|
130
|
+
await pw.hoverViaPlaywright({
|
|
131
|
+
cdpUrl,
|
|
132
|
+
targetId: tab.targetId,
|
|
133
|
+
ref,
|
|
134
|
+
timeoutMs: timeoutMs ?? undefined,
|
|
135
|
+
});
|
|
136
|
+
return res.json({ ok: true, targetId: tab.targetId });
|
|
95
137
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
138
|
+
case "scrollIntoView": {
|
|
139
|
+
const ref = toStringOrEmpty(body.ref);
|
|
140
|
+
if (!ref) {
|
|
141
|
+
return jsonError(res, 400, "ref is required");
|
|
142
|
+
}
|
|
143
|
+
const timeoutMs = toNumber(body.timeoutMs);
|
|
144
|
+
const scrollRequest = {
|
|
145
|
+
cdpUrl,
|
|
146
|
+
targetId: tab.targetId,
|
|
147
|
+
ref,
|
|
148
|
+
};
|
|
149
|
+
if (timeoutMs) {
|
|
150
|
+
scrollRequest.timeoutMs = timeoutMs;
|
|
151
|
+
}
|
|
152
|
+
await pw.scrollIntoViewViaPlaywright(scrollRequest);
|
|
153
|
+
return res.json({ ok: true, targetId: tab.targetId });
|
|
109
154
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
155
|
+
case "drag": {
|
|
156
|
+
const startRef = toStringOrEmpty(body.startRef);
|
|
157
|
+
const endRef = toStringOrEmpty(body.endRef);
|
|
158
|
+
if (!startRef || !endRef) {
|
|
159
|
+
return jsonError(res, 400, "startRef and endRef are required");
|
|
160
|
+
}
|
|
161
|
+
const timeoutMs = toNumber(body.timeoutMs);
|
|
162
|
+
await pw.dragViaPlaywright({
|
|
163
|
+
cdpUrl,
|
|
164
|
+
targetId: tab.targetId,
|
|
165
|
+
startRef,
|
|
166
|
+
endRef,
|
|
167
|
+
timeoutMs: timeoutMs ?? undefined,
|
|
168
|
+
});
|
|
169
|
+
return res.json({ ok: true, targetId: tab.targetId });
|
|
123
170
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
171
|
+
case "select": {
|
|
172
|
+
const ref = toStringOrEmpty(body.ref);
|
|
173
|
+
const values = toStringArray(body.values);
|
|
174
|
+
if (!ref || !values?.length) {
|
|
175
|
+
return jsonError(res, 400, "ref and values are required");
|
|
176
|
+
}
|
|
177
|
+
const timeoutMs = toNumber(body.timeoutMs);
|
|
178
|
+
await pw.selectOptionViaPlaywright({
|
|
179
|
+
cdpUrl,
|
|
180
|
+
targetId: tab.targetId,
|
|
181
|
+
ref,
|
|
182
|
+
values,
|
|
183
|
+
timeoutMs: timeoutMs ?? undefined,
|
|
184
|
+
});
|
|
185
|
+
return res.json({ ok: true, targetId: tab.targetId });
|
|
132
186
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
187
|
+
case "fill": {
|
|
188
|
+
const rawFields = Array.isArray(body.fields) ? body.fields : [];
|
|
189
|
+
const fields = rawFields
|
|
190
|
+
.map((field) => {
|
|
191
|
+
if (!field || typeof field !== "object") {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
const rec = field;
|
|
195
|
+
const ref = toStringOrEmpty(rec.ref);
|
|
196
|
+
const type = toStringOrEmpty(rec.type);
|
|
197
|
+
if (!ref || !type) {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
const value = typeof rec.value === "string" ||
|
|
201
|
+
typeof rec.value === "number" ||
|
|
202
|
+
typeof rec.value === "boolean"
|
|
203
|
+
? rec.value
|
|
204
|
+
: undefined;
|
|
205
|
+
const parsed = value === undefined ? { ref, type } : { ref, type, value };
|
|
206
|
+
return parsed;
|
|
207
|
+
})
|
|
208
|
+
.filter((field) => field !== null);
|
|
209
|
+
if (!fields.length) {
|
|
210
|
+
return jsonError(res, 400, "fields are required");
|
|
211
|
+
}
|
|
212
|
+
const timeoutMs = toNumber(body.timeoutMs);
|
|
213
|
+
await pw.fillFormViaPlaywright({
|
|
214
|
+
cdpUrl,
|
|
215
|
+
targetId: tab.targetId,
|
|
216
|
+
fields,
|
|
217
|
+
timeoutMs: timeoutMs ?? undefined,
|
|
218
|
+
});
|
|
219
|
+
return res.json({ ok: true, targetId: tab.targetId });
|
|
141
220
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
if (!ref || !values?.length) {
|
|
156
|
-
return jsonError(res, 400, "ref and values are required");
|
|
221
|
+
case "resize": {
|
|
222
|
+
const width = toNumber(body.width);
|
|
223
|
+
const height = toNumber(body.height);
|
|
224
|
+
if (!width || !height) {
|
|
225
|
+
return jsonError(res, 400, "width and height are required");
|
|
226
|
+
}
|
|
227
|
+
await pw.resizeViewportViaPlaywright({
|
|
228
|
+
cdpUrl,
|
|
229
|
+
targetId: tab.targetId,
|
|
230
|
+
width,
|
|
231
|
+
height,
|
|
232
|
+
});
|
|
233
|
+
return res.json({ ok: true, targetId: tab.targetId, url: tab.url });
|
|
157
234
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
.
|
|
172
|
-
if (
|
|
173
|
-
return
|
|
235
|
+
case "wait": {
|
|
236
|
+
const timeMs = toNumber(body.timeMs);
|
|
237
|
+
const text = toStringOrEmpty(body.text) || undefined;
|
|
238
|
+
const textGone = toStringOrEmpty(body.textGone) || undefined;
|
|
239
|
+
const selector = toStringOrEmpty(body.selector) || undefined;
|
|
240
|
+
const url = toStringOrEmpty(body.url) || undefined;
|
|
241
|
+
const loadStateRaw = toStringOrEmpty(body.loadState);
|
|
242
|
+
const loadState = loadStateRaw === "load" ||
|
|
243
|
+
loadStateRaw === "domcontentloaded" ||
|
|
244
|
+
loadStateRaw === "networkidle"
|
|
245
|
+
? loadStateRaw
|
|
246
|
+
: undefined;
|
|
247
|
+
const fn = toStringOrEmpty(body.fn) || undefined;
|
|
248
|
+
const timeoutMs = toNumber(body.timeoutMs) ?? undefined;
|
|
249
|
+
if (fn && !evaluateEnabled) {
|
|
250
|
+
return jsonError(res, 403, [
|
|
251
|
+
"wait --fn is disabled by config (browser.evaluateEnabled=false).",
|
|
252
|
+
"Docs: /gateway/configuration#browser-clawd-managed-browser",
|
|
253
|
+
].join("\n"));
|
|
174
254
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
255
|
+
if (timeMs === undefined &&
|
|
256
|
+
!text &&
|
|
257
|
+
!textGone &&
|
|
258
|
+
!selector &&
|
|
259
|
+
!url &&
|
|
260
|
+
!loadState &&
|
|
261
|
+
!fn) {
|
|
262
|
+
return jsonError(res, 400, "wait requires at least one of: timeMs, text, textGone, selector, url, loadState, fn");
|
|
180
263
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
await pw.fillFormViaPlaywright({
|
|
195
|
-
cdpUrl,
|
|
196
|
-
targetId: tab.targetId,
|
|
197
|
-
fields,
|
|
198
|
-
timeoutMs: timeoutMs ?? undefined,
|
|
199
|
-
});
|
|
200
|
-
return res.json({ ok: true, targetId: tab.targetId });
|
|
201
|
-
}
|
|
202
|
-
case "resize": {
|
|
203
|
-
const width = toNumber(body.width);
|
|
204
|
-
const height = toNumber(body.height);
|
|
205
|
-
if (!width || !height) {
|
|
206
|
-
return jsonError(res, 400, "width and height are required");
|
|
207
|
-
}
|
|
208
|
-
await pw.resizeViewportViaPlaywright({
|
|
209
|
-
cdpUrl,
|
|
210
|
-
targetId: tab.targetId,
|
|
211
|
-
width,
|
|
212
|
-
height,
|
|
213
|
-
});
|
|
214
|
-
return res.json({ ok: true, targetId: tab.targetId, url: tab.url });
|
|
215
|
-
}
|
|
216
|
-
case "wait": {
|
|
217
|
-
const timeMs = toNumber(body.timeMs);
|
|
218
|
-
const text = toStringOrEmpty(body.text) || undefined;
|
|
219
|
-
const textGone = toStringOrEmpty(body.textGone) || undefined;
|
|
220
|
-
const selector = toStringOrEmpty(body.selector) || undefined;
|
|
221
|
-
const url = toStringOrEmpty(body.url) || undefined;
|
|
222
|
-
const loadStateRaw = toStringOrEmpty(body.loadState);
|
|
223
|
-
const loadState = loadStateRaw === "load" ||
|
|
224
|
-
loadStateRaw === "domcontentloaded" ||
|
|
225
|
-
loadStateRaw === "networkidle"
|
|
226
|
-
? loadStateRaw
|
|
227
|
-
: undefined;
|
|
228
|
-
const fn = toStringOrEmpty(body.fn) || undefined;
|
|
229
|
-
const timeoutMs = toNumber(body.timeoutMs) ?? undefined;
|
|
230
|
-
if (fn && !evaluateEnabled) {
|
|
231
|
-
return jsonError(res, 403, [
|
|
232
|
-
"wait --fn is disabled by config (browser.evaluateEnabled=false).",
|
|
233
|
-
"Docs: /gateway/configuration#browser-clawd-managed-browser",
|
|
234
|
-
].join("\n"));
|
|
235
|
-
}
|
|
236
|
-
if (timeMs === undefined &&
|
|
237
|
-
!text &&
|
|
238
|
-
!textGone &&
|
|
239
|
-
!selector &&
|
|
240
|
-
!url &&
|
|
241
|
-
!loadState &&
|
|
242
|
-
!fn) {
|
|
243
|
-
return jsonError(res, 400, "wait requires at least one of: timeMs, text, textGone, selector, url, loadState, fn");
|
|
264
|
+
await pw.waitForViaPlaywright({
|
|
265
|
+
cdpUrl,
|
|
266
|
+
targetId: tab.targetId,
|
|
267
|
+
timeMs,
|
|
268
|
+
text,
|
|
269
|
+
textGone,
|
|
270
|
+
selector,
|
|
271
|
+
url,
|
|
272
|
+
loadState,
|
|
273
|
+
fn,
|
|
274
|
+
timeoutMs,
|
|
275
|
+
});
|
|
276
|
+
return res.json({ ok: true, targetId: tab.targetId });
|
|
244
277
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
278
|
+
case "evaluate": {
|
|
279
|
+
if (!evaluateEnabled) {
|
|
280
|
+
return jsonError(res, 403, [
|
|
281
|
+
"act:evaluate is disabled by config (browser.evaluateEnabled=false).",
|
|
282
|
+
"Docs: /gateway/configuration#browser-clawd-managed-browser",
|
|
283
|
+
].join("\n"));
|
|
284
|
+
}
|
|
285
|
+
const fn = toStringOrEmpty(body.fn);
|
|
286
|
+
if (!fn) {
|
|
287
|
+
return jsonError(res, 400, "fn is required");
|
|
288
|
+
}
|
|
289
|
+
const ref = toStringOrEmpty(body.ref) || undefined;
|
|
290
|
+
const evalTimeoutMs = toNumber(body.timeoutMs);
|
|
291
|
+
const evalRequest = {
|
|
292
|
+
cdpUrl,
|
|
293
|
+
targetId: tab.targetId,
|
|
294
|
+
fn,
|
|
295
|
+
ref,
|
|
296
|
+
signal: req.signal,
|
|
297
|
+
};
|
|
298
|
+
if (evalTimeoutMs !== undefined) {
|
|
299
|
+
evalRequest.timeoutMs = evalTimeoutMs;
|
|
300
|
+
}
|
|
301
|
+
const result = await pw.evaluateViaPlaywright(evalRequest);
|
|
302
|
+
return res.json({
|
|
303
|
+
ok: true,
|
|
304
|
+
targetId: tab.targetId,
|
|
305
|
+
url: tab.url,
|
|
306
|
+
result,
|
|
307
|
+
});
|
|
265
308
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
return
|
|
309
|
+
case "close": {
|
|
310
|
+
await pw.closePageViaPlaywright({ cdpUrl, targetId: tab.targetId });
|
|
311
|
+
return res.json({ ok: true, targetId: tab.targetId });
|
|
269
312
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
const evalRequest = {
|
|
273
|
-
cdpUrl,
|
|
274
|
-
targetId: tab.targetId,
|
|
275
|
-
fn,
|
|
276
|
-
ref,
|
|
277
|
-
signal: req.signal,
|
|
278
|
-
};
|
|
279
|
-
if (evalTimeoutMs !== undefined) {
|
|
280
|
-
evalRequest.timeoutMs = evalTimeoutMs;
|
|
313
|
+
default: {
|
|
314
|
+
return jsonError(res, 400, "unsupported kind");
|
|
281
315
|
}
|
|
282
|
-
const result = await pw.evaluateViaPlaywright(evalRequest);
|
|
283
|
-
return res.json({
|
|
284
|
-
ok: true,
|
|
285
|
-
targetId: tab.targetId,
|
|
286
|
-
url: tab.url,
|
|
287
|
-
result,
|
|
288
|
-
});
|
|
289
|
-
}
|
|
290
|
-
case "close": {
|
|
291
|
-
await pw.closePageViaPlaywright({ cdpUrl, targetId: tab.targetId });
|
|
292
|
-
return res.json({ ok: true, targetId: tab.targetId });
|
|
293
316
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
catch (err) {
|
|
300
|
-
handleRouteError(ctx, res, err);
|
|
301
|
-
}
|
|
317
|
+
},
|
|
318
|
+
});
|
|
302
319
|
});
|
|
303
320
|
app.post("/hooks/file-chooser", async (req, res) => {
|
|
304
|
-
const profileCtx = resolveProfileContext(req, res, ctx);
|
|
305
|
-
if (!profileCtx) {
|
|
306
|
-
return;
|
|
307
|
-
}
|
|
308
321
|
const body = readBody(req);
|
|
309
|
-
const targetId =
|
|
322
|
+
const targetId = resolveTargetIdFromBody(body);
|
|
310
323
|
const ref = toStringOrEmpty(body.ref) || undefined;
|
|
311
324
|
const inputRef = toStringOrEmpty(body.inputRef) || undefined;
|
|
312
325
|
const element = toStringOrEmpty(body.element) || undefined;
|
|
@@ -315,111 +328,113 @@ export function registerBrowserAgentActRoutes(app, ctx) {
|
|
|
315
328
|
if (!paths.length) {
|
|
316
329
|
return jsonError(res, 400, "paths are required");
|
|
317
330
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
cdpUrl: profileCtx.profile.cdpUrl,
|
|
330
|
-
targetId: tab.targetId,
|
|
331
|
-
inputRef,
|
|
332
|
-
element,
|
|
333
|
-
paths,
|
|
334
|
-
});
|
|
335
|
-
}
|
|
336
|
-
else {
|
|
337
|
-
await pw.armFileUploadViaPlaywright({
|
|
338
|
-
cdpUrl: profileCtx.profile.cdpUrl,
|
|
339
|
-
targetId: tab.targetId,
|
|
340
|
-
paths,
|
|
341
|
-
timeoutMs: timeoutMs ?? undefined,
|
|
331
|
+
await withPlaywrightRouteContext({
|
|
332
|
+
req,
|
|
333
|
+
res,
|
|
334
|
+
ctx,
|
|
335
|
+
targetId,
|
|
336
|
+
feature: "file chooser hook",
|
|
337
|
+
run: async ({ cdpUrl, tab, pw }) => {
|
|
338
|
+
const uploadPathsResult = resolvePathsWithinRoot({
|
|
339
|
+
rootDir: DEFAULT_UPLOAD_DIR,
|
|
340
|
+
requestedPaths: paths,
|
|
341
|
+
scopeLabel: `uploads directory (${DEFAULT_UPLOAD_DIR})`,
|
|
342
342
|
});
|
|
343
|
-
if (
|
|
344
|
-
|
|
345
|
-
|
|
343
|
+
if (!uploadPathsResult.ok) {
|
|
344
|
+
res.status(400).json({ error: uploadPathsResult.error });
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
const resolvedPaths = uploadPathsResult.paths;
|
|
348
|
+
if (inputRef || element) {
|
|
349
|
+
if (ref) {
|
|
350
|
+
return jsonError(res, 400, "ref cannot be combined with inputRef/element");
|
|
351
|
+
}
|
|
352
|
+
await pw.setInputFilesViaPlaywright({
|
|
353
|
+
cdpUrl,
|
|
346
354
|
targetId: tab.targetId,
|
|
347
|
-
|
|
355
|
+
inputRef,
|
|
356
|
+
element,
|
|
357
|
+
paths: resolvedPaths,
|
|
348
358
|
});
|
|
349
359
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
360
|
+
else {
|
|
361
|
+
await pw.armFileUploadViaPlaywright({
|
|
362
|
+
cdpUrl,
|
|
363
|
+
targetId: tab.targetId,
|
|
364
|
+
paths: resolvedPaths,
|
|
365
|
+
timeoutMs: timeoutMs ?? undefined,
|
|
366
|
+
});
|
|
367
|
+
if (ref) {
|
|
368
|
+
await pw.clickViaPlaywright({
|
|
369
|
+
cdpUrl,
|
|
370
|
+
targetId: tab.targetId,
|
|
371
|
+
ref,
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
res.json({ ok: true });
|
|
376
|
+
},
|
|
377
|
+
});
|
|
356
378
|
});
|
|
357
379
|
app.post("/hooks/dialog", async (req, res) => {
|
|
358
|
-
const profileCtx = resolveProfileContext(req, res, ctx);
|
|
359
|
-
if (!profileCtx) {
|
|
360
|
-
return;
|
|
361
|
-
}
|
|
362
380
|
const body = readBody(req);
|
|
363
|
-
const targetId =
|
|
381
|
+
const targetId = resolveTargetIdFromBody(body);
|
|
364
382
|
const accept = toBoolean(body.accept);
|
|
365
383
|
const promptText = toStringOrEmpty(body.promptText) || undefined;
|
|
366
384
|
const timeoutMs = toNumber(body.timeoutMs);
|
|
367
385
|
if (accept === undefined) {
|
|
368
386
|
return jsonError(res, 400, "accept is required");
|
|
369
387
|
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
}
|
|
388
|
+
await withPlaywrightRouteContext({
|
|
389
|
+
req,
|
|
390
|
+
res,
|
|
391
|
+
ctx,
|
|
392
|
+
targetId,
|
|
393
|
+
feature: "dialog hook",
|
|
394
|
+
run: async ({ cdpUrl, tab, pw }) => {
|
|
395
|
+
await pw.armDialogViaPlaywright({
|
|
396
|
+
cdpUrl,
|
|
397
|
+
targetId: tab.targetId,
|
|
398
|
+
accept,
|
|
399
|
+
promptText,
|
|
400
|
+
timeoutMs: timeoutMs ?? undefined,
|
|
401
|
+
});
|
|
402
|
+
res.json({ ok: true });
|
|
403
|
+
},
|
|
404
|
+
});
|
|
388
405
|
});
|
|
389
406
|
app.post("/wait/download", async (req, res) => {
|
|
390
|
-
const profileCtx = resolveProfileContext(req, res, ctx);
|
|
391
|
-
if (!profileCtx) {
|
|
392
|
-
return;
|
|
393
|
-
}
|
|
394
407
|
const body = readBody(req);
|
|
395
|
-
const targetId =
|
|
396
|
-
const out = toStringOrEmpty(body.path) ||
|
|
408
|
+
const targetId = resolveTargetIdFromBody(body);
|
|
409
|
+
const out = toStringOrEmpty(body.path) || "";
|
|
397
410
|
const timeoutMs = toNumber(body.timeoutMs);
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
411
|
+
await withPlaywrightRouteContext({
|
|
412
|
+
req,
|
|
413
|
+
res,
|
|
414
|
+
ctx,
|
|
415
|
+
targetId,
|
|
416
|
+
feature: "wait for download",
|
|
417
|
+
run: async ({ cdpUrl, tab, pw }) => {
|
|
418
|
+
let downloadPath;
|
|
419
|
+
if (out.trim()) {
|
|
420
|
+
const resolvedDownloadPath = resolveDownloadPathOrRespond(res, out);
|
|
421
|
+
if (!resolvedDownloadPath) {
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
downloadPath = resolvedDownloadPath;
|
|
425
|
+
}
|
|
426
|
+
const requestBase = buildDownloadRequestBase(cdpUrl, tab.targetId, timeoutMs);
|
|
427
|
+
const result = await pw.waitForDownloadViaPlaywright({
|
|
428
|
+
...requestBase,
|
|
429
|
+
path: downloadPath,
|
|
430
|
+
});
|
|
431
|
+
respondWithDownloadResult(res, tab.targetId, result);
|
|
432
|
+
},
|
|
433
|
+
});
|
|
415
434
|
});
|
|
416
435
|
app.post("/download", async (req, res) => {
|
|
417
|
-
const profileCtx = resolveProfileContext(req, res, ctx);
|
|
418
|
-
if (!profileCtx) {
|
|
419
|
-
return;
|
|
420
|
-
}
|
|
421
436
|
const body = readBody(req);
|
|
422
|
-
const targetId =
|
|
437
|
+
const targetId = resolveTargetIdFromBody(body);
|
|
423
438
|
const ref = toStringOrEmpty(body.ref);
|
|
424
439
|
const out = toStringOrEmpty(body.path);
|
|
425
440
|
const timeoutMs = toNumber(body.timeoutMs);
|
|
@@ -429,83 +444,75 @@ export function registerBrowserAgentActRoutes(app, ctx) {
|
|
|
429
444
|
if (!out) {
|
|
430
445
|
return jsonError(res, 400, "path is required");
|
|
431
446
|
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
447
|
+
await withPlaywrightRouteContext({
|
|
448
|
+
req,
|
|
449
|
+
res,
|
|
450
|
+
ctx,
|
|
451
|
+
targetId,
|
|
452
|
+
feature: "download",
|
|
453
|
+
run: async ({ cdpUrl, tab, pw }) => {
|
|
454
|
+
const downloadPath = resolveDownloadPathOrRespond(res, out);
|
|
455
|
+
if (!downloadPath) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
const requestBase = buildDownloadRequestBase(cdpUrl, tab.targetId, timeoutMs);
|
|
459
|
+
const result = await pw.downloadViaPlaywright({
|
|
460
|
+
...requestBase,
|
|
461
|
+
ref,
|
|
462
|
+
path: downloadPath,
|
|
463
|
+
});
|
|
464
|
+
respondWithDownloadResult(res, tab.targetId, result);
|
|
465
|
+
},
|
|
466
|
+
});
|
|
450
467
|
});
|
|
451
468
|
app.post("/response/body", async (req, res) => {
|
|
452
|
-
const profileCtx = resolveProfileContext(req, res, ctx);
|
|
453
|
-
if (!profileCtx) {
|
|
454
|
-
return;
|
|
455
|
-
}
|
|
456
469
|
const body = readBody(req);
|
|
457
|
-
const targetId =
|
|
470
|
+
const targetId = resolveTargetIdFromBody(body);
|
|
458
471
|
const url = toStringOrEmpty(body.url);
|
|
459
472
|
const timeoutMs = toNumber(body.timeoutMs);
|
|
460
473
|
const maxChars = toNumber(body.maxChars);
|
|
461
474
|
if (!url) {
|
|
462
475
|
return jsonError(res, 400, "url is required");
|
|
463
476
|
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
}
|
|
477
|
+
await withPlaywrightRouteContext({
|
|
478
|
+
req,
|
|
479
|
+
res,
|
|
480
|
+
ctx,
|
|
481
|
+
targetId,
|
|
482
|
+
feature: "response body",
|
|
483
|
+
run: async ({ cdpUrl, tab, pw }) => {
|
|
484
|
+
const result = await pw.responseBodyViaPlaywright({
|
|
485
|
+
cdpUrl,
|
|
486
|
+
targetId: tab.targetId,
|
|
487
|
+
url,
|
|
488
|
+
timeoutMs: timeoutMs ?? undefined,
|
|
489
|
+
maxChars: maxChars ?? undefined,
|
|
490
|
+
});
|
|
491
|
+
res.json({ ok: true, targetId: tab.targetId, response: result });
|
|
492
|
+
},
|
|
493
|
+
});
|
|
482
494
|
});
|
|
483
495
|
app.post("/highlight", async (req, res) => {
|
|
484
|
-
const profileCtx = resolveProfileContext(req, res, ctx);
|
|
485
|
-
if (!profileCtx) {
|
|
486
|
-
return;
|
|
487
|
-
}
|
|
488
496
|
const body = readBody(req);
|
|
489
|
-
const targetId =
|
|
497
|
+
const targetId = resolveTargetIdFromBody(body);
|
|
490
498
|
const ref = toStringOrEmpty(body.ref);
|
|
491
499
|
if (!ref) {
|
|
492
500
|
return jsonError(res, 400, "ref is required");
|
|
493
501
|
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
}
|
|
502
|
+
await withPlaywrightRouteContext({
|
|
503
|
+
req,
|
|
504
|
+
res,
|
|
505
|
+
ctx,
|
|
506
|
+
targetId,
|
|
507
|
+
feature: "highlight",
|
|
508
|
+
run: async ({ cdpUrl, tab, pw }) => {
|
|
509
|
+
await pw.highlightViaPlaywright({
|
|
510
|
+
cdpUrl,
|
|
511
|
+
targetId: tab.targetId,
|
|
512
|
+
ref,
|
|
513
|
+
});
|
|
514
|
+
res.json({ ok: true, targetId: tab.targetId });
|
|
515
|
+
},
|
|
516
|
+
});
|
|
510
517
|
});
|
|
511
518
|
}
|