ira-researcher 1.0.0 → 1.0.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/LICENSE CHANGED
@@ -1,21 +1,19 @@
1
- MIT License
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
2
3
 
3
- Copyright (c) 2026 bidre
4
+ Copyright (C) 2026 bidre / Neural Nexus AI
4
5
 
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
6
+ This program is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
11
10
 
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
11
+ This program is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU General Public License for more details.
14
15
 
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
16
+ You should have received a copy of the GNU General Public License
17
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
18
+
19
+ SPDX-License-Identifier: GPL-3.0-or-later
package/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  The **most powerful browser automation MCP server** — 40 tools, 7 ghost effects, stealth mode, and full DevTools access.
4
4
 
5
+ > **Powered by [Neural Nexus AI](https://neuralnexustech.com/)**
6
+
5
7
  ## ✨ Features
6
8
 
7
9
  - **40 tools** — navigation, interaction, vision, tabs, DevTools, utility, health
@@ -318,18 +320,4 @@ IRA-RESEARCHER/
318
320
  │ └── ghost/index.js ← Ghost effects
319
321
  ├── package.json
320
322
  └── README.md
321
- ```
322
-
323
- ## 📊 Comparison
324
-
325
- | Feature | Claude-in-Chrome | Browser MCP | browser-use | **IRA-RESEARCHER** |
326
- |---|---|---|---|---|
327
- | Tools | 18 | 12 | 14+20 | **40** |
328
- | Browser launch | Manual | Manual | Auto | **Auto** |
329
- | Extension needed | Yes | Yes | No | **No** |
330
- | Ghost effects | None | None | Panel | **7 effects** |
331
- | Stealth mode | No | No | Cloud | **puppeteer-extra** |
332
- | Proxy rotation | No | No | No | **Auto** |
333
- | DevTools access | Console+Net | Console | None | **9 tools** |
334
- | API keys needed | No | No | Yes | **No** |
335
- | Setup steps | 4 | 3 | 2 | **2** |
323
+ ```
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ira-researcher",
3
- "version": "1.0.0",
4
- "description": "The most powerful browser automation MCP server — 40 tools, ghost effects, stealth mode, DevTools access",
3
+ "version": "1.0.2",
4
+ "description": "The most powerful browser automation MCP server — 40 tools, ghost effects, stealth mode, DevTools access. Powered by Neural Nexus AI (https://neuralnexustech.com/)",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
7
7
  "bin": {
@@ -33,7 +33,7 @@
33
33
  "mcp-server"
34
34
  ],
35
35
  "author": "bidre",
36
- "license": "MIT",
36
+ "license": "GPL-3.0-or-later",
37
37
  "repository": {
38
38
  "type": "git",
39
39
  "url": "https://github.com/bidre/IRA-RESEARCHER.git"
@@ -45,4 +45,4 @@
45
45
  "engines": {
46
46
  "node": ">=18.0.0"
47
47
  }
48
- }
48
+ }
@@ -1,190 +0,0 @@
1
- /**
2
- * Browser launcher with stealth mode and proxy support.
3
- * Uses puppeteer-extra with stealth plugin for anti-detection.
4
- */
5
-
6
- import puppeteer from "puppeteer-extra";
7
- import StealthPlugin from "puppeteer-extra-plugin-stealth";
8
- import puppeteerVanilla from "puppeteer";
9
- import { ghostScript } from "./ghost/index.js";
10
-
11
- puppeteer.use(StealthPlugin());
12
-
13
- /**
14
- * Pick a random proxy from comma-separated list
15
- */
16
- function pickProxy(proxyEnv) {
17
- if (!proxyEnv || proxyEnv.trim() === "") return null;
18
- const rotate = process.env.IRA_PROXY_ROTATE === "true";
19
- const proxies = proxyEnv.split(",").map((p) => p.trim()).filter(Boolean);
20
- if (proxies.length === 0) return null;
21
- if (!rotate || proxies.length === 1) return proxies[0];
22
- const picked = proxies[Math.floor(Math.random() * proxies.length)];
23
- console.error(`[IRA] Auto-rotated proxy: ${picked} (from ${proxies.length} proxies)`);
24
- return picked;
25
- }
26
-
27
- /**
28
- * Launch browser with stealth + optional proxy
29
- */
30
- export async function launchBrowser() {
31
- const headless = process.env.IRA_HEADLESS !== "false";
32
- const ghost = process.env.IRA_GHOST !== "false";
33
- const debug = process.env.IRA_DEBUG === "true";
34
- const proxy = pickProxy(process.env.IRA_PROXY);
35
-
36
- console.error(`[IRA] Launching browser (headless: ${headless}, ghost: ${ghost}, debug: ${debug})`);
37
-
38
- const args = [
39
- "--no-sandbox",
40
- "--disable-setuid-sandbox",
41
- "--disable-blink-features=AutomationControlled",
42
- "--window-size=1920,1032",
43
- "--window-position=0,0",
44
- ];
45
-
46
- if (proxy) {
47
- args.push(`--proxy-server=${proxy}`);
48
- }
49
-
50
- let browser;
51
- try {
52
- browser = await puppeteer.launch({
53
- headless: !!headless,
54
- args,
55
- defaultViewport: null,
56
- });
57
- const pid = browser.process()?.pid ?? "unknown";
58
- console.error(`[IRA] Browser launched with stealth mode — PID: ${pid}, visible: ${!headless}`);
59
- // Force window to front via CDP
60
- try {
61
- const pages = await browser.pages();
62
- if (pages.length > 0) {
63
- const cdp = await pages[0].target().createCDPSession();
64
- await cdp.send('Browser.setWindowBounds', {
65
- windowId: (await cdp.send('Browser.getWindowForTarget')).windowId,
66
- bounds: { windowState: 'normal' }
67
- });
68
- await cdp.detach();
69
- }
70
- } catch (e) {
71
- console.error("[IRA] Failed to bring window to front:", e.message);
72
- }
73
- } catch (e) {
74
- console.error("[IRA] Stealth launch failed, trying vanilla puppeteer:", e.message);
75
- browser = await puppeteerVanilla.launch({
76
- headless: !!headless,
77
- args,
78
- defaultViewport: null,
79
- });
80
- const pid = browser.process()?.pid ?? "unknown";
81
- console.error(`[IRA] Browser launched (vanilla mode) — PID: ${pid}, visible: ${!headless}`);
82
- try {
83
- const pages = await browser.pages();
84
- if (pages.length > 0) {
85
- const cdp = await pages[0].target().createCDPSession();
86
- await cdp.send('Browser.setWindowBounds', {
87
- windowId: (await cdp.send('Browser.getWindowForTarget')).windowId,
88
- bounds: { windowState: 'normal' }
89
- });
90
- await cdp.detach();
91
- }
92
- } catch (e) {
93
- console.error("[IRA] Failed to bring window to front:", e.message);
94
- }
95
- }
96
-
97
- async function setupPageTracking(page) {
98
- // Inject ghost + console/network init into every page
99
- await page.evaluateOnNewDocument(ghostScript());
100
-
101
- // Node-side buffers — survive navigation, no round-trips
102
- const buffer = { logs: [], requests: [] };
103
- page.__iraBuffer = buffer;
104
-
105
- const cdp = await page.target().createCDPSession();
106
-
107
- await cdp.send("Runtime.enable");
108
- cdp.on("Runtime.consoleAPICalled", (msg) => {
109
- try {
110
- const text = msg.args.map((a) => a.value ?? "").join(" ").substring(0, 500);
111
- buffer.logs.push({ level: msg.type, text, time: Date.now() });
112
- if (buffer.logs.length > 1000) buffer.logs.shift();
113
- } catch (err) {
114
- console.error("[IRA] CDP console handler error:", err.message);
115
- }
116
- });
117
-
118
- await cdp.send("Network.enable");
119
- cdp.on("Network.requestWillBeSent", (msg) => {
120
- try {
121
- buffer.requests.push({
122
- url: msg.request.url,
123
- method: msg.request.method,
124
- requestId: msg.requestId,
125
- time: Date.now(),
126
- });
127
- if (buffer.requests.length > 500) buffer.requests.shift();
128
- } catch (err) {
129
- console.error("[IRA] CDP network handler error:", err.message);
130
- }
131
- });
132
- cdp.on("Network.responseReceived", (msg) => {
133
- try {
134
- const reqs = buffer.requests;
135
- for (let i = reqs.length - 1; i >= 0; i--) {
136
- if (reqs[i].requestId === msg.requestId) {
137
- reqs[i].status = msg.response.status;
138
- reqs[i].url = msg.response.url;
139
- break;
140
- }
141
- }
142
- } catch (err) {
143
- console.error("[IRA] CDP network response handler error:", err.message);
144
- }
145
- });
146
-
147
- // Cleanup CDP session on page close
148
- page.on("close", () => {
149
- try { cdp.detach(); } catch {}
150
- });
151
-
152
- // Page lifecycle logging
153
- page.on("domcontentloaded", () => {
154
- const u = page.url().substring(0, 120);
155
- console.error(`[IRA] Page DOM ready: ${u}`);
156
- });
157
- page.on("load", () => {
158
- const u = page.url().substring(0, 120);
159
- console.error(`[IRA] Page fully loaded: ${u}`);
160
- });
161
-
162
- if (debug) {
163
- page.on("console", (msg) => console.error(`[IRA-CONSOLE] [${msg.type()}] ${msg.text()}`));
164
- }
165
- return cdp;
166
- }
167
-
168
- // Track existing pages and capture first page's CDP session
169
- const pages = await browser.pages();
170
- let firstCdp;
171
- await Promise.all(pages.map(async (p) => {
172
- const cdp = await setupPageTracking(p);
173
- if (!firstCdp) firstCdp = cdp;
174
- }));
175
-
176
- // Track new pages (only real pages, not service workers or extensions)
177
- browser.on("targetcreated", (target) => {
178
- if (target.type() !== "page") return;
179
- (async () => {
180
- try {
181
- const page = await target.page();
182
- if (page) await setupPageTracking(page);
183
- } catch (err) {
184
- console.error("[IRA] Failed to setup tracking on new page:", err.message);
185
- }
186
- })();
187
- });
188
-
189
- return { browser, cdp: firstCdp };
190
- }
@@ -1,310 +0,0 @@
1
- /**
2
- * Ghost Effects — Injected into every page
3
- * Ported from browser-use's _DEMO_PANEL_SCRIPT with enhancements
4
- *
5
- * Effects: floating panel, click ripple, typing glow, drag path,
6
- * scroll indicators, element labels, screenshot flash
7
- */
8
-
9
- export function ghostScript() {
10
- return `
11
- (function() {
12
- if (window.__iraGhostLoaded) return;
13
- window.__iraGhostLoaded = true;
14
-
15
- const PANEL_ID = 'ira-researcher-panel';
16
- const TOGGLE_ID = 'ira-researcher-toggle';
17
- const STYLE_ID = 'ira-researcher-style';
18
-
19
- // ---- Styles ----
20
- function addStyles() {
21
- if (document.getElementById(STYLE_ID)) return;
22
- const style = document.createElement('style');
23
- style.id = STYLE_ID;
24
- style.textContent = \`
25
- #\${PANEL_ID} {
26
- position: fixed; top: 0; right: 0; width: 320px; height: 100vh;
27
- background: #05070d; color: #f8f9ff; font-family: monospace; font-size: 12px;
28
- box-shadow: -6px 0 25px rgba(0,0,0,0.35); z-index: 2147480000;
29
- border-left: 1px solid rgba(255,255,255,0.14);
30
- display: flex; flex-direction: column;
31
- transform: translateX(0); opacity: 1;
32
- transition: transform 0.25s ease, opacity 0.25s ease;
33
- }
34
- #\${PANEL_ID}[data-open="false"] {
35
- transform: translateX(110%); opacity: 0; pointer-events: none;
36
- }
37
- #\${PANEL_ID} .ira-header {
38
- padding: 12px; border-bottom: 1px solid rgba(255,255,255,0.14);
39
- display: flex; align-items: center; justify-content: space-between;
40
- }
41
- #\${PANEL_ID} .ira-header h1 { font-size: 14px; margin: 0; color: #f97316; }
42
- #\${PANEL_ID} .ira-counter { font-size: 11px; color: #888; }
43
- #\${PANEL_ID} .ira-close {
44
- width: 24px; height: 24px; border-radius: 50%; border: 1px solid rgba(255,255,255,0.2);
45
- background: transparent; color: #fff; cursor: pointer; font-size: 14px;
46
- display: flex; align-items: center; justify-content: center;
47
- }
48
- #\${PANEL_ID} .ira-close:hover { background: rgba(255,255,255,0.1); }
49
- #\${PANEL_ID} .ira-body { flex: 1; overflow-y: auto; padding: 4px 0; }
50
- .ira-entry {
51
- display: flex; gap: 8px; padding: 6px 12px;
52
- border-left: 2px solid transparent; border-bottom: 1px solid rgba(255,255,255,0.04);
53
- animation: iraFadeIn 0.2s ease;
54
- }
55
- .ira-entry.level-action { border-left-color: #34d399; }
56
- .ira-entry.level-success { border-left-color: #22c55e; }
57
- .ira-entry.level-thought { border-left-color: #f97316; }
58
- .ira-entry.level-error { border-left-color: #f87171; }
59
- .ira-entry-icon { font-size: 13px; width: 16px; }
60
- .ira-entry-text { flex: 1; color: #f8f9ff; font-size: 11px; word-break: break-word; }
61
- .ira-entry-time { font-size: 10px; color: #666; white-space: nowrap; }
62
- @keyframes iraFadeIn { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: translateY(0); } }
63
- @keyframes clickRipple {
64
- 0% { transform: scale(0.5); opacity: 0.8; }
65
- 100% { transform: scale(3); opacity: 0; }
66
- }
67
- @keyframes typingPulse {
68
- 0%, 100% { box-shadow: 0 0 5px rgba(34,197,94,0.3); }
69
- 50% { box-shadow: 0 0 15px rgba(34,197,94,0.5); }
70
- }
71
- #\${TOGGLE_ID} {
72
- position: fixed; top: 16px; right: 16px; width: 36px; height: 36px;
73
- border-radius: 50%; border: 1px solid rgba(255,255,255,0.2);
74
- background: rgba(5,7,13,0.92); color: #f97316;
75
- font-size: 16px; cursor: pointer; z-index: 2147480001;
76
- display: none; align-items: center; justify-content: center;
77
- box-shadow: 0 4px 12px rgba(0,0,0,0.4);
78
- }
79
- #\${TOGGLE_ID}:hover { transform: scale(1.05); }
80
- \`;
81
- document.head.appendChild(style);
82
- }
83
-
84
- // ---- Panel ----
85
- let panelLogs = [];
86
- let isOpen = true;
87
-
88
- function buildPanel() {
89
- const panel = document.createElement('section');
90
- panel.id = PANEL_ID;
91
- panel.setAttribute('data-open', 'true');
92
- panel.innerHTML = \`
93
- <div class="ira-header">
94
- <h1>🤖 IRA-RESEARCHER</h1>
95
- <span class="ira-counter" id="ira-counter">0</span>
96
- <button class="ira-close" id="ira-close-btn">&times;</button>
97
- </div>
98
- <div class="ira-body" id="ira-log-list"></div>
99
- \`;
100
- return panel;
101
- }
102
-
103
- function buildToggle() {
104
- const btn = document.createElement('button');
105
- btn.id = TOGGLE_ID;
106
- btn.textContent = '🤖';
107
- btn.addEventListener('click', () => openPanel());
108
- return btn;
109
- }
110
-
111
- function openPanel() {
112
- isOpen = true;
113
- const panel = document.getElementById(PANEL_ID);
114
- const toggle = document.getElementById(TOGGLE_ID);
115
- if (panel) panel.setAttribute('data-open', 'true');
116
- if (toggle) toggle.style.display = 'none';
117
- }
118
-
119
- function closePanel() {
120
- isOpen = false;
121
- const panel = document.getElementById(PANEL_ID);
122
- const toggle = document.getElementById(TOGGLE_ID);
123
- if (panel) panel.setAttribute('data-open', 'false');
124
- if (toggle) toggle.style.display = 'flex';
125
- }
126
-
127
- function addLog(level, message) {
128
- panelLogs.push({ level, message, time: Date.now() });
129
- if (panelLogs.length > 100) panelLogs.shift();
130
- const list = document.getElementById('ira-log-list');
131
- const counter = document.getElementById('ira-counter');
132
- if (!list || !counter) return;
133
- counter.textContent = panelLogs.length;
134
- const entry = document.createElement('div');
135
- entry.className = 'ira-entry level-' + level;
136
- const icons = { action: '▶️', info: 'ℹ️', thought: '💭', success: '✅', error: '❌' };
137
- entry.innerHTML = \`
138
- <span class="ira-entry-icon">\${icons[level] || 'ℹ️'}</span>
139
- <span class="ira-entry-text">\${message}</span>
140
- <span class="ira-entry-time">just now</span>
141
- \`;
142
- list.appendChild(entry);
143
- list.scrollTop = list.scrollHeight;
144
- }
145
-
146
- // ---- Click Ripple ----
147
- function clickRipple(x, y) {
148
- const dot = document.createElement('div');
149
- dot.style.cssText = \`
150
- position: fixed; left: \${x - 15}px; top: \${y - 15}px;
151
- width: 30px; height: 30px; border-radius: 50%;
152
- background: rgb(249, 115, 22); opacity: 0.6;
153
- pointer-events: none; z-index: 2147483647;
154
- animation: clickRipple 0.5s ease-out forwards;
155
- \`;
156
- document.body.appendChild(dot);
157
- setTimeout(() => dot.remove(), 600);
158
-
159
- // Coordinate label
160
- const label = document.createElement('div');
161
- label.style.cssText = \`
162
- position: fixed; left: \${x + 12}px; top: \${y - 12}px;
163
- padding: 2px 6px; background: rgba(0,0,0,0.7); color: #f97316;
164
- font-size: 10px; font-family: monospace; border-radius: 3px;
165
- pointer-events: none; z-index: 2147483647;
166
- opacity: 0; transition: opacity 0.3s ease;
167
- \`;
168
- label.textContent = \`(\${Math.round(x)}, \${Math.round(y)})\`;
169
- document.body.appendChild(label);
170
- requestAnimationFrame(() => label.style.opacity = '1');
171
- setTimeout(() => { label.style.opacity = '0'; setTimeout(() => label.remove(), 300); }, 1200);
172
- }
173
-
174
- // ---- Typing Glow ----
175
- function typingGlow() {
176
- const active = document.activeElement;
177
- if (!active) return;
178
- const origBoxShadow = active.style.boxShadow;
179
- active.style.boxShadow = '0 0 15px rgba(34,197,94,0.5)';
180
- active.style.transition = 'box-shadow 0.3s ease';
181
- setTimeout(() => { active.style.boxShadow = origBoxShadow || 'none'; }, 1500);
182
- }
183
-
184
- // ---- Drag Path ----
185
- function dragPath(fromX, fromY, toX, toY) {
186
- const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
187
- svg.setAttribute('style', \`
188
- position: fixed; top: 0; left: 0; width: 100%; height: 100%;
189
- pointer-events: none; z-index: 2147483646;
190
- \`);
191
- const defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
192
- const marker = document.createElementNS('http://www.w3.org/2000/svg', 'marker');
193
- marker.setAttribute('id', 'arrowhead');
194
- marker.setAttribute('markerWidth', '10');
195
- marker.setAttribute('markerHeight', '7');
196
- marker.setAttribute('refX', '10');
197
- marker.setAttribute('refY', '3.5');
198
- marker.setAttribute('orient', 'auto');
199
- marker.innerHTML = '<polygon points="0 0, 10 3.5, 0 7" fill="#ef4444" />';
200
- defs.appendChild(marker);
201
- svg.appendChild(defs);
202
- const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
203
- const angle = Math.atan2(toY - fromY, toX - fromX) * 180 / Math.PI;
204
- line.setAttribute('x1', fromX);
205
- line.setAttribute('y1', fromY);
206
- line.setAttribute('x2', toX);
207
- line.setAttribute('y2', toY);
208
- line.setAttribute('stroke', '#ef4444');
209
- line.setAttribute('stroke-width', '2');
210
- line.setAttribute('stroke-dasharray', '8,4');
211
- line.setAttribute('marker-end', 'url(#arrowhead)');
212
- svg.appendChild(line);
213
- document.body.appendChild(svg);
214
- setTimeout(() => svg.remove(), 1500);
215
- }
216
-
217
- // ---- Scroll Indicator ----
218
- function scrollIndicator(direction, amount) {
219
- const el = document.createElement('div');
220
- const arrow = direction === 'up' ? '↑' : direction === 'down' ? '↓' : direction === 'left' ? '←' : '→';
221
- el.style.cssText = \`
222
- position: fixed; \${direction === 'up' || direction === 'down' ? 'right: 30px;' : 'bottom: 50%; transform: translateY(50%);'}
223
- \${direction === 'up' ? 'top: 20px;' : direction === 'down' ? 'bottom: 20px;' : ''}
224
- \${direction === 'left' ? 'left: 20px;' : direction === 'right' ? 'right: 20px;' : ''}
225
- padding: 8px 16px; background: rgba(0,0,0,0.6); color: #f97316;
226
- font-size: 24px; font-family: monospace; border-radius: 8px;
227
- pointer-events: none; z-index: 2147483647;
228
- opacity: 1; transition: opacity 0.5s ease;
229
- \`;
230
- el.textContent = \`\${arrow} \${Math.round(amount)}px\`;
231
- document.body.appendChild(el);
232
- setTimeout(() => { el.style.opacity = '0'; setTimeout(() => el.remove(), 500); }, 1000);
233
- }
234
-
235
- // ---- Screenshot Flash ----
236
- function screenshotFlash() {
237
- const flash = document.createElement('div');
238
- flash.style.cssText = \`
239
- position: fixed; top: 0; left: 0; width: 100%; height: 100%;
240
- background: white; opacity: 0; pointer-events: none; z-index: 2147483647;
241
- animation: iraFadeIn 0.1s ease forwards;
242
- \`;
243
- document.body.appendChild(flash);
244
- requestAnimationFrame(() => {
245
- flash.style.transition = 'opacity 0.3s ease';
246
- flash.style.opacity = '0.2';
247
- setTimeout(() => { flash.style.opacity = '0'; setTimeout(() => flash.remove(), 300); }, 100);
248
- });
249
- const label = document.createElement('div');
250
- label.style.cssText = \`
251
- position: fixed; top: 20px; right: 60px; padding: 4px 10px;
252
- background: rgba(0,0,0,0.6); color: #fff; font-size: 14px; border-radius: 6px;
253
- pointer-events: none; z-index: 2147483647; opacity: 0; transition: opacity 0.3s ease;
254
- \`;
255
- label.textContent = '📸 Screenshot';
256
- document.body.appendChild(label);
257
- requestAnimationFrame(() => label.style.opacity = '1');
258
- setTimeout(() => { label.style.opacity = '0'; setTimeout(() => label.remove(), 300); }, 1000);
259
- }
260
-
261
- // ---- Listen for custom events from Puppeteer ----
262
- window.addEventListener('ira-ghost', function(e) {
263
- const detail = e.detail;
264
- if (!detail) return;
265
- switch (detail.type) {
266
- case 'log': addLog(detail.level || 'info', detail.message); break;
267
- case 'ripple': clickRipple(detail.x, detail.y); break;
268
- case 'typing': typingGlow(); break;
269
- case 'drag': dragPath(detail.fromX, detail.fromY, detail.toX, detail.toY); break;
270
- case 'scroll': scrollIndicator(detail.direction, detail.amount); break;
271
- case 'flash': screenshotFlash(); break;
272
- }
273
- });
274
-
275
- // ---- Keyboard shortcut to toggle ----
276
- document.addEventListener('keydown', function(e) {
277
- if (e.ctrlKey && e.shiftKey && e.key === 'I') {
278
- e.preventDefault();
279
- if (isOpen) closePanel(); else openPanel();
280
- }
281
- });
282
-
283
- // ---- Init ----
284
- function init() {
285
- if (document.getElementById(PANEL_ID)) return;
286
- addStyles();
287
- document.body.appendChild(buildPanel());
288
- document.body.appendChild(buildToggle());
289
- document.getElementById('ira-close-btn')?.addEventListener('click', closePanel);
290
- addLog('info', 'IRA-RESEARCHER connected');
291
- }
292
-
293
- if (document.readyState === 'loading') {
294
- document.addEventListener('DOMContentLoaded', init);
295
- } else {
296
- init();
297
- }
298
-
299
- // ---- Expose API ----
300
- window.__iraGhost = {
301
- addLog,
302
- clickRipple,
303
- typingGlow,
304
- dragPath,
305
- scrollIndicator,
306
- screenshotFlash,
307
- };
308
- })();
309
- `;
310
- }
package/test-prompt.md DELETED
@@ -1,112 +0,0 @@
1
- # Open Claude in Chrome — Integration Test Prompt
2
-
3
- Copy and paste everything below the line into a new Claude Code session that has the `open-claude-in-chrome` MCP configured.
4
-
5
- ---
6
-
7
- You are running an integration test for a Chrome browser automation extension. Your job is to execute every step below and use the specific MCP tool listed for each step. Do NOT skip any tool — every single one must be called at least once.
8
-
9
- Execute these steps in order:
10
-
11
- ## Tab Management
12
- 1. Call `tabs_context_mcp` with `createIfEmpty: true` to get current tabs.
13
- 2. Call `tabs_create_mcp` to create a new tab. Record its tab ID as TAB_1.
14
- 3. Call `tabs_create_mcp` again to create a second tab. Record its tab ID as TAB_2.
15
-
16
- ## Navigation + Window
17
- 4. Call `navigate` to go to `https://www.reddit.com` in TAB_1. (This domain is blocked in the official Claude in Chrome extension — if this works, the unblocked extension is functioning.)
18
- 5. Call `resize_window` to set the window to 1280x800 using TAB_1.
19
-
20
- ## Screenshots + Reading
21
- 6. Call `computer` with action `screenshot` on TAB_1.
22
- 7. Call `read_page` on TAB_1 with filter `"all"` to get the accessibility tree.
23
- 8. Call `get_page_text` on TAB_1 to extract the text content.
24
-
25
- ## Finding + Form Input
26
- 9. Call `find` with query `"search"` on TAB_1 to locate the search input.
27
- 10. Call `form_input` on TAB_1 using the ref from step 9 to set the search value to `"MCP server"`.
28
-
29
- ## Mouse + Keyboard Actions
30
- 11. Call `computer` with action `left_click` on TAB_1 at the coordinates of the search input (from step 9).
31
- 12. Call `computer` with action `key` on TAB_1 with text `"Enter"` to submit the search.
32
- 13. Call `computer` with action `wait` on TAB_1 with duration `3` to wait for results.
33
- 14. Call `computer` with action `scroll` on TAB_1 with scroll_direction `"down"` and scroll_amount `3` at coordinates `[640, 400]`.
34
- 15. Call `computer` with action `hover` on TAB_1 at coordinates `[640, 300]`.
35
- 16. Call `computer` with action `double_click` on TAB_1 at coordinates `[640, 300]`.
36
- 17. Call `computer` with action `triple_click` on TAB_1 at coordinates `[640, 300]`.
37
- 18. Call `computer` with action `right_click` on TAB_1 at coordinates `[640, 300]`.
38
- 19. Call `computer` with action `type` on TAB_1 with text `"integration test"`.
39
- 20. Call `computer` with action `scroll_to` on TAB_1 at coordinates `[0, 0]`.
40
-
41
- ## JavaScript + Debug
42
- 21. Call `javascript_tool` on TAB_1 with action `"javascript_exec"` and text `"document.querySelectorAll('a').length"`.
43
- 22. Call `read_console_messages` on TAB_1 with pattern `".*"`.
44
- 23. Call `read_network_requests` on TAB_1.
45
-
46
- ## Second Tab
47
- 24. Call `navigate` to go to `https://news.ycombinator.com` in TAB_2.
48
- 25. Call `computer` with action `screenshot` on TAB_2.
49
-
50
- ## Drag
51
- 26. Call `computer` with action `left_click_drag` on TAB_1 with start_coordinate `[100, 300]` and coordinate `[400, 300]`.
52
-
53
- ## Zoom
54
- 27. Call `computer` with action `zoom` on TAB_1 with region `[0, 0, 640, 400]`.
55
-
56
- ## Stub Tools (call them — they should return gracefully)
57
- 28. Call `gif_creator` with action `"start_recording"` on TAB_1.
58
- 29. Call `shortcuts_list` on TAB_1.
59
- 30. Call `shortcuts_execute` on TAB_1 with command `"test"`.
60
- 31. Call `switch_browser`.
61
-
62
- ## Plan + Upload
63
- 32. Call `update_plan` with domains `["reddit.com", "news.ycombinator.com"]` and approach `["Testing browser automation", "Validating all tools", "Checking parity with Claude in Chrome"]`.
64
- 33. Call `upload_image` on TAB_1 with the imageId from your first screenshot (step 6), using ref from step 9.
65
-
66
- ## Validation
67
-
68
- Now produce a final report. List every one of the 18 tools below and whether you successfully called it during this test. Use this exact format:
69
-
70
- ```
71
- INTEGRATION TEST RESULTS
72
- ========================
73
- 1. tabs_context_mcp : [PASS/FAIL] — step(s) used
74
- 2. tabs_create_mcp : [PASS/FAIL] — step(s) used
75
- 3. navigate : [PASS/FAIL] — step(s) used
76
- 4. computer : [PASS/FAIL] — step(s) used (list actions exercised)
77
- 5. find : [PASS/FAIL] — step(s) used
78
- 6. form_input : [PASS/FAIL] — step(s) used
79
- 7. get_page_text : [PASS/FAIL] — step(s) used
80
- 8. gif_creator : [PASS/FAIL] — step(s) used
81
- 9. javascript_tool : [PASS/FAIL] — step(s) used
82
- 10. read_console_messages : [PASS/FAIL] — step(s) used
83
- 11. read_network_requests : [PASS/FAIL] — step(s) used
84
- 12. read_page : [PASS/FAIL] — step(s) used
85
- 13. resize_window : [PASS/FAIL] — step(s) used
86
- 14. shortcuts_list : [PASS/FAIL] — step(s) used
87
- 15. shortcuts_execute : [PASS/FAIL] — step(s) used
88
- 16. switch_browser : [PASS/FAIL] — step(s) used
89
- 17. update_plan : [PASS/FAIL] — step(s) used
90
- 18. upload_image : [PASS/FAIL] — step(s) used
91
-
92
- Computer actions exercised:
93
- - screenshot: [PASS/FAIL]
94
- - left_click: [PASS/FAIL]
95
- - right_click: [PASS/FAIL]
96
- - double_click: [PASS/FAIL]
97
- - triple_click: [PASS/FAIL]
98
- - hover: [PASS/FAIL]
99
- - type: [PASS/FAIL]
100
- - key: [PASS/FAIL]
101
- - scroll: [PASS/FAIL]
102
- - scroll_to: [PASS/FAIL]
103
- - wait: [PASS/FAIL]
104
- - left_click_drag: [PASS/FAIL]
105
- - zoom: [PASS/FAIL]
106
-
107
- OVERALL: [PASS if all 18 tools called / FAIL if any missed]
108
- ```
109
-
110
- Mark a tool as PASS only if you actually called it and received a response (even if the response was "not implemented" for stub tools). Mark it as FAIL if you skipped it or encountered an error that prevented the call.
111
-
112
- If OVERALL is FAIL, list which tools failed and why.