@different-ai/opencode-browser 4.3.2 → 4.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/plugin.js CHANGED
@@ -12330,22 +12330,700 @@ function tool(input) {
12330
12330
  }
12331
12331
  tool.schema = exports_external;
12332
12332
  // src/plugin.ts
12333
+ import net2 from "net";
12334
+
12335
+ // src/agent-backend.ts
12333
12336
  import net from "net";
12334
- import { existsSync, mkdirSync, readFileSync } from "fs";
12335
- import { homedir } from "os";
12336
- import { dirname, join } from "path";
12337
+ import { readFileSync } from "fs";
12338
+ import { tmpdir } from "os";
12339
+ import { join } from "path";
12337
12340
  import { spawn } from "child_process";
12341
+ import { createRequire } from "module";
12342
+ var agentRequire = createRequire(import.meta.url);
12343
+ var REQUEST_TIMEOUT_MS = 60000;
12344
+ var DEFAULT_PAGE_TEXT_LIMIT = 20000;
12345
+ var DEFAULT_LIST_LIMIT = 50;
12346
+ var DEFAULT_POLL_MS = 200;
12347
+ function createJsonLineParser(onMessage) {
12348
+ let buffer = "";
12349
+ return (chunk) => {
12350
+ buffer += chunk.toString("utf8");
12351
+ while (true) {
12352
+ const idx = buffer.indexOf(`
12353
+ `);
12354
+ if (idx === -1)
12355
+ return;
12356
+ const line = buffer.slice(0, idx);
12357
+ buffer = buffer.slice(idx + 1);
12358
+ if (!line.trim())
12359
+ continue;
12360
+ try {
12361
+ onMessage(JSON.parse(line));
12362
+ } catch {}
12363
+ }
12364
+ };
12365
+ }
12366
+ function writeJsonLine(socket, msg) {
12367
+ socket.write(JSON.stringify(msg) + `
12368
+ `);
12369
+ }
12370
+ async function sleep(ms) {
12371
+ return await new Promise((resolve) => setTimeout(resolve, ms));
12372
+ }
12373
+ function parseEnvNumber(value) {
12374
+ if (!value)
12375
+ return null;
12376
+ const parsed = Number(value);
12377
+ return Number.isFinite(parsed) ? parsed : null;
12378
+ }
12379
+ function getAgentSession(sessionId) {
12380
+ const override = process.env.OPENCODE_BROWSER_AGENT_SESSION?.trim();
12381
+ if (override)
12382
+ return override;
12383
+ return `opencode-${sessionId}`;
12384
+ }
12385
+ function getAgentPortForSession(session) {
12386
+ let hash2 = 0;
12387
+ for (let i = 0;i < session.length; i++) {
12388
+ hash2 = (hash2 << 5) - hash2 + session.charCodeAt(i);
12389
+ hash2 |= 0;
12390
+ }
12391
+ return 49152 + Math.abs(hash2) % 16383;
12392
+ }
12393
+ function getAgentConnectionInfo(session) {
12394
+ const socketOverride = process.env.OPENCODE_BROWSER_AGENT_SOCKET?.trim();
12395
+ if (socketOverride) {
12396
+ return { type: "unix", path: socketOverride };
12397
+ }
12398
+ const hostOverride = process.env.OPENCODE_BROWSER_AGENT_HOST?.trim();
12399
+ const portOverride = parseEnvNumber(process.env.OPENCODE_BROWSER_AGENT_PORT);
12400
+ const transportOverride = process.env.OPENCODE_BROWSER_AGENT_TRANSPORT?.toLowerCase();
12401
+ const forceTcp = transportOverride === "tcp" || process.env.OPENCODE_BROWSER_AGENT_TCP === "1";
12402
+ if (hostOverride || portOverride !== null || forceTcp || process.platform === "win32") {
12403
+ const host = hostOverride || "127.0.0.1";
12404
+ const port = portOverride ?? getAgentPortForSession(session);
12405
+ return { type: "tcp", host, port };
12406
+ }
12407
+ return { type: "unix", path: join(tmpdir(), `agent-browser-${session}.sock`) };
12408
+ }
12409
+ function isLocalHost(host) {
12410
+ return host === "127.0.0.1" || host === "localhost" || host === "::1";
12411
+ }
12412
+ function resolveAgentDaemonPath() {
12413
+ const override = process.env.OPENCODE_BROWSER_AGENT_DAEMON?.trim();
12414
+ if (override)
12415
+ return override;
12416
+ try {
12417
+ return agentRequire.resolve("agent-browser/dist/daemon.js");
12418
+ } catch {
12419
+ return null;
12420
+ }
12421
+ }
12422
+ function resolveAgentNodePath() {
12423
+ const override = process.env.OPENCODE_BROWSER_AGENT_NODE?.trim();
12424
+ return override || process.execPath;
12425
+ }
12426
+ function getAgentPackageVersion() {
12427
+ try {
12428
+ const pkgPath = agentRequire.resolve("agent-browser/package.json");
12429
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
12430
+ return typeof pkg?.version === "string" ? pkg.version : null;
12431
+ } catch {
12432
+ return null;
12433
+ }
12434
+ }
12435
+ function shouldAutoStartAgent(connection) {
12436
+ const autoStart = process.env.OPENCODE_BROWSER_AGENT_AUTOSTART?.toLowerCase();
12437
+ if (autoStart && ["0", "false", "no"].includes(autoStart))
12438
+ return false;
12439
+ if (connection.type === "unix")
12440
+ return true;
12441
+ return connection.type === "tcp" && process.platform === "win32" && isLocalHost(connection.host);
12442
+ }
12443
+ async function maybeStartAgentDaemon(connection, session) {
12444
+ if (!shouldAutoStartAgent(connection))
12445
+ return;
12446
+ const daemonPath = resolveAgentDaemonPath();
12447
+ if (!daemonPath) {
12448
+ throw new Error("agent-browser dependency not found. Install agent-browser or set OPENCODE_BROWSER_AGENT_DAEMON.");
12449
+ }
12450
+ try {
12451
+ const child = spawn(resolveAgentNodePath(), [daemonPath], {
12452
+ detached: true,
12453
+ stdio: "ignore",
12454
+ env: {
12455
+ ...process.env,
12456
+ AGENT_BROWSER_SESSION: session,
12457
+ AGENT_BROWSER_DAEMON: "1"
12458
+ }
12459
+ });
12460
+ child.unref();
12461
+ } catch {}
12462
+ }
12463
+ function buildEvalScript(body) {
12464
+ return `(() => { ${body} })()`;
12465
+ }
12466
+ function buildAgentTypeScript(selector, indexValue, text, clear) {
12467
+ const payload = { selector, index: indexValue, text, clear };
12468
+ return buildEvalScript(`
12469
+ const payload = ${JSON.stringify(payload)};
12470
+ let matches = [];
12471
+ try {
12472
+ matches = Array.from(document.querySelectorAll(payload.selector));
12473
+ } catch {
12474
+ return { ok: false, error: "Invalid selector" };
12475
+ }
12476
+ const element = matches[payload.index];
12477
+ if (!element) return { ok: false, error: "Element not found" };
12478
+ const tag = element.tagName ? element.tagName.toUpperCase() : "";
12479
+ if (tag === "INPUT" || tag === "TEXTAREA") {
12480
+ if (payload.clear) element.value = "";
12481
+ element.value = (element.value || "") + payload.text;
12482
+ element.dispatchEvent(new Event("input", { bubbles: true }));
12483
+ element.dispatchEvent(new Event("change", { bubbles: true }));
12484
+ return { ok: true };
12485
+ }
12486
+ if (element.isContentEditable) {
12487
+ if (payload.clear) element.textContent = "";
12488
+ element.textContent = (element.textContent || "") + payload.text;
12489
+ element.dispatchEvent(new Event("input", { bubbles: true }));
12490
+ return { ok: true };
12491
+ }
12492
+ return { ok: false, error: "Element is not typable" };
12493
+ `);
12494
+ }
12495
+ function buildAgentSelectScript(selector, indexValue, value, label, optionIndex) {
12496
+ const payload = {
12497
+ selector,
12498
+ index: indexValue,
12499
+ value: value ?? null,
12500
+ label: label ?? null,
12501
+ optionIndex: Number.isFinite(optionIndex) ? optionIndex : null
12502
+ };
12503
+ return buildEvalScript(`
12504
+ const payload = ${JSON.stringify(payload)};
12505
+ let matches = [];
12506
+ try {
12507
+ matches = Array.from(document.querySelectorAll(payload.selector));
12508
+ } catch {
12509
+ return { ok: false, error: "Invalid selector" };
12510
+ }
12511
+ const element = matches[payload.index];
12512
+ if (!element) return { ok: false, error: "Element not found" };
12513
+ if (!element.tagName || element.tagName.toUpperCase() !== "SELECT") {
12514
+ return { ok: false, error: "Element is not a select" };
12515
+ }
12516
+ const options = Array.from(element.options || []);
12517
+ let chosen = null;
12518
+ if (payload.value !== null) {
12519
+ chosen = options.find((option) => option.value === payload.value) || null;
12520
+ }
12521
+ if (!chosen && payload.label !== null) {
12522
+ const target = payload.label.trim();
12523
+ chosen = options.find((option) => (option.label || option.textContent || "").trim() === target) || null;
12524
+ }
12525
+ if (!chosen && payload.optionIndex !== null) {
12526
+ chosen = options[payload.optionIndex] || null;
12527
+ }
12528
+ if (!chosen) return { ok: false, error: "Option not found" };
12529
+ element.value = chosen.value;
12530
+ chosen.selected = true;
12531
+ element.dispatchEvent(new Event("input", { bubbles: true }));
12532
+ element.dispatchEvent(new Event("change", { bubbles: true }));
12533
+ return {
12534
+ ok: true,
12535
+ value: element.value,
12536
+ label: (chosen.label || chosen.textContent || "").trim(),
12537
+ };
12538
+ `);
12539
+ }
12540
+ function buildAgentPageTextScript(limit, pattern, flags) {
12541
+ const payload = { limit, pattern, flags };
12542
+ return buildEvalScript(`
12543
+ const payload = ${JSON.stringify(payload)};
12544
+ const safeString = (value) => (typeof value === "string" ? value : "");
12545
+ const bodyText = safeString(document.body ? document.body.innerText : "");
12546
+ const inputText = Array.from(document.querySelectorAll("input, textarea, [contenteditable='true']"))
12547
+ .map((element) => {
12548
+ if (element.tagName === "INPUT" || element.tagName === "TEXTAREA") {
12549
+ return safeString(element.value);
12550
+ }
12551
+ return safeString(element.textContent);
12552
+ })
12553
+ .filter(Boolean)
12554
+ .join("
12555
+ ");
12556
+ const combined = [bodyText, inputText].filter(Boolean).join("
12557
+
12558
+ ");
12559
+ const maxSize = Number.isFinite(payload.limit) ? payload.limit : ${DEFAULT_PAGE_TEXT_LIMIT};
12560
+ const text = combined.slice(0, Math.max(0, maxSize));
12561
+ let matches = [];
12562
+ if (payload.pattern) {
12563
+ try {
12564
+ const re = new RegExp(payload.pattern, payload.flags || "i");
12565
+ let match;
12566
+ while ((match = re.exec(text)) && matches.length < 50) {
12567
+ matches.push(match[0]);
12568
+ if (!re.global) break;
12569
+ }
12570
+ } catch {
12571
+ matches = [];
12572
+ }
12573
+ }
12574
+ return {
12575
+ url: location.href,
12576
+ title: document.title,
12577
+ text,
12578
+ matches,
12579
+ };
12580
+ `);
12581
+ }
12582
+ function buildAgentListScript(selector, limit) {
12583
+ const payload = { selector, limit };
12584
+ return buildEvalScript(`
12585
+ const payload = ${JSON.stringify(payload)};
12586
+ let nodes = [];
12587
+ try {
12588
+ nodes = Array.from(document.querySelectorAll(payload.selector));
12589
+ } catch {
12590
+ return { ok: false, error: "Invalid selector" };
12591
+ }
12592
+ const maxItems = Math.min(Math.max(1, payload.limit || ${DEFAULT_LIST_LIMIT}), 200);
12593
+ const items = nodes.slice(0, maxItems).map((element) => ({
12594
+ text: (element.innerText || element.textContent || "").trim().slice(0, 200),
12595
+ tag: (element.tagName || "").toLowerCase(),
12596
+ ariaLabel: element.getAttribute ? element.getAttribute("aria-label") : null,
12597
+ }));
12598
+ return { ok: true, value: { items, count: nodes.length } };
12599
+ `);
12600
+ }
12601
+ function buildAgentNthValueScript(selector, indexValue) {
12602
+ const payload = { selector, index: indexValue };
12603
+ return buildEvalScript(`
12604
+ const payload = ${JSON.stringify(payload)};
12605
+ let nodes = [];
12606
+ try {
12607
+ nodes = Array.from(document.querySelectorAll(payload.selector));
12608
+ } catch {
12609
+ return { ok: false, error: "Invalid selector" };
12610
+ }
12611
+ const element = nodes[payload.index];
12612
+ if (!element) return { ok: false, error: "Element not found" };
12613
+ const value = element.value !== undefined ? element.value : "";
12614
+ return { ok: true, value: typeof value === "string" ? value : String(value ?? "") };
12615
+ `);
12616
+ }
12617
+ function buildAgentNthAttributeScript(selector, indexValue, attribute) {
12618
+ const payload = { selector, index: indexValue, attribute };
12619
+ return buildEvalScript(`
12620
+ const payload = ${JSON.stringify(payload)};
12621
+ let nodes = [];
12622
+ try {
12623
+ nodes = Array.from(document.querySelectorAll(payload.selector));
12624
+ } catch {
12625
+ return { ok: false, error: "Invalid selector" };
12626
+ }
12627
+ const element = nodes[payload.index];
12628
+ if (!element) return { ok: false, error: "Element not found" };
12629
+ const value = element.getAttribute ? element.getAttribute(payload.attribute) : null;
12630
+ return { ok: true, value };
12631
+ `);
12632
+ }
12633
+ function buildAgentNthPropertyScript(selector, indexValue, property) {
12634
+ const payload = { selector, index: indexValue, property };
12635
+ return buildEvalScript(`
12636
+ const payload = ${JSON.stringify(payload)};
12637
+ let nodes = [];
12638
+ try {
12639
+ nodes = Array.from(document.querySelectorAll(payload.selector));
12640
+ } catch {
12641
+ return { ok: false, error: "Invalid selector" };
12642
+ }
12643
+ const element = nodes[payload.index];
12644
+ if (!element) return { ok: false, error: "Element not found" };
12645
+ return { ok: true, value: element[payload.property] };
12646
+ `);
12647
+ }
12648
+ function buildAgentOuterHtmlScript(selector, indexValue) {
12649
+ const payload = { selector, index: indexValue };
12650
+ return buildEvalScript(`
12651
+ const payload = ${JSON.stringify(payload)};
12652
+ let nodes = [];
12653
+ try {
12654
+ nodes = Array.from(document.querySelectorAll(payload.selector));
12655
+ } catch {
12656
+ return { ok: false, error: "Invalid selector" };
12657
+ }
12658
+ const element = nodes[payload.index];
12659
+ if (!element) return { ok: false, error: "Element not found" };
12660
+ return { ok: true, value: element.outerHTML };
12661
+ `);
12662
+ }
12663
+ function ensureEvalResult(result, fallback) {
12664
+ if (!result || typeof result !== "object" || result.ok !== true) {
12665
+ const message = typeof result?.error === "string" ? result.error : fallback;
12666
+ throw new Error(message);
12667
+ }
12668
+ return result.value;
12669
+ }
12670
+ function createAgentBackend(sessionId) {
12671
+ const session = getAgentSession(sessionId);
12672
+ const connection = getAgentConnectionInfo(session);
12673
+ let agentSocket = null;
12674
+ let agentReqId = 0;
12675
+ const agentPending = new Map;
12676
+ async function connectToAgent() {
12677
+ return await new Promise((resolve, reject) => {
12678
+ const socket = connection.type === "unix" ? net.createConnection(connection.path) : net.createConnection({ host: connection.host, port: connection.port });
12679
+ socket.once("connect", () => resolve(socket));
12680
+ socket.once("error", (err) => reject(err));
12681
+ });
12682
+ }
12683
+ async function ensureAgentSocket() {
12684
+ if (agentSocket && !agentSocket.destroyed)
12685
+ return agentSocket;
12686
+ try {
12687
+ agentSocket = await connectToAgent();
12688
+ } catch {
12689
+ await maybeStartAgentDaemon(connection, session);
12690
+ for (let attempt = 0;attempt < 20; attempt++) {
12691
+ await sleep(100);
12692
+ try {
12693
+ agentSocket = await connectToAgent();
12694
+ break;
12695
+ } catch {}
12696
+ }
12697
+ }
12698
+ if (!agentSocket || agentSocket.destroyed) {
12699
+ const target = connection.type === "unix" ? connection.path : `${connection.host}:${connection.port}`;
12700
+ throw new Error(`Could not connect to agent-browser daemon at ${target}.`);
12701
+ }
12702
+ agentSocket.setNoDelay(true);
12703
+ agentSocket.on("data", createJsonLineParser((msg) => {
12704
+ if (!msg || msg.id === undefined)
12705
+ return;
12706
+ const messageId = typeof msg.id === "string" ? msg.id : String(msg.id);
12707
+ const pending = agentPending.get(messageId);
12708
+ if (!pending)
12709
+ return;
12710
+ agentPending.delete(messageId);
12711
+ const res = msg;
12712
+ if (!res.success)
12713
+ pending.reject(new Error(res.error || "Agent browser error"));
12714
+ else
12715
+ pending.resolve(res.data);
12716
+ }));
12717
+ agentSocket.on("close", () => {
12718
+ for (const pending of agentPending.values()) {
12719
+ pending.reject(new Error("Agent browser connection closed"));
12720
+ }
12721
+ agentPending.clear();
12722
+ agentSocket = null;
12723
+ });
12724
+ agentSocket.on("error", () => {
12725
+ agentSocket = null;
12726
+ });
12727
+ return agentSocket;
12728
+ }
12729
+ async function agentRequest(action, payload) {
12730
+ const socket = await ensureAgentSocket();
12731
+ const id = `a${++agentReqId}`;
12732
+ return await new Promise((resolve, reject) => {
12733
+ agentPending.set(id, { resolve, reject });
12734
+ writeJsonLine(socket, { id, action, ...payload });
12735
+ setTimeout(() => {
12736
+ if (!agentPending.has(id))
12737
+ return;
12738
+ agentPending.delete(id);
12739
+ reject(new Error("Timed out waiting for agent-browser response"));
12740
+ }, REQUEST_TIMEOUT_MS);
12741
+ });
12742
+ }
12743
+ async function agentCommand(action, payload) {
12744
+ return await agentRequest(action, payload);
12745
+ }
12746
+ async function withTab(tabId, action) {
12747
+ if (!Number.isFinite(tabId))
12748
+ return await action();
12749
+ await agentCommand("tab_switch", { index: tabId });
12750
+ return await action();
12751
+ }
12752
+ async function agentEvaluate(script) {
12753
+ const data = await agentCommand("evaluate", { script });
12754
+ return data?.result;
12755
+ }
12756
+ async function waitForCount(selector, minimum, timeoutMs, pollMs) {
12757
+ const timeout = Math.max(0, timeoutMs);
12758
+ const poll = Math.max(0, pollMs || DEFAULT_POLL_MS);
12759
+ const start = Date.now();
12760
+ while (true) {
12761
+ const data = await agentCommand("count", { selector });
12762
+ const count = Number(data?.count ?? 0);
12763
+ if (count >= minimum)
12764
+ return count;
12765
+ if (!timeout || Date.now() - start >= timeout)
12766
+ return count;
12767
+ await sleep(poll);
12768
+ }
12769
+ }
12770
+ async function agentQuery(args) {
12771
+ const selector = typeof args.selector === "string" ? args.selector : undefined;
12772
+ const mode = typeof args.mode === "string" && args.mode ? args.mode : "text";
12773
+ const indexValue = Number.isFinite(args.index) ? args.index : 0;
12774
+ const limitValue = Number.isFinite(args.limit) ? args.limit : mode === "page_text" ? DEFAULT_PAGE_TEXT_LIMIT : DEFAULT_LIST_LIMIT;
12775
+ const timeoutValue = Number.isFinite(args.timeoutMs) ? args.timeoutMs : 0;
12776
+ const pollValue = Number.isFinite(args.pollMs) ? args.pollMs : DEFAULT_POLL_MS;
12777
+ const pattern = typeof args.pattern === "string" ? args.pattern : null;
12778
+ const flags = typeof args.flags === "string" ? args.flags : "i";
12779
+ if (mode === "page_text") {
12780
+ if (selector && timeoutValue > 0) {
12781
+ await waitForCount(selector, 1, timeoutValue, pollValue);
12782
+ }
12783
+ const pageText = await agentEvaluate(buildAgentPageTextScript(limitValue, pattern, flags));
12784
+ return { content: JSON.stringify({ ok: true, value: pageText }, null, 2) };
12785
+ }
12786
+ if (!selector)
12787
+ throw new Error("selector is required");
12788
+ if (mode === "exists") {
12789
+ const count2 = await waitForCount(selector, 1, timeoutValue, pollValue);
12790
+ return {
12791
+ content: JSON.stringify({ ok: true, value: { exists: count2 > 0, count: count2 } }, null, 2)
12792
+ };
12793
+ }
12794
+ const count = await waitForCount(selector, indexValue + 1, timeoutValue, pollValue);
12795
+ if (count <= indexValue) {
12796
+ throw new Error(`No matches for selector: ${selector}`);
12797
+ }
12798
+ if (mode === "text") {
12799
+ const data = indexValue > 0 ? await agentCommand("nth", { selector, index: indexValue, subaction: "text" }) : await agentCommand("innertext", { selector });
12800
+ return { content: typeof data?.text === "string" ? data.text : "" };
12801
+ }
12802
+ if (mode === "value") {
12803
+ if (indexValue > 0) {
12804
+ const result = ensureEvalResult(await agentEvaluate(buildAgentNthValueScript(selector, indexValue)), "Value lookup failed");
12805
+ return { content: typeof result === "string" ? result : JSON.stringify(result) };
12806
+ }
12807
+ const data = await agentCommand("inputvalue", { selector });
12808
+ return { content: typeof data?.value === "string" ? data.value : "" };
12809
+ }
12810
+ if (mode === "attribute") {
12811
+ if (!args.attribute)
12812
+ throw new Error("attribute is required");
12813
+ if (indexValue > 0) {
12814
+ const result = ensureEvalResult(await agentEvaluate(buildAgentNthAttributeScript(selector, indexValue, args.attribute)), "Attribute lookup failed");
12815
+ return { content: typeof result === "string" ? result : JSON.stringify(result) };
12816
+ }
12817
+ const data = await agentCommand("getattribute", { selector, attribute: args.attribute });
12818
+ return { content: typeof data?.value === "string" ? data.value : JSON.stringify(data?.value) };
12819
+ }
12820
+ if (mode === "property") {
12821
+ if (!args.property)
12822
+ throw new Error("property is required");
12823
+ const result = ensureEvalResult(await agentEvaluate(buildAgentNthPropertyScript(selector, indexValue, args.property)), "Property lookup failed");
12824
+ return { content: typeof result === "string" ? result : JSON.stringify(result) };
12825
+ }
12826
+ if (mode === "html") {
12827
+ const result = ensureEvalResult(await agentEvaluate(buildAgentOuterHtmlScript(selector, indexValue)), "HTML lookup failed");
12828
+ return { content: typeof result === "string" ? result : JSON.stringify(result) };
12829
+ }
12830
+ if (mode === "list") {
12831
+ const listResult = ensureEvalResult(await agentEvaluate(buildAgentListScript(selector, limitValue)), "List lookup failed");
12832
+ return { content: JSON.stringify({ ok: true, value: listResult }, null, 2) };
12833
+ }
12834
+ throw new Error(`Unknown mode: ${mode}`);
12835
+ }
12836
+ async function requestTool(tool3, args) {
12837
+ switch (tool3) {
12838
+ case "get_tabs": {
12839
+ const data = await agentCommand("tab_list", {});
12840
+ const tabs = Array.isArray(data?.tabs) ? data.tabs : [];
12841
+ const mapped = tabs.map((tab) => ({
12842
+ id: tab.index,
12843
+ url: tab.url,
12844
+ title: tab.title,
12845
+ active: tab.active,
12846
+ windowId: tab.windowId ?? 0
12847
+ }));
12848
+ return { content: JSON.stringify(mapped, null, 2) };
12849
+ }
12850
+ case "open_tab": {
12851
+ const active = args.active;
12852
+ let previousActive = null;
12853
+ if (active === false) {
12854
+ const list = await agentCommand("tab_list", {});
12855
+ if (Number.isFinite(list?.active))
12856
+ previousActive = list.active;
12857
+ }
12858
+ const created = await agentCommand("tab_new", {});
12859
+ if (args.url) {
12860
+ await agentCommand("navigate", { url: args.url });
12861
+ }
12862
+ if (active === false && previousActive !== null) {
12863
+ await agentCommand("tab_switch", { index: previousActive });
12864
+ }
12865
+ return { content: { tabId: created.index, url: args.url, active: active !== false } };
12866
+ }
12867
+ case "navigate": {
12868
+ return await withTab(args.tabId, async () => {
12869
+ if (!args.url)
12870
+ throw new Error("URL is required");
12871
+ await agentCommand("navigate", { url: args.url });
12872
+ return { content: `Navigated to ${args.url}` };
12873
+ });
12874
+ }
12875
+ case "click": {
12876
+ return await withTab(args.tabId, async () => {
12877
+ if (!args.selector)
12878
+ throw new Error("Selector is required");
12879
+ const indexValue = Number.isFinite(args.index) ? args.index : 0;
12880
+ if (indexValue) {
12881
+ await agentCommand("nth", { selector: args.selector, index: indexValue, subaction: "click" });
12882
+ } else {
12883
+ await agentCommand("click", { selector: args.selector });
12884
+ }
12885
+ return { content: `Clicked ${args.selector}` };
12886
+ });
12887
+ }
12888
+ case "type": {
12889
+ return await withTab(args.tabId, async () => {
12890
+ if (!args.selector)
12891
+ throw new Error("Selector is required");
12892
+ if (args.text === undefined)
12893
+ throw new Error("Text is required");
12894
+ const indexValue = Number.isFinite(args.index) ? args.index : 0;
12895
+ if (!indexValue) {
12896
+ await agentCommand("type", {
12897
+ selector: args.selector,
12898
+ text: String(args.text),
12899
+ clear: args.clear
12900
+ });
12901
+ } else {
12902
+ const result = await agentEvaluate(buildAgentTypeScript(args.selector, indexValue, String(args.text), !!args.clear));
12903
+ if (!result?.ok) {
12904
+ throw new Error(result?.error || "Type failed");
12905
+ }
12906
+ }
12907
+ return { content: `Typed "${args.text}" into ${args.selector}` };
12908
+ });
12909
+ }
12910
+ case "select": {
12911
+ return await withTab(args.tabId, async () => {
12912
+ if (!args.selector)
12913
+ throw new Error("Selector is required");
12914
+ if (args.value === undefined && args.label === undefined && args.optionIndex === undefined) {
12915
+ throw new Error("value, label, or optionIndex is required");
12916
+ }
12917
+ const indexValue = Number.isFinite(args.index) ? args.index : 0;
12918
+ let selectedValue = args.value;
12919
+ let selectedLabel = args.label;
12920
+ if (indexValue || args.label !== undefined || args.optionIndex !== undefined) {
12921
+ const result = await agentEvaluate(buildAgentSelectScript(args.selector, indexValue, args.value, args.label, args.optionIndex));
12922
+ if (!result?.ok) {
12923
+ throw new Error(result?.error || "Select failed");
12924
+ }
12925
+ selectedValue = result.value;
12926
+ selectedLabel = result.label;
12927
+ } else if (args.value !== undefined) {
12928
+ await agentCommand("select", { selector: args.selector, values: args.value });
12929
+ }
12930
+ const valueText = selectedValue ? String(selectedValue) : "";
12931
+ const labelText = selectedLabel ? String(selectedLabel) : "";
12932
+ const summary = labelText && valueText && labelText !== valueText ? `${labelText} (${valueText})` : labelText || valueText || "option";
12933
+ return { content: `Selected ${summary} in ${args.selector}` };
12934
+ });
12935
+ }
12936
+ case "screenshot": {
12937
+ return await withTab(args.tabId, async () => {
12938
+ const data = await agentCommand("screenshot", { format: "png" });
12939
+ const base643 = data?.base64 ? String(data.base64) : "";
12940
+ if (!base643)
12941
+ throw new Error("Screenshot failed");
12942
+ return { content: `data:image/png;base64,${base643}` };
12943
+ });
12944
+ }
12945
+ case "snapshot": {
12946
+ return await withTab(args.tabId, async () => {
12947
+ const data = await agentCommand("snapshot", {});
12948
+ const payload = {
12949
+ snapshot: data?.snapshot ?? "",
12950
+ refs: data?.refs ?? {}
12951
+ };
12952
+ return { content: JSON.stringify(payload, null, 2) };
12953
+ });
12954
+ }
12955
+ case "query": {
12956
+ return await withTab(args.tabId, async () => {
12957
+ return await agentQuery(args);
12958
+ });
12959
+ }
12960
+ case "scroll": {
12961
+ return await withTab(args.tabId, async () => {
12962
+ const x = Number.isFinite(args.x) ? args.x : 0;
12963
+ const y = Number.isFinite(args.y) ? args.y : 0;
12964
+ await agentCommand("scroll", {
12965
+ selector: args.selector,
12966
+ x,
12967
+ y
12968
+ });
12969
+ const target = args.selector ? `to ${args.selector}` : `by (${x}, ${y})`;
12970
+ return { content: `Scrolled ${target}` };
12971
+ });
12972
+ }
12973
+ case "wait": {
12974
+ return await withTab(args.tabId, async () => {
12975
+ const ms = Number.isFinite(args.ms) ? args.ms : 1000;
12976
+ await agentCommand("wait", { timeout: ms });
12977
+ return { content: `Waited ${ms}ms` };
12978
+ });
12979
+ }
12980
+ default:
12981
+ throw new Error(`Unsupported tool for agent backend: ${tool3}`);
12982
+ }
12983
+ }
12984
+ async function status() {
12985
+ let connected = false;
12986
+ let error45;
12987
+ try {
12988
+ await ensureAgentSocket();
12989
+ connected = true;
12990
+ } catch (err) {
12991
+ error45 = err instanceof Error ? err.message : String(err);
12992
+ }
12993
+ return {
12994
+ backend: "agent-browser",
12995
+ session,
12996
+ connection,
12997
+ connected,
12998
+ error: error45,
12999
+ agentBrowserVersion: getAgentPackageVersion()
13000
+ };
13001
+ }
13002
+ return {
13003
+ mode: "agent",
13004
+ session,
13005
+ connection,
13006
+ getVersion: getAgentPackageVersion,
13007
+ status,
13008
+ requestTool
13009
+ };
13010
+ }
13011
+
13012
+ // src/plugin.ts
13013
+ import { existsSync, mkdirSync, readFileSync as readFileSync2 } from "fs";
13014
+ import { homedir } from "os";
13015
+ import { dirname, join as join2 } from "path";
13016
+ import { spawn as spawn2 } from "child_process";
12338
13017
  import { fileURLToPath } from "url";
