@opencode/browse 0.0.6-dev.25 → 0.0.6-dev.28

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.
@@ -1,4 +1,4 @@
1
- import"./main-sdmv8gfv.mjs";
1
+ import"./main-4t3a1fw9.mjs";
2
2
  import {
3
3
  cliCommand,
4
4
  installCommand,
@@ -17,15 +17,15 @@ import {
17
17
  installVersion,
18
18
  pruneVersions,
19
19
  switchVersion
20
- } from "./main-hh2mneh8.mjs";
20
+ } from "./main-8ncff5jp.mjs";
21
21
  import"./main-3gthmkzw.mjs";
22
22
  import {
23
23
  Service,
24
24
  layerFetch,
25
25
  restartRelay,
26
26
  startManagedRelay
27
- } from "./main-7pxrvrp0.mjs";
28
- import"./main-tf22ynec.mjs";
27
+ } from "./main-8h1nwn45.mjs";
28
+ import"./main-x8w3s3rv.mjs";
29
29
 
30
30
  // src/update/runtime-update.ts
31
31
  import { Effect } from "effect";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  opencodeBrowserVersion2
3
- } from "./main-sdmv8gfv.mjs";
3
+ } from "./main-4t3a1fw9.mjs";
4
4
  import {
5
5
  dataRoot,
6
6
  oldStoreMarker,
@@ -9,6 +9,7 @@ import {
9
9
  entryCommand,
10
10
  runningEntry,
11
11
  cliCommand,
12
+ PUBLIC_STORE_ID,
12
13
  storeConfig,
13
14
  devExtension,
14
15
  devExtensionFolder,
@@ -24,10 +25,10 @@ import {
24
25
  installVersion,
25
26
  pruneVersions,
26
27
  switchVersion
27
- } from "./main-hh2mneh8.mjs";
28
+ } from "./main-8ncff5jp.mjs";
28
29
  import {
29
30
  relayAuthorization
30
- } from "./main-tf22ynec.mjs";
31
+ } from "./main-x8w3s3rv.mjs";
31
32
  import {
32
33
  parseHostname,
33
34
  readRelayConfig,
@@ -44,10 +45,9 @@ import {
44
45
  legacyPairingCode,
45
46
  removeLegacyPairingCode,
46
47
  syncDevExtension
47
- } from "./main-e83a20ce.mjs";
48
+ } from "./main-xg3r1ze3.mjs";
48
49
  import {
49
50
  unpackedExtensionId,
50
- unpackedReloadAdvice,
51
51
  extensionProtocolSkew,
52
52
  extensionProtocolAdvice
53
53
  } from "./main-3gthmkzw.mjs";
@@ -10803,9 +10803,9 @@ import { existsSync, readFileSync } from "node:fs";
10803
10803
  import { homedir } from "node:os";
10804
10804
  import path2 from "node:path";
10805
10805
  import { fileURLToPath } from "node:url";
10806
- var store = storeConfig();
10807
- var STORE_IDS = [store.id].filter((id) => !!id);
10808
- var STORE_URL = store.id ? storeUrl(store.id) : undefined;
10806
+ var storeId = storeConfig().id ?? PUBLIC_STORE_ID;
10807
+ var STORE_IDS = [storeId];
10808
+ var STORE_URL = storeUrl(storeId);
10809
10809
  var PACKAGE = "@opencode/browse";
10810
10810
  var MCP_NAME = "browse";
10811
10811
  var LEGACY_MCP_NAME = "opencode-browser";
@@ -10841,7 +10841,7 @@ var checkoutExtensionDirs = (() => {
10841
10841
  })();
10842
10842
  var dev = fromSource ? undefined : devExtension();
10843
10843
  var unpackedFolder = () => checkoutExtensionDirs[1] ?? (dev ? devExtensionFolder(root) : undefined);
10844
- var connectedMessage = "✓ Connected to your browser. Restart your agent, then ask it to open a page.";
10844
+ var connectedMessage = "✓ Connected to your browser. Ask your agent to open a page.";
10845
10845
  var pairedOfflineMessage = "! Not connected yet. Open the browser profile already paired with browse and make sure the extension is enabled. It reconnects automatically.";
10846
10846
  var manualPair = (code) => `Open the Extensions menu → browse → Options, enter ${formatPairingCode(code)} as the pairing code, then click Pair.`;
