@different-ai/opencode-browser 4.2.2 → 4.2.3

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.
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: browser-automation
3
+ description: Reliable, composable browser automation using minimal OpenCode Browser primitives.
4
+ license: MIT
5
+ compatibility: opencode
6
+ metadata:
7
+ audience: agents
8
+ domain: browser
9
+ ---
10
+
11
+ ## What I do
12
+
13
+ - Provide a safe, composable workflow for browsing tasks
14
+ - Use `browser_query` list and index selection to click reliably
15
+ - Confirm state changes after each action
16
+
17
+ ## Best-practice workflow
18
+
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`
25
+
26
+ ## Query modes
27
+
28
+ - `text`: read visible text from a matched element
29
+ - `value`: read input values
30
+ - `list`: list many matches with text/metadata
31
+ - `exists`: check presence and count
32
+ - `page_text`: extract visible page text
33
+
34
+ ## Troubleshooting
35
+
36
+ - If a selector fails, run `browser_query` with `mode=page_text` to confirm the content exists
37
+ - Use `mode=list` on broad selectors (`button`, `a`, `*[role="button"]`) and choose by index
38
+ - Confirm results after each action
package/bin/cli.js CHANGED
@@ -275,13 +275,6 @@ Find it at ${color("cyan", "chrome://extensions")}:
275
275
 
276
276
  header("Step 7: Configure OpenCode");
277
277
 
278
- // OpenCode config discovery (per upstream docs):
279
- // - $HOME/.opencode.json
280
- // - $XDG_CONFIG_HOME/opencode/.opencode.json
281
- // - ./.opencode.json (project-local)
282
- // We write the project-local config to avoid touching global state.
283
- const opencodeJsonPath = join(process.cwd(), ".opencode.json");
284
-
285
278
  const desiredPlugin = "@different-ai/opencode-browser";
286
279
 
287
280
  function normalizePlugins(val) {
@@ -290,51 +283,103 @@ Find it at ${color("cyan", "chrome://extensions")}:
290
283
  return [];
291
284
  }
292
285
 
293
- function removeLegacyMcp(config) {
294
- if (config.mcp?.browser) {
295
- delete config.mcp.browser;
296
- if (Object.keys(config.mcp).length === 0) delete config.mcp;
297
- warn("Removed old MCP browser config (replaced by plugin)");
298
- }
299
- if (config.mcpServers?.browser) {
300
- delete config.mcpServers.browser;
301
- if (Object.keys(config.mcpServers).length === 0) delete config.mcpServers;
302
- warn("Removed old MCP browser config (replaced by plugin)");
286
+ function stripJsoncComments(contents) {
287
+ return contents
288
+ .replace(/\/\*[\s\S]*?\*\//g, "")
289
+ .replace(/^\s*\/\/.*$/gm, "");
290
+ }
291
+
292
+ function findOpenCodeConfigPath(configDir) {
293
+ const jsoncPath = join(configDir, "opencode.jsonc");
294
+ if (existsSync(jsoncPath)) return jsoncPath;
295
+ const jsonPath = join(configDir, "opencode.json");
296
+ return jsonPath;
297
+ }
298
+
299
+ const configOptions = [
300
+ "1) Project (./opencode.json or opencode.jsonc)",
301
+ "2) Global (~/.config/opencode/opencode.json)",
302
+ "3) Custom path",
303
+ "4) Skip (does nothing)",
304
+ ];
305
+
306
+ log(`\n${configOptions.join("\n")}`);
307
+ const selection = await ask("Choose config location [1-4]: ");
308
+
309
+ let configPath = null;
310
+ let configDir = null;
311
+
312
+ if (selection === "1") {
313
+ configDir = process.cwd();
314
+ configPath = findOpenCodeConfigPath(configDir);
315
+ } else if (selection === "2") {
316
+ const xdgConfig = process.env.XDG_CONFIG_HOME;
317
+ configDir = xdgConfig ? join(xdgConfig, "opencode") : join(homedir(), ".config", "opencode");
318
+ configPath = findOpenCodeConfigPath(configDir);
319
+ } else if (selection === "3") {
320
+ const customPath = await ask("Enter full path to opencode.json or opencode.jsonc: ");
321
+ if (customPath) {
322
+ configPath = customPath;
323
+ configDir = dirname(customPath);
324
+ } else {
325
+ warn("No path provided. Skipping OpenCode config.");
303
326
  }
327
+ } else if (selection === "4") {
328
+ warn("Skipping OpenCode config (does nothing).");
329
+ } else {
330
+ warn("Invalid selection. Skipping OpenCode config.");
304
331
  }
305
332
 
306
- if (existsSync(opencodeJsonPath)) {
307
- const shouldUpdate = await confirm("Found .opencode.json. Add plugin automatically?");
333
+ if (configPath && configDir) {
334
+ const hasExistingConfig = existsSync(configPath);
335
+ const shouldUpdate = hasExistingConfig
336
+ ? await confirm(`Found ${configPath}. Add plugin automatically?`)
337
+ : await confirm(`No config found at ${configPath}. Create one?`);
338
+
308
339
  if (shouldUpdate) {
309
340
  try {
310
- const config = JSON.parse(readFileSync(opencodeJsonPath, "utf-8"));
341
+ const config = hasExistingConfig
342
+ ? JSON.parse(stripJsoncComments(readFileSync(configPath, "utf-8")))
343
+ : { $schema: "https://opencode.ai/config.json", plugin: [] };
311
344
 
312
- // Make sure plugin is an array.
313
345
  config.plugin = normalizePlugins(config.plugin);
314
346
  if (!config.plugin.includes(desiredPlugin)) config.plugin.push(desiredPlugin);
315
-
316
- removeLegacyMcp(config);
317
-
318
- // Ensure schema is correct if present.
319
347
  if (typeof config.$schema !== "string") config.$schema = "https://opencode.ai/config.json";
320
348
 
321
- writeFileSync(opencodeJsonPath, JSON.stringify(config, null, 2) + "\n");
322
- success("Updated .opencode.json with plugin");
349
+ ensureDir(configDir);
350
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
351
+ success(`Updated ${configPath} with plugin`);
323
352
  } catch (e) {
324
- error(`Failed to update .opencode.json: ${e.message}`);
353
+ error(`Failed to update ${configPath}: ${e.message}`);
325
354
  }
326
355
  }
327
- } else {
328
- const shouldCreate = await confirm("No .opencode.json found. Create one?");
329
- if (shouldCreate) {
330
- const config = {
331
- $schema: "https://opencode.ai/config.json",
332
- theme: "opencode",
333
- plugin: [desiredPlugin],
334
- };
335
- writeFileSync(opencodeJsonPath, JSON.stringify(config, null, 2) + "\n");
336
- success("Created .opencode.json with plugin");
356
+ }
357
+
358
+ header("Step 8: Optional Agent Skill");
359
+
360
+ log(`
361
+ Agent Skills are reusable instructions discovered by OpenCode.
362
+
363
+ Format rules (summary):
364
+ - Place a skill at .opencode/skill/<name>/SKILL.md
365
+ - SKILL.md must start with YAML frontmatter with name + description
366
+ - name must match the directory and use: ^[a-z0-9]+(-[a-z0-9]+)*$
367
+ `);
368
+
369
+ const skillName = "browser-automation";
370
+ const skillSrc = join(PACKAGE_ROOT, ".opencode", "skill", skillName, "SKILL.md");
371
+ const skillDstDir = join(process.cwd(), ".opencode", "skill", skillName);
372
+ const skillDst = join(skillDstDir, "SKILL.md");
373
+
374
+ if (existsSync(skillSrc)) {
375
+ const shouldAddSkill = await confirm(`Add ${skillName} skill to this repo?`);
376
+ if (shouldAddSkill) {
377
+ ensureDir(skillDstDir);
378
+ copyFileSync(skillSrc, skillDst);
379
+ success(`Added skill: ${skillDst}`);
337
380
  }
381
+ } else {
382
+ warn("Skill template missing from package; skipping.");
338
383
  }
339
384
 
340
385
  header("Installation Complete!");
@@ -410,7 +455,7 @@ async function uninstall() {
410
455
  ${color("bright", "Note:")}
411
456
  - The unpacked extension folder remains at: ${EXTENSION_DIR}
412
457
  - Remove it manually in ${color("cyan", "chrome://extensions")}
413
- - Remove ${color("bright", "@different-ai/opencode-browser")} from your opencode.json plugin list if desired.
458
+ - Remove ${color("bright", "@different-ai/opencode-browser")} from your opencode.json/opencode.jsonc plugin list if desired.
414
459
  `);
415
460
  }
416
461
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "OpenCode Browser Automation",
4
- "version": "4.2.1",
4
+ "version": "4.2.3",
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.2",
3
+ "version": "4.2.3",
4
4
  "description": "Browser automation plugin for OpenCode (native messaging + per-tab ownership).",
5
5
  "type": "module",
6
6
  "bin": {
@@ -12,6 +12,7 @@
12
12
  "./plugin": "./dist/plugin.js"
13
13
  },
14
14
  "files": [
15
+ ".opencode",
15
16
  "bin",
16
17
  "dist",
17
18
  "extension",