@poolzin/pool-bot 2026.2.0 → 2026.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/CHANGELOG.md +118 -0
- package/README-header.png +0 -0
- package/dist/agents/bash-tools.exec.js +76 -25
- package/dist/agents/cli-runner/helpers.js +9 -11
- package/dist/agents/context.js +1 -1
- package/dist/agents/identity.js +47 -7
- package/dist/agents/memory-search.js +25 -8
- package/dist/agents/model-catalog.js +1 -1
- package/dist/agents/model-selection.js +21 -0
- package/dist/agents/pi-embedded-block-chunker.js +117 -42
- package/dist/agents/pi-embedded-helpers/errors.js +183 -78
- package/dist/agents/pi-embedded-helpers.js +1 -1
- package/dist/agents/pi-embedded-runner/compact.js +8 -10
- package/dist/agents/pi-embedded-runner/model.js +62 -3
- package/dist/agents/pi-embedded-runner/run/attempt.js +21 -11
- package/dist/agents/pi-embedded-runner/run.js +199 -46
- package/dist/agents/pi-embedded-runner/system-prompt.js +10 -2
- package/dist/agents/pi-embedded-subscribe.js +118 -29
- package/dist/agents/pi-tools.js +10 -5
- package/dist/agents/poolbot-tools.js +15 -10
- package/dist/agents/sandbox-paths.js +31 -0
- package/dist/agents/session-tool-result-guard.js +94 -15
- package/dist/agents/shell-utils.js +51 -0
- package/dist/agents/skills/bundled-context.js +23 -0
- package/dist/agents/skills/bundled-dir.js +41 -7
- package/dist/agents/skills-install.js +60 -23
- package/dist/agents/subagent-announce.js +79 -34
- package/dist/agents/tool-policy.conformance.js +14 -0
- package/dist/agents/tool-policy.js +24 -0
- package/dist/agents/tools/cron-tool.js +166 -19
- package/dist/agents/tools/discord-actions-presence.js +78 -0
- package/dist/agents/tools/image-tool.js +1 -1
- package/dist/agents/tools/message-tool.js +56 -2
- package/dist/agents/tools/sessions-history-tool.js +69 -1
- package/dist/agents/tools/web-search.js +211 -42
- package/dist/agents/usage.js +23 -1
- package/dist/agents/workspace-run.js +67 -0
- package/dist/agents/workspace-templates.js +44 -0
- package/dist/auto-reply/command-auth.js +121 -6
- package/dist/auto-reply/envelope.js +74 -82
- package/dist/auto-reply/reply/commands-compact.js +1 -0
- package/dist/auto-reply/reply/commands-context-report.js +1 -0
- package/dist/auto-reply/reply/commands-context.js +1 -0
- package/dist/auto-reply/reply/commands-models.js +107 -60
- package/dist/auto-reply/reply/commands-ptt.js +171 -0
- package/dist/auto-reply/reply/get-reply-run.js +2 -1
- package/dist/auto-reply/reply/inbound-context.js +5 -1
- package/dist/auto-reply/reply/mentions.js +1 -1
- package/dist/auto-reply/reply/model-selection.js +3 -3
- package/dist/auto-reply/thinking.js +88 -43
- package/dist/browser/bridge-server.js +13 -0
- package/dist/browser/cdp.helpers.js +38 -24
- package/dist/browser/client-fetch.js +50 -7
- package/dist/browser/config.js +1 -10
- package/dist/browser/extension-relay.js +101 -40
- package/dist/browser/pw-ai.js +1 -1
- package/dist/browser/pw-session.js +143 -8
- package/dist/browser/pw-tools-core.interactions.js +125 -27
- package/dist/browser/pw-tools-core.responses.js +1 -1
- package/dist/browser/pw-tools-core.state.js +1 -1
- package/dist/browser/routes/agent.act.js +86 -41
- package/dist/browser/routes/dispatcher.js +4 -4
- package/dist/browser/screenshot.js +1 -1
- package/dist/browser/server.js +13 -0
- package/dist/build-info.json +3 -3
- package/dist/canvas-host/a2ui/index.html +28 -28
- package/dist/channels/reply-prefix.js +8 -1
- package/dist/cli/cron-cli/register.cron-add.js +61 -40
- package/dist/cli/cron-cli/register.cron-edit.js +60 -34
- package/dist/cli/cron-cli/shared.js +56 -41
- package/dist/cli/dns-cli.js +26 -14
- package/dist/cli/gateway-cli/register.js +37 -19
- package/dist/cli/memory-cli.js +5 -5
- package/dist/cli/parse-bytes.js +37 -0
- package/dist/cli/update-cli.js +173 -52
- package/dist/commands/agent.js +1 -0
- package/dist/commands/auth-choice.apply.oauth.js +1 -1
- package/dist/commands/doctor-config-flow.js +61 -5
- package/dist/commands/doctor-state-migrations.js +1 -1
- package/dist/commands/health.js +1 -1
- package/dist/commands/model-allowlist.js +29 -0
- package/dist/commands/model-picker.js +2 -1
- package/dist/commands/models/list.registry.js +1 -1
- package/dist/commands/models/list.status-command.js +43 -23
- package/dist/commands/models/shared.js +15 -0
- package/dist/commands/onboard-custom.js +384 -0
- package/dist/commands/onboard-non-interactive/local/auth-choice-inference.js +35 -0
- package/dist/commands/onboard-non-interactive/local/auth-choice.js +6 -3
- package/dist/commands/onboard-skills.js +63 -38
- package/dist/commands/openai-model-default.js +41 -0
- package/dist/compat/legacy-names.js +2 -0
- package/dist/config/defaults.js +3 -2
- package/dist/config/paths.js +136 -35
- package/dist/config/plugin-auto-enable.js +21 -5
- package/dist/config/redact-snapshot.js +153 -0
- package/dist/config/schema.field-metadata.js +590 -0
- package/dist/config/schema.js +2 -2
- package/dist/config/sessions/store.js +291 -23
- package/dist/config/zod-schema.agent-defaults.js +3 -0
- package/dist/config/zod-schema.agent-runtime.js +13 -2
- package/dist/config/zod-schema.providers-core.js +142 -0
- package/dist/config/zod-schema.session.js +3 -0
- package/dist/control-ui/assets/{index-CIRDm-Lu.css → index-CSfXd2LO.css} +1 -1
- package/dist/control-ui/assets/{index-CmNMuoem.js → index-HRr1grwl.js} +446 -413
- package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -0
- package/dist/control-ui/index.html +4 -4
- package/dist/cron/delivery.js +57 -0
- package/dist/cron/isolated-agent/delivery-target.js +18 -3
- package/dist/cron/isolated-agent/helpers.js +22 -5
- package/dist/cron/isolated-agent/run.js +172 -63
- package/dist/cron/isolated-agent/session.js +2 -0
- package/dist/cron/normalize.js +356 -28
- package/dist/cron/parse.js +10 -5
- package/dist/cron/run-log.js +35 -10
- package/dist/cron/schedule.js +41 -6
- package/dist/cron/service/jobs.js +208 -35
- package/dist/cron/service/ops.js +72 -16
- package/dist/cron/service/state.js +2 -0
- package/dist/cron/service/store.js +386 -14
- package/dist/cron/service/timer.js +390 -147
- package/dist/cron/session-reaper.js +86 -0
- package/dist/cron/store.js +23 -8
- package/dist/cron/validate-timestamp.js +43 -0
- package/dist/discord/monitor/agent-components.js +438 -0
- package/dist/discord/monitor/allow-list.js +28 -5
- package/dist/discord/monitor/gateway-registry.js +29 -0
- package/dist/discord/monitor/native-command.js +44 -23
- package/dist/discord/monitor/sender-identity.js +45 -0
- package/dist/discord/pluralkit.js +27 -0
- package/dist/discord/send.outbound.js +92 -5
- package/dist/discord/send.shared.js +60 -23
- package/dist/discord/targets.js +84 -1
- package/dist/entry.js +15 -9
- package/dist/extensionAPI.js +8 -0
- package/dist/gateway/control-ui.js +8 -1
- package/dist/gateway/hooks-mapping.js +3 -0
- package/dist/gateway/hooks.js +65 -0
- package/dist/gateway/net.js +96 -31
- package/dist/gateway/node-command-policy.js +50 -15
- package/dist/gateway/origin-check.js +56 -0
- package/dist/gateway/protocol/client-info.js +9 -0
- package/dist/gateway/protocol/index.js +9 -2
- package/dist/gateway/protocol/schema/agents-models-skills.js +71 -1
- package/dist/gateway/protocol/schema/cron.js +22 -10
- package/dist/gateway/protocol/schema/protocol-schemas.js +16 -2
- package/dist/gateway/protocol/schema/sessions.js +12 -0
- package/dist/gateway/server/hooks.js +1 -1
- package/dist/gateway/server-broadcast.js +26 -9
- package/dist/gateway/server-chat.js +112 -23
- package/dist/gateway/server-discovery-runtime.js +10 -2
- package/dist/gateway/server-http.js +109 -11
- package/dist/gateway/server-methods/agent-timestamp.js +60 -0
- package/dist/gateway/server-methods/agents.js +321 -2
- package/dist/gateway/server-methods/usage.js +559 -16
- package/dist/gateway/server-runtime-state.js +22 -8
- package/dist/gateway/server-startup-memory.js +16 -0
- package/dist/gateway/server.impl.js +5 -1
- package/dist/gateway/session-utils.fs.js +23 -25
- package/dist/gateway/session-utils.js +20 -10
- package/dist/gateway/sessions-patch.js +7 -22
- package/dist/gateway/test-helpers.mocks.js +11 -7
- package/dist/gateway/test-helpers.server.js +35 -2
- package/dist/imessage/constants.js +2 -0
- package/dist/imessage/monitor/deliver.js +4 -1
- package/dist/imessage/monitor/monitor-provider.js +51 -1
- package/dist/infra/bonjour-discovery.js +131 -70
- package/dist/infra/control-ui-assets.js +134 -12
- package/dist/infra/errors.js +12 -0
- package/dist/infra/exec-approvals.js +266 -57
- package/dist/infra/format-time/format-datetime.js +79 -0
- package/dist/infra/format-time/format-duration.js +81 -0
- package/dist/infra/format-time/format-relative.js +80 -0
- package/dist/infra/heartbeat-runner.js +140 -49
- package/dist/infra/home-dir.js +54 -0
- package/dist/infra/net/fetch-guard.js +122 -0
- package/dist/infra/net/ssrf.js +65 -29
- package/dist/infra/outbound/abort.js +14 -0
- package/dist/infra/outbound/message-action-runner.js +77 -13
- package/dist/infra/outbound/outbound-session.js +143 -37
- package/dist/infra/poolbot-root.js +43 -1
- package/dist/infra/session-cost-usage.js +631 -41
- package/dist/infra/state-migrations.js +317 -47
- package/dist/infra/update-global.js +35 -0
- package/dist/infra/update-runner.js +149 -43
- package/dist/infra/warning-filter.js +65 -0
- package/dist/infra/widearea-dns.js +30 -9
- package/dist/logging/redact-identifier.js +12 -0
- package/dist/media/fetch.js +81 -58
- package/dist/media/store.js +2 -0
- package/dist/media-understanding/apply.js +403 -3
- package/dist/media-understanding/attachments.js +38 -27
- package/dist/media-understanding/defaults.js +16 -0
- package/dist/media-understanding/providers/deepgram/audio.js +22 -14
- package/dist/media-understanding/providers/google/audio.js +24 -17
- package/dist/media-understanding/providers/google/video.js +24 -17
- package/dist/media-understanding/providers/image.js +3 -3
- package/dist/media-understanding/providers/index.js +4 -1
- package/dist/media-understanding/providers/openai/audio.js +22 -14
- package/dist/media-understanding/providers/shared.js +16 -11
- package/dist/media-understanding/providers/zai/index.js +6 -0
- package/dist/media-understanding/runner.js +158 -90
- package/dist/memory/batch-voyage.js +277 -0
- package/dist/memory/embeddings-voyage.js +75 -0
- package/dist/memory/embeddings.js +28 -16
- package/dist/memory/internal.js +101 -18
- package/dist/memory/manager.js +154 -48
- package/dist/memory/search-manager.js +173 -0
- package/dist/memory/session-files.js +9 -3
- package/dist/node-host/runner.js +34 -24
- package/dist/node-host/with-timeout.js +27 -0
- package/dist/plugins/commands.js +5 -1
- package/dist/plugins/config-state.js +86 -7
- package/dist/plugins/source-display.js +51 -0
- package/dist/process/exec.js +20 -2
- package/dist/routing/resolve-route.js +12 -0
- package/dist/routing/session-key.js +15 -0
- package/dist/runtime.js +2 -0
- package/dist/security/audit-extra.async.js +601 -0
- package/dist/security/audit-extra.js +2 -830
- package/dist/security/audit-extra.sync.js +505 -0
- package/dist/security/channel-metadata.js +34 -0
- package/dist/security/external-content.js +88 -6
- package/dist/security/skill-scanner.js +330 -0
- package/dist/sessions/session-key-utils.js +7 -0
- package/dist/signal/monitor/event-handler.js +80 -1
- package/dist/slack/monitor/media.js +85 -15
- package/dist/tailscale/detect.js +1 -2
- package/dist/telegram/bot/helpers.js +109 -28
- package/dist/telegram/bot-handlers.js +144 -3
- package/dist/telegram/bot-message-context.js +37 -10
- package/dist/telegram/bot-message-dispatch.js +54 -17
- package/dist/telegram/bot-native-commands.js +86 -29
- package/dist/telegram/bot.js +30 -29
- package/dist/telegram/model-buttons.js +163 -0
- package/dist/telegram/monitor.js +110 -85
- package/dist/telegram/send.js +129 -47
- package/dist/terminal/restore.js +45 -0
- package/dist/test-helpers/state-dir-env.js +16 -0
- package/dist/tts/tts.js +12 -6
- package/dist/tui/tui-session-actions.js +166 -54
- package/dist/utils/fetch-timeout.js +20 -0
- package/dist/utils/normalize-secret-input.js +19 -0
- package/dist/utils/transcript-tools.js +58 -0
- package/dist/utils.js +45 -14
- package/dist/version.js +42 -5
- package/dist/wizard/clack-prompter.js +9 -6
- package/extensions/googlechat/node_modules/.bin/poolbot +21 -0
- package/extensions/googlechat/package.json +2 -2
- package/extensions/line/node_modules/.bin/poolbot +21 -0
- package/extensions/line/package.json +1 -1
- package/extensions/matrix/node_modules/.bin/poolbot +21 -0
- package/extensions/matrix/package.json +1 -1
- package/extensions/memory-core/node_modules/.bin/poolbot +21 -0
- package/extensions/memory-core/package.json +4 -1
- package/extensions/twitch/node_modules/.bin/poolbot +21 -0
- package/extensions/twitch/package.json +1 -1
- package/package.json +183 -24
- package/dist/control-ui/assets/index-CmNMuoem.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ensurePageState, getPageForTargetId, refLocator, restoreRoleRefsForTarget, } from "./pw-session.js";
|
|
1
|
+
import { ensurePageState, forceDisconnectPlaywrightForTarget, getPageForTargetId, refLocator, restoreRoleRefsForTarget, } from "./pw-session.js";
|
|
2
2
|
import { normalizeTimeoutMs, requireRef, toAIFriendlyError } from "./pw-tools-core.shared.js";
|
|
3
3
|
export async function highlightViaPlaywright(opts) {
|
|
4
4
|
const page = await getPageForTargetId(opts);
|
|
@@ -160,40 +160,138 @@ export async function fillFormViaPlaywright(opts) {
|
|
|
160
160
|
}
|
|
161
161
|
export async function evaluateViaPlaywright(opts) {
|
|
162
162
|
const fnText = String(opts.fn ?? "").trim();
|
|
163
|
-
if (!fnText)
|
|
163
|
+
if (!fnText) {
|
|
164
164
|
throw new Error("function is required");
|
|
165
|
+
}
|
|
165
166
|
const page = await getPageForTargetId(opts);
|
|
166
167
|
ensurePageState(page);
|
|
167
168
|
restoreRoleRefsForTarget({ cdpUrl: opts.cdpUrl, targetId: opts.targetId, page });
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
169
|
+
// Clamp evaluate timeout to prevent permanently blocking Playwright's command queue.
|
|
170
|
+
// Without this, a long-running async evaluate blocks all subsequent page operations
|
|
171
|
+
// because Playwright serializes CDP commands per page.
|
|
172
|
+
//
|
|
173
|
+
// NOTE: Playwright's { timeout } on evaluate only applies to installing the function,
|
|
174
|
+
// NOT to its execution time. We must inject a Promise.race timeout into the browser
|
|
175
|
+
// context itself so async functions are bounded.
|
|
176
|
+
const outerTimeout = normalizeTimeoutMs(opts.timeoutMs, 20_000);
|
|
177
|
+
// Leave headroom for routing/serialization overhead so the outer request timeout
|
|
178
|
+
// doesn't fire first and strand a long-running evaluate.
|
|
179
|
+
let evaluateTimeout = Math.max(1000, Math.min(120_000, outerTimeout - 500));
|
|
180
|
+
evaluateTimeout = Math.min(evaluateTimeout, outerTimeout);
|
|
181
|
+
const signal = opts.signal;
|
|
182
|
+
let abortListener;
|
|
183
|
+
let abortReject;
|
|
184
|
+
let abortPromise;
|
|
185
|
+
if (signal) {
|
|
186
|
+
abortPromise = new Promise((_, reject) => {
|
|
187
|
+
abortReject = reject;
|
|
188
|
+
});
|
|
189
|
+
// Ensure the abort promise never becomes an unhandled rejection if we throw early.
|
|
190
|
+
void abortPromise.catch(() => { });
|
|
191
|
+
}
|
|
192
|
+
if (signal) {
|
|
193
|
+
const disconnect = () => {
|
|
194
|
+
void forceDisconnectPlaywrightForTarget({
|
|
195
|
+
cdpUrl: opts.cdpUrl,
|
|
196
|
+
targetId: opts.targetId,
|
|
197
|
+
reason: "evaluate aborted",
|
|
198
|
+
}).catch(() => { });
|
|
199
|
+
};
|
|
200
|
+
if (signal.aborted) {
|
|
201
|
+
disconnect();
|
|
202
|
+
throw signal.reason ?? new Error("aborted");
|
|
203
|
+
}
|
|
204
|
+
abortListener = () => {
|
|
205
|
+
disconnect();
|
|
206
|
+
abortReject?.(signal.reason ?? new Error("aborted"));
|
|
207
|
+
};
|
|
208
|
+
signal.addEventListener("abort", abortListener, { once: true });
|
|
209
|
+
// If the signal aborted between the initial check and listener registration, handle it.
|
|
210
|
+
if (signal.aborted) {
|
|
211
|
+
abortListener();
|
|
212
|
+
throw signal.reason ?? new Error("aborted");
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
try {
|
|
216
|
+
if (opts.ref) {
|
|
217
|
+
const locator = refLocator(page, opts.ref);
|
|
218
|
+
// eslint-disable-next-line @typescript-eslint/no-implied-eval -- required for browser-context eval
|
|
219
|
+
const elementEvaluator = new Function("el", "args", `
|
|
220
|
+
"use strict";
|
|
221
|
+
var fnBody = args.fnBody, timeoutMs = args.timeoutMs;
|
|
222
|
+
try {
|
|
223
|
+
var candidate = eval("(" + fnBody + ")");
|
|
224
|
+
var result = typeof candidate === "function" ? candidate(el) : candidate;
|
|
225
|
+
if (result && typeof result.then === "function") {
|
|
226
|
+
return Promise.race([
|
|
227
|
+
result,
|
|
228
|
+
new Promise(function(_, reject) {
|
|
229
|
+
setTimeout(function() { reject(new Error("evaluate timed out after " + timeoutMs + "ms")); }, timeoutMs);
|
|
230
|
+
})
|
|
231
|
+
]);
|
|
232
|
+
}
|
|
233
|
+
return result;
|
|
234
|
+
} catch (err) {
|
|
235
|
+
throw new Error("Invalid evaluate function: " + (err && err.message ? err.message : String(err)));
|
|
236
|
+
}
|
|
237
|
+
`);
|
|
238
|
+
const evalPromise = locator.evaluate(elementEvaluator, {
|
|
239
|
+
fnBody: fnText,
|
|
240
|
+
timeoutMs: evaluateTimeout,
|
|
241
|
+
});
|
|
242
|
+
if (!abortPromise) {
|
|
243
|
+
return await evalPromise;
|
|
244
|
+
}
|
|
245
|
+
try {
|
|
246
|
+
return await Promise.race([evalPromise, abortPromise]);
|
|
247
|
+
}
|
|
248
|
+
catch (err) {
|
|
249
|
+
// If abort wins the race, the underlying evaluate may reject later; ensure we don't
|
|
250
|
+
// surface it as an unhandled rejection.
|
|
251
|
+
void evalPromise.catch(() => { });
|
|
252
|
+
throw err;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
172
255
|
// eslint-disable-next-line @typescript-eslint/no-implied-eval -- required for browser-context eval
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
256
|
+
const browserEvaluator = new Function("args", `
|
|
257
|
+
"use strict";
|
|
258
|
+
var fnBody = args.fnBody, timeoutMs = args.timeoutMs;
|
|
259
|
+
try {
|
|
260
|
+
var candidate = eval("(" + fnBody + ")");
|
|
261
|
+
var result = typeof candidate === "function" ? candidate() : candidate;
|
|
262
|
+
if (result && typeof result.then === "function") {
|
|
263
|
+
return Promise.race([
|
|
264
|
+
result,
|
|
265
|
+
new Promise(function(_, reject) {
|
|
266
|
+
setTimeout(function() { reject(new Error("evaluate timed out after " + timeoutMs + "ms")); }, timeoutMs);
|
|
267
|
+
})
|
|
268
|
+
]);
|
|
269
|
+
}
|
|
270
|
+
return result;
|
|
271
|
+
} catch (err) {
|
|
272
|
+
throw new Error("Invalid evaluate function: " + (err && err.message ? err.message : String(err)));
|
|
273
|
+
}
|
|
181
274
|
`);
|
|
182
|
-
|
|
275
|
+
const evalPromise = page.evaluate(browserEvaluator, {
|
|
276
|
+
fnBody: fnText,
|
|
277
|
+
timeoutMs: evaluateTimeout,
|
|
278
|
+
});
|
|
279
|
+
if (!abortPromise) {
|
|
280
|
+
return await evalPromise;
|
|
281
|
+
}
|
|
282
|
+
try {
|
|
283
|
+
return await Promise.race([evalPromise, abortPromise]);
|
|
284
|
+
}
|
|
285
|
+
catch (err) {
|
|
286
|
+
void evalPromise.catch(() => { });
|
|
287
|
+
throw err;
|
|
288
|
+
}
|
|
183
289
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
"use strict";
|
|
189
|
-
try {
|
|
190
|
-
var candidate = eval("(" + fnBody + ")");
|
|
191
|
-
return typeof candidate === "function" ? candidate() : candidate;
|
|
192
|
-
} catch (err) {
|
|
193
|
-
throw new Error("Invalid evaluate function: " + (err && err.message ? err.message : String(err)));
|
|
290
|
+
finally {
|
|
291
|
+
if (signal && abortListener) {
|
|
292
|
+
signal.removeEventListener("abort", abortListener);
|
|
293
|
+
}
|
|
194
294
|
}
|
|
195
|
-
`);
|
|
196
|
-
return await page.evaluate(browserEvaluator, fnText);
|
|
197
295
|
}
|
|
198
296
|
export async function scrollIntoViewViaPlaywright(opts) {
|
|
199
297
|
const page = await getPageForTargetId(opts);
|
|
@@ -70,7 +70,7 @@ export async function responseBodyViaPlaywright(opts) {
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
catch (err) {
|
|
73
|
-
throw new Error(`Failed to read response body for "${url}": ${String(err)}
|
|
73
|
+
throw new Error(`Failed to read response body for "${url}": ${String(err)}`, { cause: err });
|
|
74
74
|
}
|
|
75
75
|
const trimmed = bodyText.length > maxChars ? bodyText.slice(0, maxChars) : bodyText;
|
|
76
76
|
return {
|
|
@@ -100,7 +100,7 @@ export async function setTimezoneViaPlaywright(opts) {
|
|
|
100
100
|
if (msg.includes("Timezone override is already in effect"))
|
|
101
101
|
return;
|
|
102
102
|
if (msg.includes("Invalid timezone"))
|
|
103
|
-
throw new Error(`Invalid timezone ID: ${timezoneId}
|
|
103
|
+
throw new Error(`Invalid timezone ID: ${timezoneId}`, { cause: err });
|
|
104
104
|
throw err;
|
|
105
105
|
}
|
|
106
106
|
});
|
|
@@ -4,8 +4,9 @@ import { jsonError, toBoolean, toNumber, toStringArray, toStringOrEmpty } from "
|
|
|
4
4
|
export function registerBrowserAgentActRoutes(app, ctx) {
|
|
5
5
|
app.post("/act", async (req, res) => {
|
|
6
6
|
const profileCtx = resolveProfileContext(req, res, ctx);
|
|
7
|
-
if (!profileCtx)
|
|
7
|
+
if (!profileCtx) {
|
|
8
8
|
return;
|
|
9
|
+
}
|
|
9
10
|
const body = readBody(req);
|
|
10
11
|
const kindRaw = toStringOrEmpty(body.kind);
|
|
11
12
|
if (!isActKind(kindRaw)) {
|
|
@@ -20,20 +21,23 @@ export function registerBrowserAgentActRoutes(app, ctx) {
|
|
|
20
21
|
const tab = await profileCtx.ensureTabAvailable(targetId);
|
|
21
22
|
const cdpUrl = profileCtx.profile.cdpUrl;
|
|
22
23
|
const pw = await requirePwAi(res, `act:${kind}`);
|
|
23
|
-
if (!pw)
|
|
24
|
+
if (!pw) {
|
|
24
25
|
return;
|
|
26
|
+
}
|
|
25
27
|
const evaluateEnabled = ctx.state().resolved.evaluateEnabled;
|
|
26
28
|
switch (kind) {
|
|
27
29
|
case "click": {
|
|
28
30
|
const ref = toStringOrEmpty(body.ref);
|
|
29
|
-
if (!ref)
|
|
31
|
+
if (!ref) {
|
|
30
32
|
return jsonError(res, 400, "ref is required");
|
|
33
|
+
}
|
|
31
34
|
const doubleClick = toBoolean(body.doubleClick) ?? false;
|
|
32
35
|
const timeoutMs = toNumber(body.timeoutMs);
|
|
33
36
|
const buttonRaw = toStringOrEmpty(body.button) || "";
|
|
34
37
|
const button = buttonRaw ? parseClickButton(buttonRaw) : undefined;
|
|
35
|
-
if (buttonRaw && !button)
|
|
38
|
+
if (buttonRaw && !button) {
|
|
36
39
|
return jsonError(res, 400, "button must be left|right|middle");
|
|
40
|
+
}
|
|
37
41
|
const modifiersRaw = toStringArray(body.modifiers) ?? [];
|
|
38
42
|
const parsedModifiers = parseClickModifiers(modifiersRaw);
|
|
39
43
|
if (parsedModifiers.error) {
|
|
@@ -46,21 +50,26 @@ export function registerBrowserAgentActRoutes(app, ctx) {
|
|
|
46
50
|
ref,
|
|
47
51
|
doubleClick,
|
|
48
52
|
};
|
|
49
|
-
if (button)
|
|
53
|
+
if (button) {
|
|
50
54
|
clickRequest.button = button;
|
|
51
|
-
|
|
55
|
+
}
|
|
56
|
+
if (modifiers) {
|
|
52
57
|
clickRequest.modifiers = modifiers;
|
|
53
|
-
|
|
58
|
+
}
|
|
59
|
+
if (timeoutMs) {
|
|
54
60
|
clickRequest.timeoutMs = timeoutMs;
|
|
61
|
+
}
|
|
55
62
|
await pw.clickViaPlaywright(clickRequest);
|
|
56
63
|
return res.json({ ok: true, targetId: tab.targetId, url: tab.url });
|
|
57
64
|
}
|
|
58
65
|
case "type": {
|
|
59
66
|
const ref = toStringOrEmpty(body.ref);
|
|
60
|
-
if (!ref)
|
|
67
|
+
if (!ref) {
|
|
61
68
|
return jsonError(res, 400, "ref is required");
|
|
62
|
-
|
|
69
|
+
}
|
|
70
|
+
if (typeof body.text !== "string") {
|
|
63
71
|
return jsonError(res, 400, "text is required");
|
|
72
|
+
}
|
|
64
73
|
const text = body.text;
|
|
65
74
|
const submit = toBoolean(body.submit) ?? false;
|
|
66
75
|
const slowly = toBoolean(body.slowly) ?? false;
|
|
@@ -73,15 +82,17 @@ export function registerBrowserAgentActRoutes(app, ctx) {
|
|
|
73
82
|
submit,
|
|
74
83
|
slowly,
|
|
75
84
|
};
|
|
76
|
-
if (timeoutMs)
|
|
85
|
+
if (timeoutMs) {
|
|
77
86
|
typeRequest.timeoutMs = timeoutMs;
|
|
87
|
+
}
|
|
78
88
|
await pw.typeViaPlaywright(typeRequest);
|
|
79
89
|
return res.json({ ok: true, targetId: tab.targetId });
|
|
80
90
|
}
|
|
81
91
|
case "press": {
|
|
82
92
|
const key = toStringOrEmpty(body.key);
|
|
83
|
-
if (!key)
|
|
93
|
+
if (!key) {
|
|
84
94
|
return jsonError(res, 400, "key is required");
|
|
95
|
+
}
|
|
85
96
|
const delayMs = toNumber(body.delayMs);
|
|
86
97
|
await pw.pressKeyViaPlaywright({
|
|
87
98
|
cdpUrl,
|
|
@@ -93,8 +104,9 @@ export function registerBrowserAgentActRoutes(app, ctx) {
|
|
|
93
104
|
}
|
|
94
105
|
case "hover": {
|
|
95
106
|
const ref = toStringOrEmpty(body.ref);
|
|
96
|
-
if (!ref)
|
|
107
|
+
if (!ref) {
|
|
97
108
|
return jsonError(res, 400, "ref is required");
|
|
109
|
+
}
|
|
98
110
|
const timeoutMs = toNumber(body.timeoutMs);
|
|
99
111
|
await pw.hoverViaPlaywright({
|
|
100
112
|
cdpUrl,
|
|
@@ -106,24 +118,27 @@ export function registerBrowserAgentActRoutes(app, ctx) {
|
|
|
106
118
|
}
|
|
107
119
|
case "scrollIntoView": {
|
|
108
120
|
const ref = toStringOrEmpty(body.ref);
|
|
109
|
-
if (!ref)
|
|
121
|
+
if (!ref) {
|
|
110
122
|
return jsonError(res, 400, "ref is required");
|
|
123
|
+
}
|
|
111
124
|
const timeoutMs = toNumber(body.timeoutMs);
|
|
112
125
|
const scrollRequest = {
|
|
113
126
|
cdpUrl,
|
|
114
127
|
targetId: tab.targetId,
|
|
115
128
|
ref,
|
|
116
129
|
};
|
|
117
|
-
if (timeoutMs)
|
|
130
|
+
if (timeoutMs) {
|
|
118
131
|
scrollRequest.timeoutMs = timeoutMs;
|
|
132
|
+
}
|
|
119
133
|
await pw.scrollIntoViewViaPlaywright(scrollRequest);
|
|
120
134
|
return res.json({ ok: true, targetId: tab.targetId });
|
|
121
135
|
}
|
|
122
136
|
case "drag": {
|
|
123
137
|
const startRef = toStringOrEmpty(body.startRef);
|
|
124
138
|
const endRef = toStringOrEmpty(body.endRef);
|
|
125
|
-
if (!startRef || !endRef)
|
|
139
|
+
if (!startRef || !endRef) {
|
|
126
140
|
return jsonError(res, 400, "startRef and endRef are required");
|
|
141
|
+
}
|
|
127
142
|
const timeoutMs = toNumber(body.timeoutMs);
|
|
128
143
|
await pw.dragViaPlaywright({
|
|
129
144
|
cdpUrl,
|
|
@@ -137,8 +152,9 @@ export function registerBrowserAgentActRoutes(app, ctx) {
|
|
|
137
152
|
case "select": {
|
|
138
153
|
const ref = toStringOrEmpty(body.ref);
|
|
139
154
|
const values = toStringArray(body.values);
|
|
140
|
-
if (!ref || !values?.length)
|
|
155
|
+
if (!ref || !values?.length) {
|
|
141
156
|
return jsonError(res, 400, "ref and values are required");
|
|
157
|
+
}
|
|
142
158
|
const timeoutMs = toNumber(body.timeoutMs);
|
|
143
159
|
await pw.selectOptionViaPlaywright({
|
|
144
160
|
cdpUrl,
|
|
@@ -153,13 +169,15 @@ export function registerBrowserAgentActRoutes(app, ctx) {
|
|
|
153
169
|
const rawFields = Array.isArray(body.fields) ? body.fields : [];
|
|
154
170
|
const fields = rawFields
|
|
155
171
|
.map((field) => {
|
|
156
|
-
if (!field || typeof field !== "object")
|
|
172
|
+
if (!field || typeof field !== "object") {
|
|
157
173
|
return null;
|
|
174
|
+
}
|
|
158
175
|
const rec = field;
|
|
159
176
|
const ref = toStringOrEmpty(rec.ref);
|
|
160
177
|
const type = toStringOrEmpty(rec.type);
|
|
161
|
-
if (!ref || !type)
|
|
178
|
+
if (!ref || !type) {
|
|
162
179
|
return null;
|
|
180
|
+
}
|
|
163
181
|
const value = typeof rec.value === "string" ||
|
|
164
182
|
typeof rec.value === "number" ||
|
|
165
183
|
typeof rec.value === "boolean"
|
|
@@ -169,8 +187,9 @@ export function registerBrowserAgentActRoutes(app, ctx) {
|
|
|
169
187
|
return parsed;
|
|
170
188
|
})
|
|
171
189
|
.filter((field) => field !== null);
|
|
172
|
-
if (!fields.length)
|
|
190
|
+
if (!fields.length) {
|
|
173
191
|
return jsonError(res, 400, "fields are required");
|
|
192
|
+
}
|
|
174
193
|
const timeoutMs = toNumber(body.timeoutMs);
|
|
175
194
|
await pw.fillFormViaPlaywright({
|
|
176
195
|
cdpUrl,
|
|
@@ -183,8 +202,9 @@ export function registerBrowserAgentActRoutes(app, ctx) {
|
|
|
183
202
|
case "resize": {
|
|
184
203
|
const width = toNumber(body.width);
|
|
185
204
|
const height = toNumber(body.height);
|
|
186
|
-
if (!width || !height)
|
|
205
|
+
if (!width || !height) {
|
|
187
206
|
return jsonError(res, 400, "width and height are required");
|
|
207
|
+
}
|
|
188
208
|
await pw.resizeViewportViaPlaywright({
|
|
189
209
|
cdpUrl,
|
|
190
210
|
targetId: tab.targetId,
|
|
@@ -244,15 +264,22 @@ export function registerBrowserAgentActRoutes(app, ctx) {
|
|
|
244
264
|
].join("\n"));
|
|
245
265
|
}
|
|
246
266
|
const fn = toStringOrEmpty(body.fn);
|
|
247
|
-
if (!fn)
|
|
267
|
+
if (!fn) {
|
|
248
268
|
return jsonError(res, 400, "fn is required");
|
|
269
|
+
}
|
|
249
270
|
const ref = toStringOrEmpty(body.ref) || undefined;
|
|
250
|
-
const
|
|
271
|
+
const evalTimeoutMs = toNumber(body.timeoutMs);
|
|
272
|
+
const evalRequest = {
|
|
251
273
|
cdpUrl,
|
|
252
274
|
targetId: tab.targetId,
|
|
253
275
|
fn,
|
|
254
276
|
ref,
|
|
255
|
-
|
|
277
|
+
signal: req.signal,
|
|
278
|
+
};
|
|
279
|
+
if (evalTimeoutMs !== undefined) {
|
|
280
|
+
evalRequest.timeoutMs = evalTimeoutMs;
|
|
281
|
+
}
|
|
282
|
+
const result = await pw.evaluateViaPlaywright(evalRequest);
|
|
256
283
|
return res.json({
|
|
257
284
|
ok: true,
|
|
258
285
|
targetId: tab.targetId,
|
|
@@ -275,8 +302,9 @@ export function registerBrowserAgentActRoutes(app, ctx) {
|
|
|
275
302
|
});
|
|
276
303
|
app.post("/hooks/file-chooser", async (req, res) => {
|
|
277
304
|
const profileCtx = resolveProfileContext(req, res, ctx);
|
|
278
|
-
if (!profileCtx)
|
|
305
|
+
if (!profileCtx) {
|
|
279
306
|
return;
|
|
307
|
+
}
|
|
280
308
|
const body = readBody(req);
|
|
281
309
|
const targetId = toStringOrEmpty(body.targetId) || undefined;
|
|
282
310
|
const ref = toStringOrEmpty(body.ref) || undefined;
|
|
@@ -284,13 +312,15 @@ export function registerBrowserAgentActRoutes(app, ctx) {
|
|
|
284
312
|
const element = toStringOrEmpty(body.element) || undefined;
|
|
285
313
|
const paths = toStringArray(body.paths) ?? [];
|
|
286
314
|
const timeoutMs = toNumber(body.timeoutMs);
|
|
287
|
-
if (!paths.length)
|
|
315
|
+
if (!paths.length) {
|
|
288
316
|
return jsonError(res, 400, "paths are required");
|
|
317
|
+
}
|
|
289
318
|
try {
|
|
290
319
|
const tab = await profileCtx.ensureTabAvailable(targetId);
|
|
291
320
|
const pw = await requirePwAi(res, "file chooser hook");
|
|
292
|
-
if (!pw)
|
|
321
|
+
if (!pw) {
|
|
293
322
|
return;
|
|
323
|
+
}
|
|
294
324
|
if (inputRef || element) {
|
|
295
325
|
if (ref) {
|
|
296
326
|
return jsonError(res, 400, "ref cannot be combined with inputRef/element");
|
|
@@ -326,20 +356,23 @@ export function registerBrowserAgentActRoutes(app, ctx) {
|
|
|
326
356
|
});
|
|
327
357
|
app.post("/hooks/dialog", async (req, res) => {
|
|
328
358
|
const profileCtx = resolveProfileContext(req, res, ctx);
|
|
329
|
-
if (!profileCtx)
|
|
359
|
+
if (!profileCtx) {
|
|
330
360
|
return;
|
|
361
|
+
}
|
|
331
362
|
const body = readBody(req);
|
|
332
363
|
const targetId = toStringOrEmpty(body.targetId) || undefined;
|
|
333
364
|
const accept = toBoolean(body.accept);
|
|
334
365
|
const promptText = toStringOrEmpty(body.promptText) || undefined;
|
|
335
366
|
const timeoutMs = toNumber(body.timeoutMs);
|
|
336
|
-
if (accept === undefined)
|
|
367
|
+
if (accept === undefined) {
|
|
337
368
|
return jsonError(res, 400, "accept is required");
|
|
369
|
+
}
|
|
338
370
|
try {
|
|
339
371
|
const tab = await profileCtx.ensureTabAvailable(targetId);
|
|
340
372
|
const pw = await requirePwAi(res, "dialog hook");
|
|
341
|
-
if (!pw)
|
|
373
|
+
if (!pw) {
|
|
342
374
|
return;
|
|
375
|
+
}
|
|
343
376
|
await pw.armDialogViaPlaywright({
|
|
344
377
|
cdpUrl: profileCtx.profile.cdpUrl,
|
|
345
378
|
targetId: tab.targetId,
|
|
@@ -355,8 +388,9 @@ export function registerBrowserAgentActRoutes(app, ctx) {
|
|
|
355
388
|
});
|
|
356
389
|
app.post("/wait/download", async (req, res) => {
|
|
357
390
|
const profileCtx = resolveProfileContext(req, res, ctx);
|
|
358
|
-
if (!profileCtx)
|
|
391
|
+
if (!profileCtx) {
|
|
359
392
|
return;
|
|
393
|
+
}
|
|
360
394
|
const body = readBody(req);
|
|
361
395
|
const targetId = toStringOrEmpty(body.targetId) || undefined;
|
|
362
396
|
const out = toStringOrEmpty(body.path) || undefined;
|
|
@@ -364,8 +398,9 @@ export function registerBrowserAgentActRoutes(app, ctx) {
|
|
|
364
398
|
try {
|
|
365
399
|
const tab = await profileCtx.ensureTabAvailable(targetId);
|
|
366
400
|
const pw = await requirePwAi(res, "wait for download");
|
|
367
|
-
if (!pw)
|
|
401
|
+
if (!pw) {
|
|
368
402
|
return;
|
|
403
|
+
}
|
|
369
404
|
const result = await pw.waitForDownloadViaPlaywright({
|
|
370
405
|
cdpUrl: profileCtx.profile.cdpUrl,
|
|
371
406
|
targetId: tab.targetId,
|
|
@@ -380,22 +415,26 @@ export function registerBrowserAgentActRoutes(app, ctx) {
|
|
|
380
415
|
});
|
|
381
416
|
app.post("/download", async (req, res) => {
|
|
382
417
|
const profileCtx = resolveProfileContext(req, res, ctx);
|
|
383
|
-
if (!profileCtx)
|
|
418
|
+
if (!profileCtx) {
|
|
384
419
|
return;
|
|
420
|
+
}
|
|
385
421
|
const body = readBody(req);
|
|
386
422
|
const targetId = toStringOrEmpty(body.targetId) || undefined;
|
|
387
423
|
const ref = toStringOrEmpty(body.ref);
|
|
388
424
|
const out = toStringOrEmpty(body.path);
|
|
389
425
|
const timeoutMs = toNumber(body.timeoutMs);
|
|
390
|
-
if (!ref)
|
|
426
|
+
if (!ref) {
|
|
391
427
|
return jsonError(res, 400, "ref is required");
|
|
392
|
-
|
|
428
|
+
}
|
|
429
|
+
if (!out) {
|
|
393
430
|
return jsonError(res, 400, "path is required");
|
|
431
|
+
}
|
|
394
432
|
try {
|
|
395
433
|
const tab = await profileCtx.ensureTabAvailable(targetId);
|
|
396
434
|
const pw = await requirePwAi(res, "download");
|
|
397
|
-
if (!pw)
|
|
435
|
+
if (!pw) {
|
|
398
436
|
return;
|
|
437
|
+
}
|
|
399
438
|
const result = await pw.downloadViaPlaywright({
|
|
400
439
|
cdpUrl: profileCtx.profile.cdpUrl,
|
|
401
440
|
targetId: tab.targetId,
|
|
@@ -411,20 +450,23 @@ export function registerBrowserAgentActRoutes(app, ctx) {
|
|
|
411
450
|
});
|
|
412
451
|
app.post("/response/body", async (req, res) => {
|
|
413
452
|
const profileCtx = resolveProfileContext(req, res, ctx);
|
|
414
|
-
if (!profileCtx)
|
|
453
|
+
if (!profileCtx) {
|
|
415
454
|
return;
|
|
455
|
+
}
|
|
416
456
|
const body = readBody(req);
|
|
417
457
|
const targetId = toStringOrEmpty(body.targetId) || undefined;
|
|
418
458
|
const url = toStringOrEmpty(body.url);
|
|
419
459
|
const timeoutMs = toNumber(body.timeoutMs);
|
|
420
460
|
const maxChars = toNumber(body.maxChars);
|
|
421
|
-
if (!url)
|
|
461
|
+
if (!url) {
|
|
422
462
|
return jsonError(res, 400, "url is required");
|
|
463
|
+
}
|
|
423
464
|
try {
|
|
424
465
|
const tab = await profileCtx.ensureTabAvailable(targetId);
|
|
425
466
|
const pw = await requirePwAi(res, "response body");
|
|
426
|
-
if (!pw)
|
|
467
|
+
if (!pw) {
|
|
427
468
|
return;
|
|
469
|
+
}
|
|
428
470
|
const result = await pw.responseBodyViaPlaywright({
|
|
429
471
|
cdpUrl: profileCtx.profile.cdpUrl,
|
|
430
472
|
targetId: tab.targetId,
|
|
@@ -440,18 +482,21 @@ export function registerBrowserAgentActRoutes(app, ctx) {
|
|
|
440
482
|
});
|
|
441
483
|
app.post("/highlight", async (req, res) => {
|
|
442
484
|
const profileCtx = resolveProfileContext(req, res, ctx);
|
|
443
|
-
if (!profileCtx)
|
|
485
|
+
if (!profileCtx) {
|
|
444
486
|
return;
|
|
487
|
+
}
|
|
445
488
|
const body = readBody(req);
|
|
446
489
|
const targetId = toStringOrEmpty(body.targetId) || undefined;
|
|
447
490
|
const ref = toStringOrEmpty(body.ref);
|
|
448
|
-
if (!ref)
|
|
491
|
+
if (!ref) {
|
|
449
492
|
return jsonError(res, 400, "ref is required");
|
|
493
|
+
}
|
|
450
494
|
try {
|
|
451
495
|
const tab = await profileCtx.ensureTabAvailable(targetId);
|
|
452
496
|
const pw = await requirePwAi(res, "highlight");
|
|
453
|
-
if (!pw)
|
|
497
|
+
if (!pw) {
|
|
454
498
|
return;
|
|
499
|
+
}
|
|
455
500
|
await pw.highlightViaPlaywright({
|
|
456
501
|
cdpUrl: profileCtx.profile.cdpUrl,
|
|
457
502
|
targetId: tab.targetId,
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { registerBrowserRoutes } from "./index.js";
|
|
2
|
-
|
|
3
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4
|
-
}
|
|
2
|
+
import { escapeRegExp } from "../../utils.js";
|
|
5
3
|
function compileRoute(path) {
|
|
6
4
|
const paramNames = [];
|
|
7
5
|
const parts = path.split("/").map((part) => {
|
|
@@ -10,7 +8,7 @@ function compileRoute(path) {
|
|
|
10
8
|
paramNames.push(name);
|
|
11
9
|
return "([^/]+)";
|
|
12
10
|
}
|
|
13
|
-
return
|
|
11
|
+
return escapeRegExp(part);
|
|
14
12
|
});
|
|
15
13
|
return { regex: new RegExp(`^${parts.join("/")}$`), paramNames };
|
|
16
14
|
}
|
|
@@ -41,6 +39,7 @@ export function createBrowserRouteDispatcher(ctx) {
|
|
|
41
39
|
const path = normalizePath(req.path);
|
|
42
40
|
const query = req.query ?? {};
|
|
43
41
|
const body = req.body;
|
|
42
|
+
const signal = req.signal;
|
|
44
43
|
const match = registry.routes.find((route) => {
|
|
45
44
|
if (route.method !== method)
|
|
46
45
|
return false;
|
|
@@ -75,6 +74,7 @@ export function createBrowserRouteDispatcher(ctx) {
|
|
|
75
74
|
params,
|
|
76
75
|
query,
|
|
77
76
|
body,
|
|
77
|
+
signal,
|
|
78
78
|
}, res);
|
|
79
79
|
}
|
|
80
80
|
catch (err) {
|
|
@@ -16,7 +16,7 @@ export async function normalizeBrowserScreenshot(buffer, opts) {
|
|
|
16
16
|
const sideGrid = [sideStart, 1800, 1600, 1400, 1200, 1000, 800]
|
|
17
17
|
.map((v) => Math.min(maxSide, v))
|
|
18
18
|
.filter((v, i, arr) => v > 0 && arr.indexOf(v) === i)
|
|
19
|
-
.
|
|
19
|
+
.toSorted((a, b) => b - a);
|
|
20
20
|
let smallest = null;
|
|
21
21
|
for (const side of sideGrid) {
|
|
22
22
|
for (const quality of qualities) {
|
package/dist/browser/server.js
CHANGED
|
@@ -17,6 +17,19 @@ export async function startBrowserControlServerFromConfig() {
|
|
|
17
17
|
return null;
|
|
18
18
|
const app = express();
|
|
19
19
|
app.use(express.json({ limit: "1mb" }));
|
|
20
|
+
app.use((req, res, next) => {
|
|
21
|
+
const ctrl = new AbortController();
|
|
22
|
+
const abort = () => ctrl.abort(new Error("request aborted"));
|
|
23
|
+
req.once("aborted", abort);
|
|
24
|
+
res.once("close", () => {
|
|
25
|
+
if (!res.writableEnded) {
|
|
26
|
+
abort();
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
// Make the signal available to browser route handlers (best-effort).
|
|
30
|
+
req.signal = ctrl.signal;
|
|
31
|
+
next();
|
|
32
|
+
});
|
|
20
33
|
const ctx = createBrowserRouteContext({
|
|
21
34
|
getState: () => state,
|
|
22
35
|
});
|
package/dist/build-info.json
CHANGED