12339
- console.log("[opencode-browser] Plugin loading...", { pid: process.pid });
12340
13018
  var __filename2 = fileURLToPath(import.meta.url);
12341
13019
  var __dirname2 = dirname(__filename2);
12342
- var PACKAGE_JSON_PATH = join(__dirname2, "..", "package.json");
13020
+ var PACKAGE_JSON_PATH = join2(__dirname2, "..", "package.json");
12343
13021
  var cachedVersion = null;
12344
13022
  function getPackageVersion() {
12345
13023
  if (cachedVersion)
12346
13024
  return cachedVersion;
12347
13025
  try {
12348
- const pkg = JSON.parse(readFileSync(PACKAGE_JSON_PATH, "utf8"));
13026
+ const pkg = JSON.parse(readFileSync2(PACKAGE_JSON_PATH, "utf8"));
12349
13027
  if (typeof pkg?.version === "string") {
12350
13028
  cachedVersion = pkg.version;
12351
13029
  return cachedVersion;
@@ -12355,10 +13033,10 @@ function getPackageVersion() {
12355
13033
  return cachedVersion;
12356
13034
  }
12357
13035
  var { schema } = tool;
12358
- var BASE_DIR = join(homedir(), ".opencode-browser");
12359
- var SOCKET_PATH = join(BASE_DIR, "broker.sock");
13036
+ var BASE_DIR = join2(homedir(), ".opencode-browser");
13037
+ var SOCKET_PATH = join2(BASE_DIR, "broker.sock");
12360
13038
  mkdirSync(BASE_DIR, { recursive: true });
12361
- function createJsonLineParser(onMessage) {
13039
+ function createJsonLineParser2(onMessage) {
12362
13040
  let buffer = "";
12363
13041
  return (chunk) => {
12364
13042
  buffer += chunk.toString("utf8");
@@ -12377,33 +13055,36 @@ function createJsonLineParser(onMessage) {
12377
13055
  }
12378
13056
  };
12379
13057
  }
12380
- function writeJsonLine(socket, msg) {
13058
+ function writeJsonLine2(socket, msg) {
12381
13059
  socket.write(JSON.stringify(msg) + `
12382
13060
  `);
12383
13061
  }
12384
13062
  function maybeStartBroker() {
12385
- const brokerPath = join(BASE_DIR, "broker.cjs");
13063
+ const brokerPath = join2(BASE_DIR, "broker.cjs");
12386
13064
  if (!existsSync(brokerPath))
12387
13065
  return;
12388
13066
  try {
12389
- const child = spawn(process.execPath, [brokerPath], { detached: true, stdio: "ignore" });
13067
+ const child = spawn2(process.execPath, [brokerPath], { detached: true, stdio: "ignore" });
12390
13068
  child.unref();
12391
13069
  } catch {}
12392
13070
  }
12393
13071
  async function connectToBroker() {
12394
13072
  return await new Promise((resolve, reject) => {
12395
- const socket = net.createConnection(SOCKET_PATH);
13073
+ const socket = net2.createConnection(SOCKET_PATH);
12396
13074
  socket.once("connect", () => resolve(socket));
12397
13075
  socket.once("error", (err) => reject(err));
12398
13076
  });
12399
13077
  }
12400
- async function sleep(ms) {
13078
+ async function sleep2(ms) {
12401
13079
  return await new Promise((r) => setTimeout(r, ms));
12402
13080
  }
13081
+ var BACKEND_MODE = (process.env.OPENCODE_BROWSER_BACKEND ?? process.env.OPENCODE_BROWSER_MODE ?? "extension").toLowerCase().trim();
13082
+ var USE_AGENT_BACKEND = ["agent", "agent-browser", "agentbrowser"].includes(BACKEND_MODE);
12403
13083
  var socket = null;
12404
13084
  var sessionId = Math.random().toString(36).slice(2);
12405
13085
  var reqId = 0;
12406
13086
  var pending = new Map;
13087
+ var agentBackend = USE_AGENT_BACKEND ? createAgentBackend(sessionId) : null;
12407
13088
  async function ensureBrokerSocket() {
12408
13089
  if (socket && !socket.destroyed)
12409
13090
  return socket;
@@ -12412,7 +13093,7 @@ async function ensureBrokerSocket() {
12412
13093
  } catch {
12413
13094
  maybeStartBroker();
12414
13095
  for (let i = 0;i < 20; i++) {
12415
- await sleep(100);
13096
+ await sleep2(100);
12416
13097
  try {
12417
13098
  socket = await connectToBroker();
12418
13099
  break;
@@ -12423,7 +13104,7 @@ async function ensureBrokerSocket() {
12423
13104
  throw new Error("Could not connect to local broker. Run `npx @different-ai/opencode-browser install` and ensure the extension is loaded.");
12424
13105
  }
12425
13106
  socket.setNoDelay(true);
12426
- socket.on("data", createJsonLineParser((msg) => {
13107
+ socket.on("data", createJsonLineParser2((msg) => {
12427
13108
  if (msg?.type !== "response" || typeof msg.id !== "number")
12428
13109
  return;
12429
13110
  const p = pending.get(msg.id);
@@ -12442,7 +13123,7 @@ async function ensureBrokerSocket() {
12442
13123
  socket.on("error", () => {
12443
13124
  socket = null;
12444
13125
  });
12445
- writeJsonLine(socket, { type: "hello", role: "plugin", sessionId, pid: process.pid });
13126
+ writeJsonLine2(socket, { type: "hello", role: "plugin", sessionId, pid: process.pid });
12446
13127
  return socket;
12447
13128
  }
12448
13129
  async function brokerRequest(op, payload) {
@@ -12450,7 +13131,7 @@ async function brokerRequest(op, payload) {
12450
13131
  const id = ++reqId;
12451
13132
  return await new Promise((resolve, reject) => {
12452
13133
  pending.set(id, { resolve, reject });
12453
- writeJsonLine(s, { type: "request", id, op, ...payload });
13134
+ writeJsonLine2(s, { type: "request", id, op, ...payload });
12454
13135
  setTimeout(() => {
12455
13136
  if (!pending.has(id))
12456
13137
  return;
@@ -12459,6 +13140,12 @@ async function brokerRequest(op, payload) {
12459
13140
  }, 60000);
12460
13141
  });
12461
13142
  }
13143
+ async function brokerOnlyRequest(op, payload) {
13144
+ if (USE_AGENT_BACKEND) {
13145
+ throw new Error("Tab claims are not supported with agent-browser backend");
13146
+ }
13147
+ return await brokerRequest(op, payload);
13148
+ }
12462
13149
  function toolResultText(data, fallback) {
12463
13150
  if (typeof data?.content === "string")
12464
13151
  return data.content;
@@ -12468,8 +13155,29 @@ function toolResultText(data, fallback) {
12468
13155
  return JSON.stringify(data.content);
12469
13156
  return fallback;
12470
13157
  }
13158
+ async function toolRequest(toolName, args) {
13159
+ if (USE_AGENT_BACKEND) {
13160
+ if (!agentBackend) {
13161
+ throw new Error("Agent backend unavailable: configuration failed to initialize");
13162
+ }
13163
+ return await agentBackend.requestTool(toolName, args);
13164
+ }
13165
+ return await brokerRequest("tool", { tool: toolName, args });
13166
+ }
13167
+ async function statusRequest() {
13168
+ if (USE_AGENT_BACKEND) {
13169
+ if (!agentBackend) {
13170
+ return {
13171
+ backend: "agent-browser",
13172
+ connected: false,
13173
+ error: "Agent backend unavailable: configuration failed to initialize"
13174
+ };
13175
+ }
13176
+ return await agentBackend.status();
13177
+ }
13178
+ return await brokerRequest("status", {});
13179
+ }
12471
13180
  var plugin = async (ctx) => {
12472
- console.log("[opencode-browser] Plugin loading...", { pid: process.pid });
12473
13181
  return {
12474
13182
  tool: {
12475
13183
  browser_debug: tool({
@@ -12481,6 +13189,10 @@ var plugin = async (ctx) => {
12481
13189
  loaded: true,
12482
13190
  sessionId,
12483
13191
  pid: process.pid,
13192
+ backend: USE_AGENT_BACKEND ? "agent-browser" : "extension",
13193
+ agentSession: agentBackend?.session ?? null,
13194
+ agentConnection: agentBackend?.connection ?? null,
13195
+ agentBrowserVersion: agentBackend?.getVersion?.() ?? null,
12484
13196
  pluginVersion: getPackageVersion(),
12485
13197
  timestamp: new Date().toISOString()
12486
13198
  });
@@ -12494,15 +13206,17 @@ var plugin = async (ctx) => {
12494
13206
  name: "@different-ai/opencode-browser",
12495
13207
  version: getPackageVersion(),
12496
13208
  sessionId,
12497
- pid: process.pid
13209
+ pid: process.pid,
13210
+ backend: USE_AGENT_BACKEND ? "agent-browser" : "extension",
13211
+ agentBrowserVersion: agentBackend?.getVersion?.() ?? null
12498
13212
  });
12499
13213
  }
12500
13214
  }),
12501
13215
  browser_status: tool({
12502
- description: "Check broker/native-host connection status and current tab claims.",
13216
+ description: "Check backend connection status and current tab claims.",
12503
13217
  args: {},
12504
13218
  async execute(args, ctx2) {
12505
- const data = await brokerRequest("status", {});
13219
+ const data = await statusRequest();
12506
13220
  return JSON.stringify(data);
12507
13221
  }
12508
13222
  }),
@@ -12510,10 +13224,39 @@ var plugin = async (ctx) => {
12510
13224
  description: "List all open browser tabs",
12511
13225
  args: {},
12512
13226
  async execute(args, ctx2) {
12513
- const data = await brokerRequest("tool", { tool: "get_tabs", args: {} });
13227
+ const data = await toolRequest("get_tabs", {});
12514
13228
  return toolResultText(data, "ok");
12515
13229
  }
12516
13230
  }),
13231
+ browser_list_claims: tool({
13232
+ description: "List tab ownership claims",
13233
+ args: {},
13234
+ async execute(args, ctx2) {
13235
+ const data = await brokerOnlyRequest("list_claims", {});
13236
+ return JSON.stringify(data);
13237
+ }
13238
+ }),
13239
+ browser_claim_tab: tool({
13240
+ description: "Claim a browser tab for this session",
13241
+ args: {
13242
+ tabId: schema.number(),
13243
+ force: schema.boolean().optional()
13244
+ },
13245
+ async execute({ tabId, force }, ctx2) {
13246
+ const data = await brokerOnlyRequest("claim_tab", { tabId, force });
13247
+ return JSON.stringify(data);
13248
+ }
13249
+ }),
13250
+ browser_release_tab: tool({
13251
+ description: "Release a claimed browser tab",
13252
+ args: {
13253
+ tabId: schema.number()
13254
+ },
13255
+ async execute({ tabId }, ctx2) {
13256
+ const data = await brokerOnlyRequest("release_tab", { tabId });
13257
+ return JSON.stringify(data);
13258
+ }
13259
+ }),
12517
13260
  browser_open_tab: tool({
12518
13261
  description: "Open a new browser tab",
12519
13262
  args: {
@@ -12521,7 +13264,7 @@ var plugin = async (ctx) => {
12521
13264
  active: schema.boolean().optional()
12522
13265
  },
12523
13266
  async execute({ url: url2, active }, ctx2) {
12524
- const data = await brokerRequest("tool", { tool: "open_tab", args: { url: url2, active } });
13267
+ const data = await toolRequest("open_tab", { url: url2, active });
12525
13268
  return toolResultText(data, "Opened new tab");
12526
13269
  }
12527
13270
  }),
@@ -12532,7 +13275,7 @@ var plugin = async (ctx) => {
12532
13275
  tabId: schema.number().optional()
12533
13276
  },
12534
13277
  async execute({ url: url2, tabId }, ctx2) {
12535
- const data = await brokerRequest("tool", { tool: "navigate", args: { url: url2, tabId } });
13278
+ const data = await toolRequest("navigate", { url: url2, tabId });
12536
13279
  return toolResultText(data, `Navigated to ${url2}`);
12537
13280
  }
12538
13281
  }),
@@ -12541,10 +13284,12 @@ var plugin = async (ctx) => {
12541
13284
  args: {
12542
13285
  selector: schema.string(),
12543
13286
  index: schema.number().optional(),
12544
- tabId: schema.number().optional()
13287
+ tabId: schema.number().optional(),
13288
+ timeoutMs: schema.number().optional(),
13289
+ pollMs: schema.number().optional()
12545
13290
  },
12546
- async execute({ selector, index, tabId }, ctx2) {
12547
- const data = await brokerRequest("tool", { tool: "click", args: { selector, index, tabId } });
13291
+ async execute({ selector, index, tabId, timeoutMs, pollMs }, ctx2) {
13292
+ const data = await toolRequest("click", { selector, index, tabId, timeoutMs, pollMs });
12548
13293
  return toolResultText(data, `Clicked ${selector}`);
12549
13294
  }
12550
13295
  }),
@@ -12555,10 +13300,12 @@ var plugin = async (ctx) => {
12555
13300
  text: schema.string(),
12556
13301
  clear: schema.boolean().optional(),
12557
13302
  index: schema.number().optional(),
12558
- tabId: schema.number().optional()
13303
+ tabId: schema.number().optional(),
13304
+ timeoutMs: schema.number().optional(),
13305
+ pollMs: schema.number().optional()
12559
13306
  },
12560
- async execute({ selector, text, clear, index, tabId }, ctx2) {
12561
- const data = await brokerRequest("tool", { tool: "type", args: { selector, text, clear, index, tabId } });
13307
+ async execute({ selector, text, clear, index, tabId, timeoutMs, pollMs }, ctx2) {
13308
+ const data = await toolRequest("type", { selector, text, clear, index, tabId, timeoutMs, pollMs });
12562
13309
  return toolResultText(data, `Typed "${text}" into ${selector}`);
12563
13310
  }
12564
13311
  }),
@@ -12570,13 +13317,12 @@ var plugin = async (ctx) => {
12570
13317
  label: schema.string().optional(),
12571
13318
  optionIndex: schema.number().optional(),
12572
13319
  index: schema.number().optional(),
12573
- tabId: schema.number().optional()
13320
+ tabId: schema.number().optional(),
13321
+ timeoutMs: schema.number().optional(),
13322
+ pollMs: schema.number().optional()
12574
13323
  },
12575
- async execute({ selector, value, label, optionIndex, index, tabId }, ctx2) {
12576
- const data = await brokerRequest("tool", {
12577
- tool: "select",
12578
- args: { selector, value, label, optionIndex, index, tabId }
12579
- });
13324
+ async execute({ selector, value, label, optionIndex, index, tabId, timeoutMs, pollMs }, ctx2) {
13325
+ const data = await toolRequest("select", { selector, value, label, optionIndex, index, tabId, timeoutMs, pollMs });
12580
13326
  const summary = value ?? label ?? (optionIndex != null ? String(optionIndex) : "option");
12581
13327
  return toolResultText(data, `Selected ${summary} in ${selector}`);
12582
13328
  }
@@ -12587,7 +13333,7 @@ var plugin = async (ctx) => {
12587
13333
  tabId: schema.number().optional()
12588
13334
  },
12589
13335
  async execute({ tabId }, ctx2) {
12590
- const data = await brokerRequest("tool", { tool: "screenshot", args: { tabId } });
13336
+ const data = await toolRequest("screenshot", { tabId });
12591
13337
  return toolResultText(data, "Screenshot failed");
12592
13338
  }
12593
13339
  }),
@@ -12597,7 +13343,7 @@ var plugin = async (ctx) => {
12597
13343
  tabId: schema.number().optional()
12598
13344
  },
12599
13345
  async execute({ tabId }, ctx2) {
12600
- const data = await brokerRequest("tool", { tool: "snapshot", args: { tabId } });
13346
+ const data = await toolRequest("snapshot", { tabId });
12601
13347
  return toolResultText(data, "Snapshot failed");
12602
13348
  }
12603
13349
  }),
@@ -12607,10 +13353,12 @@ var plugin = async (ctx) => {
12607
13353
  selector: schema.string().optional(),
12608
13354
  x: schema.number().optional(),
12609
13355
  y: schema.number().optional(),
12610
- tabId: schema.number().optional()
13356
+ tabId: schema.number().optional(),
13357
+ timeoutMs: schema.number().optional(),
13358
+ pollMs: schema.number().optional()
12611
13359
  },
12612
- async execute({ selector, x, y, tabId }, ctx2) {
12613
- const data = await brokerRequest("tool", { tool: "scroll", args: { selector, x, y, tabId } });
13360
+ async execute({ selector, x, y, tabId, timeoutMs, pollMs }, ctx2) {
13361
+ const data = await toolRequest("scroll", { selector, x, y, tabId, timeoutMs, pollMs });
12614
13362
  return toolResultText(data, "Scrolled");
12615
13363
  }
12616
13364
  }),
@@ -12621,7 +13369,7 @@ var plugin = async (ctx) => {
12621
13369
  tabId: schema.number().optional()
12622
13370
  },
12623
13371
  async execute({ ms, tabId }, ctx2) {
12624
- const data = await brokerRequest("tool", { tool: "wait", args: { ms, tabId } });
13372
+ const data = await toolRequest("wait", { ms, tabId });
12625
13373
  return toolResultText(data, "Waited");
12626
13374
  }
12627
13375
  }),
@@ -12641,9 +13389,18 @@ var plugin = async (ctx) => {
12641
13389
  tabId: schema.number().optional()
12642
13390
  },
12643
13391
  async execute({ selector, mode, attribute, property, index, limit, timeoutMs, pollMs, pattern, flags, tabId }, ctx2) {
12644
- const data = await brokerRequest("tool", {
12645
- tool: "query",
12646
- args: { selector, mode, attribute, property, index, limit, timeoutMs, pollMs, pattern, flags, tabId }
13392
+ const data = await toolRequest("query", {
13393
+ selector,
13394
+ mode,
13395
+ attribute,
13396
+ property,
13397
+ index,
13398
+ limit,
13399
+ timeoutMs,
13400
+ pollMs,
13401
+ pattern,
13402
+ flags,
13403
+ tabId
12647
13404
  });
12648
13405
  return toolResultText(data, "Query failed");
12649
13406
  }