@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 +134 -7
- package/extension/manifest.json +1 -1
- package/package.json +1 -1
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
|
-
|
|
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;
|
package/extension/manifest.json
CHANGED