@different-ai/opencode-browser 2.0.2 → 3.0.0
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 +50 -39
- package/bin/cli.js +127 -81
- package/extension/background.js +23 -2
- package/package.json +11 -10
- package/src/mcp-server.ts +440 -0
- package/src/plugin.ts +0 -515
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# OpenCode Browser
|
|
2
2
|
|
|
3
|
-
Browser automation
|
|
3
|
+
Browser automation MCP server for [OpenCode](https://github.com/opencode-ai/opencode).
|
|
4
4
|
|
|
5
5
|
Control your real Chrome browser with existing logins, cookies, and bookmarks. No DevTools Protocol, no security prompts.
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ Control your real Chrome browser with existing logins, cookies, and bookmarks. N
|
|
|
8
8
|
|
|
9
9
|
Chrome 136+ blocks `--remote-debugging-port` on your default profile for security reasons. DevTools-based automation (like Playwright) triggers a security prompt every time.
|
|
10
10
|
|
|
11
|
-
OpenCode Browser uses a simple WebSocket connection between an
|
|
11
|
+
OpenCode Browser uses a simple WebSocket connection between an MCP server and a Chrome extension. Your automation works with your existing browser session - no prompts, no separate profiles.
|
|
12
12
|
|
|
13
13
|
## Installation
|
|
14
14
|
|
|
@@ -19,7 +19,7 @@ npx @different-ai/opencode-browser install
|
|
|
19
19
|
The installer will:
|
|
20
20
|
1. Copy the extension to `~/.opencode-browser/extension/`
|
|
21
21
|
2. Guide you to load the extension in Chrome
|
|
22
|
-
3. Update your `opencode.json`
|
|
22
|
+
3. Update your `opencode.json` with MCP server config
|
|
23
23
|
|
|
24
24
|
## Configuration
|
|
25
25
|
|
|
@@ -27,7 +27,12 @@ Add to your `opencode.json`:
|
|
|
27
27
|
|
|
28
28
|
```json
|
|
29
29
|
{
|
|
30
|
-
"
|
|
30
|
+
"mcp": {
|
|
31
|
+
"browser": {
|
|
32
|
+
"type": "local",
|
|
33
|
+
"command": ["bunx", "@different-ai/opencode-browser", "serve"]
|
|
34
|
+
}
|
|
35
|
+
}
|
|
31
36
|
}
|
|
32
37
|
```
|
|
33
38
|
|
|
@@ -40,59 +45,65 @@ Then load the extension in Chrome:
|
|
|
40
45
|
|
|
41
46
|
| Tool | Description |
|
|
42
47
|
|------|-------------|
|
|
43
|
-
| `browser_status` | Check if browser is
|
|
44
|
-
| `browser_kill_session` | Take over from another OpenCode session |
|
|
48
|
+
| `browser_status` | Check if browser extension is connected |
|
|
45
49
|
| `browser_navigate` | Navigate to a URL |
|
|
46
50
|
| `browser_click` | Click an element by CSS selector |
|
|
47
51
|
| `browser_type` | Type text into an input field |
|
|
48
|
-
| `browser_screenshot` | Capture the
|
|
49
|
-
| `browser_snapshot` | Get accessibility tree with selectors |
|
|
52
|
+
| `browser_screenshot` | Capture the page (returns base64, optionally saves to file) |
|
|
53
|
+
| `browser_snapshot` | Get accessibility tree with selectors + all page links |
|
|
50
54
|
| `browser_get_tabs` | List all open tabs |
|
|
51
55
|
| `browser_scroll` | Scroll page or element into view |
|
|
52
56
|
| `browser_wait` | Wait for a duration |
|
|
53
57
|
| `browser_execute` | Run JavaScript in page context |
|
|
54
58
|
|
|
55
|
-
|
|
59
|
+
### Screenshot Tool
|
|
56
60
|
|
|
57
|
-
|
|
61
|
+
The `browser_screenshot` tool returns base64 image data by default, allowing AI to view images directly:
|
|
58
62
|
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
```javascript
|
|
64
|
+
// Returns base64 image (AI can view it)
|
|
65
|
+
browser_screenshot()
|
|
61
66
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
67
|
+
// Save to current working directory
|
|
68
|
+
browser_screenshot({ save: true })
|
|
69
|
+
|
|
70
|
+
// Save to specific path
|
|
71
|
+
browser_screenshot({ path: "my-screenshot.png" })
|
|
72
|
+
```
|
|
65
73
|
|
|
66
74
|
## Architecture
|
|
67
75
|
|
|
68
76
|
```
|
|
69
|
-
OpenCode
|
|
70
|
-
|
|
71
|
-
|
|
77
|
+
OpenCode <──STDIO──> MCP Server <──WebSocket:19222──> Chrome Extension
|
|
78
|
+
│ │
|
|
79
|
+
└── @modelcontextprotocol/sdk └── chrome.tabs, chrome.scripting
|
|
72
80
|
```
|
|
73
81
|
|
|
74
82
|
**Two components:**
|
|
75
|
-
1.
|
|
76
|
-
2. Chrome extension (connects to
|
|
83
|
+
1. MCP Server (runs as separate process, manages WebSocket server)
|
|
84
|
+
2. Chrome extension (connects to server, executes browser commands)
|
|
77
85
|
|
|
78
|
-
**
|
|
86
|
+
**Benefits of MCP architecture:**
|
|
87
|
+
- No session conflicts between OpenCode instances
|
|
88
|
+
- Server runs independently of OpenCode process
|
|
89
|
+
- Clean separation of concerns
|
|
90
|
+
- Standard MCP protocol
|
|
79
91
|
|
|
80
|
-
## Upgrading from
|
|
92
|
+
## Upgrading from v2.x (Plugin)
|
|
81
93
|
|
|
82
|
-
|
|
94
|
+
v3.0 migrates from plugin to MCP architecture:
|
|
83
95
|
|
|
84
|
-
1. Run `npx @different-ai/opencode-browser install`
|
|
85
|
-
2. Replace
|
|
96
|
+
1. Run `npx @different-ai/opencode-browser install`
|
|
97
|
+
2. Replace plugin config with MCP config in `opencode.json`:
|
|
86
98
|
|
|
87
99
|
```diff
|
|
88
|
-
- "
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
+ "plugin": ["@different-ai/opencode-browser"]
|
|
100
|
+
- "plugin": ["@different-ai/opencode-browser"]
|
|
101
|
+
+ "mcp": {
|
|
102
|
+
+ "browser": {
|
|
103
|
+
+ "type": "local",
|
|
104
|
+
+ "command": ["bunx", "@different-ai/opencode-browser", "serve"]
|
|
105
|
+
+ }
|
|
106
|
+
+ }
|
|
96
107
|
```
|
|
97
108
|
|
|
98
109
|
3. Restart OpenCode
|
|
@@ -104,13 +115,13 @@ v2.0 is a complete rewrite with a simpler architecture:
|
|
|
104
115
|
- Check that the extension is loaded and enabled
|
|
105
116
|
- Click the extension icon to see connection status
|
|
106
117
|
|
|
107
|
-
**"Browser locked by another session"**
|
|
108
|
-
- Use `browser_kill_session` to take over
|
|
109
|
-
- Or close the other OpenCode session
|
|
110
|
-
|
|
111
118
|
**"Failed to start WebSocket server"**
|
|
112
119
|
- Port 19222 may be in use
|
|
113
|
-
-
|
|
120
|
+
- Run `lsof -i :19222` to check what's using it
|
|
121
|
+
|
|
122
|
+
**"browser_execute fails on some sites"**
|
|
123
|
+
- Sites with strict CSP block JavaScript execution
|
|
124
|
+
- Use `browser_snapshot` to get page data instead
|
|
114
125
|
|
|
115
126
|
## Uninstall
|
|
116
127
|
|
|
@@ -123,7 +134,7 @@ Then remove the extension from Chrome and delete `~/.opencode-browser/` if desir
|
|
|
123
134
|
## Platform Support
|
|
124
135
|
|
|
125
136
|
- macOS ✓
|
|
126
|
-
- Linux ✓
|
|
137
|
+
- Linux ✓
|
|
127
138
|
- Windows (not yet supported)
|
|
128
139
|
|
|
129
140
|
## License
|
package/bin/cli.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* OpenCode Browser - CLI
|
|
3
|
+
* OpenCode Browser - CLI
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Commands:
|
|
6
|
+
* install - Install Chrome extension
|
|
7
|
+
* serve - Run MCP server (used by OpenCode)
|
|
8
|
+
* status - Check connection status
|
|
7
9
|
*/
|
|
8
10
|
|
|
9
11
|
import { existsSync, mkdirSync, writeFileSync, readFileSync, copyFileSync, readdirSync, unlinkSync } from "fs";
|
|
10
12
|
import { homedir, platform } from "os";
|
|
11
13
|
import { join, dirname } from "path";
|
|
12
14
|
import { fileURLToPath } from "url";
|
|
13
|
-
import { execSync } from "child_process";
|
|
15
|
+
import { execSync, spawn } from "child_process";
|
|
14
16
|
import { createInterface } from "readline";
|
|
15
17
|
|
|
16
18
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -71,33 +73,71 @@ async function confirm(question) {
|
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
async function main() {
|
|
74
|
-
console.log(`
|
|
75
|
-
${color("cyan", color("bright", "OpenCode Browser v2.0"))}
|
|
76
|
-
${color("cyan", "Browser automation for OpenCode")}
|
|
77
|
-
`);
|
|
78
|
-
|
|
79
76
|
const command = process.argv[2];
|
|
80
77
|
|
|
81
|
-
if (command === "
|
|
78
|
+
if (command === "serve") {
|
|
79
|
+
// Run MCP server - this is called by OpenCode
|
|
80
|
+
await serve();
|
|
81
|
+
} else if (command === "install") {
|
|
82
|
+
await showHeader();
|
|
82
83
|
await install();
|
|
84
|
+
rl.close();
|
|
83
85
|
} else if (command === "uninstall") {
|
|
86
|
+
await showHeader();
|
|
84
87
|
await uninstall();
|
|
88
|
+
rl.close();
|
|
85
89
|
} else if (command === "status") {
|
|
90
|
+
await showHeader();
|
|
86
91
|
await status();
|
|
92
|
+
rl.close();
|
|
87
93
|
} else {
|
|
94
|
+
await showHeader();
|
|
88
95
|
log(`
|
|
89
96
|
${color("bright", "Usage:")}
|
|
90
97
|
npx @different-ai/opencode-browser install Install extension
|
|
91
98
|
npx @different-ai/opencode-browser uninstall Remove installation
|
|
92
|
-
npx @different-ai/opencode-browser status Check
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
99
|
+
npx @different-ai/opencode-browser status Check status
|
|
100
|
+
npx @different-ai/opencode-browser serve Run MCP server (internal)
|
|
101
|
+
|
|
102
|
+
${color("bright", "Quick Start:")}
|
|
103
|
+
1. Run: npx @different-ai/opencode-browser install
|
|
104
|
+
2. Add to your opencode.json:
|
|
105
|
+
${color("cyan", `"mcp": { "browser": { "type": "local", "command": ["bunx", "@different-ai/opencode-browser", "serve"] } }`)}
|
|
106
|
+
3. Restart OpenCode
|
|
97
107
|
`);
|
|
108
|
+
rl.close();
|
|
98
109
|
}
|
|
110
|
+
}
|
|
99
111
|
|
|
100
|
-
|
|
112
|
+
async function showHeader() {
|
|
113
|
+
console.log(`
|
|
114
|
+
${color("cyan", color("bright", "OpenCode Browser v2.1"))}
|
|
115
|
+
${color("cyan", "Browser automation MCP server for OpenCode")}
|
|
116
|
+
`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function serve() {
|
|
120
|
+
// Launch the MCP server
|
|
121
|
+
const serverPath = join(PACKAGE_ROOT, "src", "mcp-server.ts");
|
|
122
|
+
|
|
123
|
+
// Use bun to run the TypeScript server
|
|
124
|
+
const child = spawn("bun", ["run", serverPath], {
|
|
125
|
+
stdio: "inherit",
|
|
126
|
+
env: process.env,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
child.on("error", (err) => {
|
|
130
|
+
console.error("[browser-mcp] Failed to start server:", err);
|
|
131
|
+
process.exit(1);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
child.on("exit", (code) => {
|
|
135
|
+
process.exit(code || 0);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// Forward signals to child
|
|
139
|
+
process.on("SIGINT", () => child.kill("SIGINT"));
|
|
140
|
+
process.on("SIGTERM", () => child.kill("SIGTERM"));
|
|
101
141
|
}
|
|
102
142
|
|
|
103
143
|
async function install() {
|
|
@@ -157,45 +197,50 @@ To load the extension:
|
|
|
157
197
|
|
|
158
198
|
header("Step 4: Configure OpenCode");
|
|
159
199
|
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
200
|
+
const mcpConfig = {
|
|
201
|
+
browser: {
|
|
202
|
+
type: "local",
|
|
203
|
+
command: ["bunx", "@different-ai/opencode-browser", "serve"],
|
|
204
|
+
},
|
|
205
|
+
};
|
|
164
206
|
|
|
165
207
|
log(`
|
|
166
|
-
Add the
|
|
208
|
+
Add the MCP server to your ${color("cyan", "opencode.json")}:
|
|
167
209
|
|
|
168
|
-
${color("bright",
|
|
210
|
+
${color("bright", JSON.stringify({ $schema: "https://opencode.ai/config.json", mcp: mcpConfig }, null, 2))}
|
|
169
211
|
|
|
170
|
-
Or if you already have an opencode.json,
|
|
171
|
-
${color("bright",
|
|
212
|
+
Or if you already have an opencode.json, add to the "mcp" object:
|
|
213
|
+
${color("bright", JSON.stringify({ mcp: mcpConfig }, null, 2))}
|
|
172
214
|
`);
|
|
173
215
|
|
|
174
216
|
const opencodeJsonPath = join(process.cwd(), "opencode.json");
|
|
175
217
|
|
|
176
218
|
if (existsSync(opencodeJsonPath)) {
|
|
177
|
-
const shouldUpdate = await confirm(`Found opencode.json. Add
|
|
219
|
+
const shouldUpdate = await confirm(`Found opencode.json. Add MCP server automatically?`);
|
|
178
220
|
|
|
179
221
|
if (shouldUpdate) {
|
|
180
222
|
try {
|
|
181
223
|
const config = JSON.parse(readFileSync(opencodeJsonPath, "utf-8"));
|
|
182
|
-
config.
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
224
|
+
config.mcp = config.mcp || {};
|
|
225
|
+
config.mcp.browser = mcpConfig.browser;
|
|
226
|
+
|
|
227
|
+
// Remove old plugin config if present
|
|
228
|
+
if (config.plugin && Array.isArray(config.plugin)) {
|
|
229
|
+
const idx = config.plugin.indexOf("@different-ai/opencode-browser");
|
|
230
|
+
if (idx !== -1) {
|
|
231
|
+
config.plugin.splice(idx, 1);
|
|
232
|
+
warn("Removed old plugin entry (replaced by MCP)");
|
|
233
|
+
}
|
|
234
|
+
if (config.plugin.length === 0) {
|
|
235
|
+
delete config.plugin;
|
|
191
236
|
}
|
|
192
|
-
warn("Removed old MCP browser config (replaced by plugin)");
|
|
193
237
|
}
|
|
238
|
+
|
|
194
239
|
writeFileSync(opencodeJsonPath, JSON.stringify(config, null, 2) + "\n");
|
|
195
|
-
success("Updated opencode.json with
|
|
240
|
+
success("Updated opencode.json with MCP server");
|
|
196
241
|
} catch (e) {
|
|
197
242
|
error(`Failed to update opencode.json: ${e.message}`);
|
|
198
|
-
log("Please add the
|
|
243
|
+
log("Please add the MCP config manually.");
|
|
199
244
|
}
|
|
200
245
|
}
|
|
201
246
|
} else {
|
|
@@ -205,60 +250,69 @@ ${color("bright", '"plugin": ["@different-ai/opencode-browser"]')}
|
|
|
205
250
|
try {
|
|
206
251
|
const config = {
|
|
207
252
|
$schema: "https://opencode.ai/config.json",
|
|
208
|
-
|
|
253
|
+
mcp: mcpConfig,
|
|
209
254
|
};
|
|
210
255
|
writeFileSync(opencodeJsonPath, JSON.stringify(config, null, 2) + "\n");
|
|
211
|
-
success("Created opencode.json with
|
|
256
|
+
success("Created opencode.json with MCP server");
|
|
212
257
|
} catch (e) {
|
|
213
258
|
error(`Failed to create opencode.json: ${e.message}`);
|
|
214
259
|
}
|
|
215
260
|
}
|
|
216
261
|
}
|
|
217
262
|
|
|
218
|
-
// Clean up old daemon if present
|
|
219
|
-
header("Step 5: Cleanup (
|
|
263
|
+
// Clean up old daemon/plugin if present
|
|
264
|
+
header("Step 5: Cleanup (migration)");
|
|
220
265
|
|
|
221
266
|
const oldDaemonPlist = join(homedir(), "Library", "LaunchAgents", "com.opencode.browser-daemon.plist");
|
|
222
267
|
if (existsSync(oldDaemonPlist)) {
|
|
223
268
|
try {
|
|
224
269
|
execSync(`launchctl unload "${oldDaemonPlist}" 2>/dev/null || true`, { stdio: "ignore" });
|
|
225
270
|
unlinkSync(oldDaemonPlist);
|
|
226
|
-
success("Removed old daemon (no longer needed
|
|
271
|
+
success("Removed old daemon (no longer needed)");
|
|
227
272
|
} catch {
|
|
228
273
|
warn("Could not remove old daemon plist. Remove manually if needed.");
|
|
229
274
|
}
|
|
230
|
-
} else {
|
|
231
|
-
success("No old daemon to clean up");
|
|
232
275
|
}
|
|
233
276
|
|
|
277
|
+
// Remove old lock file
|
|
278
|
+
const oldLockFile = join(homedir(), ".opencode-browser", "lock.json");
|
|
279
|
+
if (existsSync(oldLockFile)) {
|
|
280
|
+
try {
|
|
281
|
+
unlinkSync(oldLockFile);
|
|
282
|
+
success("Removed old lock file (not needed with MCP)");
|
|
283
|
+
} catch {}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
success("Cleanup complete");
|
|
287
|
+
|
|
234
288
|
header("Installation Complete!");
|
|
235
289
|
|
|
236
290
|
log(`
|
|
237
291
|
${color("green", "")} Extension: ${extensionDir}
|
|
238
|
-
${color("green", "")}
|
|
292
|
+
${color("green", "")} MCP Server: @different-ai/opencode-browser
|
|
239
293
|
|
|
240
294
|
${color("bright", "How it works:")}
|
|
241
|
-
1. OpenCode
|
|
242
|
-
2.
|
|
295
|
+
1. OpenCode spawns MCP server on demand
|
|
296
|
+
2. MCP server starts WebSocket server on port 19222
|
|
243
297
|
3. Chrome extension connects automatically
|
|
244
|
-
4. Browser tools are available!
|
|
298
|
+
4. Browser tools are available to any OpenCode session!
|
|
245
299
|
|
|
246
300
|
${color("bright", "Available tools:")}
|
|
247
|
-
browser_status - Check if browser is
|
|
248
|
-
browser_kill_session - Take over from another session
|
|
301
|
+
browser_status - Check if browser is connected
|
|
249
302
|
browser_navigate - Go to a URL
|
|
250
303
|
browser_click - Click an element
|
|
251
304
|
browser_type - Type into an input
|
|
252
305
|
browser_screenshot - Capture the page
|
|
253
|
-
browser_snapshot - Get accessibility tree
|
|
306
|
+
browser_snapshot - Get accessibility tree + all links
|
|
254
307
|
browser_get_tabs - List open tabs
|
|
255
308
|
browser_scroll - Scroll the page
|
|
256
309
|
browser_wait - Wait for duration
|
|
257
310
|
browser_execute - Run JavaScript
|
|
258
311
|
|
|
259
|
-
${color("bright", "
|
|
260
|
-
|
|
261
|
-
|
|
312
|
+
${color("bright", "Benefits of MCP architecture:")}
|
|
313
|
+
- No session conflicts between OpenCode instances
|
|
314
|
+
- Server runs independently of OpenCode process
|
|
315
|
+
- Clean separation of concerns
|
|
262
316
|
|
|
263
317
|
${color("bright", "Test it:")}
|
|
264
318
|
Restart OpenCode and try: ${color("cyan", '"Check browser status"')}
|
|
@@ -266,35 +320,27 @@ ${color("bright", "Test it:")}
|
|
|
266
320
|
}
|
|
267
321
|
|
|
268
322
|
async function status() {
|
|
269
|
-
header("Browser
|
|
270
|
-
|
|
271
|
-
const lockFile = join(homedir(), ".opencode-browser", "lock.json");
|
|
272
|
-
|
|
273
|
-
if (!existsSync(lockFile)) {
|
|
274
|
-
success("Browser available (no lock file)");
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
323
|
+
header("Browser Status");
|
|
277
324
|
|
|
325
|
+
// Check if port 19222 is in use
|
|
278
326
|
try {
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
Started: ${lock.startedAt}
|
|
286
|
-
Working directory: ${lock.cwd}
|
|
287
|
-
`);
|
|
288
|
-
|
|
289
|
-
// Check if process is alive
|
|
290
|
-
try {
|
|
291
|
-
process.kill(lock.pid, 0);
|
|
292
|
-
warn(`Process ${lock.pid} is running. Browser is locked.`);
|
|
293
|
-
} catch {
|
|
294
|
-
success(`Process ${lock.pid} is dead. Lock is stale and will be auto-cleaned.`);
|
|
327
|
+
const result = execSync("lsof -i :19222 2>/dev/null || true", { encoding: "utf-8" });
|
|
328
|
+
if (result.trim()) {
|
|
329
|
+
success("WebSocket server is running on port 19222");
|
|
330
|
+
log(result);
|
|
331
|
+
} else {
|
|
332
|
+
warn("WebSocket server not running (starts on demand via MCP)");
|
|
295
333
|
}
|
|
296
|
-
} catch
|
|
297
|
-
|
|
334
|
+
} catch {
|
|
335
|
+
warn("Could not check port status");
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Check extension directory
|
|
339
|
+
const extensionDir = join(homedir(), ".opencode-browser", "extension");
|
|
340
|
+
if (existsSync(extensionDir)) {
|
|
341
|
+
success(`Extension installed at: ${extensionDir}`);
|
|
342
|
+
} else {
|
|
343
|
+
warn("Extension not installed. Run: npx @different-ai/opencode-browser install");
|
|
298
344
|
}
|
|
299
345
|
}
|
|
300
346
|
|
|
@@ -338,7 +384,7 @@ ${color("bright", "Note:")} Extension files at ~/.opencode-browser/ were not rem
|
|
|
338
384
|
Remove manually if needed:
|
|
339
385
|
rm -rf ~/.opencode-browser/
|
|
340
386
|
|
|
341
|
-
Also remove "
|
|
387
|
+
Also remove the "browser" entry from your opencode.json mcp section.
|
|
342
388
|
`);
|
|
343
389
|
}
|
|
344
390
|
|
package/extension/background.js
CHANGED
|
@@ -215,7 +215,8 @@ async function toolSnapshot({ tabId }) {
|
|
|
215
215
|
if (rect.width > 0 && rect.height > 0 && (isInteractive || el.innerText?.trim())) {
|
|
216
216
|
const node = { uid: `e${uid}`, role: el.getAttribute("role") || el.tagName.toLowerCase(),
|
|
217
217
|
name: getName(el).slice(0, 200), tag: el.tagName.toLowerCase() };
|
|
218
|
-
|
|
218
|
+
// Capture href for any element that has one (links, area, base, etc.)
|
|
219
|
+
if (el.href) node.href = el.href;
|
|
219
220
|
if (el.tagName === "INPUT") { node.type = el.type; node.value = el.value; }
|
|
220
221
|
if (el.id) node.selector = `#${el.id}`;
|
|
221
222
|
else if (el.className && typeof el.className === "string") {
|
|
@@ -234,7 +235,27 @@ async function toolSnapshot({ tabId }) {
|
|
|
234
235
|
return { nodes, nextUid: uid };
|
|
235
236
|
}
|
|
236
237
|
|
|
237
|
-
|
|
238
|
+
// Collect all links on the page separately for easy access
|
|
239
|
+
function getAllLinks() {
|
|
240
|
+
const links = [];
|
|
241
|
+
const seen = new Set();
|
|
242
|
+
document.querySelectorAll("a[href]").forEach(a => {
|
|
243
|
+
const href = a.href;
|
|
244
|
+
if (href && !seen.has(href) && !href.startsWith("javascript:")) {
|
|
245
|
+
seen.add(href);
|
|
246
|
+
const text = a.innerText?.trim().slice(0, 100) || a.getAttribute("aria-label") || "";
|
|
247
|
+
links.push({ href, text });
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
return links.slice(0, 100); // Limit to 100 links
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return {
|
|
254
|
+
url: location.href,
|
|
255
|
+
title: document.title,
|
|
256
|
+
nodes: build(document.body).nodes.slice(0, 500),
|
|
257
|
+
links: getAllLinks()
|
|
258
|
+
};
|
|
238
259
|
}
|
|
239
260
|
});
|
|
240
261
|
|
package/package.json
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@different-ai/opencode-browser",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Browser automation
|
|
3
|
+
"version": "3.0.0",
|
|
4
|
+
"description": "Browser automation MCP server for OpenCode. Control your real Chrome browser with existing logins and cookies.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"opencode-browser": "./bin/cli.js"
|
|
8
8
|
},
|
|
9
|
-
"main": "./src/
|
|
9
|
+
"main": "./src/mcp-server.ts",
|
|
10
10
|
"exports": {
|
|
11
|
-
".": "./src/
|
|
12
|
-
"./plugin": "./src/plugin.ts"
|
|
11
|
+
".": "./src/mcp-server.ts"
|
|
13
12
|
},
|
|
14
13
|
"files": [
|
|
15
14
|
"bin",
|
|
@@ -18,14 +17,16 @@
|
|
|
18
17
|
"README.md"
|
|
19
18
|
],
|
|
20
19
|
"scripts": {
|
|
21
|
-
"install-extension": "node bin/cli.js install"
|
|
20
|
+
"install-extension": "node bin/cli.js install",
|
|
21
|
+
"serve": "bun run src/mcp-server.ts"
|
|
22
22
|
},
|
|
23
23
|
"keywords": [
|
|
24
24
|
"opencode",
|
|
25
25
|
"browser",
|
|
26
26
|
"automation",
|
|
27
27
|
"chrome",
|
|
28
|
-
"
|
|
28
|
+
"mcp",
|
|
29
|
+
"model-context-protocol"
|
|
29
30
|
],
|
|
30
31
|
"author": "Benjamin Shafii",
|
|
31
32
|
"license": "MIT",
|
|
@@ -37,11 +38,11 @@
|
|
|
37
38
|
"url": "https://github.com/different-ai/opencode-browser/issues"
|
|
38
39
|
},
|
|
39
40
|
"homepage": "https://github.com/different-ai/opencode-browser#readme",
|
|
40
|
-
"
|
|
41
|
-
"@
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@modelcontextprotocol/sdk": "^1.25.2",
|
|
43
|
+
"zod": "^4.3.5"
|
|
42
44
|
},
|
|
43
45
|
"devDependencies": {
|
|
44
|
-
"@opencode-ai/plugin": "*",
|
|
45
46
|
"bun-types": "*"
|
|
46
47
|
}
|
|
47
48
|
}
|