@different-ai/opencode-browser 4.2.3 → 4.2.5
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 +132 -14
- package/extension/manifest.json +1 -1
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -25,6 +25,7 @@ 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";
|
|
28
29
|
|
|
29
30
|
const __filename = fileURLToPath(import.meta.url);
|
|
30
31
|
const __dirname = dirname(__filename);
|
|
@@ -35,6 +36,7 @@ const EXTENSION_DIR = join(BASE_DIR, "extension");
|
|
|
35
36
|
const BROKER_DST = join(BASE_DIR, "broker.cjs");
|
|
36
37
|
const NATIVE_HOST_DST = join(BASE_DIR, "native-host.cjs");
|
|
37
38
|
const CONFIG_DST = join(BASE_DIR, "config.json");
|
|
39
|
+
const BROKER_SOCKET = join(BASE_DIR, "broker.sock");
|
|
38
40
|
|
|
39
41
|
const NATIVE_HOST_NAME = "com.opencode.browser_automation";
|
|
40
42
|
|
|
@@ -92,6 +94,68 @@ function ensureDir(p) {
|
|
|
92
94
|
mkdirSync(p, { recursive: true });
|
|
93
95
|
}
|
|
94
96
|
|
|
97
|
+
function createJsonLineParser(onMessage) {
|
|
98
|
+
let buffer = "";
|
|
99
|
+
return (chunk) => {
|
|
100
|
+
buffer += chunk.toString("utf8");
|
|
101
|
+
while (true) {
|
|
102
|
+
const idx = buffer.indexOf("\n");
|
|
103
|
+
if (idx === -1) return;
|
|
104
|
+
const line = buffer.slice(0, idx);
|
|
105
|
+
buffer = buffer.slice(idx + 1);
|
|
106
|
+
if (!line.trim()) continue;
|
|
107
|
+
try {
|
|
108
|
+
onMessage(JSON.parse(line));
|
|
109
|
+
} catch {
|
|
110
|
+
// ignore
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function getBrokerStatus(timeoutMs = 2000) {
|
|
117
|
+
return await new Promise((resolve) => {
|
|
118
|
+
let done = false;
|
|
119
|
+
const socket = createConnection(BROKER_SOCKET);
|
|
120
|
+
|
|
121
|
+
const finish = (result) => {
|
|
122
|
+
if (done) return;
|
|
123
|
+
done = true;
|
|
124
|
+
try {
|
|
125
|
+
socket.end();
|
|
126
|
+
} catch {}
|
|
127
|
+
resolve(result);
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const timeout = setTimeout(() => {
|
|
131
|
+
finish({ ok: false, error: "Timed out waiting for broker" });
|
|
132
|
+
}, timeoutMs);
|
|
133
|
+
|
|
134
|
+
socket.once("error", (err) => {
|
|
135
|
+
clearTimeout(timeout);
|
|
136
|
+
finish({ ok: false, error: err.message || "Broker connection failed" });
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
socket.once("connect", () => {
|
|
140
|
+
socket.write(JSON.stringify({ type: "request", id: 1, op: "status" }) + "\n");
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
socket.on(
|
|
144
|
+
"data",
|
|
145
|
+
createJsonLineParser((msg) => {
|
|
146
|
+
if (msg && msg.type === "response" && msg.id === 1) {
|
|
147
|
+
clearTimeout(timeout);
|
|
148
|
+
if (msg.ok) {
|
|
149
|
+
finish({ ok: true, data: msg.data });
|
|
150
|
+
} else {
|
|
151
|
+
finish({ ok: false, error: msg.error || "Broker status error" });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
})
|
|
155
|
+
);
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
95
159
|
function copyDirRecursive(srcDir, destDir) {
|
|
96
160
|
ensureDir(destDir);
|
|
97
161
|
const entries = readdirSync(srcDir, { recursive: true });
|
|
@@ -289,6 +353,10 @@ Find it at ${color("cyan", "chrome://extensions")}:
|
|
|
289
353
|
.replace(/^\s*\/\/.*$/gm, "");
|
|
290
354
|
}
|
|
291
355
|
|
|
356
|
+
function sanitizeJson(contents) {
|
|
357
|
+
return stripJsoncComments(contents).replace(/,\s*(\]|\})/g, "$1");
|
|
358
|
+
}
|
|
359
|
+
|
|
292
360
|
function findOpenCodeConfigPath(configDir) {
|
|
293
361
|
const jsoncPath = join(configDir, "opencode.jsonc");
|
|
294
362
|
if (existsSync(jsoncPath)) return jsoncPath;
|
|
@@ -338,17 +406,38 @@ Find it at ${color("cyan", "chrome://extensions")}:
|
|
|
338
406
|
|
|
339
407
|
if (shouldUpdate) {
|
|
340
408
|
try {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
409
|
+
let config = { $schema: "https://opencode.ai/config.json", plugin: [] };
|
|
410
|
+
let canWriteConfig = true;
|
|
411
|
+
|
|
412
|
+
if (hasExistingConfig) {
|
|
413
|
+
const rawConfig = readFileSync(configPath, "utf-8");
|
|
414
|
+
try {
|
|
415
|
+
config = JSON.parse(sanitizeJson(rawConfig));
|
|
416
|
+
} catch (e) {
|
|
417
|
+
error(`Failed to parse ${configPath}: ${e.message}`);
|
|
418
|
+
const shouldOverwrite = await confirm("Config is invalid JSON. Back up and recreate it?");
|
|
419
|
+
if (shouldOverwrite) {
|
|
420
|
+
const backupPath = `${configPath}.bak-${Date.now()}`;
|
|
421
|
+
writeFileSync(backupPath, rawConfig);
|
|
422
|
+
warn(`Backed up invalid config to ${backupPath}`);
|
|
423
|
+
config = { $schema: "https://opencode.ai/config.json", plugin: [] };
|
|
424
|
+
} else {
|
|
425
|
+
canWriteConfig = false;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (canWriteConfig) {
|
|
431
|
+
config.plugin = normalizePlugins(config.plugin);
|
|
432
|
+
if (!config.plugin.includes(desiredPlugin)) config.plugin.push(desiredPlugin);
|
|
433
|
+
if (typeof config.$schema !== "string") config.$schema = "https://opencode.ai/config.json";
|
|
434
|
+
|
|
435
|
+
ensureDir(configDir);
|
|
436
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
437
|
+
success(`Updated ${configPath} with plugin`);
|
|
438
|
+
} else {
|
|
439
|
+
warn(`Skipped updating ${configPath}. Fix JSON manually and rerun install.`);
|
|
440
|
+
}
|
|
352
441
|
} catch (e) {
|
|
353
442
|
error(`Failed to update ${configPath}: ${e.message}`);
|
|
354
443
|
}
|
|
@@ -382,19 +471,48 @@ Format rules (summary):
|
|
|
382
471
|
warn("Skill template missing from package; skipping.");
|
|
383
472
|
}
|
|
384
473
|
|
|
474
|
+
header("Step 9: Verify Extension Connection (optional)");
|
|
475
|
+
|
|
476
|
+
const shouldCheck = await confirm("Check broker + extension connection now?");
|
|
477
|
+
if (shouldCheck) {
|
|
478
|
+
while (true) {
|
|
479
|
+
const status = await getBrokerStatus();
|
|
480
|
+
if (status.ok && status.data?.hostConnected) {
|
|
481
|
+
success("Broker is running and extension is connected.");
|
|
482
|
+
break;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
if (status.ok && !status.data?.hostConnected) {
|
|
486
|
+
warn("Broker is running but extension is not connected.");
|
|
487
|
+
} else {
|
|
488
|
+
warn(`Could not connect to local broker (${status.error || "unknown error"}).`);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
log(`
|
|
492
|
+
Open Chrome and:
|
|
493
|
+
- Verify the extension is loaded in chrome://extensions
|
|
494
|
+
- Click the OpenCode Browser extension icon to connect
|
|
495
|
+
`);
|
|
496
|
+
|
|
497
|
+
const retry = await confirm("Retry broker check?");
|
|
498
|
+
if (!retry) break;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
385
502
|
header("Installation Complete!");
|
|
386
503
|
|
|
387
504
|
log(`
|
|
388
|
-
${color("bright", "What happens now:")}
|
|
505
|
+
${color("bright", "What happens now:")}
|
|
389
506
|
- The extension connects to the native host automatically.
|
|
390
507
|
- OpenCode loads the plugin, which talks to the broker.
|
|
391
508
|
- The broker enforces ${color("bright", "per-tab ownership")}. First touch auto-claims.
|
|
392
509
|
|
|
393
|
-
${color("bright", "Try it:")}
|
|
510
|
+
${color("bright", "Try it:")}
|
|
394
511
|
Restart OpenCode and run: ${color("cyan", "browser_get_tabs")}
|
|
395
|
-
`);
|
|
512
|
+
`);
|
|
396
513
|
}
|
|
397
514
|
|
|
515
|
+
|
|
398
516
|
async function status() {
|
|
399
517
|
header("Status");
|
|
400
518
|
|
package/extension/manifest.json
CHANGED