@different-ai/opencode-browser 4.2.4 → 4.2.6

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/bin/cli.js CHANGED
@@ -25,6 +25,8 @@ import { homedir, platform } from "os";
25
25
  import { join, dirname } from "path";
26
26
  import { fileURLToPath } from "url";
27
27
  import { createInterface } from "readline";
28
+ import { createConnection } from "net";
29
+ import { execSync } from "child_process";
28
30
 
29
31
  const __filename = fileURLToPath(import.meta.url);
30
32
  const __dirname = dirname(__filename);
@@ -34,7 +36,9 @@ const BASE_DIR = join(homedir(), ".opencode-browser");
34
36
  const EXTENSION_DIR = join(BASE_DIR, "extension");
35
37
  const BROKER_DST = join(BASE_DIR, "broker.cjs");
36
38
  const NATIVE_HOST_DST = join(BASE_DIR, "native-host.cjs");
39
+ const NATIVE_HOST_WRAPPER = join(BASE_DIR, "host-wrapper.sh");
37
40
  const CONFIG_DST = join(BASE_DIR, "config.json");
41
+ const BROKER_SOCKET = join(BASE_DIR, "broker.sock");
38
42
 
39
43
  const NATIVE_HOST_NAME = "com.opencode.browser_automation";
40
44
 
@@ -92,6 +96,88 @@ function ensureDir(p) {
92
96
  mkdirSync(p, { recursive: true });
93
97
  }
