@opencode/browse 0.0.6-dev.27 → 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,7 +1,7 @@
1
1
  import {
2
2
  opencodeBrowserVersion2,
3
3
  opencodeBrowserBuildId2
4
- } from "./main-vp3devjb.mjs";
4
+ } from "./main-4t3a1fw9.mjs";
5
5
  import {
6
6
  dataRoot,
7
7
  oldStoreMarker,
@@ -19,7 +19,7 @@ import {
19
19
  setStoredMode,
20
20
  autoUpdateStatus,
21
21
  createAutoUpdater
22
- } from "./main-a6mpfd6z.mjs";
22
+ } from "./main-8ncff5jp.mjs";
23
23
  import {
24
24
  extensionProtocolVersion,
25
25
  minExtensionProtocolVersion,
@@ -43,7 +43,7 @@ import {
43
43
  relayToken,
44
44
  secretsEqual,
45
45
  ensurePrivateDirectory
46
- } from "./main-bq8az9yr.mjs";
46
+ } from "./main-x8w3s3rv.mjs";
47
47
  import {
48
48
  ExecuteRequest,
49
49
  SessionAdoptRequest,
@@ -57,7 +57,6 @@ import {
57
57
  NetworkStartRequest,
58
58
  NetworkSessionRequest,
59
59
  NetworkStopRequest,
60
- DeadboltLoginsRequest,
61
60
  AuthProfileRequest,
62
61
  AuthRefreshRequest,
63
62
  AuthRunRequest,
@@ -93,7 +92,7 @@ import {
93
92
  isRestrictedTarget2,
94
93
  isRestrictedUrl2,
95
94
  dataUrlDocument2
96
- } from "./main-xzc7qp1c.mjs";
95
+ } from "./main-8h1nwn45.mjs";
97
96
  import {
98
97
  relayHostname,
99
98
  isLocalPeer,
@@ -115,7 +114,7 @@ import {
115
114
  wrapToken,
116
115
  installedDevExtension,
117
116
  syncDevExtension
118
- } from "./main-3g2t8jtq.mjs";
117
+ } from "./main-xg3r1ze3.mjs";
119
118
  import {
120
119
  SettingsInputError,
121
120
  parseSettingChanges,
@@ -123,17 +122,6 @@ import {
123
122
  settingsToExtension,
124
123
  describeSettings,
125
124
  describeChanges,
126
- DeadboltError,
127
- readClient,
128
- connect,
129
- parseDeadboltName,
130
- originOnSite,
131
- pickLogin,
132
- originsFromPatterns,
133
- listLogins,
134
- pickPasskey,
135
- isBase64Url,
136
- randomName,
137
125
  terminateChildProcess,
138
126
  SecretCollector,
139
127
  looksLikeTokenSegment,
@@ -153,16 +141,16 @@ import {
153
141
  checkNavigationUrl,
154
142
  readSecret,
155
143
  secretText
156
- } from "./main-cxrsjs2z.mjs";
144
+ } from "./main-rsqahh12.mjs";
157
145
  import {
158
146
  defaultJournalBaseDir,
159
147
  makeJournalEntry,
160
148
  appendJournalEntry,
161
149
  startJournalRetention
162
- } from "./main-a4yj9bpn.mjs";
150
+ } from "./main-tfkqwwrb.mjs";
163
151
  import {
164
152
  createUpdateWatcher2
165
- } from "./main-x96gxahz.mjs";
153
+ } from "./main-scyv26xb.mjs";
166
154
 
167
155
  // src/relay.ts
168
156
  import crypto10 from "node:crypto";
@@ -4221,15 +4209,6 @@ async function detectChallenge(page, options) {
4221
4209
  recommendation: current.kind === "block" ? "stop" : "handoff"
4222
4210
  };
4223
4211
  }
