@different-ai/opencode-browser 4.2.5 → 4.3.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.
@@ -17,11 +17,12 @@ metadata:
17
17
  ## Best-practice workflow
18
18
 
19
19
  1. Inspect tabs with `browser_get_tabs`
20
- 2. Navigate with `browser_navigate` if needed
21
- 3. Wait for UI using `browser_query` with `timeoutMs`
22
- 4. Discover candidates using `browser_query` with `mode=list`
23
- 5. Click or type using `index`
24
- 6. Confirm using `browser_query` or `browser_snapshot`
20
+ 2. Open new tabs with `browser_open_tab` when needed
21
+ 3. Navigate with `browser_navigate` if needed
22
+ 4. Wait for UI using `browser_query` with `timeoutMs`
23
+ 5. Discover candidates using `browser_query` with `mode=list`
24
+ 6. Click or type using `index`
25
+ 7. Confirm using `browser_query` or `browser_snapshot`
25
26
 
26
27
  ## Query modes
27
28
 
@@ -31,6 +32,11 @@ metadata:
31
32
  - `exists`: check presence and count
32
33
  - `page_text`: extract visible page text
33
34
 
35
+ ## Opening tabs
36
+
37
+ - Use `browser_open_tab` to create a new tab, optionally with `url` and `active`
38
+ - Example: `browser_open_tab({ url: "https://example.com", active: false })`
39
+
34
40
  ## Troubleshooting
35
41
 
36
42
  - If a selector fails, run `browser_query` with `mode=page_text` to confirm the content exists
package/README.md CHANGED
@@ -57,6 +57,7 @@ OpenCode Plugin <-> Local Broker (unix socket) <-> Native Host <-> Chrome Extens
57
57
  Core primitives:
58
58
  - `browser_status`
59
59
  - `browser_get_tabs`
60
+ - `browser_open_tab`
60
61
  - `browser_navigate`
61
62
  - `browser_query` (modes: `text`, `value`, `list`, `exists`, `page_text`; optional `timeoutMs`/`pollMs`)
62
63
  - `browser_click`
package/bin/cli.js CHANGED
@@ -26,6 +26,7 @@ import { join, dirname } from "path";
26
26
  import { fileURLToPath } from "url";
27
27
  import { createInterface } from "readline";
28
28
  import { createConnection } from "net";
29
+ import { execSync } from "child_process";
29
30
 
30
31
  const __filename = fileURLToPath(import.meta.url);
31
32
  const __dirname = dirname(__filename);
@@ -35,6 +36,7 @@ const BASE_DIR = join(homedir(), ".opencode-browser");
35
36
  const EXTENSION_DIR = join(BASE_DIR, "extension");
36
37
  const BROKER_DST = join(BASE_DIR, "broker.cjs");
37
38
  const NATIVE_HOST_DST = join(BASE_DIR, "native-host.cjs");
39
+ const NATIVE_HOST_WRAPPER = join(BASE_DIR, "host-wrapper.sh");
38
40
  const CONFIG_DST = join(BASE_DIR, "config.json");
39
41
  const BROKER_SOCKET = join(BASE_DIR, "broker.sock");
40
42
 
@@ -94,6 +96,26 @@ function ensureDir(p) {
94
96
  mkdirSync(p, { recursive: true });
95
97
  }
96
98
 
