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.
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/auto-webmcp.cjs.js +208 -10
- package/dist/auto-webmcp.cjs.js.map +2 -2
- package/dist/auto-webmcp.esm.js +208 -10
- package/dist/auto-webmcp.esm.js.map +2 -2
- package/dist/auto-webmcp.iife.js +2 -2
- package/dist/auto-webmcp.iife.js.map +3 -3
- package/dist/interceptor.d.ts.map +1 -1
- package/dist/schema.d.ts +7 -0
- package/dist/schema.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/analyzer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,UAAU,
|
|
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"}
|
package/dist/auto-webmcp.cjs.js
CHANGED
|
@@ -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
|
|
196
|
-
|
|
197
|
-
|
|
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
|
-
|
|
200
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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 ?? "");
|