@agent360/browser-mcp 1.29.0 → 1.29.1
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 +161 -46
- package/bin/cli.js +99 -21
- package/extension/background.js +1210 -192
- package/extension/manifest.json +1 -1
- package/extension/offscreen.js +62 -5
- package/extension/popup.html +9 -4
- package/extension/popup.js +5 -5
- package/index.js +251 -118
- package/package.json +3 -3
- package/tools.js +16 -16
- package/vagt.js +3 -3
package/extension/manifest.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"name": "Agent360 Browser MCP",
|
|
4
|
-
"version": "1.29.
|
|
4
|
+
"version": "1.29.1",
|
|
5
5
|
"description": "Give Claude Code your real logged-in Chrome. Two parts: this extension + one npx command. Multi-session, local, MIT.",
|
|
6
6
|
"permissions": [
|
|
7
7
|
"tabs",
|
package/extension/offscreen.js
CHANGED
|
@@ -14,12 +14,54 @@
|
|
|
14
14
|
// samtidig den rigtige semantik: et dokument oprettet af en aeldre udgave baerer
|
|
15
15
|
// den aeldre version, saa `offscreenSvarer()` kan se forskel paa "svarer" og
|
|
16
16
|
// "er den udgave vi koerer nu".
|
|
17
|
+
// Plan 1.10 / R2 (Astra): versionsnummeret beviser ikke hvilken KODE der koerer - to kopier med samme nummer kan vaere
|
|
18
|
+
// forskellige, og udgivelsens flowtest kunne derfor godkendes mod gammel kode. Udvidelsen sender derfor et fingeraftryk af
|
|
19
|
+
// sin egen background.js. Kun et hash, og kun til 127.0.0.1: ingen kode forlader maskinen.
|
|
20
|
+
// MAALT 12/9: aftrykket daekkede kun background.js. Samme dag aendrede offscreen.js sig, og den aendring var USYNLIG for
|
|
21
|
+
// udgivelsens spaerre - en gammel kopi af broen kunne passere som kandidaten. Begge kodefiler taeller nu med, i fast
|
|
22
|
+
// raekkefoelge. Det er stadig en byggekontrol: filerne laeses fra den indlaeste udvidelses egen mappe, saa aftrykket
|
|
23
|
+
// beviser hvilke FILER der er indlaest - ikke hvilken kode der koerer i et gammelt offscreen-dokument.
|
|
24
|
+
const KODEFILER = ['background.js', 'offscreen.js'];
|
|
25
|
+
let kodeAftrykCache = null;
|
|
26
|
+
async function kodeAftryk() {
|
|
27
|
+
if (kodeAftrykCache) return kodeAftrykCache;
|
|
28
|
+
try {
|
|
29
|
+
const dele = [];
|
|
30
|
+
for (const fil of KODEFILER) {
|
|
31
|
+
const svar = await fetch(chrome.runtime.getURL(fil));
|
|
32
|
+
dele.push(new Uint8Array(await svar.arrayBuffer()));
|
|
33
|
+
}
|
|
34
|
+
const samlet = new Uint8Array(dele.reduce((n, d) => n + d.length, 0));
|
|
35
|
+
let i = 0;
|
|
36
|
+
for (const d of dele) { samlet.set(d, i); i += d.length; }
|
|
37
|
+
const bytes = new Uint8Array(await crypto.subtle.digest('SHA-256', samlet));
|
|
38
|
+
kodeAftrykCache = Array.from(bytes.slice(0, 6)).map((b) => b.toString(16).padStart(2, '0')).join('');
|
|
39
|
+
} catch (e) {
|
|
40
|
+
console.warn('[Offscreen] kunne ikke beregne kode-aftrykket:', e?.message || e);
|
|
41
|
+
}
|
|
42
|
+
return kodeAftrykCache;
|
|
43
|
+
}
|
|
44
|
+
|
|
17
45
|
function minVersion() {
|
|
18
46
|
try { return new URLSearchParams(location.search).get('v') || null; } catch { return null; }
|
|
19
47
|
}
|
|
20
48
|
|
|
21
|
-
|
|
22
|
-
|
|
49
|
+
// Portomraadet kommer fra dokumentets egen adresse, sat af background.js ud fra chrome.storage.local.
|
|
50
|
+
// Standarden er 9876-9895. En isoleret testbrowser kan flytte den UDEN at repoets filer aendrer sig, saa
|
|
51
|
+
// kode-aftrykket - og dermed udgivelsens spaerre - er uroert. Vagten staar begge steder: en vaerdi er kun gyldig
|
|
52
|
+
// hvis den er to tal i et fornuftigt spaend. (Astra 12/9.)
|
|
53
|
+
function portOmraade() {
|
|
54
|
+
try {
|
|
55
|
+
const v = new URLSearchParams(location.search).get('porte');
|
|
56
|
+
const m = /^(\d{4,5})-(\d{4,5})$/.exec(v || '');
|
|
57
|
+
if (m) {
|
|
58
|
+
const fra = Number(m[1]), til = Number(m[2]);
|
|
59
|
+
if (fra >= 1024 && til <= 65535 && til >= fra && til - fra < 200) return [fra, til];
|
|
60
|
+
}
|
|
61
|
+
} catch {}
|
|
62
|
+
return [9876, 9895];
|
|
63
|
+
}
|
|
64
|
+
const [BASE_PORT, MAX_PORT] = portOmraade();
|
|
23
65
|
const connections = new Map(); // port → WebSocket
|
|
24
66
|
|
|
25
67
|
// ── Hvorfor porte foerst PROBES med fetch, og ikke bare aabnes (MAALT 22/8) ──────
|
|
@@ -122,7 +164,7 @@ function tryConnect(port) {
|
|
|
122
164
|
if (ws.readyState !== WebSocket.OPEN) ws.close();
|
|
123
165
|
}, 8000);
|
|
124
166
|
|
|
125
|
-
ws.onopen = () => {
|
|
167
|
+
ws.onopen = async () => {
|
|
126
168
|
clearTimeout(connectTimeout);
|
|
127
169
|
connections.set(port, ws); // stadig vores? scanPorts har ikke lavet en nyere
|
|
128
170
|
|
|
@@ -141,9 +183,15 @@ function tryConnect(port) {
|
|
|
141
183
|
// To rettelser: haandtrykket sendes nu UANSET om manifest-opslaget lykkes (det er
|
|
142
184
|
// selve beskeden serveren har brug for, ikke felterne i den), og en fejl bliver
|
|
143
185
|
// logget i stedet for at forsvinde.
|
|
144
|
-
|
|
186
|
+
// MAALT 11/9 af Astra (e2e runde 2): haenger hentningen af background.js, blev haandtrykket ALDRIG sendt - og
|
|
187
|
+
// serveren saa en forbindelse uden version. Aftrykket er en bekvemmelighed for udgivelsens gate; hilsenen er ikke.
|
|
188
|
+
// MAALT 12/9 af Astra (efterproevning): fristen paa 1 s var et kaploeb. Tog hentningen 1,3 s, sendte vi `kode: null`,
|
|
189
|
+
// og udgivelsens gate afviste sin EGEN kandidat - et langsomt svar er ikke et forkert svar. Hilsenen sendes derfor
|
|
190
|
+
// med det aftryk der allerede ER beregnet (som regel intet ved foerste forbindelse), og aftrykket EFTERSENDES.
|
|
191
|
+
const kode = kodeAftrykCache;
|
|
192
|
+
let hilsen = { type: 'hello', extensionId: null, version: minVersion(), name: null, kode };
|
|
145
193
|
try {
|
|
146
|
-
hilsen = { type: 'hello', extensionId: chrome.runtime.id, version: minVersion(), name: null };
|
|
194
|
+
hilsen = { type: 'hello', extensionId: chrome.runtime.id, version: minVersion(), name: null, kode };
|
|
147
195
|
} catch (e) {
|
|
148
196
|
console.warn('[Offscreen] kunne ikke bygge haandtrykket:', e?.message || e);
|
|
149
197
|
}
|
|
@@ -153,6 +201,15 @@ function tryConnect(port) {
|
|
|
153
201
|
console.warn('[Offscreen] kunne ikke sende haandtrykket:', e?.message || e);
|
|
154
202
|
}
|
|
155
203
|
|
|
204
|
+
// Eftersendelsen: naar aftrykket er beregnet, faar serveren det - uanset hvor lang tid hentningen tog.
|
|
205
|
+
if (!kode) {
|
|
206
|
+
kodeAftryk().then((k) => {
|
|
207
|
+
if (!k || ws.readyState !== WebSocket.OPEN) return;
|
|
208
|
+
try { ws.send(JSON.stringify({ type: 'kode', kode: k })); }
|
|
209
|
+
catch (e) { console.warn('[Offscreen] kunne ikke eftersende kode-aftrykket:', e?.message || e); }
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
156
213
|
console.log(`[Offscreen] Connected to MCP server on port ${port} (${connections.size} total)`);
|
|
157
214
|
updateStatus();
|
|
158
215
|
};
|
package/extension/popup.html
CHANGED
|
@@ -37,16 +37,18 @@
|
|
|
37
37
|
.color-purple { border-color: #a855f7; } .color-purple .session-header { color: #a855f7; }
|
|
38
38
|
.color-cyan { border-color: #06b6d4; } .color-cyan .session-header { color: #06b6d4; }
|
|
39
39
|
.color-orange { border-color: #f97316; } .color-orange .session-header { color: #f97316; }
|
|
40
|
-
/* Shown only while the MCP server is missing
|
|
40
|
+
/* Shown only while the MCP server is missing - the store can't install it for you */
|
|
41
41
|
.setup { display: none; background: #1e293b; border: 1px solid #334155; border-left: 3px solid #f59e0b; border-radius: 8px; padding: 10px; margin-bottom: 8px; }
|
|
42
42
|
.setup.show { display: block; }
|
|
43
43
|
.setup p { font-size: 11px; line-height: 1.5; color: #94a3b8; margin-bottom: 8px; }
|
|
44
44
|
.setup p b { color: #e2e8f0; }
|
|
45
45
|
.setup code { display: block; background: #0f172a; border-radius: 6px; padding: 8px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 10px; color: #22c55e; word-break: break-all; margin-bottom: 6px; }
|
|
46
|
+
/* De andre klienters kommandoer staar inde i en saetning - de maa ikke blive til blokke som hovedkommandoen. */
|
|
47
|
+
.setup p.clients code { display: inline; padding: 1px 5px; margin: 0; font-size: 10px; }
|
|
46
48
|
.setup .row { display: flex; gap: 6px; }
|
|
47
49
|
.setup .row button, .setup .row a { flex: 1; margin: 0; padding: 6px; border-radius: 6px; background: #334155; color: #e2e8f0; font-size: 11px; text-align: center; text-decoration: none; border: none; cursor: pointer; }
|
|
48
50
|
.setup .row a:hover, .setup .row button:hover { background: #475569; }
|
|
49
|
-
/* Shown once both halves are up but the agent has not touched a tab yet
|
|
51
|
+
/* Shown once both halves are up but the agent has not touched a tab yet - the
|
|
50
52
|
moment a new user checks "did it work?" and needs to know what to say next. */
|
|
51
53
|
.try { display: none; background: #1e293b; border: 1px solid #334155; border-left: 3px solid #22c55e; border-radius: 8px; padding: 10px; margin-bottom: 8px; }
|
|
52
54
|
.try.show { display: block; }
|
|
@@ -66,8 +68,11 @@
|
|
|
66
68
|
</div>
|
|
67
69
|
|
|
68
70
|
<div class="setup" id="setup">
|
|
69
|
-
<p><b>
|
|
71
|
+
<p><b>Already registered the server?</b> Then this is normal: the server only takes a port the first time your agent actually uses the browser. Ask it for a screenshot once, and this turns into "Connected".</p>
|
|
72
|
+
<p><b>Otherwise: this extension is only half of Browser MCP.</b> It needs a local MCP server to talk to - the Chrome Web Store cannot install that part. Register it once, then restart your agent. Claude Code:</p>
|
|
70
73
|
<code id="setupCmd">claude mcp add --scope user browser-mcp -- npx @agent360/browser-mcp@latest</code>
|
|
74
|
+
<p class="clients">Codex: <code>codex mcp add browser-mcp -- npx @agent360/browser-mcp@latest</code><br>
|
|
75
|
+
Cursor, VS Code and the rest: run <code>npx @agent360/browser-mcp install</code> - it registers with the clients you have.</p>
|
|
71
76
|
<div class="row">
|
|
72
77
|
<button id="copyCmd">Copy command</button>
|
|
73
78
|
<a href="https://browsermcp.dev/docs/install-claude-code/" target="_blank" rel="noopener">Setup guide</a>
|
|
@@ -75,7 +80,7 @@
|
|
|
75
80
|
</div>
|
|
76
81
|
|
|
77
82
|
<div class="try" id="try">
|
|
78
|
-
<p><b>Connected
|
|
83
|
+
<p><b>Connected - you're ready.</b> Nothing happens until you ask your agent for something. Try saying:</p>
|
|
79
84
|
<span class="prompt">"Take a screenshot of my current Chrome tab."</span>
|
|
80
85
|
<span class="prompt">"Open my Gmail tab and tell me who sent my last 3 emails."</span>
|
|
81
86
|
<a href="https://browsermcp.dev/#try" target="_blank" rel="noopener">More things to try</a>
|
package/extension/popup.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
// ── Status ──────────────────────────────────────────────────────────────────
|
|
2
2
|
|
|
3
3
|
// mcpCount = agent clients on the socket. `sessions` = agents that have actually claimed a
|
|
4
|
-
// tab. They differ, and showing "Connected
|
|
4
|
+
// tab. They differ, and showing "Connected - 1 session" above "No active sessions" read as a
|
|
5
5
|
// contradiction at exactly the moment a new user checks whether the install worked.
|
|
6
6
|
function paintStatus(connected, count, sessionCount) {
|
|
7
7
|
document.getElementById('dot').className = `dot ${connected ? 'on' : 'off'}`;
|
|
8
8
|
document.getElementById('label').textContent = !connected
|
|
9
|
-
? 'Not connected
|
|
9
|
+
? 'Not connected - nothing on ports 9876-9895 yet'
|
|
10
10
|
: sessionCount > 0
|
|
11
|
-
? `Connected
|
|
12
|
-
: `Connected
|
|
11
|
+
? `Connected - ${sessionCount} active session${sessionCount === 1 ? '' : 's'}`
|
|
12
|
+
: `Connected - ready (${count} agent${count === 1 ? '' : 's'} listening)`;
|
|
13
13
|
// The Chrome Web Store can only install the extension. If nothing is listening on
|
|
14
|
-
// ports 9876-9895, the user almost certainly never ran the npx install
|
|
14
|
+
// ports 9876-9895, the user almost certainly never ran the npx install - say so.
|
|
15
15
|
document.getElementById('setup').classList.toggle('show', !connected);
|
|
16
16
|
// Connected but idle: they got it working and now need to know what to say.
|
|
17
17
|
document.getElementById('try').classList.toggle('show', connected && sessionCount === 0);
|