99
+ function resolveNodePath() {
100
+ if (process.env.OPENCODE_BROWSER_NODE) return process.env.OPENCODE_BROWSER_NODE;
101
+ if (process.execPath && /node(\.exe)?$/.test(process.execPath)) return process.execPath;
102
+ try {
103
+ const output = execSync("which node", { stdio: ["ignore", "pipe", "ignore"] })
104
+ .toString("utf8")
105
+ .trim();
106
+ if (output) return output;
107
+ } catch {}
108
+ return process.execPath;
109
+ }
110
+
111
+ function writeHostWrapper(nodePath) {
112
+ ensureDir(BASE_DIR);
113
+ const script = `#!/bin/sh\n"${nodePath}" "${NATIVE_HOST_DST}"\n`;
114
+ writeFileSync(NATIVE_HOST_WRAPPER, script, { mode: 0o755 });
115
+ chmodSync(NATIVE_HOST_WRAPPER, 0o755);
116
+ return NATIVE_HOST_WRAPPER;
117
+ }
118
+
97
119
  function createJsonLineParser(onMessage) {
98
120
  let buffer = "";
99
121
  return (chunk) => {
@@ -196,13 +218,13 @@ function nativeHostManifestPath(dir) {
196
218
  return join(dir, `${NATIVE_HOST_NAME}.json`);
197
219
  }
198
220
 
199
- function writeNativeHostManifest(dir, extensionId) {
221
+ function writeNativeHostManifest(dir, extensionId, hostPath) {
200
222
  ensureDir(dir);
201
223
 
202
224
  const manifest = {
203
225
  name: NATIVE_HOST_NAME,
204
226
  description: "OpenCode Browser native messaging host",
205
- path: NATIVE_HOST_DST,
227
+ path: hostPath || NATIVE_HOST_DST,
206
228
  type: "stdio",
207
229
  allowed_origins: [`chrome-extension://${extensionId}/`],
208
230
  };
@@ -323,14 +345,21 @@ Find it at ${color("cyan", "chrome://extensions")}:
323
345
  success(`Installed broker: ${BROKER_DST}`);
324
346
  success(`Installed native host: ${NATIVE_HOST_DST}`);
325
347
 
326
- saveConfig({ extensionId, installedAt: new Date().toISOString() });
348
+ const nodePath = resolveNodePath();
349
+ if (!/node(\.exe)?$/.test(nodePath)) {
350
+ warn(`Node not detected; using ${nodePath}. Set OPENCODE_BROWSER_NODE if needed.`);
351
+ }
352
+ const hostPath = writeHostWrapper(nodePath);
353
+ success(`Installed host wrapper: ${hostPath}`);
354
+
355
+ saveConfig({ extensionId, installedAt: new Date().toISOString(), nodePath });
327
356
 
328
357
  header("Step 6: Register Native Messaging Host");
329
358
 
330
359
  const hostDirs = getNativeHostDirs(osName);
331
360
  for (const dir of hostDirs) {
332
361
  try {
333
- writeNativeHostManifest(dir, extensionId);
362
+ writeNativeHostManifest(dir, extensionId, hostPath);
334
363
  success(`Wrote native host manifest: ${nativeHostManifestPath(dir)}`);
335
364
  } catch (e) {
336
365
  warn(`Could not write native host manifest to: ${dir}`);
@@ -520,6 +549,7 @@ async function status() {
520
549
  success(`Extension dir present: ${existsSync(EXTENSION_DIR)}`);
521
550
  success(`Broker installed: ${existsSync(BROKER_DST)}`);
522
551
  success(`Native host installed: ${existsSync(NATIVE_HOST_DST)}`);
552
+ success(`Host wrapper installed: ${existsSync(NATIVE_HOST_WRAPPER)}`);
523
553
 
524
554
  const cfg = loadConfig();
525
555
  if (cfg?.extensionId) {
@@ -528,6 +558,10 @@ async function status() {
528
558
  warn("No config.json found (run install)");
529
559
  }
530
560
 
561
+ if (cfg?.nodePath) {
562
+ success(`Node path: ${cfg.nodePath}`);
563
+ }
564
+
531
565
  const osName = platform();
532
566
  const hostDirs = getNativeHostDirs(osName);
533
567
  let foundAny = false;
package/dist/plugin.js CHANGED
@@ -12514,6 +12514,17 @@ var plugin = async (ctx) => {
12514
12514
  return toolResultText(data, "ok");
12515
12515
  }
12516
12516
  }),
12517
+ browser_open_tab: tool({
12518
+ description: "Open a new browser tab",
12519
+ args: {
12520
+ url: schema.string().optional(),
12521
+ active: schema.boolean().optional()
12522
+ },
12523
+ async execute({ url: url2, active }, ctx2) {
12524
+ const data = await brokerRequest("tool", { tool: "open_tab", args: { url: url2, active } });
12525
+ return toolResultText(data, "Opened new tab");
12526
+ }
12527
+ }),
12517
12528
  browser_navigate: tool({
12518
12529
  description: "Navigate to a URL in the browser",
12519
12530
  args: {
@@ -100,6 +100,7 @@ async function executeTool(toolName, args) {
100
100
  const tools = {
101
101
  get_active_tab: toolGetActiveTab,
102
102
  get_tabs: toolGetTabs,
103
+ open_tab: toolOpenTab,
103
104
  navigate: toolNavigate,
104
105
  click: toolClick,
105
106
  type: toolType,
@@ -487,6 +488,15 @@ async function toolGetActiveTab() {
487
488
  return { tabId: tab.id, content: { tabId: tab.id, url: tab.url, title: tab.title } }
488
489
  }
489
490
 
491
+ async function toolOpenTab({ url, active = true }) {
492
+ const createOptions = {}
493
+ if (typeof url === "string" && url.trim()) createOptions.url = url.trim()
494
+ if (typeof active === "boolean") createOptions.active = active
495
+
496
+ const tab = await chrome.tabs.create(createOptions)
497
+ return { tabId: tab.id, content: { tabId: tab.id, url: tab.url, active: tab.active } }
498
+ }
499
+
490
500
  async function toolNavigate({ url, tabId }) {
491
501
  if (!url) throw new Error("URL is required")
492
502
  const tab = await getTabById(tabId)
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "OpenCode Browser Automation",
4
- "version": "4.2.5",
4
+ "version": "4.3.0",
5
5
  "description": "Browser automation for OpenCode",
6
6
  "permissions": [
7
7
  "tabs",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@different-ai/opencode-browser",
3
- "version": "4.2.5",
3
+ "version": "4.3.0",
4
4
  "description": "Browser automation plugin for OpenCode (native messaging + per-tab ownership).",
5
5
  "type": "module",
6
6
  "bin": {