4224
- var requestEvents = new Set(["request", "requestfinished", "requestfailed", "response", "websocket", "route"]);
4225
- function pageHookCall(fn, args) {
4226
- const name = typeof fn.name === "string" ? fn.name.replace(/^bound /, "") : "";
4227
- if (["route", "routeFromHAR", "routeWebSocket", "addInitScript", "exposeBinding", "exposeFunction", "newCDPSession", "waitForRequest", "waitForResponse"].includes(name))
4228
- return `${name}()`;
4229
- if (["on", "once", "addListener", "prependListener", "waitForEvent"].includes(name) && typeof args[0] === "string" && requestEvents.has(args[0]))
4230
- return `${name}("${args[0]}")`;
4231
- return;
4232
- }
4233
4212
  function pageVisibleApiSourceWarnings(code) {
4234
4213
  const warnings = [];
4235
4214
  for (const name of ["addInitScript", "exposeFunction", "exposeBinding"]) {
@@ -8739,9 +8718,7 @@ function buildSandboxGlobals(deps) {
8739
8718
  stop: (target) => stop(target ?? page)
8740
8719
  },
8741
8720
  secrets: {
8742
- type: (target, name, typeOptions) => deps.secrets.typeSecret(page, target, name, typeOptions),
8743
- signIn: (signInOptions) => deps.secrets.signIn(page, signInOptions),
8744
- usePasskey: (passkeyOptions) => deps.secrets.usePasskey(passkeyOptions?.page ?? page, passkeyOptions)
8721
+ type: (target, name, typeOptions) => deps.secrets.typeSecret(page, target, name, typeOptions)
8745
8722
  },
8746
8723
  handoffTracker
8747
8724
  };
@@ -8749,554 +8726,6 @@ function buildSandboxGlobals(deps) {
8749
8726
 
8750
8727
  // src/execute/secrets-bridge.ts
8751
8728
  import { Effect as Effect10 } from "effect";
8752
-
8753
- // src/auth/sign-in.ts
8754
- var googleSite = "accounts.google.com";
8755
- var handoffHint = 'Hand it to the user with handoff("Finish signing in, then click Continue"), then check the page.';
8756
- var readPageScript = () => {
8757
- const limit = 20000;
8758
- const all = [];
8759
- const walk = (root) => {
8760
- for (const element of Array.from(root.querySelectorAll("*"))) {
8761
- if (all.length >= limit)
8762
- return;
8763
- all.push(element);
8764
- if (element.shadowRoot)
8765
- walk(element.shadowRoot);
8766
- }
8767
- };
8768
- walk(document);
8769
- const visible = (element) => {
8770
- const rect = element.getBoundingClientRect();
8771
- if (rect.width < 2 || rect.height < 2)
8772
- return false;
8773
- const style = getComputedStyle(element);
8774
- if (style.visibility === "hidden" || style.display === "none" || Number(style.opacity) < 0.05)
8775
- return false;
8776
- return !element.closest("[hidden], [inert], [aria-hidden='true']");
8777
- };
8778
- const describe = (element) => {
8779
- const input = element;
8780
- const labelled = (element.getAttribute("aria-labelledby") ?? "").split(/\s+/).filter(Boolean).map((id) => element.ownerDocument.getElementById(id)?.textContent ?? "").join(" ");
8781
- return [
8782
- element.getAttribute("aria-label"),
8783
- labelled,
8784
- Array.from(input.labels ?? []).map((label) => label.textContent).join(" "),
8785
- input.placeholder,
8786
- input.name,
8787
- element.id,
8788
- element.getAttribute("autocomplete"),
8789
- element.getAttribute("data-testid")
8790
- ].filter(Boolean).join(" ").toLowerCase().replace(/\s+/g, " ");
8791
- };
8792
- const ownText = (element) => `${element.innerText ?? element.textContent ?? ""} ${element.getAttribute("aria-label") ?? ""} ${element.value && element.type === "submit" ? element.value : ""}`.trim().toLowerCase().replace(/\s+/g, " ");
8793
- const inputs = all.filter((element) => element instanceof HTMLInputElement && !element.disabled && !element.readOnly && visible(element) && !["hidden", "checkbox", "radio", "submit", "button", "image", "reset", "file", "range", "color", "search"].includes(element.type));
8794
- const autocomplete = (element) => (element.getAttribute("autocomplete") ?? "").toLowerCase();
8795
- const password = inputs.find((element) => element.type === "password" && !/new-password/.test(autocomplete(element)) && !/confirm|repeat|re-?enter|new password|retype/.test(describe(element)));
8796
- const codeWords = /one[- ]?time|\botp\b|totp|\b2fa\b|\bmfa\b|two[- ]factor|verification|security code|auth\w* code|passcode|\bpin\b|digit|\bcode\b/;
8797
- const singleBoxes = inputs.filter((element) => element.maxLength === 1 && element.type !== "password");
8798
- const otpSingle = inputs.find((element) => element !== password && element.maxLength !== 1 && (autocomplete(element).includes("one-time-code") || codeWords.test(describe(element)) && !/zip|postal|promo|coupon|country|area|referral|invite/.test(describe(element))));
8799
- const otp = singleBoxes.length >= 4 && singleBoxes.length <= 8 ? singleBoxes : otpSingle ? [otpSingle] : [];
8800
- const usernameCandidates = inputs.filter((element) => element !== password && !otp.includes(element) && ["text", "email", "tel", ""].includes(element.type) && (/username|email|webauthn/.test(autocomplete(element)) || element.type === "email" || /user|e-?mail|login|identifier|account|phone|handle/.test(describe(element))) && !/search|newsletter|subscribe|coupon|promo/.test(describe(element)));
8801
- const form = password?.form ?? null;
8802
- const username = usernameCandidates.find((element) => form && element.form === form) ?? usernameCandidates[0] ?? null;
8803
- const clickables = all.filter((element) => (element instanceof HTMLButtonElement || element instanceof HTMLAnchorElement || element.getAttribute("role") === "button" || element.getAttribute("role") === "link" || element instanceof HTMLInputElement && (element.type === "submit" || element.type === "button")) && visible(element) && !element.disabled);
8804
- const byText = (pattern) => clickables.find((element) => pattern.test(ownText(element)));
8805
- const field = otp[0] ?? password ?? username ?? null;
8806
- const submitFor = (target) => {
8807
- if (!target)
8808
- return null;
8809
- const owner = target.form;
8810
- if (owner) {
8811
- const inForm = clickables.filter((element) => element.form === owner || owner.contains(element));
8812
- const typed = inForm.find((element) => element instanceof HTMLButtonElement && element.type === "submit" || element instanceof HTMLInputElement && element.type === "submit");
8813
- if (typed)
8814
- return typed;
8815
- }
8816
- const words = /^(sign ?in|log ?in|login|next|continue|verify|submit|confirm|done|weiter|suivant)\b/;
8817
- const following = clickables.filter((element) => target.compareDocumentPosition(element) & Node.DOCUMENT_POSITION_FOLLOWING);
8818
- return following.find((element) => words.test(ownText(element))) ?? clickables.find((element) => words.test(ownText(element))) ?? null;
8819
- };
8820
- const frames = Array.from(document.querySelectorAll("iframe")).filter(visible);
8821
- const captchaFrame = frames.find((frame) => {
8822
- const src = frame.src;
8823
- if (/size=invisible/.test(src) || frame.closest(".grecaptcha-badge"))
8824
- return false;
8825
- if (/challenges\.cloudflare\.com/.test(src)) {
8826
- const response = frame.ownerDocument.querySelector("input[name='cf-turnstile-response']");
8827
- return !response?.value;
8828
- }
8829
- return /\/recaptcha\/(api2|enterprise)\/(anchor|bframe)|hcaptcha\.com|arkoselabs|funcaptcha|geo\.captcha-delivery|captcha/.test(src);
8830
- });
8831
- const captchaWidget = all.find((element) => /^(px-captcha|sec-cpt-if|ddv1-captcha-container)$/.test(element.id) && visible(element));
8832
- const errors = all.filter((element) => (element.getAttribute("role") === "alert" || element.getAttribute("aria-live") === "assertive" || /(^|[\s_-])(error|alert|invalid)([\s_-]|$)/i.test(typeof element.className === "string" ? element.className : "")) && visible(element));
8833
- const handles = {
8834
- password: password ?? null,
8835
- username,
8836
- otp,
8837
- submit: submitFor(field),
8838
- passwordToggle: byText(/use (your )?password|(sign|log) ?in with (a |your )?password|enter (your )?password( instead)?|password instead|try another way/) ?? null,
8839
- google: byText(/(sign ?in|log ?in|continue|sign ?up) (with|using|via) google|^google$/) ?? null,
8840
- signInLink: byText(/^(sign ?in|log ?in|login)$/) ?? null,
8841
- passkeyButton: byText(/(sign ?in|log ?in|continue) (with|using) (a |your )?passkey|use (a |your )?passkey|^passkey$/) ?? null
8842
- };
8843
- const state = {
8844
- captcha: captchaFrame ? captchaFrame.src.slice(0, 120) : captchaWidget ? `#${captchaWidget.id}` : null,
8845
- hasPassword: Boolean(password),
8846
- hasUsername: Boolean(username),
8847
- usernameFilled: username?.value ?? "",
8848
- otpCount: otp.length,
8849
- hasPasswordToggle: Boolean(handles.passwordToggle),
8850
- hasGoogle: Boolean(handles.google),
8851
- hasSignInLink: Boolean(handles.signInLink),
8852
- hasPasskeyButton: Boolean(handles.passkeyButton),
8853
- text: `${document.title}
8854
- ${document.body?.innerText ?? ""}`.slice(0, 4000).toLowerCase(),
8855
- errorText: errors.map((element) => element.innerText ?? "").join(" ").replace(/\s+/g, " ").trim().slice(0, 120)
8856
- };
8857
- return { state, handles };
8858
- };
8859
- async function element(read, name) {
8860
- const property = await read.handles.getProperty(name);
8861
- return property.asElement() ?? undefined;
8862
- }
8863
- async function otpElements(read) {
8864
- const list = await read.handles.getProperty("otp");
8865
- const properties = await list.getProperties();
8866
- const elements = [];
8867
- for (const [key, value] of properties) {
8868
- if (!/^\d+$/.test(key))
8869
- continue;
8870
- const handle = value.asElement();
8871
- if (handle)
8872
- elements.push(handle);
8873
- }
8874
- return elements;
8875
- }
8876
- async function readFrame(frame) {
8877
- try {
8878
- const handles = await frame.evaluateHandle(readPageScript);
8879
- const state = await (await handles.getProperty("state")).jsonValue();
8880
- return { frame, state, handles: await handles.getProperty("handles") };
8881
- } catch {
8882
- return;
8883
- }
8884
- }
8885
- var actionable = (state) => Boolean(state.captcha || state.otpCount || state.hasPassword || state.hasUsername || state.hasPasswordToggle || state.hasGoogle || state.hasPasskeyButton);
8886
- async function readPage(page, site) {
8887
- const main = await readFrame(page.mainFrame());
8888
- if (main && actionable(main.state))
8889
- return main;
8890
- for (const frame of page.frames()) {
8891
- if (frame === page.mainFrame())
8892
- continue;
8893
- let origin;
8894
- try {
8895
- origin = new URL(frame.url()).origin;
8896
- } catch {
8897
- continue;
8898
- }
8899
- if (!originOnSite(origin, site))
8900
- continue;
8901
- const read = await readFrame(frame);
8902
- if (read && actionable(read.state))
8903
- return read;
8904
- }
8905
- return main;
8906
- }
8907
- var delay3 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
8908
- function defaultSite(url) {
8909
- let parsed;
8910
- try {
8911
- parsed = new URL(url);
8912
- } catch {
8913
- return;
8914
- }
8915
- if (parsed.protocol !== "https:" && parsed.protocol !== "http:")
8916
- return;
8917
- const host = parsed.hostname.toLowerCase();
8918
- if (parsed.protocol === "http:" || /^[\d.]+$/.test(host) || host.includes(":") || host === "localhost" || host.endsWith(".localhost"))
8919
- return parsed.origin;
8920
- const labels = host.split(".");
8921
- const twoLevel = labels.length >= 3 && /^(co|com|org|net|gov|ac|edu|ne|or)$/.test(labels.at(-2)) && labels.at(-1).length === 2;
8922
- return labels.slice(twoLevel ? -3 : -2).join(".");
8923
- }
8924
- function codeKind(text) {
8925
- if (/authenticator|authentication app|auth app|google authenticator|\btotp\b|one-time password app|security key app|2fa app|your app/.test(text))
8926
- return "totp";
8927
- if (/\bsms\b|text message|texted|we (just )?sent|sent (a|the|you a) code|sent to|check your (email|inbox|phone)|by email|via email|phone call|call you|ending in|whatsapp/.test(text))
8928
- return "sent";
8929
- return "unknown";
8930
- }
8931
- function verificationKind(text) {
8932
- if (/approve (the|this|your) (sign|log)|check your phone|tap yes|duo push|push notification|approve (it|the request)|waiting for (your )?approval|open the .{0,20} app on your/.test(text))
8933
- return "push_approval";
8934
- if (/new device|unrecognized device|unfamiliar device|verify (it'?s|that it'?s) you|confirm (it'?s|your identity|that it'?s you)|verify your identity|security check/.test(text))
8935
- return "new_device";
8936
- if (/security question/.test(text))
8937
- return "security_question";
8938
- return;
8939
- }
8940
- function needsUser(step, why) {
8941
- return { status: "needs_user", step, reason: `${why} ${handoffHint}` };
8942
- }
8943
- async function clickHandle(handle) {
8944
- await handle.scrollIntoViewIfNeeded().catch(() => {});
8945
- await handle.click({ timeout: 5000 }).catch(async () => {
8946
- await handle.evaluate((node) => node.click());
8947
- });
8948
- }
8949
- async function domSignature(frame) {
8950
- return await frame.evaluate(() => `${document.querySelectorAll("input, iframe, [role='alert']").length}:${document.body?.innerText.length ?? 0}`).catch(() => "");
8951
- }
8952
- async function waitForProgress(page, field, timeoutMs) {
8953
- const url = page.url();
8954
- const until = Date.now() + timeoutMs;
8955
- const frame = await field?.ownerFrame().catch(() => null) ?? page.mainFrame();
8956
- const before = await domSignature(frame);
8957
- while (Date.now() < until) {
8958
- await delay3(250);
8959
- if (page.isClosed())
8960
- return true;
8961
- if (page.url() !== url) {
8962
- await page.waitForLoadState("domcontentloaded", { timeout: Math.max(1, until - Date.now()) }).catch(() => {});
8963
- return true;
8964
- }
8965
- if (field && !await field.isVisible().catch(() => false))
8966
- return true;
8967
- const now = await domSignature(frame);
8968
- if (before && now && now !== before) {
8969
- await delay3(400);
8970
- return true;
8971
- }
8972
- }
8973
- return false;
8974
- }
8975
- async function clearSecretInputs(frames, fields = []) {
8976
- const clearScript = () => {
8977
- const own = (element) => {
8978
- const input = element;
8979
- if (!(input instanceof HTMLInputElement) || !input.value)
8980
- return;
8981
- const setter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")?.set;
8982
- if (setter)
8983
- setter.call(input, "");
8984
- else
8985
- input.value = "";
8986
- input.dispatchEvent(new Event("input", { bubbles: true }));
8987
- input.dispatchEvent(new Event("change", { bubbles: true }));
8988
- };
8989
- const all = [];
8990
- const walk = (root) => {
8991
- for (const element of Array.from(root.querySelectorAll("input"))) {
8992
- const input = element;
8993
- if (input.type === "password" || /one-time-code/i.test(input.autocomplete) || input.maxLength === 1 && input.type !== "checkbox" && input.type !== "radio")
8994
- all.push(input);
8995
- }
8996
- for (const host of Array.from(root.querySelectorAll("*")))
8997
- if (host.shadowRoot)
8998
- walk(host.shadowRoot);
8999
- };
9000
- walk(document);
9001
- for (const element of all)
9002
- own(element);
9003
- };
9004
- for (const field of fields) {
9005
- await field.evaluate((node) => {
9006
- const input = node;
9007
- if (!(input instanceof HTMLInputElement) || !input.value)
9008
- return;
9009
- Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")?.set?.call(input, "");
9010
- input.dispatchEvent(new Event("input", { bubbles: true }));
9011
- input.dispatchEvent(new Event("change", { bubbles: true }));
9012
- }).catch(() => {});
9013
- }
9014
- for (const frame of new Set(frames)) {
9015
- if (frame.isDetached())
9016
- continue;
9017
- await frame.evaluate(clearScript).catch(() => {});
9018
- }
9019
- }
9020
- async function submit(read, field) {
9021
- const button = await element(read, "submit");
9022
- if (button)
9023
- await clickHandle(button);
9024
- else
9025
- await field.press("Enter");
9026
- }
9027
- async function signIn(deps, options = {}) {
9028
- const timeoutMs = options.timeoutMs ?? 90000;
9029
- const deadline = Date.now() + timeoutMs;
9030
- let page = deps.page;
9031
- if (options.site !== undefined && (typeof options.site !== "string" || !options.site))
9032
- return { status: "failed", step: "start", reason: "site must be a host like example.com or an origin" };
9033
- const startSite = options.site?.toLowerCase() ?? defaultSite(page.url());
9034
- if (!startSite)
9035
- return { status: "failed", step: "start", reason: "The page isn't on a website; navigate to the site's sign-in page first, or pass {site}." };
9036
- const siteSpec = { site: startSite, ...options.username ? { username: options.username } : {} };
9037
- const typedFrames = new Set;
9038
- const typedFields = [];
9039
- const tracked = {
9040
- ...deps,
9041
- type: async (handles, spec, field) => {
9042
- for (const handle of handles) {
9043
- const frame = await handle.ownerFrame().catch(() => null);
9044
- if (frame)
9045
- typedFrames.add(frame);
9046
- if (field !== "username")
9047
- typedFields.push(handle);
9048
- }
9049
- return await deps.type(handles, spec, field);
9050
- }
9051
- };
9052
- let result;
9053
- try {
9054
- const onSite = (() => {
9055
- try {
9056
- return originOnSite(new URL(page.url()).origin, startSite);
9057
- } catch {
9058
- return false;
9059
- }
9060
- })();
9061
- if (!onSite) {
9062
- const start = /^https?:\/\//.test(startSite) ? startSite : `https://${startSite}/`;
9063
- await page.goto(start, { waitUntil: "domcontentloaded", timeout: Math.min(30000, timeoutMs) });
9064
- }
9065
- if (options.username && deps.passkeys)
9066
- await deps.passkeys.select(page, startSite, options.username).catch(() => {});
9067
- result = await run2(tracked, page, siteSpec, deadline, 0);
9068
- return result;
9069
- } catch (error) {
9070
- result = fromError(error, "start");
9071
- return result;
9072
- } finally {
9073
- if (result?.status !== "signed_in" && typedFrames.size > 0)
9074
- await clearSecretInputs(typedFrames, typedFields);
9075
- if (options.username && deps.passkeys && !page.isClosed())
9076
- await deps.passkeys.select(page, startSite, "").catch(() => {});
9077
- }
9078
- }
9079
- function fromError(error, step) {
9080
- if (error instanceof DeadboltError) {
9081
- if (error.code === "not_granted")
9082
- return { status: "not_granted", step, reason: error.message };
9083
- if (error.code === "locked")
9084
- return { status: "needs_user", step, reason: error.message };
9085
- return { status: "failed", step, reason: `${error.code}: ${error.message}` };
9086
- }
9087
- const message = error instanceof Error ? error.message : String(error);
9088
- if (/domain_not_allowed/.test(message))
9089
- return { status: "failed", step, reason: message.replace(/^secrets\.\w+: /, "") };
9090
- return { status: "failed", step, reason: message.slice(0, 300) };
9091
- }
9092
- async function run2(deps, startPage, siteSpec, deadline, depth) {
9093
- const page = startPage;
9094
- let step = "start";
9095
- let googleSpec;
9096
- let clickedSignInLink = false;
9097
- let toggles = 0;
9098
- let lastSignature = "";
9099
- let repeats = 0;
9100
- let passkeyClicks = 0;
9101
- const started = Date.now();
9102
- const needsChoice = async () => {
9103
- if (siteSpec.username || !deps.passkeys)
9104
- return;
9105
- const choices = await deps.passkeys.choiceSince(page, started).catch(() => {
9106
- return;
9107
- });
9108
- if (!choices || choices.length === 0)
9109
- return;
9110
- return { status: "needs_choice", step: "passkey", reason: `Several passkeys can sign in to ${siteSpec.site}; call again with { username } set to one of the choices.`, choices };
9111
- };
9112
- const specFor = (url) => {
9113
- let origin;
9114
- try {
9115
- origin = new URL(url).origin;
9116
- } catch {
9117
- return;
9118
- }
9119
- if (originOnSite(origin, siteSpec.site))
9120
- return siteSpec;
9121
- if (googleSpec && originOnSite(origin, googleSite))
9122
- return googleSpec;
9123
- return;
9124
- };
9125
- while (Date.now() < deadline) {
9126
- if (page.isClosed())
9127
- return step === "start" ? { status: "failed", step, reason: "The page closed." } : { status: "signed_in", step: "done", reason: "The sign-in window closed after the last step." };
9128
- await page.waitForLoadState("domcontentloaded", { timeout: Math.max(1, Math.min(15000, deadline - Date.now())) }).catch(() => {});
9129
- const url = page.url();
9130
- const spec = specFor(url);
9131
- if (!spec) {
9132
- if (step === "start")
9133
- return { status: "failed", step, reason: `The page is on ${safeOrigin(url)}, not ${siteSpec.site}.` };
9134
- return needsUser("provider", `The sign-in moved to ${safeOrigin(url)}, which isn't ${siteSpec.site}${googleSpec ? " or Google" : ""}.`);
9135
- }
9136
- const choice = await needsChoice();
9137
- if (choice)
9138
- return choice;
9139
- let read;
9140
- const settleUntil = Math.min(deadline, Date.now() + (step === "start" ? 8000 : 5000));
9141
- while (true) {
9142
- read = await readPage(page, spec.site);
9143
- if (read && actionable(read.state) || Date.now() >= settleUntil)
9144
- break;
9145
- await delay3(300);
9146
- }
9147
- if (!read) {
9148
- await delay3(300);
9149
- continue;
9150
- }
9151
- const state = read.state;
9152
- const signature = `${url}|${state.hasUsername}|${state.hasPassword}|${state.otpCount}|${state.usernameFilled.length}`;
9153
- repeats = signature === lastSignature ? repeats + 1 : 0;
9154
- lastSignature = signature;
9155
- if (repeats >= 2) {
9156
- return { status: "failed", step, reason: state.errorText ? `The site didn't accept the ${step === "code" ? "code" : step}: ${state.errorText}` : `The page didn't move on after the ${step} step.` };
9157
- }
9158
- if (state.captcha)
9159
- return needsUser(step, `A captcha is showing (${state.captcha}).`);
9160
- const challenge = await deps.challenge(page).catch(() => {
9161
- return;
9162
- });
9163
- if (challenge && challenge.kind !== "non-interactive") {
9164
- return challenge.kind === "block" ? { status: "failed", step, reason: `The site blocked the visit (${challenge.vendor}).` } : needsUser(step, `A ${challenge.vendor} bot check is showing.`);
9165
- }
9166
- if (state.otpCount > 0) {
9167
- const kind = codeKind(state.text);
9168
- if (kind === "sent")
9169
- return needsUser("code", "The site sent a one-time code by SMS, email or call.");
9170
- const logins = await deps.logins(new URL(read.frame.url()).origin).catch(() => []);
9171
- const hasTotp = logins.some((login) => login.hasTotp && login.granted && (!login.fields || login.fields.includes("totp")) && (!spec.username || login.username?.toLowerCase() === spec.username.toLowerCase()));
9172
- if (!hasTotp)
9173
- return needsUser("code", kind === "totp" ? "The site asks for an authenticator code, and the granted login has no one-time password." : "The site asks for a one-time code.");
9174
- const boxes = await otpElements(read);
9175
- step = "code";
9176
- await deps.type(boxes, spec, "totp");
9177
- if (!await waitForProgress(page, boxes[0], 1500)) {
9178
- const stillThere = await boxes[0].isVisible().catch(() => false);
9179
- if (stillThere)
9180
- await submit(read, boxes.at(-1));
9181
- await waitForProgress(page, boxes[0], 15000);
9182
- }
9183
- continue;
9184
- }
9185
- if (state.hasPassword) {
9186
- const passwordField = await element(read, "password");
9187
- const usernameField = await element(read, "username");
9188
- if (usernameField && !state.usernameFilled)
9189
- await deps.type([usernameField], spec, "username");
9190
- step = "password";
9191
- await deps.type([passwordField], spec, "password");
9192
- await submit(read, passwordField);
9193
- await waitForProgress(page, passwordField, 15000);
9194
- continue;
9195
- }
9196
- if (state.hasUsername) {
9197
- const usernameField = await element(read, "username");
9198
- await deps.type([usernameField], spec, "username");
9199
- step = "identifier";
9200
- await submit(read, usernameField);
9201
- await waitForProgress(page, usernameField, 15000);
9202
- continue;
9203
- }
9204
- if (state.hasPasswordToggle && toggles < 2) {
9205
- toggles += 1;
9206
- await clickHandle(await element(read, "passwordToggle"));
9207
- await waitForProgress(page, undefined, 2000);
9208
- continue;
9209
- }
9210
- if (state.hasGoogle && !googleSpec && depth === 0 && spec === siteSpec) {
9211
- const siteLogins = await deps.logins(new URL(read.frame.url()).origin).catch(() => []);
9212
- const viaGoogle = siteLogins.find((login) => login.signIn?.provider === "google");
9213
- const googleLogins = (await deps.logins(`https://${googleSite}`).catch(() => [])).filter((login) => login.granted);
9214
- const account = siteSpec.username ?? viaGoogle?.signIn?.account;
9215
- const googleLogin = googleLogins.find((login) => !account || login.username?.toLowerCase() === account.toLowerCase()) ?? (googleLogins.length === 1 ? googleLogins[0] : undefined);
9216
- if (googleLogin && (viaGoogle || !siteLogins.some((login) => login.granted && login.hasPassword))) {
9217
- if (!deps.allowed(`https://${googleSite}/`))
9218
- return needsUser("provider", `Signing in uses Google, but ${googleSite} isn't in this session's allowlist (add it with session_allowlist).`);
9219
- googleSpec = { site: googleSite, ...googleLogin.username ? { username: googleLogin.username } : {} };
9220
- step = "provider";
9221
- const popupPromise = deps.context.waitForEvent("page", { timeout: 5000 }).catch(() => {
9222
- return;
9223
- });
9224
- await clickHandle(await element(read, "google"));
9225
- const popup = await popupPromise;
9226
- if (popup) {
9227
- await popup.waitForLoadState("domcontentloaded").catch(() => {});
9228
- const result = await runProvider(deps, popup, googleSpec, deadline);
9229
- if (result.status !== "signed_in")
9230
- return result;
9231
- await waitForProgress(page, undefined, 5000);
9232
- } else {
9233
- await waitForProgress(page, undefined, 1e4);
9234
- }
9235
- continue;
9236
- }
9237
- }
9238
- if (state.hasPasskeyButton && passkeyClicks < 1) {
9239
- const origin = new URL(read.frame.url()).origin;
9240
- const passkeys = await deps.passkeyAccounts(origin).catch(() => []);
9241
- const granted = spec.username ? pickPasskey(passkeys, spec.username) !== undefined : passkeys.some((passkey) => passkey.granted);
9242
- if (granted) {
9243
- passkeyClicks += 1;
9244
- step = "passkey";
9245
- const before = page.url();
9246
- await clickHandle(await element(read, "passkeyButton"));
9247
- const until = Math.min(deadline, Date.now() + 1e4);
9248
- while (Date.now() < until && !page.isClosed() && page.url() === before) {
9249
- const picked = await needsChoice();
9250
- if (picked)
9251
- return picked;
9252
- await delay3(250);
9253
- }
9254
- await page.waitForLoadState("domcontentloaded").catch(() => {});
9255
- continue;
9256
- }
9257
- }
9258
- const verification = verificationKind(state.text);
9259
- if (verification)
9260
- return needsUser("verify", verification === "push_approval" ? "The site waits for a push approval on another device." : verification === "security_question" ? "The site asks a security question." : "The site wants to verify a new device or that it's really the user.");
9261
- if (step !== "start" && codeKind(state.text) === "sent")
9262
- return needsUser("code", "The site sent a one-time code by SMS, email or call.");
9263
- if (step === "start") {
9264
- if (state.hasSignInLink && !clickedSignInLink) {
9265
- clickedSignInLink = true;
9266
- await clickHandle(await element(read, "signInLink"));
9267
- await waitForProgress(page, undefined, 8000);
9268
- continue;
9269
- }
9270
- if (state.hasGoogle)
9271
- return needsUser("provider", "The page offers Sign in with Google, but Deadbolt grants no Google login for it.");
9272
- return { status: "failed", step, reason: "No sign-in form found on this page. Open the site's sign-in page first." };
9273
- }
9274
- if (state.errorText && /incorrect|wrong|invalid|doesn'?t match|not recognized|couldn'?t find|try again/i.test(state.errorText)) {
9275
- return { status: "failed", step, reason: `The site refused the sign-in: ${state.errorText}` };
9276
- }
9277
- if (spec === googleSpec && spec !== siteSpec)
9278
- return needsUser("provider", "Google shows a screen to choose an account or to give consent.");
9279
- return { status: "signed_in", step: "done", reason: `Signed in to ${spec.site}: no sign-in field is left after the ${step} step.` };
9280
- }
9281
- return { status: "failed", step: "start", reason: "The sign-in didn't finish in time." };
9282
- }
9283
- async function runProvider(deps, popup, spec, deadline) {
9284
- const result = await run2({ ...deps, page: popup }, popup, spec, deadline, 1);
9285
- if (result.status !== "signed_in")
9286
- return result;
9287
- if (!popup.isClosed())
9288
- await popup.waitForEvent("close", { timeout: Math.max(1, Math.min(20000, deadline - Date.now())) }).catch(() => {});
9289
- return popup.isClosed() ? result : needsUser("provider", "Google's sign-in window stayed open (an account choice or consent screen).");
9290
- }
9291
- function safeOrigin(url) {
9292
- try {
9293
- return new URL(url).origin;
9294
- } catch {
9295
- return url.slice(0, 80);
9296
- }
9297
- }
9298
-
9299
- // src/execute/secrets-bridge.ts
9300
8729
  var isOpaqueDocumentUrl = (url) => /^(?:about|data|blob):/i.test(url);
9301
8730
  var frameOrigin = (url) => {
9302
8731
  try {
@@ -9313,8 +8742,6 @@ var elementHasFocus = (handle) => handle.evaluate((node) => {
9313
8742
  class ExecuteSecretsBridge {
9314
8743
  deps;
9315
8744
  typedSecrets = new Map;
9316
- deadboltConnection;
9317
- pageHooks = new Set;
9318
8745
  constructor(deps) {
9319
8746
  this.deps = deps;
9320
8747
  }
@@ -9372,9 +8799,6 @@ class ExecuteSecretsBridge {
9372
8799
  async typeSecret(page, target, name, options = {}) {
9373
8800
  if (typeof name !== "string" || !name)
9374
8801
  throw new Error("secrets.type(target, name): name must be the name of a stored secret");
9375
- const deadboltSpec = parseDeadboltName(name);
9376
- if (deadboltSpec)
9377
- return await this.typeDeadboltSecret(page, target, name, deadboltSpec, options);
9378
8802
  const secret = await Effect10.runPromise(readSecret(name)).catch((error) => {
9379
8803
  throw new Error(`secrets.type: ${error instanceof Error ? error.message : "could not read the secret"}`);
9380
8804
  });
@@ -9429,158 +8853,6 @@ class ExecuteSecretsBridge {
9429
8853
  await handle.dispose().catch(() => {});
9430
8854
  }
9431
8855
  }
9432
- refuseWithPageHooks(label) {
9433
- if (this.pageHooks.size === 0)
9434
- return;
9435
- throw new Error(`${label}: page_hooks: this session's code used ${[...this.pageHooks].sort().join(", ")}, which could capture what is typed, so Deadbolt passwords and codes aren't typed here. Reset the session (session_reset) and sign in before setting those up. Nothing was typed.`);
9436
- }
9437
- deadbolt() {
9438
- const client = readClient();
9439
- if (!this.deadboltConnection || !client || this.deadboltConnection.client.id !== client.id || this.deadboltConnection.client.token !== client.token) {
9440
- this.deadboltConnection = connect(client ? { client } : {});
9441
- }
9442
- return this.deadboltConnection;
9443
- }
9444
- sessionAllows(url) {
9445
- const patterns = this.deps.allowedOrigins();
9446
- return !patterns || patterns.length === 0 || checkUrl(url, parseOriginPatterns(patterns)).allowed;
9447
- }
9448
- async typeDeadbolt(handles, spec, field, label) {
9449
- if (!["username", "password", "totp"].includes(field))
9450
- throw new Error(`${label}: field must be "username", "password" or "totp"`);
9451
- if (handles.length === 0)
9452
- throw new Error(`${label}: target_not_editable: no field to type into`);
9453
- if (field !== "username")
9454
- this.refuseWithPageHooks(label);
9455
- const frame = await handles[0].ownerFrame();
9456
- if (!frame)
9457
- throw new Error(`${label}: target_not_editable: the field is not in a frame`);
9458
- for (const handle of handles.slice(1)) {
9459
- if (await handle.ownerFrame() !== frame)
9460
- throw new Error(`${label}: target_not_editable: the code boxes are in different frames`);
9461
- }
9462
- const checkOrigin = () => {
9463
- const url = frame.url();
9464
- const origin = frameOrigin(url);
9465
- if (isOpaqueDocumentUrl(url) || !originOnSite(origin, spec.site)) {
9466
- throw new Error(`${label}: domain_not_allowed: the field's frame is ${origin}, which is not on ${spec.site}. Nothing was typed.`);
9467
- }
9468
- if (!this.sessionAllows(url)) {
9469
- throw new Error(`${label}: domain_not_allowed: the field's frame is ${origin}, which is outside this session's domain allowlist. Nothing was typed.`);
9470
- }
9471
- return origin;
9472
- };
9473
- const origin = checkOrigin();
9474
- for (const handle of handles) {
9475
- if (!await handle.isEditable().catch(() => false))
9476
- throw new Error(`${label}: target_not_editable: the target is not an editable field. Nothing was typed.`);
9477
- }
9478
- const connection = this.deadbolt();
9479
- const login = pickLogin(await connection.logins(origin), spec.site, spec.username, field);
9480
- const values = await connection.fill(origin, login, [field]);
9481
- const value = values[field];
9482
- if (!value)
9483
- throw new DeadboltError("not_found", `The Deadbolt login for ${spec.site} has no ${field}.`);
9484
- const name = `deadbolt:${spec.site}${login.username ? `/${login.username}` : ""} ${field}`;
9485
- if (field !== "username") {
9486
- this.typedSecrets.set(value, name);
9487
- this.deps.networkCapture.addRedaction(value);
9488
- }
9489
- const characters = handles.length === 1 ? [value] : Array.from(value);
9490
- if (handles.length > 1 && characters.length !== handles.length) {
9491
- throw new Error(`${label}: target_not_editable: ${handles.length} code boxes for a ${characters.length}-character code. Nothing was typed.`);
9492
- }
9493
- for (const [index, handle] of handles.entries()) {
9494
- await handle.fill("");
9495
- await handle.focus();
9496
- if (!await elementHasFocus(handle)) {
9497
- throw new Error(`${label}: target_not_editable: the field did not keep focus.`);
9498
- }
9499
- checkOrigin();
9500
- await frame.page().keyboard.insertText(characters[index]);
9501
- }
9502
- if (!this.deps.allowedOrigins()?.length) {
9503
- this.deps.addWarningOnce(`A Deadbolt login was typed in a session without a domain allowlist. A prompt-injected page could steer this session elsewhere after the value is in the page: set one with session_allowlist (or \`session allow\`) before signing in.`);
9504
- }
9505
- return { origin, login };
9506
- }
9507
- async typeDeadboltSecret(page, target, name, spec, options) {
9508
- const field = options.field ?? "password";
9509
- if (field !== "username" && options.submit !== true) {
9510
- throw new Error(`secrets.type: submit_required: a Deadbolt ${field} is typed only with { submit: true }, which submits the form in the same call and clears the field afterwards. Prefer secrets.signIn(). Nothing was typed.`);
9511
- }
9512
- const locator = typeof target === "string" ? page.locator(target) : target;
9513
- if (!locator || typeof locator.elementHandle !== "function") {
9514
- throw new Error("secrets.type(target, name): target must be a selector string or a Locator");
9515
- }
9516
- const handle = await locator.elementHandle({ timeout: options.timeout ?? 1e4 });
9517
- const frame = await handle.ownerFrame();
9518
- let typed = false;
9519
- try {
9520
- const { origin } = await this.typeDeadbolt([handle], spec, field, "secrets.type").catch((error) => {
9521
- if (error instanceof DeadboltError)
9522
- throw new Error(`secrets.type: ${error.code}: ${error.message} Nothing was typed.`);
9523
- throw error;
9524
- });
9525
- typed = true;
9526
- if (options.submit !== true)
9527
- return { typed: true, name, origin, field };
9528
- const submitControl = await handle.evaluateHandle((node) => {
9529
- const form = node.form;
9530
- if (!form)
9531
- return null;
9532
- return form.querySelector("button[type=submit], input[type=submit], button:not([type])");
9533
- });
9534
- const button = submitControl.asElement();
9535
- if (button)
9536
- await button.click({ timeout: 5000 }).catch(() => handle.press("Enter"));
9537
- else
9538
- await handle.press("Enter");
9539
- const progressed = await waitForProgress(frame?.page() ?? page, handle, 15000);
9540
- return { typed: true, name, origin, field, submitted: true, progressed };
9541
- } finally {
9542
- if (typed && field !== "username")
9543
- await clearSecretInputs(frame ? [frame] : [], [handle]);
9544
- await handle.dispose().catch(() => {});
9545
- }
9546
- }
9547
- async usePasskey(page, options) {
9548
- const site = typeof options?.site === "string" ? options.site.trim().toLowerCase() : "";
9549
- const username = typeof options?.username === "string" ? options.username.trim() : "";
9550
- if (!site || !username)
9551
- throw new Error("secrets.usePasskey({ site, username }): site and username are required");
9552
- if (!this.deps.passkeys)
9553
- throw new Error("secrets.usePasskey: passkeys aren't available in this relay");
9554
- this.deps.passkeys.select(await pageTargetId(page), site, username);
9555
- return { selected: true, site, username };
9556
- }
9557
- async signIn(page, options = {}) {
9558
- if (options !== null && typeof options !== "object")
9559
- throw new Error("secrets.signIn(options): options must be an object like { site, username }");
9560
- const site = typeof options?.site === "string" ? options.site : undefined;
9561
- const username = typeof options?.username === "string" ? options.username : undefined;
9562
- const timeoutMs = typeof options?.timeoutMs === "number" && options.timeoutMs > 0 ? Math.min(options.timeoutMs, 300000) : undefined;
9563
- return await signIn({
9564
- page,
9565
- context: page.context(),
9566
- type: async (handles, spec, field) => (await this.typeDeadbolt(handles, spec, field, "secrets.signIn")).login,
9567
- logins: (origin) => this.deadbolt().logins(origin),
9568
- passkeyAccounts: async (origin) => (await this.deadbolt().site(origin)).passkeys,
9569
- challenge: async (target) => {
9570
- const outcome = await detectChallenge(target, { waitMs: challengeWaitMs() }).catch(() => {
9571
- return;
9572
- });
9573
- return outcome && outcome.status === "blocked" ? outcome : undefined;
9574
- },
9575
- allowed: (url) => this.sessionAllows(url),
9576
- ...this.deps.passkeys ? {
9577
- passkeys: {
9578
- select: async (target, passkeySite, passkeyUsername) => this.deps.passkeys.select(await pageTargetId(target), passkeySite, passkeyUsername),
9579
- choiceSince: async (target, since) => this.deps.passkeys.choiceSince(await pageTargetId(target), since)
9580
- }
9581
- } : {}
9582
- }, { ...site ? { site } : {}, ...username ? { username } : {}, ...timeoutMs ? { timeoutMs } : {} });
9583
- }
9584
8856
  }
9585
8857
 
9586
8858
  // src/network-capture.ts
@@ -10476,7 +9748,6 @@ class ExecuteSandbox extends DefaultPageHost {
10476
9748
  this.secrets = new ExecuteSecretsBridge({
10477
9749
  networkCapture: this.networkCapture,
10478
9750
  allowedOrigins: () => this.options.allowedOrigins?.(),
10479
- ...this.options.passkeys ? { passkeys: this.options.passkeys } : {},
10480
9751
  addWarningOnce: (warning) => this.addWarningOnce(warning)
10481
9752
  });
10482
9753
  this.realm = this.allowNode ? undefined : this.createRealm();
@@ -10489,9 +9760,6 @@ class ExecuteSandbox extends DefaultPageHost {
10489
9760
  return new ExecuteRealm({
10490
9761
  onHostCall: (fn, receiver, args) => {
10491
9762
  this.addWarningOnce(pageVisibleApiWarning(fn, receiver, args));
10492
- const hook = pageHookCall(fn, args);
10493
- if (hook)
10494
- this.secrets.pageHooks.add(hook);
10495
9763
  }
10496
9764
  });
10497
9765
  }
@@ -10641,7 +9909,6 @@ class ExecuteSandbox extends DefaultPageHost {
10641
9909
  sandbox.realm?.dispose();
10642
9910
  sandbox.realm = sandbox.allowNode ? undefined : sandbox.createRealm();
10643
9911
  sandbox.state = sandbox.realm?.state ?? {};
10644
- sandbox.secrets.pageHooks.clear();
10645
9912
  const browser = sandbox.browser;
10646
9913
  sandbox.browser = undefined;
10647
9914
  sandbox.unbindDefaultPage(sandbox.defaultPageTargetId, {
@@ -10947,36 +10214,36 @@ function unsignedInteger(value) {
10947
10214
  }
10948
10215
  return Buffer.from(bytes);
10949
10216
  }
10950
- function element2(id, payload) {
10217
+ function element(id, payload) {
10951
10218
  return Buffer.concat([id, variableInteger(payload.length), payload]);
10952
10219
  }
10953
10220
  function mjpegMatroskaHeader(width, height) {
10954
- const ebmlHeader = element2(ebml, Buffer.concat([
10955
- element2(ebmlVersion, unsignedInteger(1)),
10956
- element2(ebmlReadVersion, unsignedInteger(1)),
10957
- element2(ebmlMaxIdLength, unsignedInteger(4)),
10958
- element2(ebmlMaxSizeLength, unsignedInteger(8)),
10959
- element2(docType, Buffer.from("matroska")),
10960
- element2(docTypeVersion, unsignedInteger(4)),
10961
- element2(docTypeReadVersion, unsignedInteger(2))
10221
+ const ebmlHeader = element(ebml, Buffer.concat([
10222
+ element(ebmlVersion, unsignedInteger(1)),
10223
+ element(ebmlReadVersion, unsignedInteger(1)),
10224
+ element(ebmlMaxIdLength, unsignedInteger(4)),
10225
+ element(ebmlMaxSizeLength, unsignedInteger(8)),
10226
+ element(docType, Buffer.from("matroska")),
10227
+ element(docTypeVersion, unsignedInteger(4)),
10228
+ element(docTypeReadVersion, unsignedInteger(2))
10962
10229
  ]));
10963
- const streamInfo = element2(info, Buffer.concat([
10964
- element2(timestampScale, unsignedInteger(1e6)),
10965
- element2(muxingApp, Buffer.from("opencode-browser")),
10966
- element2(writingApp, Buffer.from("opencode-browser"))
10230
+ const streamInfo = element(info, Buffer.concat([
10231
+ element(timestampScale, unsignedInteger(1e6)),
10232
+ element(muxingApp, Buffer.from("opencode-browser")),
10233
+ element(writingApp, Buffer.from("opencode-browser"))
10967
10234
  ]));
10968
- const track = element2(trackEntry, Buffer.concat([
10969
- element2(trackNumber, unsignedInteger(1)),
10970
- element2(trackUid, unsignedInteger(1)),
10971
- element2(trackType, unsignedInteger(1)),
10972
- element2(flagLacing, unsignedInteger(0)),
10973
- element2(codecId, Buffer.from("V_MJPEG")),
10974
- element2(video, Buffer.concat([
10975
- element2(pixelWidth, unsignedInteger(width)),
10976
- element2(pixelHeight, unsignedInteger(height))
10235
+ const track = element(trackEntry, Buffer.concat([
10236
+ element(trackNumber, unsignedInteger(1)),
10237
+ element(trackUid, unsignedInteger(1)),
10238
+ element(trackType, unsignedInteger(1)),
10239
+ element(flagLacing, unsignedInteger(0)),
10240
+ element(codecId, Buffer.from("V_MJPEG")),
10241
+ element(video, Buffer.concat([
10242
+ element(pixelWidth, unsignedInteger(width)),
10243
+ element(pixelHeight, unsignedInteger(height))
10977
10244
  ]))
10978
10245
  ]));
10979
- return Buffer.concat([ebmlHeader, segment, unknownSize, streamInfo, element2(tracks, track)]);
10246
+ return Buffer.concat([ebmlHeader, segment, unknownSize, streamInfo, element(tracks, track)]);
10980
10247
  }
10981
10248
  function mjpegMatroskaFrame(timestampMs, durationMs, frameLength) {
10982
10249
  const blockHeader = Buffer.concat([
@@ -10986,8 +10253,8 @@ function mjpegMatroskaFrame(timestampMs, durationMs, frameLength) {
10986
10253
  Buffer.from([0, 0]),
10987
10254
  Buffer.from([0])
10988
10255
  ]);
10989
- const clusterTimestamp = element2(timestamp, unsignedInteger(timestampMs));
10990
- const duration = element2(blockDuration, unsignedInteger(Math.max(1, durationMs)));
10256
+ const clusterTimestamp = element(timestamp, unsignedInteger(timestampMs));
10257
+ const duration = element(blockDuration, unsignedInteger(Math.max(1, durationMs)));
10991
10258
  const groupHeader = Buffer.concat([
10992
10259
  blockGroup,
10993
10260
  variableInteger(blockHeader.length + frameLength + duration.length),
@@ -14331,28 +13598,6 @@ function handleAuthRequest(options) {
14331
13598
  }
14332
13599
  function handleCliRequest(options) {
14333
13600
  return Effect22.gen(function* () {
14334
- if (options.pathname === "/cli/deadbolt/logins" && options.request.method === "POST") {
14335
- const request = yield* decodeRequest(DeadboltLoginsRequest, yield* readJsonBody2(options.request), "deadbolt logins");
14336
- const sessionId = request.sessionId;
14337
- const patterns = sessionId ? options.sessions.allowedOrigins(sessionId) : undefined;
14338
- const pageOrigins = options.registry.listRootTargets().filter((target) => sessionId !== undefined && target.opencodeBrowserSessionId === sessionId).flatMap((target) => {
14339
- try {
14340
- const url = new URL(target.targetInfo.url);
14341
- const allowed = !patterns || patterns.length === 0 || checkUrl(url.href, parseOriginPatterns(patterns)).allowed;
14342
- return (url.protocol === "https:" || url.protocol === "http:") && allowed ? [url.origin] : [];
14343
- } catch {
14344
- return [];
14345
- }
14346
- });
14347
- const origins = Array.from(new Set([...patterns && patterns.length > 0 ? originsFromPatterns(patterns) : [], ...pageOrigins]));
14348
- const listing = yield* Effect22.promise(() => listLogins(origins));
14349
- sendJson2(options.response, {
14350
- ...listing,
14351
- origins,
14352
- ...patterns && patterns.length > 0 ? {} : { note: "This session has no domain allowlist, so only the sites of its open pages were checked. Set one with session_allowlist to see the logins for the sites it may visit." }
14353
- });
14354
- return;
14355
- }
14356
13601
  if (options.pathname === "/cli/sessions" && options.request.method === "GET") {
14357
13602
  sendJson2(options.response, { sessions: options.sessions.listSummaries() });
14358
13603
  return;
@@ -14832,285 +14077,6 @@ async function paceKey(tab, params) {
14832
14077
  tab.lastKeyAt = Date.now();
14833
14078
  }
14834
14079
 
14835
- // src/auth/passkey-bridge.ts
14836
- var selectionTtlMs = 2 * 60000;
14837
- var maxPayloadBytes = 64 * 1024;
14838
- function hookSource(binding, reply) {
14839
- return `(() => {
14840
- const BINDING = ${JSON.stringify(binding)};
14841
- const REPLY = ${JSON.stringify(reply)};
14842
- // Chrome adds a binding only to the documents that exist when it is added (without Runtime.enable, which pages can
14843
- // detect), so the relay adds it again after every navigation. Take it off the global object as soon as it shows up.
14844
- let send = null;
14845
- const capture = () => {
14846
- if (send) return send;
14847
- const binding = globalThis[BINDING];
14848
- if (typeof binding === "function") {
14849
- send = binding;
14850
- try { delete globalThis[BINDING]; } catch {}
14851
- }
14852
- return send;
14853
- };
14854
- capture();
14855
- document.addEventListener("readystatechange", capture);
14856
- if (window !== window.top) return;
14857
- const container = globalThis.CredentialsContainer && CredentialsContainer.prototype;
14858
- if (!container || typeof container.get !== "function" || globalThis[REPLY]) return;
14859
- const originalGet = container.get;
14860
- const pending = new Map();
14861
- let nextId = 1;
14862
- const toBytes = (value) => value instanceof ArrayBuffer ? new Uint8Array(value) : ArrayBuffer.isView(value) ? new Uint8Array(value.buffer, value.byteOffset, value.byteLength) : null;
14863
- const encode = (value) => {
14864
- const bytes = toBytes(value);
14865
- if (!bytes) return null;
14866
- let text = "";
14867
- for (const byte of bytes) text += String.fromCharCode(byte);
14868
- return btoa(text).replace(/\\+/g, "-").replace(/\\//g, "_").replace(/=+$/, "");
14869
- };
14870
- const decode = (text) => {
14871
- const binary = atob(text.replace(/-/g, "+").replace(/_/g, "/") + "===".slice((text.length + 3) % 4));
14872
- const bytes = new Uint8Array(binary.length);
14873
- for (let index = 0; index < binary.length; index++) bytes[index] = binary.charCodeAt(index);
14874
- return bytes.buffer;
14875
- };
14876
- const assertion = (result) => {
14877
- const value = (v) => ({ value: v, enumerable: true });
14878
- const response = Object.create(AuthenticatorAssertionResponse.prototype, {
14879
- clientDataJSON: value(decode(result.client_data_json)),
14880
- authenticatorData: value(decode(result.authenticator_data)),
14881
- signature: value(decode(result.signature)),
14882
- userHandle: value(result.user_handle ? decode(result.user_handle) : null),
14883
- });
14884
- const json = {
14885
- id: result.credential_id, rawId: result.credential_id, type: "public-key", authenticatorAttachment: "platform",
14886
- response: { clientDataJSON: result.client_data_json, authenticatorData: result.authenticator_data, signature: result.signature, ...(result.user_handle ? { userHandle: result.user_handle } : {}) },
14887
- clientExtensionResults: {},
14888
- };
14889
- return Object.create(PublicKeyCredential.prototype, {
14890
- id: value(result.credential_id),
14891
- rawId: value(decode(result.credential_id)),
14892
- type: value("public-key"),
14893
- authenticatorAttachment: value("platform"),
14894
- response: value(response),
14895
- getClientExtensionResults: { value: () => ({}) },
14896
- toJSON: { value: () => json },
14897
- });
14898
- };
14899
- Object.defineProperty(globalThis, REPLY, {
14900
- value: (id, result) => {
14901
- const entry = pending.get(id);
14902
- if (!entry) return;
14903
- pending.delete(id);
14904
- if (result && result.ok) {
14905
- try { entry.resolve(assertion(result.assertion)); } catch (error) { entry.reject(new DOMException("The passkey answer was malformed.", "NotAllowedError")); }
14906
- } else {
14907
- entry.reject(new DOMException("The operation either timed out or was not allowed.", "NotAllowedError"));
14908
- }
14909
- },
14910
- });
14911
- const get = { get(options) {
14912
- const publicKey = options && options.publicKey;
14913
- if (!publicKey || options.mediation === "conditional" || options.password || options.federated || options.identity || options.otp) return originalGet.call(this, options);
14914
- const challenge = encode(publicKey.challenge);
14915
- if (!challenge) return Promise.reject(new TypeError("publicKey.challenge must be a BufferSource"));
14916
- const allowCredentials = Array.isArray(publicKey.allowCredentials) ? publicKey.allowCredentials.map((credential) => encode(credential && credential.id)).filter(Boolean) : [];
14917
- const signal = options.signal;
14918
- if (signal && signal.aborted) return Promise.reject(signal.reason ?? new DOMException("The operation was aborted.", "AbortError"));
14919
- return new Promise(async (resolve, reject) => {
14920
- const id = nextId++;
14921
- pending.set(id, { resolve, reject });
14922
- if (signal) signal.addEventListener("abort", () => { if (pending.delete(id)) reject(signal.reason ?? new DOMException("The operation was aborted.", "AbortError")); }, { once: true });
14923
- for (let tries = 0; tries < 60 && !capture(); tries++) await new Promise((wake) => setTimeout(wake, 50));
14924
- if (!send) {
14925
- pending.delete(id);
14926
- return reject(new DOMException("The operation either timed out or was not allowed.", "NotAllowedError"));
14927
- }
14928
- send(JSON.stringify({ id, challenge, rpId: typeof publicKey.rpId === "string" ? publicKey.rpId : undefined, allowCredentials, userVerification: typeof publicKey.userVerification === "string" ? publicKey.userVerification : "preferred" }));
14929
- });
14930
- } }.get;
14931
- Object.defineProperty(container, "get", { value: get, writable: true, configurable: true, enumerable: true });
14932
- })();`;
14933
- }
14934
- function parsePasskeyRequest(payload) {
14935
- if (typeof payload !== "string" || payload.length > maxPayloadBytes)
14936
- return;
14937
- let value;
14938
- try {
14939
- value = JSON.parse(payload);
14940
- } catch {
14941
- return;
14942
- }
14943
- if (!value || typeof value !== "object")
14944
- return;
14945
- const { id, challenge, rpId, allowCredentials, userVerification } = value;
14946
- if (typeof id !== "number" || !Number.isSafeInteger(id) || id < 1)
14947
- return;
14948
- if (!isBase64Url(challenge, 1024) || challenge.length < 16)
14949
- return;
14950
- if (rpId !== undefined && (typeof rpId !== "string" || rpId.length > 253 || !/^[a-z0-9.-]+$/i.test(rpId)))
14951
- return;
14952
- if (!Array.isArray(allowCredentials) || allowCredentials.length > 64 || !allowCredentials.every((entry) => isBase64Url(entry, 1400)))
14953
- return;
14954
- const verification = typeof userVerification === "string" && ["required", "preferred", "discouraged"].includes(userVerification) ? userVerification : "preferred";
14955
- return { id, challenge, ...rpId ? { rpId: rpId.toLowerCase() } : {}, allowCredentials, userVerification: verification };
14956
- }
14957
- function rpIdAllowed(origin, rpId) {
14958
- let url;
14959
- try {
14960
- url = new URL(origin);
14961
- } catch {
14962
- return false;
14963
- }
14964
- const host = url.hostname.toLowerCase();
14965
- const secure = url.protocol === "https:" || url.protocol === "http:" && (host === "localhost" || host.endsWith(".localhost") || /^127\.\d+\.\d+\.\d+$/.test(host) || host === "[::1]");
14966
- if (!secure)
14967
- return false;
14968
- if (rpId === undefined)
14969
- return true;
14970
- return host === rpId || rpId.includes(".") && host.endsWith(`.${rpId}`);
14971
- }
14972
-
14973
- class PasskeyBridge {
14974
- installs = new Map;
14975
- selections = new Map;
14976
- choices = new Map;
14977
- options;
14978
- constructor(options) {
14979
- this.options = options;
14980
- }
14981
- async install(tabId) {
14982
- const connected = this.options.connected ?? (() => readClient() !== undefined);
14983
- if (!connected())
14984
- return false;
14985
- const install = { binding: randomName("__b"), reply: randomName("__r") };
14986
- this.installs.set(tabId, install);
14987
- try {
14988
- await this.options.send(tabId, "Runtime.addBinding", { name: install.binding });
14989
- await this.options.send(tabId, "Page.addScriptToEvaluateOnNewDocument", { source: hookSource(install.binding, install.reply), runImmediately: true });
14990
- this.options.log?.("passkey.install", { tabId });
14991
- return true;
14992
- } catch (error) {
14993
- this.installs.delete(tabId);
14994
- this.options.log?.("passkey.install.failed", { tabId, error: error instanceof Error ? error.message : String(error) });
14995
- return false;
14996
- }
14997
- }
14998
- navigated(tabId) {
14999
- const install = this.installs.get(tabId);
15000
- if (!install)
15001
- return;
15002
- this.options.send(tabId, "Runtime.addBinding", { name: install.binding }).catch(() => {});
15003
- }
15004
- select(tabId, site, username, now = Date.now()) {
15005
- this.selections.set(tabId, { site, username, expiresAt: now + selectionTtlMs });
15006
- }
15007
- clearSelection(tabId) {
15008
- this.selections.delete(tabId);
15009
- }
15010
- choiceSince(tabId, since) {
15011
- const event = this.choices.get(tabId);
15012
- return event && event.at >= since ? event.choices : undefined;
15013
- }
15014
- forget(tabId) {
15015
- this.installs.delete(tabId);
15016
- this.selections.delete(tabId);
15017
- this.choices.delete(tabId);
15018
- }
15019
- handleEvent(tabId, method, params, fromRoot) {
15020
- if (method !== "Runtime.bindingCalled")
15021
- return false;
15022
- const install = this.installs.get(tabId);
15023
- if (!install || params?.name !== install.binding)
15024
- return false;
15025
- this.options.log?.("passkey.request", { tabId, fromRoot });
15026
- if (!fromRoot)
15027
- return true;
15028
- const contextId = params.executionContextId;
15029
- if (typeof contextId !== "number")
15030
- return true;
15031
- this.answer(tabId, install, contextId, params.payload);
15032
- return true;
15033
- }
15034
- async answer(tabId, install, contextId, payload) {
15035
- const request = parsePasskeyRequest(payload);
15036
- if (!request) {
15037
- this.options.log?.("passkey.refused", { tabId, reason: "malformed request" });
15038
- return;
15039
- }
15040
- let result;
15041
- try {
15042
- result = { ok: true, assertion: await this.assert(tabId, contextId, request) };
15043
- this.options.log?.("passkey.signed", { tabId });
15044
- } catch (error) {
15045
- result = { ok: false };
15046
- this.options.log?.("passkey.refused", { tabId, reason: error instanceof DeadboltError ? error.code : error instanceof Error ? error.message : "failed" });
15047
- }
15048
- await this.options.send(tabId, "Runtime.evaluate", {
15049
- expression: `globalThis[${JSON.stringify(install.reply)}]?.(${request.id}, ${JSON.stringify(result)})`,
15050
- contextId,
15051
- returnByValue: true
15052
- }).catch(() => {});
15053
- }
15054
- async assert(tabId, contextId, request) {
15055
- const top = await this.options.send(tabId, "Runtime.evaluate", { expression: "window === top", contextId, returnByValue: true });
15056
- if (top.result?.value !== true)
15057
- throw new Error("not the top frame");
15058
- const tree = await this.options.send(tabId, "Page.getFrameTree", {});
15059
- const frame = tree.frameTree?.frame;
15060
- const url = typeof frame?.url === "string" ? frame.url : "";
15061
- let origin = typeof frame?.securityOrigin === "string" ? frame.securityOrigin : "";
15062
- try {
15063
- if (!origin || origin === "null")
15064
- origin = new URL(url).origin;
15065
- } catch {
15066
- throw new Error("no origin");
15067
- }
15068
- if (!rpIdAllowed(origin, request.rpId))
15069
- throw new Error("rpId not allowed for the origin");
15070
- const patterns = this.options.allowedOrigins(tabId);
15071
- if (patterns && patterns.length > 0 && !checkUrl(url, parseOriginPatterns(patterns)).allowed)
15072
- throw new Error("outside the session allowlist");
15073
- const deadbolt = (this.options.deadbolt ?? (() => connect()))();
15074
- const selection = this.selections.get(tabId);
15075
- this.selections.delete(tabId);
15076
- let login;
15077
- if (selection && selection.expiresAt >= Date.now() && originOnSite(origin, selection.site)) {
15078
- const passkey = pickPasskey((await deadbolt.site(origin)).passkeys, selection.username);
15079
- if (!passkey)
15080
- throw new DeadboltError("not_granted", `No granted passkey for ${selection.username} on ${selection.site}`);
15081
- login = passkey.ref;
15082
- }
15083
- const assertion = await deadbolt.passkeyGet({
15084
- origin,
15085
- ...request.rpId ? { rpId: request.rpId } : {},
15086
- challenge: request.challenge,
15087
- allowCredentials: request.allowCredentials,
15088
- userVerification: request.userVerification,
15089
- ...login !== undefined ? { login } : {}
15090
- }).catch((error) => {
15091
- if (error instanceof DeadboltError && error.code === "choose_passkey") {
15092
- const offered = (error.detail.choices ?? []).map((choice) => ({
15093
- ...choice.username ? { username: choice.username } : {},
15094
- ...choice.displayName ? { displayName: choice.displayName } : {}
15095
- }));
15096
- this.choices.set(tabId, { at: Date.now(), choices: offered });
15097
- }
15098
- throw error;
15099
- });
15100
- for (const value of [assertion.credentialId, assertion.clientDataJson, assertion.authenticatorData, assertion.signature, assertion.userHandle ?? ""]) {
15101
- if (!isBase64Url(value, 16384))
15102
- throw new Error("Deadbolt's assertion isn't base64url");
15103
- }
15104
- return {
15105
- credential_id: assertion.credentialId,
15106
- client_data_json: assertion.clientDataJson,
15107
- authenticator_data: assertion.authenticatorData,
15108
- signature: assertion.signature,
15109
- ...assertion.userHandle ? { user_handle: assertion.userHandle } : {}
15110
- };
15111
- }
15112
- }
15113
-
15114
14080
  // src/cdp/protected-frames.ts
15115
14081
  import { Predicate as Predicate18 } from "effect";
15116
14082
  var navigationIntentMethods = new Set(["Page.frameRequestedNavigation", "Page.frameScheduledNavigation", "Page.frameStartedNavigating"]);
@@ -15354,7 +14320,7 @@ function guardCdpMethod(context) {
15354
14320
  return `browse blocked ${context.method}: ${reason}. This command is always blocked by the relay.`;
15355
14321
  }
15356
14322
  if (context.method.startsWith("WebAuthn.")) {
15357
- return `browse blocked ${context.method}: agents can't drive WebAuthn or add virtual authenticators. Passkey sign-ins in agent tabs use the logins the user granted in Deadbolt. This command is always blocked by the relay.`;
14323
+ return `browse blocked ${context.method}: agents can't drive WebAuthn or add virtual authenticators. This command is always blocked by the relay.`;
15358
14324
  }
15359
14325
  if (context.method === "Page.bringToFront" && !allowsForeground()) {
15360
14326
  return "browse blocked Page.bringToFront: it raises the user's browser window over their work. Use handoff() when the user needs to act in the tab, or set OPENCODE_BROWSER_ALLOW_FOREGROUND=1 on the relay to allow it.";
@@ -16007,7 +14973,6 @@ function createExtensionEventHandler(options) {
16007
14973
  handoffs,
16008
14974
  recordingRelay,
16009
14975
  flightRecorder,
16010
- passkeys,
16011
14976
  navigationGuard,
16012
14977
  ghostCursor,
16013
14978
  tabActivity,
@@ -16316,8 +15281,6 @@ function createExtensionEventHandler(options) {
16316
15281
  if (sourceSessionId && method !== "Target.attachedToTarget" && method !== "Target.detachedFromTarget" && method !== "Target.targetInfoChanged" && (suppressedChildSessions.has(sourceSessionId) || sourceChild && !shouldExposeChildTarget(sourceChild))) {
16317
15282
  return;
16318
15283
  }
16319
- if (passkeys.handleEvent(tabId, method, params, sourceSessionId === undefined || sourceSessionId === target.sessionId))
16320
- return;
16321
15284
  if (flightRecorder.handleDebuggerEvent({ tabId, method, params }) || recordingRelay.handleDebuggerEvent({ tabId, method, params }))
16322
15285
  return;
16323
15286
  if (sourceSessionId === undefined || sourceSessionId === target.sessionId)
@@ -16450,7 +15413,6 @@ function createExtensionEventHandler(options) {
16450
15413
  Effect27.runPromise(Effect27.ignore(ghostCursor.documentCreated(tabId))).catch(() => {});
16451
15414
  contextDebugLog?.(`main-frame-navigated frame=${boundedToken(frameId)} loader=${boundedToken(getString2(frame, "loaderId"))} ${targetDiagnosticIdentity(registry, target)} ${summarizeDiagnosticUrl(frameUrl)}`);
16452
15415
  registry.updateTargetUrl(tabId, frameUrl);
16453
- passkeys.navigated(tabId);
16454
15416
  blankNavigationWaiters.get(tabId)?.(frameUrl, getString2(frame, "loaderId"));
16455
15417
  Effect27.runPromise(Effect27.ignore(sendDebuggerCommand({ tabId, method: "Emulation.setFocusEmulationEnabled", params: { enabled: tabActivity.isFocused(tabId) } }))).catch(() => {});
16456
15418
  }
@@ -17157,7 +16119,6 @@ function createTabPresentation(options) {
17157
16119
  registry,
17158
16120
  browsers,
17159
16121
  handoffs,
17160
- passkeys,
17161
16122
  tabActivity,
17162
16123
  mainFrameIdsByTab,
17163
16124
  sendToExtension,
@@ -17380,9 +16341,6 @@ function createTabPresentation(options) {
17380
16341
  if (committedTarget.opencodeBrowserSessionId) {
17381
16342
  pruneInvisibleAnnouncementsForSession(committedTarget.opencodeBrowserSessionId);
17382
16343
  }
17383
- if (committedTarget.owner === "relay" && committedTarget.opencodeBrowserSessionId) {
17384
- passkeys.install(tabId);
17385
- }
17386
16344
  if (browsers.browserTab(tabId)?.link.rpc.connected) {
17387
16345
  refreshTabGrouping(tabId);
17388
16346
  }
@@ -18195,14 +17153,6 @@ var makeRelay = Effect34.fnUntraced(function* (options = {}) {
18195
17153
  const handoffs = new HandoffRegistry;
18196
17154
  const ownersAcrossReconnect = new Map;
18197
17155
  const sweptBrowsers = new Set;
18198
- const passkeys = new PasskeyBridge({
18199
- send: (tabId, method, params) => Effect34.runPromise(sendDebuggerCommand({ tabId, method, params })),
18200
- allowedOrigins: (tabId) => {
18201
- const sessionId = registry.tabTargets.get(tabId)?.opencodeBrowserSessionId;
18202
- return sessionId ? sessions.allowedOrigins(sessionId) : undefined;
18203
- },
18204
- log: (event, fields) => relayLog(event, fields)
18205
- });
18206
17156
  const recordingRelay = new RecordingRelay({
18207
17157
  sendToExtension: (command) => Effect34.runPromise(sendToExtension(command)),
18208
17158
  sendDebuggerCommand: (command) => Effect34.runPromise(sendDebuggerCommand(command)),
@@ -18237,7 +17187,6 @@ var makeRelay = Effect34.fnUntraced(function* (options = {}) {
18237
17187
  registry,
18238
17188
  browsers,
18239
17189
  handoffs,
18240
- passkeys,
18241
17190
  tabActivity,
18242
17191
  mainFrameIdsByTab,
18243
17192
  sendToExtension,
@@ -18304,22 +17253,6 @@ var makeRelay = Effect34.fnUntraced(function* (options = {}) {
18304
17253
  onDefaultTargetChange,
18305
17254
  allowedOrigins: () => sessions.allowedOrigins(id),
18306
17255
  drainGuardWarnings: () => navigationGuard.drainWarnings(id),
18307
- passkeys: {
18308
- select: (targetId, site, username) => {
18309
- const target = registry.targetsByTargetId.get(targetId);
18310
- if (!target || !("owner" in target) || target.owner !== "relay" || target.opencodeBrowserSessionId !== id) {
18311
- throw new Error("secrets.usePasskey: passkeys are answered only in tabs this session opened");
18312
- }
18313
- if (username)
18314
- passkeys.select(target.tabId, site, username);
18315
- else
18316
- passkeys.clearSelection(target.tabId);
18317
- },
18318
- choiceSince: (targetId, since) => {
18319
- const target = registry.targetsByTargetId.get(targetId);
18320
- return target && target.opencodeBrowserSessionId === id ? passkeys.choiceSince(target.tabId, since) : undefined;
18321
- }
18322
- },
18323
17256
  isRecordingTarget: (targetId) => {
18324
17257
  const target = registry.targetsByTargetId.get(targetId);
18325
17258
  return target !== undefined && isTabRecorded(target.tabId);
@@ -18515,7 +17448,6 @@ var makeRelay = Effect34.fnUntraced(function* (options = {}) {
18515
17448
  navigationGuard.forgetTab(tabId);
18516
17449
  ghostCursor.forget(tabId);
18517
17450
  forgetTab(tabId);
18518
- passkeys.forget(tabId);
18519
17451
  for (const [sessionId, childTabId] of suppressedChildSessions) {
18520
17452
  if (childTabId === tabId)
18521
17453
  suppressedChildSessions.delete(sessionId);
@@ -18596,7 +17528,6 @@ var makeRelay = Effect34.fnUntraced(function* (options = {}) {
18596
17528
  handoffs,
18597
17529
  recordingRelay,
18598
17530
  flightRecorder,
18599
- passkeys,
18600
17531
  navigationGuard,
18601
17532
  ghostCursor,
18602
17533
  tabActivity,