auto-webmcp 0.2.7 → 0.2.9

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 +1 @@
1
- {"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAA8H,MAAM,aAAa,CAAC;AACrK,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,UAAU,CAAC;IACxB,6FAA6F;IAC7F,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAKD,iDAAiD;AACjD,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED,gDAAgD;AAChD,wBAAgB,WAAW,CAAC,IAAI,EAAE,eAAe,EAAE,QAAQ,CAAC,EAAE,YAAY,GAAG,YAAY,CAMxF;AAkaD;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,OAAO,EAClB,MAAM,EAAE,KAAK,CAAC,gBAAgB,GAAG,mBAAmB,GAAG,iBAAiB,CAAC,EACzE,SAAS,EAAE,iBAAiB,GAAG,gBAAgB,GAAG,IAAI,GACrD,YAAY,CAKd"}
1
+ {"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAAmJ,MAAM,aAAa,CAAC;AAC1L,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,UAAU,CAAC;IACxB,6FAA6F;IAC7F,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAKD,iDAAiD;AACjD,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED,gDAAgD;AAChD,wBAAgB,WAAW,CAAC,IAAI,EAAE,eAAe,EAAE,QAAQ,CAAC,EAAE,YAAY,GAAG,YAAY,CAMxF;AAsgBD;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,OAAO,EAClB,MAAM,EAAE,KAAK,CAAC,gBAAgB,GAAG,mBAAmB,GAAG,iBAAiB,CAAC,EACzE,SAAS,EAAE,iBAAiB,GAAG,gBAAgB,GAAG,IAAI,GACrD,YAAY,CAKd"}
@@ -189,17 +189,62 @@ function buildStringSchema(input) {
189
189
  prop.maxLength = input.maxLength;
190
190
  if (input.pattern)
191
191
  prop.pattern = input.pattern;
192
+ const listId = input.getAttribute("list");
193
+ if (listId) {
194
+ const datalist = input.ownerDocument.getElementById(listId);
195
+ if (datalist instanceof HTMLDataListElement) {
196
+ const options = Array.from(datalist.options).filter(
197
+ (o) => !o.disabled && o.value.trim() !== ""
198
+ );
199
+ if (options.length > 0) {
200
+ prop.enum = options.map((o) => o.value.trim());
201
+ prop.oneOf = options.map((o) => ({
202
+ const: o.value.trim(),
203
+ title: o.textContent?.trim() || o.value.trim()
204
+ }));
205
+ }
206
+ }
207
+ }
192
208
  return prop;
193
209
  }
