mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-03 15:04:09 +00:00
fix(browser): centralize snapshot numeric parsing
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { ResolvedBrowserProfile } from "../config.js";
|
||||
import { DEFAULT_AI_SNAPSHOT_MAX_CHARS } from "../constants.js";
|
||||
import { resolveSnapshotPlan } from "./agent.snapshot.plan.js";
|
||||
|
||||
function profile(driver: "existing-session" | "openclaw"): ResolvedBrowserProfile {
|
||||
@@ -68,6 +69,70 @@ describe("resolveSnapshotPlan", () => {
|
||||
expect(plan.timeoutMs).toBe(2_147_483_647);
|
||||
});
|
||||
|
||||
it("parses snapshot numeric query options as strict integers", () => {
|
||||
const plan = resolveSnapshotPlan({
|
||||
profile: profile("openclaw"),
|
||||
query: {
|
||||
limit: "25",
|
||||
maxChars: "5000",
|
||||
depth: "2",
|
||||
timeoutMs: "12345",
|
||||
},
|
||||
hasPlaywright: true,
|
||||
});
|
||||
|
||||
expect(plan.limit).toBe(25);
|
||||
expect(plan.resolvedMaxChars).toBe(5000);
|
||||
expect(plan.depth).toBe(2);
|
||||
expect(plan.timeoutMs).toBe(12345);
|
||||
});
|
||||
|
||||
it("accepts structured numeric snapshot query options from proxy dispatch", () => {
|
||||
const plan = resolveSnapshotPlan({
|
||||
profile: profile("openclaw"),
|
||||
query: {
|
||||
limit: 25,
|
||||
maxChars: 5000,
|
||||
depth: 2,
|
||||
timeoutMs: 12345,
|
||||
},
|
||||
hasPlaywright: true,
|
||||
});
|
||||
|
||||
expect(plan.limit).toBe(25);
|
||||
expect(plan.resolvedMaxChars).toBe(5000);
|
||||
expect(plan.depth).toBe(2);
|
||||
expect(plan.timeoutMs).toBe(12345);
|
||||
});
|
||||
|
||||
it("rejects loose snapshot numeric query tokens", () => {
|
||||
const plan = resolveSnapshotPlan({
|
||||
profile: profile("openclaw"),
|
||||
query: {
|
||||
limit: "0x10",
|
||||
maxChars: "1.5",
|
||||
depth: "1e0",
|
||||
timeoutMs: "1000ms",
|
||||
},
|
||||
hasPlaywright: true,
|
||||
});
|
||||
|
||||
expect(plan.limit).toBeUndefined();
|
||||
expect(plan.resolvedMaxChars).toBe(DEFAULT_AI_SNAPSHOT_MAX_CHARS);
|
||||
expect(plan.depth).toBeUndefined();
|
||||
expect(plan.timeoutMs).toBeUndefined();
|
||||
});
|
||||
|
||||
it("keeps maxChars zero as an explicit uncapped snapshot request", () => {
|
||||
const plan = resolveSnapshotPlan({
|
||||
profile: profile("openclaw"),
|
||||
query: { maxChars: "0" },
|
||||
hasPlaywright: true,
|
||||
});
|
||||
|
||||
expect(plan.resolvedMaxChars).toBeUndefined();
|
||||
});
|
||||
|
||||
it("ignores non-positive timeoutMs values", () => {
|
||||
expect(
|
||||
resolveSnapshotPlan({
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import {
|
||||
normalizeOptionalString,
|
||||
readStringValue,
|
||||
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||
parseStrictNonNegativeInteger,
|
||||
parseStrictPositiveInteger,
|
||||
} from "openclaw/plugin-sdk/number-runtime";
|
||||
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||
import type { ResolvedBrowserProfile } from "../config.js";
|
||||
import {
|
||||
DEFAULT_AI_SNAPSHOT_EFFICIENT_DEPTH,
|
||||
@@ -14,7 +15,7 @@ import {
|
||||
shouldUsePlaywrightForScreenshot,
|
||||
} from "../profile-capabilities.js";
|
||||
import { normalizeBrowserTimerDelayMs } from "../timer-delay.js";
|
||||
import { toBoolean, toNumber, toStringOrEmpty } from "./utils.js";
|
||||
import { toBoolean, toStringOrEmpty } from "./utils.js";
|
||||
|
||||
type BrowserSnapshotPlan = {
|
||||
format: "ai" | "aria";
|
||||
@@ -49,25 +50,25 @@ export function resolveSnapshotPlan(params: {
|
||||
explicitFormat,
|
||||
mode,
|
||||
});
|
||||
const limitRaw = readStringValue(params.query.limit);
|
||||
const limit = parseStrictPositiveInteger(params.query.limit);
|
||||
const hasMaxChars = Object.hasOwn(params.query, "maxChars");
|
||||
const maxCharsRaw = readStringValue(params.query.maxChars);
|
||||
const limit = Number.isFinite(Number(limitRaw)) ? Number(limitRaw) : undefined;
|
||||
const maxChars =
|
||||
Number.isFinite(Number(maxCharsRaw)) && Number(maxCharsRaw) > 0
|
||||
? Math.floor(Number(maxCharsRaw))
|
||||
: undefined;
|
||||
const maxCharsRaw = parseStrictNonNegativeInteger(params.query.maxChars);
|
||||
const maxChars = maxCharsRaw !== undefined && maxCharsRaw > 0 ? maxCharsRaw : undefined;
|
||||
const resolvedMaxChars =
|
||||
format === "ai"
|
||||
? hasMaxChars
|
||||
? maxChars
|
||||
? maxCharsRaw === undefined
|
||||
? mode === "efficient"
|
||||
? DEFAULT_AI_SNAPSHOT_EFFICIENT_MAX_CHARS
|
||||
: DEFAULT_AI_SNAPSHOT_MAX_CHARS
|
||||
: maxChars
|
||||
: mode === "efficient"
|
||||
? DEFAULT_AI_SNAPSHOT_EFFICIENT_MAX_CHARS
|
||||
: DEFAULT_AI_SNAPSHOT_MAX_CHARS
|
||||
: undefined;
|
||||
const interactiveRaw = toBoolean(params.query.interactive);
|
||||
const compactRaw = toBoolean(params.query.compact);
|
||||
const depthRaw = toNumber(params.query.depth);
|
||||
const depthRaw = parseStrictNonNegativeInteger(params.query.depth);
|
||||
const refsModeRaw = toStringOrEmpty(params.query.refs).trim();
|
||||
const refsMode: "aria" | "role" | undefined =
|
||||
refsModeRaw === "aria" ? "aria" : refsModeRaw === "role" ? "role" : undefined;
|
||||
@@ -77,11 +78,9 @@ export function resolveSnapshotPlan(params: {
|
||||
depthRaw ?? (mode === "efficient" ? DEFAULT_AI_SNAPSHOT_EFFICIENT_DEPTH : undefined);
|
||||
const selectorValue = normalizeOptionalString(toStringOrEmpty(params.query.selector));
|
||||
const frameSelectorValue = normalizeOptionalString(toStringOrEmpty(params.query.frame));
|
||||
const timeoutMsRaw = toNumber(params.query.timeoutMs);
|
||||
const timeoutMsRaw = parseStrictPositiveInteger(params.query.timeoutMs);
|
||||
const timeoutMs =
|
||||
timeoutMsRaw !== undefined && Number.isFinite(timeoutMsRaw) && timeoutMsRaw > 0
|
||||
? normalizeBrowserTimerDelayMs(timeoutMsRaw)
|
||||
: undefined;
|
||||
timeoutMsRaw !== undefined ? normalizeBrowserTimerDelayMs(timeoutMsRaw) : undefined;
|
||||
|
||||
return {
|
||||
format,
|
||||
|
||||
Reference in New Issue
Block a user