auto-webmcp 0.3.20 → 0.3.22

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.
@@ -1900,16 +1900,121 @@ function getMissingRequired(metadata, params) {
1900
1900
  return [];
1901
1901
  return metadata.inputSchema.required.filter((fieldKey) => !(fieldKey in params));
1902
1902
  }
1903
+ function queryShadowAll(root, selector) {
1904
+ const results = [];
1905
+ const hosts = Array.from(root.querySelectorAll?.("*") ?? []);
1906
+ for (const host of hosts) {
1907
+ const sr = host.shadowRoot;
1908
+ if (!sr)
1909
+ continue;
1910
+ results.push(...Array.from(sr.querySelectorAll(selector)));
1911
+ results.push(...queryShadowAll(sr, selector));
1912
+ }
1913
+ return results;
1914
+ }
1915
+ async function fillLookupInput(el, value) {
1916
+ const text = String(value ?? "").trim();
1917
+ const input = el;
1918
+ console.log("[auto-webmcp] fillLookupInput: typing value=", JSON.stringify(text));
1919
+ setReactValue(input, text);
1920
+ input.dispatchEvent(new KeyboardEvent("keydown", { bubbles: true, cancelable: true, key: text.slice(-1) || "" }));
1921
+ input.dispatchEvent(new KeyboardEvent("keyup", { bubbles: true, cancelable: true, key: text.slice(-1) || "" }));
1922
+ const ariaControlsId = el.getAttribute("aria-controls") ?? el.getAttribute("aria-owns");
1923
+ const listbox = await new Promise((resolve) => {
1924
+ const deadline = Date.now() + 3e3;
1925
+ const poll = () => {
1926
+ if (ariaControlsId) {
1927
+ const byId = document.getElementById(ariaControlsId);
1928
+ if (byId) {
1929
+ resolve(byId);
1930
+ return;
1931
+ }
1932
+ const inShadow = queryShadowAll(document.body, `#${CSS.escape(ariaControlsId)}`)[0] ?? null;
1933
+ if (inShadow) {
1934
+ resolve(inShadow);
1935
+ return;
1936
+ }
1937
+ }
1938
+ const lightCandidate = document.querySelector('[role="listbox"]') ?? document.querySelector('[role="option"]')?.closest('[role="listbox"]') ?? null;
1939
+ if (lightCandidate) {
1940
+ resolve(lightCandidate);
1941
+ return;
1942
+ }
1943
+ const shadowCandidate = queryShadowAll(document.body, '[role="listbox"]')[0] ?? null;
1944
+ if (shadowCandidate) {
1945
+ resolve(shadowCandidate);
1946
+ return;
1947
+ }
1948
+ if (Date.now() >= deadline) {
1949
+ resolve(null);
1950
+ return;
1951
+ }
1952
+ setTimeout(poll, 50);
1953
+ };
1954
+ poll();
1955
+ });
1956
+ if (!listbox) {
1957
+ console.warn("[auto-webmcp] fillLookupInput: listbox did not appear after 3s, leaving text as-is");
1958
+ return;
1959
+ }
1960
+ const lightOptions = Array.from(listbox.querySelectorAll('[role="option"]'));
1961
+ const shadowOptions = queryShadowAll(listbox, '[role="option"]');
1962
+ const options = lightOptions.length > 0 ? lightOptions : shadowOptions;
1963
+ console.log("[auto-webmcp] fillLookupInput: listbox has", options.length, "option(s)");
1964
+ const lowerValue = text.toLowerCase();
1965
+ const match = options.find((opt) => {
1966
+ const dataValue = (opt.getAttribute("data-value") ?? "").toLowerCase();
1967
+ const ariaLabel = (opt.getAttribute("aria-label") ?? "").toLowerCase();
1968
+ const optText = (opt.textContent ?? "").trim().toLowerCase();
1969
+ return dataValue === lowerValue || ariaLabel === lowerValue || optText === lowerValue;
1970
+ }) ?? options.find((opt) => {
1971
+ const optText = (opt.textContent ?? "").trim().toLowerCase();
1972
+ return optText.startsWith(lowerValue) || optText.includes(lowerValue);
1973
+ });
1974
+ if (match) {
1975
+ console.log("[auto-webmcp] fillLookupInput: selecting option", match.textContent?.trim());
1976
+ match.dispatchEvent(new PointerEvent("pointerdown", { bubbles: true, cancelable: true }));
1977
+ match.dispatchEvent(new MouseEvent("mousedown", { bubbles: true, cancelable: true }));
1978
+ match.dispatchEvent(new MouseEvent("click", { bubbles: true, cancelable: true }));
1979
+ } else {
1980
+ console.warn(
1981
+ "[auto-webmcp] fillLookupInput: no option matched",
1982
+ JSON.stringify(text),
1983
+ "available:",
1984
+ options.map((o) => o.getAttribute("data-value") ?? o.textContent?.trim())
1985
+ );
1986
+ }
1987
+ }
1903
1988
  async function fillComboboxButton(el, value) {
1904
1989
  const text = String(value ?? "").trim();
1905
1990
  console.log("[auto-webmcp] fillComboboxButton: clicking button, value=", JSON.stringify(text));
1991
+ el.dispatchEvent(new PointerEvent("pointerdown", { bubbles: true, cancelable: true }));
1992
+ el.dispatchEvent(new MouseEvent("mousedown", { bubbles: true, cancelable: true }));
1906
1993
  el.dispatchEvent(new MouseEvent("click", { bubbles: true, cancelable: true }));
1994
+ const ariaControlsId = el.getAttribute("aria-controls");
1907
1995
  const listbox = await new Promise((resolve) => {
1908
1996
  const deadline = Date.now() + 3e3;
1909
1997
  const poll = () => {
1910
- const candidate = document.querySelector('[role="listbox"]') ?? document.querySelector('[role="option"]')?.closest('[role="listbox"]') ?? null;
1911
- if (candidate) {
1912
- resolve(candidate);
1998
+ if (ariaControlsId) {
1999
+ const byId = document.getElementById(ariaControlsId);
2000
+ if (byId) {
2001
+ resolve(byId);
2002
+ return;
2003
+ }
2004
+ const inShadow = queryShadowAll(document.body, `#${CSS.escape(ariaControlsId)}`)[0] ?? null;
2005
+ if (inShadow) {
2006
+ resolve(inShadow);
2007
+ return;
2008
+ }
2009
+ }
2010
+ const lightCandidate = document.querySelector('[role="listbox"]') ?? document.querySelector('[role="option"]')?.closest('[role="listbox"]') ?? null;
2011
+ if (lightCandidate) {
2012
+ resolve(lightCandidate);
2013
+ return;
2014
+ }
2015
+ const shadowCandidate = queryShadowAll(document.body, '[role="listbox"]')[0] ?? null;
2016
+ if (shadowCandidate) {
2017
+ resolve(shadowCandidate);
1913
2018
  return;
1914
2019
  }
1915
2020
  if (Date.now() >= deadline) {
@@ -1921,11 +2026,13 @@ async function fillComboboxButton(el, value) {
1921
2026
  poll();
1922
2027
  });
1923
2028
  if (!listbox) {
1924
- console.warn("[auto-webmcp] fillComboboxButton: listbox did not appear after 1s");
2029
+ console.warn("[auto-webmcp] fillComboboxButton: listbox did not appear after 3s");
1925
2030
  return;
1926
2031
  }
1927
- const options = Array.from(listbox.querySelectorAll('[role="option"]'));
1928
- console.log("[auto-webmcp] fillComboboxButton: listbox has", options.length, "options");
2032
+ const lightOptions = Array.from(listbox.querySelectorAll('[role="option"]'));
2033
+ const shadowOptions = queryShadowAll(listbox, '[role="option"]');
2034
+ const options = lightOptions.length > 0 ? lightOptions : shadowOptions;
2035
+ console.log("[auto-webmcp] fillComboboxButton: listbox has", options.length, "option(s)");
1929
2036
  const lowerValue = text.toLowerCase();
1930
2037
  const match = options.find((opt) => {
1931
2038
  const dataValue = (opt.getAttribute("data-value") ?? "").toLowerCase();
@@ -1934,14 +2041,16 @@ async function fillComboboxButton(el, value) {
1934
2041
  return dataValue === lowerValue || ariaLabel === lowerValue || optText === lowerValue;
1935
2042
  });
1936
2043
  if (match) {
1937
- console.log("[auto-webmcp] fillComboboxButton: clicking option", match.textContent?.trim());
2044
+ console.log("[auto-webmcp] fillComboboxButton: selecting option", match.textContent?.trim());
2045
+ match.dispatchEvent(new PointerEvent("pointerdown", { bubbles: true, cancelable: true }));
2046
+ match.dispatchEvent(new MouseEvent("mousedown", { bubbles: true, cancelable: true }));
1938
2047
  match.dispatchEvent(new MouseEvent("click", { bubbles: true, cancelable: true }));
1939
2048
  } else {
1940
2049
  console.warn(
1941
2050
  "[auto-webmcp] fillComboboxButton: no option matched",
1942
2051
  JSON.stringify(text),
1943
2052
  "available:",
1944
- options.map((o) => o.textContent?.trim())
2053
+ options.map((o) => o.getAttribute("data-value") ?? o.textContent?.trim())
1945
2054
  );
1946
2055
  }
1947
2056
  }
@@ -2415,7 +2524,9 @@ async function scanOrphanInputs(config) {
2415
2524
  for (const { key, el } of inputPairs) {
2416
2525
  if (params[key] !== void 0) {
2417
2526
  console.log(`[auto-webmcp] orphan execute: filling key="${key}" value=`, params[key], "element=", el);
2418
- if (el.getAttribute("role") === "combobox" && el.tagName.toLowerCase() === "button") {
2527
+ if (el.getAttribute("role") === "combobox" && el.tagName.toLowerCase() === "input" && (el.getAttribute("aria-autocomplete") === "list" || el.getAttribute("aria-haspopup") === "listbox")) {
2528
+ await fillLookupInput(el, params[key]);
2529
+ } else if (el.getAttribute("role") === "combobox" && el.tagName.toLowerCase() === "button") {
2419
2530
  await fillComboboxButton(el, params[key]);
2420
2531
  } else {
2421
2532
  fillElement(el, params[key]);