194
210
  function mapSelectElement(select) {
195
- const filtered = Array.from(select.options).filter((o) => o.value !== "" && !o.disabled);
196
- if (filtered.length === 0) {
197
- return { type: "string" };
211
+ const enumValues = [];
212
+ const oneOf = [];
213
+ for (const child of Array.from(select.children)) {
214
+ if (child instanceof HTMLOptGroupElement) {
215
+ if (child.disabled)
216
+ continue;
217
+ const groupLabel = child.label?.trim() ?? "";
218
+ for (const opt of Array.from(child.children)) {
219
+ if (!(opt instanceof HTMLOptionElement))
220
+ continue;
221
+ if (opt.disabled || opt.value === "")
222
+ continue;
223
+ enumValues.push(opt.value);
224
+ const entry = {
225
+ const: opt.value,
226
+ title: opt.text.trim() || opt.value
227
+ };
228
+ if (groupLabel)
229
+ entry.group = groupLabel;
230
+ oneOf.push(entry);
231
+ }
232
+ } else if (child instanceof HTMLOptionElement) {
233
+ if (child.disabled || child.value === "")
234
+ continue;
235
+ enumValues.push(child.value);
236
+ oneOf.push({ const: child.value, title: child.text.trim() || child.value });
237
+ }
198
238
  }
199
- const enumValues = filtered.map((o) => o.value);
200
- const oneOf = filtered.map((o) => ({ const: o.value, title: o.text.trim() || o.value }));
239
+ if (enumValues.length === 0)
240
+ return { type: "string" };
201
241
  return { type: "string", enum: enumValues, oneOf };
202
242
  }
243
+ function collectCheckboxEnum(form, name) {
244
+ return Array.from(
245
+ form.querySelectorAll(`input[type="checkbox"][name="${CSS.escape(name)}"]`)
246
+ ).map((cb) => cb.value).filter((v) => v !== "" && v !== "on");
247
+ }
203
248
  function collectRadioEnum(form, name) {
204
249
  const radios = Array.from(
205
250
  form.querySelectorAll(`input[type="radio"][name="${CSS.escape(name)}"]`)
@@ -386,6 +431,7 @@ function buildSchema(form) {
386
431
  const required = [];
387
432
  const fieldElements = /* @__PURE__ */ new Map();
388
433
  const processedRadioGroups = /* @__PURE__ */ new Set();
434
+ const processedCheckboxGroups = /* @__PURE__ */ new Set();
389
435
  const controls = Array.from(
390
436
  form.querySelectorAll(
391
437
  "input, textarea, select"
@@ -401,9 +447,16 @@ function buildSchema(form) {
401
447
  continue;
402
448
  processedRadioGroups.add(fieldKey);
403
449
  }
450
+ if (control instanceof HTMLInputElement && control.type === "checkbox") {
451
+ if (processedCheckboxGroups.has(fieldKey))
452
+ continue;
453
+ processedCheckboxGroups.add(fieldKey);
454
+ }
404
455
  const schemaProp = inputTypeToSchema(control);
405
456
  if (!schemaProp)
406
457
  continue;
458
+ if (!isControlVisible(control))
459
+ continue;
407
460
  schemaProp.title = inferFieldTitle(control);
408
461
  const desc = inferFieldDescription(control);
409
462
  if (desc)
@@ -414,6 +467,22 @@ function buildSchema(form) {
414
467
  if (radioOneOf.length > 0)
415
468
  schemaProp.oneOf = radioOneOf;
416
469
  }
470
+ if (control instanceof HTMLInputElement && control.type === "checkbox") {
471
+ const checkboxValues = collectCheckboxEnum(form, fieldKey);
472
+ if (checkboxValues.length > 1) {
473
+ const arrayProp = {
474
+ type: "array",
475
+ items: { type: "string", enum: checkboxValues },
476
+ title: schemaProp.title
477
+ };
478
+ if (schemaProp.description)
479
+ arrayProp.description = schemaProp.description;
480
+ properties[fieldKey] = arrayProp;
481
+ if (control.required)
482
+ required.push(fieldKey);
483
+ continue;
484
+ }
485
+ }
417
486
  properties[fieldKey] = schemaProp;
418
487
  if (!name) {
419
488
  fieldElements.set(fieldKey, control);
@@ -424,7 +493,7 @@ function buildSchema(form) {
424
493
  }
425
494
  const ariaControls = collectAriaControls(form);
426
495
  const processedAriaRadioGroups = /* @__PURE__ */ new Set();
427
- for (const { el, role, key } of ariaControls) {
496
+ for (const { el, role, key, enumValues, enumOneOf } of ariaControls) {
428
497
  if (properties[key])
429
498
  continue;
430
499
  if (role === "radio") {
@@ -433,6 +502,11 @@ function buildSchema(form) {
433
502
  processedAriaRadioGroups.add(key);
434
503
  }
435
504
  const schemaProp = ariaRoleToSchema(el, role);
505
+ if (enumValues && enumValues.length > 0) {
506
+ schemaProp.enum = enumValues;
507
+ if (enumOneOf && enumOneOf.length > 0)
508
+ schemaProp.oneOf = enumOneOf;
509
+ }
436
510
  schemaProp.title = inferAriaFieldTitle(el);
437
511
  const desc = inferAriaFieldDescription(el);
438
512
  if (desc)
@@ -464,7 +538,7 @@ function resolveNativeControlFallbackKey(control) {
464
538
  }
465
539
  function collectAriaControls(form) {
466
540
  const selector = ARIA_ROLES_TO_SCAN.map((r) => `[role="${r}"]`).join(", ");
467
- const results = [];
541
+ const rawResults = [];
468
542
  for (const el of Array.from(form.querySelectorAll(selector))) {
469
543
  if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement)
470
544
  continue;
@@ -474,9 +548,38 @@ function collectAriaControls(form) {
474
548
  const key = resolveAriaFieldKey(el);
475
549
  if (!key)
476
550
  continue;
477
- results.push({ el, role, key });
551
+ rawResults.push({ el, role, key });
552
+ }
553
+ const radioEntries = rawResults.filter((e) => e.role === "radio");
554
+ const nonRadioEntries = rawResults.filter((e) => e.role !== "radio");
555
+ const radioGroupMap = /* @__PURE__ */ new Map();
556
+ const ungroupedRadios = [];
557
+ for (const entry of radioEntries) {
558
+ const group = entry.el.closest('[role="radiogroup"]');
559
+ if (group) {
560
+ if (!radioGroupMap.has(group))
561
+ radioGroupMap.set(group, []);
562
+ radioGroupMap.get(group).push(entry.el);
563
+ } else {
564
+ ungroupedRadios.push(entry);
565
+ }
566
+ }
567
+ const groupedEntries = [];
568
+ for (const [group, members] of radioGroupMap) {
569
+ const groupKey = resolveAriaFieldKey(group);
570
+ if (!groupKey)
571
+ continue;
572
+ const enumValues = members.map((el) => (el.getAttribute("data-value") ?? el.getAttribute("aria-label") ?? el.textContent ?? "").trim()).filter(Boolean);
573
+ const enumOneOf = members.map((el) => {
574
+ const val = (el.getAttribute("data-value") ?? el.getAttribute("aria-label") ?? el.textContent ?? "").trim();
575
+ const title = (el.getAttribute("aria-label") ?? el.textContent ?? "").trim();
576
+ return { const: val, title: title || val };
577
+ }).filter((e) => e.const !== "");
578
+ if (enumValues.length > 0) {
579
+ groupedEntries.push({ el: group, role: "radio", key: groupKey, enumValues, enumOneOf });
580
+ }
478
581
  }
479
- return results;
582
+ return [...nonRadioEntries, ...groupedEntries, ...ungroupedRadios];
480
583
  }
481
584
  function resolveAriaFieldKey(el) {
482
585
  const htmlEl = el;
@@ -597,6 +700,24 @@ function labelTextWithoutNested(label) {
597
700
  function humanizeName(raw) {
598
701
  return raw.replace(/[-_]/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").trim().replace(/\b\w/g, (c) => c.toUpperCase());
599
702
  }
703
+ function isControlVisible(el) {
704
+ const style = window.getComputedStyle(el);
705
+ if (style.display === "none")
706
+ return false;
707
+ if (style.visibility === "hidden")
708
+ return false;
709
+ if (el.offsetParent === null && style.position !== "fixed")
710
+ return false;
711
+ let node = el;
712
+ while (node && node !== document.body) {
713
+ if (node.getAttribute("aria-hidden") === "true")
714
+ return false;
715
+ node = node.parentElement;
716
+ }
717
+ if (el.closest("fieldset")?.disabled)
718
+ return false;
719
+ return true;
720
+ }
600
721
  function analyzeOrphanInputGroup(container, inputs, submitBtn) {
601
722
  const name = inferOrphanToolName(container, submitBtn);
602
723
  const description = inferOrphanToolDescription(container);
@@ -654,6 +775,7 @@ function buildSchemaFromInputs(inputs) {
654
775
  const required = [];
655
776
  const fieldElements = /* @__PURE__ */ new Map();
656
777
  const processedRadioGroups = /* @__PURE__ */ new Set();
778
+ const processedCheckboxGroups = /* @__PURE__ */ new Set();
657
779
  for (const control of inputs) {
658
780
  const name = control.name;
659
781
  const fieldKey = name || resolveNativeControlFallbackKey(control);
@@ -664,13 +786,36 @@ function buildSchemaFromInputs(inputs) {
664
786
  continue;
665
787
  processedRadioGroups.add(fieldKey);
666
788
  }
789
+ if (control instanceof HTMLInputElement && control.type === "checkbox") {
790
+ if (processedCheckboxGroups.has(fieldKey))
791
+ continue;
792
+ processedCheckboxGroups.add(fieldKey);
793
+ }
667
794
  const schemaProp = inputTypeToSchema(control);
668
795
  if (!schemaProp)
669
796
  continue;
797
+ if (!isControlVisible(control))
798
+ continue;
670
799
  schemaProp.title = inferFieldTitle(control);
671
800
  const desc = inferFieldDescription(control);
672
801
  if (desc)
673
802
  schemaProp.description = desc;
803
+ if (control instanceof HTMLInputElement && control.type === "checkbox") {
804
+ const checkboxValues = inputs.filter((i) => i instanceof HTMLInputElement && i.type === "checkbox" && i.name === fieldKey).map((cb) => cb.value).filter((v) => v !== "" && v !== "on");
805
+ if (checkboxValues.length > 1) {
806
+ const arrayProp = {
807
+ type: "array",
808
+ items: { type: "string", enum: checkboxValues },
809
+ title: schemaProp.title
810
+ };
811
+ if (schemaProp.description)
812
+ arrayProp.description = schemaProp.description;
813
+ properties[fieldKey] = arrayProp;
814
+ if (control.required)
815
+ required.push(fieldKey);
816
+ continue;
817
+ }
818
+ }
674
819
  properties[fieldKey] = schemaProp;
675
820
  if (!name)
676
821
  fieldElements.set(fieldKey, control);
@@ -688,6 +833,7 @@ var pendingExecutions = /* @__PURE__ */ new WeakMap();
688
833
  var lastParams = /* @__PURE__ */ new WeakMap();
689
834
  var formFieldElements = /* @__PURE__ */ new WeakMap();
690
835
  var pendingWarnings = /* @__PURE__ */ new WeakMap();
836
+ var pendingFillWarnings = /* @__PURE__ */ new WeakMap();
691
837
  var _inputValueSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")?.set;
692
838
  var _textareaValueSetter = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, "value")?.set;
693
839
  var _checkedSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "checked")?.set;
@@ -697,6 +843,7 @@ function buildExecuteHandler(form, config, toolName, metadata) {
697
843
  }
698
844
  attachSubmitInterceptor(form, toolName);
699
845
  return async (params) => {
846
+ pendingFillWarnings.set(form, []);
700
847
  fillFormFields(form, params);
701
848
  window.dispatchEvent(new CustomEvent("toolactivated", { detail: { toolName } }));
702
849
  return new Promise((resolve, reject) => {
@@ -749,7 +896,13 @@ function attachSubmitInterceptor(form, toolName) {
749
896
  const formData = serializeFormData(form, lastParams.get(form), formFieldElements.get(form));
750
897
  const missing = pendingWarnings.get(form);
751
898
  pendingWarnings.delete(form);
752
- const warningText = missing?.length ? ` Note: required fields were not filled: ${missing.join(", ")}.` : "";
899
+ const fillWarnings = pendingFillWarnings.get(form) ?? [];
900
+ pendingFillWarnings.delete(form);
901
+ const allWarnings = [
902
+ ...missing?.length ? [`required fields were not filled: ${missing.join(", ")}`] : [],
903
+ ...fillWarnings
904
+ ];
905
+ const warningText = allWarnings.length ? ` Note: ${allWarnings.join("; ")}.` : "";
753
906
  const text = `Form submitted. Fields: ${JSON.stringify(formData)}${warningText}`;
754
907
  const result = { content: [{ type: "text", text }] };
755
908
  if (e.agentInvoked && typeof e.respondWith === "function") {
@@ -851,9 +1004,38 @@ function fillFormFields(form, params) {
851
1004
  function fillInput(input, form, key, value) {
852
1005
  const type = input.type.toLowerCase();
853
1006
  if (type === "checkbox") {
1007
+ if (Array.isArray(value)) {
1008
+ const esc = CSS.escape(key);
1009
+ const allBoxes = form.querySelectorAll(`input[type="checkbox"][name="${esc}"]`);
1010
+ for (const box of allBoxes) {
1011
+ setReactChecked(box, value.map(String).includes(box.value));
1012
+ }
1013
+ return;
1014
+ }
854
1015
  setReactChecked(input, Boolean(value));
855
1016
  return;
856
1017
  }
1018
+ if (type === "number" || type === "range") {
1019
+ const raw = String(value ?? "");
1020
+ const num = Number(raw);
1021
+ if (raw === "" || isNaN(num)) {
1022
+ pendingFillWarnings.get(form)?.push(`"${key}" expects a number, got: ${JSON.stringify(value)}`);
1023
+ return;
1024
+ }
1025
+ const min = input.min !== "" ? parseFloat(input.min) : -Infinity;
1026
+ const max = input.max !== "" ? parseFloat(input.max) : Infinity;
1027
+ if (num < min || num > max) {
1028
+ pendingFillWarnings.get(form)?.push(
1029
+ `"${key}" value ${num} is outside allowed range [${input.min || "?"}, ${input.max || "?"}]`
1030
+ );
1031
+ input.value = String(Math.min(Math.max(num, min), max));
1032
+ } else {
1033
+ input.value = String(num);
1034
+ }
1035
+ input.dispatchEvent(new InputEvent("input", { bubbles: true, cancelable: true, inputType: "insertText", data: String(num) }));
1036
+ input.dispatchEvent(new Event("change", { bubbles: true }));
1037
+ return;
1038
+ }
857
1039
  if (type === "radio") {
858
1040
  const esc = CSS.escape(key);
859
1041
  const radios = form.querySelectorAll(
@@ -886,6 +1068,22 @@ function fillAriaField(el, value) {
886
1068
  el.dispatchEvent(new MouseEvent("click", { bubbles: true }));
887
1069
  return;
888
1070
  }
1071
+ if (role === "radiogroup") {
1072
+ const radios = Array.from(el.querySelectorAll('[role="radio"]'));
1073
+ for (const radio of radios) {
1074
+ const val = (radio.getAttribute("data-value") ?? radio.getAttribute("aria-label") ?? radio.textContent ?? "").trim();
1075
+ if (val === String(value)) {
1076
+ radio.setAttribute("aria-checked", "true");
1077
+ radio.dispatchEvent(new MouseEvent("click", { bubbles: true }));
1078
+ for (const other of radios) {
1079
+ if (other !== radio)
1080
+ other.setAttribute("aria-checked", "false");
1081
+ }
1082
+ break;
1083
+ }
1084
+ }
1085
+ return;
1086
+ }
889
1087
  const htmlEl = el;
890
1088
  if (htmlEl.isContentEditable) {
891
1089
  htmlEl.textContent = String(value ?? "");