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 +15 -17
- package/README.md +3 -15
- package/package.json +4 -4
- package/src/browser_old.js +0 -190
- package/src/ghost/index_old.js +0 -310
- package/test-prompt.md +0 -112
package/LICENSE
CHANGED
|
@@ -1,21 +1,19 @@
|
|
|
1
|
-
|
|
1
|
+
GNU GENERAL PUBLIC LICENSE
|
|
2
|
+
Version 3, 29 June 2007
|
|
2
3
|
|
|
3
|
-
Copyright (
|
|
4
|
+
Copyright (C) 2026 bidre / Neural Nexus AI
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
13
|
-
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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.
|
|
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": "
|
|
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
|
+
}
|
package/src/browser_old.js
DELETED
|
@@ -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
|
-
}
|
package/src/ghost/index_old.js
DELETED
|
@@ -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">×</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.
|