agent-browser-stealth 0.17.0-fork.2 → 0.24.0-fork.2
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/README.md +1256 -240
- package/bin/agent-browser-darwin-arm64 +0 -0
- package/bin/agent-browser-darwin-x64 +0 -0
- package/bin/agent-browser-linux-arm64 +0 -0
- package/bin/agent-browser-linux-x64 +0 -0
- package/bin/agent-browser-win32-x64.exe +0 -0
- package/bin/agent-browser.js +13 -2
- package/extensions/tab-group-cdp/content-script.js +425 -0
- package/extensions/tab-group-cdp/icons/icon.svg +7 -0
- package/extensions/tab-group-cdp/manifest.json +34 -0
- package/extensions/tab-group-cdp/page-bridge.js +133 -0
- package/extensions/tab-group-cdp/service-worker.js +2249 -0
- package/extensions/tab-group-cdp/sidepanel.css +258 -0
- package/extensions/tab-group-cdp/sidepanel.html +28 -0
- package/extensions/tab-group-cdp/sidepanel.js +1225 -0
- package/package.json +17 -69
- package/scripts/build-all-platforms.sh +6 -0
- package/scripts/check-version-sync.js +14 -2
- package/scripts/copy-native.js +8 -50
- package/scripts/postinstall.js +149 -165
- package/scripts/windows-debug/provision.sh +220 -0
- package/scripts/windows-debug/run.sh +92 -0
- package/scripts/windows-debug/start.sh +43 -0
- package/scripts/windows-debug/stop.sh +28 -0
- package/scripts/windows-debug/sync.sh +27 -0
- package/skills/agent-browser/SKILL.md +256 -159
- package/skills/agent-browser/references/authentication.md +101 -0
- package/skills/agent-browser/references/commands.md +34 -2
- package/skills/agent-browser/references/snapshot-refs.md +25 -0
- package/skills/agentcore/SKILL.md +115 -0
- package/skills/dogfood/SKILL.md +4 -2
- package/skills/electron/SKILL.md +26 -2
- package/skills/slack/SKILL.md +0 -9
- package/skills/slack/references/slack-tasks.md +2 -8
- package/skills/vercel-sandbox/SKILL.md +280 -0
- package/bin/agent-browser-local +0 -0
- package/bin/agent-browser-stealth +0 -0
- package/bin/agent-browser-stealth.d +0 -1
- package/dist/action-policy.d.ts +0 -14
- package/dist/action-policy.d.ts.map +0 -1
- package/dist/action-policy.js +0 -253
- package/dist/action-policy.js.map +0 -1
- package/dist/actions.d.ts +0 -21
- package/dist/actions.d.ts.map +0 -1
- package/dist/actions.js +0 -2139
- package/dist/actions.js.map +0 -1
- package/dist/auth-cli.d.ts +0 -2
- package/dist/auth-cli.d.ts.map +0 -1
- package/dist/auth-cli.js +0 -97
- package/dist/auth-cli.js.map +0 -1
- package/dist/auth-vault.d.ts +0 -36
- package/dist/auth-vault.d.ts.map +0 -1
- package/dist/auth-vault.js +0 -125
- package/dist/auth-vault.js.map +0 -1
- package/dist/browser.d.ts +0 -665
- package/dist/browser.d.ts.map +0 -1
- package/dist/browser.js +0 -3210
- package/dist/browser.js.map +0 -1
- package/dist/confirmation.d.ts +0 -8
- package/dist/confirmation.d.ts.map +0 -1
- package/dist/confirmation.js +0 -30
- package/dist/confirmation.js.map +0 -1
- package/dist/daemon.d.ts +0 -78
- package/dist/daemon.d.ts.map +0 -1
- package/dist/daemon.js +0 -744
- package/dist/daemon.js.map +0 -1
- package/dist/diff.d.ts +0 -18
- package/dist/diff.d.ts.map +0 -1
- package/dist/diff.js +0 -271
- package/dist/diff.js.map +0 -1
- package/dist/domain-filter.d.ts +0 -28
- package/dist/domain-filter.d.ts.map +0 -1
- package/dist/domain-filter.js +0 -149
- package/dist/domain-filter.js.map +0 -1
- package/dist/encryption.d.ts +0 -73
- package/dist/encryption.d.ts.map +0 -1
- package/dist/encryption.js +0 -171
- package/dist/encryption.js.map +0 -1
- package/dist/ios-actions.d.ts +0 -11
- package/dist/ios-actions.d.ts.map +0 -1
- package/dist/ios-actions.js +0 -228
- package/dist/ios-actions.js.map +0 -1
- package/dist/ios-manager.d.ts +0 -266
- package/dist/ios-manager.d.ts.map +0 -1
- package/dist/ios-manager.js +0 -1073
- package/dist/ios-manager.js.map +0 -1
- package/dist/protocol.d.ts +0 -26
- package/dist/protocol.d.ts.map +0 -1
- package/dist/protocol.js +0 -990
- package/dist/protocol.js.map +0 -1
- package/dist/snapshot.d.ts +0 -67
- package/dist/snapshot.d.ts.map +0 -1
- package/dist/snapshot.js +0 -514
- package/dist/snapshot.js.map +0 -1
- package/dist/state-utils.d.ts +0 -77
- package/dist/state-utils.d.ts.map +0 -1
- package/dist/state-utils.js +0 -178
- package/dist/state-utils.js.map +0 -1
- package/dist/stealth.d.ts +0 -41
- package/dist/stealth.d.ts.map +0 -1
- package/dist/stealth.js +0 -1743
- package/dist/stealth.js.map +0 -1
- package/dist/stream-server.d.ts +0 -117
- package/dist/stream-server.d.ts.map +0 -1
- package/dist/stream-server.js +0 -309
- package/dist/stream-server.js.map +0 -1
- package/dist/types.d.ts +0 -973
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
- package/scripts/check-creepjs-headless.js +0 -137
- package/scripts/check-daemon-pid-recovery.js +0 -148
- package/scripts/check-sannysoft-webdriver.js +0 -112
- package/scripts/check-stealth-regression.js +0 -199
- package/scripts/check-turnstile-testkey.ts +0 -125
- package/scripts/clawhub-sync.sh +0 -27
- package/scripts/sync-upstream.sh +0 -142
- package/scripts/verify-bundled-binaries.js +0 -71
- package/scripts/verify-native-version.js +0 -48
- package/scripts/verify-packed-host-binary.js +0 -88
- package/scripts/verify-registry-host-binary.js +0 -120
- package/skills/agent-browser-stealth/SKILL.md +0 -127
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/bin/agent-browser.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* binary directly (zero overhead).
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { spawn } from 'child_process';
|
|
11
|
+
import { spawn, execSync } from 'child_process';
|
|
12
12
|
import { existsSync, accessSync, chmodSync, constants } from 'fs';
|
|
13
13
|
import { dirname, join } from 'path';
|
|
14
14
|
import { fileURLToPath } from 'url';
|
|
@@ -16,6 +16,17 @@ import { platform, arch } from 'os';
|
|
|
16
16
|
|
|
17
17
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
18
18
|
|
|
19
|
+
// Detect if the system uses musl libc (e.g. Alpine Linux)
|
|
20
|
+
function isMusl() {
|
|
21
|
+
if (platform() !== 'linux') return false;
|
|
22
|
+
try {
|
|
23
|
+
const result = execSync('ldd --version 2>&1 || true', { encoding: 'utf8' });
|
|
24
|
+
return result.toLowerCase().includes('musl');
|
|
25
|
+
} catch {
|
|
26
|
+
return existsSync('/lib/ld-musl-x86_64.so.1') || existsSync('/lib/ld-musl-aarch64.so.1');
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
19
30
|
// Map Node.js platform/arch to binary naming convention
|
|
20
31
|
function getBinaryName() {
|
|
21
32
|
const os = platform();
|
|
@@ -27,7 +38,7 @@ function getBinaryName() {
|
|
|
27
38
|
osKey = 'darwin';
|
|
28
39
|
break;
|
|
29
40
|
case 'linux':
|
|
30
|
-
osKey = 'linux';
|
|
41
|
+
osKey = isMusl() ? 'linux-musl' : 'linux';
|
|
31
42
|
break;
|
|
32
43
|
case 'win32':
|
|
33
44
|
osKey = 'win32';
|
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
const REQUEST_TYPE = 'AB_TAB_GROUP_REQUEST';
|
|
3
|
+
const RESPONSE_TYPE = 'AB_TAB_GROUP_RESPONSE';
|
|
4
|
+
|
|
5
|
+
const CONTENT_EVENT_TYPE = 'AB_CONTENT_EVENT';
|
|
6
|
+
const CONTENT_EXECUTE_ACTION = 'AB_CONTENT_EXECUTE_ACTION';
|
|
7
|
+
const CONTENT_GET_DOM_STATE = 'AB_CONTENT_GET_DOM_STATE';
|
|
8
|
+
const CONTENT_PING = 'AB_CONTENT_PING';
|
|
9
|
+
const PAGE_BRIDGE_EVENT = 'AB_PAGE_BRIDGE_EVENT';
|
|
10
|
+
const STORAGE_OPTIONS_KEY = 'abExtensionOptionsV1';
|
|
11
|
+
|
|
12
|
+
const mutationState = {
|
|
13
|
+
total: 0,
|
|
14
|
+
recent: [],
|
|
15
|
+
observerReady: false,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
function pushMutationSummary(entry) {
|
|
19
|
+
mutationState.total += 1;
|
|
20
|
+
mutationState.recent.push({
|
|
21
|
+
...entry,
|
|
22
|
+
timestamp: Date.now(),
|
|
23
|
+
});
|
|
24
|
+
if (mutationState.recent.length > 40) {
|
|
25
|
+
mutationState.recent.splice(0, mutationState.recent.length - 40);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function serializeValue(value, depth = 0) {
|
|
30
|
+
if (value === null || typeof value === 'undefined') return value;
|
|
31
|
+
if (typeof value === 'string') return value.slice(0, 300);
|
|
32
|
+
if (typeof value === 'number' || typeof value === 'boolean') return value;
|
|
33
|
+
if (value instanceof Error) return `${value.name}: ${value.message}`;
|
|
34
|
+
if (depth > 2) return '[depth-limit]';
|
|
35
|
+
|
|
36
|
+
if (Array.isArray(value)) {
|
|
37
|
+
return value.slice(0, 10).map((item) => serializeValue(item, depth + 1));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (typeof value === 'object') {
|
|
41
|
+
const out = {};
|
|
42
|
+
for (const [key, entry] of Object.entries(value).slice(0, 15)) {
|
|
43
|
+
out[key] = serializeValue(entry, depth + 1);
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return String(value).slice(0, 300);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function sendRuntimeEvent(kind, payload) {
|
|
52
|
+
try {
|
|
53
|
+
chrome.runtime.sendMessage({
|
|
54
|
+
type: CONTENT_EVENT_TYPE,
|
|
55
|
+
kind,
|
|
56
|
+
payload: serializeValue(payload),
|
|
57
|
+
url: window.location.href,
|
|
58
|
+
title: document.title,
|
|
59
|
+
timestamp: Date.now(),
|
|
60
|
+
});
|
|
61
|
+
} catch {
|
|
62
|
+
// Ignore runtime channel errors.
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getPageBridgeEnabled() {
|
|
67
|
+
return new Promise((resolve) => {
|
|
68
|
+
try {
|
|
69
|
+
chrome.storage.local.get([STORAGE_OPTIONS_KEY], (result) => {
|
|
70
|
+
if (chrome.runtime.lastError) {
|
|
71
|
+
resolve(false);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const rawOptions = result?.[STORAGE_OPTIONS_KEY];
|
|
75
|
+
resolve(Boolean(rawOptions && typeof rawOptions === 'object' && rawOptions.pageBridgeEnabled === true));
|
|
76
|
+
});
|
|
77
|
+
} catch {
|
|
78
|
+
resolve(false);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function installPageBridge() {
|
|
84
|
+
// Receives events emitted by the injected page-world hook script.
|
|
85
|
+
const bridgeListener = (event) => {
|
|
86
|
+
if (event.source !== window) return;
|
|
87
|
+
const data = event.data;
|
|
88
|
+
if (!data || data.type !== PAGE_BRIDGE_EVENT) return;
|
|
89
|
+
sendRuntimeEvent(data.kind || 'page-event', data.payload || {});
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
window.addEventListener('message', bridgeListener);
|
|
93
|
+
|
|
94
|
+
const parent = document.documentElement || document.head || document.body;
|
|
95
|
+
if (!parent) return;
|
|
96
|
+
|
|
97
|
+
if (!(await getPageBridgeEnabled())) {
|
|
98
|
+
sendRuntimeEvent('lifecycle', {
|
|
99
|
+
event: 'bridge-disabled-default',
|
|
100
|
+
});
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Use external extension script instead of inline text to reduce CSP conflicts.
|
|
105
|
+
const script = document.createElement('script');
|
|
106
|
+
script.src = chrome.runtime.getURL('page-bridge.js');
|
|
107
|
+
script.async = false;
|
|
108
|
+
script.dataset.abBridgeEvent = PAGE_BRIDGE_EVENT;
|
|
109
|
+
script.onload = () => script.remove();
|
|
110
|
+
script.onerror = () => {
|
|
111
|
+
sendRuntimeEvent('lifecycle', {
|
|
112
|
+
event: 'bridge-load-failed',
|
|
113
|
+
host: window.location.hostname,
|
|
114
|
+
});
|
|
115
|
+
script.remove();
|
|
116
|
+
};
|
|
117
|
+
parent.appendChild(script);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function ensureMutationObserver() {
|
|
121
|
+
if (mutationState.observerReady) return;
|
|
122
|
+
if (!document.documentElement) return;
|
|
123
|
+
|
|
124
|
+
const observer = new MutationObserver((records) => {
|
|
125
|
+
const summary = {
|
|
126
|
+
records: records.length,
|
|
127
|
+
addedNodes: 0,
|
|
128
|
+
removedNodes: 0,
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
for (const record of records.slice(0, 40)) {
|
|
132
|
+
summary.addedNodes += record.addedNodes?.length || 0;
|
|
133
|
+
summary.removedNodes += record.removedNodes?.length || 0;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
pushMutationSummary(summary);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
observer.observe(document.documentElement, {
|
|
140
|
+
childList: true,
|
|
141
|
+
subtree: true,
|
|
142
|
+
attributes: true,
|
|
143
|
+
attributeFilter: ['class', 'style', 'hidden', 'disabled', 'aria-hidden'],
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
mutationState.observerReady = true;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function toSimpleNode(element) {
|
|
150
|
+
if (!element || typeof element !== 'object') return null;
|
|
151
|
+
const node = {
|
|
152
|
+
tag: element.tagName?.toLowerCase() || 'unknown',
|
|
153
|
+
id: element.id || undefined,
|
|
154
|
+
className: typeof element.className === 'string' ? element.className.slice(0, 120) : '',
|
|
155
|
+
role: element.getAttribute?.('role') || undefined,
|
|
156
|
+
name:
|
|
157
|
+
element.getAttribute?.('aria-label') ||
|
|
158
|
+
element.getAttribute?.('name') ||
|
|
159
|
+
element.getAttribute?.('placeholder') ||
|
|
160
|
+
'',
|
|
161
|
+
text: (element.textContent || '').trim().replace(/\s+/g, ' ').slice(0, 160),
|
|
162
|
+
disabled: element.disabled === true,
|
|
163
|
+
hidden: element.hidden === true,
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
return node;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function collectInteractiveElements(root, limit = 80) {
|
|
170
|
+
const selector = [
|
|
171
|
+
'a[href]',
|
|
172
|
+
'button',
|
|
173
|
+
'input',
|
|
174
|
+
'select',
|
|
175
|
+
'textarea',
|
|
176
|
+
'summary',
|
|
177
|
+
'[role="button"]',
|
|
178
|
+
'[role="link"]',
|
|
179
|
+
'[tabindex]'
|
|
180
|
+
].join(',');
|
|
181
|
+
|
|
182
|
+
const out = [];
|
|
183
|
+
const nodes = root.querySelectorAll(selector);
|
|
184
|
+
for (const element of nodes) {
|
|
185
|
+
if (out.length >= limit) break;
|
|
186
|
+
out.push(toSimpleNode(element));
|
|
187
|
+
}
|
|
188
|
+
return out.filter(Boolean);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function collectDomState(options = {}) {
|
|
192
|
+
const selector = typeof options.selector === 'string' ? options.selector.trim() : '';
|
|
193
|
+
const root = selector ? document.querySelector(selector) : document.body || document.documentElement;
|
|
194
|
+
|
|
195
|
+
if (!root) {
|
|
196
|
+
return {
|
|
197
|
+
ok: false,
|
|
198
|
+
error: selector ? `selector-not-found: ${selector}` : 'root-not-found',
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const textPreview = (root.textContent || '').replace(/\s+/g, ' ').trim().slice(0, 1000);
|
|
203
|
+
const interactiveOnly = options.interactiveOnly === true;
|
|
204
|
+
const interactiveElements = collectInteractiveElements(root, options.maxNodes || 80);
|
|
205
|
+
|
|
206
|
+
const dom = {
|
|
207
|
+
href: window.location.href,
|
|
208
|
+
title: document.title,
|
|
209
|
+
readyState: document.readyState,
|
|
210
|
+
selector: selector || null,
|
|
211
|
+
rootTag: root.tagName?.toLowerCase() || 'unknown',
|
|
212
|
+
textPreview,
|
|
213
|
+
interactiveCount: interactiveElements.length,
|
|
214
|
+
interactiveElements,
|
|
215
|
+
mutation: {
|
|
216
|
+
total: mutationState.total,
|
|
217
|
+
recent: mutationState.recent.slice(-10),
|
|
218
|
+
},
|
|
219
|
+
capturedAt: Date.now(),
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
if (interactiveOnly) {
|
|
223
|
+
dom.textPreview = '';
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
ok: true,
|
|
228
|
+
state: dom,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function queryElement(selector) {
|
|
233
|
+
if (typeof selector !== 'string' || selector.trim().length === 0) {
|
|
234
|
+
throw new Error('selector is required');
|
|
235
|
+
}
|
|
236
|
+
const element = document.querySelector(selector);
|
|
237
|
+
if (!element) {
|
|
238
|
+
throw new Error(`Element not found: ${selector}`);
|
|
239
|
+
}
|
|
240
|
+
return element;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function focusElement(element) {
|
|
244
|
+
if (typeof element.focus === 'function') {
|
|
245
|
+
element.focus({ preventScroll: false });
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function dispatchInputEvents(element) {
|
|
250
|
+
element.dispatchEvent(new Event('input', { bubbles: true }));
|
|
251
|
+
element.dispatchEvent(new Event('change', { bubbles: true }));
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async function executeAction(command, args = {}) {
|
|
255
|
+
switch (command) {
|
|
256
|
+
case 'click': {
|
|
257
|
+
const element = queryElement(args.selector);
|
|
258
|
+
focusElement(element);
|
|
259
|
+
element.click();
|
|
260
|
+
return { ok: true, action: command, selector: args.selector };
|
|
261
|
+
}
|
|
262
|
+
case 'fill': {
|
|
263
|
+
const element = queryElement(args.selector);
|
|
264
|
+
if (!('value' in element)) {
|
|
265
|
+
throw new Error(`Element is not fillable: ${args.selector}`);
|
|
266
|
+
}
|
|
267
|
+
focusElement(element);
|
|
268
|
+
element.value = typeof args.value === 'string' ? args.value : String(args.value || '');
|
|
269
|
+
dispatchInputEvents(element);
|
|
270
|
+
return { ok: true, action: command, selector: args.selector, valueLength: element.value.length };
|
|
271
|
+
}
|
|
272
|
+
case 'press': {
|
|
273
|
+
const key = typeof args.key === 'string' && args.key.trim().length > 0 ? args.key.trim() : 'Enter';
|
|
274
|
+
let target;
|
|
275
|
+
if (typeof args.selector === 'string' && args.selector.trim().length > 0) {
|
|
276
|
+
target = queryElement(args.selector);
|
|
277
|
+
focusElement(target);
|
|
278
|
+
} else {
|
|
279
|
+
target = document.activeElement || document.body;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const down = new KeyboardEvent('keydown', { key, bubbles: true });
|
|
283
|
+
const up = new KeyboardEvent('keyup', { key, bubbles: true });
|
|
284
|
+
target.dispatchEvent(down);
|
|
285
|
+
target.dispatchEvent(up);
|
|
286
|
+
return { ok: true, action: command, key };
|
|
287
|
+
}
|
|
288
|
+
case 'eval': {
|
|
289
|
+
if (typeof args.expression !== 'string' || args.expression.trim().length === 0) {
|
|
290
|
+
throw new Error('expression is required');
|
|
291
|
+
}
|
|
292
|
+
const fn = new Function(`return (${args.expression});`);
|
|
293
|
+
const result = fn();
|
|
294
|
+
return { ok: true, action: command, result: serializeValue(result) };
|
|
295
|
+
}
|
|
296
|
+
case 'snapshot': {
|
|
297
|
+
return {
|
|
298
|
+
ok: true,
|
|
299
|
+
action: command,
|
|
300
|
+
...collectDomState({
|
|
301
|
+
selector: args.selector,
|
|
302
|
+
interactiveOnly: args.interactiveOnly === true,
|
|
303
|
+
maxNodes: args.maxNodes,
|
|
304
|
+
}),
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
default:
|
|
308
|
+
throw new Error(`Unknown content action: ${command}`);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
ensureMutationObserver();
|
|
313
|
+
installPageBridge();
|
|
314
|
+
|
|
315
|
+
window.addEventListener('message', (event) => {
|
|
316
|
+
if (event.source !== window) {
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const data = event.data;
|
|
321
|
+
if (!data || data.type !== REQUEST_TYPE) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const request = {
|
|
326
|
+
type: REQUEST_TYPE,
|
|
327
|
+
nonce: data.nonce,
|
|
328
|
+
session: data.session,
|
|
329
|
+
groupTitle: data.groupTitle,
|
|
330
|
+
pluginId: data.pluginId,
|
|
331
|
+
allowedDomains: Array.isArray(data.allowedDomains) ? data.allowedDomains : undefined,
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
try {
|
|
335
|
+
chrome.runtime.sendMessage(request, (response) => {
|
|
336
|
+
const lastError = chrome.runtime.lastError;
|
|
337
|
+
if (lastError) {
|
|
338
|
+
window.postMessage(
|
|
339
|
+
{
|
|
340
|
+
type: RESPONSE_TYPE,
|
|
341
|
+
nonce: request.nonce,
|
|
342
|
+
ok: false,
|
|
343
|
+
error: lastError.message,
|
|
344
|
+
},
|
|
345
|
+
'*'
|
|
346
|
+
);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const payload = response && typeof response === 'object' ? response : { ok: false };
|
|
351
|
+
|
|
352
|
+
window.postMessage(
|
|
353
|
+
{
|
|
354
|
+
type: RESPONSE_TYPE,
|
|
355
|
+
nonce: request.nonce,
|
|
356
|
+
ok: payload.ok === true,
|
|
357
|
+
extensionId:
|
|
358
|
+
typeof payload.extensionId === 'string' && payload.extensionId.length > 0
|
|
359
|
+
? payload.extensionId
|
|
360
|
+
: chrome.runtime.id,
|
|
361
|
+
groupId: typeof payload.groupId === 'number' ? payload.groupId : undefined,
|
|
362
|
+
windowId: typeof payload.windowId === 'number' ? payload.windowId : undefined,
|
|
363
|
+
color: typeof payload.color === 'string' ? payload.color : undefined,
|
|
364
|
+
collapsed: payload.collapsed === true,
|
|
365
|
+
policy:
|
|
366
|
+
payload.policy && typeof payload.policy === 'object'
|
|
367
|
+
? {
|
|
368
|
+
enforced: payload.policy.enforced === true,
|
|
369
|
+
blocked: payload.policy.blocked === true,
|
|
370
|
+
reason:
|
|
371
|
+
typeof payload.policy.reason === 'string' ? payload.policy.reason : undefined,
|
|
372
|
+
}
|
|
373
|
+
: undefined,
|
|
374
|
+
riskHints: Array.isArray(payload.riskHints) ? payload.riskHints : undefined,
|
|
375
|
+
error: typeof payload.error === 'string' ? payload.error : undefined,
|
|
376
|
+
},
|
|
377
|
+
'*'
|
|
378
|
+
);
|
|
379
|
+
});
|
|
380
|
+
} catch (error) {
|
|
381
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
382
|
+
window.postMessage(
|
|
383
|
+
{
|
|
384
|
+
type: RESPONSE_TYPE,
|
|
385
|
+
nonce: request.nonce,
|
|
386
|
+
ok: false,
|
|
387
|
+
error: errorMessage,
|
|
388
|
+
},
|
|
389
|
+
'*'
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
|
395
|
+
if (!message || typeof message !== 'object') return;
|
|
396
|
+
|
|
397
|
+
if (message.type === CONTENT_PING) {
|
|
398
|
+
sendResponse({
|
|
399
|
+
ok: true,
|
|
400
|
+
href: window.location.href,
|
|
401
|
+
title: document.title,
|
|
402
|
+
readyState: document.readyState,
|
|
403
|
+
});
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (message.type === CONTENT_GET_DOM_STATE) {
|
|
408
|
+
sendResponse(collectDomState(message.options || {}));
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (message.type === CONTENT_EXECUTE_ACTION) {
|
|
413
|
+
executeAction(message.command, message.args || {})
|
|
414
|
+
.then((result) => sendResponse(result))
|
|
415
|
+
.catch((error) => {
|
|
416
|
+
sendResponse({
|
|
417
|
+
ok: false,
|
|
418
|
+
action: message.command,
|
|
419
|
+
error: error instanceof Error ? error.message : String(error),
|
|
420
|
+
});
|
|
421
|
+
});
|
|
422
|
+
return true;
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
})();
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<rect width="128" height="128" rx="32" fill="#1A73E8"/>
|
|
3
|
+
<rect x="30" y="34" width="68" height="10" rx="2" fill="white"/>
|
|
4
|
+
<rect x="30" y="54" width="48" height="10" rx="2" fill="white" fill-opacity="0.8"/>
|
|
5
|
+
<rect x="30" y="74" width="28" height="10" rx="2" fill="white" fill-opacity="0.6"/>
|
|
6
|
+
<circle cx="94" cy="90" r="10" fill="#34A853" stroke="#1A73E8" stroke-width="4"/>
|
|
7
|
+
</svg>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"manifest_version": 3,
|
|
3
|
+
"name": "agent-browser-stealth",
|
|
4
|
+
"version": "0.2.0",
|
|
5
|
+
"description": "Session-aware tab grouping and coordination for CDP-driven agent-browser workflows.",
|
|
6
|
+
"icons": {
|
|
7
|
+
"128": "icons/icon.svg"
|
|
8
|
+
},
|
|
9
|
+
"permissions": ["tabs", "tabGroups", "downloads", "storage", "sidePanel", "alarms"],
|
|
10
|
+
"host_permissions": ["<all_urls>"],
|
|
11
|
+
"background": {
|
|
12
|
+
"service_worker": "service-worker.js"
|
|
13
|
+
},
|
|
14
|
+
"action": {
|
|
15
|
+
"default_title": "agent-browser-stealth"
|
|
16
|
+
},
|
|
17
|
+
"side_panel": {
|
|
18
|
+
"default_path": "sidepanel.html"
|
|
19
|
+
},
|
|
20
|
+
"content_scripts": [
|
|
21
|
+
{
|
|
22
|
+
"matches": ["<all_urls>"],
|
|
23
|
+
"js": ["content-script.js"],
|
|
24
|
+
"run_at": "document_start",
|
|
25
|
+
"match_about_blank": true
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"web_accessible_resources": [
|
|
29
|
+
{
|
|
30
|
+
"resources": ["page-bridge.js"],
|
|
31
|
+
"matches": ["<all_urls>"]
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
if (window.__AB_STEALTH_BRIDGE_INSTALLED__) return;
|
|
3
|
+
window.__AB_STEALTH_BRIDGE_INSTALLED__ = true;
|
|
4
|
+
|
|
5
|
+
const currentScript = document.currentScript;
|
|
6
|
+
const TYPE = currentScript?.dataset?.abBridgeEvent || 'AB_PAGE_BRIDGE_EVENT';
|
|
7
|
+
|
|
8
|
+
const post = (kind, payload) => {
|
|
9
|
+
try {
|
|
10
|
+
window.postMessage({ type: TYPE, kind, payload, timestamp: Date.now() }, '*');
|
|
11
|
+
} catch {
|
|
12
|
+
// Ignore post failures.
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const serializeArg = (value, depth = 0) => {
|
|
17
|
+
if (value === null || typeof value === 'undefined') return value;
|
|
18
|
+
if (typeof value === 'string') return value.slice(0, 250);
|
|
19
|
+
if (typeof value === 'number' || typeof value === 'boolean') return value;
|
|
20
|
+
if (value instanceof Error) return `${value.name}: ${value.message}`;
|
|
21
|
+
if (depth > 2) return '[depth-limit]';
|
|
22
|
+
if (Array.isArray(value)) return value.slice(0, 10).map((item) => serializeArg(item, depth + 1));
|
|
23
|
+
if (typeof value === 'object') {
|
|
24
|
+
const out = {};
|
|
25
|
+
const entries = Object.entries(value).slice(0, 12);
|
|
26
|
+
for (const [k, v] of entries) {
|
|
27
|
+
out[k] = serializeArg(v, depth + 1);
|
|
28
|
+
}
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
return String(value).slice(0, 250);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const patchConsoleMethod = (name) => {
|
|
35
|
+
const original = console[name];
|
|
36
|
+
if (typeof original !== 'function') return;
|
|
37
|
+
console[name] = function patchedConsole(...args) {
|
|
38
|
+
post('console', {
|
|
39
|
+
level: name,
|
|
40
|
+
args: args.map((arg) => serializeArg(arg)),
|
|
41
|
+
});
|
|
42
|
+
return original.apply(this, args);
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
patchConsoleMethod('error');
|
|
47
|
+
patchConsoleMethod('warn');
|
|
48
|
+
|
|
49
|
+
window.addEventListener('error', (event) => {
|
|
50
|
+
post('console', {
|
|
51
|
+
level: 'error',
|
|
52
|
+
message: event.message,
|
|
53
|
+
source: event.filename,
|
|
54
|
+
line: event.lineno,
|
|
55
|
+
column: event.colno,
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
window.addEventListener('unhandledrejection', (event) => {
|
|
60
|
+
post('console', {
|
|
61
|
+
level: 'error',
|
|
62
|
+
message: 'Unhandled rejection',
|
|
63
|
+
reason: serializeArg(event.reason),
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
if (typeof window.fetch === 'function') {
|
|
68
|
+
const originalFetch = window.fetch.bind(window);
|
|
69
|
+
window.fetch = async (...args) => {
|
|
70
|
+
const startedAt = Date.now();
|
|
71
|
+
const requestInfo = args[0];
|
|
72
|
+
const requestInit = args[1] || {};
|
|
73
|
+
const method = requestInit.method || 'GET';
|
|
74
|
+
const url = typeof requestInfo === 'string' ? requestInfo : requestInfo?.url || '';
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
const response = await originalFetch(...args);
|
|
78
|
+
post('network', {
|
|
79
|
+
transport: 'fetch',
|
|
80
|
+
method,
|
|
81
|
+
url,
|
|
82
|
+
status: response.status,
|
|
83
|
+
ok: response.ok,
|
|
84
|
+
durationMs: Date.now() - startedAt,
|
|
85
|
+
});
|
|
86
|
+
return response;
|
|
87
|
+
} catch (error) {
|
|
88
|
+
post('network', {
|
|
89
|
+
transport: 'fetch',
|
|
90
|
+
method,
|
|
91
|
+
url,
|
|
92
|
+
error: serializeArg(error),
|
|
93
|
+
durationMs: Date.now() - startedAt,
|
|
94
|
+
});
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (typeof window.XMLHttpRequest === 'function') {
|
|
101
|
+
const originalOpen = XMLHttpRequest.prototype.open;
|
|
102
|
+
const originalSend = XMLHttpRequest.prototype.send;
|
|
103
|
+
|
|
104
|
+
XMLHttpRequest.prototype.open = function patchedOpen(method, url, ...rest) {
|
|
105
|
+
this.__abRequestMeta = {
|
|
106
|
+
method: typeof method === 'string' ? method : 'GET',
|
|
107
|
+
url: typeof url === 'string' ? url : String(url || ''),
|
|
108
|
+
startedAt: Date.now(),
|
|
109
|
+
};
|
|
110
|
+
return originalOpen.call(this, method, url, ...rest);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
XMLHttpRequest.prototype.send = function patchedSend(...args) {
|
|
114
|
+
this.addEventListener('loadend', () => {
|
|
115
|
+
const meta = this.__abRequestMeta || {};
|
|
116
|
+
post('network', {
|
|
117
|
+
transport: 'xhr',
|
|
118
|
+
method: meta.method || 'GET',
|
|
119
|
+
url: meta.url || '',
|
|
120
|
+
status: this.status,
|
|
121
|
+
ok: this.status >= 200 && this.status < 400,
|
|
122
|
+
durationMs: Date.now() - (meta.startedAt || Date.now()),
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
return originalSend.apply(this, args);
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
post('lifecycle', {
|
|
130
|
+
event: 'bridge-installed',
|
|
131
|
+
href: location.href,
|
|
132
|
+
});
|
|
133
|
+
})();
|