@crestapps/webmcp 2.0.0-preview.258 → 2.0.0-preview.259
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.
|
@@ -14,11 +14,12 @@
|
|
|
14
14
|
var MAX_TABLES = 10;
|
|
15
15
|
var MAX_TABLE_ROWS = 50;
|
|
16
16
|
var MAX_CELL_LENGTH = 300;
|
|
17
|
+
var MAX_FIELD_TEXT_LENGTH = 500;
|
|
17
18
|
var selectors = {
|
|
18
19
|
description: "meta[name=\"description\"]",
|
|
19
20
|
headings: "h1, h2",
|
|
20
21
|
main: "main, [role=\"main\"]",
|
|
21
|
-
ignored: "script, style, noscript, template, svg, nav, aside,
|
|
22
|
+
ignored: "script, style, noscript, template, svg, nav, aside, datalist, dialog:not([open]), [hidden], [aria-hidden=\"true\"], [role=\"navigation\"], [data-webmcp-ignore], [class*=\"ai-chat-widget\"]",
|
|
22
23
|
links: "nav a[href], header a[href], aside a[href], [role=\"navigation\"] a[href]",
|
|
23
24
|
fields: "form input, form select, form textarea"
|
|
24
25
|
};
|
|
@@ -71,6 +72,27 @@
|
|
|
71
72
|
if (node.hidden || typeof node.matches === "function" && node.matches(selectors.ignored)) return true;
|
|
72
73
|
return typeof node.checkVisibility === "function" && !node.checkVisibility();
|
|
73
74
|
}
|
|
75
|
+
var FIELD_TAGS = [
|
|
76
|
+
"INPUT",
|
|
77
|
+
"SELECT",
|
|
78
|
+
"TEXTAREA"
|
|
79
|
+
];
|
|
80
|
+
/**
|
|
81
|
+
* The text page_read_content shows for a form field, where the page shows it: its value, the chosen option of a
|
|
82
|
+
* list, or [x] / [ ] for a check box. Fields an agent never reads (hidden, password, file, buttons, disabled, the
|
|
83
|
+
* antiforgery token) and empty fields show nothing.
|
|
84
|
+
*/
|
|
85
|
+
function fieldText(field) {
|
|
86
|
+
if (!isListed(field)) return "";
|
|
87
|
+
var type = fieldType(field);
|
|
88
|
+
if (type === "checkbox" || type === "radio") return field.checked ? "[x]" : "[ ]";
|
|
89
|
+
var text = type === "select" ? Array.prototype.filter.call(field.options || [], function(option) {
|
|
90
|
+
return option.selected;
|
|
91
|
+
}).map(function(option) {
|
|
92
|
+
return cleanText(option.text);
|
|
93
|
+
}).join(", ") : cleanText(field.value);
|
|
94
|
+
return text.length > MAX_FIELD_TEXT_LENGTH ? text.slice(0, MAX_FIELD_TEXT_LENGTH) + "…" : text;
|
|
95
|
+
}
|
|
74
96
|
function cellText(cell) {
|
|
75
97
|
var text = cleanText(cell.textContent);
|
|
76
98
|
return text.length > MAX_CELL_LENGTH ? text.slice(0, MAX_CELL_LENGTH) + "…" : text;
|
|
@@ -112,6 +134,10 @@
|
|
|
112
134
|
}
|
|
113
135
|
if (node.nodeType !== 1 || isIgnored(node)) return;
|
|
114
136
|
var tag = (node.tagName || "").toUpperCase();
|
|
137
|
+
if (FIELD_TAGS.indexOf(tag) >= 0) {
|
|
138
|
+
line.push(fieldText(node));
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
115
141
|
if (tag === "TABLE") {
|
|
116
142
|
endLine();
|
|
117
143
|
tableCount++;
|
|
@@ -276,7 +302,7 @@
|
|
|
276
302
|
{
|
|
277
303
|
name: "page_read_content",
|
|
278
304
|
title: "Read page content",
|
|
279
|
-
description: "Reads what the page shows in its main area: its text, and its tables as headers and rows (for example the records a list page shows). Menus
|
|
305
|
+
description: "Reads what the page shows in its main area: its text, what its form fields hold (next to their labels), and its tables as headers and rows (for example the records a list page shows). Menus and hidden parts are left out. Use page_list_form_fields for the fields' names and choices before filling them, and page_list_links for the menus.",
|
|
280
306
|
inputSchema: {
|
|
281
307
|
type: "object",
|
|
282
308
|
properties: {}
|
|
@@ -357,7 +383,7 @@
|
|
|
357
383
|
{
|
|
358
384
|
name: "page_set_form_field",
|
|
359
385
|
title: "Set form field",
|
|
360
|
-
description: "Puts a value into one form field on the page, found by its name, id or label. It does not save or submit the form: the user reviews the change and saves it.",
|
|
386
|
+
description: "Puts a value into one form field on the page, found by its name, id or label. It does not save or submit the form: the user reviews the change and saves it. The result has the field's value before the change (previousValue), so the change can be undone by setting it back.",
|
|
361
387
|
inputSchema: {
|
|
362
388
|
type: "object",
|
|
363
389
|
properties: {
|
|
@@ -374,6 +400,7 @@
|
|
|
374
400
|
if (!field) throw new Error("No editable field on the page is named \"" + cleanText(input && input.field) + "\".");
|
|
375
401
|
var type = fieldType(field);
|
|
376
402
|
var value = input.value;
|
|
403
|
+
var previousValue = describeField(field).value;
|
|
377
404
|
if (type === "checkbox" || type === "radio") field.checked = value === true || String(value).toLowerCase() === "true";
|
|
378
405
|
else if (type === "select") {
|
|
379
406
|
var wanted = cleanText(value).toLowerCase();
|
|
@@ -385,7 +412,10 @@
|
|
|
385
412
|
} else field.value = value == null ? "" : String(value);
|
|
386
413
|
raise(field, "input");
|
|
387
414
|
raise(field, "change");
|
|
388
|
-
return {
|
|
415
|
+
return {
|
|
416
|
+
field: describeField(field),
|
|
417
|
+
previousValue
|
|
418
|
+
};
|
|
389
419
|
}
|
|
390
420
|
}
|
|
391
421
|
];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webmcp-page-tools.js","sources":["../../Assets/js/webmcp-page-tools.js"],"sourcesContent":["// @ts-check\n/*\n * WebMCP page tools.\n *\n * Tools that work on the page the user sees: what the page is, what it shows, where its menus lead, opening another\n * page of the same site, and reading and filling its forms. They never submit a form and never leave the site, so the user always\n * reviews and saves the changes an agent makes.\n *\n * The tools are registered with the standard `document.modelContext.registerTool`, so any WebMCP agent can use them:\n * the browser's own assistant, or the CrestApps AI chat. As WebMCP defines, each answers a JSON-serializable value and\n * reports a failure by rejecting. Load webmcp-polyfill.js first in browsers without native WebMCP.\n */\n\n(function () {\n 'use strict';\n\n var MAX_HEADINGS = 20;\n var MAX_LINKS = 200;\n var MAX_FIELDS = 200;\n var MAX_VALUE_LENGTH = 2000;\n var MAX_CONTENT_LENGTH = 8000;\n var MAX_TABLES = 10;\n var MAX_TABLE_ROWS = 50;\n var MAX_CELL_LENGTH = 300;\n\n // The selectors the tools read the page with.\n var selectors = {\n description: 'meta[name=\"description\"]',\n headings: 'h1, h2',\n main: 'main, [role=\"main\"]',\n // What page_read_content leaves out of the page's text: scripts, menus, the choices and text of form controls\n // (page_list_form_fields reads them), hidden parts, the chat itself, and anything a page marks with\n // data-webmcp-ignore. A form's other content is read: admin pages often wrap a whole list in one, for bulk actions.\n ignored: 'script, style, noscript, template, svg, nav, aside, select, textarea, datalist, dialog:not([open]), [hidden], [aria-hidden=\"true\"], [role=\"navigation\"], [data-webmcp-ignore], [class*=\"ai-chat-widget\"]',\n links: 'nav a[href], header a[href], aside a[href], [role=\"navigation\"] a[href]',\n fields: 'form input, form select, form textarea',\n };\n\n // Field types an agent never reads or fills.\n var SKIPPED_TYPES = ['hidden', 'password', 'file', 'submit', 'button', 'reset', 'image'];\n var SKIPPED_NAMES = ['__RequestVerificationToken'];\n\n function cleanText(value) {\n return (value == null ? '' : String(value)).replace(/\\s+/g, ' ').trim();\n }\n\n\n // An address that starts with a scheme, such as \"https:\" or \"javascript:\".\n var ABSOLUTE_ADDRESS = /^[A-Za-z][A-Za-z0-9+.-]*:/;\n\n // The tools register() registered, by ModelContext and name, so the CrestApps chat can run them through their own\n // execute and tell the model why one failed (executeTool reports every failure as \"Tool execution failed\").\n var registeredTools = new WeakMap();\n\n // Elements that start a new line in the text page_read_content reads.\n var BLOCK_TAGS = ['ADDRESS', 'ARTICLE', 'BLOCKQUOTE', 'BR', 'DD', 'DETAILS', 'DIV', 'DL', 'DT', 'FIELDSET', 'FIGCAPTION', 'FIGURE', 'FOOTER', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'HEADER', 'HR', 'LI', 'OL', 'P', 'PRE', 'SECTION', 'SUMMARY', 'UL'];\n\n function isIgnored(node) {\n if (node.hidden || (typeof node.matches === 'function' && node.matches(selectors.ignored))) {\n return true;\n }\n\n // Parts the page hides with CSS, such as closed menus and modals.\n return typeof node.checkVisibility === 'function' && !node.checkVisibility();\n }\n\n function cellText(cell) {\n var text = cleanText(cell.textContent);\n\n return text.length > MAX_CELL_LENGTH ? text.slice(0, MAX_CELL_LENGTH) + '…' : text;\n }\n\n function readTable(table) {\n var rows = Array.prototype.slice.call(table.rows || []);\n var headers = [];\n\n if (rows.length && Array.prototype.every.call(rows[0].cells || [], function (cell) { return (cell.tagName || '').toUpperCase() === 'TH'; })) {\n headers = Array.prototype.map.call(rows.shift().cells, cellText);\n }\n\n var caption = table.caption ? cleanText(table.caption.textContent) : '';\n\n return {\n caption: caption || null,\n headers: headers,\n rows: rows.slice(0, MAX_TABLE_ROWS).map(function (row) {\n return Array.prototype.map.call(row.cells || [], cellText);\n }),\n totalRows: rows.length,\n };\n }\n\n /**\n * Reads the text and the tables of the page's main region, the way a person reads it: menus, forms, hidden parts\n * and the chat are left out, and each table is read once, as rows, where the text says [table N].\n */\n function readContent(doc) {\n var root = doc.querySelector(selectors.main) || doc.body;\n var lines = [];\n var line = [];\n var tables = [];\n var tableCount = 0;\n\n function endLine() {\n var text = cleanText(line.join(' '));\n\n if (text) {\n lines.push(text);\n }\n\n line = [];\n }\n\n function walk(node) {\n if (node.nodeType === 3) {\n line.push(node.nodeValue);\n\n return;\n }\n\n if (node.nodeType !== 1 || isIgnored(node)) {\n return;\n }\n\n var tag = (node.tagName || '').toUpperCase();\n\n if (tag === 'TABLE') {\n endLine();\n tableCount++;\n\n if (tables.length < MAX_TABLES) {\n tables.push(readTable(node));\n lines.push('[table ' + tables.length + ']');\n }\n\n return;\n }\n\n var isBlock = BLOCK_TAGS.indexOf(tag) >= 0;\n\n if (isBlock) {\n endLine();\n }\n\n var children = node.childNodes || [];\n\n for (var i = 0; i < children.length; i++) {\n walk(children[i]);\n }\n\n if (isBlock) {\n endLine();\n }\n }\n\n if (root) {\n walk(root);\n endLine();\n }\n\n var text = lines.join('\\n');\n var truncated = text.length > MAX_CONTENT_LENGTH || tableCount > tables.length ||\n tables.some(function (table) { return table.totalRows > table.rows.length; });\n\n return {\n text: text.length > MAX_CONTENT_LENGTH ? text.slice(0, MAX_CONTENT_LENGTH) + '…' : text,\n tables: tables,\n truncated: truncated,\n };\n }\n\n function resolveSameOrigin(href, loc) {\n if (!href) {\n return null;\n }\n\n try {\n var url = new URL(href, loc.href);\n\n return url.origin === loc.origin ? url : null;\n } catch (error) {\n return null;\n }\n }\n\n function fieldType(field) {\n var tag = (field.tagName || '').toLowerCase();\n\n if (tag === 'select' || tag === 'textarea') {\n return tag;\n }\n\n return (field.type || 'text').toLowerCase();\n }\n\n function fieldLabel(field) {\n if (field.labels && field.labels.length > 0) {\n var label = cleanText(field.labels[0].textContent);\n\n if (label) {\n return label;\n }\n }\n\n return cleanText(field.getAttribute('aria-label')) || cleanText(field.getAttribute('placeholder'));\n }\n\n function isListed(field) {\n return !field.disabled &&\n SKIPPED_TYPES.indexOf(fieldType(field)) < 0 &&\n SKIPPED_NAMES.indexOf(field.name) < 0;\n }\n\n function listFields(doc) {\n var fields = [];\n var all = doc.querySelectorAll(selectors.fields);\n\n for (var i = 0; i < all.length && fields.length < MAX_FIELDS; i++) {\n if (isListed(all[i])) {\n fields.push(all[i]);\n }\n }\n\n return fields;\n }\n\n function describeField(field) {\n var type = fieldType(field);\n var description = {\n key: field.name || field.id,\n name: field.name,\n id: field.id,\n label: fieldLabel(field),\n type: type,\n value: type === 'checkbox' || type === 'radio'\n ? !!field.checked\n : String(field.value == null ? '' : field.value).substring(0, MAX_VALUE_LENGTH),\n required: !!field.required,\n };\n\n if (type === 'select') {\n description.options = Array.prototype.map.call(field.options || [], function (option) {\n return { value: option.value, text: cleanText(option.text), selected: !!option.selected };\n });\n }\n\n return description;\n }\n\n function findField(doc, key) {\n var wanted = cleanText(key).toLowerCase();\n\n if (!wanted) {\n return null;\n }\n\n var fields = listFields(doc);\n var matchers = [\n function (field) {\n return (field.name || '').toLowerCase() === wanted;\n },\n function (field) {\n return (field.id || '').toLowerCase() === wanted;\n },\n function (field) {\n return fieldLabel(field).toLowerCase() === wanted;\n },\n ];\n\n for (var m = 0; m < matchers.length; m++) {\n for (var i = 0; i < fields.length; i++) {\n if (matchers[m](fields[i])) {\n return fields[i];\n }\n }\n }\n\n return null;\n }\n\n /**\n * Asks the site whether a page exists, with a HEAD request that downloads nothing. Only a 404 or 410 answer means it\n * does not: a page that refuses HEAD (405), sends to the login page or cannot be reached is opened as asked.\n *\n * @param {Function | undefined} fetchImpl The fetch function, or nothing to skip the check.\n * @param {string} href The address.\n */\n async function pageIsMissing(fetchImpl, href) {\n if (typeof fetchImpl !== 'function') {\n return false;\n }\n\n try {\n var response = await fetchImpl(href, { method: 'HEAD', credentials: 'same-origin', redirect: 'follow' });\n\n return response.status === 404 || response.status === 410;\n } catch (error) {\n return false;\n }\n }\n\n function raise(field, type) {\n field.dispatchEvent(new Event(type, { bubbles: true }));\n }\n\n /**\n * Creates the page tools for a document.\n *\n * @param {{ doc: any, loc: any, isAdmin?: boolean, fetch?: Function, navigateDelayMs?: number }} options The document,\n * its location, whether it is an admin page, the fetch function page_navigate checks that a page exists with (no\n * check without it), and how long page_navigate waits after answering before it opens the page.\n */\n function createPageTools(options) {\n var doc = options.doc;\n var loc = options.loc;\n var navigateDelayMs = typeof options.navigateDelayMs === 'number' ? options.navigateDelayMs : 400;\n\n return [\n {\n name: 'page_get_info',\n title: 'Get page info',\n description: 'Describes the page the user has open: its title, address, description and main headings, and whether it is an admin page. To read what the page shows, use page_read_content.',\n inputSchema: { type: 'object', properties: {} },\n annotations: { readOnlyHint: true },\n execute: function () {\n var meta = doc.querySelector(selectors.description);\n var headings = [];\n var all = doc.querySelectorAll(selectors.headings);\n\n for (var i = 0; i < all.length && headings.length < MAX_HEADINGS; i++) {\n var text = cleanText(all[i].textContent);\n\n if (text) {\n headings.push({ level: (all[i].tagName || '').toUpperCase() === 'H1' ? 1 : 2, text: text });\n }\n }\n\n return ({\n title: doc.title,\n url: loc.href,\n description: meta ? meta.getAttribute('content') : null,\n headings: headings,\n isAdmin: !!options.isAdmin,\n });\n },\n },\n {\n name: 'page_read_content',\n title: 'Read page content',\n description: 'Reads what the page shows in its main area: its text, and its tables as headers and rows (for example the records a list page shows). Menus, forms and hidden parts are left out; use page_list_links and page_list_form_fields for those.',\n inputSchema: { type: 'object', properties: {} },\n annotations: { readOnlyHint: true },\n execute: function () {\n return readContent(doc);\n },\n },\n {\n name: 'page_list_links',\n title: 'List page links',\n description: 'Lists the links in the page\\'s menus and navigation that stay on this site, with their text. Use it to find the page to open for a task.',\n inputSchema: {\n type: 'object',\n properties: {\n filter: { type: 'string', description: 'Only list links whose text contains these words.' },\n },\n },\n annotations: { readOnlyHint: true },\n execute: function (input) {\n var filter = cleanText(input && input.filter).toLowerCase();\n var seen = new Set();\n var links = [];\n var all = doc.querySelectorAll(selectors.links);\n\n for (var i = 0; i < all.length && links.length < MAX_LINKS; i++) {\n var url = resolveSameOrigin(all[i].getAttribute('href'), loc);\n var text = cleanText(all[i].textContent);\n\n if (!url || !text || seen.has(url.href) || (filter && text.toLowerCase().indexOf(filter) < 0)) {\n continue;\n }\n\n seen.add(url.href);\n links.push({ text: text, url: url.href });\n }\n\n return ({ links: links });\n },\n },\n {\n name: 'page_navigate',\n title: 'Open page',\n description: 'Opens another page of this site in the current tab. Only addresses on this site are allowed; take them from page_list_links, and start a site path with /. Unsaved changes on the current page are lost.',\n inputSchema: {\n type: 'object',\n properties: {\n url: { type: 'string', description: 'The address to open: a path that starts with \"/\", or a full address on this site, as page_list_links lists them.' },\n },\n required: ['url'],\n },\n execute: async function (input) {\n var address = cleanText(input && input.url);\n\n if (!address) {\n throw new Error('Give the address of the page to open. Call page_list_links to find it.');\n }\n\n // A bare relative path, such as \"ArticlesAdmin\", resolves against the current page and usually names a page\n // that does not exist: it is a guess, not an address the site gave.\n if (address.charAt(0) !== '/' && !ABSOLUTE_ADDRESS.test(address)) {\n throw new Error('\"' + address + '\" is not an address on this site. Give a path that starts with \"/\" or a full address on this site, as page_list_links lists them. Call page_list_links to find the page to open.');\n }\n\n var url = resolveSameOrigin(address, loc);\n\n if (!url) {\n throw new Error('Only addresses on this site can be opened. Call page_list_links to find the page to open.');\n }\n\n if (await pageIsMissing(options.fetch, url.href)) {\n throw new Error('The site has no page at \"' + url.pathname + url.search + '\" (404). Do not guess addresses: call page_list_links to find the page to open.');\n }\n\n // Answer first and open the page a moment later, so the agent gets the result before this page (and\n // the chat connection on it) goes away. The chat finishes the reply on the server and shows it on the\n // next page.\n setTimeout(function () {\n loc.assign(url.href);\n }, navigateDelayMs);\n\n return ({ navigatedTo: url.href });\n },\n },\n {\n name: 'page_list_form_fields',\n title: 'List form fields',\n description: 'Lists the fields of the forms on the page: name, label, type, current value, whether it is required and, for lists, the choices. Hidden, password and file fields are not listed.',\n inputSchema: { type: 'object', properties: {} },\n annotations: { readOnlyHint: true },\n execute: function () {\n return ({ fields: listFields(doc).map(describeField) });\n },\n },\n {\n name: 'page_set_form_field',\n title: 'Set form field',\n description: 'Puts a value into one form field on the page, found by its name, id or label. It does not save or submit the form: the user reviews the change and saves it.',\n inputSchema: {\n type: 'object',\n properties: {\n field: { type: 'string', description: 'The field\\'s name, id or label, as page_list_form_fields lists it.' },\n value: { description: 'The value. For a check box, true or false. For a list, a choice\\'s value or text.' },\n },\n required: ['field', 'value'],\n },\n execute: function (input) {\n var field = findField(doc, input && input.field);\n\n if (!field) {\n throw new Error('No editable field on the page is named \"' + cleanText(input && input.field) + '\".');\n }\n\n var type = fieldType(field);\n var value = input.value;\n\n if (type === 'checkbox' || type === 'radio') {\n field.checked = value === true || String(value).toLowerCase() === 'true';\n } else if (type === 'select') {\n var wanted = cleanText(value).toLowerCase();\n var option = Array.prototype.find.call(field.options || [], function (candidate) {\n return String(candidate.value).toLowerCase() === wanted || cleanText(candidate.text).toLowerCase() === wanted;\n });\n\n if (!option) {\n throw new Error('The list \"' + fieldLabel(field) + '\" has no choice \"' + cleanText(value) + '\".');\n }\n\n field.value = option.value;\n } else {\n field.value = value == null ? '' : String(value);\n }\n\n raise(field, 'input');\n raise(field, 'change');\n\n return ({ field: describeField(field) });\n },\n },\n ];\n }\n\n /**\n * Registers the page tools with the page's ModelContext.\n *\n * @param {any} modelContext The page's ModelContext (document.modelContext).\n * @param {{ doc: any, loc: any, isAdmin?: boolean, fetch?: Function }} options The document, its location, whether it\n * is an admin page, and the fetch function page_navigate checks that a page exists with (no check without it).\n * @param {AbortSignal} [signal] Unregisters the tools when aborted.\n */\n function register(modelContext, options, signal) {\n var tools = registeredTools.get(modelContext);\n\n if (!tools) {\n tools = new Map();\n registeredTools.set(modelContext, tools);\n }\n\n return Promise.all(createPageTools(options).map(function (tool) {\n return modelContext.registerTool(tool, signal ? { signal: signal } : {}).then(function () {\n if (signal && signal.aborted) {\n return;\n }\n\n tools.set(tool.name, tool);\n\n if (signal) {\n signal.addEventListener('abort', function () {\n if (tools.get(tool.name) === tool) {\n tools.delete(tool.name);\n }\n }, { once: true });\n }\n }, function (error) {\n console.warn('The page tool \"' + tool.name + '\" could not be registered.', error);\n });\n }));\n }\n\n /**\n * Answers the page tool register() registered with a ModelContext under a name, with its execute callback, or null\n * when it registered none: another script may have registered a tool of that name first.\n *\n * @param {any} modelContext The ModelContext the tool was registered with.\n * @param {string} name The tool's name.\n */\n function getRegisteredTool(modelContext, name) {\n var tools = modelContext ? registeredTools.get(modelContext) : null;\n\n return (tools && tools.get(name)) || null;\n }\n\n window.CrestAppsWebMcpPageTools = {\n selectors: selectors,\n createPageTools: createPageTools,\n register: register,\n getRegisteredTool: getRegisteredTool,\n };\n\n if (window.document && window.document.modelContext) {\n var config = window.crestAppsWebMcp && window.crestAppsWebMcp.config;\n\n register(window.document.modelContext, {\n doc: window.document,\n loc: window.location,\n fetch: typeof window.fetch === 'function' ? window.fetch.bind(window) : undefined,\n isAdmin: !!(config && config.isAdmin),\n });\n }\n})();\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"webmcp-page-tools.js","sources":["../../Assets/js/webmcp-page-tools.js"],"sourcesContent":["// @ts-check\n/*\n * WebMCP page tools.\n *\n * Tools that work on the page the user sees: what the page is, what it shows, where its menus lead, opening another\n * page of the same site, and reading and filling its forms. They never submit a form and never leave the site, so the user always\n * reviews and saves the changes an agent makes.\n *\n * The tools are registered with the standard `document.modelContext.registerTool`, so any WebMCP agent can use them:\n * the browser's own assistant, or the CrestApps AI chat. As WebMCP defines, each answers a JSON-serializable value and\n * reports a failure by rejecting. Load webmcp-polyfill.js first in browsers without native WebMCP.\n */\n\n(function () {\n 'use strict';\n\n var MAX_HEADINGS = 20;\n var MAX_LINKS = 200;\n var MAX_FIELDS = 200;\n var MAX_VALUE_LENGTH = 2000;\n var MAX_CONTENT_LENGTH = 8000;\n var MAX_TABLES = 10;\n var MAX_TABLE_ROWS = 50;\n var MAX_CELL_LENGTH = 300;\n var MAX_FIELD_TEXT_LENGTH = 500;\n\n // The selectors the tools read the page with.\n var selectors = {\n description: 'meta[name=\"description\"]',\n headings: 'h1, h2',\n main: 'main, [role=\"main\"]',\n // What page_read_content leaves out of the page's text: scripts, menus, hidden parts, the chat itself, and anything\n // a page marks with data-webmcp-ignore. A form is read, with what its fields hold: admin pages often wrap a whole\n // list in one, for bulk actions, and an editor's values are what the user sees.\n ignored: 'script, style, noscript, template, svg, nav, aside, datalist, dialog:not([open]), [hidden], [aria-hidden=\"true\"], [role=\"navigation\"], [data-webmcp-ignore], [class*=\"ai-chat-widget\"]',\n links: 'nav a[href], header a[href], aside a[href], [role=\"navigation\"] a[href]',\n fields: 'form input, form select, form textarea',\n };\n\n // Field types an agent never reads or fills.\n var SKIPPED_TYPES = ['hidden', 'password', 'file', 'submit', 'button', 'reset', 'image'];\n var SKIPPED_NAMES = ['__RequestVerificationToken'];\n\n function cleanText(value) {\n return (value == null ? '' : String(value)).replace(/\\s+/g, ' ').trim();\n }\n\n\n // An address that starts with a scheme, such as \"https:\" or \"javascript:\".\n var ABSOLUTE_ADDRESS = /^[A-Za-z][A-Za-z0-9+.-]*:/;\n\n // The tools register() registered, by ModelContext and name, so the CrestApps chat can run them through their own\n // execute and tell the model why one failed (executeTool reports every failure as \"Tool execution failed\").\n var registeredTools = new WeakMap();\n\n // Elements that start a new line in the text page_read_content reads.\n var BLOCK_TAGS = ['ADDRESS', 'ARTICLE', 'BLOCKQUOTE', 'BR', 'DD', 'DETAILS', 'DIV', 'DL', 'DT', 'FIELDSET', 'FIGCAPTION', 'FIGURE', 'FOOTER', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'HEADER', 'HR', 'LI', 'OL', 'P', 'PRE', 'SECTION', 'SUMMARY', 'UL'];\n\n function isIgnored(node) {\n if (node.hidden || (typeof node.matches === 'function' && node.matches(selectors.ignored))) {\n return true;\n }\n\n // Parts the page hides with CSS, such as closed menus and modals.\n return typeof node.checkVisibility === 'function' && !node.checkVisibility();\n }\n\n var FIELD_TAGS = ['INPUT', 'SELECT', 'TEXTAREA'];\n\n /**\n * The text page_read_content shows for a form field, where the page shows it: its value, the chosen option of a\n * list, or [x] / [ ] for a check box. Fields an agent never reads (hidden, password, file, buttons, disabled, the\n * antiforgery token) and empty fields show nothing.\n */\n function fieldText(field) {\n if (!isListed(field)) {\n return '';\n }\n\n var type = fieldType(field);\n\n if (type === 'checkbox' || type === 'radio') {\n return field.checked ? '[x]' : '[ ]';\n }\n\n var text = type === 'select'\n ? Array.prototype.filter.call(field.options || [], function (option) { return option.selected; })\n .map(function (option) { return cleanText(option.text); })\n .join(', ')\n : cleanText(field.value);\n\n return text.length > MAX_FIELD_TEXT_LENGTH ? text.slice(0, MAX_FIELD_TEXT_LENGTH) + '…' : text;\n }\n\n function cellText(cell) {\n var text = cleanText(cell.textContent);\n\n return text.length > MAX_CELL_LENGTH ? text.slice(0, MAX_CELL_LENGTH) + '…' : text;\n }\n\n function readTable(table) {\n var rows = Array.prototype.slice.call(table.rows || []);\n var headers = [];\n\n if (rows.length && Array.prototype.every.call(rows[0].cells || [], function (cell) { return (cell.tagName || '').toUpperCase() === 'TH'; })) {\n headers = Array.prototype.map.call(rows.shift().cells, cellText);\n }\n\n var caption = table.caption ? cleanText(table.caption.textContent) : '';\n\n return {\n caption: caption || null,\n headers: headers,\n rows: rows.slice(0, MAX_TABLE_ROWS).map(function (row) {\n return Array.prototype.map.call(row.cells || [], cellText);\n }),\n totalRows: rows.length,\n };\n }\n\n /**\n * Reads the text and the tables of the page's main region, the way a person reads it: menus, forms, hidden parts\n * and the chat are left out, and each table is read once, as rows, where the text says [table N].\n */\n function readContent(doc) {\n var root = doc.querySelector(selectors.main) || doc.body;\n var lines = [];\n var line = [];\n var tables = [];\n var tableCount = 0;\n\n function endLine() {\n var text = cleanText(line.join(' '));\n\n if (text) {\n lines.push(text);\n }\n\n line = [];\n }\n\n function walk(node) {\n if (node.nodeType === 3) {\n line.push(node.nodeValue);\n\n return;\n }\n\n if (node.nodeType !== 1 || isIgnored(node)) {\n return;\n }\n\n var tag = (node.tagName || '').toUpperCase();\n\n if (FIELD_TAGS.indexOf(tag) >= 0) {\n line.push(fieldText(node));\n\n return;\n }\n\n if (tag === 'TABLE') {\n endLine();\n tableCount++;\n\n if (tables.length < MAX_TABLES) {\n tables.push(readTable(node));\n lines.push('[table ' + tables.length + ']');\n }\n\n return;\n }\n\n var isBlock = BLOCK_TAGS.indexOf(tag) >= 0;\n\n if (isBlock) {\n endLine();\n }\n\n var children = node.childNodes || [];\n\n for (var i = 0; i < children.length; i++) {\n walk(children[i]);\n }\n\n if (isBlock) {\n endLine();\n }\n }\n\n if (root) {\n walk(root);\n endLine();\n }\n\n var text = lines.join('\\n');\n var truncated = text.length > MAX_CONTENT_LENGTH || tableCount > tables.length ||\n tables.some(function (table) { return table.totalRows > table.rows.length; });\n\n return {\n text: text.length > MAX_CONTENT_LENGTH ? text.slice(0, MAX_CONTENT_LENGTH) + '…' : text,\n tables: tables,\n truncated: truncated,\n };\n }\n\n function resolveSameOrigin(href, loc) {\n if (!href) {\n return null;\n }\n\n try {\n var url = new URL(href, loc.href);\n\n return url.origin === loc.origin ? url : null;\n } catch (error) {\n return null;\n }\n }\n\n function fieldType(field) {\n var tag = (field.tagName || '').toLowerCase();\n\n if (tag === 'select' || tag === 'textarea') {\n return tag;\n }\n\n return (field.type || 'text').toLowerCase();\n }\n\n function fieldLabel(field) {\n if (field.labels && field.labels.length > 0) {\n var label = cleanText(field.labels[0].textContent);\n\n if (label) {\n return label;\n }\n }\n\n return cleanText(field.getAttribute('aria-label')) || cleanText(field.getAttribute('placeholder'));\n }\n\n function isListed(field) {\n return !field.disabled &&\n SKIPPED_TYPES.indexOf(fieldType(field)) < 0 &&\n SKIPPED_NAMES.indexOf(field.name) < 0;\n }\n\n function listFields(doc) {\n var fields = [];\n var all = doc.querySelectorAll(selectors.fields);\n\n for (var i = 0; i < all.length && fields.length < MAX_FIELDS; i++) {\n if (isListed(all[i])) {\n fields.push(all[i]);\n }\n }\n\n return fields;\n }\n\n function describeField(field) {\n var type = fieldType(field);\n var description = {\n key: field.name || field.id,\n name: field.name,\n id: field.id,\n label: fieldLabel(field),\n type: type,\n value: type === 'checkbox' || type === 'radio'\n ? !!field.checked\n : String(field.value == null ? '' : field.value).substring(0, MAX_VALUE_LENGTH),\n required: !!field.required,\n };\n\n if (type === 'select') {\n description.options = Array.prototype.map.call(field.options || [], function (option) {\n return { value: option.value, text: cleanText(option.text), selected: !!option.selected };\n });\n }\n\n return description;\n }\n\n function findField(doc, key) {\n var wanted = cleanText(key).toLowerCase();\n\n if (!wanted) {\n return null;\n }\n\n var fields = listFields(doc);\n var matchers = [\n function (field) {\n return (field.name || '').toLowerCase() === wanted;\n },\n function (field) {\n return (field.id || '').toLowerCase() === wanted;\n },\n function (field) {\n return fieldLabel(field).toLowerCase() === wanted;\n },\n ];\n\n for (var m = 0; m < matchers.length; m++) {\n for (var i = 0; i < fields.length; i++) {\n if (matchers[m](fields[i])) {\n return fields[i];\n }\n }\n }\n\n return null;\n }\n\n /**\n * Asks the site whether a page exists, with a HEAD request that downloads nothing. Only a 404 or 410 answer means it\n * does not: a page that refuses HEAD (405), sends to the login page or cannot be reached is opened as asked.\n *\n * @param {Function | undefined} fetchImpl The fetch function, or nothing to skip the check.\n * @param {string} href The address.\n */\n async function pageIsMissing(fetchImpl, href) {\n if (typeof fetchImpl !== 'function') {\n return false;\n }\n\n try {\n var response = await fetchImpl(href, { method: 'HEAD', credentials: 'same-origin', redirect: 'follow' });\n\n return response.status === 404 || response.status === 410;\n } catch (error) {\n return false;\n }\n }\n\n function raise(field, type) {\n field.dispatchEvent(new Event(type, { bubbles: true }));\n }\n\n /**\n * Creates the page tools for a document.\n *\n * @param {{ doc: any, loc: any, isAdmin?: boolean, fetch?: Function, navigateDelayMs?: number }} options The document,\n * its location, whether it is an admin page, the fetch function page_navigate checks that a page exists with (no\n * check without it), and how long page_navigate waits after answering before it opens the page.\n */\n function createPageTools(options) {\n var doc = options.doc;\n var loc = options.loc;\n var navigateDelayMs = typeof options.navigateDelayMs === 'number' ? options.navigateDelayMs : 400;\n\n return [\n {\n name: 'page_get_info',\n title: 'Get page info',\n description: 'Describes the page the user has open: its title, address, description and main headings, and whether it is an admin page. To read what the page shows, use page_read_content.',\n inputSchema: { type: 'object', properties: {} },\n annotations: { readOnlyHint: true },\n execute: function () {\n var meta = doc.querySelector(selectors.description);\n var headings = [];\n var all = doc.querySelectorAll(selectors.headings);\n\n for (var i = 0; i < all.length && headings.length < MAX_HEADINGS; i++) {\n var text = cleanText(all[i].textContent);\n\n if (text) {\n headings.push({ level: (all[i].tagName || '').toUpperCase() === 'H1' ? 1 : 2, text: text });\n }\n }\n\n return ({\n title: doc.title,\n url: loc.href,\n description: meta ? meta.getAttribute('content') : null,\n headings: headings,\n isAdmin: !!options.isAdmin,\n });\n },\n },\n {\n name: 'page_read_content',\n title: 'Read page content',\n description: 'Reads what the page shows in its main area: its text, what its form fields hold (next to their labels), and its tables as headers and rows (for example the records a list page shows). Menus and hidden parts are left out. Use page_list_form_fields for the fields\\' names and choices before filling them, and page_list_links for the menus.',\n inputSchema: { type: 'object', properties: {} },\n annotations: { readOnlyHint: true },\n execute: function () {\n return readContent(doc);\n },\n },\n {\n name: 'page_list_links',\n title: 'List page links',\n description: 'Lists the links in the page\\'s menus and navigation that stay on this site, with their text. Use it to find the page to open for a task.',\n inputSchema: {\n type: 'object',\n properties: {\n filter: { type: 'string', description: 'Only list links whose text contains these words.' },\n },\n },\n annotations: { readOnlyHint: true },\n execute: function (input) {\n var filter = cleanText(input && input.filter).toLowerCase();\n var seen = new Set();\n var links = [];\n var all = doc.querySelectorAll(selectors.links);\n\n for (var i = 0; i < all.length && links.length < MAX_LINKS; i++) {\n var url = resolveSameOrigin(all[i].getAttribute('href'), loc);\n var text = cleanText(all[i].textContent);\n\n if (!url || !text || seen.has(url.href) || (filter && text.toLowerCase().indexOf(filter) < 0)) {\n continue;\n }\n\n seen.add(url.href);\n links.push({ text: text, url: url.href });\n }\n\n return ({ links: links });\n },\n },\n {\n name: 'page_navigate',\n title: 'Open page',\n description: 'Opens another page of this site in the current tab. Only addresses on this site are allowed; take them from page_list_links, and start a site path with /. Unsaved changes on the current page are lost.',\n inputSchema: {\n type: 'object',\n properties: {\n url: { type: 'string', description: 'The address to open: a path that starts with \"/\", or a full address on this site, as page_list_links lists them.' },\n },\n required: ['url'],\n },\n execute: async function (input) {\n var address = cleanText(input && input.url);\n\n if (!address) {\n throw new Error('Give the address of the page to open. Call page_list_links to find it.');\n }\n\n // A bare relative path, such as \"ArticlesAdmin\", resolves against the current page and usually names a page\n // that does not exist: it is a guess, not an address the site gave.\n if (address.charAt(0) !== '/' && !ABSOLUTE_ADDRESS.test(address)) {\n throw new Error('\"' + address + '\" is not an address on this site. Give a path that starts with \"/\" or a full address on this site, as page_list_links lists them. Call page_list_links to find the page to open.');\n }\n\n var url = resolveSameOrigin(address, loc);\n\n if (!url) {\n throw new Error('Only addresses on this site can be opened. Call page_list_links to find the page to open.');\n }\n\n if (await pageIsMissing(options.fetch, url.href)) {\n throw new Error('The site has no page at \"' + url.pathname + url.search + '\" (404). Do not guess addresses: call page_list_links to find the page to open.');\n }\n\n // Answer first and open the page a moment later, so the agent gets the result before this page (and\n // the chat connection on it) goes away. The chat finishes the reply on the server and shows it on the\n // next page.\n setTimeout(function () {\n loc.assign(url.href);\n }, navigateDelayMs);\n\n return ({ navigatedTo: url.href });\n },\n },\n {\n name: 'page_list_form_fields',\n title: 'List form fields',\n description: 'Lists the fields of the forms on the page: name, label, type, current value, whether it is required and, for lists, the choices. Hidden, password and file fields are not listed.',\n inputSchema: { type: 'object', properties: {} },\n annotations: { readOnlyHint: true },\n execute: function () {\n return ({ fields: listFields(doc).map(describeField) });\n },\n },\n {\n name: 'page_set_form_field',\n title: 'Set form field',\n description: 'Puts a value into one form field on the page, found by its name, id or label. It does not save or submit the form: the user reviews the change and saves it. The result has the field\\'s value before the change (previousValue), so the change can be undone by setting it back.',\n inputSchema: {\n type: 'object',\n properties: {\n field: { type: 'string', description: 'The field\\'s name, id or label, as page_list_form_fields lists it.' },\n value: { description: 'The value. For a check box, true or false. For a list, a choice\\'s value or text.' },\n },\n required: ['field', 'value'],\n },\n execute: function (input) {\n var field = findField(doc, input && input.field);\n\n if (!field) {\n throw new Error('No editable field on the page is named \"' + cleanText(input && input.field) + '\".');\n }\n\n var type = fieldType(field);\n var value = input.value;\n var previousValue = describeField(field).value;\n\n if (type === 'checkbox' || type === 'radio') {\n field.checked = value === true || String(value).toLowerCase() === 'true';\n } else if (type === 'select') {\n var wanted = cleanText(value).toLowerCase();\n var option = Array.prototype.find.call(field.options || [], function (candidate) {\n return String(candidate.value).toLowerCase() === wanted || cleanText(candidate.text).toLowerCase() === wanted;\n });\n\n if (!option) {\n throw new Error('The list \"' + fieldLabel(field) + '\" has no choice \"' + cleanText(value) + '\".');\n }\n\n field.value = option.value;\n } else {\n field.value = value == null ? '' : String(value);\n }\n\n raise(field, 'input');\n raise(field, 'change');\n\n return ({ field: describeField(field), previousValue: previousValue });\n },\n },\n ];\n }\n\n /**\n * Registers the page tools with the page's ModelContext.\n *\n * @param {any} modelContext The page's ModelContext (document.modelContext).\n * @param {{ doc: any, loc: any, isAdmin?: boolean, fetch?: Function }} options The document, its location, whether it\n * is an admin page, and the fetch function page_navigate checks that a page exists with (no check without it).\n * @param {AbortSignal} [signal] Unregisters the tools when aborted.\n */\n function register(modelContext, options, signal) {\n var tools = registeredTools.get(modelContext);\n\n if (!tools) {\n tools = new Map();\n registeredTools.set(modelContext, tools);\n }\n\n return Promise.all(createPageTools(options).map(function (tool) {\n return modelContext.registerTool(tool, signal ? { signal: signal } : {}).then(function () {\n if (signal && signal.aborted) {\n return;\n }\n\n tools.set(tool.name, tool);\n\n if (signal) {\n signal.addEventListener('abort', function () {\n if (tools.get(tool.name) === tool) {\n tools.delete(tool.name);\n }\n }, { once: true });\n }\n }, function (error) {\n console.warn('The page tool \"' + tool.name + '\" could not be registered.', error);\n });\n }));\n }\n\n /**\n * Answers the page tool register() registered with a ModelContext under a name, with its execute callback, or null\n * when it registered none: another script may have registered a tool of that name first.\n *\n * @param {any} modelContext The ModelContext the tool was registered with.\n * @param {string} name The tool's name.\n */\n function getRegisteredTool(modelContext, name) {\n var tools = modelContext ? registeredTools.get(modelContext) : null;\n\n return (tools && tools.get(name)) || null;\n }\n\n window.CrestAppsWebMcpPageTools = {\n selectors: selectors,\n createPageTools: createPageTools,\n register: register,\n getRegisteredTool: getRegisteredTool,\n };\n\n if (window.document && window.document.modelContext) {\n var config = window.crestAppsWebMcp && window.crestAppsWebMcp.config;\n\n register(window.document.modelContext, {\n doc: window.document,\n loc: window.location,\n fetch: typeof window.fetch === 'function' ? window.fetch.bind(window) : undefined,\n isAdmin: !!(config && config.isAdmin),\n });\n }\n})();\n"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
(function(){(function(){"use strict";var e=8e3,t={description:`meta[name="description"]`,headings:`h1, h2`,main:`main, [role="main"]`,ignored:`script, style, noscript, template, svg, nav, aside,
|
|
2
|
-
`),
|
|
1
|
+
(function(){(function(){"use strict";var e=8e3,t={description:`meta[name="description"]`,headings:`h1, h2`,main:`main, [role="main"]`,ignored:`script, style, noscript, template, svg, nav, aside, datalist, dialog:not([open]), [hidden], [aria-hidden="true"], [role="navigation"], [data-webmcp-ignore], [class*="ai-chat-widget"]`,links:`nav a[href], header a[href], aside a[href], [role="navigation"] a[href]`,fields:`form input, form select, form textarea`},n=[`hidden`,`password`,`file`,`submit`,`button`,`reset`,`image`],r=[`__RequestVerificationToken`];function i(e){return(e==null?``:String(e)).replace(/\s+/g,` `).trim()}var a=/^[A-Za-z][A-Za-z0-9+.-]*:/,o=new WeakMap,s=`ADDRESS.ARTICLE.BLOCKQUOTE.BR.DD.DETAILS.DIV.DL.DT.FIELDSET.FIGCAPTION.FIGURE.FOOTER.H1.H2.H3.H4.H5.H6.HEADER.HR.LI.OL.P.PRE.SECTION.SUMMARY.UL`.split(`.`);function c(e){return e.hidden||typeof e.matches==`function`&&e.matches(t.ignored)?!0:typeof e.checkVisibility==`function`&&!e.checkVisibility()}var l=[`INPUT`,`SELECT`,`TEXTAREA`];function u(e){if(!_(e))return``;var t=h(e);if(t===`checkbox`||t===`radio`)return e.checked?`[x]`:`[ ]`;var n=t===`select`?Array.prototype.filter.call(e.options||[],function(e){return e.selected}).map(function(e){return i(e.text)}).join(`, `):i(e.value);return n.length>500?n.slice(0,500)+`…`:n}function d(e){var t=i(e.textContent);return t.length>300?t.slice(0,300)+`…`:t}function f(e){var t=Array.prototype.slice.call(e.rows||[]),n=[];return t.length&&Array.prototype.every.call(t[0].cells||[],function(e){return(e.tagName||``).toUpperCase()===`TH`})&&(n=Array.prototype.map.call(t.shift().cells,d)),{caption:(e.caption?i(e.caption.textContent):``)||null,headers:n,rows:t.slice(0,50).map(function(e){return Array.prototype.map.call(e.cells||[],d)}),totalRows:t.length}}function p(n){var r=n.querySelector(t.main)||n.body,a=[],o=[],d=[],p=0;function m(){var e=i(o.join(` `));e&&a.push(e),o=[]}function h(e){if(e.nodeType===3)o.push(e.nodeValue);else if(!(e.nodeType!==1||c(e))){var t=(e.tagName||``).toUpperCase();if(l.indexOf(t)>=0)o.push(u(e));else if(t===`TABLE`)m(),p++,d.length<10&&(d.push(f(e)),a.push(`[table `+d.length+`]`));else{var n=s.indexOf(t)>=0;n&&m();for(var r=e.childNodes||[],i=0;i<r.length;i++)h(r[i]);n&&m()}}}r&&(h(r),m());var g=a.join(`
|
|
2
|
+
`),_=g.length>e||p>d.length||d.some(function(e){return e.totalRows>e.rows.length});return{text:g.length>e?g.slice(0,e)+`…`:g,tables:d,truncated:_}}function m(e,t){if(!e)return null;try{var n=new URL(e,t.href);return n.origin===t.origin?n:null}catch{return null}}function h(e){var t=(e.tagName||``).toLowerCase();return t===`select`||t===`textarea`?t:(e.type||`text`).toLowerCase()}function g(e){if(e.labels&&e.labels.length>0){var t=i(e.labels[0].textContent);if(t)return t}return i(e.getAttribute(`aria-label`))||i(e.getAttribute(`placeholder`))}function _(e){return!e.disabled&&n.indexOf(h(e))<0&&r.indexOf(e.name)<0}function v(e){for(var n=[],r=e.querySelectorAll(t.fields),i=0;i<r.length&&n.length<200;i++)_(r[i])&&n.push(r[i]);return n}function y(e){var t=h(e),n={key:e.name||e.id,name:e.name,id:e.id,label:g(e),type:t,value:t===`checkbox`||t===`radio`?!!e.checked:String(e.value==null?``:e.value).substring(0,2e3),required:!!e.required};return t===`select`&&(n.options=Array.prototype.map.call(e.options||[],function(e){return{value:e.value,text:i(e.text),selected:!!e.selected}})),n}function b(e,t){var n=i(t).toLowerCase();if(!n)return null;for(var r=v(e),a=[function(e){return(e.name||``).toLowerCase()===n},function(e){return(e.id||``).toLowerCase()===n},function(e){return g(e).toLowerCase()===n}],o=0;o<a.length;o++)for(var s=0;s<r.length;s++)if(a[o](r[s]))return r[s];return null}async function x(e,t){if(typeof e!=`function`)return!1;try{var n=await e(t,{method:`HEAD`,credentials:`same-origin`,redirect:`follow`});return n.status===404||n.status===410}catch{return!1}}function S(e,t){e.dispatchEvent(new Event(t,{bubbles:!0}))}function C(e){var n=e.doc,r=e.loc,o=typeof e.navigateDelayMs==`number`?e.navigateDelayMs:400;return[{name:`page_get_info`,title:`Get page info`,description:`Describes the page the user has open: its title, address, description and main headings, and whether it is an admin page. To read what the page shows, use page_read_content.`,inputSchema:{type:`object`,properties:{}},annotations:{readOnlyHint:!0},execute:function(){for(var a=n.querySelector(t.description),o=[],s=n.querySelectorAll(t.headings),c=0;c<s.length&&o.length<20;c++){var l=i(s[c].textContent);l&&o.push({level:(s[c].tagName||``).toUpperCase()===`H1`?1:2,text:l})}return{title:n.title,url:r.href,description:a?a.getAttribute(`content`):null,headings:o,isAdmin:!!e.isAdmin}}},{name:`page_read_content`,title:`Read page content`,description:`Reads what the page shows in its main area: its text, what its form fields hold (next to their labels), and its tables as headers and rows (for example the records a list page shows). Menus and hidden parts are left out. Use page_list_form_fields for the fields' names and choices before filling them, and page_list_links for the menus.`,inputSchema:{type:`object`,properties:{}},annotations:{readOnlyHint:!0},execute:function(){return p(n)}},{name:`page_list_links`,title:`List page links`,description:`Lists the links in the page's menus and navigation that stay on this site, with their text. Use it to find the page to open for a task.`,inputSchema:{type:`object`,properties:{filter:{type:`string`,description:`Only list links whose text contains these words.`}}},annotations:{readOnlyHint:!0},execute:function(e){for(var a=i(e&&e.filter).toLowerCase(),o=new Set,s=[],c=n.querySelectorAll(t.links),l=0;l<c.length&&s.length<200;l++){var u=m(c[l].getAttribute(`href`),r),d=i(c[l].textContent);!u||!d||o.has(u.href)||a&&d.toLowerCase().indexOf(a)<0||(o.add(u.href),s.push({text:d,url:u.href}))}return{links:s}}},{name:`page_navigate`,title:`Open page`,description:`Opens another page of this site in the current tab. Only addresses on this site are allowed; take them from page_list_links, and start a site path with /. Unsaved changes on the current page are lost.`,inputSchema:{type:`object`,properties:{url:{type:`string`,description:`The address to open: a path that starts with "/", or a full address on this site, as page_list_links lists them.`}},required:[`url`]},execute:async function(t){var n=i(t&&t.url);if(!n)throw Error(`Give the address of the page to open. Call page_list_links to find it.`);if(n.charAt(0)!==`/`&&!a.test(n))throw Error(`"`+n+`" is not an address on this site. Give a path that starts with "/" or a full address on this site, as page_list_links lists them. Call page_list_links to find the page to open.`);var s=m(n,r);if(!s)throw Error(`Only addresses on this site can be opened. Call page_list_links to find the page to open.`);if(await x(e.fetch,s.href))throw Error(`The site has no page at "`+s.pathname+s.search+`" (404). Do not guess addresses: call page_list_links to find the page to open.`);return setTimeout(function(){r.assign(s.href)},o),{navigatedTo:s.href}}},{name:`page_list_form_fields`,title:`List form fields`,description:`Lists the fields of the forms on the page: name, label, type, current value, whether it is required and, for lists, the choices. Hidden, password and file fields are not listed.`,inputSchema:{type:`object`,properties:{}},annotations:{readOnlyHint:!0},execute:function(){return{fields:v(n).map(y)}}},{name:`page_set_form_field`,title:`Set form field`,description:`Puts a value into one form field on the page, found by its name, id or label. It does not save or submit the form: the user reviews the change and saves it. The result has the field's value before the change (previousValue), so the change can be undone by setting it back.`,inputSchema:{type:`object`,properties:{field:{type:`string`,description:`The field's name, id or label, as page_list_form_fields lists it.`},value:{description:`The value. For a check box, true or false. For a list, a choice's value or text.`}},required:[`field`,`value`]},execute:function(e){var t=b(n,e&&e.field);if(!t)throw Error(`No editable field on the page is named "`+i(e&&e.field)+`".`);var r=h(t),a=e.value,o=y(t).value;if(r===`checkbox`||r===`radio`)t.checked=a===!0||String(a).toLowerCase()===`true`;else if(r===`select`){var s=i(a).toLowerCase(),c=Array.prototype.find.call(t.options||[],function(e){return String(e.value).toLowerCase()===s||i(e.text).toLowerCase()===s});if(!c)throw Error(`The list "`+g(t)+`" has no choice "`+i(a)+`".`);t.value=c.value}else t.value=a==null?``:String(a);return S(t,`input`),S(t,`change`),{field:y(t),previousValue:o}}}]}function w(e,t,n){var r=o.get(e);return r||(r=new Map,o.set(e,r)),Promise.all(C(t).map(function(t){return e.registerTool(t,n?{signal:n}:{}).then(function(){n&&n.aborted||(r.set(t.name,t),n&&n.addEventListener(`abort`,function(){r.get(t.name)===t&&r.delete(t.name)},{once:!0}))},function(e){console.warn(`The page tool "`+t.name+`" could not be registered.`,e)})}))}function T(e,t){var n=e?o.get(e):null;return n&&n.get(t)||null}if(window.CrestAppsWebMcpPageTools={selectors:t,createPageTools:C,register:w,getRegisteredTool:T},window.document&&window.document.modelContext){var E=window.crestAppsWebMcp&&window.crestAppsWebMcp.config;w(window.document.modelContext,{doc:window.document,loc:window.location,fetch:typeof window.fetch==`function`?window.fetch.bind(window):void 0,isAdmin:!!(E&&E.isAdmin)})}})()})();
|
|
3
3
|
//# sourceMappingURL=webmcp-page-tools.min.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webmcp-page-tools.min.js","sources":["../../Assets/js/webmcp-page-tools.js"],"sourcesContent":["// @ts-check\n/*\n * WebMCP page tools.\n *\n * Tools that work on the page the user sees: what the page is, what it shows, where its menus lead, opening another\n * page of the same site, and reading and filling its forms. They never submit a form and never leave the site, so the user always\n * reviews and saves the changes an agent makes.\n *\n * The tools are registered with the standard `document.modelContext.registerTool`, so any WebMCP agent can use them:\n * the browser's own assistant, or the CrestApps AI chat. As WebMCP defines, each answers a JSON-serializable value and\n * reports a failure by rejecting. Load webmcp-polyfill.js first in browsers without native WebMCP.\n */\n\n(function () {\n 'use strict';\n\n var MAX_HEADINGS = 20;\n var MAX_LINKS = 200;\n var MAX_FIELDS = 200;\n var MAX_VALUE_LENGTH = 2000;\n var MAX_CONTENT_LENGTH = 8000;\n var MAX_TABLES = 10;\n var MAX_TABLE_ROWS = 50;\n var MAX_CELL_LENGTH = 300;\n\n // The selectors the tools read the page with.\n var selectors = {\n description: 'meta[name=\"description\"]',\n headings: 'h1, h2',\n main: 'main, [role=\"main\"]',\n // What page_read_content leaves out of the page's text: scripts, menus, the choices and text of form controls\n // (page_list_form_fields reads them), hidden parts, the chat itself, and anything a page marks with\n // data-webmcp-ignore. A form's other content is read: admin pages often wrap a whole list in one, for bulk actions.\n ignored: 'script, style, noscript, template, svg, nav, aside, select, textarea, datalist, dialog:not([open]), [hidden], [aria-hidden=\"true\"], [role=\"navigation\"], [data-webmcp-ignore], [class*=\"ai-chat-widget\"]',\n links: 'nav a[href], header a[href], aside a[href], [role=\"navigation\"] a[href]',\n fields: 'form input, form select, form textarea',\n };\n\n // Field types an agent never reads or fills.\n var SKIPPED_TYPES = ['hidden', 'password', 'file', 'submit', 'button', 'reset', 'image'];\n var SKIPPED_NAMES = ['__RequestVerificationToken'];\n\n function cleanText(value) {\n return (value == null ? '' : String(value)).replace(/\\s+/g, ' ').trim();\n }\n\n\n // An address that starts with a scheme, such as \"https:\" or \"javascript:\".\n var ABSOLUTE_ADDRESS = /^[A-Za-z][A-Za-z0-9+.-]*:/;\n\n // The tools register() registered, by ModelContext and name, so the CrestApps chat can run them through their own\n // execute and tell the model why one failed (executeTool reports every failure as \"Tool execution failed\").\n var registeredTools = new WeakMap();\n\n // Elements that start a new line in the text page_read_content reads.\n var BLOCK_TAGS = ['ADDRESS', 'ARTICLE', 'BLOCKQUOTE', 'BR', 'DD', 'DETAILS', 'DIV', 'DL', 'DT', 'FIELDSET', 'FIGCAPTION', 'FIGURE', 'FOOTER', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'HEADER', 'HR', 'LI', 'OL', 'P', 'PRE', 'SECTION', 'SUMMARY', 'UL'];\n\n function isIgnored(node) {\n if (node.hidden || (typeof node.matches === 'function' && node.matches(selectors.ignored))) {\n return true;\n }\n\n // Parts the page hides with CSS, such as closed menus and modals.\n return typeof node.checkVisibility === 'function' && !node.checkVisibility();\n }\n\n function cellText(cell) {\n var text = cleanText(cell.textContent);\n\n return text.length > MAX_CELL_LENGTH ? text.slice(0, MAX_CELL_LENGTH) + '…' : text;\n }\n\n function readTable(table) {\n var rows = Array.prototype.slice.call(table.rows || []);\n var headers = [];\n\n if (rows.length && Array.prototype.every.call(rows[0].cells || [], function (cell) { return (cell.tagName || '').toUpperCase() === 'TH'; })) {\n headers = Array.prototype.map.call(rows.shift().cells, cellText);\n }\n\n var caption = table.caption ? cleanText(table.caption.textContent) : '';\n\n return {\n caption: caption || null,\n headers: headers,\n rows: rows.slice(0, MAX_TABLE_ROWS).map(function (row) {\n return Array.prototype.map.call(row.cells || [], cellText);\n }),\n totalRows: rows.length,\n };\n }\n\n /**\n * Reads the text and the tables of the page's main region, the way a person reads it: menus, forms, hidden parts\n * and the chat are left out, and each table is read once, as rows, where the text says [table N].\n */\n function readContent(doc) {\n var root = doc.querySelector(selectors.main) || doc.body;\n var lines = [];\n var line = [];\n var tables = [];\n var tableCount = 0;\n\n function endLine() {\n var text = cleanText(line.join(' '));\n\n if (text) {\n lines.push(text);\n }\n\n line = [];\n }\n\n function walk(node) {\n if (node.nodeType === 3) {\n line.push(node.nodeValue);\n\n return;\n }\n\n if (node.nodeType !== 1 || isIgnored(node)) {\n return;\n }\n\n var tag = (node.tagName || '').toUpperCase();\n\n if (tag === 'TABLE') {\n endLine();\n tableCount++;\n\n if (tables.length < MAX_TABLES) {\n tables.push(readTable(node));\n lines.push('[table ' + tables.length + ']');\n }\n\n return;\n }\n\n var isBlock = BLOCK_TAGS.indexOf(tag) >= 0;\n\n if (isBlock) {\n endLine();\n }\n\n var children = node.childNodes || [];\n\n for (var i = 0; i < children.length; i++) {\n walk(children[i]);\n }\n\n if (isBlock) {\n endLine();\n }\n }\n\n if (root) {\n walk(root);\n endLine();\n }\n\n var text = lines.join('\\n');\n var truncated = text.length > MAX_CONTENT_LENGTH || tableCount > tables.length ||\n tables.some(function (table) { return table.totalRows > table.rows.length; });\n\n return {\n text: text.length > MAX_CONTENT_LENGTH ? text.slice(0, MAX_CONTENT_LENGTH) + '…' : text,\n tables: tables,\n truncated: truncated,\n };\n }\n\n function resolveSameOrigin(href, loc) {\n if (!href) {\n return null;\n }\n\n try {\n var url = new URL(href, loc.href);\n\n return url.origin === loc.origin ? url : null;\n } catch (error) {\n return null;\n }\n }\n\n function fieldType(field) {\n var tag = (field.tagName || '').toLowerCase();\n\n if (tag === 'select' || tag === 'textarea') {\n return tag;\n }\n\n return (field.type || 'text').toLowerCase();\n }\n\n function fieldLabel(field) {\n if (field.labels && field.labels.length > 0) {\n var label = cleanText(field.labels[0].textContent);\n\n if (label) {\n return label;\n }\n }\n\n return cleanText(field.getAttribute('aria-label')) || cleanText(field.getAttribute('placeholder'));\n }\n\n function isListed(field) {\n return !field.disabled &&\n SKIPPED_TYPES.indexOf(fieldType(field)) < 0 &&\n SKIPPED_NAMES.indexOf(field.name) < 0;\n }\n\n function listFields(doc) {\n var fields = [];\n var all = doc.querySelectorAll(selectors.fields);\n\n for (var i = 0; i < all.length && fields.length < MAX_FIELDS; i++) {\n if (isListed(all[i])) {\n fields.push(all[i]);\n }\n }\n\n return fields;\n }\n\n function describeField(field) {\n var type = fieldType(field);\n var description = {\n key: field.name || field.id,\n name: field.name,\n id: field.id,\n label: fieldLabel(field),\n type: type,\n value: type === 'checkbox' || type === 'radio'\n ? !!field.checked\n : String(field.value == null ? '' : field.value).substring(0, MAX_VALUE_LENGTH),\n required: !!field.required,\n };\n\n if (type === 'select') {\n description.options = Array.prototype.map.call(field.options || [], function (option) {\n return { value: option.value, text: cleanText(option.text), selected: !!option.selected };\n });\n }\n\n return description;\n }\n\n function findField(doc, key) {\n var wanted = cleanText(key).toLowerCase();\n\n if (!wanted) {\n return null;\n }\n\n var fields = listFields(doc);\n var matchers = [\n function (field) {\n return (field.name || '').toLowerCase() === wanted;\n },\n function (field) {\n return (field.id || '').toLowerCase() === wanted;\n },\n function (field) {\n return fieldLabel(field).toLowerCase() === wanted;\n },\n ];\n\n for (var m = 0; m < matchers.length; m++) {\n for (var i = 0; i < fields.length; i++) {\n if (matchers[m](fields[i])) {\n return fields[i];\n }\n }\n }\n\n return null;\n }\n\n /**\n * Asks the site whether a page exists, with a HEAD request that downloads nothing. Only a 404 or 410 answer means it\n * does not: a page that refuses HEAD (405), sends to the login page or cannot be reached is opened as asked.\n *\n * @param {Function | undefined} fetchImpl The fetch function, or nothing to skip the check.\n * @param {string} href The address.\n */\n async function pageIsMissing(fetchImpl, href) {\n if (typeof fetchImpl !== 'function') {\n return false;\n }\n\n try {\n var response = await fetchImpl(href, { method: 'HEAD', credentials: 'same-origin', redirect: 'follow' });\n\n return response.status === 404 || response.status === 410;\n } catch (error) {\n return false;\n }\n }\n\n function raise(field, type) {\n field.dispatchEvent(new Event(type, { bubbles: true }));\n }\n\n /**\n * Creates the page tools for a document.\n *\n * @param {{ doc: any, loc: any, isAdmin?: boolean, fetch?: Function, navigateDelayMs?: number }} options The document,\n * its location, whether it is an admin page, the fetch function page_navigate checks that a page exists with (no\n * check without it), and how long page_navigate waits after answering before it opens the page.\n */\n function createPageTools(options) {\n var doc = options.doc;\n var loc = options.loc;\n var navigateDelayMs = typeof options.navigateDelayMs === 'number' ? options.navigateDelayMs : 400;\n\n return [\n {\n name: 'page_get_info',\n title: 'Get page info',\n description: 'Describes the page the user has open: its title, address, description and main headings, and whether it is an admin page. To read what the page shows, use page_read_content.',\n inputSchema: { type: 'object', properties: {} },\n annotations: { readOnlyHint: true },\n execute: function () {\n var meta = doc.querySelector(selectors.description);\n var headings = [];\n var all = doc.querySelectorAll(selectors.headings);\n\n for (var i = 0; i < all.length && headings.length < MAX_HEADINGS; i++) {\n var text = cleanText(all[i].textContent);\n\n if (text) {\n headings.push({ level: (all[i].tagName || '').toUpperCase() === 'H1' ? 1 : 2, text: text });\n }\n }\n\n return ({\n title: doc.title,\n url: loc.href,\n description: meta ? meta.getAttribute('content') : null,\n headings: headings,\n isAdmin: !!options.isAdmin,\n });\n },\n },\n {\n name: 'page_read_content',\n title: 'Read page content',\n description: 'Reads what the page shows in its main area: its text, and its tables as headers and rows (for example the records a list page shows). Menus, forms and hidden parts are left out; use page_list_links and page_list_form_fields for those.',\n inputSchema: { type: 'object', properties: {} },\n annotations: { readOnlyHint: true },\n execute: function () {\n return readContent(doc);\n },\n },\n {\n name: 'page_list_links',\n title: 'List page links',\n description: 'Lists the links in the page\\'s menus and navigation that stay on this site, with their text. Use it to find the page to open for a task.',\n inputSchema: {\n type: 'object',\n properties: {\n filter: { type: 'string', description: 'Only list links whose text contains these words.' },\n },\n },\n annotations: { readOnlyHint: true },\n execute: function (input) {\n var filter = cleanText(input && input.filter).toLowerCase();\n var seen = new Set();\n var links = [];\n var all = doc.querySelectorAll(selectors.links);\n\n for (var i = 0; i < all.length && links.length < MAX_LINKS; i++) {\n var url = resolveSameOrigin(all[i].getAttribute('href'), loc);\n var text = cleanText(all[i].textContent);\n\n if (!url || !text || seen.has(url.href) || (filter && text.toLowerCase().indexOf(filter) < 0)) {\n continue;\n }\n\n seen.add(url.href);\n links.push({ text: text, url: url.href });\n }\n\n return ({ links: links });\n },\n },\n {\n name: 'page_navigate',\n title: 'Open page',\n description: 'Opens another page of this site in the current tab. Only addresses on this site are allowed; take them from page_list_links, and start a site path with /. Unsaved changes on the current page are lost.',\n inputSchema: {\n type: 'object',\n properties: {\n url: { type: 'string', description: 'The address to open: a path that starts with \"/\", or a full address on this site, as page_list_links lists them.' },\n },\n required: ['url'],\n },\n execute: async function (input) {\n var address = cleanText(input && input.url);\n\n if (!address) {\n throw new Error('Give the address of the page to open. Call page_list_links to find it.');\n }\n\n // A bare relative path, such as \"ArticlesAdmin\", resolves against the current page and usually names a page\n // that does not exist: it is a guess, not an address the site gave.\n if (address.charAt(0) !== '/' && !ABSOLUTE_ADDRESS.test(address)) {\n throw new Error('\"' + address + '\" is not an address on this site. Give a path that starts with \"/\" or a full address on this site, as page_list_links lists them. Call page_list_links to find the page to open.');\n }\n\n var url = resolveSameOrigin(address, loc);\n\n if (!url) {\n throw new Error('Only addresses on this site can be opened. Call page_list_links to find the page to open.');\n }\n\n if (await pageIsMissing(options.fetch, url.href)) {\n throw new Error('The site has no page at \"' + url.pathname + url.search + '\" (404). Do not guess addresses: call page_list_links to find the page to open.');\n }\n\n // Answer first and open the page a moment later, so the agent gets the result before this page (and\n // the chat connection on it) goes away. The chat finishes the reply on the server and shows it on the\n // next page.\n setTimeout(function () {\n loc.assign(url.href);\n }, navigateDelayMs);\n\n return ({ navigatedTo: url.href });\n },\n },\n {\n name: 'page_list_form_fields',\n title: 'List form fields',\n description: 'Lists the fields of the forms on the page: name, label, type, current value, whether it is required and, for lists, the choices. Hidden, password and file fields are not listed.',\n inputSchema: { type: 'object', properties: {} },\n annotations: { readOnlyHint: true },\n execute: function () {\n return ({ fields: listFields(doc).map(describeField) });\n },\n },\n {\n name: 'page_set_form_field',\n title: 'Set form field',\n description: 'Puts a value into one form field on the page, found by its name, id or label. It does not save or submit the form: the user reviews the change and saves it.',\n inputSchema: {\n type: 'object',\n properties: {\n field: { type: 'string', description: 'The field\\'s name, id or label, as page_list_form_fields lists it.' },\n value: { description: 'The value. For a check box, true or false. For a list, a choice\\'s value or text.' },\n },\n required: ['field', 'value'],\n },\n execute: function (input) {\n var field = findField(doc, input && input.field);\n\n if (!field) {\n throw new Error('No editable field on the page is named \"' + cleanText(input && input.field) + '\".');\n }\n\n var type = fieldType(field);\n var value = input.value;\n\n if (type === 'checkbox' || type === 'radio') {\n field.checked = value === true || String(value).toLowerCase() === 'true';\n } else if (type === 'select') {\n var wanted = cleanText(value).toLowerCase();\n var option = Array.prototype.find.call(field.options || [], function (candidate) {\n return String(candidate.value).toLowerCase() === wanted || cleanText(candidate.text).toLowerCase() === wanted;\n });\n\n if (!option) {\n throw new Error('The list \"' + fieldLabel(field) + '\" has no choice \"' + cleanText(value) + '\".');\n }\n\n field.value = option.value;\n } else {\n field.value = value == null ? '' : String(value);\n }\n\n raise(field, 'input');\n raise(field, 'change');\n\n return ({ field: describeField(field) });\n },\n },\n ];\n }\n\n /**\n * Registers the page tools with the page's ModelContext.\n *\n * @param {any} modelContext The page's ModelContext (document.modelContext).\n * @param {{ doc: any, loc: any, isAdmin?: boolean, fetch?: Function }} options The document, its location, whether it\n * is an admin page, and the fetch function page_navigate checks that a page exists with (no check without it).\n * @param {AbortSignal} [signal] Unregisters the tools when aborted.\n */\n function register(modelContext, options, signal) {\n var tools = registeredTools.get(modelContext);\n\n if (!tools) {\n tools = new Map();\n registeredTools.set(modelContext, tools);\n }\n\n return Promise.all(createPageTools(options).map(function (tool) {\n return modelContext.registerTool(tool, signal ? { signal: signal } : {}).then(function () {\n if (signal && signal.aborted) {\n return;\n }\n\n tools.set(tool.name, tool);\n\n if (signal) {\n signal.addEventListener('abort', function () {\n if (tools.get(tool.name) === tool) {\n tools.delete(tool.name);\n }\n }, { once: true });\n }\n }, function (error) {\n console.warn('The page tool \"' + tool.name + '\" could not be registered.', error);\n });\n }));\n }\n\n /**\n * Answers the page tool register() registered with a ModelContext under a name, with its execute callback, or null\n * when it registered none: another script may have registered a tool of that name first.\n *\n * @param {any} modelContext The ModelContext the tool was registered with.\n * @param {string} name The tool's name.\n */\n function getRegisteredTool(modelContext, name) {\n var tools = modelContext ? registeredTools.get(modelContext) : null;\n\n return (tools && tools.get(name)) || null;\n }\n\n window.CrestAppsWebMcpPageTools = {\n selectors: selectors,\n createPageTools: createPageTools,\n register: register,\n getRegisteredTool: getRegisteredTool,\n };\n\n if (window.document && window.document.modelContext) {\n var config = window.crestAppsWebMcp && window.crestAppsWebMcp.config;\n\n register(window.document.modelContext, {\n doc: window.document,\n loc: window.location,\n fetch: typeof window.fetch === 'function' ? window.fetch.bind(window) : undefined,\n isAdmin: !!(config && config.isAdmin),\n });\n }\n})();\n"],"names":["MAX_CONTENT_LENGTH","selectors","SKIPPED_TYPES","SKIPPED_NAMES","cleanText","value","ABSOLUTE_ADDRESS","registeredTools","BLOCK_TAGS","isIgnored","node","cellText","text","cell","readTable","rows","table","headers","row","readContent","root","doc","lines","line","tables","tableCount","endLine","walk","tag","isBlock","children","truncated","resolveSameOrigin","href","url","loc","fieldType","field","fieldLabel","label","isListed","listFields","fields","all","describeField","type","description","option","findField","wanted","key","matchers","m","i","pageIsMissing","fetchImpl","response","raise","createPageTools","options","navigateDelayMs","meta","headings","filter","input","seen","links","address","candidate","register","tools","modelContext","tool","signal","error","getRegisteredTool","name","config"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"webmcp-page-tools.min.js","sources":["../../Assets/js/webmcp-page-tools.js"],"sourcesContent":["// @ts-check\n/*\n * WebMCP page tools.\n *\n * Tools that work on the page the user sees: what the page is, what it shows, where its menus lead, opening another\n * page of the same site, and reading and filling its forms. They never submit a form and never leave the site, so the user always\n * reviews and saves the changes an agent makes.\n *\n * The tools are registered with the standard `document.modelContext.registerTool`, so any WebMCP agent can use them:\n * the browser's own assistant, or the CrestApps AI chat. As WebMCP defines, each answers a JSON-serializable value and\n * reports a failure by rejecting. Load webmcp-polyfill.js first in browsers without native WebMCP.\n */\n\n(function () {\n 'use strict';\n\n var MAX_HEADINGS = 20;\n var MAX_LINKS = 200;\n var MAX_FIELDS = 200;\n var MAX_VALUE_LENGTH = 2000;\n var MAX_CONTENT_LENGTH = 8000;\n var MAX_TABLES = 10;\n var MAX_TABLE_ROWS = 50;\n var MAX_CELL_LENGTH = 300;\n var MAX_FIELD_TEXT_LENGTH = 500;\n\n // The selectors the tools read the page with.\n var selectors = {\n description: 'meta[name=\"description\"]',\n headings: 'h1, h2',\n main: 'main, [role=\"main\"]',\n // What page_read_content leaves out of the page's text: scripts, menus, hidden parts, the chat itself, and anything\n // a page marks with data-webmcp-ignore. A form is read, with what its fields hold: admin pages often wrap a whole\n // list in one, for bulk actions, and an editor's values are what the user sees.\n ignored: 'script, style, noscript, template, svg, nav, aside, datalist, dialog:not([open]), [hidden], [aria-hidden=\"true\"], [role=\"navigation\"], [data-webmcp-ignore], [class*=\"ai-chat-widget\"]',\n links: 'nav a[href], header a[href], aside a[href], [role=\"navigation\"] a[href]',\n fields: 'form input, form select, form textarea',\n };\n\n // Field types an agent never reads or fills.\n var SKIPPED_TYPES = ['hidden', 'password', 'file', 'submit', 'button', 'reset', 'image'];\n var SKIPPED_NAMES = ['__RequestVerificationToken'];\n\n function cleanText(value) {\n return (value == null ? '' : String(value)).replace(/\\s+/g, ' ').trim();\n }\n\n\n // An address that starts with a scheme, such as \"https:\" or \"javascript:\".\n var ABSOLUTE_ADDRESS = /^[A-Za-z][A-Za-z0-9+.-]*:/;\n\n // The tools register() registered, by ModelContext and name, so the CrestApps chat can run them through their own\n // execute and tell the model why one failed (executeTool reports every failure as \"Tool execution failed\").\n var registeredTools = new WeakMap();\n\n // Elements that start a new line in the text page_read_content reads.\n var BLOCK_TAGS = ['ADDRESS', 'ARTICLE', 'BLOCKQUOTE', 'BR', 'DD', 'DETAILS', 'DIV', 'DL', 'DT', 'FIELDSET', 'FIGCAPTION', 'FIGURE', 'FOOTER', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'HEADER', 'HR', 'LI', 'OL', 'P', 'PRE', 'SECTION', 'SUMMARY', 'UL'];\n\n function isIgnored(node) {\n if (node.hidden || (typeof node.matches === 'function' && node.matches(selectors.ignored))) {\n return true;\n }\n\n // Parts the page hides with CSS, such as closed menus and modals.\n return typeof node.checkVisibility === 'function' && !node.checkVisibility();\n }\n\n var FIELD_TAGS = ['INPUT', 'SELECT', 'TEXTAREA'];\n\n /**\n * The text page_read_content shows for a form field, where the page shows it: its value, the chosen option of a\n * list, or [x] / [ ] for a check box. Fields an agent never reads (hidden, password, file, buttons, disabled, the\n * antiforgery token) and empty fields show nothing.\n */\n function fieldText(field) {\n if (!isListed(field)) {\n return '';\n }\n\n var type = fieldType(field);\n\n if (type === 'checkbox' || type === 'radio') {\n return field.checked ? '[x]' : '[ ]';\n }\n\n var text = type === 'select'\n ? Array.prototype.filter.call(field.options || [], function (option) { return option.selected; })\n .map(function (option) { return cleanText(option.text); })\n .join(', ')\n : cleanText(field.value);\n\n return text.length > MAX_FIELD_TEXT_LENGTH ? text.slice(0, MAX_FIELD_TEXT_LENGTH) + '…' : text;\n }\n\n function cellText(cell) {\n var text = cleanText(cell.textContent);\n\n return text.length > MAX_CELL_LENGTH ? text.slice(0, MAX_CELL_LENGTH) + '…' : text;\n }\n\n function readTable(table) {\n var rows = Array.prototype.slice.call(table.rows || []);\n var headers = [];\n\n if (rows.length && Array.prototype.every.call(rows[0].cells || [], function (cell) { return (cell.tagName || '').toUpperCase() === 'TH'; })) {\n headers = Array.prototype.map.call(rows.shift().cells, cellText);\n }\n\n var caption = table.caption ? cleanText(table.caption.textContent) : '';\n\n return {\n caption: caption || null,\n headers: headers,\n rows: rows.slice(0, MAX_TABLE_ROWS).map(function (row) {\n return Array.prototype.map.call(row.cells || [], cellText);\n }),\n totalRows: rows.length,\n };\n }\n\n /**\n * Reads the text and the tables of the page's main region, the way a person reads it: menus, forms, hidden parts\n * and the chat are left out, and each table is read once, as rows, where the text says [table N].\n */\n function readContent(doc) {\n var root = doc.querySelector(selectors.main) || doc.body;\n var lines = [];\n var line = [];\n var tables = [];\n var tableCount = 0;\n\n function endLine() {\n var text = cleanText(line.join(' '));\n\n if (text) {\n lines.push(text);\n }\n\n line = [];\n }\n\n function walk(node) {\n if (node.nodeType === 3) {\n line.push(node.nodeValue);\n\n return;\n }\n\n if (node.nodeType !== 1 || isIgnored(node)) {\n return;\n }\n\n var tag = (node.tagName || '').toUpperCase();\n\n if (FIELD_TAGS.indexOf(tag) >= 0) {\n line.push(fieldText(node));\n\n return;\n }\n\n if (tag === 'TABLE') {\n endLine();\n tableCount++;\n\n if (tables.length < MAX_TABLES) {\n tables.push(readTable(node));\n lines.push('[table ' + tables.length + ']');\n }\n\n return;\n }\n\n var isBlock = BLOCK_TAGS.indexOf(tag) >= 0;\n\n if (isBlock) {\n endLine();\n }\n\n var children = node.childNodes || [];\n\n for (var i = 0; i < children.length; i++) {\n walk(children[i]);\n }\n\n if (isBlock) {\n endLine();\n }\n }\n\n if (root) {\n walk(root);\n endLine();\n }\n\n var text = lines.join('\\n');\n var truncated = text.length > MAX_CONTENT_LENGTH || tableCount > tables.length ||\n tables.some(function (table) { return table.totalRows > table.rows.length; });\n\n return {\n text: text.length > MAX_CONTENT_LENGTH ? text.slice(0, MAX_CONTENT_LENGTH) + '…' : text,\n tables: tables,\n truncated: truncated,\n };\n }\n\n function resolveSameOrigin(href, loc) {\n if (!href) {\n return null;\n }\n\n try {\n var url = new URL(href, loc.href);\n\n return url.origin === loc.origin ? url : null;\n } catch (error) {\n return null;\n }\n }\n\n function fieldType(field) {\n var tag = (field.tagName || '').toLowerCase();\n\n if (tag === 'select' || tag === 'textarea') {\n return tag;\n }\n\n return (field.type || 'text').toLowerCase();\n }\n\n function fieldLabel(field) {\n if (field.labels && field.labels.length > 0) {\n var label = cleanText(field.labels[0].textContent);\n\n if (label) {\n return label;\n }\n }\n\n return cleanText(field.getAttribute('aria-label')) || cleanText(field.getAttribute('placeholder'));\n }\n\n function isListed(field) {\n return !field.disabled &&\n SKIPPED_TYPES.indexOf(fieldType(field)) < 0 &&\n SKIPPED_NAMES.indexOf(field.name) < 0;\n }\n\n function listFields(doc) {\n var fields = [];\n var all = doc.querySelectorAll(selectors.fields);\n\n for (var i = 0; i < all.length && fields.length < MAX_FIELDS; i++) {\n if (isListed(all[i])) {\n fields.push(all[i]);\n }\n }\n\n return fields;\n }\n\n function describeField(field) {\n var type = fieldType(field);\n var description = {\n key: field.name || field.id,\n name: field.name,\n id: field.id,\n label: fieldLabel(field),\n type: type,\n value: type === 'checkbox' || type === 'radio'\n ? !!field.checked\n : String(field.value == null ? '' : field.value).substring(0, MAX_VALUE_LENGTH),\n required: !!field.required,\n };\n\n if (type === 'select') {\n description.options = Array.prototype.map.call(field.options || [], function (option) {\n return { value: option.value, text: cleanText(option.text), selected: !!option.selected };\n });\n }\n\n return description;\n }\n\n function findField(doc, key) {\n var wanted = cleanText(key).toLowerCase();\n\n if (!wanted) {\n return null;\n }\n\n var fields = listFields(doc);\n var matchers = [\n function (field) {\n return (field.name || '').toLowerCase() === wanted;\n },\n function (field) {\n return (field.id || '').toLowerCase() === wanted;\n },\n function (field) {\n return fieldLabel(field).toLowerCase() === wanted;\n },\n ];\n\n for (var m = 0; m < matchers.length; m++) {\n for (var i = 0; i < fields.length; i++) {\n if (matchers[m](fields[i])) {\n return fields[i];\n }\n }\n }\n\n return null;\n }\n\n /**\n * Asks the site whether a page exists, with a HEAD request that downloads nothing. Only a 404 or 410 answer means it\n * does not: a page that refuses HEAD (405), sends to the login page or cannot be reached is opened as asked.\n *\n * @param {Function | undefined} fetchImpl The fetch function, or nothing to skip the check.\n * @param {string} href The address.\n */\n async function pageIsMissing(fetchImpl, href) {\n if (typeof fetchImpl !== 'function') {\n return false;\n }\n\n try {\n var response = await fetchImpl(href, { method: 'HEAD', credentials: 'same-origin', redirect: 'follow' });\n\n return response.status === 404 || response.status === 410;\n } catch (error) {\n return false;\n }\n }\n\n function raise(field, type) {\n field.dispatchEvent(new Event(type, { bubbles: true }));\n }\n\n /**\n * Creates the page tools for a document.\n *\n * @param {{ doc: any, loc: any, isAdmin?: boolean, fetch?: Function, navigateDelayMs?: number }} options The document,\n * its location, whether it is an admin page, the fetch function page_navigate checks that a page exists with (no\n * check without it), and how long page_navigate waits after answering before it opens the page.\n */\n function createPageTools(options) {\n var doc = options.doc;\n var loc = options.loc;\n var navigateDelayMs = typeof options.navigateDelayMs === 'number' ? options.navigateDelayMs : 400;\n\n return [\n {\n name: 'page_get_info',\n title: 'Get page info',\n description: 'Describes the page the user has open: its title, address, description and main headings, and whether it is an admin page. To read what the page shows, use page_read_content.',\n inputSchema: { type: 'object', properties: {} },\n annotations: { readOnlyHint: true },\n execute: function () {\n var meta = doc.querySelector(selectors.description);\n var headings = [];\n var all = doc.querySelectorAll(selectors.headings);\n\n for (var i = 0; i < all.length && headings.length < MAX_HEADINGS; i++) {\n var text = cleanText(all[i].textContent);\n\n if (text) {\n headings.push({ level: (all[i].tagName || '').toUpperCase() === 'H1' ? 1 : 2, text: text });\n }\n }\n\n return ({\n title: doc.title,\n url: loc.href,\n description: meta ? meta.getAttribute('content') : null,\n headings: headings,\n isAdmin: !!options.isAdmin,\n });\n },\n },\n {\n name: 'page_read_content',\n title: 'Read page content',\n description: 'Reads what the page shows in its main area: its text, what its form fields hold (next to their labels), and its tables as headers and rows (for example the records a list page shows). Menus and hidden parts are left out. Use page_list_form_fields for the fields\\' names and choices before filling them, and page_list_links for the menus.',\n inputSchema: { type: 'object', properties: {} },\n annotations: { readOnlyHint: true },\n execute: function () {\n return readContent(doc);\n },\n },\n {\n name: 'page_list_links',\n title: 'List page links',\n description: 'Lists the links in the page\\'s menus and navigation that stay on this site, with their text. Use it to find the page to open for a task.',\n inputSchema: {\n type: 'object',\n properties: {\n filter: { type: 'string', description: 'Only list links whose text contains these words.' },\n },\n },\n annotations: { readOnlyHint: true },\n execute: function (input) {\n var filter = cleanText(input && input.filter).toLowerCase();\n var seen = new Set();\n var links = [];\n var all = doc.querySelectorAll(selectors.links);\n\n for (var i = 0; i < all.length && links.length < MAX_LINKS; i++) {\n var url = resolveSameOrigin(all[i].getAttribute('href'), loc);\n var text = cleanText(all[i].textContent);\n\n if (!url || !text || seen.has(url.href) || (filter && text.toLowerCase().indexOf(filter) < 0)) {\n continue;\n }\n\n seen.add(url.href);\n links.push({ text: text, url: url.href });\n }\n\n return ({ links: links });\n },\n },\n {\n name: 'page_navigate',\n title: 'Open page',\n description: 'Opens another page of this site in the current tab. Only addresses on this site are allowed; take them from page_list_links, and start a site path with /. Unsaved changes on the current page are lost.',\n inputSchema: {\n type: 'object',\n properties: {\n url: { type: 'string', description: 'The address to open: a path that starts with \"/\", or a full address on this site, as page_list_links lists them.' },\n },\n required: ['url'],\n },\n execute: async function (input) {\n var address = cleanText(input && input.url);\n\n if (!address) {\n throw new Error('Give the address of the page to open. Call page_list_links to find it.');\n }\n\n // A bare relative path, such as \"ArticlesAdmin\", resolves against the current page and usually names a page\n // that does not exist: it is a guess, not an address the site gave.\n if (address.charAt(0) !== '/' && !ABSOLUTE_ADDRESS.test(address)) {\n throw new Error('\"' + address + '\" is not an address on this site. Give a path that starts with \"/\" or a full address on this site, as page_list_links lists them. Call page_list_links to find the page to open.');\n }\n\n var url = resolveSameOrigin(address, loc);\n\n if (!url) {\n throw new Error('Only addresses on this site can be opened. Call page_list_links to find the page to open.');\n }\n\n if (await pageIsMissing(options.fetch, url.href)) {\n throw new Error('The site has no page at \"' + url.pathname + url.search + '\" (404). Do not guess addresses: call page_list_links to find the page to open.');\n }\n\n // Answer first and open the page a moment later, so the agent gets the result before this page (and\n // the chat connection on it) goes away. The chat finishes the reply on the server and shows it on the\n // next page.\n setTimeout(function () {\n loc.assign(url.href);\n }, navigateDelayMs);\n\n return ({ navigatedTo: url.href });\n },\n },\n {\n name: 'page_list_form_fields',\n title: 'List form fields',\n description: 'Lists the fields of the forms on the page: name, label, type, current value, whether it is required and, for lists, the choices. Hidden, password and file fields are not listed.',\n inputSchema: { type: 'object', properties: {} },\n annotations: { readOnlyHint: true },\n execute: function () {\n return ({ fields: listFields(doc).map(describeField) });\n },\n },\n {\n name: 'page_set_form_field',\n title: 'Set form field',\n description: 'Puts a value into one form field on the page, found by its name, id or label. It does not save or submit the form: the user reviews the change and saves it. The result has the field\\'s value before the change (previousValue), so the change can be undone by setting it back.',\n inputSchema: {\n type: 'object',\n properties: {\n field: { type: 'string', description: 'The field\\'s name, id or label, as page_list_form_fields lists it.' },\n value: { description: 'The value. For a check box, true or false. For a list, a choice\\'s value or text.' },\n },\n required: ['field', 'value'],\n },\n execute: function (input) {\n var field = findField(doc, input && input.field);\n\n if (!field) {\n throw new Error('No editable field on the page is named \"' + cleanText(input && input.field) + '\".');\n }\n\n var type = fieldType(field);\n var value = input.value;\n var previousValue = describeField(field).value;\n\n if (type === 'checkbox' || type === 'radio') {\n field.checked = value === true || String(value).toLowerCase() === 'true';\n } else if (type === 'select') {\n var wanted = cleanText(value).toLowerCase();\n var option = Array.prototype.find.call(field.options || [], function (candidate) {\n return String(candidate.value).toLowerCase() === wanted || cleanText(candidate.text).toLowerCase() === wanted;\n });\n\n if (!option) {\n throw new Error('The list \"' + fieldLabel(field) + '\" has no choice \"' + cleanText(value) + '\".');\n }\n\n field.value = option.value;\n } else {\n field.value = value == null ? '' : String(value);\n }\n\n raise(field, 'input');\n raise(field, 'change');\n\n return ({ field: describeField(field), previousValue: previousValue });\n },\n },\n ];\n }\n\n /**\n * Registers the page tools with the page's ModelContext.\n *\n * @param {any} modelContext The page's ModelContext (document.modelContext).\n * @param {{ doc: any, loc: any, isAdmin?: boolean, fetch?: Function }} options The document, its location, whether it\n * is an admin page, and the fetch function page_navigate checks that a page exists with (no check without it).\n * @param {AbortSignal} [signal] Unregisters the tools when aborted.\n */\n function register(modelContext, options, signal) {\n var tools = registeredTools.get(modelContext);\n\n if (!tools) {\n tools = new Map();\n registeredTools.set(modelContext, tools);\n }\n\n return Promise.all(createPageTools(options).map(function (tool) {\n return modelContext.registerTool(tool, signal ? { signal: signal } : {}).then(function () {\n if (signal && signal.aborted) {\n return;\n }\n\n tools.set(tool.name, tool);\n\n if (signal) {\n signal.addEventListener('abort', function () {\n if (tools.get(tool.name) === tool) {\n tools.delete(tool.name);\n }\n }, { once: true });\n }\n }, function (error) {\n console.warn('The page tool \"' + tool.name + '\" could not be registered.', error);\n });\n }));\n }\n\n /**\n * Answers the page tool register() registered with a ModelContext under a name, with its execute callback, or null\n * when it registered none: another script may have registered a tool of that name first.\n *\n * @param {any} modelContext The ModelContext the tool was registered with.\n * @param {string} name The tool's name.\n */\n function getRegisteredTool(modelContext, name) {\n var tools = modelContext ? registeredTools.get(modelContext) : null;\n\n return (tools && tools.get(name)) || null;\n }\n\n window.CrestAppsWebMcpPageTools = {\n selectors: selectors,\n createPageTools: createPageTools,\n register: register,\n getRegisteredTool: getRegisteredTool,\n };\n\n if (window.document && window.document.modelContext) {\n var config = window.crestAppsWebMcp && window.crestAppsWebMcp.config;\n\n register(window.document.modelContext, {\n doc: window.document,\n loc: window.location,\n fetch: typeof window.fetch === 'function' ? window.fetch.bind(window) : undefined,\n isAdmin: !!(config && config.isAdmin),\n });\n }\n})();\n"],"names":["MAX_CONTENT_LENGTH","selectors","SKIPPED_TYPES","SKIPPED_NAMES","cleanText","value","ABSOLUTE_ADDRESS","registeredTools","BLOCK_TAGS","isIgnored","node","FIELD_TAGS","fieldText","isListed","field","type","fieldType","text","option","cellText","cell","readTable","rows","table","headers","row","readContent","root","doc","lines","line","tables","tableCount","endLine","walk","tag","isBlock","children","truncated","resolveSameOrigin","href","url","loc","fieldLabel","label","listFields","fields","all","describeField","description","findField","wanted","key","matchers","m","i","pageIsMissing","fetchImpl","response","raise","createPageTools","options","navigateDelayMs","meta","headings","filter","input","seen","links","address","previousValue","candidate","register","tools","modelContext","tool","signal","error","getRegisteredTool","name","config"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crestapps/webmcp",
|
|
3
|
-
"version": "2.0.0-preview.
|
|
3
|
+
"version": "2.0.0-preview.259",
|
|
4
4
|
"description": "WebMCP for CrestApps: registers a site's AI tools and page tools with the standard document.modelContext API, and lets the CrestApps AI chat discover and run any WebMCP tool a page exposes. Ships the W3C Web Machine Learning Community Group polyfill for browsers without native WebMCP.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "CrestApps",
|