mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
feat(browser): add evaluate timeout CLI option (#83696)
Summary: - The branch adds `openclaw browser evaluate --timeout-ms`, forwards it to the evaluate body and request timeo ... ents and tests it, adds a changelog entry, and includes a config.patch no-op shortcut from the repair pass. - Reproducibility: not applicable. this is a feature PR rather than a bug report. Source inspection shows current main lacks the CLI flag while the branch wires it into an already-supported evaluate `timeoutMs` payload. Automerge notes: - PR branch already contained follow-up commit before automerge: feat(browser): add evaluate timeout CLI option Validation: - ClawSweeper review passed for head0d81d3d93e. - Required merge gates passed before the squash merge. Prepared head SHA:0d81d3d93eReview: https://github.com/openclaw/openclaw/pull/83696#issuecomment-4479900502 Co-authored-by: fred <fengruifree@gmail.com> Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com> Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com> Approved-by: takhoffman Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
This commit is contained in:
@@ -18,6 +18,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Skills: add a meme-maker skill for curated template search, local SVG/PNG rendering, Imgflip hosted rendering, and Know Your Meme provenance links.
|
||||
- Skills CLI: allow `openclaw skills install` and `openclaw skills update` to target shared managed skills with `--global`. (#74466) Thanks @Marvae.
|
||||
- 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.
|
||||
- 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.
|
||||
- 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.
|
||||
- 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.
|
||||
- Skills: add node inspector debugging, fused diagram generation, and throwaway spike workflow skills.
|
||||
|
||||
@@ -191,8 +191,12 @@ openclaw browser select <ref> OptionA OptionB
|
||||
openclaw browser fill --fields '[{"ref":"1","value":"Ada"}]'
|
||||
openclaw browser wait --text "Done"
|
||||
openclaw browser evaluate --fn '(el) => el.textContent' --ref <ref>
|
||||
openclaw browser evaluate --timeout-ms 30000 --fn 'async () => { await window.ready; return true; }'
|
||||
```
|
||||
|
||||
Use `evaluate --timeout-ms <ms>` when the page-side function may need longer
|
||||
than the default evaluate timeout.
|
||||
|
||||
Action responses return the current raw `targetId` after action-triggered page
|
||||
replacement when OpenClaw can prove the replacement tab. Scripts should still
|
||||
store and pass `suggestedTargetId`/labels for long-lived workflows.
|
||||
|
||||
@@ -197,6 +197,7 @@ openclaw browser dialog --dismiss --dialog-id d1
|
||||
openclaw browser wait --text "Done"
|
||||
openclaw browser wait "#main" --url "**/dash" --load networkidle --fn "window.ready===true"
|
||||
openclaw browser evaluate --fn '(el) => el.textContent' --ref 7
|
||||
openclaw browser evaluate --timeout-ms 30000 --fn 'async () => { await window.ready; return true; }'
|
||||
openclaw browser highlight e12
|
||||
openclaw browser trace start
|
||||
openclaw browser trace stop
|
||||
@@ -362,6 +363,8 @@ These are useful for "make the site behave like X" workflows:
|
||||
- `browser act kind=evaluate` / `openclaw browser evaluate` and `wait --fn`
|
||||
execute arbitrary JavaScript in the page context. Prompt injection can steer
|
||||
this. Disable it with `browser.evaluateEnabled=false` if you do not need it.
|
||||
- Use `openclaw browser evaluate --timeout-ms <ms>` when the page-side function
|
||||
may need longer than the default evaluate timeout.
|
||||
- For logins and anti-bot notes (X/Twitter, etc.), see [Browser login + X/Twitter posting](/tools/browser-login).
|
||||
- Keep the Gateway/node host private (loopback or tailnet-only).
|
||||
- Remote CDP endpoints are powerful; tunnel and protect them.
|
||||
|
||||
@@ -65,3 +65,28 @@ describe("browser action input wait command", () => {
|
||||
expect(options?.timeoutMs).toBeGreaterThan(21000);
|
||||
});
|
||||
});
|
||||
|
||||
describe("browser action input evaluate command", () => {
|
||||
beforeEach(() => {
|
||||
mocks.callBrowserRequest.mockClear();
|
||||
getBrowserCliRuntimeCapture().resetRuntimeCapture();
|
||||
});
|
||||
|
||||
it("passes timeout-ms through to the evaluate action and outer request", async () => {
|
||||
const program = createActionInputProgram();
|
||||
|
||||
await program.parseAsync(
|
||||
["browser", "evaluate", "--fn", "() => true", "--timeout-ms", "30000"],
|
||||
{ from: "user" },
|
||||
);
|
||||
|
||||
const request = mocks.callBrowserRequest.mock.calls.at(-1)?.[1] as
|
||||
| { body?: { timeoutMs?: number } }
|
||||
| undefined;
|
||||
const options = mocks.callBrowserRequest.mock.calls.at(-1)?.[2] as
|
||||
| { timeoutMs?: number }
|
||||
| undefined;
|
||||
expect(request?.body?.timeoutMs).toBe(30000);
|
||||
expect(options?.timeoutMs).toBeGreaterThan(30000);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -107,6 +107,11 @@ export function registerBrowserFormWaitEvalCommands(
|
||||
.description("Evaluate a function against the page or a ref")
|
||||
.option("--fn <code>", "Function source, e.g. (el) => el.textContent")
|
||||
.option("--ref <id>", "Ref from snapshot")
|
||||
.option(
|
||||
"--timeout-ms <ms>",
|
||||
"How long to allow the evaluate function to run (default: 20000)",
|
||||
(v: string) => Number(v),
|
||||
)
|
||||
.option("--target-id <id>", "CDP target id (or unique prefix)")
|
||||
.action(async (opts, cmd) => {
|
||||
const { parent, profile } = resolveBrowserActionContext(cmd, parentOpts);
|
||||
@@ -116,6 +121,7 @@ export function registerBrowserFormWaitEvalCommands(
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const timeoutMs = Number.isFinite(opts.timeoutMs) ? opts.timeoutMs : undefined;
|
||||
const result = await callBrowserAct<{ result?: unknown }>({
|
||||
parent,
|
||||
profile,
|
||||
@@ -124,7 +130,9 @@ export function registerBrowserFormWaitEvalCommands(
|
||||
fn: opts.fn,
|
||||
ref: normalizeOptionalString(opts.ref),
|
||||
targetId: normalizeOptionalString(opts.targetId),
|
||||
timeoutMs,
|
||||
},
|
||||
timeoutMs,
|
||||
});
|
||||
if (parent?.json) {
|
||||
defaultRuntime.writeJson(result);
|
||||
|
||||
@@ -433,6 +433,24 @@ export const configHandlers: GatewayRequestHandlers = {
|
||||
);
|
||||
return;
|
||||
}
|
||||
const restoredChangedPaths = diffConfigPaths(snapshot.config, restoredMerge.result);
|
||||
const actor = resolveControlPlaneActor(client);
|
||||
if (restoredChangedPaths.length === 0) {
|
||||
context?.logGateway?.info(
|
||||
`config.patch noop ${formatControlPlaneActor(actor)} (no changed paths)`,
|
||||
);
|
||||
respond(
|
||||
true,
|
||||
{
|
||||
ok: true,
|
||||
noop: true,
|
||||
path: resolveGatewayConfigPath(snapshot),
|
||||
config: redactConfigObject(snapshot.config, schemaPatch.uiHints),
|
||||
},
|
||||
undefined,
|
||||
);
|
||||
return;
|
||||
}
|
||||
const validated = validateConfigObjectWithPlugins(restoredMerge.result);
|
||||
if (!validated.ok) {
|
||||
respond(
|
||||
@@ -452,7 +470,6 @@ export const configHandlers: GatewayRequestHandlers = {
|
||||
return;
|
||||
}
|
||||
const changedPaths = diffConfigPaths(snapshot.config, validated.config);
|
||||
const actor = resolveControlPlaneActor(client);
|
||||
|
||||
// No-op: if the validated config is identical to the current config,
|
||||
// skip the file write and SIGUSR1 restart entirely. This avoids a full
|
||||
|
||||
Reference in New Issue
Block a user