feat(browser): add evaluate timeout CLI option (#83696) · openclaw/openclaw@fa814eb
clawsweeper
·
2026-05-19
·
via Recent Commits to openclaw:main
| Original file line number | Diff line number | Diff line change |
|---|
@@ -18,6 +18,7 @@ Docs: https://docs.openclaw.ai
|
18 | 18 | - Skills: add a meme-maker skill for curated template search, local SVG/PNG rendering, Imgflip hosted rendering, and Know Your Meme provenance links. |
19 | 19 | - Skills CLI: allow `openclaw skills install` and `openclaw skills update` to target shared managed skills with `--global`. (#74466) Thanks @Marvae. |
20 | 20 | - Browser: surface pending and recently handled modal dialogs in snapshots, return `blockedByDialog` when an action opens a modal, and allow `browser dialog --dialog-id` to answer pending dialogs. |
| 21 | +- Browser CLI: add `openclaw browser evaluate --timeout-ms` so long-running page functions can extend both the evaluate action and request timeout budgets. (#83447) Thanks @eefreenyc. |
21 | 22 | - Codex app-server: scope OpenClaw prompt guidance by runtime surface so native Codex keeps Codex-owned base/personality instructions while OpenClaw contributes only runtime context, delivery guidance, and explicitly scoped command hints. (#83454) Thanks @100yenadmin. |
22 | 23 | - Agents/tools: shorten built-in tool descriptions and schema hints across media, messaging, sessions, cron, Gateway, web, image/PDF, TTS, nodes, and plan tools while preserving routing guardrails. |
23 | 24 | - Skills: add node inspector debugging, fused diagram generation, and throwaway spike workflow skills. |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -191,8 +191,12 @@ openclaw browser select <ref> OptionA OptionB
|
191 | 191 | openclaw browser fill --fields '[{"ref":"1","value":"Ada"}]' |
192 | 192 | openclaw browser wait --text "Done" |
193 | 193 | openclaw browser evaluate --fn '(el) => el.textContent' --ref <ref> |
| 194 | +openclaw browser evaluate --timeout-ms 30000 --fn 'async () => { await window.ready; return true; }' |
194 | 195 | ``` |
195 | 196 | |
| 197 | +Use `evaluate --timeout-ms <ms>` when the page-side function may need longer |
| 198 | +than the default evaluate timeout. |
| 199 | + |
196 | 200 | Action responses return the current raw `targetId` after action-triggered page |
197 | 201 | replacement when OpenClaw can prove the replacement tab. Scripts should still |
198 | 202 | store and pass `suggestedTargetId`/labels for long-lived workflows. |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -197,6 +197,7 @@ openclaw browser dialog --dismiss --dialog-id d1
|
197 | 197 | openclaw browser wait --text "Done" |
198 | 198 | openclaw browser wait "#main" --url "**/dash" --load networkidle --fn "window.ready===true" |
199 | 199 | openclaw browser evaluate --fn '(el) => el.textContent' --ref 7 |
| 200 | +openclaw browser evaluate --timeout-ms 30000 --fn 'async () => { await window.ready; return true; }' |
200 | 201 | openclaw browser highlight e12 |
201 | 202 | openclaw browser trace start |
202 | 203 | openclaw browser trace stop |
@@ -362,6 +363,8 @@ These are useful for "make the site behave like X" workflows:
|
362 | 363 | - `browser act kind=evaluate` / `openclaw browser evaluate` and `wait --fn` |
363 | 364 | execute arbitrary JavaScript in the page context. Prompt injection can steer |
364 | 365 | this. Disable it with `browser.evaluateEnabled=false` if you do not need it. |
| 366 | +- Use `openclaw browser evaluate --timeout-ms <ms>` when the page-side function |
| 367 | + may need longer than the default evaluate timeout. |
365 | 368 | - For logins and anti-bot notes (X/Twitter, etc.), see [Browser login + X/Twitter posting](/tools/browser-login). |
366 | 369 | - Keep the Gateway/node host private (loopback or tailnet-only). |
367 | 370 | - Remote CDP endpoints are powerful; tunnel and protect them. |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -65,3 +65,28 @@ describe("browser action input wait command", () => {
|
65 | 65 | expect(options?.timeoutMs).toBeGreaterThan(21000); |
66 | 66 | }); |
67 | 67 | }); |
| 68 | + |
| 69 | +describe("browser action input evaluate command", () => { |
| 70 | +beforeEach(() => { |
| 71 | +mocks.callBrowserRequest.mockClear(); |
| 72 | +getBrowserCliRuntimeCapture().resetRuntimeCapture(); |
| 73 | +}); |
| 74 | + |
| 75 | +it("passes timeout-ms through to the evaluate action and outer request", async () => { |
| 76 | +const program = createActionInputProgram(); |
| 77 | + |
| 78 | +await program.parseAsync( |
| 79 | +["browser", "evaluate", "--fn", "() => true", "--timeout-ms", "30000"], |
| 80 | +{ from: "user" }, |
| 81 | +); |
| 82 | + |
| 83 | +const request = mocks.callBrowserRequest.mock.calls.at(-1)?.[1] as |
| 84 | +| { body?: { timeoutMs?: number } } |
| 85 | +| undefined; |
| 86 | +const options = mocks.callBrowserRequest.mock.calls.at(-1)?.[2] as |
| 87 | +| { timeoutMs?: number } |
| 88 | +| undefined; |
| 89 | +expect(request?.body?.timeoutMs).toBe(30000); |
| 90 | +expect(options?.timeoutMs).toBeGreaterThan(30000); |
| 91 | +}); |
| 92 | +}); |
| Original file line number | Diff line number | Diff line change |
|---|
@@ -107,6 +107,11 @@ export function registerBrowserFormWaitEvalCommands(
|
107 | 107 | .description("Evaluate a function against the page or a ref") |
108 | 108 | .option("--fn <code>", "Function source, e.g. (el) => el.textContent") |
109 | 109 | .option("--ref <id>", "Ref from snapshot") |
| 110 | +.option( |
| 111 | +"--timeout-ms <ms>", |
| 112 | +"How long to allow the evaluate function to run (default: 20000)", |
| 113 | +(v: string) => Number(v), |
| 114 | +) |
110 | 115 | .option("--target-id <id>", "CDP target id (or unique prefix)") |
111 | 116 | .action(async (opts, cmd) => { |
112 | 117 | const { parent, profile } = resolveBrowserActionContext(cmd, parentOpts); |
@@ -116,6 +121,7 @@ export function registerBrowserFormWaitEvalCommands(
|
116 | 121 | return; |
117 | 122 | } |
118 | 123 | try { |
| 124 | +const timeoutMs = Number.isFinite(opts.timeoutMs) ? opts.timeoutMs : undefined; |
119 | 125 | const result = await callBrowserAct<{ result?: unknown }>({ |
120 | 126 | parent, |
121 | 127 | profile, |
@@ -124,7 +130,9 @@ export function registerBrowserFormWaitEvalCommands(
|
124 | 130 | fn: opts.fn, |
125 | 131 | ref: normalizeOptionalString(opts.ref), |
126 | 132 | targetId: normalizeOptionalString(opts.targetId), |
| 133 | + timeoutMs, |
127 | 134 | }, |
| 135 | + timeoutMs, |
128 | 136 | }); |
129 | 137 | if (parent?.json) { |
130 | 138 | defaultRuntime.writeJson(result); |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -433,6 +433,24 @@ export const configHandlers: GatewayRequestHandlers = {
|
433 | 433 | ); |
434 | 434 | return; |
435 | 435 | } |
| 436 | +const restoredChangedPaths = diffConfigPaths(snapshot.config, restoredMerge.result); |
| 437 | +const actor = resolveControlPlaneActor(client); |
| 438 | +if (restoredChangedPaths.length === 0) { |
| 439 | +context?.logGateway?.info( |
| 440 | +`config.patch noop ${formatControlPlaneActor(actor)} (no changed paths)`, |
| 441 | +); |
| 442 | +respond( |
| 443 | +true, |
| 444 | +{ |
| 445 | +ok: true, |
| 446 | +noop: true, |
| 447 | +path: resolveGatewayConfigPath(snapshot), |
| 448 | +config: redactConfigObject(snapshot.config, schemaPatch.uiHints), |
| 449 | +}, |
| 450 | +undefined, |
| 451 | +); |
| 452 | +return; |
| 453 | +} |
436 | 454 | const validated = validateConfigObjectWithPlugins(restoredMerge.result); |
437 | 455 | if (!validated.ok) { |
438 | 456 | respond( |
@@ -452,7 +470,6 @@ export const configHandlers: GatewayRequestHandlers = {
|
452 | 470 | return; |
453 | 471 | } |
454 | 472 | const changedPaths = diffConfigPaths(snapshot.config, validated.config); |
455 | | -const actor = resolveControlPlaneActor(client); |
456 | 473 | |
457 | 474 | // No-op: if the validated config is identical to the current config, |
458 | 475 | // skip the file write and SIGUSR1 restart entirely. This avoids a full |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。