auto-webmcp 0.3.6 → 0.3.8
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 +95 -17
- package/dist/auto-webmcp.cjs.js.map +2 -2
- package/dist/auto-webmcp.esm.js +95 -17
- package/dist/auto-webmcp.esm.js.map +2 -2
- package/dist/auto-webmcp.iife.js +1 -1
- package/dist/auto-webmcp.iife.js.map +3 -3
- package/dist/discovery.d.ts.map +1 -1
- package/dist/schema.d.ts +1 -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,EAAmJ,MAAM,aAAa,CAAC;AAC1L,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,WAAW,eAAe;IAC9B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,UAAU,CAAC;IACxB,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,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,CAOxF;
|
|
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,eAAe;IAC9B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,UAAU,CAAC;IACxB,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,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,CAOxF;AAspBD;;;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,CAMd"}
|
package/dist/auto-webmcp.cjs.js
CHANGED
|
@@ -630,13 +630,14 @@ function buildSchema(form) {
|
|
|
630
630
|
required.push(key);
|
|
631
631
|
}
|
|
632
632
|
}
|
|
633
|
-
return { schema: { type: "object", properties, required }, fieldElements };
|
|
633
|
+
return { schema: { "$schema": "https://json-schema.org/draft/2020-12/schema", type: "object", properties, required }, fieldElements };
|
|
634
634
|
}
|
|
635
|
+
var AUTO_GENERATED_ID_RE = /^_r_[0-9a-z]+_$|^:[a-z0-9]+:$/i;
|
|
635
636
|
function resolveNativeControlFallbackKey(control) {
|
|
636
637
|
const el = control;
|
|
637
638
|
if (el.dataset["webmcpName"])
|
|
638
639
|
return sanitizeName(el.dataset["webmcpName"]);
|
|
639
|
-
if (control.id)
|
|
640
|
+
if (control.id && !AUTO_GENERATED_ID_RE.test(control.id))
|
|
640
641
|
return sanitizeName(control.id);
|
|
641
642
|
const label = control.getAttribute("aria-label");
|
|
642
643
|
if (label)
|
|
@@ -835,7 +836,24 @@ function analyzeOrphanInputGroup(container, inputs, submitBtn) {
|
|
|
835
836
|
const name = inferOrphanToolName(container, submitBtn);
|
|
836
837
|
const description = inferOrphanToolDescription(container);
|
|
837
838
|
const { schema: inputSchema, fieldElements } = buildSchemaFromInputs(inputs);
|
|
838
|
-
|
|
839
|
+
const annotations = inferOrphanAnnotations(submitBtn);
|
|
840
|
+
return { name, description, inputSchema, annotations, fieldElements };
|
|
841
|
+
}
|
|
842
|
+
function inferOrphanAnnotations(submitBtn) {
|
|
843
|
+
const annotations = {};
|
|
844
|
+
const submitText = submitBtn instanceof HTMLInputElement ? submitBtn.value.trim() : submitBtn?.textContent?.trim() ?? "";
|
|
845
|
+
if (READONLY_BUTTON_PATTERNS.test(submitText)) {
|
|
846
|
+
annotations.readOnlyHint = true;
|
|
847
|
+
annotations.idempotentHint = true;
|
|
848
|
+
}
|
|
849
|
+
if (DESTRUCTIVE_BUTTON_PATTERNS.test(submitText)) {
|
|
850
|
+
annotations.destructiveHint = true;
|
|
851
|
+
}
|
|
852
|
+
if (annotations.readOnlyHint !== true) {
|
|
853
|
+
annotations.openWorldHint = true;
|
|
854
|
+
}
|
|
855
|
+
const hasNonDefault = annotations.readOnlyHint === true || annotations.destructiveHint === true || annotations.idempotentHint === true || annotations.openWorldHint === false;
|
|
856
|
+
return hasNonDefault ? annotations : {};
|
|
839
857
|
}
|
|
840
858
|
function inferOrphanToolName(container, submitBtn) {
|
|
841
859
|
if (submitBtn) {
|
|
@@ -890,8 +908,8 @@ function buildSchemaFromInputs(inputs) {
|
|
|
890
908
|
const processedRadioGroups = /* @__PURE__ */ new Set();
|
|
891
909
|
const processedCheckboxGroups = /* @__PURE__ */ new Set();
|
|
892
910
|
for (const control of inputs) {
|
|
893
|
-
const
|
|
894
|
-
const fieldKey =
|
|
911
|
+
const rawName = control.name;
|
|
912
|
+
const fieldKey = (rawName ? sanitizeName(rawName) : null) || resolveNativeControlFallbackKey(control);
|
|
895
913
|
if (!fieldKey)
|
|
896
914
|
continue;
|
|
897
915
|
if (control instanceof HTMLInputElement && control.type === "radio") {
|
|
@@ -930,12 +948,12 @@ function buildSchemaFromInputs(inputs) {
|
|
|
930
948
|
}
|
|
931
949
|
}
|
|
932
950
|
properties[fieldKey] = schemaProp;
|
|
933
|
-
if (!
|
|
951
|
+
if (!rawName)
|
|
934
952
|
fieldElements.set(fieldKey, control);
|
|
935
953
|
if (control.required)
|
|
936
954
|
required.push(fieldKey);
|
|
937
955
|
}
|
|
938
|
-
return { schema: { type: "object", properties, required }, fieldElements };
|
|
956
|
+
return { schema: { "$schema": "https://json-schema.org/draft/2020-12/schema", type: "object", properties, required }, fieldElements };
|
|
939
957
|
}
|
|
940
958
|
|
|
941
959
|
// src/discovery.ts
|
|
@@ -1442,7 +1460,7 @@ async function registerForm(form, config) {
|
|
|
1442
1460
|
registeredForms.add(form);
|
|
1443
1461
|
registeredFormCount++;
|
|
1444
1462
|
if (config.debug) {
|
|
1445
|
-
console.
|
|
1463
|
+
console.log(`[auto-webmcp] Registered: ${metadata.name}`, metadata);
|
|
1446
1464
|
}
|
|
1447
1465
|
emit("form:registered", form, metadata.name);
|
|
1448
1466
|
}
|
|
@@ -1454,7 +1472,7 @@ async function unregisterForm(form, config) {
|
|
|
1454
1472
|
await unregisterFormTool(form);
|
|
1455
1473
|
registeredForms.delete(form);
|
|
1456
1474
|
if (config.debug) {
|
|
1457
|
-
console.
|
|
1475
|
+
console.log(`[auto-webmcp] Unregistered: ${name}`);
|
|
1458
1476
|
}
|
|
1459
1477
|
emit("form:unregistered", form, name);
|
|
1460
1478
|
}
|
|
@@ -1555,7 +1573,7 @@ async function scanOrphanInputs(config) {
|
|
|
1555
1573
|
if (!isWebMCPSupported())
|
|
1556
1574
|
return;
|
|
1557
1575
|
const SUBMIT_BTN_SELECTOR = '[type="submit"]:not([disabled]), button:not([type]):not([disabled])';
|
|
1558
|
-
const SUBMIT_BTN_GROUPING_SELECTOR = '[type="submit"]
|
|
1576
|
+
const SUBMIT_BTN_GROUPING_SELECTOR = '[type="submit"]';
|
|
1559
1577
|
const SUBMIT_TEXT_RE = /subscribe|submit|sign[\s-]?up|send|join|go|search/i;
|
|
1560
1578
|
const orphanInputs = Array.from(
|
|
1561
1579
|
document.querySelectorAll(
|
|
@@ -1563,11 +1581,17 @@ async function scanOrphanInputs(config) {
|
|
|
1563
1581
|
)
|
|
1564
1582
|
).filter((el) => {
|
|
1565
1583
|
if (el instanceof HTMLInputElement && ORPHAN_EXCLUDED_TYPES.has(el.type.toLowerCase())) {
|
|
1584
|
+
console.log(`[auto-webmcp] orphan: skipping excluded type "${el.type}" (name="${el.name}" id="${el.id}")`);
|
|
1566
1585
|
return false;
|
|
1567
1586
|
}
|
|
1568
1587
|
const rect = el.getBoundingClientRect();
|
|
1569
|
-
|
|
1588
|
+
if (rect.width === 0 || rect.height === 0) {
|
|
1589
|
+
console.log(`[auto-webmcp] orphan: skipping invisible input (name="${el.name}" id="${el.id}")`);
|
|
1590
|
+
return false;
|
|
1591
|
+
}
|
|
1592
|
+
return true;
|
|
1570
1593
|
});
|
|
1594
|
+
console.log(`[auto-webmcp] orphan: found ${orphanInputs.length} visible orphan input(s)`);
|
|
1571
1595
|
if (orphanInputs.length === 0)
|
|
1572
1596
|
return;
|
|
1573
1597
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -1584,10 +1608,12 @@ async function scanOrphanInputs(config) {
|
|
|
1584
1608
|
}
|
|
1585
1609
|
container = container.parentElement;
|
|
1586
1610
|
}
|
|
1611
|
+
console.log(`[auto-webmcp] orphan: input (name="${input.name}" id="${input.id}") grouped into container`, foundContainer);
|
|
1587
1612
|
if (!groups.has(foundContainer))
|
|
1588
1613
|
groups.set(foundContainer, []);
|
|
1589
1614
|
groups.get(foundContainer).push(input);
|
|
1590
1615
|
}
|
|
1616
|
+
console.log(`[auto-webmcp] orphan: ${groups.size} group(s) found`);
|
|
1591
1617
|
for (const [container, inputs] of groups) {
|
|
1592
1618
|
const allCandidates = Array.from(
|
|
1593
1619
|
container.querySelectorAll(SUBMIT_BTN_SELECTOR)
|
|
@@ -1596,6 +1622,12 @@ async function scanOrphanInputs(config) {
|
|
|
1596
1622
|
return r.width > 0 && r.height > 0;
|
|
1597
1623
|
});
|
|
1598
1624
|
let submitBtn = allCandidates[allCandidates.length - 1] ?? null;
|
|
1625
|
+
const disabledCandidates = Array.from(
|
|
1626
|
+
container.querySelectorAll(SUBMIT_BTN_GROUPING_SELECTOR)
|
|
1627
|
+
).filter((b) => b.disabled);
|
|
1628
|
+
if (!submitBtn && disabledCandidates.length > 0) {
|
|
1629
|
+
console.log(`[auto-webmcp] orphan: no enabled submit button found in container \u2014 ${disabledCandidates.length} disabled button(s) present:`, disabledCandidates.map((b) => b.textContent?.trim()));
|
|
1630
|
+
}
|
|
1599
1631
|
if (!submitBtn) {
|
|
1600
1632
|
const pageBtns = Array.from(document.querySelectorAll("button")).filter(
|
|
1601
1633
|
(b) => {
|
|
@@ -1604,36 +1636,82 @@ async function scanOrphanInputs(config) {
|
|
|
1604
1636
|
}
|
|
1605
1637
|
);
|
|
1606
1638
|
submitBtn = pageBtns[pageBtns.length - 1] ?? null;
|
|
1639
|
+
if (submitBtn)
|
|
1640
|
+
console.log(`[auto-webmcp] orphan: using page-wide fallback submit button: "${submitBtn.textContent?.trim()}"`);
|
|
1607
1641
|
}
|
|
1642
|
+
console.log(`[auto-webmcp] orphan: submit button for group:`, submitBtn ? `"${submitBtn.textContent?.trim()}" disabled=${submitBtn.disabled}` : "none");
|
|
1608
1643
|
const metadata = analyzeOrphanInputGroup(container, inputs, submitBtn);
|
|
1644
|
+
console.log(`[auto-webmcp] orphan: tool="${metadata.name}" schema keys:`, Object.keys(metadata.inputSchema.properties));
|
|
1609
1645
|
const inputPairs = [];
|
|
1610
1646
|
const schemaProps = metadata.inputSchema.properties;
|
|
1647
|
+
const AUTO_ID_RE = /^_r_[0-9a-z]+_$/i;
|
|
1611
1648
|
for (const el of inputs) {
|
|
1612
|
-
const
|
|
1649
|
+
const id = el.id && !AUTO_ID_RE.test(el.id) ? el.id : null;
|
|
1650
|
+
const key = el.name || el.dataset["webmcpName"] || id || el.getAttribute("aria-label") || (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement ? el.placeholder || null : null) || null;
|
|
1613
1651
|
const safeKey = key ? key.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 64) : null;
|
|
1614
|
-
|
|
1652
|
+
const matched = !!(safeKey && schemaProps[safeKey]);
|
|
1653
|
+
console.log(`[auto-webmcp] orphan: field (name="${el.name}" id="${el.id}") rawKey="${key}" safeKey="${safeKey}" matched=${matched}`);
|
|
1654
|
+
if (matched) {
|
|
1615
1655
|
inputPairs.push({ key: safeKey, el });
|
|
1616
1656
|
}
|
|
1617
1657
|
}
|
|
1658
|
+
console.log(`[auto-webmcp] orphan: ${inputPairs.length}/${inputs.length} input(s) mapped to schema keys`);
|
|
1618
1659
|
const toolName = metadata.name;
|
|
1619
1660
|
const execute = async (params) => {
|
|
1661
|
+
console.log(`[auto-webmcp] orphan execute: tool="${toolName}" params=`, params);
|
|
1662
|
+
console.log(`[auto-webmcp] orphan execute: inputPairs=`, inputPairs.map((p) => p.key));
|
|
1620
1663
|
for (const { key, el } of inputPairs) {
|
|
1621
1664
|
if (params[key] !== void 0) {
|
|
1665
|
+
console.log(`[auto-webmcp] orphan execute: filling key="${key}" value=`, params[key], "element=", el);
|
|
1622
1666
|
fillElement(el, params[key]);
|
|
1667
|
+
console.log(`[auto-webmcp] orphan execute: after fill, element value=`, el.value);
|
|
1668
|
+
} else {
|
|
1669
|
+
console.log(`[auto-webmcp] orphan execute: key="${key}" not in params, skipping`);
|
|
1623
1670
|
}
|
|
1624
1671
|
}
|
|
1625
1672
|
window.dispatchEvent(new CustomEvent("toolactivated", { detail: { toolName } }));
|
|
1626
|
-
|
|
1673
|
+
if (!config.autoSubmit) {
|
|
1674
|
+
console.log(`[auto-webmcp] orphan execute: autoSubmit=false, returning without clicking submit`);
|
|
1675
|
+
return { content: [{ type: "text", text: "Fields filled. Ready to submit." }] };
|
|
1676
|
+
}
|
|
1677
|
+
console.log(`[auto-webmcp] orphan execute: polling for enabled submit button (up to 2s)...`);
|
|
1678
|
+
let btn = null;
|
|
1679
|
+
const deadline = Date.now() + 2e3;
|
|
1680
|
+
while (Date.now() < deadline) {
|
|
1681
|
+
const candidates = Array.from(
|
|
1682
|
+
container.querySelectorAll(SUBMIT_BTN_SELECTOR)
|
|
1683
|
+
).filter((b) => {
|
|
1684
|
+
const r = b.getBoundingClientRect();
|
|
1685
|
+
return r.width > 0 && r.height > 0;
|
|
1686
|
+
});
|
|
1687
|
+
const last = candidates[candidates.length - 1] ?? null;
|
|
1688
|
+
if (last) {
|
|
1689
|
+
btn = last;
|
|
1690
|
+
break;
|
|
1691
|
+
}
|
|
1692
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
1693
|
+
}
|
|
1694
|
+
if (!btn) {
|
|
1695
|
+
console.warn(`[auto-webmcp] orphan execute: submit button still disabled after 2s`);
|
|
1696
|
+
return { content: [{ type: "text", text: "Fields filled but the submit button is still disabled. The page may require additional input before submitting." }] };
|
|
1697
|
+
}
|
|
1698
|
+
console.log(`[auto-webmcp] orphan execute: clicking submit button "${btn.textContent?.trim()}"`);
|
|
1699
|
+
btn.click();
|
|
1700
|
+
return { content: [{ type: "text", text: "Fields filled and form submitted." }] };
|
|
1627
1701
|
};
|
|
1628
1702
|
try {
|
|
1629
|
-
|
|
1703
|
+
const toolDef = {
|
|
1630
1704
|
name: metadata.name,
|
|
1631
1705
|
description: metadata.description,
|
|
1632
1706
|
inputSchema: metadata.inputSchema,
|
|
1633
1707
|
execute
|
|
1634
|
-
}
|
|
1708
|
+
};
|
|
1709
|
+
if (metadata.annotations && Object.keys(metadata.annotations).length > 0) {
|
|
1710
|
+
toolDef.annotations = metadata.annotations;
|
|
1711
|
+
}
|
|
1712
|
+
await navigator.modelContext.registerTool(toolDef);
|
|
1635
1713
|
if (config.debug) {
|
|
1636
|
-
console.
|
|
1714
|
+
console.log(`[auto-webmcp] Orphan tool registered: ${metadata.name}`, metadata);
|
|
1637
1715
|
}
|
|
1638
1716
|
} catch {
|
|
1639
1717
|
}
|