auto-webmcp 0.2.8 → 0.2.10

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;AAgcD;;;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"}
@@ -207,6 +207,14 @@ function buildStringSchema(input) {
207
207
  }
208
208
  return prop;
209
209
  }
210
+ var PLACEHOLDER_PATTERNS = /^(select|choose|pick)\b|^--+|---/i;
211
+ function isPlaceholderOption(opt) {
212
+ if (opt.disabled)
213
+ return true;
214
+ if (opt.value !== "")
215
+ return false;
216
+ return PLACEHOLDER_PATTERNS.test(opt.text.trim());
217
+ }
210
218
  function mapSelectElement(select) {
211
219
  const enumValues = [];
212
220
  const oneOf = [];
@@ -218,7 +226,7 @@ function mapSelectElement(select) {
218
226
  for (const opt of Array.from(child.children)) {
219
227
  if (!(opt instanceof HTMLOptionElement))
220
228
  continue;
221
- if (opt.disabled || opt.value === "")
229
+ if (isPlaceholderOption(opt))
222
230
  continue;
223
231
  enumValues.push(opt.value);
224
232
  const entry = {
@@ -230,7 +238,7 @@ function mapSelectElement(select) {
230
238
  oneOf.push(entry);
231
239
  }
232
240
  } else if (child instanceof HTMLOptionElement) {
233
- if (child.disabled || child.value === "")
241
+ if (isPlaceholderOption(child))
234
242
  continue;
235
243
  enumValues.push(child.value);
236
244
  oneOf.push({ const: child.value, title: child.text.trim() || child.value });
@@ -238,8 +246,16 @@ function mapSelectElement(select) {
238
246
  }
239
247
  if (enumValues.length === 0)
240
248
  return { type: "string" };
249
+ if (select.multiple) {
250
+ return { type: "array", items: { type: "string", enum: enumValues } };
251
+ }
241
252
  return { type: "string", enum: enumValues, oneOf };
242
253
  }
254
+ function collectCheckboxEnum(form, name) {
255
+ return Array.from(
256
+ form.querySelectorAll(`input[type="checkbox"][name="${CSS.escape(name)}"]`)
257
+ ).map((cb) => cb.value).filter((v) => v !== "" && v !== "on");
258
+ }
243
259
  function collectRadioEnum(form, name) {
244
260
  const radios = Array.from(
245
261
  form.querySelectorAll(`input[type="radio"][name="${CSS.escape(name)}"]`)
@@ -426,6 +442,7 @@ function buildSchema(form) {
426
442
  const required = [];
427
443
  const fieldElements = /* @__PURE__ */ new Map();
428
444
  const processedRadioGroups = /* @__PURE__ */ new Set();
445
+ const processedCheckboxGroups = /* @__PURE__ */ new Set();
429
446
  const controls = Array.from(
430
447
  form.querySelectorAll(
431
448
  "input, textarea, select"
@@ -441,6 +458,11 @@ function buildSchema(form) {
441
458
  continue;
442
459
  processedRadioGroups.add(fieldKey);
443
460
  }
461
+ if (control instanceof HTMLInputElement && control.type === "checkbox") {
462
+ if (processedCheckboxGroups.has(fieldKey))
463
+ continue;
464
+ processedCheckboxGroups.add(fieldKey);
465
+ }
444
466
  const schemaProp = inputTypeToSchema(control);
445
467
  if (!schemaProp)
446
468
  continue;
@@ -456,6 +478,22 @@ function buildSchema(form) {
456
478
  if (radioOneOf.length > 0)
457
479
  schemaProp.oneOf = radioOneOf;
458
480
  }
481
+ if (control instanceof HTMLInputElement && control.type === "checkbox") {
482
+ const checkboxValues = collectCheckboxEnum(form, fieldKey);
483
+ if (checkboxValues.length > 1) {
484
+ const arrayProp = {
485
+ type: "array",
486
+ items: { type: "string", enum: checkboxValues },
487
+ title: schemaProp.title
488
+ };
489
+ if (schemaProp.description)
490
+ arrayProp.description = schemaProp.description;
491
+ properties[fieldKey] = arrayProp;
492
+ if (control.required)
493
+ required.push(fieldKey);
494
+ continue;
495
+ }
496
+ }
459
497
  properties[fieldKey] = schemaProp;
460
498
  if (!name) {
461
499
  fieldElements.set(fieldKey, control);
@@ -466,7 +504,7 @@ function buildSchema(form) {
466
504
  }
467
505
  const ariaControls = collectAriaControls(form);
468
506
  const processedAriaRadioGroups = /* @__PURE__ */ new Set();
469
- for (const { el, role, key } of ariaControls) {
507
+ for (const { el, role, key, enumValues, enumOneOf } of ariaControls) {
470
508
  if (properties[key])
471
509
  continue;
472
510
  if (role === "radio") {
@@ -475,6 +513,11 @@ function buildSchema(form) {
475
513
  processedAriaRadioGroups.add(key);
476
514
  }
477
515
  const schemaProp = ariaRoleToSchema(el, role);
516
+ if (enumValues && enumValues.length > 0) {
517
+ schemaProp.enum = enumValues;
518
+ if (enumOneOf && enumOneOf.length > 0)
519
+ schemaProp.oneOf = enumOneOf;
520
+ }
478
521
  schemaProp.title = inferAriaFieldTitle(el);
479
522
  const desc = inferAriaFieldDescription(el);
480
523
  if (desc)
@@ -506,7 +549,7 @@ function resolveNativeControlFallbackKey(control) {
506
549
  }
507
550
  function collectAriaControls(form) {
508
551
  const selector = ARIA_ROLES_TO_SCAN.map((r) => `[role="${r}"]`).join(", ");
509
- const results = [];
552
+ const rawResults = [];
510
553
  for (const el of Array.from(form.querySelectorAll(selector))) {
511
554
  if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement)
512
555
  continue;
@@ -516,9 +559,38 @@ function collectAriaControls(form) {
516
559
  const key = resolveAriaFieldKey(el);
517
560
  if (!key)
518
561
  continue;
519
- results.push({ el, role, key });
562
+ rawResults.push({ el, role, key });
563
+ }
564
+ const radioEntries = rawResults.filter((e) => e.role === "radio");
565
+ const nonRadioEntries = rawResults.filter((e) => e.role !== "radio");
566
+ const radioGroupMap = /* @__PURE__ */ new Map();
567
+ const ungroupedRadios = [];
568
+ for (const entry of radioEntries) {
569
+ const group = entry.el.closest('[role="radiogroup"]');
570
+ if (group) {
571
+ if (!radioGroupMap.has(group))
572
+ radioGroupMap.set(group, []);
573
+ radioGroupMap.get(group).push(entry.el);
574
+ } else {
575
+ ungroupedRadios.push(entry);
576
+ }
577
+ }
578
+ const groupedEntries = [];
579
+ for (const [group, members] of radioGroupMap) {
580
+ const groupKey = resolveAriaFieldKey(group);
581
+ if (!groupKey)
582
+ continue;
583
+ const enumValues = members.map((el) => (el.getAttribute("data-value") ?? el.getAttribute("aria-label") ?? el.textContent ?? "").trim()).filter(Boolean);
584
+ const enumOneOf = members.map((el) => {
585
+ const val = (el.getAttribute("data-value") ?? el.getAttribute("aria-label") ?? el.textContent ?? "").trim();
586
+ const title = (el.getAttribute("aria-label") ?? el.textContent ?? "").trim();
587
+ return { const: val, title: title || val };
588
+ }).filter((e) => e.const !== "");
589
+ if (enumValues.length > 0) {
590
+ groupedEntries.push({ el: group, role: "radio", key: groupKey, enumValues, enumOneOf });
591
+ }
520
592
  }
521
- return results;
593
+ return [...nonRadioEntries, ...groupedEntries, ...ungroupedRadios];
522
594
  }
523
595
  function resolveAriaFieldKey(el) {
524
596
  const htmlEl = el;
@@ -714,6 +786,7 @@ function buildSchemaFromInputs(inputs) {
714
786
  const required = [];
715
787
  const fieldElements = /* @__PURE__ */ new Map();
716
788
  const processedRadioGroups = /* @__PURE__ */ new Set();
789
+ const processedCheckboxGroups = /* @__PURE__ */ new Set();
717
790
  for (const control of inputs) {
718
791
  const name = control.name;
719
792
  const fieldKey = name || resolveNativeControlFallbackKey(control);
@@ -724,6 +797,11 @@ function buildSchemaFromInputs(inputs) {
724
797
  continue;
725
798
  processedRadioGroups.add(fieldKey);
726
799
  }
800
+ if (control instanceof HTMLInputElement && control.type === "checkbox") {
801
+ if (processedCheckboxGroups.has(fieldKey))
802
+ continue;
803
+ processedCheckboxGroups.add(fieldKey);
804
+ }
727
805
  const schemaProp = inputTypeToSchema(control);
728
806
  if (!schemaProp)
729
807
  continue;
@@ -733,6 +811,22 @@ function buildSchemaFromInputs(inputs) {
733
811
  const desc = inferFieldDescription(control);
734
812
  if (desc)
735
813
  schemaProp.description = desc;
814
+ if (control instanceof HTMLInputElement && control.type === "checkbox") {
815
+ const checkboxValues = inputs.filter((i) => i instanceof HTMLInputElement && i.type === "checkbox" && i.name === fieldKey).map((cb) => cb.value).filter((v) => v !== "" && v !== "on");
816
+ if (checkboxValues.length > 1) {
817
+ const arrayProp = {
818
+ type: "array",
819
+ items: { type: "string", enum: checkboxValues },
820
+ title: schemaProp.title
821
+ };
822
+ if (schemaProp.description)
823
+ arrayProp.description = schemaProp.description;
824
+ properties[fieldKey] = arrayProp;
825
+ if (control.required)
826
+ required.push(fieldKey);
827
+ continue;
828
+ }
829
+ }
736
830
  properties[fieldKey] = schemaProp;
737
831
  if (!name)
738
832
  fieldElements.set(fieldKey, control);
@@ -750,6 +844,8 @@ var pendingExecutions = /* @__PURE__ */ new WeakMap();
750
844
  var lastParams = /* @__PURE__ */ new WeakMap();
751
845
  var formFieldElements = /* @__PURE__ */ new WeakMap();
752
846
  var pendingWarnings = /* @__PURE__ */ new WeakMap();
847
+ var pendingFillWarnings = /* @__PURE__ */ new WeakMap();
848
+ var lastFilledSnapshot = /* @__PURE__ */ new WeakMap();
753
849
  var _inputValueSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")?.set;
754
850
  var _textareaValueSetter = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, "value")?.set;
755
851
  var _checkedSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "checked")?.set;
@@ -759,6 +855,7 @@ function buildExecuteHandler(form, config, toolName, metadata) {
759
855
  }
760
856
  attachSubmitInterceptor(form, toolName);
761
857
  return async (params) => {
858
+ pendingFillWarnings.set(form, []);
762
859
  fillFormFields(form, params);
763
860
  window.dispatchEvent(new CustomEvent("toolactivated", { detail: { toolName } }));
764
861
  return new Promise((resolve, reject) => {
@@ -809,9 +906,16 @@ function attachSubmitInterceptor(form, toolName) {
809
906
  const { resolve } = pending;
810
907
  pendingExecutions.delete(form);
811
908
  const formData = serializeFormData(form, lastParams.get(form), formFieldElements.get(form));
909
+ lastFilledSnapshot.delete(form);
812
910
  const missing = pendingWarnings.get(form);
813
911
  pendingWarnings.delete(form);
814
- const warningText = missing?.length ? ` Note: required fields were not filled: ${missing.join(", ")}.` : "";
912
+ const fillWarnings = pendingFillWarnings.get(form) ?? [];
913
+ pendingFillWarnings.delete(form);
914
+ const allWarnings = [
915
+ ...missing?.length ? [`required fields were not filled: ${missing.join(", ")}`] : [],
916
+ ...fillWarnings
917
+ ];
918
+ const warningText = allWarnings.length ? ` Note: ${allWarnings.join("; ")}.` : "";
815
919
  const text = `Form submitted. Fields: ${JSON.stringify(formData)}${warningText}`;
816
920
  const result = { content: [{ type: "text", text }] };
817
921
  if (e.agentInvoked && typeof e.respondWith === "function") {
@@ -821,6 +925,7 @@ function attachSubmitInterceptor(form, toolName) {
821
925
  resolve(result);
822
926
  });
823
927
  form.addEventListener("reset", () => {
928
+ lastFilledSnapshot.delete(form);
824
929
  window.dispatchEvent(new CustomEvent("toolcancel", { detail: { toolName } }));
825
930
  });
826
931
  }
@@ -873,16 +978,30 @@ function findNativeField(form, key) {
873
978
  function fillFormFields(form, params) {
874
979
  lastParams.set(form, params);
875
980
  const fieldEls = formFieldElements.get(form);
981
+ const snapshot = {};
876
982
  for (const [key, value] of Object.entries(params)) {
877
983
  const input = findNativeField(form, key);
878
984
  if (input) {
879
985
  if (input instanceof HTMLInputElement) {
880
986
  fillInput(input, form, key, value);
987
+ if (input.type === "checkbox") {
988
+ if (Array.isArray(value)) {
989
+ const esc = CSS.escape(key);
990
+ snapshot[key] = Array.from(
991
+ form.querySelectorAll(`input[type="checkbox"][name="${esc}"]`)
992
+ ).filter((b) => b.checked).map((b) => b.value);
993
+ } else {
994
+ snapshot[key] = input.checked;
995
+ }
996
+ } else {
997
+ snapshot[key] = input.value;
998
+ }
881
999
  } else if (input instanceof HTMLTextAreaElement) {
882
1000
  setReactValue(input, String(value ?? ""));
1001
+ snapshot[key] = input.value;
883
1002
  } else if (input instanceof HTMLSelectElement) {
884
- input.value = String(value ?? "");
885
- input.dispatchEvent(new Event("change", { bubbles: true }));
1003
+ fillSelectElement(input, value);
1004
+ snapshot[key] = input.multiple ? Array.from(input.options).filter((o) => o.selected).map((o) => o.value) : input.value;
886
1005
  }
887
1006
  continue;
888
1007
  }
@@ -899,23 +1018,56 @@ function fillFormFields(form, params) {
899
1018
  }
900
1019
  if (effectiveEl instanceof HTMLInputElement) {
901
1020
  fillInput(effectiveEl, form, key, value);
1021
+ snapshot[key] = effectiveEl.type === "checkbox" ? effectiveEl.checked : effectiveEl.value;
902
1022
  } else if (effectiveEl instanceof HTMLTextAreaElement) {
903
1023
  setReactValue(effectiveEl, String(value ?? ""));
1024
+ snapshot[key] = effectiveEl.value;
904
1025
  } else if (effectiveEl instanceof HTMLSelectElement) {
905
- effectiveEl.value = String(value ?? "");
906
- effectiveEl.dispatchEvent(new Event("change", { bubbles: true }));
1026
+ fillSelectElement(effectiveEl, value);
1027
+ snapshot[key] = effectiveEl.multiple ? Array.from(effectiveEl.options).filter((o) => o.selected).map((o) => o.value) : effectiveEl.value;
907
1028
  } else {
908
1029
  fillAriaField(effectiveEl, value);
1030
+ snapshot[key] = value;
909
1031
  }
910
1032
  }
911
1033
  }
1034
+ lastFilledSnapshot.set(form, snapshot);
912
1035
  }
913
1036
  function fillInput(input, form, key, value) {
914
1037
  const type = input.type.toLowerCase();
915
1038
  if (type === "checkbox") {
1039
+ if (Array.isArray(value)) {
1040
+ const esc = CSS.escape(key);
1041
+ const allBoxes = form.querySelectorAll(`input[type="checkbox"][name="${esc}"]`);
1042
+ for (const box of allBoxes) {
1043
+ setReactChecked(box, value.map(String).includes(box.value));
1044
+ }
1045
+ return;
1046
+ }
916
1047
  setReactChecked(input, Boolean(value));
917
1048
  return;
918
1049
  }
1050
+ if (type === "number" || type === "range") {
1051
+ const raw = String(value ?? "");
1052
+ const num = Number(raw);
1053
+ if (raw === "" || isNaN(num)) {
1054
+ pendingFillWarnings.get(form)?.push(`"${key}" expects a number, got: ${JSON.stringify(value)}`);
1055
+ return;
1056
+ }
1057
+ const min = input.min !== "" ? parseFloat(input.min) : -Infinity;
1058
+ const max = input.max !== "" ? parseFloat(input.max) : Infinity;
1059
+ if (num < min || num > max) {
1060
+ pendingFillWarnings.get(form)?.push(
1061
+ `"${key}" value ${num} is outside allowed range [${input.min || "?"}, ${input.max || "?"}]`
1062
+ );
1063
+ input.value = String(Math.min(Math.max(num, min), max));
1064
+ } else {
1065
+ input.value = String(num);
1066
+ }
1067
+ input.dispatchEvent(new InputEvent("input", { bubbles: true, cancelable: true, inputType: "insertText", data: String(num) }));
1068
+ input.dispatchEvent(new Event("change", { bubbles: true }));
1069
+ return;
1070
+ }
919
1071
  if (type === "radio") {
920
1072
  const esc = CSS.escape(key);
921
1073
  const radios = form.querySelectorAll(
@@ -936,6 +1088,18 @@ function fillInput(input, form, key, value) {
936
1088
  }
937
1089
  setReactValue(input, String(value ?? ""));
938
1090
  }
1091
+ function fillSelectElement(select, value) {
1092
+ if (select.multiple) {
1093
+ const vals = Array.isArray(value) ? value.map(String) : [String(value ?? "")];
1094
+ for (const opt of Array.from(select.options)) {
1095
+ opt.selected = vals.includes(opt.value);
1096
+ }
1097
+ select.dispatchEvent(new Event("change", { bubbles: true }));
1098
+ return;
1099
+ }
1100
+ select.value = String(value ?? "");
1101
+ select.dispatchEvent(new Event("change", { bubbles: true }));
1102
+ }
939
1103
  function fillAriaField(el, value) {
940
1104
  const role = el.getAttribute("role");
941
1105
  if (role === "checkbox" || role === "switch") {
@@ -948,6 +1112,22 @@ function fillAriaField(el, value) {
948
1112
  el.dispatchEvent(new MouseEvent("click", { bubbles: true }));
949
1113
  return;
950
1114
  }
1115
+ if (role === "radiogroup") {
1116
+ const radios = Array.from(el.querySelectorAll('[role="radio"]'));
1117
+ for (const radio of radios) {
1118
+ const val = (radio.getAttribute("data-value") ?? radio.getAttribute("aria-label") ?? radio.textContent ?? "").trim();
1119
+ if (val === String(value)) {
1120
+ radio.setAttribute("aria-checked", "true");
1121
+ radio.dispatchEvent(new MouseEvent("click", { bubbles: true }));
1122
+ for (const other of radios) {
1123
+ if (other !== radio)
1124
+ other.setAttribute("aria-checked", "false");
1125
+ }
1126
+ break;
1127
+ }
1128
+ }
1129
+ return;
1130
+ }
951
1131
  const htmlEl = el;
952
1132
  if (htmlEl.isContentEditable) {
953
1133
  htmlEl.textContent = String(value ?? "");
@@ -958,6 +1138,7 @@ function fillAriaField(el, value) {
958
1138
  function serializeFormData(form, params, fieldEls) {
959
1139
  const result = {};
960
1140
  const data = new FormData(form);
1141
+ const snapshot = lastFilledSnapshot.get(form);
961
1142
  for (const [key, val] of data.entries()) {
962
1143
  if (result[key] !== void 0) {
963
1144
  const existing = result[key];
@@ -974,6 +1155,10 @@ function serializeFormData(form, params, fieldEls) {
974
1155
  for (const key of Object.keys(params)) {
975
1156
  if (key in result)
976
1157
  continue;
1158
+ if (snapshot && key in snapshot) {
1159
+ result[key] = snapshot[key];
1160
+ continue;
1161
+ }
977
1162
  const el = findNativeField(form, key) ?? fieldEls?.get(key) ?? null;
978
1163
  if (!el)
979
1164
  continue;
@@ -1012,8 +1197,7 @@ function fillElement(el, value) {
1012
1197
  } else if (el instanceof HTMLTextAreaElement) {
1013
1198
  setReactValue(el, String(value ?? ""));
1014
1199
  } else if (el instanceof HTMLSelectElement) {
1015
- el.value = String(value ?? "");
1016
- el.dispatchEvent(new Event("change", { bubbles: true }));
1200
+ fillSelectElement(el, value);
1017
1201
  } else {
1018
1202
  fillAriaField(el, value);
1019
1203
  }