10847
10847
  function flag(args, name) {
@@ -11008,10 +11008,6 @@ function browsers() {
11008
11008
  browser("Helium", configRoot, "net.imput.helium", ["helium", "helium-browser"])
11009
11009
  ];
11010
11010
  }
11011
- function extensionsPage(browser) {
11012
- const scheme = { "Microsoft Edge": "edge", Brave: "brave", Vivaldi: "vivaldi", Opera: "opera" }[browser.name];
11013
- return `${scheme ?? "chrome"}://extensions`;
11014
- }
11015
11011
  function detectBrowsers() {
11016
11012
  return browsers().flatMap((browser) => {
11017
11013
  const state = readLocked(path3.join(browser.userData, "Local State"));
@@ -11298,24 +11294,11 @@ ${block}`, `
11298
11294
  function locateExtension(options, effects = systemEffects) {
11299
11295
  const { log } = effects;
11300
11296
  const listing = options.storeUrl ?? STORE_URL;
11301
- const noListing = () => log(`! This package doesn't know its Chrome Web Store item, so it can't install the extension. Install a released version (\`npx @opencode/browse install\`), or set up browse from a checkout.`);
11302
- const pairLater = () => log(` Once it's added, run \`${cliCommand("install")}\` again to pair it with the relay.`);
11303
11297
  if (options.pairedOffline)
11304
11298
  return log(pairedOfflineMessage), "ready";
11305
- if (options.env.ssh) {
11306
- if (options.connected)
11307
- return log(connectedMessage), "ready";
11308
- if (options.folder)
11309
- unpackedSteps({ folder: options.folder, platform: options.env.platform, log });
11310
- else if (listing)
11311
- log(`Install browse in your browser from the Chrome Web Store: ${listing}`), pairLater();
11312
- else
11313
- noListing();
11314
- return "guided";
11315
- }
11316
11299
  if (options.connected)
11317
11300
  return log(connectedMessage), "ready";
11318
- if (options.detections.some((detection) => detection.enabled.length))
11301
+ if (!options.env.ssh && options.detections.some((detection) => detection.enabled.length))
11319
11302
  return "ready";
11320
11303
  const off = options.detections.flatMap((detection) => detection.disabled);
11321
11304
  if (off.length) {
@@ -11326,26 +11309,9 @@ function locateExtension(options, effects = systemEffects) {
11326
11309
  if (blocked.length)
11327
11310
  log(`! Couldn't check ${blocked.join(", ")} for browse (${blockedReason()})`);
11328
11311
  const target = options.preferred ?? options.detections.find((detection) => !detection.blocked)?.browser;
11329
- const interactive = options.allowOpen && options.env.display;
11330
- if (!options.folder) {
11331
- if (!listing)
11332
- return noListing(), "guided";
11333
- const opened = interactive && !blocked.length && (target ? effects.open(target, listing) : effects.openDefault?.(listing));
11334
- log(`Add browse to ${target?.name ?? "your browser"} from the Chrome Web Store${opened ? " (opened)" : ""}: ${listing}`);
11335
- return "guided";
11336
- }
11337
- const act = interactive && !blocked.length;
11338
- const page = target ? extensionsPage(target) : "chrome://extensions";
11339
- unpackedSteps({
11340
- folder: options.folder,
11341
- platform: options.env.platform,
11342
- log,
11343
- browser: target?.name,
11344
- page,
11345
- opened: act && target !== undefined && effects.open(target, page),
11346
- revealed: act && options.env.tty && effects.reveal(options.folder),
11347
- copied: act && options.env.tty && effects.copy(options.folder)
11348
- });
11312
+ const interactive = options.allowOpen && options.env.display && !options.env.ssh;
11313
+ const opened = interactive && !blocked.length && (target ? effects.open(target, listing) : effects.openDefault?.(listing));
11314
+ log(`Add browse to ${target?.name ?? "your browser"} from the Chrome Web Store${opened ? " (opened)" : ""}: ${listing}`);
11349
11315
  return "guided";
11350
11316
  }
11351
11317
  function pairStoreExtension(options, effects = systemEffects) {
@@ -11362,13 +11328,6 @@ function pairStoreExtension(options, effects = systemEffects) {
11362
11328
  effects.log(`To connect browse in ${match.browser.name}: ${manualPair(options.code())}`);
11363
11329
  return "manual";
11364
11330
  }
11365
- function unpackedSteps(step) {
11366
- const { log } = step;
11367
- const page = step.page ?? "chrome://extensions";
11368
- const fileManager = step.platform === "darwin" ? "Finder" : step.platform === "win32" ? "Explorer" : "your file manager";
11369
- const paste = step.platform === "darwin" ? "press Cmd+Shift+G in the folder picker and paste" : "paste it into the folder picker";
11370
- log(`Load browse in ${step.browser ?? "your browser"}: open ${page}${step.opened ? " (opened)" : ""}, turn on Developer mode, click Load unpacked and choose ${step.folder}${step.copied ? ` (path copied: ${paste})` : step.revealed ? ` (shown in ${fileManager})` : ""}`);
11371
- }
11372
11331
  async function waitForExtension(options) {
11373
11332
  const sleep = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
11374
11333
  for (let waited = 0;; waited += options.intervalMs) {
@@ -11599,8 +11558,7 @@ async function syncDev() {
11599
11558
  if (!dev)
11600
11559
  return;
11601
11560
  try {
11602
- const result = await syncDevExtension(dev, { root });
11603
- log2(result === "updated" ? `✓ Downloaded the dev extension (${dev.file}) to ${devExtensionFolder(root)}` : `✓ Dev extension ${dev.file} is in ${devExtensionFolder(root)}`);
11561
+ await syncDevExtension(dev, { root });
11604
11562
  } catch (error) {
11605
11563
  fail(`Could not get the dev extension: ${error.message}`);
11606
11564
  }
@@ -11873,7 +11831,7 @@ async function install(args) {
11873
11831
  if (!rail.active)
11874
11832
  return;
11875
11833
  rail.waiting?.stop();
11876
- outro(process.exitCode ? "browse isn't fully set up yet. Fix the steps above, then run browse install again." : byHand ? "Done. Add the MCP server above to your agent, restart it, then ask it to open a page." : "Done. Restart your agents, then ask one to open a page.");
11834
+ outro(process.exitCode ? "browse isn't fully set up yet. Fix the steps above, then run browse install again." : byHand ? "Done. Add the MCP server above to your agent, then ask it to open a page." : "Done. Ask your agent to open a page.");
11877
11835
  }
11878
11836
  var byHand = false;
11879
11837
  function showManualMcp(command, caller) {
@@ -11992,8 +11950,6 @@ async function installSteps(args, tty, caller) {
11992
11950
  const reload = connected ? await reloadOutdatedExtension(connected, folder) : "current";
11993
11951
  if (reload === "reloaded")
11994
11952
  log2("✓ Reloaded browse in your browser");
11995
- if (reload === "stale")
11996
- return log2(`! ${unpackedReloadAdvice}`);
11997
11953
  }
11998
11954
  const reportMismatch = (status) => {
11999
11955
  const advice = status.nextStep ?? extensionProtocolAdvice(status.protocolVersion, { install: installCommand() });
@@ -12030,12 +11986,11 @@ async function installSteps(args, tty, caller) {
12030
11986
  allowOpen: !args.includes("--no-open"),
12031
11987
  connected: !!connected,
12032
11988
  pairedOffline,
12033
- ...folder ? { folder } : {},
12034
11989
  detections,
12035
11990
  preferred: ssh ? undefined : defaultBrowser(detections.map((detection) => detection.browser)),
12036
11991
  env
12037
11992
  });
12038
- if (connected || found === "off" || pairedOffline || !folder && !STORE_URL)
11993
+ if (connected || found === "off" || pairedOffline)
12039
11994
  return;
12040
11995
  ensureRelay(entry);
12041
11996
  let code;
@@ -12051,11 +12006,9 @@ async function installSteps(args, tty, caller) {
12051
12006
  const blocked = detections.filter((d) => d.blocked).map((d) => d.browser.name);
12052
12007
  if (offer === "none" && blocked.length)
12053
12008
  log2(`Couldn't check ${blocked.join(", ")}. If browse is already installed there, pair it manually. ${manualPair(pairingCode())}`);
12054
- const next = folder ? `If browse is already loaded unpacked: ${unpackedReloadAdvice}` : `If browse isn't connected after you add it, run \`${cliCommand("install")}\` again to pair it.`;
12009
+ const next = `If browse isn't connected after you add it, run \`${cliCommand("install")}\` again to pair it.`;
12055
12010
  const agentNext = () => {
12056
- if (folder && offer === "none" && !blocked.length)
12057
- return `Agent: if browse is already loaded unpacked, ask the user: ${unpackedReloadAdvice} Otherwise ask them to load ${folder}. Check the connection with the browse status tool.`;
12058
- return `Agent: ${offer === "opened" ? "ask the user to open browse's status page in their browser" : offer === "manual" || blocked.length ? "ask the user to finish pairing in browse's Options" : "ask the user to add browse in their browser"}. Then re-run \`${cliCommand("install")}\` to finish, or call the browse status tool to check the connection.`;
12011
+ return `Agent: ${offer === "opened" ? "ask the user to open browse's status page in their browser" : offer === "manual" || blocked.length ? "ask the user to finish pairing in browse's Options" : "ask the user to add browse from the Chrome Web Store"}. Then re-run \`${cliCommand("install")}\` to finish, or call the browse status tool to check the connection.`;
12059
12012
  };
12060
12013
  if (!env.tty)
12061
12014
  log2(agentNext());
@@ -12088,7 +12041,7 @@ async function installSteps(args, tty, caller) {
12088
12041
  return log2(connectedMessage);
12089
12042
  if (status?.pairingState === "paired-offline")
12090
12043
  return log2(pairedOfflineMessage);
12091
- if (!folder && offer === "none") {
12044
+ if (offer === "none") {
12092
12045
  const scan = detectBrowsers();
12093
12046
  if (!checkStore(scan))
12094
12047
  return;
@@ -12128,7 +12081,7 @@ async function runSetup(command, args) {
12128
12081
  if (command === "set")
12129
12082
  return set(args);
12130
12083
  if (command === "rollback") {
12131
- const { runRollback } = await import("./runtime-update-ypj67htb.mjs");
12084
+ const { runRollback } = await import("./runtime-update-3925nwpw.mjs");
12132
12085
  return runRollback(args);
12133
12086
  }
12134
12087
  }
@@ -581,81 +581,6 @@ secret in a session without an allowlist adds a loud warning: set one first.
581
581
  Once typed, the value is in the page, so don't read it back or copy it
582
582
  elsewhere.
583
583
 
584
- ### Signing in with Deadbolt
585
-
586
- When the user connected browse to Deadbolt (\`opencode-browser deadbolt
587
- connect\`), you can sign in with the logins they granted to agents, without
588
- seeing any value. \`secrets_list\` shows them for the session's allowed sites:
589
- title, username, \`granted\`, which \`fields\` (username, password, totp) and
590
- whether \`passkeys\` are allowed, plus the site's passkey accounts (\`username\`,
591
- \`displayName\`). Only the user grants, in Deadbolt: \`deadbolt agents allow
592
- <site>\` (everything for that site's logins: username, password, one-time code
593
- and passkeys, unless narrowed with \`--login\`, \`--fields\`, \`--no-passkeys\`,
594
- \`--expires\`) or
595
- \`deadbolt agents allow --all\` (every login).
596
- Never try to grant, pair or unlock yourself; Deadbolt refuses AI callers.
597
-
598
- Prefer the whole sign-in in one call (the \`signin\` tool, or in execute):
599
-
600
- \`\`\`js
601
- await page.goto("https://github.com/login")
602
- const result = await secrets.signIn() // or { site: "github.com", username: "me@example.com" }
603
- // { status: "signed_in" | "needs_user" | "not_granted" | "failed", step, reason }
604
- \`\`\`
605
-
606
- It fills the username, submits, waits, fills the password, types an
607
- authenticator (TOTP) code when the site asks for one (single field or split
608
- boxes), follows "Use password instead", and follows Sign in with Google when
609
- Deadbolt grants a Google login, all inside the relay. Then:
610
-
611
- - \`signed_in\`: carry on.
612
- - \`needs_user\`: a captcha, a code sent by SMS or email, a push approval, a
613
- new-device or "verify it's you" check, or Deadbolt is locked. Call
614
- \`handoff("Finish signing in, then click Continue")\` so the user does it.
615
- Don't retry in a loop.
616
- - \`needs_choice\`: several passkeys can sign in; \`choices\` lists their
617
- \`username\` / \`displayName\`. Ask the user which one if it isn't obvious, then
618
- call again with \`{ username }\`.
619
- - \`not_granted\`: tell the user what to allow (the reason names the site and
620
- login).
621
- - \`failed\`: the reason says why (no form found, wrong password, the page didn't
622
- move on). Don't loop.
623
-
624
- It doesn't handle security questions, image or canvas keypads, closed shadow
625
- roots, or sign-in providers other than Google; those return \`needs_user\` or
626
- \`failed\`. When it stops after a fill it empties the password and code fields
627
- first. For a form it misreads, type one value at a time: a username on its
628
- own, a password or code only with \`{ submit: true }\`, which types it, submits
629
- the form, waits for the page to move on and empties password and code fields
630
- in that frame, all in the same call:
631
-
632
- \`\`\`js
633
- await secrets.type("#login", "deadbolt:github.com/me@example.com", { field: "username" })
634
- await secrets.type("#password", "deadbolt:github.com/me@example.com", { submit: true }) // field defaults to "password"
635
- \`\`\`
636
-
637
- The same origin checks as stored secrets apply (the field's frame must be on
638
- the login's site and in the session allowlist; Deadbolt checks again), and
639
- values never appear in what you get back. Errors start with the code:
640
- \`submit_required\`, \`domain_not_allowed\`, \`not_granted\`, \`locked\`, \`forbidden\`
641
- (the user must reconnect), \`rate_limited\`, \`page_hooks\`. \`page_hooks\` means
642
- this session's code set up request interception or listeners
643
- (\`page.route\`, \`page.on("request")\`, \`waitForRequest\`...), init scripts,
644
- exposed functions or a CDP session: Deadbolt passwords and codes aren't typed
645
- then. Sign in first, in a fresh session (\`session_reset\`), and set those up
646
- afterwards. Never try to read or capture a value that was typed: the user
647
- grants logins on the understanding that you don't. Set a session allowlist
648
- before signing in.
649
-
650
- Passkeys: in tabs you opened, a site's passkey sign-in
651
- (\`navigator.credentials.get\`) is answered by Deadbolt when the grant allows
652
- passkeys. \`signIn({ username })\` picks that account's passkey; to choose one
653
- before your own click on a "Sign in with a passkey" button, call
654
- \`await secrets.usePasskey({ site: "example.com", username: "me@example.com" })\`
655
- (it applies to the tab's next passkey request, once). Deadbolt never verifies the user, so sites that require that fall back to
656
- other methods. Otherwise the request fails with NotAllowedError, as if the user
657
- dismissed it. WebAuthn CDP methods are blocked.
658
-
659
584
  For destructive UI work, use a two-phase **read, confirm, verify** flow:
660
585
 
661
586
  1. Read candidates and return exact stable identifiers or row text.
@@ -900,8 +825,7 @@ with the command it names. If the user asks you to update browse, run that
900
825
  command yourself (\`node <browse>/cli/dist/cli.mjs update\`): it fast-forwards the
901
826
  git clone and reruns setup, and refuses with the fix when the clone has local
902
827
  changes or has diverged. Pass its refusal on rather than resolving git state
903
- yourself. The update takes effect after the user restarts opencode, so finish
904
- browser work first and tell them to restart.
828
+ yourself. Finish in-flight browser work before you run it.
905
829
 
906
830
  ## Troubleshooting
907
831
 
@@ -922,7 +846,7 @@ Common diagnoses:
922
846
  \`unpaired\` or \`refused\` means browse is installed: re-run install to pair it, **not** the store.
923
847
  \`paired-offline\`: reopen the paired browser profile and enable browse; its worker wakes every 30 seconds.
924
848
  \`not-seen\`: the relay hasn't heard from a browser. If browse is already added, open that browser and
925
- rerun install. Otherwise follow install's store or Load unpacked steps.
849
+ rerun install. Otherwise follow install's store step.
926
850
  - Incompatible extension protocol: follow \`extension.nextStep\`. For an older unpacked extension after setup,
927
851
  ask the user: “Reload browse on your browser's extensions page (↻).” Running setup again does not reload older workers.
928
852
  - Competing browser/profile connections: the active browser is preserved and
@@ -1,9 +1,9 @@
1
- import"./main-sdmv8gfv.mjs";
1
+ import"./main-4t3a1fw9.mjs";
2
2
  import {
3
3
  versionEntry,
4
4
  readCurrent,
5
5
  npmRuntime
6
- } from "./main-hh2mneh8.mjs";
6
+ } from "./main-8ncff5jp.mjs";
7
7
 
8
8
  // src/mcp/supervisor.ts
9
9
  import { spawn } from "node:child_process";
@@ -14,7 +14,7 @@ async function runBinaryMcp(args) {
14
14
  const runtime = npmRuntime();
15
15
  if (!runtime) {
16
16
  process.env[directEnvironmentVariable] = "1";
17
- const { runCli } = await import("./cli-df3reayk.mjs");
17
+ const { runCli } = await import("./cli-wgxfjgb3.mjs");
18
18
  return runCli();
19
19
  }
20
20
  const { layout } = runtime;
@@ -4,9 +4,9 @@ import {
4
4
  updateNotice2,
5
5
  createUpdateWatcher2,
6
6
  runUpdateCommand2
7
- } from "./main-spqvtmdy.mjs";
8
- import"./main-hh2mneh8.mjs";
9
- import"./main-sdmv8gfv.mjs";
7
+ } from "./main-scyv26xb.mjs";
8
+ import"./main-8ncff5jp.mjs";
9
+ import"./main-4t3a1fw9.mjs";
10
10
  import"./main-3gthmkzw.mjs";
11
11
  export {
12
12
  createUpdateWatcher2 as createUpdateWatcher,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  opencodeBrowserVersion2,
3
3
  opencodeBrowserBuildId2
4
- } from "./main-sdmv8gfv.mjs";
4
+ } from "./main-4t3a1fw9.mjs";
5
5
  export {
6
6
  opencodeBrowserBuildId2 as opencodeBrowserBuildId,
7
7
  opencodeBrowserVersion2 as opencodeBrowserVersion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opencode/browse",
3
- "version": "0.0.6-dev.25",
3
+ "version": "0.0.6-dev.28",
4
4
  "description": "browse: connect the extension to opencode, and drive your browser from any agent (CLI, MCP, Playwright).",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -580,81 +580,6 @@ secret in a session without an allowlist adds a loud warning: set one first.
580
580
  Once typed, the value is in the page, so don't read it back or copy it
581
581
  elsewhere.
582
582
 
583
- ### Signing in with Deadbolt
584
-
585
- When the user connected browse to Deadbolt (`opencode-browser deadbolt
586
- connect`), you can sign in with the logins they granted to agents, without
587
- seeing any value. `secrets_list` shows them for the session's allowed sites:
588
- title, username, `granted`, which `fields` (username, password, totp) and
589
- whether `passkeys` are allowed, plus the site's passkey accounts (`username`,
590
- `displayName`). Only the user grants, in Deadbolt: `deadbolt agents allow
591
- <site>` (everything for that site's logins: username, password, one-time code
592
- and passkeys, unless narrowed with `--login`, `--fields`, `--no-passkeys`,
593
- `--expires`) or
594
- `deadbolt agents allow --all` (every login).
595
- Never try to grant, pair or unlock yourself; Deadbolt refuses AI callers.
596
-
597
- Prefer the whole sign-in in one call (the `signin` tool, or in execute):
598
-
599
- ```js
600
- await page.goto("https://github.com/login")
601
- const result = await secrets.signIn() // or { site: "github.com", username: "me@example.com" }
602
- // { status: "signed_in" | "needs_user" | "not_granted" | "failed", step, reason }
603
- ```
604
-
605
- It fills the username, submits, waits, fills the password, types an
606
- authenticator (TOTP) code when the site asks for one (single field or split
607
- boxes), follows "Use password instead", and follows Sign in with Google when
608
- Deadbolt grants a Google login, all inside the relay. Then:
609
-
610
- - `signed_in`: carry on.
611
- - `needs_user`: a captcha, a code sent by SMS or email, a push approval, a
612
- new-device or "verify it's you" check, or Deadbolt is locked. Call
613
- `handoff("Finish signing in, then click Continue")` so the user does it.
614
- Don't retry in a loop.
615
- - `needs_choice`: several passkeys can sign in; `choices` lists their
616
- `username` / `displayName`. Ask the user which one if it isn't obvious, then
617
- call again with `{ username }`.
618
- - `not_granted`: tell the user what to allow (the reason names the site and
619
- login).
620
- - `failed`: the reason says why (no form found, wrong password, the page didn't
621
- move on). Don't loop.
622
-
623
- It doesn't handle security questions, image or canvas keypads, closed shadow
624
- roots, or sign-in providers other than Google; those return `needs_user` or
625
- `failed`. When it stops after a fill it empties the password and code fields
626
- first. For a form it misreads, type one value at a time: a username on its
627
- own, a password or code only with `{ submit: true }`, which types it, submits
628
- the form, waits for the page to move on and empties password and code fields
629
- in that frame, all in the same call:
630
-
631
- ```js
632
- await secrets.type("#login", "deadbolt:github.com/me@example.com", { field: "username" })
633
- await secrets.type("#password", "deadbolt:github.com/me@example.com", { submit: true }) // field defaults to "password"
634
- ```
635
-
636
- The same origin checks as stored secrets apply (the field's frame must be on
637
- the login's site and in the session allowlist; Deadbolt checks again), and
638
- values never appear in what you get back. Errors start with the code:
639
- `submit_required`, `domain_not_allowed`, `not_granted`, `locked`, `forbidden`
640
- (the user must reconnect), `rate_limited`, `page_hooks`. `page_hooks` means
641
- this session's code set up request interception or listeners
642
- (`page.route`, `page.on("request")`, `waitForRequest`...), init scripts,
643
- exposed functions or a CDP session: Deadbolt passwords and codes aren't typed
644
- then. Sign in first, in a fresh session (`session_reset`), and set those up
645
- afterwards. Never try to read or capture a value that was typed: the user
646
- grants logins on the understanding that you don't. Set a session allowlist
647
- before signing in.
648
-
649
- Passkeys: in tabs you opened, a site's passkey sign-in
650
- (`navigator.credentials.get`) is answered by Deadbolt when the grant allows
651
- passkeys. `signIn({ username })` picks that account's passkey; to choose one
652
- before your own click on a "Sign in with a passkey" button, call
653
- `await secrets.usePasskey({ site: "example.com", username: "me@example.com" })`
654
- (it applies to the tab's next passkey request, once). Deadbolt never verifies the user, so sites that require that fall back to
655
- other methods. Otherwise the request fails with NotAllowedError, as if the user
656
- dismissed it. WebAuthn CDP methods are blocked.
657
-
658
583
  For destructive UI work, use a two-phase **read, confirm, verify** flow:
659
584
 
660
585
  1. Read candidates and return exact stable identifiers or row text.
@@ -899,8 +824,7 @@ with the command it names. If the user asks you to update browse, run that
899
824
  command yourself (`node <browse>/cli/dist/cli.mjs update`): it fast-forwards the
900
825
  git clone and reruns setup, and refuses with the fix when the clone has local
901
826
  changes or has diverged. Pass its refusal on rather than resolving git state
902
- yourself. The update takes effect after the user restarts opencode, so finish
903
- browser work first and tell them to restart.
827
+ yourself. Finish in-flight browser work before you run it.
904
828
 
905
829
  ## Troubleshooting
906
830
 
@@ -921,7 +845,7 @@ Common diagnoses:
921
845
  `unpaired` or `refused` means browse is installed: re-run install to pair it, **not** the store.
922
846
  `paired-offline`: reopen the paired browser profile and enable browse; its worker wakes every 30 seconds.
923
847
  `not-seen`: the relay hasn't heard from a browser. If browse is already added, open that browser and
924
- rerun install. Otherwise follow install's store or Load unpacked steps.
848
+ rerun install. Otherwise follow install's store step.
925
849
  - Incompatible extension protocol: follow `extension.nextStep`. For an older unpacked extension after setup,
926
850
  ask the user: “Reload browse on your browser's extensions page (↻).” Running setup again does not reload older workers.
927
851
  - Competing browser/profile connections: the active browser is preserved and
@@ -1,4 +0,0 @@
1
- // src/version.ts
2
- var opencodeBrowserVersion2 = "0.0.6-dev.25";
3
- var opencodeBrowserBuildId2 = "2026-11-04T19:22:13.572Z";
4
- export { opencodeBrowserVersion2, opencodeBrowserBuildId2 };