94
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
+
119
+ function createJsonLineParser(onMessage) {
120
+ let buffer = "";
121
+ return (chunk) => {
122
+ buffer += chunk.toString("utf8");
123
+ while (true) {
124
+ const idx = buffer.indexOf("\n");
125
+ if (idx === -1) return;
126
+ const line = buffer.slice(0, idx);
127
+ buffer = buffer.slice(idx + 1);
128
+ if (!line.trim()) continue;
129
+ try {
130
+ onMessage(JSON.parse(line));
131
+ } catch {
132
+ // ignore
133
+ }
134
+ }
135
+ };
136
+ }
137
+
138
+ async function getBrokerStatus(timeoutMs = 2000) {
139
+ return await new Promise((resolve) => {
140
+ let done = false;
141
+ const socket = createConnection(BROKER_SOCKET);
142
+
143
+ const finish = (result) => {
144
+ if (done) return;
145
+ done = true;
146
+ try {
147
+ socket.end();
148
+ } catch {}
149
+ resolve(result);
150
+ };
151
+
152
+ const timeout = setTimeout(() => {
153
+ finish({ ok: false, error: "Timed out waiting for broker" });
154
+ }, timeoutMs);
155
+
156
+ socket.once("error", (err) => {
157
+ clearTimeout(timeout);
158
+ finish({ ok: false, error: err.message || "Broker connection failed" });
159
+ });
160
+
161
+ socket.once("connect", () => {
162
+ socket.write(JSON.stringify({ type: "request", id: 1, op: "status" }) + "\n");
163
+ });
164
+
165
+ socket.on(
166
+ "data",
167
+ createJsonLineParser((msg) => {
168
+ if (msg && msg.type === "response" && msg.id === 1) {
169
+ clearTimeout(timeout);
170
+ if (msg.ok) {
171
+ finish({ ok: true, data: msg.data });
172
+ } else {
173
+ finish({ ok: false, error: msg.error || "Broker status error" });
174
+ }
175
+ }
176
+ })
177
+ );
178
+ });
179
+ }
180
+
95
181
  function copyDirRecursive(srcDir, destDir) {
96
182
  ensureDir(destDir);
97
183
  const entries = readdirSync(srcDir, { recursive: true });
@@ -132,13 +218,13 @@ function nativeHostManifestPath(dir) {
132
218
  return join(dir, `${NATIVE_HOST_NAME}.json`);
133
219
  }
134
220
 
135
- function writeNativeHostManifest(dir, extensionId) {
221
+ function writeNativeHostManifest(dir, extensionId, hostPath) {
136
222
  ensureDir(dir);
137
223
 
138
224
  const manifest = {
139
225
  name: NATIVE_HOST_NAME,
140
226
  description: "OpenCode Browser native messaging host",
141
- path: NATIVE_HOST_DST,
227
+ path: hostPath || NATIVE_HOST_DST,
142
228
  type: "stdio",
143
229
  allowed_origins: [`chrome-extension://${extensionId}/`],
144
230
  };
@@ -259,14 +345,21 @@ Find it at ${color("cyan", "chrome://extensions")}:
259
345
  success(`Installed broker: ${BROKER_DST}`);
260
346
  success(`Installed native host: ${NATIVE_HOST_DST}`);
261
347
 
262
- 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 });
263
356
 
264
357
  header("Step 6: Register Native Messaging Host");
265
358
 
266
359
  const hostDirs = getNativeHostDirs(osName);
267
360
  for (const dir of hostDirs) {
268
361
  try {
269
- writeNativeHostManifest(dir, extensionId);
362
+ writeNativeHostManifest(dir, extensionId, hostPath);
270
363
  success(`Wrote native host manifest: ${nativeHostManifestPath(dir)}`);
271
364
  } catch (e) {
272
365
  warn(`Could not write native host manifest to: ${dir}`);
@@ -407,19 +500,48 @@ Format rules (summary):
407
500
  warn("Skill template missing from package; skipping.");
408
501
  }
409
502
 
503
+ header("Step 9: Verify Extension Connection (optional)");
504
+
505
+ const shouldCheck = await confirm("Check broker + extension connection now?");
506
+ if (shouldCheck) {
507
+ while (true) {
508
+ const status = await getBrokerStatus();
509
+ if (status.ok && status.data?.hostConnected) {
510
+ success("Broker is running and extension is connected.");
511
+ break;
512
+ }
513
+
514
+ if (status.ok && !status.data?.hostConnected) {
515
+ warn("Broker is running but extension is not connected.");
516
+ } else {
517
+ warn(`Could not connect to local broker (${status.error || "unknown error"}).`);
518
+ }
519
+
520
+ log(`
521
+ Open Chrome and:
522
+ - Verify the extension is loaded in chrome://extensions
523
+ - Click the OpenCode Browser extension icon to connect
524
+ `);
525
+
526
+ const retry = await confirm("Retry broker check?");
527
+ if (!retry) break;
528
+ }
529
+ }
530
+
410
531
  header("Installation Complete!");
411
532
 
412
533
  log(`
413
- ${color("bright", "What happens now:")}
534
+ ${color("bright", "What happens now:")}
414
535
  - The extension connects to the native host automatically.
415
536
  - OpenCode loads the plugin, which talks to the broker.
416
537
  - The broker enforces ${color("bright", "per-tab ownership")}. First touch auto-claims.
417
538
 
418
- ${color("bright", "Try it:")}
539
+ ${color("bright", "Try it:")}
419
540
  Restart OpenCode and run: ${color("cyan", "browser_get_tabs")}
420
- `);
541
+ `);
421
542
  }
422
543
 
544
+
423
545
  async function status() {
424
546
  header("Status");
425
547
 
@@ -427,6 +549,7 @@ async function status() {
427
549
  success(`Extension dir present: ${existsSync(EXTENSION_DIR)}`);
428
550
  success(`Broker installed: ${existsSync(BROKER_DST)}`);
429
551
  success(`Native host installed: ${existsSync(NATIVE_HOST_DST)}`);
552
+ success(`Host wrapper installed: ${existsSync(NATIVE_HOST_WRAPPER)}`);
430
553
 
431
554
  const cfg = loadConfig();
432
555
  if (cfg?.extensionId) {
@@ -435,6 +558,10 @@ async function status() {
435
558
  warn("No config.json found (run install)");
436
559
  }
437
560
 
561
+ if (cfg?.nodePath) {
562
+ success(`Node path: ${cfg.nodePath}`);
563
+ }
564
+
438
565
  const osName = platform();
439
566
  const hostDirs = getNativeHostDirs(osName);
440
567
  let foundAny = false;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "OpenCode Browser Automation",
4
- "version": "4.2.4",
4
+ "version": "4.2.6",
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.4",
3
+ "version": "4.2.6",
4
4
  "description": "Browser automation plugin for OpenCode (native messaging + per-tab ownership).",
5
5
  "type": "module",
6
6
  "bin": {