






















@@ -4,8 +4,8 @@ import {
44normalizeOptionalString,
55} from "openclaw/plugin-sdk/string-coerce-runtime";
66import type { Page } from "playwright-core";
7-import { ACT_MAX_VIEWPORT_DIMENSION } from "./act-policy.js";
87import type { SsrFPolicy } from "../infra/net/ssrf.js";
8+import { ACT_MAX_VIEWPORT_DIMENSION } from "./act-policy.js";
99import { type AriaSnapshotNode, formatAriaSnapshot, type RawAXNode } from "./cdp.js";
1010import {
1111assertBrowserNavigationAllowed,
@@ -30,11 +30,7 @@ import {
3030storeRoleRefsForTarget,
3131} from "./pw-session.js";
3232import { markBackendDomRefsOnPage, withPageScopedCdpClient } from "./pw-session.page-cdp.js";
33-34-type SnapshotUrlEntry = {
35-text: string;
36-url: string;
37-};
33+import { appendSnapshotUrls, type SnapshotUrlEntry } from "./snapshot-urls.js";
38343935function resolveBoundedTimeoutMs(
4036timeoutMs: number | undefined,
@@ -89,14 +85,6 @@ async function collectSnapshotUrls(page: Page): Promise<SnapshotUrlEntry[]> {
8985return Array.isArray(urls) ? urls : [];
9086}
918792-function appendSnapshotUrls(snapshot: string, urls: SnapshotUrlEntry[]): string {
93-if (urls.length === 0) {
94-return snapshot;
95-}
96-const lines = urls.map((entry, index) => `${index + 1}. ${entry.text} -> ${entry.url}`);
97-return `${snapshot}\n\nLinks:\n${lines.join("\n")}`;
98-}
99-10088function buildStoredAriaRefs(
10189nodes: AriaSnapshotNode[],
10290markedRefs: Set<string>,
@@ -164,14 +152,11 @@ export async function storeAriaSnapshotRefsViaPlaywright(opts: {
164152});
165153}
166154167-export async function snapshotAriaViaPlaywright(opts: {
155+async function prepareSnapshotPageViaPlaywright(opts: {
168156cdpUrl: string;
169157targetId?: string;
170-limit?: number;
171-timeoutMs?: number;
172158ssrfPolicy?: SsrFPolicy;
173-}): Promise<{ nodes: AriaSnapshotNode[] }> {
174-const limit = resolveIntegerOption(opts.limit, 500, { min: 1, max: 2000 });
159+}): Promise<Page> {
175160const page = await getPageForTargetId({
176161cdpUrl: opts.cdpUrl,
177162targetId: opts.targetId,
@@ -186,6 +171,22 @@ export async function snapshotAriaViaPlaywright(opts: {
186171targetId: opts.targetId,
187172});
188173}
174+return page;
175+}
176+177+export async function snapshotAriaViaPlaywright(opts: {
178+cdpUrl: string;
179+targetId?: string;
180+limit?: number;
181+timeoutMs?: number;
182+ssrfPolicy?: SsrFPolicy;
183+}): Promise<{ nodes: AriaSnapshotNode[] }> {
184+const limit = resolveIntegerOption(opts.limit, 500, { min: 1, max: 2000 });
185+const page = await prepareSnapshotPageViaPlaywright({
186+cdpUrl: opts.cdpUrl,
187+targetId: opts.targetId,
188+ssrfPolicy: opts.ssrfPolicy,
189+});
189190const ariaTimeoutMs =
190191typeof opts.timeoutMs === "number" && Number.isFinite(opts.timeoutMs) && opts.timeoutMs > 0
191192 ? Math.max(500, Math.min(60_000, Math.floor(opts.timeoutMs)))
@@ -238,20 +239,11 @@ export async function snapshotAiViaPlaywright(opts: {
238239urls?: boolean;
239240ssrfPolicy?: SsrFPolicy;
240241}): Promise<{ snapshot: string; truncated?: boolean; refs: RoleRefMap }> {
241-const page = await getPageForTargetId({
242+const page = await prepareSnapshotPageViaPlaywright({
242243cdpUrl: opts.cdpUrl,
243244targetId: opts.targetId,
245+ssrfPolicy: opts.ssrfPolicy,
244246});
245-ensurePageState(page);
246-if (opts.ssrfPolicy) {
247-await assertPageNavigationCompletedSafely({
248-cdpUrl: opts.cdpUrl,
249- page,
250-response: null,
251-ssrfPolicy: opts.ssrfPolicy,
252-targetId: opts.targetId,
253-});
254-}
255247256248let snapshot = await page.ariaSnapshot({
257249mode: "ai",
@@ -282,6 +274,37 @@ export async function snapshotAiViaPlaywright(opts: {
282274return truncated ? { snapshot, truncated, refs: built.refs } : { snapshot, refs: built.refs };
283275}
284276277+async function finalizeRoleSnapshotViaPlaywright(params: {
278+page: Page;
279+cdpUrl: string;
280+targetId?: string;
281+frameSelector?: string;
282+mode: "aria" | "role";
283+built: { snapshot: string; refs: RoleRefMap };
284+urls?: boolean;
285+}): Promise<{
286+snapshot: string;
287+refs: RoleRefMap;
288+stats: { lines: number; chars: number; refs: number; interactive: number };
289+}> {
290+const snapshot = params.urls
291+ ? appendSnapshotUrls(params.built.snapshot, await collectSnapshotUrls(params.page))
292+ : params.built.snapshot;
293+storeRoleRefsForTarget({
294+page: params.page,
295+cdpUrl: params.cdpUrl,
296+targetId: params.targetId,
297+refs: params.built.refs,
298+ ...(params.frameSelector ? { frameSelector: params.frameSelector } : {}),
299+mode: params.mode,
300+});
301+return {
302+ snapshot,
303+refs: params.built.refs,
304+stats: getRoleSnapshotStats(snapshot, params.built.refs),
305+};
306+}
307+285308export async function snapshotRoleViaPlaywright(opts: {
286309cdpUrl: string;
287310targetId?: string;
@@ -297,20 +320,11 @@ export async function snapshotRoleViaPlaywright(opts: {
297320refs: Record<string, { role: string; name?: string; nth?: number }>;
298321stats: { lines: number; chars: number; refs: number; interactive: number };
299322}> {
300-const page = await getPageForTargetId({
323+const page = await prepareSnapshotPageViaPlaywright({
301324cdpUrl: opts.cdpUrl,
302325targetId: opts.targetId,
326+ssrfPolicy: opts.ssrfPolicy,
303327});
304-ensurePageState(page);
305-if (opts.ssrfPolicy) {
306-await assertPageNavigationCompletedSafely({
307-cdpUrl: opts.cdpUrl,
308- page,
309-response: null,
310-ssrfPolicy: opts.ssrfPolicy,
311-targetId: opts.targetId,
312-});
313-}
314328315329const ariaSnapshotTimeout = resolveSnapshotTimeoutMs(opts.timeoutMs);
316330@@ -323,21 +337,14 @@ export async function snapshotRoleViaPlaywright(opts: {
323337timeout: ariaSnapshotTimeout,
324338});
325339const built = buildRoleSnapshotFromAiSnapshot(snapshot, opts.options);
326-const snapshotWithUrls = opts.urls
327- ? appendSnapshotUrls(built.snapshot, await collectSnapshotUrls(page))
328- : built.snapshot;
329-storeRoleRefsForTarget({
340+return await finalizeRoleSnapshotViaPlaywright({
330341 page,
331342cdpUrl: opts.cdpUrl,
332343targetId: opts.targetId,
333-refs: built.refs,
344+ built,
334345mode: "aria",
346+urls: opts.urls,
335347});
336-return {
337-snapshot: snapshotWithUrls,
338-refs: built.refs,
339-stats: getRoleSnapshotStats(snapshotWithUrls, built.refs),
340-};
341348}
342349343350const frameSelector = normalizeOptionalString(opts.frameSelector) ?? "";
@@ -352,22 +359,15 @@ export async function snapshotRoleViaPlaywright(opts: {
352359353360const ariaSnapshot = await locator.ariaSnapshot({ timeout: ariaSnapshotTimeout });
354361const built = buildRoleSnapshotFromAriaSnapshot(ariaSnapshot ?? "", opts.options);
355-const snapshotWithUrls = opts.urls
356- ? appendSnapshotUrls(built.snapshot, await collectSnapshotUrls(page))
357- : built.snapshot;
358-storeRoleRefsForTarget({
362+return await finalizeRoleSnapshotViaPlaywright({
359363 page,
360364cdpUrl: opts.cdpUrl,
361365targetId: opts.targetId,
362-refs: built.refs,
363366frameSelector: frameSelector || undefined,
367+ built,
364368mode: "role",
369+urls: opts.urls,
365370});
366-return {
367-snapshot: snapshotWithUrls,
368-refs: built.refs,
369-stats: getRoleSnapshotStats(snapshotWithUrls, built.refs),
370-};
371371}
372372373373export async function navigateViaPlaywright(opts: {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。