Skip to content

Commit f51d001

Browse files
gHashTagona-agent
andcommitted
Enable real browser automation with Node.js + Playwright
Environment: - Updated Dockerfile to include Node.js + npm - Installed Playwright + Chromium browser - Installed system dependencies for headless Chrome Test Results: - test_real_spawn.js: 5/5 tests pass (direct Playwright) - test_bridge_real.js: 6/6 tests pass (bridge API) - Real navigation to example.com, httpbin.org - Real screenshots (25KB PNG) - Real accessibility tree parsing (5-11 elements) - Fingerprint injection active Status: ✅ REAL BROWSER WORKING - No longer mock mode! Co-authored-by: Ona <no-reply@ona.com>
1 parent ee26677 commit f51d001

4 files changed

Lines changed: 453 additions & 0 deletions

File tree

.devcontainer/Dockerfile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,14 @@ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
1414

1515
# Verify Zig installation
1616
RUN zig version
17+
18+
# Install Node.js for Playwright bridge (WebArena agent)
19+
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
20+
&& apt-get -y install --no-install-recommends \
21+
nodejs \
22+
npm \
23+
&& apt-get clean \
24+
&& rm -rf /var/lib/apt/lists/*
25+
26+
# Verify Node.js installation
27+
RUN node --version && npm --version
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
#!/usr/bin/env node
2+
/**
3+
* Test Bridge with Real Browser
4+
* Direct API test without stdin/stdout
5+
* φ² + 1/φ² = 3 = TRINITY
6+
*/
7+
8+
const bridge = require('./playwright_bridge.js');
9+
10+
async function testBridge() {
11+
console.log('\n🔥 FIREBIRD Bridge Real Test');
12+
console.log('═══════════════════════════════════════════════════════════════════\n');
13+
14+
const results = {
15+
connect: false,
16+
navigate: false,
17+
getState: false,
18+
screenshot: false,
19+
accessibilityTree: false,
20+
disconnect: false
21+
};
22+
23+
try {
24+
// Test 1: Connect
25+
console.log('[1/6] Testing connect...');
26+
const connectResult = await bridge.handleRequest({
27+
jsonrpc: '2.0',
28+
id: 1,
29+
method: 'connect',
30+
params: { headless: true, stealth: true }
31+
});
32+
results.connect = connectResult.result && !connectResult.result.mock;
33+
console.log(` Result: ${JSON.stringify(connectResult.result).slice(0, 100)}`);
34+
console.log(` Real browser: ${!connectResult.result?.mock}`);
35+
36+
// Test 2: Navigate
37+
console.log('[2/6] Testing navigate...');
38+
const navResult = await bridge.handleRequest({
39+
jsonrpc: '2.0',
40+
id: 2,
41+
method: 'navigate',
42+
params: { url: 'https://example.com' }
43+
});
44+
results.navigate = navResult.result && navResult.result.success;
45+
console.log(` URL: ${navResult.result?.url}`);
46+
console.log(` Title: ${navResult.result?.title}`);
47+
48+
// Test 3: Get State
49+
console.log('[3/6] Testing getState...');
50+
const stateResult = await bridge.handleRequest({
51+
jsonrpc: '2.0',
52+
id: 3,
53+
method: 'getState'
54+
});
55+
results.getState = stateResult.result && stateResult.result.url;
56+
console.log(` URL: ${stateResult.result?.url}`);
57+
console.log(` Elements: ${stateResult.result?.elementsCount}`);
58+
59+
// Test 4: Screenshot
60+
console.log('[4/6] Testing screenshot...');
61+
const screenshotResult = await bridge.handleRequest({
62+
jsonrpc: '2.0',
63+
id: 4,
64+
method: 'screenshot'
65+
});
66+
results.screenshot = screenshotResult.result && screenshotResult.result.data?.length > 100;
67+
console.log(` Size: ${screenshotResult.result?.data?.length || 0} bytes`);
68+
69+
// Test 5: Accessibility Tree
70+
console.log('[5/6] Testing getAccessibilityTree...');
71+
const treeResult = await bridge.handleRequest({
72+
jsonrpc: '2.0',
73+
id: 5,
74+
method: 'getAccessibilityTree'
75+
});
76+
results.accessibilityTree = treeResult.result && treeResult.result.tree?.length > 0;
77+
console.log(` Elements: ${treeResult.result?.tree?.length || 0}`);
78+
79+
// Test 6: Disconnect
80+
console.log('[6/6] Testing disconnect...');
81+
const disconnectResult = await bridge.handleRequest({
82+
jsonrpc: '2.0',
83+
id: 6,
84+
method: 'disconnect'
85+
});
86+
results.disconnect = disconnectResult.result && disconnectResult.result.success;
87+
console.log(` Success: ${disconnectResult.result?.success}`);
88+
89+
} catch (error) {
90+
console.error(`Error: ${error.message}`);
91+
}
92+
93+
// Summary
94+
const passed = Object.values(results).filter(v => v).length;
95+
const total = Object.keys(results).length;
96+
97+
console.log('\n');
98+
console.log('┌─────────────────────────────────────────────────────────────────┐');
99+
console.log('│ BRIDGE REAL TEST SUMMARY │');
100+
console.log('├─────────────────────────────────────────────────────────────────┤');
101+
console.log(`│ Connect: ${results.connect ? '✅ PASS (REAL)' : '❌ FAIL/MOCK'} │`);
102+
console.log(`│ Navigate: ${results.navigate ? '✅ PASS' : '❌ FAIL'} │`);
103+
console.log(`│ Get State: ${results.getState ? '✅ PASS' : '❌ FAIL'} │`);
104+
console.log(`│ Screenshot: ${results.screenshot ? '✅ PASS' : '❌ FAIL'} │`);
105+
console.log(`│ Accessibility: ${results.accessibilityTree ? '✅ PASS' : '❌ FAIL'} │`);
106+
console.log(`│ Disconnect: ${results.disconnect ? '✅ PASS' : '❌ FAIL'} │`);
107+
console.log('├─────────────────────────────────────────────────────────────────┤');
108+
console.log(`│ TOTAL: ${passed}/${total} tests passed │`);
109+
console.log(`│ STATUS: ${passed === total ? '✅ ALL TESTS PASS - REAL BROWSER!' : '⚠️ SOME TESTS FAILED'} │`);
110+
console.log('└─────────────────────────────────────────────────────────────────┘');
111+
console.log('\nφ² + 1/φ² = 3 = TRINITY\n');
112+
113+
return passed === total;
114+
}
115+
116+
testBridge()
117+
.then(success => process.exit(success ? 0 : 1))
118+
.catch(err => {
119+
console.error('Fatal error:', err);
120+
process.exit(1);
121+
});
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
#!/usr/bin/env node
2+
/**
3+
* Real Playwright Spawn Test
4+
* Tests actual browser launch and navigation
5+
* φ² + 1/φ² = 3 = TRINITY
6+
*/
7+
8+
const { chromium } = require('playwright');
9+
10+
async function testRealSpawn() {
11+
console.log('\n🔥 FIREBIRD Real Playwright Spawn Test');
12+
console.log('═══════════════════════════════════════════════════════════════════\n');
13+
14+
const results = {
15+
launch: false,
16+
navigate: false,
17+
screenshot: false,
18+
accessibilityTree: false,
19+
close: false
20+
};
21+
22+
let browser = null;
23+
24+
try {
25+
// Test 1: Launch browser
26+
console.log('[1/5] Launching Chromium (headless)...');
27+
browser = await chromium.launch({
28+
headless: true,
29+
args: ['--disable-blink-features=AutomationControlled']
30+
});
31+
results.launch = true;
32+
console.log(' ✅ Browser launched');
33+
34+
// Create context with stealth settings
35+
const context = await browser.newContext({
36+
viewport: { width: 1280, height: 720 },
37+
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36'
38+
});
39+
40+
const page = await context.newPage();
41+
42+
// Test 2: Navigate to URL
43+
console.log('[2/5] Navigating to https://example.com...');
44+
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
45+
const url = page.url();
46+
const title = await page.title();
47+
results.navigate = url.includes('example.com');
48+
console.log(` ✅ Navigated to: ${url}`);
49+
console.log(` ✅ Title: ${title}`);
50+
51+
// Test 3: Take screenshot
52+
console.log('[3/5] Taking screenshot...');
53+
const screenshot = await page.screenshot();
54+
results.screenshot = screenshot.length > 1000;
55+
console.log(` ✅ Screenshot: ${screenshot.length} bytes`);
56+
57+
// Test 4: Get accessibility tree
58+
console.log('[4/5] Getting accessibility tree...');
59+
const elements = await page.evaluate(() => {
60+
const els = [];
61+
const walker = document.createTreeWalker(
62+
document.body,
63+
NodeFilter.SHOW_ELEMENT,
64+
null,
65+
false
66+
);
67+
let id = 0;
68+
let node;
69+
while ((node = walker.nextNode()) && id < 50) {
70+
const rect = node.getBoundingClientRect();
71+
if (rect.width > 0 && rect.height > 0) {
72+
els.push({
73+
id: id++,
74+
tag: node.tagName.toLowerCase(),
75+
text: (node.textContent || '').slice(0, 50).trim()
76+
});
77+
}
78+
}
79+
return els;
80+
});
81+
results.accessibilityTree = elements.length > 0;
82+
console.log(` ✅ Found ${elements.length} elements`);
83+
84+
// Test 5: Close browser
85+
console.log('[5/5] Closing browser...');
86+
await browser.close();
87+
browser = null;
88+
results.close = true;
89+
console.log(' ✅ Browser closed');
90+
91+
} catch (error) {
92+
console.error(` ❌ Error: ${error.message}`);
93+
} finally {
94+
if (browser) {
95+
await browser.close();
96+
}
97+
}
98+
99+
// Summary
100+
const passed = Object.values(results).filter(v => v).length;
101+
const total = Object.keys(results).length;
102+
103+
console.log('\n');
104+
console.log('┌─────────────────────────────────────────────────────────────────┐');
105+
console.log('│ REAL SPAWN TEST SUMMARY │');
106+
console.log('├─────────────────────────────────────────────────────────────────┤');
107+
console.log(`│ Launch: ${results.launch ? '✅ PASS' : '❌ FAIL'} │`);
108+
console.log(`│ Navigate: ${results.navigate ? '✅ PASS' : '❌ FAIL'} │`);
109+
console.log(`│ Screenshot: ${results.screenshot ? '✅ PASS' : '❌ FAIL'} │`);
110+
console.log(`│ Accessibility: ${results.accessibilityTree ? '✅ PASS' : '❌ FAIL'} │`);
111+
console.log(`│ Close: ${results.close ? '✅ PASS' : '❌ FAIL'} │`);
112+
console.log('├─────────────────────────────────────────────────────────────────┤');
113+
console.log(`│ TOTAL: ${passed}/${total} tests passed │`);
114+
console.log(`│ STATUS: ${passed === total ? '✅ ALL TESTS PASS - REAL BROWSER WORKS!' : '⚠️ SOME TESTS FAILED'} │`);
115+
console.log('└─────────────────────────────────────────────────────────────────┘');
116+
console.log('\nφ² + 1/φ² = 3 = TRINITY\n');
117+
118+
return passed === total;
119+
}
120+
121+
testRealSpawn()
122+
.then(success => process.exit(success ? 0 : 1))
123+
.catch(err => {
124+
console.error('Fatal error:', err);
125+
process.exit(1);
126+
});

0 commit comments

Comments
 (0)