- Rebased onto upstream/main (v0.24.0, full Rust native) - Renamed package to agent-browser-stealth, version 0.24.0-fork.1 - Preserved fork-specific: abs alias, extensions/tab-group-cdp, .husky hooks - Removed upstream-only: docs/, packages/dashboard, examples/, benchmarks/ - Simplified pnpm workspace to root-only - Added [[bin]] section to keep binary name as "agent-browser" Track 1 of native-stealth migration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
134 lines
4.0 KiB
JavaScript
134 lines
4.0 KiB
JavaScript
(() => {
|
|
if (window.__AB_STEALTH_BRIDGE_INSTALLED__) return;
|
|
window.__AB_STEALTH_BRIDGE_INSTALLED__ = true;
|
|
|
|
const currentScript = document.currentScript;
|
|
const TYPE = currentScript?.dataset?.abBridgeEvent || 'AB_PAGE_BRIDGE_EVENT';
|
|
|
|
const post = (kind, payload) => {
|
|
try {
|
|
window.postMessage({ type: TYPE, kind, payload, timestamp: Date.now() }, '*');
|
|
} catch {
|
|
// Ignore post failures.
|
|
}
|
|
};
|
|
|
|
const serializeArg = (value, depth = 0) => {
|
|
if (value === null || typeof value === 'undefined') return value;
|
|
if (typeof value === 'string') return value.slice(0, 250);
|
|
if (typeof value === 'number' || typeof value === 'boolean') return value;
|
|
if (value instanceof Error) return `${value.name}: ${value.message}`;
|
|
if (depth > 2) return '[depth-limit]';
|
|
if (Array.isArray(value)) return value.slice(0, 10).map((item) => serializeArg(item, depth + 1));
|
|
if (typeof value === 'object') {
|
|
const out = {};
|
|
const entries = Object.entries(value).slice(0, 12);
|
|
for (const [k, v] of entries) {
|
|
out[k] = serializeArg(v, depth + 1);
|
|
}
|
|
return out;
|
|
}
|
|
return String(value).slice(0, 250);
|
|
};
|
|
|
|
const patchConsoleMethod = (name) => {
|
|
const original = console[name];
|
|
if (typeof original !== 'function') return;
|
|
console[name] = function patchedConsole(...args) {
|
|
post('console', {
|
|
level: name,
|
|
args: args.map((arg) => serializeArg(arg)),
|
|
});
|
|
return original.apply(this, args);
|
|
};
|
|
};
|
|
|
|
patchConsoleMethod('error');
|
|
patchConsoleMethod('warn');
|
|
|
|
window.addEventListener('error', (event) => {
|
|
post('console', {
|
|
level: 'error',
|
|
message: event.message,
|
|
source: event.filename,
|
|
line: event.lineno,
|
|
column: event.colno,
|
|
});
|
|
});
|
|
|
|
window.addEventListener('unhandledrejection', (event) => {
|
|
post('console', {
|
|
level: 'error',
|
|
message: 'Unhandled rejection',
|
|
reason: serializeArg(event.reason),
|
|
});
|
|
});
|
|
|
|
if (typeof window.fetch === 'function') {
|
|
const originalFetch = window.fetch.bind(window);
|
|
window.fetch = async (...args) => {
|
|
const startedAt = Date.now();
|
|
const requestInfo = args[0];
|
|
const requestInit = args[1] || {};
|
|
const method = requestInit.method || 'GET';
|
|
const url = typeof requestInfo === 'string' ? requestInfo : requestInfo?.url || '';
|
|
|
|
try {
|
|
const response = await originalFetch(...args);
|
|
post('network', {
|
|
transport: 'fetch',
|
|
method,
|
|
url,
|
|
status: response.status,
|
|
ok: response.ok,
|
|
durationMs: Date.now() - startedAt,
|
|
});
|
|
return response;
|
|
} catch (error) {
|
|
post('network', {
|
|
transport: 'fetch',
|
|
method,
|
|
url,
|
|
error: serializeArg(error),
|
|
durationMs: Date.now() - startedAt,
|
|
});
|
|
throw error;
|
|
}
|
|
};
|
|
}
|
|
|
|
if (typeof window.XMLHttpRequest === 'function') {
|
|
const originalOpen = XMLHttpRequest.prototype.open;
|
|
const originalSend = XMLHttpRequest.prototype.send;
|
|
|
|
XMLHttpRequest.prototype.open = function patchedOpen(method, url, ...rest) {
|
|
this.__abRequestMeta = {
|
|
method: typeof method === 'string' ? method : 'GET',
|
|
url: typeof url === 'string' ? url : String(url || ''),
|
|
startedAt: Date.now(),
|
|
};
|
|
return originalOpen.call(this, method, url, ...rest);
|
|
};
|
|
|
|
XMLHttpRequest.prototype.send = function patchedSend(...args) {
|
|
this.addEventListener('loadend', () => {
|
|
const meta = this.__abRequestMeta || {};
|
|
post('network', {
|
|
transport: 'xhr',
|
|
method: meta.method || 'GET',
|
|
url: meta.url || '',
|
|
status: this.status,
|
|
ok: this.status >= 200 && this.status < 400,
|
|
durationMs: Date.now() - (meta.startedAt || Date.now()),
|
|
});
|
|
});
|
|
return originalSend.apply(this, args);
|
|
};
|
|
}
|
|
|
|
post('lifecycle', {
|
|
event: 'bridge-installed',
|
|
href: location.href,
|
|
});
|
|
})();
|