aws-runtime-bridge 1.9.98 → 1.9.101
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/dist/adapter/AcodeSdkAdapter.d.ts +7 -1
- package/dist/adapter/AcodeSdkAdapter.js +5 -5
- package/dist/adapter/ClaudeSdkAdapter.d.ts +4 -0
- package/dist/adapter/ClaudeSdkAdapter.js +2 -2
- package/dist/adapter/CodexSdkAdapter.d.ts +2 -0
- package/dist/adapter/CodexSdkAdapter.js +3 -3
- package/dist/adapter/OpencodeSdkAdapter.d.ts +2 -0
- package/dist/adapter/OpencodeSdkAdapter.js +2 -2
- package/dist/adapter/idle-poll-loop.d.ts +4 -1
- package/dist/adapter/idle-poll-loop.js +1 -1
- package/dist/adapter/types.d.ts +4 -2
- package/dist/browser/browser-bootstrap.d.ts +11 -0
- package/dist/browser/browser-bootstrap.js +4 -0
- package/dist/browser/browser-connect-token.d.ts +10 -0
- package/dist/browser/browser-connect-token.js +2 -0
- package/dist/browser/browser-pool.d.ts +75 -0
- package/dist/browser/browser-pool.js +2 -0
- package/dist/browser/browser-profile-store.d.ts +28 -0
- package/dist/browser/browser-profile-store.js +2 -0
- package/dist/browser/browser-ws-bridge.d.ts +12 -0
- package/dist/browser/browser-ws-bridge.js +6 -0
- package/dist/browser/built-in-browser-tools.d.ts +27 -0
- package/dist/browser/built-in-browser-tools.js +2 -0
- package/dist/browser/element-locator.d.ts +24 -0
- package/dist/browser/element-locator.js +2 -0
- package/dist/browser/element-picker.d.ts +17 -0
- package/dist/browser/element-picker.js +2 -0
- package/dist/browser/input-dispatcher.d.ts +34 -0
- package/dist/browser/input-dispatcher.js +2 -0
- package/dist/browser/listener-store.d.ts +32 -0
- package/dist/browser/listener-store.js +2 -0
- package/dist/browser/page-listener-manager.d.ts +106 -0
- package/dist/browser/page-listener-manager.js +307 -0
- package/dist/browser/page-listener-types.d.ts +90 -0
- package/dist/browser/page-listener-types.js +1 -0
- package/dist/browser/screencast-manager.d.ts +42 -0
- package/dist/browser/screencast-manager.js +2 -0
- package/dist/browser/snapshot-serializer.d.ts +15 -0
- package/dist/browser/snapshot-serializer.js +5 -0
- package/dist/browser/ssrf-guard.d.ts +15 -0
- package/dist/browser/ssrf-guard.js +2 -0
- package/dist/browser/stealth.d.ts +7 -0
- package/dist/browser/stealth.js +173 -0
- package/dist/browser/step-archive.d.ts +52 -0
- package/dist/browser/step-archive.js +2 -0
- package/dist/browser/types.d.ts +192 -0
- package/dist/browser/types.js +2 -0
- package/dist/browser/ws-server.d.ts +67 -0
- package/dist/browser/ws-server.js +2 -0
- package/dist/index.js +1 -1
- package/dist/middleware/auth.d.ts +2 -0
- package/dist/middleware/auth.js +1 -1
- package/dist/remote-desktop/computer-control.d.ts +30 -0
- package/dist/remote-desktop/computer-control.js +2 -0
- package/dist/remote-desktop/coords.d.ts +13 -0
- package/dist/remote-desktop/coords.js +2 -0
- package/dist/remote-desktop/frame-backpressure.d.ts +7 -0
- package/dist/remote-desktop/frame-backpressure.js +2 -0
- package/dist/remote-desktop/index.d.ts +7 -0
- package/dist/remote-desktop/index.js +4 -0
- package/dist/remote-desktop/input-backends/interface.d.ts +17 -0
- package/dist/remote-desktop/input-backends/interface.js +1 -0
- package/dist/remote-desktop/input-backends/key-map.d.ts +2 -0
- package/dist/remote-desktop/input-backends/key-map.js +2 -0
- package/dist/remote-desktop/input-dispatcher.d.ts +11 -0
- package/dist/remote-desktop/input-dispatcher.js +2 -0
- package/dist/remote-desktop/permission-manager.d.ts +7 -0
- package/dist/remote-desktop/permission-manager.js +2 -0
- package/dist/remote-desktop/screen-capture.d.ts +27 -0
- package/dist/remote-desktop/screen-capture.js +2 -0
- package/dist/remote-desktop/session-manager.d.ts +59 -0
- package/dist/remote-desktop/session-manager.js +2 -0
- package/dist/remote-desktop/system-backend.d.ts +65 -0
- package/dist/remote-desktop/system-backend.js +5 -0
- package/dist/remote-desktop/types.d.ts +68 -0
- package/dist/remote-desktop/types.js +1 -0
- package/dist/remote-desktop/ui-automation.d.ts +48 -0
- package/dist/remote-desktop/ui-automation.js +31 -0
- package/dist/remote-desktop/win32-api.d.ts +29 -0
- package/dist/remote-desktop/win32-api.js +2 -0
- package/dist/routes/background-tasks.d.ts +2 -0
- package/dist/routes/background-tasks.js +2 -0
- package/dist/routes/browser-listeners.d.ts +2 -0
- package/dist/routes/browser-listeners.js +2 -0
- package/dist/routes/browser-sessions.d.ts +2 -0
- package/dist/routes/browser-sessions.js +2 -0
- package/dist/routes/pty.js +3 -3
- package/dist/routes/terminal.js +30 -30
- package/dist/services/agent-pty-manager.d.ts +22 -0
- package/dist/services/agent-pty-manager.js +5 -0
- package/dist/services/browser-session-manager.d.ts +55 -0
- package/dist/services/browser-session-manager.js +12 -0
- package/dist/services/cli-commands.d.ts +4 -0
- package/dist/services/cli-commands.js +5 -5
- package/package/acode/dist/await-complete-tool.d.ts +17 -0
- package/package/acode/dist/await-complete-tool.js +21 -0
- package/package/acode/dist/background-command-manager.d.ts +148 -0
- package/package/acode/dist/background-command-manager.js +8 -0
- package/package/acode/dist/background-command-store.d.ts +46 -0
- package/package/acode/dist/background-command-store.js +2 -0
- package/package/acode/dist/background-command-tools.d.ts +5 -0
- package/package/acode/dist/background-command-tools.js +18 -0
- package/package/acode/dist/background-output-ringbuffer.d.ts +23 -0
- package/package/acode/dist/background-output-ringbuffer.js +3 -0
- package/package/acode/dist/built-in-file-tools.d.ts +8 -1
- package/package/acode/dist/built-in-file-tools.js +2 -2
- package/package/acode/dist/compaction.js +6 -6
- package/package/acode/dist/index.d.ts +1 -0
- package/package/acode/dist/index.js +1 -1
- package/package/acode/dist/runtime.d.ts +6 -0
- package/package/acode/dist/runtime.js +27 -27
- package/package/acode/dist/sub-agent-tools.d.ts +0 -1
- package/package/acode/dist/sub-agent-tools.js +11 -28
- package/package/acode/dist/terminal-tool.d.ts +7 -0
- package/package/acode/dist/terminal-tool.js +2 -0
- package/package/acode/dist/terminal-types.d.ts +38 -0
- package/package/acode/dist/terminal-types.js +1 -0
- package/package/acode/dist/types.d.ts +10 -1
- package/package/aws-client-agent-mcp/dist/agent-client.d.ts +3 -1
- package/package/aws-client-agent-mcp/dist/agent-client.js +1 -1
- package/package/aws-client-agent-mcp/dist/logger.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/logger.js +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +8 -0
- package/package/aws-client-agent-mcp/dist/mcp-server.js +30 -19
- package/package/aws-client-agent-mcp/dist/types.d.ts +25 -0
- package/package.json +6 -2
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
|
|
2
|
+
import type { Page } from "playwright";
|
|
3
|
+
import type { ListenerChangeType, ListenerId, ListenerTriggerEvent, PageListener, PageListenerInfo, PageListenerManagerConfig } from "./page-listener-types.js";
|
|
4
|
+
export declare function matchPath(target: string, pattern: string): boolean;
|
|
5
|
+
|
|
6
|
+
export declare function extractPathname(url: string): string;
|
|
7
|
+
|
|
8
|
+
export declare function derivePathPatternFromUrl(page: Page | null): string;
|
|
9
|
+
|
|
10
|
+
interface DetectNodeLike {
|
|
11
|
+
nodeType: number;
|
|
12
|
+
}
|
|
13
|
+
interface DetectMutationLike {
|
|
14
|
+
type: string;
|
|
15
|
+
addedNodes?: ArrayLike<DetectNodeLike> | null;
|
|
16
|
+
removedNodes?: ArrayLike<DetectNodeLike> | null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export declare function nodesContainElement(nodes: ArrayLike<DetectNodeLike> | null | undefined): boolean;
|
|
20
|
+
|
|
21
|
+
export declare function detectChangeTypes(mutations: ArrayLike<DetectMutationLike> | null | undefined): ListenerChangeType[];
|
|
22
|
+
|
|
23
|
+
export declare function detectChangeType(mutations: ArrayLike<DetectMutationLike> | null | undefined): ListenerChangeType;
|
|
24
|
+
|
|
25
|
+
export interface AddListenerParams {
|
|
26
|
+
selector: string;
|
|
27
|
+
pathPattern: string;
|
|
28
|
+
prompt: string;
|
|
29
|
+
enabled?: boolean;
|
|
30
|
+
|
|
31
|
+
changeTypes?: ListenerChangeType[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface UpdateListenerParams {
|
|
35
|
+
listenerId: ListenerId;
|
|
36
|
+
selector?: string;
|
|
37
|
+
pathPattern?: string;
|
|
38
|
+
prompt?: string;
|
|
39
|
+
enabled?: boolean;
|
|
40
|
+
|
|
41
|
+
changeTypes?: ListenerChangeType[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export declare class PageListenerManager {
|
|
45
|
+
private readonly agentId;
|
|
46
|
+
private readonly browserId;
|
|
47
|
+
private readonly config;
|
|
48
|
+
private readonly listeners;
|
|
49
|
+
private readonly eventQueue;
|
|
50
|
+
private readonly triggerMeta;
|
|
51
|
+
|
|
52
|
+
private readonly pendingChangeTypes;
|
|
53
|
+
|
|
54
|
+
private readonly existsLatched;
|
|
55
|
+
|
|
56
|
+
private readonly attachedPages;
|
|
57
|
+
|
|
58
|
+
private readonly navHandledPages;
|
|
59
|
+
private currentPage;
|
|
60
|
+
constructor(agentId: string, browserId: string, config?: PageListenerManagerConfig);
|
|
61
|
+
|
|
62
|
+
getCurrentPage(): Page | null;
|
|
63
|
+
|
|
64
|
+
loadFromStore(): Promise<number>;
|
|
65
|
+
|
|
66
|
+
attach(page: Page): Promise<void>;
|
|
67
|
+
|
|
68
|
+
reattach(page: Page): Promise<void>;
|
|
69
|
+
|
|
70
|
+
addListener(params: AddListenerParams): Promise<PageListener>;
|
|
71
|
+
|
|
72
|
+
updateListener(params: UpdateListenerParams): Promise<PageListener | undefined>;
|
|
73
|
+
|
|
74
|
+
removeListener(id: ListenerId): Promise<boolean>;
|
|
75
|
+
|
|
76
|
+
toggleListener(id: ListenerId, enabled?: boolean): Promise<PageListener | undefined>;
|
|
77
|
+
|
|
78
|
+
listListeners(): Promise<PageListenerInfo[]>;
|
|
79
|
+
|
|
80
|
+
hasPendingEvents(): boolean;
|
|
81
|
+
|
|
82
|
+
enqueueEvent(event: ListenerTriggerEvent): void;
|
|
83
|
+
|
|
84
|
+
dequeueEvent(): ListenerTriggerEvent | undefined;
|
|
85
|
+
|
|
86
|
+
collectEvents(): ListenerTriggerEvent[];
|
|
87
|
+
|
|
88
|
+
scanExistsAtPoll(): Promise<ListenerTriggerEvent[]>;
|
|
89
|
+
|
|
90
|
+
dispose(): Promise<void>;
|
|
91
|
+
|
|
92
|
+
setHighlightEnabled(enabled: boolean): Promise<void>;
|
|
93
|
+
|
|
94
|
+
private ensureExposed;
|
|
95
|
+
|
|
96
|
+
private attachNavigationHandler;
|
|
97
|
+
|
|
98
|
+
private handleDomChange;
|
|
99
|
+
|
|
100
|
+
private installOnPage;
|
|
101
|
+
|
|
102
|
+
private uninstallOnPage;
|
|
103
|
+
|
|
104
|
+
private elementExists;
|
|
105
|
+
}
|
|
106
|
+
export {};
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import{randomUUID as T}from"node:crypto";const w=500,_=200,P=300,E="__bridgeNotifyDomChange";function u(n){if(!n)return"";try{const e=new URL(n);return e.origin+e.pathname}catch{return n}}function y(n,e){let t=n,r=e;e.includes("://")?(t=u(n),r=u(e)):t=L(n)||n;let i="^";for(let s=0;s<r.length;s++){const a=r[s];if(a===":")if(r[s+1]==="/")i+="\\:";else if(r[s+1]!==void 0&&/[0-9]/.test(r[s+1]))i+="\\:";else{for(;s+1<r.length&&/[a-zA-Z0-9_]/.test(r[s+1]);)s++;i+="[^/]+"}else a==="*"?r[s+1]==="*"?(i+=".*",s++):i+="[^/]*":a==="?"?i+=".":".+^${}()|[]\\".includes(a)?i+="\\"+a:i+=a}i+="$";try{return new RegExp(i).test(t)}catch{return!1}}function L(n){if(!n)return"";try{return new URL(n).pathname}catch{return""}}function S(n){if(!n)return"";try{return u(n.url())}catch{return""}}function b(n,e){const t=n.trim();if(!t)return S(e);if(t.includes("://"))return t;if(e)try{const r=new URL(e.url()).origin;if(r)return r+(t.startsWith("/")?"":"/")+t}catch{}return t}const v=["added","removed","changed"];function x(n){if(!Array.isArray(n)||n.length===0)return[...v];const e=new Set;for(const t of n)typeof t=="string"&&(t==="added"||t==="removed"||t==="changed"||t==="exists")&&e.add(t);return e.size>0?[...e]:[...v]}function f(n){if(!n||n.length===0)return!1;for(let e=0;e<n.length;e++){const t=n[e];if(t&&t.nodeType===1)return!0}return!1}function m(n){const e=new Set;if(!n)return e.add("changed"),["changed"];for(let r=0;r<n.length;r++){const i=n[r];if(i.type==="childList"){const s=f(i.addedNodes),a=f(i.removedNodes);s&&e.add("added"),a&&e.add("removed"),!s&&!a&&(i.addedNodes&&i.addedNodes.length>0||i.removedNodes&&i.removedNodes.length>0)&&e.add("changed")}else i.type==="characterData"&&e.add("changed")}if(e.size===0)return["changed"];const t=[];return e.has("added")&&t.push("added"),e.has("removed")&&t.push("removed"),e.has("changed")&&t.push("changed"),t}function I(n){return m(n)[0]??"changed"}function H(n,e,t){return`
|
|
2
|
+
(function() {
|
|
3
|
+
var w = window;
|
|
4
|
+
var DEBOUNCE_MS = ${n};
|
|
5
|
+
var MAX_TEXT = ${e};
|
|
6
|
+
var HIGHLIGHT_ENABLED = ${t?"true":"false"};
|
|
7
|
+
var POLL_INTERVAL = 500;
|
|
8
|
+
var POLL_TIMEOUT = 30000;
|
|
9
|
+
// \u6BCF\u6B21 navigation \u540E\u91CD\u7F6E\uFF08init script \u91CD\u65B0\u6267\u884C\uFF09
|
|
10
|
+
w.__bridgeListeners = {};
|
|
11
|
+
w.__bridgeHighlightEnabled = HIGHLIGHT_ENABLED;
|
|
12
|
+
w.__bridgeHighlightedEls = new Set();
|
|
13
|
+
var overlayLayer = null;
|
|
14
|
+
var boxMap = new Map();
|
|
15
|
+
var badgeRafId = null;
|
|
16
|
+
var highlightStyleReady = false;
|
|
17
|
+
function normalizeUrlForMatch(url) {
|
|
18
|
+
if (!url) return '';
|
|
19
|
+
try { var u = new URL(url); return u.origin + u.pathname; } catch (e) { return url; }
|
|
20
|
+
}
|
|
21
|
+
function matchPath(target, pattern) {
|
|
22
|
+
var targetStr = target;
|
|
23
|
+
var pat = pattern;
|
|
24
|
+
if (pattern.indexOf('://') >= 0) {
|
|
25
|
+
targetStr = normalizeUrlForMatch(target);
|
|
26
|
+
pat = normalizeUrlForMatch(pattern);
|
|
27
|
+
} else {
|
|
28
|
+
try { targetStr = new URL(target).pathname; } catch (e) { targetStr = target; }
|
|
29
|
+
}
|
|
30
|
+
var regexStr = '^';
|
|
31
|
+
for (var i = 0; i < pat.length; i++) {
|
|
32
|
+
var ch = pat[i];
|
|
33
|
+
if (ch === ':') {
|
|
34
|
+
if (pat[i+1] === '/') { regexStr += '\\:'; }
|
|
35
|
+
else if (pat[i+1] !== undefined && /[0-9]/.test(pat[i+1])) { regexStr += '\\:'; }
|
|
36
|
+
else { while (i+1 < pat.length && /[a-zA-Z0-9_]/.test(pat[i+1])) { i++; } regexStr += '[^/]+'; }
|
|
37
|
+
} else if (ch === '*') {
|
|
38
|
+
if (pat[i+1] === '*') { regexStr += '.*'; i++; } else { regexStr += '[^/]*'; }
|
|
39
|
+
} else if (ch === '?') {
|
|
40
|
+
regexStr += '.';
|
|
41
|
+
} else if ('.+^\${}()|[]\\\\'.indexOf(ch) >= 0) {
|
|
42
|
+
regexStr += '\\\\' + ch;
|
|
43
|
+
} else {
|
|
44
|
+
regexStr += ch;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
regexStr += '$';
|
|
48
|
+
try { return new RegExp(regexStr).test(targetStr); } catch (e) { return false; }
|
|
49
|
+
}
|
|
50
|
+
// \u6CE8\u5165\uFF08\u590D\u7528\uFF09\u6A21\u5757\u7EA7\u7EAF\u51FD\u6570\uFF1A\u8282\u70B9/\u53D8\u5316\u7C7B\u578B\u68C0\u6D4B\uFF08\u4E0E Node \u4FA7\u5355\u6D4B\u540C\u4E00\u4EFD\u5B9E\u73B0\uFF0C\u4FDD\u8BC1\u6D4F\u89C8\u5668\u884C\u4E3A\u4E00\u81F4\uFF09\u3002
|
|
51
|
+
${f.toString()}
|
|
52
|
+
${m.toString()}
|
|
53
|
+
// \u5143\u7D20\u300C\u5B58\u5728\u300D\u76D1\u542C\uFF1A\u8FB9\u6CBF\u89E6\u53D1\uFF08\u4E0D\u5B58\u5728 \u2192 \u5B58\u5728 \u65F6\u901A\u77E5\u4E00\u6B21\uFF1B\u6D88\u5931\u540E\u518D\u6B21\u51FA\u73B0\u518D\u901A\u77E5\uFF09\u3002
|
|
54
|
+
// \u4E0E added/removed/changed \u4E0D\u540C\uFF0Cexists \u662F\u72B6\u6001\u578B\u76D1\u542C\uFF0C\u7531\u8F6E\u8BE2\u68C0\u6D4B\u800C\u975E MutationObserver\u3002
|
|
55
|
+
function checkPresenceFor(listenerId, entry) {
|
|
56
|
+
if (!entry || !entry.subscribedExists) return;
|
|
57
|
+
if (!matchPath(location.href, entry.pathPattern)) return;
|
|
58
|
+
var present;
|
|
59
|
+
try { present = !!document.querySelector(entry.selector); } catch (e) { present = false; }
|
|
60
|
+
if (present && !entry.lastPresence) {
|
|
61
|
+
// \u5143\u7D20\u4ECE\u4E0D\u5B58\u5728\u53D8\u4E3A\u5B58\u5728\uFF1A\u89E6\u53D1 exists \u4E8B\u4EF6\uFF08\u8FB9\u6CBF\u89E6\u53D1\uFF0C\u907F\u514D\u968F\u8F6E\u8BE2\u5237\u5C4F\uFF09
|
|
62
|
+
var text = '';
|
|
63
|
+
try {
|
|
64
|
+
var el = document.querySelector(entry.selector);
|
|
65
|
+
text = (el && el.textContent ? el.textContent : '').slice(0, MAX_TEXT);
|
|
66
|
+
} catch (e) {}
|
|
67
|
+
entry.lastPresence = true;
|
|
68
|
+
if (w.__bridgeNotifyDomChange) {
|
|
69
|
+
try { w.__bridgeNotifyDomChange(listenerId, text, 'exists'); } catch (e) {}
|
|
70
|
+
}
|
|
71
|
+
} else if (!present && entry.lastPresence) {
|
|
72
|
+
// \u5143\u7D20\u6D88\u5931\uFF1A\u590D\u4F4D\uFF0C\u7B49\u5F85\u4E0B\u6B21\u51FA\u73B0\u518D\u89E6\u53D1
|
|
73
|
+
entry.lastPresence = false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// \u2500\u2500 \u9AD8\u4EAE\uFF08\u56DE\u663E\uFF09\uFF1A\u76D1\u542C\u5668\u751F\u6548\u65F6\u5728\u76EE\u6807\u5143\u7D20\u4E0A\u53E0\u52A0\u53EF\u89C6\u6807\u6CE8\uFF08\u9752\u8272\u63CF\u8FB9\u6846\uFF09 \u2500\u2500
|
|
77
|
+
// active \u76D1\u542C\u5668 \u2192 \u5728\u72EC\u7ACB overlay \u5C42\u4E3A\u8BE5\u5143\u7D20\u753B\u4E00\u4E2A\u9752\u8272\u63CF\u8FB9\u6846\uFF08position:fixed + getBoundingClientRect \u5B9A\u4F4D\uFF09\uFF0C
|
|
78
|
+
// \u4E0D\u53D7\u76EE\u6807\u5143\u7D20 z-index / \u540C\u7EA7\u906E\u6321\u5F71\u54CD\uFF0C\u59CB\u7EC8\u4F4D\u4E8E\u6700\u4E0A\u5C42\uFF1B\u4F46\u4F1A\u8DDF\u968F\u5143\u7D20\u5728 overflow:hidden \u7B49\u88C1\u526A\u7956\u5148\u4E2D\u7684\u53EF\u89C1\u533A\u57DF\uFF0C
|
|
79
|
+
// \u5143\u7D20\u88AB\u6EDA\u51FA\u88C1\u526A\u5BB9\u5668\u65F6\u9AD8\u4EAE\u540C\u6B65\u9690\u85CF\uFF08\u5C0A\u91CD overflow \u88C1\u526A\uFF0C\u65B9\u6848 1\uFF09\u3002
|
|
80
|
+
// \u6BCF\u79D2\u7531 presence \u5B9A\u65F6\u5668 reconcile \u4E00\u6B21\uFF0C\u5E76\u5728 install/uninstall \u65F6\u7ACB\u5373 reconcile\uFF0C\u4FDD\u8BC1\u72B6\u6001\u4E0E\u9AD8\u4EAE\u4E00\u81F4\u3002
|
|
81
|
+
var HIGHLIGHT_CSS = ""
|
|
82
|
+
+ ".bridge-listener-box{position:fixed;left:0;top:0;box-sizing:border-box;border:2px solid #14b8a6;border-radius:4px;pointer-events:none;z-index:2147483646;}"
|
|
83
|
+
+ ".bridge-listener-box--added{border-color:#22c55e;}"
|
|
84
|
+
+ ".bridge-listener-box--removed{border-color:#ef4444;}"
|
|
85
|
+
+ ".bridge-listener-box--changed{border-color:#facc15;}"
|
|
86
|
+
+ ".bridge-listener-box--exists{border-color:#a855f7;}";
|
|
87
|
+
function ensureHighlightApi() {
|
|
88
|
+
if (highlightStyleReady) return;
|
|
89
|
+
var s = document.createElement('style');
|
|
90
|
+
s.id = 'bridge-listener-highlight-style';
|
|
91
|
+
s.textContent = HIGHLIGHT_CSS;
|
|
92
|
+
(document.head || document.documentElement).appendChild(s);
|
|
93
|
+
highlightStyleReady = true;
|
|
94
|
+
}
|
|
95
|
+
function ensureOverlayLayer() {
|
|
96
|
+
if (overlayLayer) return overlayLayer;
|
|
97
|
+
overlayLayer = document.createElement('div');
|
|
98
|
+
overlayLayer.id = 'bridge-listener-overlay';
|
|
99
|
+
overlayLayer.style.cssText = 'position:fixed;inset:0;pointer-events:none;z-index:2147483647;';
|
|
100
|
+
(document.body || document.documentElement).appendChild(overlayLayer);
|
|
101
|
+
return overlayLayer;
|
|
102
|
+
}
|
|
103
|
+
// \u8BA1\u7B97\u5143\u7D20\u5728\u300C\u6240\u6709 overflow \u88C1\u526A\u7956\u5148\u300D\u9650\u5236\u4E0B\u7684\u53EF\u89C1\u77E9\u5F62\uFF1A
|
|
104
|
+
// \u8BA9\u9AD8\u4EAE\u6846\u53EA\u63CF\u51FA\u771F\u6B63\u53EF\u89C1\u7684\u90E8\u5206\uFF0C\u5143\u7D20\u88AB overflow:hidden \u6EDA\u51FA\u5BB9\u5668\u65F6\u9AD8\u4EAE\u968F\u4E4B\u6D88\u5931\uFF08\u5C0A\u91CD overflow \u88C1\u526A\uFF09\u3002
|
|
105
|
+
function computeVisibleRect(el) {
|
|
106
|
+
var r = el.getBoundingClientRect();
|
|
107
|
+
var left = r.left, top = r.top, right = r.right, bottom = r.bottom;
|
|
108
|
+
var parent = el.parentElement;
|
|
109
|
+
while (parent && parent.nodeType === 1) {
|
|
110
|
+
var cs = getComputedStyle(parent);
|
|
111
|
+
var ox = cs.overflowX, oy = cs.overflowY;
|
|
112
|
+
if ((ox && ox !== 'visible') || (oy && oy !== 'visible')) {
|
|
113
|
+
var pr = parent.getBoundingClientRect();
|
|
114
|
+
var bl = parseFloat(cs.borderLeftWidth) || 0;
|
|
115
|
+
var bt = parseFloat(cs.borderTopWidth) || 0;
|
|
116
|
+
var br = parseFloat(cs.borderRightWidth) || 0;
|
|
117
|
+
var bb = parseFloat(cs.borderBottomWidth) || 0;
|
|
118
|
+
var pl = pr.left + bl, pt = pr.top + bt, prr = pr.right - br, pbb = pr.bottom - bb;
|
|
119
|
+
if (ox && ox !== 'visible') { left = Math.max(left, pl); right = Math.min(right, prr); }
|
|
120
|
+
if (oy && oy !== 'visible') { top = Math.max(top, pt); bottom = Math.min(bottom, pbb); }
|
|
121
|
+
}
|
|
122
|
+
parent = parent.parentElement;
|
|
123
|
+
}
|
|
124
|
+
return { left: left, top: top, right: right, bottom: bottom, width: right - left, height: bottom - top };
|
|
125
|
+
}
|
|
126
|
+
function startBoxRaf() {
|
|
127
|
+
if (badgeRafId) return;
|
|
128
|
+
function loop() {
|
|
129
|
+
if (boxMap.size === 0) { badgeRafId = null; return; }
|
|
130
|
+
boxMap.forEach(function(box, el) {
|
|
131
|
+
try {
|
|
132
|
+
var vr = computeVisibleRect(el);
|
|
133
|
+
if (vr.width <= 0 || vr.height <= 0) {
|
|
134
|
+
box.style.display = 'none';
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
box.style.display = '';
|
|
138
|
+
box.style.left = vr.left + 'px';
|
|
139
|
+
box.style.top = vr.top + 'px';
|
|
140
|
+
box.style.width = vr.width + 'px';
|
|
141
|
+
box.style.height = vr.height + 'px';
|
|
142
|
+
} catch (e) {}
|
|
143
|
+
});
|
|
144
|
+
badgeRafId = requestAnimationFrame(loop);
|
|
145
|
+
}
|
|
146
|
+
badgeRafId = requestAnimationFrame(loop);
|
|
147
|
+
}
|
|
148
|
+
function cleanupHighlight(el) {
|
|
149
|
+
var box = boxMap.get(el);
|
|
150
|
+
if (box) { if (box.parentNode) box.parentNode.removeChild(box); boxMap.delete(el); }
|
|
151
|
+
if (w.__bridgeHighlightedEls) w.__bridgeHighlightedEls.delete(el);
|
|
152
|
+
}
|
|
153
|
+
function applyHighlightClasses(el, ids) {
|
|
154
|
+
ensureHighlightApi();
|
|
155
|
+
var layer = ensureOverlayLayer();
|
|
156
|
+
// \u63CF\u8FB9\u6846\uFF1A\u72EC\u7ACB overlay \u5C42\uFF0C\u907F\u514D\u88AB\u76EE\u6807\u5143\u7D20 z-index / overflow:hidden / \u540C\u7EA7\u906E\u6321
|
|
157
|
+
var box = boxMap.get(el);
|
|
158
|
+
if (!box) {
|
|
159
|
+
box = document.createElement('div');
|
|
160
|
+
box.className = 'bridge-listener-box';
|
|
161
|
+
layer.appendChild(box);
|
|
162
|
+
boxMap.set(el, box);
|
|
163
|
+
}
|
|
164
|
+
// \u6309\u76D1\u542C\u7C7B\u578B\u4E0A\u8272\uFF1A\u547D\u4E2D\u8BE5\u5143\u7D20\u7684\u6240\u6709\u76D1\u542C\u5668\u8BA2\u9605\u7C7B\u578B\u5408\u5E76\u540E\u82E5\u4E3A\u5355\u4E00\u7C7B\u578B \u2192 \u5BF9\u5E94\u989C\u8272\uFF1B
|
|
165
|
+
// \u591A\u7C7B\u578B / \u6DF7\u5408 \u2192 \u4FDD\u6301\u9ED8\u8BA4\u9752\u8272\u3002\u6539\u53D8=\u9EC4 \u5220\u9664=\u7EA2 \u65B0\u589E=\u7EFF \u5B58\u5728=\u7D2B\u3002
|
|
166
|
+
var types = new Set();
|
|
167
|
+
ids.forEach(function(lid) {
|
|
168
|
+
var ent = w.__bridgeListeners[lid];
|
|
169
|
+
if (ent && ent.changeTypesList) {
|
|
170
|
+
for (var i = 0; i < ent.changeTypesList.length; i++) types.add(ent.changeTypesList[i]);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
var typeClass = '';
|
|
174
|
+
if (types.size === 1) {
|
|
175
|
+
var only = types.values().next().value;
|
|
176
|
+
if (only === 'added' || only === 'removed' || only === 'changed' || only === 'exists') {
|
|
177
|
+
typeClass = 'bridge-listener-box--' + only;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
box.className = 'bridge-listener-box' + (typeClass ? ' ' + typeClass : '');
|
|
181
|
+
w.__bridgeHighlightedEls.add(el);
|
|
182
|
+
startBoxRaf();
|
|
183
|
+
}
|
|
184
|
+
function clearAllHighlights() {
|
|
185
|
+
if (w.__bridgeHighlightedEls) {
|
|
186
|
+
var arr = Array.from(w.__bridgeHighlightedEls);
|
|
187
|
+
for (var i = 0; i < arr.length; i++) cleanupHighlight(arr[i]);
|
|
188
|
+
w.__bridgeHighlightedEls.clear();
|
|
189
|
+
}
|
|
190
|
+
boxMap.forEach(function(box) { if (box.parentNode) box.parentNode.removeChild(box); });
|
|
191
|
+
boxMap.clear();
|
|
192
|
+
}
|
|
193
|
+
function reconcileHighlights() {
|
|
194
|
+
if (w.__bridgeHighlightEnabled === false) return;
|
|
195
|
+
var activeEls = new Map();
|
|
196
|
+
for (var lid in w.__bridgeListeners) {
|
|
197
|
+
if (!w.__bridgeListeners.hasOwnProperty(lid)) continue;
|
|
198
|
+
var entry = w.__bridgeListeners[lid];
|
|
199
|
+
var el = null;
|
|
200
|
+
if (matchPath(location.href, entry.pathPattern)) {
|
|
201
|
+
try { el = document.querySelector(entry.selector); } catch (e) { el = null; }
|
|
202
|
+
}
|
|
203
|
+
if (el) {
|
|
204
|
+
if (!activeEls.has(el)) activeEls.set(el, new Set());
|
|
205
|
+
activeEls.get(el).add(lid);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (w.__bridgeHighlightedEls) {
|
|
209
|
+
w.__bridgeHighlightedEls.forEach(function(el) { if (!activeEls.has(el)) cleanupHighlight(el); });
|
|
210
|
+
}
|
|
211
|
+
activeEls.forEach(function(ids, el) { applyHighlightClasses(el, ids); });
|
|
212
|
+
}
|
|
213
|
+
function installListener(listenerId, selector, pathPattern, changeTypes) {
|
|
214
|
+
var existing = w.__bridgeListeners[listenerId];
|
|
215
|
+
if (existing) {
|
|
216
|
+
try { if (existing.observer) existing.observer.disconnect(); } catch (e) {}
|
|
217
|
+
if (existing.pollTimer) clearInterval(existing.pollTimer);
|
|
218
|
+
if (existing.debounceTimer) clearTimeout(existing.debounceTimer);
|
|
219
|
+
}
|
|
220
|
+
// \u8BA2\u9605\u7684\u53D8\u5316\u7C7B\u578B\u96C6\u5408\uFF08\u7A7A\u6570\u7EC4\u89C6\u4E3A\u5168\u90E8\uFF0C\u4E0E\u540E\u7AEF normalizeChangeTypes \u4E00\u81F4\uFF09
|
|
221
|
+
var typeSet = {};
|
|
222
|
+
var allMode = true;
|
|
223
|
+
if (changeTypes && Object.prototype.toString.call(changeTypes) === '[object Array]' && changeTypes.length > 0) {
|
|
224
|
+
allMode = false;
|
|
225
|
+
for (var k = 0; k < changeTypes.length; k++) {
|
|
226
|
+
var t = changeTypes[k];
|
|
227
|
+
if (t === 'added' || t === 'removed' || t === 'changed' || t === 'exists') typeSet[t] = true;
|
|
228
|
+
}
|
|
229
|
+
// \u82E5\u6570\u7EC4\u91CC\u6CA1\u6709\u4EFB\u4F55\u5408\u6CD5\u503C\uFF0C\u56DE\u843D\u5168\u90E8\u6A21\u5F0F
|
|
230
|
+
var cnt = 0;
|
|
231
|
+
for (var key in typeSet) { if (typeSet.hasOwnProperty(key)) cnt++; }
|
|
232
|
+
if (cnt === 0) allMode = true;
|
|
233
|
+
}
|
|
234
|
+
function isSubscribed(ct) { return allMode || typeSet.hasOwnProperty(ct); }
|
|
235
|
+
// exists \u4E3A\u72EC\u7ACB\u7684\u72B6\u6001\u578B\u76D1\u542C\uFF08\u975E MutationObserver \u53D8\u5316\u7C7B\u578B\uFF09
|
|
236
|
+
var subscribedExists = allMode || (typeSet['exists'] === true);
|
|
237
|
+
// \u7528\u4E8E\u9AD8\u4EAE\u4E0A\u8272\uFF1A\u5408\u5E76\u8BE5\u76D1\u542C\u5668\u8BA2\u9605\u7684\u5168\u90E8\u53D8\u5316\u7C7B\u578B\uFF08allMode \u89C6\u4E3A added/removed/changed/exists \u5168\u8BA2\u9605\uFF09
|
|
238
|
+
var subscribedTypes = allMode ? ['added', 'removed', 'changed', 'exists'] : Object.keys(typeSet);
|
|
239
|
+
var entry = { selector: selector, pathPattern: pathPattern, observer: null, pollTimer: null, debounceTimer: null, pendingTypes: null, subscribedExists: subscribedExists, lastPresence: null, changeTypesList: subscribedTypes };
|
|
240
|
+
w.__bridgeListeners[listenerId] = entry;
|
|
241
|
+
// exists \u4E0D\u5728\u9875\u9762\u4FA7\u89E6\u53D1\uFF08\u8FB9\u6CBF\u89E6\u53D1\u5DF2\u79FB\u9664\uFF09\uFF1Aexists \u662F\u72B6\u6001\u578B\u76D1\u542C\uFF0C
|
|
242
|
+
// \u4EC5\u7531 Node \u4FA7 scanExistsAtPoll \u5728 poll_message \u65F6\u6309\u5F53\u524D\u771F\u5B9E DOM \u8865\u67E5\u3002
|
|
243
|
+
function tryAttach() {
|
|
244
|
+
if (!matchPath(location.href, pathPattern)) return false;
|
|
245
|
+
var el = null;
|
|
246
|
+
try { el = document.querySelector(selector); } catch (e) { return false; }
|
|
247
|
+
if (!el) return false;
|
|
248
|
+
var obs = new MutationObserver(function(mutations) {
|
|
249
|
+
if (entry.debounceTimer) clearTimeout(entry.debounceTimer);
|
|
250
|
+
// \u9632\u6296\u7A97\u53E3\u5185\u7D2F\u8BA1\u300C\u51FA\u73B0\u8FC7\u7684\u6240\u6709\u53D8\u5316\u7C7B\u578B\u300D\uFF08added/removed/changed \u5404\u81EA\u72EC\u7ACB\uFF0C\u4E92\u4E0D\u8986\u76D6\uFF09
|
|
251
|
+
var batchTypes = detectChangeTypes(mutations);
|
|
252
|
+
if (!entry.pendingTypes) entry.pendingTypes = {};
|
|
253
|
+
for (var bi = 0; bi < batchTypes.length; bi++) entry.pendingTypes[batchTypes[bi]] = true;
|
|
254
|
+
entry.debounceTimer = setTimeout(function() {
|
|
255
|
+
var pts = entry.pendingTypes || {};
|
|
256
|
+
entry.pendingTypes = null;
|
|
257
|
+
var text = (el.textContent || '').slice(0, MAX_TEXT);
|
|
258
|
+
// \u6BCF\u4E2A\u547D\u4E2D\u8BA2\u9605\u96C6\u5408\u7684\u53D8\u5316\u7C7B\u578B\u90FD\u89E6\u53D1\u4E00\u6B21\uFF1A\u65B0\u589E/\u5220\u9664/\u6539\u53D8\u53EF\u540C\u65F6\u53D1\u751F\uFF0C\u4E0D\u80FD\u4E92\u76F8\u541E\u6CA1
|
|
259
|
+
// \uFF08\u4FEE\u590D Issue A\uFF1A\u65E7\u903B\u8F91\u628A add+remove \u5408\u5E76\u6210\u5355\u4E00\u7C7B\u578B\uFF0C\u5BFC\u81F4 removed-only \u6F0F\u89E6\u53D1\uFF09
|
|
260
|
+
for (var t in pts) {
|
|
261
|
+
if (!pts.hasOwnProperty(t)) continue;
|
|
262
|
+
if (!isSubscribed(t)) continue;
|
|
263
|
+
if (w.__bridgeNotifyDomChange) {
|
|
264
|
+
try { w.__bridgeNotifyDomChange(listenerId, text, t); } catch (e) {}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}, DEBOUNCE_MS);
|
|
268
|
+
});
|
|
269
|
+
try {
|
|
270
|
+
obs.observe(el, { childList: true, characterData: true, subtree: true });
|
|
271
|
+
entry.observer = obs;
|
|
272
|
+
return true;
|
|
273
|
+
} catch (e) { return false; }
|
|
274
|
+
}
|
|
275
|
+
if (tryAttach()) { reconcileHighlights(); return; }
|
|
276
|
+
var startTime = Date.now();
|
|
277
|
+
entry.pollTimer = setInterval(function() {
|
|
278
|
+
if (tryAttach()) { clearInterval(entry.pollTimer); entry.pollTimer = null; reconcileHighlights(); }
|
|
279
|
+
else if (Date.now() - startTime > POLL_TIMEOUT) { clearInterval(entry.pollTimer); entry.pollTimer = null; }
|
|
280
|
+
}, POLL_INTERVAL);
|
|
281
|
+
}
|
|
282
|
+
function uninstallListener(listenerId) {
|
|
283
|
+
var entry = w.__bridgeListeners[listenerId];
|
|
284
|
+
if (entry) {
|
|
285
|
+
try { if (entry.observer) entry.observer.disconnect(); } catch (e) {}
|
|
286
|
+
if (entry.pollTimer) clearInterval(entry.pollTimer);
|
|
287
|
+
if (entry.debounceTimer) clearTimeout(entry.debounceTimer);
|
|
288
|
+
delete w.__bridgeListeners[listenerId];
|
|
289
|
+
}
|
|
290
|
+
reconcileHighlights();
|
|
291
|
+
}
|
|
292
|
+
// \u5168\u5C40\u5B9A\u65F6\u5668\uFF1A\u6BCF 1s \u4EC5\u505A\u9AD8\u4EAE reconcile\uFF08exists \u8FB9\u6CBF\u89E6\u53D1\u5DF2\u79FB\u9664\uFF0C
|
|
293
|
+
// exists \u7531 Node \u4FA7 scanExistsAtPoll \u5728 poll_message \u65F6\u6309\u5F53\u524D DOM \u8865\u67E5\uFF09
|
|
294
|
+
if (w.__bridgePresenceTimer) { try { clearInterval(w.__bridgePresenceTimer); } catch (e) {} }
|
|
295
|
+
w.__bridgePresenceTimer = setInterval(function() {
|
|
296
|
+
if (w.__bridgeHighlightEnabled !== false) reconcileHighlights();
|
|
297
|
+
}, 1000);
|
|
298
|
+
w.__bridgeInstallListener = installListener;
|
|
299
|
+
w.__bridgeUninstallListener = uninstallListener;
|
|
300
|
+
w.__bridgeSetHighlightEnabled = function(v) {
|
|
301
|
+
w.__bridgeHighlightEnabled = !!v;
|
|
302
|
+
if (!w.__bridgeHighlightEnabled) clearAllHighlights();
|
|
303
|
+
else reconcileHighlights();
|
|
304
|
+
};
|
|
305
|
+
})();
|
|
306
|
+
`}class C{constructor(e,t,r){this.listeners=new Map,this.eventQueue=[],this.triggerMeta=new Map,this.pendingChangeTypes=new Map,this.existsLatched=new Set,this.attachedPages=new WeakSet,this.navHandledPages=new WeakSet,this.currentPage=null,this.agentId=e,this.browserId=t,this.config={debounceMs:r?.debounceMs??w,maxTextLength:r?.maxTextLength??_,reattachDelayMs:r?.reattachDelayMs??P,onTrigger:r?.onTrigger,store:r?.store??null,scopeKey:r?.scopeKey??`agent:${e}`,highlight:r?.highlight??(typeof process>"u"||process.env?.AWSB_LISTENER_HIGHLIGHT!=="false")}}getCurrentPage(){return this.currentPage}async loadFromStore(){if(!this.config.store)return 0;try{const e=await this.config.store.load(this.config.scopeKey);let t=0;for(const r of e)this.listeners.has(r.id)||(this.listeners.set(r.id,{...r}),t++);return t>0&&this.currentPage&&await this.reattach(this.currentPage).catch(()=>{}),t}catch{return 0}}async attach(e){this.currentPage=e,await this.ensureExposed(e),this.config.store&&this.listeners.size===0&&await this.loadFromStore().catch(()=>{}),await this.reattach(e),this.attachNavigationHandler(e)}async reattach(e){this.currentPage=e,await this.ensureExposed(e);const t=[];for(const r of this.listeners.values())r.enabled&&t.push(this.installOnPage(e,r).catch(()=>{}));await Promise.all(t)}async addListener(e){if(!e.selector||!e.selector.trim())throw new Error("selector is required");if(!e.prompt||!e.prompt.trim())throw new Error("prompt is required");const t={id:T(),selector:e.selector.trim(),pathPattern:b(e.pathPattern??"",this.currentPage),prompt:e.prompt.trim(),enabled:e.enabled??!0,changeTypes:x(e.changeTypes),createdAt:Date.now()};if(this.listeners.set(t.id,t),t.enabled&&this.currentPage&&await this.installOnPage(this.currentPage,t).catch(()=>{}),this.config.store)try{await this.config.store.add(this.config.scopeKey,t)}catch{}return t}async updateListener(e){const t=this.listeners.get(e.listenerId);if(t){if(typeof e.selector=="string"&&e.selector.trim()&&(t.selector=e.selector.trim()),typeof e.prompt=="string"&&e.prompt.trim()&&(t.prompt=e.prompt.trim()),typeof e.pathPattern=="string"&&(t.pathPattern=b(e.pathPattern,this.currentPage)),Array.isArray(e.changeTypes)&&(t.changeTypes=x(e.changeTypes)),typeof e.enabled=="boolean"&&(t.enabled=e.enabled),this.currentPage&&(t.enabled?await this.installOnPage(this.currentPage,t).catch(()=>{}):await this.uninstallOnPage(this.currentPage,t.id).catch(()=>{})),this.config.store)try{await this.config.store.update(this.config.scopeKey,t)}catch{}return t}}async removeListener(e){if(!this.listeners.get(e))return!1;if(this.listeners.delete(e),this.triggerMeta.delete(e),this.pendingChangeTypes.delete(e),this.existsLatched.delete(e),this.currentPage&&await this.uninstallOnPage(this.currentPage,e).catch(()=>{}),this.config.store)try{await this.config.store.remove(this.config.scopeKey,e)}catch{}return!0}async toggleListener(e,t){const r=this.listeners.get(e);if(!r)return;const i=t??!r.enabled;if(i===r.enabled)return r;if(r.enabled=i,this.currentPage&&(i?await this.installOnPage(this.currentPage,r).catch(()=>{}):await this.uninstallOnPage(this.currentPage,r.id).catch(()=>{})),this.config.store)try{await this.config.store.update(this.config.scopeKey,r)}catch{}return r}async listListeners(){const e=this.currentPage?this.currentPage.url():"",t=[];for(const r of this.listeners.values()){const i={...r,status:"disabled"};r.enabled&&(y(e,r.pathPattern)?this.currentPage&&await this.elementExists(this.currentPage,r.selector)?i.status="active":i.status="inactive_element_missing":i.status="inactive_path_mismatch");const s=this.triggerMeta.get(r.id);s&&(i.lastTriggeredAt=s.at,i.currentUrl=s.url,i.currentText=s.text),t.push(i)}return t}hasPendingEvents(){return this.eventQueue.length>0}enqueueEvent(e){this.eventQueue.push(e)}dequeueEvent(){return this.eventQueue.shift()}collectEvents(){const e=[...this.eventQueue];this.eventQueue.length=0,e.sort((t,r)=>t.triggeredAt-r.triggeredAt);for(const t of e)t.changeType&&this.pendingChangeTypes.get(t.listenerId)?.delete(t.changeType);return this.existsLatched.clear(),e}async scanExistsAtPoll(){const e=this.currentPage;if(!e)return[];const t=e.url(),r=[];for(const l of this.listeners.values())l.enabled&&l.changeTypes.includes("exists")&&y(t,l.pathPattern)&&r.push({id:l.id,selector:l.selector});if(r.length===0)return[];let i=[];try{i=await e.evaluate(l=>l.map(o=>{let c=!1,h="";try{const d=globalThis.document?.querySelector(o.selector)??null;c=!!d,d&&(h=(d.textContent||"").slice(0,4096))}catch{c=!1}return{id:o.id,present:c,text:h}}),r)}catch{return[]}const s=[],a=Date.now();for(const l of i){const o=l.id;if(!l.present){this.existsLatched.delete(o);continue}if(this.existsLatched.has(o))continue;const c=this.listeners.get(o);if(!c||this.eventQueue.some(p=>p.listenerId===l.id&&p.changeType==="exists"))continue;const g=l.text.slice(0,this.config.maxTextLength);this.triggerMeta.set(o,{at:a,url:t,text:g});const d={listenerId:o,prompt:c.prompt,triggeredAt:a,url:t,browserId:this.browserId,text:g,selector:c.selector,changeType:"exists"};this.eventQueue.push(d),this.existsLatched.add(o),s.push(d)}return s}async dispose(){if(this.currentPage)for(const e of this.listeners.values())await this.uninstallOnPage(this.currentPage,e.id).catch(()=>{});this.listeners.clear(),this.triggerMeta.clear(),this.pendingChangeTypes.clear(),this.existsLatched.clear(),this.eventQueue.length=0}async setHighlightEnabled(e){this.currentPage&&await this.currentPage.evaluate(t=>{const r=globalThis;typeof r.__bridgeSetHighlightEnabled=="function"&&r.__bridgeSetHighlightEnabled(t)},e).catch(()=>{})}async ensureExposed(e){if(!this.attachedPages.has(e)){try{await e.exposeFunction(E,(t,r,i)=>{this.handleDomChange(t,r,i)})}catch{}try{await e.addInitScript({content:H(this.config.debounceMs,this.config.maxTextLength,this.config.highlight)})}catch{}this.attachedPages.add(e)}}attachNavigationHandler(e){if(this.navHandledPages.has(e))return;const t=e.on;if(typeof t!="function")return;this.navHandledPages.add(e);let r;t.call(e,"framenavigated",i=>{(typeof i?.isMainFrame!="function"||i.isMainFrame())&&(r&&clearTimeout(r),r=setTimeout(()=>{r=void 0,!(this.currentPage!==e||e.isClosed?.())&&this.reattach(e).catch(()=>{})},this.config.reattachDelayMs))})}handleDomChange(e,t,r){const i=this.listeners.get(e);if(!i||!i.enabled)return;const s=this.currentPage?this.currentPage.url():"",a=Date.now(),l=t?.slice(0,this.config.maxTextLength)??"",o=r??"changed";if(this.triggerMeta.set(e,{at:a,url:s,text:l}),o!=="exists"){let h=this.pendingChangeTypes.get(e);if(h||(h=new Set,this.pendingChangeTypes.set(e,h)),h.has(o))return;h.add(o)}else this.existsLatched.add(e);const c={listenerId:e,prompt:i.prompt,triggeredAt:a,url:s,browserId:this.browserId,text:l,selector:i.selector,changeType:o};this.eventQueue.push(c);try{this.config.onTrigger?.(c,i)}catch{}}async installOnPage(e,t){await e.evaluate(r=>{const i=globalThis;typeof i.__bridgeInstallListener=="function"&&i.__bridgeInstallListener(r.listenerId,r.selector,r.pathPattern,r.changeTypes)},{listenerId:t.id,selector:t.selector,pathPattern:t.pathPattern,changeTypes:t.changeTypes})}async uninstallOnPage(e,t){await e.evaluate(r=>{const i=globalThis;typeof i.__bridgeUninstallListener=="function"&&i.__bridgeUninstallListener(r)},t)}async elementExists(e,t){try{return await e.evaluate(r=>{try{return!!globalThis.document?.querySelector(r)}catch{return!1}},t)}catch{return!1}}}export{C as PageListenerManager,S as derivePathPatternFromUrl,I as detectChangeType,m as detectChangeTypes,L as extractPathname,y as matchPath,f as nodesContainElement};
|
|
307
|
+
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
export type ListenerId = string;
|
|
4
|
+
|
|
5
|
+
export type ListenerChangeType = "added" | "removed" | "changed" | "exists";
|
|
6
|
+
|
|
7
|
+
export type ListenerStatus = "active" | "inactive_element_missing" | "inactive_path_mismatch" | "disabled";
|
|
8
|
+
|
|
9
|
+
export interface PageListener {
|
|
10
|
+
|
|
11
|
+
readonly id: ListenerId;
|
|
12
|
+
|
|
13
|
+
selector: string;
|
|
14
|
+
|
|
15
|
+
pathPattern: string;
|
|
16
|
+
|
|
17
|
+
prompt: string;
|
|
18
|
+
|
|
19
|
+
enabled: boolean;
|
|
20
|
+
|
|
21
|
+
changeTypes: ListenerChangeType[];
|
|
22
|
+
|
|
23
|
+
readonly createdAt: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface PageListenerInfo extends PageListener {
|
|
27
|
+
|
|
28
|
+
status: ListenerStatus;
|
|
29
|
+
|
|
30
|
+
currentUrl?: string;
|
|
31
|
+
|
|
32
|
+
lastTriggeredAt?: number;
|
|
33
|
+
|
|
34
|
+
currentText?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface ListenerTriggerEvent {
|
|
38
|
+
|
|
39
|
+
readonly listenerId: ListenerId;
|
|
40
|
+
|
|
41
|
+
readonly prompt: string;
|
|
42
|
+
|
|
43
|
+
readonly triggeredAt: number;
|
|
44
|
+
|
|
45
|
+
readonly url: string;
|
|
46
|
+
|
|
47
|
+
readonly browserId: string;
|
|
48
|
+
|
|
49
|
+
readonly text?: string;
|
|
50
|
+
|
|
51
|
+
readonly selector?: string;
|
|
52
|
+
|
|
53
|
+
readonly changeType?: ListenerChangeType;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface ListenerEventQueue {
|
|
57
|
+
|
|
58
|
+
enqueue(event: ListenerTriggerEvent): void;
|
|
59
|
+
|
|
60
|
+
dequeue(): ListenerTriggerEvent | undefined;
|
|
61
|
+
|
|
62
|
+
peek(): ListenerTriggerEvent | undefined;
|
|
63
|
+
|
|
64
|
+
length(): number;
|
|
65
|
+
|
|
66
|
+
clear(): void;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface PageListenerManagerConfig {
|
|
70
|
+
|
|
71
|
+
debounceMs?: number;
|
|
72
|
+
|
|
73
|
+
maxTextLength?: number;
|
|
74
|
+
|
|
75
|
+
reattachDelayMs?: number;
|
|
76
|
+
|
|
77
|
+
onTrigger?: (event: ListenerTriggerEvent, listener: PageListener) => void;
|
|
78
|
+
|
|
79
|
+
store?: import("./listener-store.js").ListenerStore | null;
|
|
80
|
+
|
|
81
|
+
scopeKey?: string;
|
|
82
|
+
|
|
83
|
+
highlight?: boolean;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface PathMatchResult {
|
|
87
|
+
matched: boolean;
|
|
88
|
+
|
|
89
|
+
reason?: string;
|
|
90
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
|
|
2
|
+
import type { CDPSession, Page } from "playwright";
|
|
3
|
+
import { type FrameSubscriber, type QualityPresetConfig, type ScreencastConfig, type ScreencastFrame } from "./types.js";
|
|
4
|
+
|
|
5
|
+
export declare class ScreencastManager {
|
|
6
|
+
private config;
|
|
7
|
+
|
|
8
|
+
private currentPage;
|
|
9
|
+
|
|
10
|
+
private currentSession;
|
|
11
|
+
|
|
12
|
+
private isRunning;
|
|
13
|
+
|
|
14
|
+
private readonly subscribers;
|
|
15
|
+
|
|
16
|
+
private lastFrame;
|
|
17
|
+
constructor(config?: ScreencastConfig);
|
|
18
|
+
|
|
19
|
+
subscribe(fn: FrameSubscriber): () => void;
|
|
20
|
+
|
|
21
|
+
start(page: Page): Promise<void>;
|
|
22
|
+
|
|
23
|
+
private handleScreencastFrame;
|
|
24
|
+
|
|
25
|
+
stop(page: Page): Promise<void>;
|
|
26
|
+
|
|
27
|
+
stopAll(): Promise<void>;
|
|
28
|
+
|
|
29
|
+
capture(page: Page): Promise<ScreencastFrame | undefined>;
|
|
30
|
+
|
|
31
|
+
getLastFrame(): ScreencastFrame | undefined;
|
|
32
|
+
|
|
33
|
+
getCurrentCdpSession(): CDPSession | undefined;
|
|
34
|
+
|
|
35
|
+
restartAfterNavigation(page: Page): Promise<void>;
|
|
36
|
+
|
|
37
|
+
updateQuality(preset: QualityPresetConfig): Promise<boolean>;
|
|
38
|
+
|
|
39
|
+
forceCaptureAndPush(page: Page): Promise<void>;
|
|
40
|
+
|
|
41
|
+
dispose(): Promise<void>;
|
|
42
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{QUALITY_PRESETS as o}from"./types.js";const h=o.original,u=1,f="jpeg",g=30;class d{constructor(t){this.currentPage=null,this.currentSession=null,this.isRunning=!1,this.subscribers=new Set,this.config={quality:t?.quality??h.quality,maxWidth:t?.maxWidth??h.maxWidth,maxHeight:t?.maxHeight??h.maxHeight,everyNthFrame:t?.everyNthFrame??u,maxFramesPerSecond:t?.maxFramesPerSecond??g,format:t?.format??f}}subscribe(t){if(this.subscribers.add(t),this.subscribers.size===1&&this.currentPage&&!this.isRunning&&this.start(this.currentPage).catch(()=>{}),this.lastFrame){const s=this.lastFrame;setImmediate(()=>{if(this.subscribers.has(t))try{t(s)}catch{}})}return()=>{this.subscribers.delete(t)}}async start(t){if(this.currentPage===t&&this.isRunning)return;this.currentPage&&this.currentPage!==t&&await this.stop(this.currentPage),this.currentPage=t;let s;try{s=await t.context().newCDPSession(t)}catch(e){throw this.isRunning=!1,new Error(`Failed to create CDPSession for screencast: ${e instanceof Error?e.message:String(e)}`)}this.currentSession=s,s.on("Page.screencastFrame",e=>{this.handleScreencastFrame(e,t)});try{await s.send("Page.startScreencast",{format:this.config.format,quality:this.config.quality,maxWidth:this.config.maxWidth,maxHeight:this.config.maxHeight,everyNthFrame:this.config.everyNthFrame,...this.config.maxFramesPerSecond?{maxFramesPerSecond:this.config.maxFramesPerSecond}:{}}),this.isRunning=!0}catch(e){try{await s.detach()}catch{}throw this.currentSession=null,this.isRunning=!1,new Error(`Failed to start screencast: ${e instanceof Error?e.message:String(e)}`)}}handleScreencastFrame(t,s){this.currentSession&&t.sessionId&&this.currentSession.send("Page.screencastFrameAck",{sessionId:t.sessionId}).catch(()=>{});const e=t.data??"",i=t.metadata?.deviceWidth??0,r=t.metadata?.deviceHeight??0;let a="";try{a=s.url()}catch{}const n={dataUrl:`data:image/${this.config.format==="png"?"png":"jpeg"};base64,${e}`,base64:e,width:i,height:r,timestamp:Date.now(),url:a};this.lastFrame=n;for(const c of this.subscribers)try{c(n)}catch{}}async stop(t){if(this.currentPage!==t)return;const s=this.currentSession;if(s){try{await s.send("Page.stopScreencast")}catch{}try{await s.detach()}catch{}}this.currentSession=null,this.isRunning=!1,this.currentPage=null}async stopAll(){this.currentPage&&await this.stop(this.currentPage)}async capture(t){if(this.lastFrame&&this.currentPage===t)return this.lastFrame;try{const e=(await t.screenshot({type:this.config.format==="png"?"png":"jpeg",quality:this.config.format==="png"?void 0:this.config.quality})).toString("base64"),i=`data:image/${this.config.format==="png"?"png":"jpeg"};base64,${e}`,r=t.viewportSize();return{dataUrl:i,base64:e,width:r?.width??0,height:r?.height??0,timestamp:Date.now(),url:t.url()}}catch{return}}getLastFrame(){return this.lastFrame}getCurrentCdpSession(){return this.currentSession??void 0}async restartAfterNavigation(t){if(this.currentPage===t)try{await this.stop(t)}catch{}let s=!1;for(let e=0;e<2;e++)try{await this.start(t),s=!0;break}catch{e===0&&await new Promise(i=>setTimeout(i,300))}s||(this.isRunning=!1,this.currentSession=null,this.currentPage=null),await this.forceCaptureAndPush(t).catch(()=>{})}async updateQuality(t){this.config={...this.config,quality:t.quality,maxWidth:t.maxWidth,maxHeight:t.maxHeight};const s=this.currentPage;if(!s||!this.isRunning)return!1;try{await this.stop(s)}catch{}try{await this.start(s)}catch{return!1}return this.forceCaptureAndPush(s).catch(()=>{}),!0}async forceCaptureAndPush(t){if(!t||t.isClosed())return;let s;const e=t.viewportSize(),i=e?.width??0,r=e?.height??0;if(this.currentSession)try{s=(await this.currentSession.send("Page.captureScreenshot",{format:this.config.format==="png"?"png":"jpeg",quality:this.config.format==="png"?void 0:this.config.quality})).data}catch{}if(!s)try{s=(await t.screenshot({type:this.config.format==="png"?"png":"jpeg",quality:this.config.format==="png"?void 0:this.config.quality})).toString("base64")}catch{return}let a="";try{a=t.url()}catch{}const n={dataUrl:`data:image/${this.config.format==="png"?"png":"jpeg"};base64,${s}`,base64:s,width:i,height:r,timestamp:Date.now(),url:a};this.lastFrame=n;for(const c of this.subscribers)try{c(n)}catch{}}async dispose(){await this.stopAll(),this.subscribers.clear(),this.lastFrame=void 0}}export{d as ScreencastManager};
|
|
2
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
import type { Locator, Page } from "playwright";
|
|
3
|
+
|
|
4
|
+
export declare function buildRefMap(snapshot: string): Map<string, {
|
|
5
|
+
role: string;
|
|
6
|
+
name: string;
|
|
7
|
+
}>;
|
|
8
|
+
|
|
9
|
+
export declare function serializeSnapshot(page: Page, options?: {
|
|
10
|
+
maxChars?: number;
|
|
11
|
+
}): Promise<string>;
|
|
12
|
+
|
|
13
|
+
export declare function locateByRoleName(page: Page, role: string, name?: string): Locator | null;
|
|
14
|
+
|
|
15
|
+
export declare function findElementByRef(page: Page, ref: string): Promise<Locator | null>;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
const f=16e3,c="ref must be a non-empty string",l=/^\s*-\s+(?<role>[a-zA-Z][\w-]*)(?:\s+"(?<name>[^"]*)")?[^[]*(?:\[ref=(?<ref>e\d+)\])/;function a(t){const n=l.exec(t);if(!n||!n.groups)return null;const r=n.groups.role??"",e=n.groups.name??"",o=n.groups.ref??null;return!r||!o?null:{role:r,name:e,ref:o}}function p(t){const n=new Map;if(!t)return n;for(const r of t.split(`
|
|
2
|
+
`)){const e=a(r);e&&e.ref&&n.set(e.ref,{role:e.role,name:e.name})}return n}async function m(t,n){const r=n?.maxChars??16e3,e=await t.ariaSnapshot({mode:"ai"});return e?e.length<=r?e:`${e.slice(0,r)}
|
|
3
|
+
... (truncated at ${r} chars)`:"(empty accessibility tree)"}function u(t,n,r){if(!n)return null;try{if(r){const e=r.replace(/\\/g,"\\\\").replace(/"/g,'\\"');return t.locator(`role=${n}[name="${e}"]`).first()}return t.locator(`role=${n}`).first()}catch{return null}}async function d(t,n){if(typeof n!="string"||!n.trim())throw new Error(c);const r=n.trim();if(!/^e\d+$/.test(r))return null;const e=await t.ariaSnapshot({mode:"ai"});if(!e)return null;const o=e.split(`
|
|
4
|
+
`);for(const i of o){const s=a(i);if(!(!s||s.ref!==r))return u(t,s.role,s.name)}return null}export{p as buildRefMap,d as findElementByRef,u as locateByRoleName,m as serializeSnapshot};
|
|
5
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
import type { SsrfConfig } from "./types.js";
|
|
3
|
+
|
|
4
|
+
export declare function validateUrl(url: string, config?: SsrfConfig): string;
|
|
5
|
+
|
|
6
|
+
export declare function validateUrlWithDns(url: string, config?: SsrfConfig): Promise<string>;
|
|
7
|
+
|
|
8
|
+
export declare class SsrfGuard {
|
|
9
|
+
private readonly config;
|
|
10
|
+
constructor(config?: SsrfConfig);
|
|
11
|
+
|
|
12
|
+
validate(url: string): string;
|
|
13
|
+
|
|
14
|
+
validateWithDns(url: string): Promise<string>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import*as d from"node:dns/promises";function l(e){const t=e.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/);if(!t)return!1;const r=Number(t[1]),o=Number(t[2]);return r===10||r===172&&o>=16&&o<=31||r===192&&o===168||r===127||r===169&&o===254||r===0}function f(e){const t=e.toLowerCase();if(t==="::1"||t.startsWith("fe80:")||t.startsWith("fc")||t.startsWith("fd")||t.startsWith("::")&&!t.startsWith("::ffff:"))return!0;const r=t.match(/::ffff:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/);return r?l(r[1]):!1}function u(e){const t=e.replace(/^\[/,"").replace(/\]$/,"");return t.includes(":")&&!t.match(/^\d+\.\d+\.\d+\.\d+$/)?f(t):/^\d{1,3}(\.\d{1,3}){3}$/.test(t)?l(t):t.toLowerCase()==="localhost"}function c(e,t){const r=t??{};let o;try{o=new URL(e)}catch{throw new Error(`Invalid URL: ${e}`)}const a=o.protocol.toLowerCase();if(a!=="http:"&&a!=="https:")throw new Error(`Disallowed protocol '${a}' \u2014 only http/https are allowed`);const s=o.hostname;if(r.allowlist&&r.allowlist.length>0&&!r.allowlist.map(n=>n.toLowerCase()).includes(s.toLowerCase()))throw new Error(`Host '${s}' is not in the allowlist`);if(r.blocklist&&r.blocklist.length>0&&r.blocklist.map(n=>n.toLowerCase()).includes(s.toLowerCase()))throw new Error(`Host '${s}' is blocked by ssrf blocklist`);if(!r.allowPrivate&&u(s))throw new Error(`Refusing to navigate to private/loopback address: ${s} (set ssrfConfig.allowPrivate=true to override)`);return o.href}async function w(e,t){const r=c(e,t);if((t??{}).allowPrivate)return r;const s=new URL(r).hostname.replace(/^\[/,"").replace(/\]$/,"");try{const i=await d.lookup(s,{all:!0});for(const n of i)if(l(n.address)||f(n.address))throw new Error(`Refusing to navigate to '${s}' which resolves to private address ${n.address} (DNS rebinding protection; set ssrfConfig.allowPrivate=true to override)`)}catch(i){if(i instanceof Error&&i.message.includes("DNS rebinding protection"))throw i}return r}class h{constructor(t){this.config=t??{}}validate(t){return c(t,this.config)}validateWithDns(t){return w(t,this.config)}}export{h as SsrfGuard,c as validateUrl,w as validateUrlWithDns};
|
|
2
|
+
|