auto-webmcp 0.2.8 → 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.
@@ -221,6 +221,11 @@ function mapSelectElement(select) {
221
221
  return { type: "string" };
222
222
  return { type: "string", enum: enumValues, oneOf };
223
223
  }
224
+ function collectCheckboxEnum(form, name) {
225
+ return Array.from(
226
+ form.querySelectorAll(`input[type="checkbox"][name="${CSS.escape(name)}"]`)
227
+ ).map((cb) => cb.value).filter((v) => v !== "" && v !== "on");
228
+ }
224
229
  function collectRadioEnum(form, name) {
225
230
  const radios = Array.from(
226
231
  form.querySelectorAll(`input[type="radio"][name="${CSS.escape(name)}"]`)
@@ -407,6 +412,7 @@ function buildSchema(form) {
407
412
  const required = [];
408
413
  const fieldElements = /* @__PURE__ */ new Map();
409
414
  const processedRadioGroups = /* @__PURE__ */ new Set();
415
+ const processedCheckboxGroups = /* @__PURE__ */ new Set();
410
416
  const controls = Array.from(
411
417
  form.querySelectorAll(
412
418
  "input, textarea, select"
@@ -422,6 +428,11 @@ function buildSchema(form) {
422
428
  continue;
423
429
  processedRadioGroups.add(fieldKey);
424
430
  }
431
+ if (control instanceof HTMLInputElement && control.type === "checkbox") {
432
+ if (processedCheckboxGroups.has(fieldKey))
433
+ continue;
434
+ processedCheckboxGroups.add(fieldKey);
435
+ }
425
436
  const schemaProp = inputTypeToSchema(control);
426
437
  if (!schemaProp)
427
438
  continue;
@@ -437,6 +448,22 @@ function buildSchema(form) {
437
448
  if (radioOneOf.length > 0)
438
449
  schemaProp.oneOf = radioOneOf;
439
450
  }
451
+ if (control instanceof HTMLInputElement && control.type === "checkbox") {
452
+ const checkboxValues = collectCheckboxEnum(form, fieldKey);
453
+ if (checkboxValues.length > 1) {
454
+ const arrayProp = {
455
+ type: "array",
456
+ items: { type: "string", enum: checkboxValues },
457
+ title: schemaProp.title
458
+ };
459
+ if (schemaProp.description)
460
+ arrayProp.description = schemaProp.description;
461
+ properties[fieldKey] = arrayProp;
462
+ if (control.required)
463
+ required.push(fieldKey);
464
+ continue;
465
+ }
466
+ }
440
467
  properties[fieldKey] = schemaProp;
441
468
  if (!name) {
442
469
  fieldElements.set(fieldKey, control);
@@ -447,7 +474,7 @@ function buildSchema(form) {
447
474
  }
448
475
  const ariaControls = collectAriaControls(form);
449
476
  const processedAriaRadioGroups = /* @__PURE__ */ new Set();
450
- for (const { el, role, key } of ariaControls) {
477
+ for (const { el, role, key, enumValues, enumOneOf } of ariaControls) {
451
478
  if (properties[key])
452
479
  continue;
453
480
  if (role === "radio") {
@@ -456,6 +483,11 @@ function buildSchema(form) {
456
483
  processedAriaRadioGroups.add(key);
457
484
  }
458
485
  const schemaProp = ariaRoleToSchema(el, role);
486
+ if (enumValues && enumValues.length > 0) {
487
+ schemaProp.enum = enumValues;
488
+ if (enumOneOf && enumOneOf.length > 0)
489
+ schemaProp.oneOf = enumOneOf;
490
+ }
459
491
  schemaProp.title = inferAriaFieldTitle(el);
460
492
  const desc = inferAriaFieldDescription(el);
461
493
  if (desc)
@@ -487,7 +519,7 @@ function resolveNativeControlFallbackKey(control) {
487
519
  }
488
520
  function collectAriaControls(form) {
489
521
  const selector = ARIA_ROLES_TO_SCAN.map((r) => `[role="${r}"]`).join(", ");
490
- const results = [];
522
+ const rawResults = [];
491
523
  for (const el of Array.from(form.querySelectorAll(selector))) {
492
524
  if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement)
493
525
  continue;
@@ -497,9 +529,38 @@ function collectAriaControls(form) {
497
529
  const key = resolveAriaFieldKey(el);
498
530
  if (!key)
499
531
  continue;
500
- results.push({ el, role, key });
532
+ rawResults.push({ el, role, key });
533
+ }
534
+ const radioEntries = rawResults.filter((e) => e.role === "radio");
535
+ const nonRadioEntries = rawResults.filter((e) => e.role !== "radio");
536
+ const radioGroupMap = /* @__PURE__ */ new Map();
537
+ const ungroupedRadios = [];
538
+ for (const entry of radioEntries) {
539
+ const group = entry.el.closest('[role="radiogroup"]');
540
+ if (group) {
541
+ if (!radioGroupMap.has(group))
542
+ radioGroupMap.set(group, []);
543
+ radioGroupMap.get(group).push(entry.el);
544
+ } else {
545
+ ungroupedRadios.push(entry);
546
+ }
547
+ }
548
+ const groupedEntries = [];
549
+ for (const [group, members] of radioGroupMap) {
550
+ const groupKey = resolveAriaFieldKey(group);
551
+ if (!groupKey)
552
+ continue;
553
+ const enumValues = members.map((el) => (el.getAttribute("data-value") ?? el.getAttribute("aria-label") ?? el.textContent ?? "").trim()).filter(Boolean);
554
+ const enumOneOf = members.map((el) => {
555
+ const val = (el.getAttribute("data-value") ?? el.getAttribute("aria-label") ?? el.textContent ?? "").trim();
556
+ const title = (el.getAttribute("aria-label") ?? el.textContent ?? "").trim();
557
+ return { const: val, title: title || val };
558
+ }).filter((e) => e.const !== "");
559
+ if (enumValues.length > 0) {
560
+ groupedEntries.push({ el: group, role: "radio", key: groupKey, enumValues, enumOneOf });
561
+ }
501
562
  }
502
- return results;
563
+ return [...nonRadioEntries, ...groupedEntries, ...ungroupedRadios];
503
564
  }
504
565
  function resolveAriaFieldKey(el) {
505
566
  const htmlEl = el;
@@ -695,6 +756,7 @@ function buildSchemaFromInputs(inputs) {
695
756
  const required = [];
696
757
  const fieldElements = /* @__PURE__ */ new Map();
697
758
  const processedRadioGroups = /* @__PURE__ */ new Set();
759
+ const processedCheckboxGroups = /* @__PURE__ */ new Set();
698
760
  for (const control of inputs) {
699
761
  const name = control.name;
700
762
  const fieldKey = name || resolveNativeControlFallbackKey(control);
@@ -705,6 +767,11 @@ function buildSchemaFromInputs(inputs) {
705
767
  continue;
706
768
  processedRadioGroups.add(fieldKey);
707
769
  }
770
+ if (control instanceof HTMLInputElement && control.type === "checkbox") {
771
+ if (processedCheckboxGroups.has(fieldKey))
772
+ continue;
773
+ processedCheckboxGroups.add(fieldKey);
774
+ }
708
775
  const schemaProp = inputTypeToSchema(control);
709
776
  if (!schemaProp)
710
777
  continue;
@@ -714,6 +781,22 @@ function buildSchemaFromInputs(inputs) {
714
781
  const desc = inferFieldDescription(control);
715
782
  if (desc)
716
783
  schemaProp.description = desc;
784
+ if (control instanceof HTMLInputElement && control.type === "checkbox") {
785
+ const checkboxValues = inputs.filter((i) => i instanceof HTMLInputElement && i.type === "checkbox" && i.name === fieldKey).map((cb) => cb.value).filter((v) => v !== "" && v !== "on");
786
+ if (checkboxValues.length > 1) {
787
+ const arrayProp = {
788
+ type: "array",
789
+ items: { type: "string", enum: checkboxValues },
790
+ title: schemaProp.title
791
+ };
792
+ if (schemaProp.description)
793
+ arrayProp.description = schemaProp.description;
794
+ properties[fieldKey] = arrayProp;
795
+ if (control.required)
796
+ required.push(fieldKey);
797
+ continue;
798
+ }
799
+ }
717
800
  properties[fieldKey] = schemaProp;
718
801
  if (!name)
719
802
  fieldElements.set(fieldKey, control);
@@ -731,6 +814,7 @@ var pendingExecutions = /* @__PURE__ */ new WeakMap();
731
814
  var lastParams = /* @__PURE__ */ new WeakMap();
732
815
  var formFieldElements = /* @__PURE__ */ new WeakMap();
733
816
  var pendingWarnings = /* @__PURE__ */ new WeakMap();
817
+ var pendingFillWarnings = /* @__PURE__ */ new WeakMap();
734
818
  var _inputValueSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")?.set;
735
819
  var _textareaValueSetter = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, "value")?.set;
736
820
  var _checkedSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "checked")?.set;
@@ -740,6 +824,7 @@ function buildExecuteHandler(form, config, toolName, metadata) {
740
824
  }
741
825
  attachSubmitInterceptor(form, toolName);
742
826
  return async (params) => {
827
+ pendingFillWarnings.set(form, []);
743
828
  fillFormFields(form, params);
744
829
  window.dispatchEvent(new CustomEvent("toolactivated", { detail: { toolName } }));
745
830
  return new Promise((resolve, reject) => {
@@ -792,7 +877,13 @@ function attachSubmitInterceptor(form, toolName) {
792
877
  const formData = serializeFormData(form, lastParams.get(form), formFieldElements.get(form));
793
878
  const missing = pendingWarnings.get(form);
794
879
  pendingWarnings.delete(form);
795
- const warningText = missing?.length ? ` Note: required fields were not filled: ${missing.join(", ")}.` : "";
880
+ const fillWarnings = pendingFillWarnings.get(form) ?? [];
881
+ pendingFillWarnings.delete(form);
882
+ const allWarnings = [
883
+ ...missing?.length ? [`required fields were not filled: ${missing.join(", ")}`] : [],
884
+ ...fillWarnings
885
+ ];
886
+ const warningText = allWarnings.length ? ` Note: ${allWarnings.join("; ")}.` : "";
796
887
  const text = `Form submitted. Fields: ${JSON.stringify(formData)}${warningText}`;
797
888
  const result = { content: [{ type: "text", text }] };
798
889
  if (e.agentInvoked && typeof e.respondWith === "function") {
@@ -894,9 +985,38 @@ function fillFormFields(form, params) {
894
985
  function fillInput(input, form, key, value) {
895
986
  const type = input.type.toLowerCase();
896
987
  if (type === "checkbox") {
988
+ if (Array.isArray(value)) {
989
+ const esc = CSS.escape(key);
990
+ const allBoxes = form.querySelectorAll(`input[type="checkbox"][name="${esc}"]`);
991
+ for (const box of allBoxes) {
992
+ setReactChecked(box, value.map(String).includes(box.value));
993
+ }
994
+ return;
995
+ }
897
996
  setReactChecked(input, Boolean(value));
898
997
  return;
899
998
  }
999
+ if (type === "number" || type === "range") {
1000
+ const raw = String(value ?? "");
1001
+ const num = Number(raw);
1002
+ if (raw === "" || isNaN(num)) {
1003
+ pendingFillWarnings.get(form)?.push(`"${key}" expects a number, got: ${JSON.stringify(value)}`);
1004
+ return;
1005
+ }
1006
+ const min = input.min !== "" ? parseFloat(input.min) : -Infinity;
1007
+ const max = input.max !== "" ? parseFloat(input.max) : Infinity;
1008
+ if (num < min || num > max) {
1009
+ pendingFillWarnings.get(form)?.push(
1010
+ `"${key}" value ${num} is outside allowed range [${input.min || "?"}, ${input.max || "?"}]`
1011
+ );
1012
+ input.value = String(Math.min(Math.max(num, min), max));
1013
+ } else {
1014
+ input.value = String(num);
1015
+ }
1016
+ input.dispatchEvent(new InputEvent("input", { bubbles: true, cancelable: true, inputType: "insertText", data: String(num) }));
1017
+ input.dispatchEvent(new Event("change", { bubbles: true }));
1018
+ return;
1019
+ }
900
1020
  if (type === "radio") {
901
1021
  const esc = CSS.escape(key);
902
1022
  const radios = form.querySelectorAll(
@@ -929,6 +1049,22 @@ function fillAriaField(el, value) {
929
1049
  el.dispatchEvent(new MouseEvent("click", { bubbles: true }));
930
1050
  return;
931
1051
  }
1052
+ if (role === "radiogroup") {
1053
+ const radios = Array.from(el.querySelectorAll('[role="radio"]'));
1054
+ for (const radio of radios) {
1055
+ const val = (radio.getAttribute("data-value") ?? radio.getAttribute("aria-label") ?? radio.textContent ?? "").trim();
1056
+ if (val === String(value)) {
1057
+ radio.setAttribute("aria-checked", "true");
1058
+ radio.dispatchEvent(new MouseEvent("click", { bubbles: true }));
1059
+ for (const other of radios) {
1060
+ if (other !== radio)
1061
+ other.setAttribute("aria-checked", "false");
1062
+ }
1063
+ break;
1064
+ }
1065
+ }
1066
+ return;
1067
+ }
932
1068
  const htmlEl = el;
933
1069
  if (htmlEl.isContentEditable) {
934
1070
  htmlEl.textContent = String(value ?? "");