auto-webmcp 0.3.15 → 0.3.16
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 +209 -115
- package/dist/auto-webmcp.cjs.js.map +4 -4
- package/dist/auto-webmcp.esm.js +209 -122
- package/dist/auto-webmcp.esm.js.map +4 -4
- package/dist/auto-webmcp.iife.js +1 -1
- package/dist/auto-webmcp.iife.js.map +4 -4
- package/dist/discovery.d.ts.map +1 -1
- package/dist/interceptor.d.ts +1 -1
- package/dist/interceptor.d.ts.map +1 -1
- package/dist/registry.d.ts +7 -3
- package/dist/registry.d.ts.map +1 -1
- 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;AAuwBD;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,OAAO,EAClB,MAAM,EAAE,KAAK,CAAC,gBAAgB,GAAG,mBAAmB,GAAG,iBAAiB,GAAG,WAAW,CAAC,EACvF,SAAS,EAAE,iBAAiB,GAAG,gBAAgB,GAAG,IAAI,GACrD,YAAY,CAMd"}
|
package/dist/auto-webmcp.cjs.js
CHANGED
|
@@ -3,9 +3,6 @@ var __defProp = Object.defineProperty;
|
|
|
3
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __esm = (fn, res) => function __init() {
|
|
7
|
-
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
8
|
-
};
|
|
9
6
|
var __export = (target, all) => {
|
|
10
7
|
for (var name in all)
|
|
11
8
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -20,76 +17,6 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
20
17
|
};
|
|
21
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
22
19
|
|
|
23
|
-
// src/registry.ts
|
|
24
|
-
var registry_exports = {};
|
|
25
|
-
__export(registry_exports, {
|
|
26
|
-
getAllRegisteredTools: () => getAllRegisteredTools,
|
|
27
|
-
getRegisteredToolName: () => getRegisteredToolName,
|
|
28
|
-
isWebMCPSupported: () => isWebMCPSupported,
|
|
29
|
-
registerFormTool: () => registerFormTool,
|
|
30
|
-
unregisterAll: () => unregisterAll,
|
|
31
|
-
unregisterFormTool: () => unregisterFormTool
|
|
32
|
-
});
|
|
33
|
-
function isWebMCPSupported() {
|
|
34
|
-
return typeof navigator !== "undefined" && typeof navigator.modelContext !== "undefined";
|
|
35
|
-
}
|
|
36
|
-
async function registerFormTool(form, metadata, execute) {
|
|
37
|
-
if (!isWebMCPSupported())
|
|
38
|
-
return;
|
|
39
|
-
const existing = registeredTools.get(form);
|
|
40
|
-
if (existing) {
|
|
41
|
-
await unregisterFormTool(form);
|
|
42
|
-
}
|
|
43
|
-
const toolDef = {
|
|
44
|
-
name: metadata.name,
|
|
45
|
-
description: metadata.description,
|
|
46
|
-
inputSchema: metadata.inputSchema,
|
|
47
|
-
execute
|
|
48
|
-
};
|
|
49
|
-
if (metadata.annotations && Object.keys(metadata.annotations).length > 0) {
|
|
50
|
-
toolDef.annotations = metadata.annotations;
|
|
51
|
-
}
|
|
52
|
-
try {
|
|
53
|
-
await navigator.modelContext.registerTool(toolDef);
|
|
54
|
-
} catch {
|
|
55
|
-
try {
|
|
56
|
-
await navigator.modelContext.unregisterTool(metadata.name);
|
|
57
|
-
await navigator.modelContext.registerTool(toolDef);
|
|
58
|
-
} catch {
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
registeredTools.set(form, metadata.name);
|
|
62
|
-
}
|
|
63
|
-
async function unregisterFormTool(form) {
|
|
64
|
-
if (!isWebMCPSupported())
|
|
65
|
-
return;
|
|
66
|
-
const name = registeredTools.get(form);
|
|
67
|
-
if (!name)
|
|
68
|
-
return;
|
|
69
|
-
try {
|
|
70
|
-
await navigator.modelContext.unregisterTool(name);
|
|
71
|
-
} catch {
|
|
72
|
-
}
|
|
73
|
-
registeredTools.delete(form);
|
|
74
|
-
}
|
|
75
|
-
function getRegisteredToolName(form) {
|
|
76
|
-
return registeredTools.get(form);
|
|
77
|
-
}
|
|
78
|
-
function getAllRegisteredTools() {
|
|
79
|
-
return Array.from(registeredTools.entries()).map(([form, name]) => ({ form, name }));
|
|
80
|
-
}
|
|
81
|
-
async function unregisterAll() {
|
|
82
|
-
const entries = Array.from(registeredTools.entries());
|
|
83
|
-
await Promise.all(entries.map(([form]) => unregisterFormTool(form)));
|
|
84
|
-
}
|
|
85
|
-
var registeredTools;
|
|
86
|
-
var init_registry = __esm({
|
|
87
|
-
"src/registry.ts"() {
|
|
88
|
-
"use strict";
|
|
89
|
-
registeredTools = /* @__PURE__ */ new Map();
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
|
|
93
20
|
// src/index.ts
|
|
94
21
|
var src_exports = {};
|
|
95
22
|
__export(src_exports, {
|
|
@@ -250,19 +177,19 @@ function mapSelectElement(select) {
|
|
|
250
177
|
return { type: "string", enum: enumValues, oneOf };
|
|
251
178
|
}
|
|
252
179
|
function collectCheckboxEnum(form, name) {
|
|
253
|
-
return Array.from(
|
|
254
|
-
|
|
180
|
+
return Array.from(form.elements).filter(
|
|
181
|
+
(el) => el instanceof HTMLInputElement && el.type === "checkbox" && el.name === name
|
|
255
182
|
).map((cb) => cb.value).filter((v) => v !== "" && v !== "on");
|
|
256
183
|
}
|
|
257
184
|
function collectRadioEnum(form, name) {
|
|
258
|
-
const radios = Array.from(
|
|
259
|
-
|
|
185
|
+
const radios = Array.from(form.elements).filter(
|
|
186
|
+
(el) => el instanceof HTMLInputElement && el.type === "radio" && el.name === name
|
|
260
187
|
);
|
|
261
188
|
return radios.map((r) => r.value).filter((v) => v !== "");
|
|
262
189
|
}
|
|
263
190
|
function collectRadioOneOf(form, name) {
|
|
264
|
-
const radios = Array.from(
|
|
265
|
-
|
|
191
|
+
const radios = Array.from(form.elements).filter(
|
|
192
|
+
(el) => el instanceof HTMLInputElement && el.type === "radio" && el.name === name
|
|
266
193
|
).filter((r) => r.value !== "");
|
|
267
194
|
return radios.map((r) => {
|
|
268
195
|
const title = getRadioLabelText(r);
|
|
@@ -522,20 +449,26 @@ function collectShadowControls(root, visited = /* @__PURE__ */ new Set()) {
|
|
|
522
449
|
}
|
|
523
450
|
return results;
|
|
524
451
|
}
|
|
452
|
+
function collectFormAssociatedControls(form) {
|
|
453
|
+
const controls = Array.from(form.elements).filter(
|
|
454
|
+
(el) => el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement
|
|
455
|
+
);
|
|
456
|
+
const seen = new Set(controls);
|
|
457
|
+
for (const shadowControl of collectShadowControls(form)) {
|
|
458
|
+
if (!seen.has(shadowControl)) {
|
|
459
|
+
controls.push(shadowControl);
|
|
460
|
+
seen.add(shadowControl);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
return controls;
|
|
464
|
+
}
|
|
525
465
|
function buildSchema(form) {
|
|
526
466
|
const properties = {};
|
|
527
467
|
const required = [];
|
|
528
468
|
const fieldElements = /* @__PURE__ */ new Map();
|
|
529
469
|
const processedRadioGroups = /* @__PURE__ */ new Set();
|
|
530
470
|
const processedCheckboxGroups = /* @__PURE__ */ new Set();
|
|
531
|
-
const controls =
|
|
532
|
-
...Array.from(
|
|
533
|
-
form.querySelectorAll(
|
|
534
|
-
"input, textarea, select"
|
|
535
|
-
)
|
|
536
|
-
),
|
|
537
|
-
...collectShadowControls(form)
|
|
538
|
-
];
|
|
471
|
+
const controls = collectFormAssociatedControls(form);
|
|
539
472
|
for (const control of controls) {
|
|
540
473
|
const name = control.name;
|
|
541
474
|
const fieldKey = name || resolveNativeControlFallbackKey(control);
|
|
@@ -568,8 +501,8 @@ function buildSchema(form) {
|
|
|
568
501
|
const radioOneOf = collectRadioOneOf(form, fieldKey);
|
|
569
502
|
if (radioOneOf.length > 0)
|
|
570
503
|
schemaProp.oneOf = radioOneOf;
|
|
571
|
-
const checkedRadio = form.
|
|
572
|
-
|
|
504
|
+
const checkedRadio = Array.from(form.elements).find(
|
|
505
|
+
(el) => el instanceof HTMLInputElement && el.type === "radio" && el.name === fieldKey && el.checked
|
|
573
506
|
);
|
|
574
507
|
if (checkedRadio?.value)
|
|
575
508
|
schemaProp.default = checkedRadio.value;
|
|
@@ -584,10 +517,8 @@ function buildSchema(form) {
|
|
|
584
517
|
};
|
|
585
518
|
if (schemaProp.description)
|
|
586
519
|
arrayProp.description = schemaProp.description;
|
|
587
|
-
const checkedBoxes = Array.from(
|
|
588
|
-
|
|
589
|
-
`input[type="checkbox"][name="${CSS.escape(fieldKey)}"]:checked`
|
|
590
|
-
)
|
|
520
|
+
const checkedBoxes = Array.from(form.elements).filter(
|
|
521
|
+
(el) => el instanceof HTMLInputElement && el.type === "checkbox" && el.name === fieldKey && el.checked
|
|
591
522
|
).map((b) => b.value);
|
|
592
523
|
if (checkedBoxes.length > 0)
|
|
593
524
|
arrayProp.default = checkedBoxes;
|
|
@@ -1057,8 +988,68 @@ function buildSchemaFromInputs(inputs) {
|
|
|
1057
988
|
return { schema: { "$schema": "https://json-schema.org/draft/2020-12/schema", type: "object", properties, required }, fieldElements };
|
|
1058
989
|
}
|
|
1059
990
|
|
|
1060
|
-
// src/
|
|
1061
|
-
|
|
991
|
+
// src/registry.ts
|
|
992
|
+
var registeredTools = /* @__PURE__ */ new Map();
|
|
993
|
+
var registrationControllers = /* @__PURE__ */ new Map();
|
|
994
|
+
function isWebMCPSupported() {
|
|
995
|
+
return typeof navigator !== "undefined" && typeof navigator.modelContext !== "undefined";
|
|
996
|
+
}
|
|
997
|
+
async function registerFormTool(form, metadata, execute) {
|
|
998
|
+
if (!isWebMCPSupported())
|
|
999
|
+
return;
|
|
1000
|
+
const existing = registeredTools.get(form);
|
|
1001
|
+
if (existing) {
|
|
1002
|
+
await unregisterFormTool(form);
|
|
1003
|
+
}
|
|
1004
|
+
const toolDef = {
|
|
1005
|
+
name: metadata.name,
|
|
1006
|
+
description: metadata.description,
|
|
1007
|
+
inputSchema: metadata.inputSchema,
|
|
1008
|
+
execute
|
|
1009
|
+
};
|
|
1010
|
+
if (metadata.annotations && Object.keys(metadata.annotations).length > 0) {
|
|
1011
|
+
toolDef.annotations = metadata.annotations;
|
|
1012
|
+
}
|
|
1013
|
+
const controller = new AbortController();
|
|
1014
|
+
registrationControllers.set(form, controller);
|
|
1015
|
+
try {
|
|
1016
|
+
await navigator.modelContext.registerTool(toolDef, { signal: controller.signal });
|
|
1017
|
+
} catch {
|
|
1018
|
+
try {
|
|
1019
|
+
await navigator.modelContext.unregisterTool?.(metadata.name);
|
|
1020
|
+
await navigator.modelContext.registerTool(toolDef, { signal: controller.signal });
|
|
1021
|
+
} catch {
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
registeredTools.set(form, metadata.name);
|
|
1025
|
+
}
|
|
1026
|
+
async function unregisterFormTool(form) {
|
|
1027
|
+
if (!isWebMCPSupported())
|
|
1028
|
+
return;
|
|
1029
|
+
const name = registeredTools.get(form);
|
|
1030
|
+
if (!name)
|
|
1031
|
+
return;
|
|
1032
|
+
const controller = registrationControllers.get(form);
|
|
1033
|
+
if (controller) {
|
|
1034
|
+
controller.abort();
|
|
1035
|
+
registrationControllers.delete(form);
|
|
1036
|
+
}
|
|
1037
|
+
try {
|
|
1038
|
+
await navigator.modelContext.unregisterTool?.(name);
|
|
1039
|
+
} catch {
|
|
1040
|
+
}
|
|
1041
|
+
registeredTools.delete(form);
|
|
1042
|
+
}
|
|
1043
|
+
function getRegisteredToolName(form) {
|
|
1044
|
+
return registeredTools.get(form);
|
|
1045
|
+
}
|
|
1046
|
+
function getAllRegisteredTools() {
|
|
1047
|
+
return Array.from(registeredTools.entries()).map(([form, name]) => ({ form, name }));
|
|
1048
|
+
}
|
|
1049
|
+
async function unregisterAll() {
|
|
1050
|
+
const entries = Array.from(registeredTools.entries());
|
|
1051
|
+
await Promise.all(entries.map(([form]) => unregisterFormTool(form)));
|
|
1052
|
+
}
|
|
1062
1053
|
|
|
1063
1054
|
// src/interceptor.ts
|
|
1064
1055
|
var pendingExecutions = /* @__PURE__ */ new WeakMap();
|
|
@@ -1075,7 +1066,20 @@ function buildExecuteHandler(form, config, toolName, metadata) {
|
|
|
1075
1066
|
formFieldElements.set(form, metadata.fieldElements);
|
|
1076
1067
|
}
|
|
1077
1068
|
attachSubmitInterceptor(form, toolName);
|
|
1078
|
-
return async (params) => {
|
|
1069
|
+
return async (params, client) => {
|
|
1070
|
+
const modelContextClient = client;
|
|
1071
|
+
if (config.autoSubmit && metadata?.annotations?.destructiveHint === true && typeof modelContextClient?.requestUserInteraction === "function") {
|
|
1072
|
+
const approved = await modelContextClient.requestUserInteraction(async () => {
|
|
1073
|
+
return new Promise((resolve) => {
|
|
1074
|
+
const ok = window.confirm(`Agent requested a destructive action via "${toolName}". Continue?`);
|
|
1075
|
+
resolve(ok);
|
|
1076
|
+
});
|
|
1077
|
+
});
|
|
1078
|
+
if (!approved) {
|
|
1079
|
+
window.dispatchEvent(new CustomEvent("toolcancel", { detail: { toolName } }));
|
|
1080
|
+
return { content: [{ type: "text", text: `Cancelled "${toolName}" by user.` }] };
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1079
1083
|
pendingFillWarnings.set(form, []);
|
|
1080
1084
|
pendingWarnings.delete(form);
|
|
1081
1085
|
fillFormFields(form, params);
|
|
@@ -1212,7 +1216,23 @@ function findInShadowRoots(root, selector) {
|
|
|
1212
1216
|
}
|
|
1213
1217
|
return null;
|
|
1214
1218
|
}
|
|
1219
|
+
function getAssociatedInputsByName(form, type, name) {
|
|
1220
|
+
return Array.from(form.elements).filter(
|
|
1221
|
+
(el) => el instanceof HTMLInputElement && el.type === type && el.name === name
|
|
1222
|
+
);
|
|
1223
|
+
}
|
|
1215
1224
|
function findNativeField(form, key) {
|
|
1225
|
+
const named = form.elements.namedItem(key);
|
|
1226
|
+
if (typeof named === "object" && named !== null && (named instanceof HTMLInputElement || named instanceof HTMLTextAreaElement || named instanceof HTMLSelectElement)) {
|
|
1227
|
+
return named;
|
|
1228
|
+
}
|
|
1229
|
+
if (named instanceof RadioNodeList) {
|
|
1230
|
+
const first = named[0];
|
|
1231
|
+
const firstObj = first;
|
|
1232
|
+
if (firstObj instanceof HTMLInputElement || firstObj instanceof HTMLTextAreaElement || firstObj instanceof HTMLSelectElement) {
|
|
1233
|
+
return firstObj;
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1216
1236
|
const esc = CSS.escape(key);
|
|
1217
1237
|
const light = form.querySelector(`[name="${esc}"]`) ?? form.querySelector(
|
|
1218
1238
|
`input#${esc}, textarea#${esc}, select#${esc}`
|
|
@@ -1232,10 +1252,7 @@ function fillFormFields(form, params) {
|
|
|
1232
1252
|
fillInput(input, form, key, value);
|
|
1233
1253
|
if (input.type === "checkbox") {
|
|
1234
1254
|
if (Array.isArray(value)) {
|
|
1235
|
-
|
|
1236
|
-
snapshot[key] = Array.from(
|
|
1237
|
-
form.querySelectorAll(`input[type="checkbox"][name="${esc}"]`)
|
|
1238
|
-
).filter((b) => b.checked).map((b) => b.value);
|
|
1255
|
+
snapshot[key] = getAssociatedInputsByName(form, "checkbox", key).filter((b) => b.checked).map((b) => b.value);
|
|
1239
1256
|
} else {
|
|
1240
1257
|
snapshot[key] = input.checked;
|
|
1241
1258
|
}
|
|
@@ -1284,8 +1301,7 @@ function fillInput(input, form, key, value) {
|
|
|
1284
1301
|
const type = input.type.toLowerCase();
|
|
1285
1302
|
if (type === "checkbox") {
|
|
1286
1303
|
if (Array.isArray(value)) {
|
|
1287
|
-
const
|
|
1288
|
-
const allBoxes = form.querySelectorAll(`input[type="checkbox"][name="${esc}"]`);
|
|
1304
|
+
const allBoxes = getAssociatedInputsByName(form, "checkbox", key);
|
|
1289
1305
|
for (const box of allBoxes) {
|
|
1290
1306
|
setReactChecked(box, value.map(String).includes(box.value));
|
|
1291
1307
|
}
|
|
@@ -1326,10 +1342,7 @@ function fillInput(input, form, key, value) {
|
|
|
1326
1342
|
return;
|
|
1327
1343
|
}
|
|
1328
1344
|
if (type === "radio") {
|
|
1329
|
-
const
|
|
1330
|
-
const radios = form.querySelectorAll(
|
|
1331
|
-
`input[type="radio"][name="${esc}"]`
|
|
1332
|
-
);
|
|
1345
|
+
const radios = getAssociatedInputsByName(form, "radio", key);
|
|
1333
1346
|
for (const radio of radios) {
|
|
1334
1347
|
if (radio.value === String(value)) {
|
|
1335
1348
|
if (_checkedSetter) {
|
|
@@ -1652,9 +1665,35 @@ function isExcluded(form, config) {
|
|
|
1652
1665
|
}
|
|
1653
1666
|
return false;
|
|
1654
1667
|
}
|
|
1668
|
+
function withNumericSuffix(baseName, n) {
|
|
1669
|
+
const suffix = `_${n}`;
|
|
1670
|
+
return `${baseName.slice(0, Math.max(1, 64 - suffix.length))}${suffix}`;
|
|
1671
|
+
}
|
|
1672
|
+
function getUsedToolNames(excludeForm) {
|
|
1673
|
+
const names = new Set(registeredOrphanToolNames);
|
|
1674
|
+
for (const { form, name } of getAllRegisteredTools()) {
|
|
1675
|
+
if (excludeForm && form === excludeForm)
|
|
1676
|
+
continue;
|
|
1677
|
+
names.add(name);
|
|
1678
|
+
}
|
|
1679
|
+
return names;
|
|
1680
|
+
}
|
|
1681
|
+
function ensureUniqueToolName(baseName, excludeForm) {
|
|
1682
|
+
const used = getUsedToolNames(excludeForm);
|
|
1683
|
+
if (!used.has(baseName))
|
|
1684
|
+
return baseName;
|
|
1685
|
+
let i = 2;
|
|
1686
|
+
let candidate = withNumericSuffix(baseName, i);
|
|
1687
|
+
while (used.has(candidate)) {
|
|
1688
|
+
i++;
|
|
1689
|
+
candidate = withNumericSuffix(baseName, i);
|
|
1690
|
+
}
|
|
1691
|
+
return candidate;
|
|
1692
|
+
}
|
|
1655
1693
|
async function registerForm(form, config) {
|
|
1656
1694
|
if (isExcluded(form, config))
|
|
1657
1695
|
return;
|
|
1696
|
+
const previousName = getRegisteredToolName(form);
|
|
1658
1697
|
let override;
|
|
1659
1698
|
for (const [selector, ovr] of Object.entries(config.overrides)) {
|
|
1660
1699
|
try {
|
|
@@ -1666,6 +1705,11 @@ async function registerForm(form, config) {
|
|
|
1666
1705
|
}
|
|
1667
1706
|
}
|
|
1668
1707
|
const metadata = analyzeForm(form, override);
|
|
1708
|
+
const resolvedName = ensureUniqueToolName(metadata.name, form);
|
|
1709
|
+
if (resolvedName !== metadata.name && config.debug) {
|
|
1710
|
+
console.warn(`[auto-webmcp] tool name collision: "${metadata.name}" renamed to "${resolvedName}"`);
|
|
1711
|
+
}
|
|
1712
|
+
metadata.name = resolvedName;
|
|
1669
1713
|
if (config.debug) {
|
|
1670
1714
|
warnToolQuality(metadata.name, metadata.description);
|
|
1671
1715
|
}
|
|
@@ -1677,6 +1721,9 @@ async function registerForm(form, config) {
|
|
|
1677
1721
|
'[type="submit"], button[data-variant="primary"], button:not([type])'
|
|
1678
1722
|
) ?? null;
|
|
1679
1723
|
const pendingBtns = window["__pendingSubmitBtns"] ??= {};
|
|
1724
|
+
if (previousName && previousName !== metadata.name) {
|
|
1725
|
+
delete pendingBtns[previousName];
|
|
1726
|
+
}
|
|
1680
1727
|
pendingBtns[metadata.name] = formSubmitBtn;
|
|
1681
1728
|
if (config.debug) {
|
|
1682
1729
|
console.log(`[auto-webmcp] Registered: ${metadata.name}`, metadata);
|
|
@@ -1684,12 +1731,14 @@ async function registerForm(form, config) {
|
|
|
1684
1731
|
emit("form:registered", form, metadata.name);
|
|
1685
1732
|
}
|
|
1686
1733
|
async function unregisterForm(form, config) {
|
|
1687
|
-
const
|
|
1688
|
-
const name = getRegisteredToolName2(form);
|
|
1734
|
+
const name = getRegisteredToolName(form);
|
|
1689
1735
|
if (!name)
|
|
1690
1736
|
return;
|
|
1691
1737
|
await unregisterFormTool(form);
|
|
1692
1738
|
registeredForms.delete(form);
|
|
1739
|
+
const pendingBtns = window["__pendingSubmitBtns"];
|
|
1740
|
+
if (pendingBtns)
|
|
1741
|
+
delete pendingBtns[name];
|
|
1693
1742
|
if (config.debug) {
|
|
1694
1743
|
console.log(`[auto-webmcp] Unregistered: ${name}`);
|
|
1695
1744
|
}
|
|
@@ -1738,11 +1787,47 @@ function scheduleReAnalysis(form, config) {
|
|
|
1738
1787
|
}, RE_ANALYSIS_DEBOUNCE_MS)
|
|
1739
1788
|
);
|
|
1740
1789
|
}
|
|
1790
|
+
function scheduleFormReAnalysisById(formId, config) {
|
|
1791
|
+
const owner = document.getElementById(formId);
|
|
1792
|
+
if (owner instanceof HTMLFormElement && registeredForms.has(owner)) {
|
|
1793
|
+
scheduleReAnalysis(owner, config);
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
function resolveOwnerForm(el) {
|
|
1797
|
+
const closest = el.closest("form");
|
|
1798
|
+
if (closest instanceof HTMLFormElement)
|
|
1799
|
+
return closest;
|
|
1800
|
+
const explicitOwner = el.form;
|
|
1801
|
+
if (explicitOwner instanceof HTMLFormElement)
|
|
1802
|
+
return explicitOwner;
|
|
1803
|
+
const formId = el.getAttribute("form");
|
|
1804
|
+
if (formId) {
|
|
1805
|
+
const byId = document.getElementById(formId);
|
|
1806
|
+
if (byId instanceof HTMLFormElement)
|
|
1807
|
+
return byId;
|
|
1808
|
+
}
|
|
1809
|
+
return null;
|
|
1810
|
+
}
|
|
1741
1811
|
function startObserver(config) {
|
|
1742
1812
|
if (observer)
|
|
1743
1813
|
return;
|
|
1744
1814
|
observer = new MutationObserver((mutations) => {
|
|
1745
1815
|
for (const mutation of mutations) {
|
|
1816
|
+
if (mutation.type === "attributes" && mutation.target instanceof Element) {
|
|
1817
|
+
const target = mutation.target;
|
|
1818
|
+
const ownerForm = resolveOwnerForm(target);
|
|
1819
|
+
if (ownerForm && registeredForms.has(ownerForm)) {
|
|
1820
|
+
scheduleReAnalysis(ownerForm, config);
|
|
1821
|
+
} else if (target instanceof HTMLFormElement) {
|
|
1822
|
+
void registerForm(target, config);
|
|
1823
|
+
} else if (isInterestingNode(target) && !target.closest("form")) {
|
|
1824
|
+
scheduleOrphanRescan(config);
|
|
1825
|
+
}
|
|
1826
|
+
if (mutation.attributeName === "form" && mutation.oldValue) {
|
|
1827
|
+
scheduleFormReAnalysisById(mutation.oldValue, config);
|
|
1828
|
+
}
|
|
1829
|
+
continue;
|
|
1830
|
+
}
|
|
1746
1831
|
for (const node of mutation.addedNodes) {
|
|
1747
1832
|
if (!(node instanceof Element))
|
|
1748
1833
|
continue;
|
|
@@ -1771,7 +1856,12 @@ function startObserver(config) {
|
|
|
1771
1856
|
}
|
|
1772
1857
|
}
|
|
1773
1858
|
});
|
|
1774
|
-
observer.observe(document.body, {
|
|
1859
|
+
observer.observe(document.body, {
|
|
1860
|
+
childList: true,
|
|
1861
|
+
subtree: true,
|
|
1862
|
+
attributes: true,
|
|
1863
|
+
attributeOldValue: true
|
|
1864
|
+
});
|
|
1775
1865
|
}
|
|
1776
1866
|
function listenForRouteChanges(config) {
|
|
1777
1867
|
window.addEventListener("hashchange", () => scanForms(config));
|
|
@@ -1915,6 +2005,15 @@ async function scanOrphanInputs(config) {
|
|
|
1915
2005
|
}
|
|
1916
2006
|
console.log(`[auto-webmcp] orphan: submit button for group:`, submitBtn ? `"${submitBtn.textContent?.trim()}" disabled=${submitBtn.disabled}` : "none");
|
|
1917
2007
|
const metadata = analyzeOrphanInputGroup(container, inputs, submitBtn);
|
|
2008
|
+
if (registeredOrphanToolNames.has(metadata.name)) {
|
|
2009
|
+
console.log(`[auto-webmcp] orphan: "${metadata.name}" already registered, skipping`);
|
|
2010
|
+
continue;
|
|
2011
|
+
}
|
|
2012
|
+
const orphanName = ensureUniqueToolName(metadata.name);
|
|
2013
|
+
if (orphanName !== metadata.name && config.debug) {
|
|
2014
|
+
console.warn(`[auto-webmcp] orphan tool name collision: "${metadata.name}" renamed to "${orphanName}"`);
|
|
2015
|
+
}
|
|
2016
|
+
metadata.name = orphanName;
|
|
1918
2017
|
console.log(`[auto-webmcp] orphan: tool="${metadata.name}" schema keys:`, Object.keys(metadata.inputSchema.properties));
|
|
1919
2018
|
const inputPairs = [];
|
|
1920
2019
|
const schemaProps = metadata.inputSchema.properties;
|
|
@@ -1935,7 +2034,7 @@ async function scanOrphanInputs(config) {
|
|
|
1935
2034
|
continue;
|
|
1936
2035
|
}
|
|
1937
2036
|
const toolName = metadata.name;
|
|
1938
|
-
const execute = async (params) => {
|
|
2037
|
+
const execute = async (params, _client) => {
|
|
1939
2038
|
console.log(`[auto-webmcp] orphan execute: tool="${toolName}" params=`, params);
|
|
1940
2039
|
console.log(`[auto-webmcp] orphan execute: inputPairs=`, inputPairs.map((p) => p.key));
|
|
1941
2040
|
for (const { key, el } of inputPairs) {
|
|
@@ -2004,10 +2103,6 @@ async function scanOrphanInputs(config) {
|
|
|
2004
2103
|
return { content: [{ type: "text", text: "Fields filled and form submitted." }] };
|
|
2005
2104
|
};
|
|
2006
2105
|
try {
|
|
2007
|
-
if (registeredOrphanToolNames.has(metadata.name)) {
|
|
2008
|
-
console.log(`[auto-webmcp] orphan: "${metadata.name}" already registered, skipping`);
|
|
2009
|
-
continue;
|
|
2010
|
-
}
|
|
2011
2106
|
const toolDef = {
|
|
2012
2107
|
name: metadata.name,
|
|
2013
2108
|
description: metadata.description,
|
|
@@ -2058,7 +2153,6 @@ function stopDiscovery() {
|
|
|
2058
2153
|
}
|
|
2059
2154
|
|
|
2060
2155
|
// src/index.ts
|
|
2061
|
-
init_registry();
|
|
2062
2156
|
async function autoWebMCP(config) {
|
|
2063
2157
|
const resolved = resolveConfig(config);
|
|
2064
2158
|
if (resolved.debug) {
|