@wenathlan/extension 1.1.53 → 1.1.54
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/README.md +6 -5
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +999 -543
- package/dist/index.js.map +4 -4
- package/dist/mcpserver.d.ts +167 -0
- package/dist/mcpserver.d.ts.map +1 -0
- package/dist/memory.d.ts +27 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/policy.d.ts +25 -1
- package/dist/policy.d.ts.map +1 -1
- package/dist/protocol.d.ts +24 -1
- package/dist/protocol.d.ts.map +1 -1
- package/dist/toolcatalog.d.ts +31 -0
- package/dist/toolcatalog.d.ts.map +1 -0
- package/dist/types.d.ts +164 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/extension/dist/background.js +534 -4
- package/extension/dist/background.js.map +3 -3
- package/extension/dist/manifest.json +1 -1
- package/extension/dist/pagebridge.js +1 -1
- package/extension/dist/pagebridge.js.map +2 -2
- package/extension/dist/popup.html +1 -1
- package/extension/dist/popup.js +12 -1
- package/extension/dist/popup.js.map +2 -2
- package/extension/dist/sidepanel.html +1 -1
- package/extension/dist/sidepanel.js +136 -1
- package/extension/dist/sidepanel.js.map +2 -2
- package/extension/manifest.json +1 -1
- package/package.json +1 -1
|
@@ -2383,6 +2383,61 @@ var sessionmemory = class {
|
|
|
2383
2383
|
async removeworkflowversion(id, version) {
|
|
2384
2384
|
await this.adapter.set("workflowrecords", (await this.getworkflowrecordversions()).filter((entry) => !(entry.id === id && entry.version === version)));
|
|
2385
2385
|
}
|
|
2386
|
+
/** Returns every stored mcp client record, newest first. */
|
|
2387
|
+
async getclients() {
|
|
2388
|
+
return await this.adapter.get("mcpclients") ?? [];
|
|
2389
|
+
}
|
|
2390
|
+
/** Upserts one mcp client record by its id so one clientrecord stays per connected transport. */
|
|
2391
|
+
async setclient(client) {
|
|
2392
|
+
const records = (await this.getclients()).filter((entry) => entry.id !== client.id);
|
|
2393
|
+
await this.adapter.set("mcpclients", [client, ...records]);
|
|
2394
|
+
}
|
|
2395
|
+
/** Returns the connected client records — every client whose disconnect time is absent. */
|
|
2396
|
+
async listclients() {
|
|
2397
|
+
return (await this.getclients()).filter((client) => client.disconnectedat === void 0);
|
|
2398
|
+
}
|
|
2399
|
+
/** Stores the negotiated capability set of one client on its record. */
|
|
2400
|
+
async setclientcapabilities(id, capabilities) {
|
|
2401
|
+
await this.adapter.set("mcpclients", (await this.getclients()).map((client) => client.id === id ? { ...client, capabilities } : client));
|
|
2402
|
+
}
|
|
2403
|
+
/** Drops every stored client record when the server stops. */
|
|
2404
|
+
async clearclients() {
|
|
2405
|
+
await this.adapter.set("mcpclients", []);
|
|
2406
|
+
}
|
|
2407
|
+
/** Records one stdio bridge launch event with its process id; a restart marker distinguishes the relaunch of a dead client process. */
|
|
2408
|
+
async addbridgelaunch(launch) {
|
|
2409
|
+
await this.adapter.set("mcbridgelaunches", [launch, ...await this.adapter.get("mcbridgelaunches") ?? []]);
|
|
2410
|
+
}
|
|
2411
|
+
/** Returns every stdio bridge launch event, newest first. */
|
|
2412
|
+
async listbridgelaunches() {
|
|
2413
|
+
return await this.adapter.get("mcbridgelaunches") ?? [];
|
|
2414
|
+
}
|
|
2415
|
+
/** Returns the user configured mcp server config; an absent record keeps the documented localhost default. */
|
|
2416
|
+
async getmcpconfig() {
|
|
2417
|
+
return this.adapter.get("mcpconfig");
|
|
2418
|
+
}
|
|
2419
|
+
/** Stores the user configured mcp server config: bind address, port, transports, frame size, queue depth and enablement all stay user choices. */
|
|
2420
|
+
async setmcpconfig(config) {
|
|
2421
|
+
return this.adapter.set("mcpconfig", config);
|
|
2422
|
+
}
|
|
2423
|
+
/** Returns the persisted mcp server runtime state. */
|
|
2424
|
+
async getmcpstate() {
|
|
2425
|
+
return this.adapter.get("mcpstate");
|
|
2426
|
+
}
|
|
2427
|
+
/** Stores the mcp server runtime state with the stdio bridge status. */
|
|
2428
|
+
async setmcpstate(state) {
|
|
2429
|
+
return this.adapter.set("mcpstate", state);
|
|
2430
|
+
}
|
|
2431
|
+
/** Records one mcp tool call — the client, the tool, the origin and the outcome without any payload — under the user configured call retention with no code ceiling. */
|
|
2432
|
+
async addtoolcall(record2) {
|
|
2433
|
+
const records = await this.listtoolcalls();
|
|
2434
|
+
const retention = (await this.getmcpconfig())?.callretention;
|
|
2435
|
+
await this.adapter.set("mcptoolcalls", retention === void 0 ? [record2, ...records] : [record2, ...records].slice(0, retention));
|
|
2436
|
+
}
|
|
2437
|
+
/** Returns every stored mcp tool call record, newest first. */
|
|
2438
|
+
async listtoolcalls() {
|
|
2439
|
+
return await this.adapter.get("mcptoolcalls") ?? [];
|
|
2440
|
+
}
|
|
2386
2441
|
};
|
|
2387
2442
|
function mediakindof(record2) {
|
|
2388
2443
|
if ("pages" in record2) return "pdf";
|
|
@@ -2426,6 +2481,93 @@ function randomid() {
|
|
|
2426
2481
|
return crypto.randomUUID();
|
|
2427
2482
|
}
|
|
2428
2483
|
|
|
2484
|
+
// toolcatalog.ts
|
|
2485
|
+
var toolcatalogversion = 1;
|
|
2486
|
+
var toolnamespaces = ["browser", "workflow", "memory", "system"];
|
|
2487
|
+
function toolschemaof(properties) {
|
|
2488
|
+
return { type: "object", properties, required: Object.entries(properties).filter(([, property]) => property.required === true).map(([name]) => name) };
|
|
2489
|
+
}
|
|
2490
|
+
function readtool(name, kind, description, inputs = {}) {
|
|
2491
|
+
return { name, version: toolcatalogversion, description, inputschema: toolschemaof({ target: { type: "string", description: "Reviewed css selector the tool addresses." }, value: { type: "string", description: "Reviewed literal value the tool carries." }, options: { type: "object", description: "Reviewed json options of the wrapped action kind with the empty default.", default: {} }, ...inputs }), kind, risk: "read" };
|
|
2492
|
+
}
|
|
2493
|
+
function gatedtool(name, kind, risk, description, review) {
|
|
2494
|
+
return { name, version: toolcatalogversion, description, inputschema: toolschemaof({ stepid: { type: "string", description: "Id of the approved plan step this tool executes.", required: true } }), kind, risk, consentmeta: { review } };
|
|
2495
|
+
}
|
|
2496
|
+
function browserdomain() {
|
|
2497
|
+
return {
|
|
2498
|
+
namespace: "browser",
|
|
2499
|
+
version: toolcatalogversion,
|
|
2500
|
+
tools: [
|
|
2501
|
+
readtool("browser.snapshot", "observe", "Captures the semantic snapshot of the active tab: url, title, text preview, forms and interactive elements. Read only with no side effects; runs under the dryrun risk class once the session is approved."),
|
|
2502
|
+
readtool("browser.extract", "extract", "Extracts the reviewed structured data of the page. Read only with no side effects."),
|
|
2503
|
+
readtool("browser.readtext", "readtext", "Reads the text of the addressed element. Read only with no side effects.", { target: { type: "string", description: "Reviewed css selector of the element to read.", required: true } }),
|
|
2504
|
+
readtool("browser.readtable", "readtable", "Reads the rows of the addressed data table. Read only with no side effects.", { target: { type: "string", description: "Reviewed css selector of the table to read.", required: true } }),
|
|
2505
|
+
readtool("browser.readlinks", "readlinks", "Reads the link inventory of the page. Read only with no side effects."),
|
|
2506
|
+
readtool("browser.a11ytree", "a11ytree", "Reads the accessibility tree of the page. Read only with no side effects."),
|
|
2507
|
+
readtool("browser.tablist", "tablist", "Lists the open tabs. Read only with no side effects."),
|
|
2508
|
+
readtool("browser.windowlist", "windowlist", "Lists the open windows. Read only with no side effects."),
|
|
2509
|
+
gatedtool("browser.click", "click", "sensitive", "Clicks the addressed element. Sensitive: it changes page state, so it executes exactly one approved plan step.", "The click runs only as the approved plan step it names; a paired client can never widen the reviewed target or options."),
|
|
2510
|
+
gatedtool("browser.type", "type", "sensitive", "Types the reviewed text into the addressed element. Sensitive: it changes page state, so it executes exactly one approved plan step.", "The typing runs only as the approved plan step it names; the reviewed target, text and options stay fixed."),
|
|
2511
|
+
gatedtool("browser.presskey", "presskey", "sensitive", "Presses the reviewed key. Sensitive: it changes page state, so it executes exactly one approved plan step.", "The key press runs only as the approved plan step it names."),
|
|
2512
|
+
gatedtool("browser.navigate", "navigate", "sensitive", "Navigates the active tab to the reviewed url. Sensitive: it changes browser state, so it executes exactly one approved plan step.", "The navigation runs only as the approved plan step it names and stays inside the session origin grants."),
|
|
2513
|
+
gatedtool("browser.back", "back", "sensitive", "Navigates back in the history of the active tab. Sensitive: it changes browser state, so it executes exactly one approved plan step.", "The history navigation runs only as the approved plan step it names."),
|
|
2514
|
+
gatedtool("browser.forward", "forward", "sensitive", "Navigates forward in the history of the active tab. Sensitive: it changes browser state, so it executes exactly one approved plan step.", "The history navigation runs only as the approved plan step it names."),
|
|
2515
|
+
gatedtool("browser.reload", "reload", "sensitive", "Reloads the active tab. Sensitive: it changes browser state, so it executes exactly one approved plan step.", "The reload runs only as the approved plan step it names."),
|
|
2516
|
+
gatedtool("browser.tabcreate", "tabcreate", "sensitive", "Opens a new tab. Sensitive: it changes browser state, so it executes exactly one approved plan step.", "The tab creation runs only as the approved plan step it names."),
|
|
2517
|
+
gatedtool("browser.tabactivate", "tabactivate", "sensitive", "Activates the reviewed tab. Sensitive: it moves focus, so it executes exactly one approved plan step.", "The tab activation runs only as the approved plan step it names."),
|
|
2518
|
+
gatedtool("browser.tabclose", "tabclose", "sensitive", "Closes the reviewed tab. Sensitive: it destroys browser state, so it executes exactly one approved plan step.", "The tab close runs only as the approved plan step it names."),
|
|
2519
|
+
gatedtool("browser.windowcreate", "windowcreate", "sensitive", "Opens a new window. Sensitive: it changes browser state, so it executes exactly one approved plan step.", "The window creation runs only as the approved plan step it names."),
|
|
2520
|
+
gatedtool("browser.windowclose", "windowclose", "sensitive", "Closes the reviewed window. Sensitive: it destroys browser state, so it executes exactly one approved plan step.", "The window close runs only as the approved plan step it names."),
|
|
2521
|
+
gatedtool("browser.windowresize", "windowresize", "sensitive", "Resizes the reviewed window. Sensitive: it changes browser state, so it executes exactly one approved plan step.", "The window resize runs only as the approved plan step it names.")
|
|
2522
|
+
]
|
|
2523
|
+
};
|
|
2524
|
+
}
|
|
2525
|
+
function workflowdomain() {
|
|
2526
|
+
return {
|
|
2527
|
+
namespace: "workflow",
|
|
2528
|
+
version: toolcatalogversion,
|
|
2529
|
+
tools: [
|
|
2530
|
+
readtool("workflow.list", "composeworkflow", "Lists the composed workflows with their names, versions, origins and step counts. Read only with no side effects."),
|
|
2531
|
+
readtool("workflow.dryrun", "dryrun", "Runs a composed workflow as a dry run: read steps project their would be outcome and every step with side effects is refused. Read only with no side effects."),
|
|
2532
|
+
gatedtool("workflow.run", "runworkflow", "sensitive", "Runs a composed workflow for real. Sensitive: it executes every step of the workflow, so it executes exactly one approved runworkflow plan step with its explicit run review.", "The workflow run needs the explicit run review: the approved runworkflow plan step with its expanded step list shown before the first step executes."),
|
|
2533
|
+
gatedtool("workflow.triggers", "eventrule", "sensitive", "Lists the armed trigger rules with their schedules, cooldowns and fire counters so a client can inspect what launches runs automatically. Sensitive by its trigger family: automatic launchers stay behind the arm review class.", "The trigger listing runs behind the approved plan review because trigger rules launch runs automatically.")
|
|
2534
|
+
]
|
|
2535
|
+
};
|
|
2536
|
+
}
|
|
2537
|
+
function memorydomain() {
|
|
2538
|
+
return {
|
|
2539
|
+
namespace: "memory",
|
|
2540
|
+
version: toolcatalogversion,
|
|
2541
|
+
tools: [
|
|
2542
|
+
readtool("memory.list", "listruns", "Lists the stored workflow run records with their states and step cursors from local memory. Read only with no page access.", { target: { type: "string", description: "Unused by the memory read; kept for schema uniformity." }, value: { type: "string", description: "Unused by the memory read; kept for schema uniformity." }, state: { type: "string", description: "Optional reviewed run state filter of the listing.", default: "" } }),
|
|
2543
|
+
readtool("memory.variables", "extractvars", "Reads the stored variable scopes of a run from local memory. Read only with no page access."),
|
|
2544
|
+
readtool("memory.audit", "trailaudit", "Reads the audit summary of the session trail from local memory. Read only with no page access.")
|
|
2545
|
+
]
|
|
2546
|
+
};
|
|
2547
|
+
}
|
|
2548
|
+
function systemdomain() {
|
|
2549
|
+
return {
|
|
2550
|
+
namespace: "system",
|
|
2551
|
+
version: toolcatalogversion,
|
|
2552
|
+
tools: [
|
|
2553
|
+
readtool("system.status", "observe", "Reports the mcp server status, the session state and the connected clients. Read only with no side effects."),
|
|
2554
|
+
readtool("system.version", "readmeta", "Reports the protocol version, the catalog version and the extension version. Read only with no side effects."),
|
|
2555
|
+
readtool("system.capabilities", "observe", "Reports the optional browser capabilities the user has granted. Read only with no side effects.")
|
|
2556
|
+
]
|
|
2557
|
+
};
|
|
2558
|
+
}
|
|
2559
|
+
function buildtoolcatalog() {
|
|
2560
|
+
return { version: toolcatalogversion, domains: [browserdomain(), workflowdomain(), memorydomain(), systemdomain()] };
|
|
2561
|
+
}
|
|
2562
|
+
function alltools(catalog) {
|
|
2563
|
+
return catalog.domains.flatMap((domain) => domain.tools);
|
|
2564
|
+
}
|
|
2565
|
+
function resolvetool(catalog, name) {
|
|
2566
|
+
if (name.includes(".")) return alltools(catalog).find((tool) => tool.name === name);
|
|
2567
|
+
const matches = alltools(catalog).filter((tool) => tool.name.split(".")[1] === name);
|
|
2568
|
+
return matches.length === 1 ? matches[0] : void 0;
|
|
2569
|
+
}
|
|
2570
|
+
|
|
2429
2571
|
// socketbus.ts
|
|
2430
2572
|
var socketkinds = ["opensocket", "sendmessage", "waitmessage", "subscribesse", "longpoll"];
|
|
2431
2573
|
function channelorigin(url) {
|
|
@@ -5232,7 +5374,7 @@ function consolediff(input) {
|
|
|
5232
5374
|
// policy.ts
|
|
5233
5375
|
var sensitiveactions = /* @__PURE__ */ new Set(["click", "type", "navigate", "select", "presskey", "drag", "drop", "upload", "clear", "check", "uncheck", "toggle", "submit", "reload", "back", "forward", "writestorage", "setattribute", "removeattribute", "evaluate", "tabcreate", "tabactivate", "tabclose", "tabreload", "windowcreate", "windowclose", "windowresize", "downloadfile", "clickpoint", "shiftclick", "dismissdialog", "enterframe", "typetime", "appendtext", "setvalue", "typeedit", "keyhold", "keyrelease", "submitsearch", "selectmulti", "chooseradio", "setslider", "setdate", "setcolor", "openlink", "openprivate", "reloadcache", "stopnav", "followlink", "spanav", "rewritequery", "setfragment", "navlist", "navprofile", "handleauth", "printpdf", "prefetch", "preconnect", "deeplink", "reopentab", "pausenav", "navrate", "openclipboard", "batchopen", "duplicatetab", "closepattern", "pintab", "mutetab", "movetab", "movetabwindow", "grouptabs", "colorgroup", "collapsegroup", "discardtab", "reloadtabs", "zoomin", "zoomout", "switchtab", "maximizewindow", "minimizewindow", "restorewindow", "focuswindow", "scratchwindow", "incognitowindow", "restoretab", "restorelayout", "reopenrun", "badgetab", "fillform", "filllabel", "fillplaceholder", "submitform", "retryform", "runwizard", "selectchain", "picktypeahead", "pickdate", "attachfile", "fillcard", "fillcode", "consentpassword", "exportcsv", "exportjson", "exportexcel", "copytable", "pushsheets", "streamdisk", "paginateextract", "resumeextract", "batchdownload", "pausedownload", "resumedownload", "interceptmime", "readclipboard", "writeclipboard", "copyscreen", "quarantinedownload", "scanvirus", "cleanupartifacts", "recordscreen", "captureaudio", "downloadimages", "callrest", "callgraphql", "sendmessage", "blockrequest", "mockresponse", "rewriteheaders", "setcookies", "clearcookies", "authflow", "saveapikey", "routeproxy", "postform", "postfiles", "attachcdp", "detachcdp", "cdpcmd", "overridescript", "heapshot", "profilecpu", "capturesourcemaps", "emulatedevice", "emulatenetwork", "emulatelocate", "setuseragent", "overridepermission", "restoresession", "exportsessions", "importsessions", "runworkflow", "visitrule", "urlrule", "menurule", "keyrule", "buttonrule", "cronrule", "intervalrule", "urllistrule", "webhookrule", "eventrule"]);
|
|
5234
5376
|
var interactionactions = /* @__PURE__ */ new Set(["focus", "scroll", "hover", "clickdeep", "rightclick", "doubleclick", "scrollpage", "scrollby", "scrollend", "scrolltop", "fullscreen", "zoomset", "movepointer", "clicktext", "clickaria", "clickname", "expanddetails", "pierceshadow", "retryaction", "capturebodies", "setbreakpoint", "stepcode", "watchexpr", "loop", "repeatuntil", "whileloop", "foreach", "parallel", "trycatch"]);
|
|
5235
|
-
var readactions = /* @__PURE__ */ new Set(["observe", "inspect", "extract", "wait", "waitfor", "waittext", "readattribute", "readstyle", "readgeometry", "readvalue", "readtext", "readhtml", "countelements", "readtable", "readlinks", "readimages", "readmeta", "readforms", "readstorage", "highlight", "tablist", "windowlist", "tabsnapshot", "mapclicks", "verifyvisible", "verifyenabled", "resolvexpath", "a11ytree", "readvisible", "readertree", "detectlists", "detecttables", "readjson", "watchmutate", "waitquiet", "watchbanner", "detectinfinitescroll", "detectvirtual", "detectlazy", "readscrollpos", "readlang", "readoutline", "countpages", "listshadow", "listframes", "classifypage", "fingerprintsection", "diffsnapshots", "readselection", "watchfocus", "detectsticky", "detectscrolllock", "readopengraph", "detectlanguage", "deriveselector", "waitload", "waiturl", "spawait", "detecthttp", "readredirects", "readfinalurl", "trailaudit", "navintent", "checksafe", "querytabs", "watchtab", "findclones", "searchtabs", "listaudio", "snapshotsession", "savelayout", "attachmeta", "detectfields", "generatevalues", "saveprofiles", "asksubmit", "readerrors", "skiphoneypot", "detectlogin", "detecttemplate", "handoffcaptcha", "scrapetable", "importcsv", "looprows", "transformvalues", "deduperows", "mergepages", "stamplerows", "previewgrid", "logprovenance", "verifydownload", "exportnetlog", "namecaptures", "shotview", "shotfullpage", "shotelement", "shotregion", "contactsheet", "capturepdf", "captureframe", "readmedia", "readassets", "probestream", "timelapse", "shotcanvas", "convertimage", "makethumbs", "fetchurl", "parsejson", "parsehtml", "opensocket", "waitmessage", "watchrequests", "readheaders", "mapapi", "subscribesse", "longpoll", "extractapi", "readcookies", "watchconsole", "watcherrors", "watchtasks", "watchcdp", "measureflow", "trackmemory", "watchshifts", "traceload", "annotatetrace", "replaytrace", "blackboxscripts", "persiststate", "capturesession", "namedsessions", "diffsessions", "searchsessions", "composeworkflow", "savetemplate", "dryrun", "delay", "waitelement", "compute", "extractvars", "condition", "branch"]);
|
|
5377
|
+
var readactions = /* @__PURE__ */ new Set(["observe", "inspect", "extract", "wait", "waitfor", "waittext", "readattribute", "readstyle", "readgeometry", "readvalue", "readtext", "readhtml", "countelements", "readtable", "readlinks", "readimages", "readmeta", "readforms", "readstorage", "highlight", "tablist", "windowlist", "tabsnapshot", "mapclicks", "verifyvisible", "verifyenabled", "resolvexpath", "a11ytree", "readvisible", "readertree", "detectlists", "detecttables", "readjson", "watchmutate", "waitquiet", "watchbanner", "detectinfinitescroll", "detectvirtual", "detectlazy", "readscrollpos", "readlang", "readoutline", "countpages", "listshadow", "listframes", "classifypage", "fingerprintsection", "diffsnapshots", "readselection", "watchfocus", "detectsticky", "detectscrolllock", "readopengraph", "detectlanguage", "deriveselector", "waitload", "waiturl", "spawait", "detecthttp", "readredirects", "readfinalurl", "trailaudit", "navintent", "checksafe", "querytabs", "watchtab", "findclones", "searchtabs", "listaudio", "snapshotsession", "savelayout", "attachmeta", "detectfields", "generatevalues", "saveprofiles", "asksubmit", "readerrors", "skiphoneypot", "detectlogin", "detecttemplate", "handoffcaptcha", "scrapetable", "importcsv", "looprows", "transformvalues", "deduperows", "mergepages", "stamplerows", "previewgrid", "logprovenance", "verifydownload", "exportnetlog", "namecaptures", "shotview", "shotfullpage", "shotelement", "shotregion", "contactsheet", "capturepdf", "captureframe", "readmedia", "readassets", "probestream", "timelapse", "shotcanvas", "convertimage", "makethumbs", "fetchurl", "parsejson", "parsehtml", "opensocket", "waitmessage", "watchrequests", "readheaders", "mapapi", "subscribesse", "longpoll", "extractapi", "readcookies", "watchconsole", "watcherrors", "watchtasks", "watchcdp", "measureflow", "trackmemory", "watchshifts", "traceload", "annotatetrace", "replaytrace", "blackboxscripts", "persiststate", "capturesession", "namedsessions", "diffsessions", "searchsessions", "composeworkflow", "savetemplate", "dryrun", "delay", "waitelement", "compute", "extractvars", "listruns", "condition", "branch"]);
|
|
5236
5378
|
var allowedactions = /* @__PURE__ */ new Set([...sensitiveactions, ...interactionactions, ...readactions]);
|
|
5237
5379
|
var watchactions = /* @__PURE__ */ new Set(["watchmutate", "watchbanner", "watchfocus", "watchtab"]);
|
|
5238
5380
|
var targetactions = /* @__PURE__ */ new Set(["inspect", "focus", "click", "type", "scroll", "select", "hover", "clickdeep", "rightclick", "doubleclick", "drag", "drop", "upload", "clear", "check", "uncheck", "toggle", "submit", "readattribute", "readstyle", "readgeometry", "readvalue", "readtext", "readhtml", "countelements", "readtable", "highlight", "setattribute", "removeattribute", "waitfor", "shiftclick", "typetime", "appendtext", "setvalue", "typeedit", "submitsearch", "selectmulti", "chooseradio", "setslider", "setdate", "setcolor", "expanddetails", "verifyvisible", "verifyenabled", "pierceshadow", "deriveselector", "fingerprintsection", "submitform", "retryform", "selectchain", "picktypeahead", "pickdate", "attachfile", "fillcode", "consentpassword", "scrapetable", "paginateextract", "shotelement", "captureframe", "shotcanvas"]);
|
|
@@ -8119,6 +8261,37 @@ function watchdogconfigvalid(config) {
|
|
|
8119
8261
|
if (config.zombiewindow !== void 0 && (typeof config.zombiewindow !== "number" || !Number.isFinite(config.zombiewindow) || config.zombiewindow <= 0)) return { allowed: false, reason: "The watchdog zombie window, when configured, must be a positive number of milliseconds with no code ceiling." };
|
|
8120
8262
|
return { allowed: true };
|
|
8121
8263
|
}
|
|
8264
|
+
function serverbindgate(config) {
|
|
8265
|
+
const bind = config.bind !== void 0 && config.bind.trim() !== "" ? config.bind.trim() : "127.0.0.1";
|
|
8266
|
+
const local = bind === "127.0.0.1" || bind === "localhost" || bind === "::1";
|
|
8267
|
+
if (!local && config.remote !== true) return { allowed: false, reason: `The bind ${bind} leaves localhost and grades sensitive: the explicit remote review must approve it first.` };
|
|
8268
|
+
return { allowed: true };
|
|
8269
|
+
}
|
|
8270
|
+
function serverenablementgate(config) {
|
|
8271
|
+
if (config.enabled !== true) return { allowed: false, reason: "The mcp server starts only after the user enables it; the protocol surface stays closed by default." };
|
|
8272
|
+
const bind = serverbindgate(config);
|
|
8273
|
+
if (!bind.allowed) return bind;
|
|
8274
|
+
if (!Array.isArray(config.transports) || config.transports.length === 0) return { allowed: false, reason: "The mcp server needs at least one allowed transport of stdio or http." };
|
|
8275
|
+
if (!config.transports.every((transport) => transport === "stdio" || transport === "http")) return { allowed: false, reason: "The allowed transports of the mcp server are stdio and http." };
|
|
8276
|
+
if (typeof config.port !== "number" || !Number.isFinite(config.port) || config.port <= 0 || config.port > 65535) return { allowed: false, reason: "The http listener port must be a valid port number." };
|
|
8277
|
+
if (config.framesize !== void 0 && (typeof config.framesize !== "number" || !Number.isFinite(config.framesize) || config.framesize <= 0)) return { allowed: false, reason: "The user configured frame size must stay a positive number with no code ceiling." };
|
|
8278
|
+
if (config.queuedepth !== void 0 && (typeof config.queuedepth !== "number" || !Number.isFinite(config.queuedepth) || config.queuedepth <= 0)) return { allowed: false, reason: "The user configured queue depth must stay a positive number with no code ceiling." };
|
|
8279
|
+
return { allowed: true };
|
|
8280
|
+
}
|
|
8281
|
+
function tooldispatchgate(input) {
|
|
8282
|
+
if (input.client.disconnectedat !== void 0) return { allowed: false, reason: "The mcp client is disconnected and its tool calls are refused." };
|
|
8283
|
+
if (!input.client.paired) return { allowed: false, reason: "The mcp client waits for the user pairing approval; unpaired clients never dispatch tools." };
|
|
8284
|
+
if (!input.session || input.session.stoppedat || input.session.pausedat) return { allowed: false, reason: "Tool dispatch needs the live browser session behind the consent gates." };
|
|
8285
|
+
if (input.session.expiresat <= input.now) return { allowed: false, reason: "The browser session has expired and tool dispatch is refused." };
|
|
8286
|
+
if (!input.plan || input.plan.state !== "approved") return { allowed: false, reason: "Tool dispatch needs the approved plan review before any tool runs." };
|
|
8287
|
+
if (!origingranted(input.session, input.origin)) return { allowed: false, reason: `The tool call origin ${input.origin} stays outside the session grants and is refused.` };
|
|
8288
|
+
if (input.tool.risk === "read") return { allowed: true };
|
|
8289
|
+
if (input.stepid === void 0 || input.stepid.trim() === "") return { allowed: false, reason: `The ${input.tool.name} tool has side effects and needs the id of the approved plan step it executes.` };
|
|
8290
|
+
const step = input.plan.steps.find((candidate) => candidate.id === input.stepid);
|
|
8291
|
+
if (step === void 0) return { allowed: false, reason: `The tool call names the step ${input.stepid} which the approved plan does not carry.` };
|
|
8292
|
+
if (step.kind !== input.tool.kind) return { allowed: false, reason: `The tool call names the step ${input.stepid} whose kind ${String(step.kind)} does not match the ${input.tool.name} tool.` };
|
|
8293
|
+
return { allowed: true };
|
|
8294
|
+
}
|
|
8122
8295
|
|
|
8123
8296
|
// progress.ts
|
|
8124
8297
|
function emptyprogress(planid, now) {
|
|
@@ -8291,9 +8464,14 @@ function recordtrigger(progress, planid, stepid, entry, now) {
|
|
|
8291
8464
|
const outcome = { stepid, ok: true, summary: `${entry.detail}${counts.length > 0 ? ` with ${counts}` : ""}.`, details: { trigger: entry }, at: now };
|
|
8292
8465
|
return recordoutcome(base, planid, outcome, now);
|
|
8293
8466
|
}
|
|
8467
|
+
function recordtoolcall(progress, planid, stepid, entry, now) {
|
|
8468
|
+
const base = progress && progress.planid === planid ? progress : emptyprogress(planid, now);
|
|
8469
|
+
const outcome = { stepid, ok: entry.ok, summary: `The ${entry.tool} tool call of the client ${entry.clientid} ${entry.ok ? "ran behind the consent gates" : `was refused${entry.code !== void 0 ? ` with the ${entry.code} error` : ""}`}.`, details: { tool: entry }, at: now };
|
|
8470
|
+
return recordoutcome(base, planid, outcome, now);
|
|
8471
|
+
}
|
|
8294
8472
|
|
|
8295
8473
|
// version.ts
|
|
8296
|
-
var packageversion = "1.1.
|
|
8474
|
+
var packageversion = "1.1.54";
|
|
8297
8475
|
|
|
8298
8476
|
// types.ts
|
|
8299
8477
|
var protocolversion = packageversion;
|
|
@@ -8630,7 +8808,7 @@ function requestbody(input) {
|
|
|
8630
8808
|
return JSON.stringify({ version: protocolversion, objective: input.objective, session: input.session, observation: input.observation, capabilities: input.capabilities });
|
|
8631
8809
|
}
|
|
8632
8810
|
function outcomeresponse(input) {
|
|
8633
|
-
return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, outcome: input.outcome, ...input.resolvedtarget ? { resolvedtarget: input.resolvedtarget } : {}, ...input.capture ? { capture: input.capture } : {}, ...input.media ? { media: input.media } : {}, ...input.transport ? { transport: input.transport } : {}, ...input.network ? { network: input.network } : {}, ...input.control ? { control: input.control } : {}, ...input.timeline ? { timeline: input.timeline } : {}, ...input.cdp ? { cdp: input.cdp } : {}, ...input.profile ? { profile: input.profile } : {}, ...input.emulation ? { emulation: input.emulation } : {}, ...input.session ? { session: input.session } : {}, ...input.workflow ? { workflow: { runid: input.workflow.runid, state: input.workflow.state, ...input.workflow.dryrun === true ? { dryrun: true } : {}, produced: input.workflow.produced, consumed: input.workflow.consumed, ...input.workflow.timeout !== void 0 ? { timeout: input.workflow.timeout } : {}, ...input.workflow.retry !== void 0 ? { retry: input.workflow.retry } : {} } } : {}, ...input.trigger ? { trigger: { ruleid: input.trigger.ruleid, kind: input.trigger.kind, enabled: input.trigger.enabled, ...input.trigger.nextfireat !== void 0 ? { nextfireat: input.trigger.nextfireat } : {} } } : {} });
|
|
8811
|
+
return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, outcome: input.outcome, ...input.resolvedtarget ? { resolvedtarget: input.resolvedtarget } : {}, ...input.capture ? { capture: input.capture } : {}, ...input.media ? { media: input.media } : {}, ...input.transport ? { transport: input.transport } : {}, ...input.network ? { network: input.network } : {}, ...input.control ? { control: input.control } : {}, ...input.timeline ? { timeline: input.timeline } : {}, ...input.cdp ? { cdp: input.cdp } : {}, ...input.profile ? { profile: input.profile } : {}, ...input.emulation ? { emulation: input.emulation } : {}, ...input.session ? { session: input.session } : {}, ...input.workflow ? { workflow: { runid: input.workflow.runid, state: input.workflow.state, ...input.workflow.dryrun === true ? { dryrun: true } : {}, produced: input.workflow.produced, consumed: input.workflow.consumed, ...input.workflow.timeout !== void 0 ? { timeout: input.workflow.timeout } : {}, ...input.workflow.retry !== void 0 ? { retry: input.workflow.retry } : {} } } : {}, ...input.trigger ? { trigger: { ruleid: input.trigger.ruleid, kind: input.trigger.kind, enabled: input.trigger.enabled, ...input.trigger.nextfireat !== void 0 ? { nextfireat: input.trigger.nextfireat } : {} } } : {}, ...input.tool ? { tool: { clientid: input.tool.clientid, tool: input.tool.tool, origin: input.tool.origin, ok: input.tool.ok, ...input.tool.code !== void 0 ? { code: input.tool.code } : {} } } : {} });
|
|
8634
8812
|
}
|
|
8635
8813
|
function mapresponse(input) {
|
|
8636
8814
|
return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, map: input.map });
|
|
@@ -9894,6 +10072,142 @@ function yamlscalarvalue(text2) {
|
|
|
9894
10072
|
return text2;
|
|
9895
10073
|
}
|
|
9896
10074
|
|
|
10075
|
+
// mcpserver.ts
|
|
10076
|
+
var localhostbind = "127.0.0.1";
|
|
10077
|
+
var defaultmcpport = 7436;
|
|
10078
|
+
function rpcerrorof(code, message, data) {
|
|
10079
|
+
return { code, message, ...data !== void 0 ? { data } : {} };
|
|
10080
|
+
}
|
|
10081
|
+
function defaultmcpconfig() {
|
|
10082
|
+
return { port: defaultmcpport, transports: ["stdio", "http"], enabled: false };
|
|
10083
|
+
}
|
|
10084
|
+
function unwraphttppost(value) {
|
|
10085
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
10086
|
+
const candidate = value;
|
|
10087
|
+
if (candidate.transport === "http" && candidate.frame && typeof candidate.frame === "object" && !Array.isArray(candidate.frame)) return candidate.frame;
|
|
10088
|
+
}
|
|
10089
|
+
return value;
|
|
10090
|
+
}
|
|
10091
|
+
function parseframe(raw) {
|
|
10092
|
+
const parsed = unwraphttppost(JSON.parse(raw));
|
|
10093
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("A json rpc frame must be an object.");
|
|
10094
|
+
return parsed;
|
|
10095
|
+
}
|
|
10096
|
+
function serializeframe(frame) {
|
|
10097
|
+
return JSON.stringify(frame);
|
|
10098
|
+
}
|
|
10099
|
+
function validateframe(frame, methods, config) {
|
|
10100
|
+
if (frame.jsonrpc !== "2.0") return rpcerrorof("parse", "The frame must carry the jsonrpc 2.0 tag.");
|
|
10101
|
+
if (frame.id !== void 0 && typeof frame.id !== "number" && typeof frame.id !== "string" && frame.id !== null) return rpcerrorof("parse", "The frame id must be a number, a string or null.");
|
|
10102
|
+
if (frame.method === void 0 || frame.method.trim() === "") return rpcerrorof("method", "The frame carries no method to route.");
|
|
10103
|
+
if (!methods.some((entry) => entry.method === frame.method)) return rpcerrorof("method", `The server routes no method named ${frame.method}.`);
|
|
10104
|
+
if (frame.params !== void 0 && (typeof frame.params !== "object" || Array.isArray(frame.params))) return rpcerrorof("params", "The frame params must be an object.");
|
|
10105
|
+
if (config?.framesize !== void 0 && serializeframe(frame).length > config.framesize) return rpcerrorof("params", `The serialized frame exceeds the user configured frame size of ${config.framesize} characters.`);
|
|
10106
|
+
return void 0;
|
|
10107
|
+
}
|
|
10108
|
+
function respond(input) {
|
|
10109
|
+
return { jsonrpc: "2.0", ...input.id === void 0 ? input.error !== void 0 ? { id: null } : {} : { id: input.id }, ...input.error !== void 0 ? { error: input.error } : { result: input.result } };
|
|
10110
|
+
}
|
|
10111
|
+
function servermethods() {
|
|
10112
|
+
return [
|
|
10113
|
+
{ method: "initialize", handler: "initialize", description: "Completes the mcp handshake and returns the server info." },
|
|
10114
|
+
{ method: "ping", handler: "ping", description: "Answers keepalive frames with pong." },
|
|
10115
|
+
{ method: "tools/list", handler: "listtools", description: "Returns every tool with its version and json schema inputs." },
|
|
10116
|
+
{ method: "negotiate", handler: "negotiate", description: "Exchanges capability sets with the client." },
|
|
10117
|
+
{ method: "tools/call", handler: "dispatch", description: "Invokes one tool behind the consent gates." }
|
|
10118
|
+
];
|
|
10119
|
+
}
|
|
10120
|
+
function servercapabilities(input) {
|
|
10121
|
+
return { protocolversion, name: "devthink", version: protocolversion, toolversion: input.catalog.version, tools: alltools(input.catalog).length, namespaces: toolnamespaces, transports: input.config.transports };
|
|
10122
|
+
}
|
|
10123
|
+
function initialize(input) {
|
|
10124
|
+
void input.params;
|
|
10125
|
+
return { serverinfo: servercapabilities({ config: input.config, catalog: input.catalog }), protocolversion, instructions: "Devthink serves browser tools behind the human review gates: read only tools run once a session is approved while every tool with side effects executes exactly the approved plan step it names. No endpoint, provider or key is hardcoded; the user pairs every client." };
|
|
10126
|
+
}
|
|
10127
|
+
function ping(input) {
|
|
10128
|
+
return { pong: true, at: input.now };
|
|
10129
|
+
}
|
|
10130
|
+
function listtools(catalog) {
|
|
10131
|
+
return { tools: alltools(catalog).map((tool) => ({ name: tool.name, version: tool.version, description: tool.description, inputschema: tool.inputschema, risk: tool.risk, ...tool.consentmeta !== void 0 ? { consentmeta: tool.consentmeta.review } : {} })) };
|
|
10132
|
+
}
|
|
10133
|
+
function negotiate(input) {
|
|
10134
|
+
const client = input.client;
|
|
10135
|
+
if (client?.protocolversion !== void 0 && client.protocolversion !== input.server.protocolversion) return { agreed: false, mismatch: `The client speaks protocol version ${String(client.protocolversion)} while the server offers ${input.server.protocolversion}.` };
|
|
10136
|
+
if (client?.toolversion !== void 0 && client.toolversion > input.server.toolversion) return { agreed: false, mismatch: `The client requires tool version ${String(client.toolversion)} while the server offers ${String(input.server.toolversion)}.` };
|
|
10137
|
+
if (client?.transports !== void 0 && client.transports.some((transport) => !input.server.transports.includes(transport))) return { agreed: false, mismatch: "The client requires a transport the server configuration does not allow." };
|
|
10138
|
+
return { agreed: true, capabilities: input.server };
|
|
10139
|
+
}
|
|
10140
|
+
function connectclient(input) {
|
|
10141
|
+
return { id: input.id ?? `client-${input.now}`, transport: input.transport, paired: false, connectedat: input.now };
|
|
10142
|
+
}
|
|
10143
|
+
function disconnectclient(clients, id, now) {
|
|
10144
|
+
return clients.map((client) => client.id === id && client.disconnectedat === void 0 ? { ...client, disconnectedat: now } : client);
|
|
10145
|
+
}
|
|
10146
|
+
async function dispatchtool(input) {
|
|
10147
|
+
const params = input.params;
|
|
10148
|
+
if (!params || typeof params !== "object" || Array.isArray(params)) return { error: rpcerrorof("params", "The tool call needs its params object.") };
|
|
10149
|
+
if (typeof params.name !== "string" || !params.name.trim()) return { error: rpcerrorof("params", "The tool call needs the namespaced name of the tool it invokes.") };
|
|
10150
|
+
const tool = resolvetool(input.catalog, params.name.trim());
|
|
10151
|
+
if (tool === void 0) return { error: rpcerrorof("params", `The catalog holds no unambiguous tool named ${params.name.trim()}.`) };
|
|
10152
|
+
const floor = input.client.capabilities?.toolversion ?? input.catalog.version;
|
|
10153
|
+
if (tool.version < floor) return { error: rpcerrorof("params", `The tool ${tool.name} of version ${tool.version} stays below the negotiated compatibility floor of ${floor}.`) };
|
|
10154
|
+
const stepid = typeof params.stepid === "string" ? params.stepid : void 0;
|
|
10155
|
+
const gate = tooldispatchgate({ client: input.client, tool, session: input.session, plan: input.plan, origin: input.origin, ...stepid !== void 0 ? { stepid } : {}, now: input.now });
|
|
10156
|
+
if (!gate.allowed) return { error: rpcerrorof("consentrefused", gate.reason ?? "The consent gates refused the tool call.") };
|
|
10157
|
+
const step = tool.risk === "read" ? { id: `mcp-${input.client.id}-${input.now}`, kind: tool.kind, summary: tool.description.split(".")[0] ?? tool.description, risk: "read", ...typeof params.target === "string" ? { target: params.target } : {}, ...typeof params.value === "string" ? { value: params.value } : {}, ...params.options !== void 0 && typeof params.options === "object" && !Array.isArray(params.options) ? { options: JSON.stringify(params.options) } : {} } : input.plan?.steps.find((candidate) => candidate.id === stepid);
|
|
10158
|
+
if (step === void 0) return { error: rpcerrorof("consentrefused", "The tool call names a step the approved plan does not carry.") };
|
|
10159
|
+
try {
|
|
10160
|
+
const result = await input.execute(step);
|
|
10161
|
+
return { result, step };
|
|
10162
|
+
} catch (error) {
|
|
10163
|
+
return { error: rpcerrorof("internal", error instanceof Error ? error.message : String(error)) };
|
|
10164
|
+
}
|
|
10165
|
+
}
|
|
10166
|
+
async function handleframe(input) {
|
|
10167
|
+
if (input.raw !== void 0 && input.config.framesize !== void 0 && input.raw.length > input.config.framesize) return respond({ id: null, error: rpcerrorof("params", `The wire frame exceeds the user configured frame size of ${input.config.framesize} characters.`) });
|
|
10168
|
+
let frame;
|
|
10169
|
+
if (input.raw !== void 0) {
|
|
10170
|
+
try {
|
|
10171
|
+
frame = parseframe(input.raw);
|
|
10172
|
+
} catch {
|
|
10173
|
+
return respond({ id: null, error: rpcerrorof("parse", "The wire frame does not parse as json.") });
|
|
10174
|
+
}
|
|
10175
|
+
} else if (input.frame !== void 0) {
|
|
10176
|
+
frame = input.frame;
|
|
10177
|
+
} else {
|
|
10178
|
+
return respond({ id: null, error: rpcerrorof("parse", "The server received no frame to route.") });
|
|
10179
|
+
}
|
|
10180
|
+
const invalid = validateframe(frame, servermethods(), input.config);
|
|
10181
|
+
if (invalid !== void 0) return respond({ ...frame.id !== void 0 ? { id: frame.id } : {}, error: invalid });
|
|
10182
|
+
const entry = servermethods().find((candidate) => candidate.method === frame.method);
|
|
10183
|
+
if (entry === void 0) return respond({ ...frame.id !== void 0 ? { id: frame.id } : {}, error: rpcerrorof("method", `The server routes no method named ${String(frame.method)}.`) });
|
|
10184
|
+
const params = frame.params;
|
|
10185
|
+
if (entry.handler === "initialize") return respond({ ...frame.id !== void 0 ? { id: frame.id } : {}, result: initialize({ ...params !== void 0 ? { params } : {}, config: input.config, catalog: input.catalog }) });
|
|
10186
|
+
if (entry.handler === "ping") return respond({ ...frame.id !== void 0 ? { id: frame.id } : {}, result: ping({ now: input.now }) });
|
|
10187
|
+
if (entry.handler === "listtools") return respond({ ...frame.id !== void 0 ? { id: frame.id } : {}, result: listtools(input.catalog) });
|
|
10188
|
+
if (entry.handler === "negotiate") {
|
|
10189
|
+
const server = servercapabilities({ config: input.config, catalog: input.catalog });
|
|
10190
|
+
const clientcaps = params?.capabilities && typeof params.capabilities === "object" && !Array.isArray(params.capabilities) ? params.capabilities : void 0;
|
|
10191
|
+
const outcome = negotiate({ ...clientcaps !== void 0 ? { client: clientcaps } : {}, server });
|
|
10192
|
+
return respond({ ...frame.id !== void 0 ? { id: frame.id } : {}, ...outcome.agreed ? { result: outcome.capabilities } : { error: rpcerrorof("params", outcome.mismatch ?? "The capability negotiation did not agree.") } });
|
|
10193
|
+
}
|
|
10194
|
+
const dispatched = await dispatchtool({ ...params !== void 0 ? { params } : {}, client: input.client, catalog: input.catalog, ...input.session !== void 0 ? { session: input.session } : {}, ...input.plan !== void 0 ? { plan: input.plan } : {}, origin: input.origin, now: input.now, execute: input.execute });
|
|
10195
|
+
return respond({ ...frame.id !== void 0 ? { id: frame.id } : {}, ...dispatched.error !== void 0 ? { error: dispatched.error } : { result: dispatched.result } });
|
|
10196
|
+
}
|
|
10197
|
+
function bindlocalhost(config) {
|
|
10198
|
+
const bind = config.bind !== void 0 && config.bind.trim() !== "" ? config.bind.trim() : localhostbind;
|
|
10199
|
+
return { bind, port: config.port, localhost: bind === localhostbind || bind === "localhost" || bind === "::1" };
|
|
10200
|
+
}
|
|
10201
|
+
function launchbridge(input) {
|
|
10202
|
+
return { id: input.id ?? `bridge-${input.now}`, host: input.host, connected: true, ...input.pid !== void 0 ? { pid: input.pid } : {}, startedat: input.now, restarts: 0, received: 0, sent: 0 };
|
|
10203
|
+
}
|
|
10204
|
+
function relayframe(input) {
|
|
10205
|
+
return { ...input.bridge, connected: true, received: input.bridge.received + (input.direction === "inbound" ? 1 : 0), sent: input.bridge.sent + (input.direction === "outbound" ? 1 : 0), lastframeat: input.now };
|
|
10206
|
+
}
|
|
10207
|
+
function toolcallevent(input) {
|
|
10208
|
+
return { id: input.id, clientid: input.clientid, tool: input.tool, origin: input.origin, ok: input.ok, ...input.code !== void 0 ? { code: input.code } : {}, at: input.now };
|
|
10209
|
+
}
|
|
10210
|
+
|
|
9897
10211
|
// extension/pagesession.ts
|
|
9898
10212
|
function capturepagestate(sections) {
|
|
9899
10213
|
const wants = (section) => sections.includes(section);
|
|
@@ -16083,6 +16397,8 @@ async function executeaction(step, session, plan, tabid2, origin, settings, verd
|
|
|
16083
16397
|
} else if (istriggeraction(step.kind)) {
|
|
16084
16398
|
if (!session || !plan || plan.state !== "approved") throw new Error("Trigger kinds refuse to run outside an approved session plan.");
|
|
16085
16399
|
output = await executetriggerstep(step, session, plan, tabid2, origin);
|
|
16400
|
+
} else if (step.kind === "listruns") {
|
|
16401
|
+
output = await executelistruns(step, session);
|
|
16086
16402
|
} else {
|
|
16087
16403
|
if (step.target && freshcheckkinds.has(step.kind)) {
|
|
16088
16404
|
const fresh = await snapshot(tabid2);
|
|
@@ -16337,7 +16653,7 @@ async function handlerequest(message, sender) {
|
|
|
16337
16653
|
const livetab = session ? await chrome.tabs.get(session.tabid).catch(() => void 0) : void 0;
|
|
16338
16654
|
const waitprofile = session ? waitprofiles.find((record2) => record2.origin === session.origin) : void 0;
|
|
16339
16655
|
const livestate = { phase: livetab?.status === "loading" ? "loading" : "complete", ...navrecords[0] ? { finalurl: navrecords[0].finalurl, redirects: navrecords[0].chain } : {} };
|
|
16340
|
-
return { config: await memory.getconfig(), session, plan, progress: plan && progress?.planid === plan.id ? progress : void 0, diagnostic: await memory.getdiagnostic(), audit: await memory.getaudit(), capabilities: await refreshcapabilities(), outcomes: await memory.getoutcomes(), holds: heldkeysreport({ tabid: session?.tabid ?? 0, holds }), dialogs: await memory.getdialogs(), retries: await memory.getretries(), ...signals ? { signals: signalsreport({ signals }) } : { signals: signalsreport({}) }, banners: await memory.getbanners(), mutationevents: await memory.getmutationevents(), focusevents: await memory.getfocusevents(), diffs: await memory.getdiffs(), selectors: await memory.getselectors(), ...a11y ? { a11y } : {}, ...reader ? { reader } : {}, ...map ? { map } : {}, trail: trailreport({ ...session ? { sessionid: session.id } : {}, trail }), navrecords, ratestates, safeties, curated, waitprofiles, auths, navcontrol, navqueues, artifacts, navstate: livestate, ...waitprofile ? { waitprofile } : {}, offline: !navigator.onLine, tabs, windows, layouts: layoutreport({ layouts }), tabgroups, tabmetas, badges, snapshots, closedtabs, tabwatchevents, clones, tasktabgauge: taskgauge, ...controltab ? { controltab } : {}, tabreport: report, profiles, tickets, wizards: wizardreport({ ...session ? { sessionid: session.id } : {}, wizards, picks }), picks, errorreports, captchas, detections, ...codeentry !== void 0 ? { codeentry: true } : {}, datasets, imports, extractsessions, streams, exports, provenances, taskrules, sheetendpoints: sheetgrants, downloads, netlogs, clipconsents, clips, quarantines, cleanuprules, cleanupruns, capturecounters, inventory, mimefilters, scanhooks, captures: capturemetadata, capturepairs, capturepolicy: runsettings?.capturepolicy ?? "manual", media: mediarecords, imagebatches, recordingconsents, recordingactive: [...activerecordings.values()].map((active) => ({ id: active.record.id, kind: active.record.kind, scope: active.record.scope, startedat: active.record.startedat, stopat: active.stopat })), recordingwindow: runsettings?.recordingwindow, calls, endpoints, fetchconsents, apikeys, callretention: runsettings?.callretention, fetchesactive: activefetches.size, exchanges, channels, subscriptions, apimap, messages: messagecount, webrequestgrant: runsettings?.webrequestgrant === true, bodyretention: runsettings?.bodyretention, timelineretention: runsettings?.timelineretention, timeline, consoleconsents: await memory.getconsoleconsents(), rotationtargets: await memory.getrotationtargets(), levelsummaries: await memory.getlevelsummaries(), cdpsessions: await memory.getcdpsessions(), cdpcommands: await memory.getcdpcommands(), cdpeventrules: await memory.getcdpeventrules(), breakpoints: await memory.getbreakpoints(), pauses: await memory.getpauses(), watchexpressions: await memory.getwatchexpressions(), scriptoverrides: await memory.getscriptoverrides(), debuggergrants: await memory.getdebuggergrants(), pauseretention: runsettings?.pauseretention, breakpointceiling: runsettings?.breakpointceiling, cdpattached: [...activecdpsessions.values()].filter((active) => active.session.detachedat === void 0).length, profileretention: runsettings?.profileretention, traceceiling: runsettings?.traceceiling, profile: profilereport({ flows: await memory.getflowmetrics(), heaps: await memory.getheaprecords(), samples: await memory.getgrowsamples(), trends: await memory.gettrends(), profiles: await memory.getcpuprofiles(), shifts: await memory.getshiftentries(), traces: await memory.gettracerecords(), sourcemaps: await memory.getsourcemaps(), consents: await memory.getsourcemapconsents() }), profileactive: activememorytrackers.size + activeprofiletargets.size, profiletargets: [...activeprofiletargets.values()].flatMap((entry) => entry.targets), socketsactive: activesockets.size, emulation: emulationreport({ ...plan && await loademulationstate(plan.id) !== void 0 ? { state: await loademulationstate(plan.id) } : {}, devices: await memory.getdevicepresets(), networks: await memory.getnetworkpresets(), locations: await memory.getlocationpresets(), agents: await memory.getagentpresets(), blackbox: await memory.getblackboxrules(), permissions: await memory.getpermissionoverrides(), consents: await memory.getlocationconsents() }), emulatedlayers: plan ? layernames(await loademulationstate(plan.id)) : [], emulationretention: runsettings?.emulationretention, traffic, tokens, authflows, activerules: [...activerules.values()].reduce((total, ruleset) => total + ruleset.blocks.filter((rule) => rule.revertedat === void 0).length + ruleset.mocks.filter((rule) => rule.revertedat === void 0).length + ruleset.rewrites.filter((rule) => rule.revertedat === void 0).length + (ruleset.proxy !== void 0 && ruleset.proxy.revertedat === void 0 ? 1 : 0), 0), sessionmemory: sessionreport({ records: sessionrecords, events: await memory.getsessionevents(), folders: await memory.getsessionfolders(), diffs: await memory.getsessiondiffs(), ...autosnapshot !== void 0 ? { auto: autosnapshot.interval } : {}, ...crashed ? { crashed: true } : {} }), autosnapshotstate: autosnapshot, sessionretention: runsettings?.sessionretention, trigger: triggerlist({ rules: await memory.gettriggerules(), workflows: await memory.listworkflows(), queue: await memory.gettriggerqueue() }), triggerretention: runsettings?.triggerretention, workflow: workflowreport({ workflows: await memory.listworkflows(), runs: await memory.listworkflowruns(), templates: await memory.getsteptemplates(), ...newestworkflowrun !== void 0 ? { log: await memory.getrunlog(newestworkflowrun.id), scopes: await memory.getrunscopes(newestworkflowrun.id), provenance: await memory.getworkflowprovenance(newestworkflowrun.id), control: await memory.listcontroldecisions(newestworkflowrun.id) } : {} }), runlogretention: runsettings?.runlogretention, runhistoryretention: runsettings?.runhistoryretention, editor: editorstate({ versions: await memory.listworkflowversions(), diffs: await memory.listversiondiffs(), history: await memory.gethistory(), overrides: await memory.listsiteoverrides(), imports: (await memory.listworkflowimports()).map((entry) => ({ id: entry.id, workflowid: entry.record.id, name: entry.record.name, version: entry.record.version, steps: entry.record.steps.length, risk: entry.record.risk, importedat: entry.importedat, ...entry.filename !== void 0 ? { filename: entry.filename } : {} })), backgroundruns: await memory.getbackgroundruns(), watchdog: { ...runsettings?.watchdog !== void 0 ? { config: runsettings.watchdog } : {}, events: await memory.listwatchdogevents() } }), ...taskstate !== void 0 ? { taskstate } : {}, ...stitchprogress.size > 0 ? { stitchprogress: [...stitchprogress.values()] } : {} };
|
|
16656
|
+
return { config: await memory.getconfig(), session, plan, progress: plan && progress?.planid === plan.id ? progress : void 0, diagnostic: await memory.getdiagnostic(), audit: await memory.getaudit(), capabilities: await refreshcapabilities(), outcomes: await memory.getoutcomes(), holds: heldkeysreport({ tabid: session?.tabid ?? 0, holds }), dialogs: await memory.getdialogs(), retries: await memory.getretries(), ...signals ? { signals: signalsreport({ signals }) } : { signals: signalsreport({}) }, banners: await memory.getbanners(), mutationevents: await memory.getmutationevents(), focusevents: await memory.getfocusevents(), diffs: await memory.getdiffs(), selectors: await memory.getselectors(), ...a11y ? { a11y } : {}, ...reader ? { reader } : {}, ...map ? { map } : {}, trail: trailreport({ ...session ? { sessionid: session.id } : {}, trail }), navrecords, ratestates, safeties, curated, waitprofiles, auths, navcontrol, navqueues, artifacts, navstate: livestate, ...waitprofile ? { waitprofile } : {}, offline: !navigator.onLine, tabs, windows, layouts: layoutreport({ layouts }), tabgroups, tabmetas, badges, snapshots, closedtabs, tabwatchevents, clones, tasktabgauge: taskgauge, ...controltab ? { controltab } : {}, tabreport: report, profiles, tickets, wizards: wizardreport({ ...session ? { sessionid: session.id } : {}, wizards, picks }), picks, errorreports, captchas, detections, ...codeentry !== void 0 ? { codeentry: true } : {}, datasets, imports, extractsessions, streams, exports, provenances, taskrules, sheetendpoints: sheetgrants, downloads, netlogs, clipconsents, clips, quarantines, cleanuprules, cleanupruns, capturecounters, inventory, mimefilters, scanhooks, captures: capturemetadata, capturepairs, capturepolicy: runsettings?.capturepolicy ?? "manual", media: mediarecords, imagebatches, recordingconsents, recordingactive: [...activerecordings.values()].map((active) => ({ id: active.record.id, kind: active.record.kind, scope: active.record.scope, startedat: active.record.startedat, stopat: active.stopat })), recordingwindow: runsettings?.recordingwindow, calls, endpoints, fetchconsents, apikeys, callretention: runsettings?.callretention, fetchesactive: activefetches.size, exchanges, channels, subscriptions, apimap, messages: messagecount, webrequestgrant: runsettings?.webrequestgrant === true, bodyretention: runsettings?.bodyretention, timelineretention: runsettings?.timelineretention, timeline, consoleconsents: await memory.getconsoleconsents(), rotationtargets: await memory.getrotationtargets(), levelsummaries: await memory.getlevelsummaries(), cdpsessions: await memory.getcdpsessions(), cdpcommands: await memory.getcdpcommands(), cdpeventrules: await memory.getcdpeventrules(), breakpoints: await memory.getbreakpoints(), pauses: await memory.getpauses(), watchexpressions: await memory.getwatchexpressions(), scriptoverrides: await memory.getscriptoverrides(), debuggergrants: await memory.getdebuggergrants(), pauseretention: runsettings?.pauseretention, breakpointceiling: runsettings?.breakpointceiling, cdpattached: [...activecdpsessions.values()].filter((active) => active.session.detachedat === void 0).length, profileretention: runsettings?.profileretention, traceceiling: runsettings?.traceceiling, profile: profilereport({ flows: await memory.getflowmetrics(), heaps: await memory.getheaprecords(), samples: await memory.getgrowsamples(), trends: await memory.gettrends(), profiles: await memory.getcpuprofiles(), shifts: await memory.getshiftentries(), traces: await memory.gettracerecords(), sourcemaps: await memory.getsourcemaps(), consents: await memory.getsourcemapconsents() }), profileactive: activememorytrackers.size + activeprofiletargets.size, profiletargets: [...activeprofiletargets.values()].flatMap((entry) => entry.targets), socketsactive: activesockets.size, emulation: emulationreport({ ...plan && await loademulationstate(plan.id) !== void 0 ? { state: await loademulationstate(plan.id) } : {}, devices: await memory.getdevicepresets(), networks: await memory.getnetworkpresets(), locations: await memory.getlocationpresets(), agents: await memory.getagentpresets(), blackbox: await memory.getblackboxrules(), permissions: await memory.getpermissionoverrides(), consents: await memory.getlocationconsents() }), emulatedlayers: plan ? layernames(await loademulationstate(plan.id)) : [], emulationretention: runsettings?.emulationretention, traffic, tokens, authflows, activerules: [...activerules.values()].reduce((total, ruleset) => total + ruleset.blocks.filter((rule) => rule.revertedat === void 0).length + ruleset.mocks.filter((rule) => rule.revertedat === void 0).length + ruleset.rewrites.filter((rule) => rule.revertedat === void 0).length + (ruleset.proxy !== void 0 && ruleset.proxy.revertedat === void 0 ? 1 : 0), 0), sessionmemory: sessionreport({ records: sessionrecords, events: await memory.getsessionevents(), folders: await memory.getsessionfolders(), diffs: await memory.getsessiondiffs(), ...autosnapshot !== void 0 ? { auto: autosnapshot.interval } : {}, ...crashed ? { crashed: true } : {} }), autosnapshotstate: autosnapshot, sessionretention: runsettings?.sessionretention, trigger: triggerlist({ rules: await memory.gettriggerules(), workflows: await memory.listworkflows(), queue: await memory.gettriggerqueue() }), triggerretention: runsettings?.triggerretention, workflow: workflowreport({ workflows: await memory.listworkflows(), runs: await memory.listworkflowruns(), templates: await memory.getsteptemplates(), ...newestworkflowrun !== void 0 ? { log: await memory.getrunlog(newestworkflowrun.id), scopes: await memory.getrunscopes(newestworkflowrun.id), provenance: await memory.getworkflowprovenance(newestworkflowrun.id), control: await memory.listcontroldecisions(newestworkflowrun.id) } : {} }), runlogretention: runsettings?.runlogretention, runhistoryretention: runsettings?.runhistoryretention, editor: editorstate({ versions: await memory.listworkflowversions(), diffs: await memory.listversiondiffs(), history: await memory.gethistory(), overrides: await memory.listsiteoverrides(), imports: (await memory.listworkflowimports()).map((entry) => ({ id: entry.id, workflowid: entry.record.id, name: entry.record.name, version: entry.record.version, steps: entry.record.steps.length, risk: entry.record.risk, importedat: entry.importedat, ...entry.filename !== void 0 ? { filename: entry.filename } : {} })), backgroundruns: await memory.getbackgroundruns(), watchdog: { ...runsettings?.watchdog !== void 0 ? { config: runsettings.watchdog } : {}, events: await memory.listwatchdogevents() } }), ...taskstate !== void 0 ? { taskstate } : {}, ...stitchprogress.size > 0 ? { stitchprogress: [...stitchprogress.values()] } : {}, mcp: await mcpstateof() };
|
|
16341
16657
|
}
|
|
16342
16658
|
case "capabilities":
|
|
16343
16659
|
return refreshcapabilities();
|
|
@@ -17875,6 +18191,89 @@ async function handlerequest(message, sender) {
|
|
|
17875
18191
|
const fires = await memory.listtriggerfires();
|
|
17876
18192
|
return triggerlist({ rules: await memory.gettriggerules(), workflows: await memory.listworkflows(), queue: await memory.gettriggerqueue() });
|
|
17877
18193
|
}
|
|
18194
|
+
case "mcpstate": {
|
|
18195
|
+
return mcpstateof();
|
|
18196
|
+
}
|
|
18197
|
+
case "mcpserverconfig": {
|
|
18198
|
+
const inputconfig = message;
|
|
18199
|
+
const current = await mcpconfigof();
|
|
18200
|
+
const transports = Array.isArray(inputconfig.transports) && inputconfig.transports.length > 0 ? [...new Set(inputconfig.transports.filter((transport) => transport === "stdio" || transport === "http"))] : current.transports;
|
|
18201
|
+
const config = {
|
|
18202
|
+
...inputconfig.bind !== void 0 ? { bind: inputconfig.bind } : current.bind !== void 0 ? { bind: current.bind } : {},
|
|
18203
|
+
port: typeof inputconfig.port === "number" && Number.isFinite(inputconfig.port) && inputconfig.port > 0 && inputconfig.port <= 65535 ? Math.floor(inputconfig.port) : current.port,
|
|
18204
|
+
transports,
|
|
18205
|
+
...inputconfig.framesize !== void 0 || current.framesize !== void 0 ? { framesize: typeof inputconfig.framesize === "number" ? inputconfig.framesize : current.framesize } : {},
|
|
18206
|
+
...inputconfig.queuedepth !== void 0 || current.queuedepth !== void 0 ? { queuedepth: typeof inputconfig.queuedepth === "number" ? inputconfig.queuedepth : current.queuedepth } : {},
|
|
18207
|
+
...inputconfig.callretention !== void 0 || current.callretention !== void 0 ? { callretention: typeof inputconfig.callretention === "number" ? inputconfig.callretention : current.callretention } : {},
|
|
18208
|
+
enabled: inputconfig.enabled === true || inputconfig.enabled === void 0 && current.enabled === true,
|
|
18209
|
+
...inputconfig.remote === true || inputconfig.remote === void 0 && current.remote === true ? { remote: true } : {}
|
|
18210
|
+
};
|
|
18211
|
+
const bindcheck = serverbindgate(config);
|
|
18212
|
+
if (!bindcheck.allowed) throw new Error(bindcheck.reason ?? "The server bind failed its gate.");
|
|
18213
|
+
await memory.setmcpconfig(config);
|
|
18214
|
+
const binding = bindlocalhost(config);
|
|
18215
|
+
await audit("protocol", `Configured the mcp server for the ${binding.bind} bind on port ${binding.port} with the transports ${config.transports.join(" and ")}${config.framesize !== void 0 ? `, a frame size of ${config.framesize} characters` : " and no frame size cap"}${config.queuedepth !== void 0 ? `, a queue depth of ${config.queuedepth}` : " and an unbounded queue"}; every value stays the user choice and the server ${config.enabled ? "stays enabled" : "stays disabled"}.`, {});
|
|
18216
|
+
return mcpstateof();
|
|
18217
|
+
}
|
|
18218
|
+
case "mcpserverstart": {
|
|
18219
|
+
const config = { ...await mcpconfigof(), enabled: true };
|
|
18220
|
+
const gate = serverenablementgate(config);
|
|
18221
|
+
if (!gate.allowed) throw new Error(gate.reason ?? "The mcp server failed its enablement gate.");
|
|
18222
|
+
await memory.setmcpconfig(config);
|
|
18223
|
+
const binding = bindlocalhost(config);
|
|
18224
|
+
const bridge = await trybridgelaunch(false);
|
|
18225
|
+
await memory.setmcpstate({ state: "running", startedat: Date.now(), ...bridge !== void 0 ? { bridge } : {} });
|
|
18226
|
+
await audit("protocol", `The user started the mcp server on ${binding.bind}:${binding.port} with the tool catalog of ${listtools(buildtoolcatalog()).tools.length} tools across the browser, workflow, memory and system namespaces; the localhost bind stays the default and every client waits for the pairing approval.`, {});
|
|
18227
|
+
return mcpstateof();
|
|
18228
|
+
}
|
|
18229
|
+
case "mcpserverstop": {
|
|
18230
|
+
const clients = await memory.getclients();
|
|
18231
|
+
const now = Date.now();
|
|
18232
|
+
for (const client of clients) {
|
|
18233
|
+
const updated = disconnectclient(clients, client.id, now).find((entry) => entry.id === client.id);
|
|
18234
|
+
if (updated) await memory.setclient(updated);
|
|
18235
|
+
}
|
|
18236
|
+
await memory.setmcpstate({ state: "stopped", stoppedat: now });
|
|
18237
|
+
await audit("protocol", `The user stopped the mcp server; ${clients.length} connected client${clients.length === 1 ? "" : "s"} disconnected and no tool call passes the gates until the next start.`, {});
|
|
18238
|
+
return mcpstateof();
|
|
18239
|
+
}
|
|
18240
|
+
case "mcpclientdecision": {
|
|
18241
|
+
const inputdecision = message;
|
|
18242
|
+
const clientid = inputdecision.clientid ?? "";
|
|
18243
|
+
const clients = await memory.getclients();
|
|
18244
|
+
const client = clients.find((entry) => entry.id === clientid);
|
|
18245
|
+
if (!client || client.disconnectedat !== void 0) throw new Error(`No connected mcp client matches ${clientid}.`);
|
|
18246
|
+
if (typeof inputdecision.approved !== "boolean") throw new Error("The client pairing decision needs the reviewed approved flag.");
|
|
18247
|
+
const updated = inputdecision.approved ? { ...client, paired: true, pairedat: Date.now() } : { ...client, paired: false, disconnectedat: Date.now() };
|
|
18248
|
+
await memory.setclient(updated);
|
|
18249
|
+
await audit("protocol", `The user ${inputdecision.approved ? "approved the pairing of" : "refused and disconnected"} the mcp client ${clientid} on the ${client.transport} transport${inputdecision.approved ? "; its tool calls now pass the same consent gates as the panels" : "; its tool calls stay refused"}.`, {});
|
|
18250
|
+
return mcpstateof();
|
|
18251
|
+
}
|
|
18252
|
+
case "mcpclientdisconnect": {
|
|
18253
|
+
const inputdisconnect = message;
|
|
18254
|
+
const clientid = inputdisconnect.clientid ?? "";
|
|
18255
|
+
const clients = await memory.getclients();
|
|
18256
|
+
const client = clients.find((entry) => entry.id === clientid);
|
|
18257
|
+
if (!client || client.disconnectedat !== void 0) throw new Error(`No connected mcp client matches ${clientid}.`);
|
|
18258
|
+
await memory.setclient({ ...client, disconnectedat: Date.now() });
|
|
18259
|
+
await audit("protocol", `The user disconnected the mcp client ${clientid} on the ${client.transport} transport; its record stays for the audit trail.`, {});
|
|
18260
|
+
return mcpstateof();
|
|
18261
|
+
}
|
|
18262
|
+
case "mcpbridge": {
|
|
18263
|
+
const inputbridge = message;
|
|
18264
|
+
const state = await memory.getmcpstate();
|
|
18265
|
+
if (state?.state !== "running") throw new Error("The stdio bridge restart needs the running mcp server.");
|
|
18266
|
+
const bridge = await trybridgelaunch(true);
|
|
18267
|
+
await memory.setmcpstate({ ...state, ...bridge !== void 0 ? { bridge } : {} });
|
|
18268
|
+
await audit("protocol", bridge !== void 0 ? `The stdio bridge restarted the local client process on demand; the bridge record counts the restart.` : `The stdio bridge restart found no native messaging host because the browser exposes no native messaging permission under the current permission set; the mcp frame grammar and the panel path stay fully available.`, {});
|
|
18269
|
+
return mcpstateof();
|
|
18270
|
+
}
|
|
18271
|
+
case "mcpframe": {
|
|
18272
|
+
const inputframe = message;
|
|
18273
|
+
if (typeof inputframe.raw !== "string" || inputframe.raw.trim() === "") throw new Error("The mcp frame intake needs the raw wire frame.");
|
|
18274
|
+
const transport = inputframe.transport === "http" ? "http" : "stdio";
|
|
18275
|
+
return processmcpframe(inputframe.raw, inputframe.clientid ?? "", transport);
|
|
18276
|
+
}
|
|
17878
18277
|
case "runtobreakpoint": {
|
|
17879
18278
|
const inputdebug = message;
|
|
17880
18279
|
const session = await memory.getsession();
|
|
@@ -18238,6 +18637,137 @@ async function restorebackgroundruns() {
|
|
|
18238
18637
|
await audit("workflow", `The worker wake restored the background run ${run.id} of the workflow ${record2.name} from its checkpoint at step cursor ${run.cursor}; the session, plan and origin gates re-passed.`, { sessionid: session.id, planid: plan.id });
|
|
18239
18638
|
}
|
|
18240
18639
|
}
|
|
18640
|
+
async function mcpconfigof() {
|
|
18641
|
+
return await memory.getmcpconfig() ?? defaultmcpconfig();
|
|
18642
|
+
}
|
|
18643
|
+
async function mcpstateof() {
|
|
18644
|
+
const config = await mcpconfigof();
|
|
18645
|
+
const state = await memory.getmcpstate();
|
|
18646
|
+
const binding = bindlocalhost(config);
|
|
18647
|
+
return { state: state?.state ?? "stopped", config, bind: binding.bind, port: binding.port, localhost: binding.localhost, clients: await memory.listclients(), ...state?.bridge !== void 0 ? { bridge: state.bridge } : {}, calls: (await memory.listtoolcalls()).slice(0, 25), catalog: listtools(buildtoolcatalog()), launches: (await memory.listbridgelaunches()).slice(0, 10) };
|
|
18648
|
+
}
|
|
18649
|
+
async function executelistruns(step, session) {
|
|
18650
|
+
const options = stepoptions2(step);
|
|
18651
|
+
const statefilter = typeof options.state === "string" && options.state.trim() !== "" ? options.state : void 0;
|
|
18652
|
+
const runs = await memory.listworkflowruns();
|
|
18653
|
+
const selected = statefilter !== void 0 ? runs.filter((run) => run.state === statefilter) : runs;
|
|
18654
|
+
await audit("workflow", `Listed ${selected.length} stored workflow run record${selected.length === 1 ? "" : "s"}${statefilter !== void 0 ? ` of the ${statefilter} state` : ""} from local memory as the read only listruns step; the listing carries no page data.`, { ...session ? { sessionid: session.id } : {}, stepid: step.id });
|
|
18655
|
+
return { ok: true, summary: `Listed ${selected.length} stored workflow run${selected.length === 1 ? "" : "s"} from local memory.`, details: { runs: selected.map((run) => ({ id: run.id, workflowid: run.workflowid, state: run.state, cursor: run.cursor, startedat: run.startedat, ...run.dryrun === true ? { dryrun: true } : {} })) } };
|
|
18656
|
+
}
|
|
18657
|
+
async function executemcpstep(step) {
|
|
18658
|
+
const session = await memory.getsession();
|
|
18659
|
+
if (!session) throw new Error("No active browser session exists.");
|
|
18660
|
+
const plan = await memory.getplan();
|
|
18661
|
+
const output = await executeaction(step, session, plan, session.tabid, session.origin, await memory.getsettings(), void 0, "plan");
|
|
18662
|
+
return { content: output.summary, ...output.details !== void 0 ? { payload: output.details } : {}, iserror: !output.ok };
|
|
18663
|
+
}
|
|
18664
|
+
async function trybridgelaunch(restart) {
|
|
18665
|
+
const host = "com.wenathlan.devthink";
|
|
18666
|
+
const runtime = chrome.runtime;
|
|
18667
|
+
try {
|
|
18668
|
+
const port = runtime.connectNative?.(host);
|
|
18669
|
+
if (!port) throw new Error("The browser exposes no native messaging api under the current permission set.");
|
|
18670
|
+
const previous = (await memory.getmcpstate())?.bridge;
|
|
18671
|
+
const bridge = restart && previous !== void 0 ? restartbridgeof(previous) : launchbridge({ host, now: Date.now() });
|
|
18672
|
+
port.onDisconnect.addListener(() => {
|
|
18673
|
+
void (async () => {
|
|
18674
|
+
const state = await memory.getmcpstate();
|
|
18675
|
+
if (state?.bridge !== void 0) await memory.setmcpstate({ ...state, bridge: { ...state.bridge, connected: false } });
|
|
18676
|
+
})().catch(() => {
|
|
18677
|
+
});
|
|
18678
|
+
});
|
|
18679
|
+
port.onMessage.addListener((message) => {
|
|
18680
|
+
if (typeof message !== "string") return;
|
|
18681
|
+
void (async () => {
|
|
18682
|
+
port.postMessage(serializeframe(await relaystdin(message)));
|
|
18683
|
+
})().catch(() => {
|
|
18684
|
+
});
|
|
18685
|
+
});
|
|
18686
|
+
return bridge;
|
|
18687
|
+
} catch {
|
|
18688
|
+
return void 0;
|
|
18689
|
+
}
|
|
18690
|
+
}
|
|
18691
|
+
function restartbridgeof(bridge) {
|
|
18692
|
+
return { ...bridge, connected: true, restarts: bridge.restarts + 1, startedat: Date.now() };
|
|
18693
|
+
}
|
|
18694
|
+
async function routemcpframe(client, frame, config) {
|
|
18695
|
+
const session = await memory.getsession();
|
|
18696
|
+
const plan = await memory.getplan();
|
|
18697
|
+
const catalog = buildtoolcatalog();
|
|
18698
|
+
const response = await handleframe({ frame, client, catalog, config, ...session !== void 0 ? { session } : {}, ...plan !== void 0 ? { plan } : {}, origin: session?.origin ?? "", tabid: session?.tabid ?? 0, now: Date.now(), execute: executemcpstep });
|
|
18699
|
+
const now = Date.now();
|
|
18700
|
+
if (frame.method === "initialize") {
|
|
18701
|
+
const clientinfo = frame.params?.clientinfo && typeof frame.params.clientinfo === "object" && !Array.isArray(frame.params.clientinfo) ? frame.params.clientinfo : void 0;
|
|
18702
|
+
const pid = typeof clientinfo?.pid === "number" && Number.isFinite(clientinfo.pid) ? clientinfo.pid : void 0;
|
|
18703
|
+
if (pid !== void 0) {
|
|
18704
|
+
const bridge = (await memory.getmcpstate())?.bridge;
|
|
18705
|
+
await memory.addbridgelaunch({ id: randomid(), host: bridge?.host ?? "com.wenathlan.devthink", pid, restart: false, at: now });
|
|
18706
|
+
}
|
|
18707
|
+
await audit("protocol", `The mcp client ${client.id} completed the initialize handshake${pid !== void 0 ? ` and reported its process id ${pid} for the bridge launch record` : ""}; the server offered protocol version ${String(response.result?.protocolversion ?? "") || "its own"} and the client waits for the pairing approval.`, {});
|
|
18708
|
+
}
|
|
18709
|
+
if (frame.method === "tools/list") await audit("protocol", `The mcp client ${client.id} listed the tool catalog of ${listtools(catalog).tools.length} tools with their json schema inputs; the listing carries no page data.`, {});
|
|
18710
|
+
if (frame.method === "negotiate") {
|
|
18711
|
+
const agreed = response.error === void 0;
|
|
18712
|
+
if (agreed && response.result !== void 0) await memory.setclientcapabilities(client.id, response.result);
|
|
18713
|
+
await audit("protocol", `The mcp client ${client.id} ${agreed ? "negotiated its capability set with the server and the negotiated floor stays stored on its record" : `failed the capability negotiation: ${response.error?.message ?? "the sets did not agree"}`}.`, {});
|
|
18714
|
+
}
|
|
18715
|
+
if (frame.method === "tools/call") {
|
|
18716
|
+
const name = typeof frame.params?.name === "string" ? frame.params.name : "";
|
|
18717
|
+
const code = response.error?.code;
|
|
18718
|
+
const ok = response.error === void 0 && response.result?.iserror !== true;
|
|
18719
|
+
await memory.addtoolcall(toolcallevent({ id: randomid(), clientid: client.id, tool: name, origin: session?.origin ?? "", ok, now, ...code !== void 0 ? { code } : {} }));
|
|
18720
|
+
if (plan !== void 0) {
|
|
18721
|
+
const stepid = typeof frame.params?.stepid === "string" ? frame.params.stepid : "mcp";
|
|
18722
|
+
await memory.setprogress(recordtoolcall(await memory.getprogress(), plan.id, stepid, { clientid: client.id, tool: name, ok, ...code !== void 0 ? { code } : {} }, now));
|
|
18723
|
+
}
|
|
18724
|
+
await audit("tool", `The mcp client ${client.id} called the ${name} tool on ${session?.origin ?? "no origin"} and ${ok ? "it ran behind the consent gates" : `it was refused${code !== void 0 ? ` with the ${code} error` : ""}`}; no payload rides the record.`, {});
|
|
18725
|
+
}
|
|
18726
|
+
return response;
|
|
18727
|
+
}
|
|
18728
|
+
async function processmcpframe(raw, clientid, transport) {
|
|
18729
|
+
const state = await memory.getmcpstate();
|
|
18730
|
+
if (state?.state !== "running") return { jsonrpc: "2.0", id: null, error: rpcerrorof("consentrefused", "The mcp server is not running and no frame is routed.") };
|
|
18731
|
+
const config = await mcpconfigof();
|
|
18732
|
+
if (!config.transports.includes(transport)) return { jsonrpc: "2.0", id: null, error: rpcerrorof("consentrefused", `The ${transport} transport is not allowed by the server configuration.`) };
|
|
18733
|
+
let frame;
|
|
18734
|
+
try {
|
|
18735
|
+
frame = parseframe(raw);
|
|
18736
|
+
} catch {
|
|
18737
|
+
await audit("protocol", "The mcp server refused a wire frame that does not parse as json; the parse error answered the client.", {});
|
|
18738
|
+
return { jsonrpc: "2.0", id: null, error: rpcerrorof("parse", "The wire frame does not parse as json.") };
|
|
18739
|
+
}
|
|
18740
|
+
let client = (await memory.getclients()).find((entry) => entry.id === clientid && entry.disconnectedat === void 0);
|
|
18741
|
+
if (client === void 0) {
|
|
18742
|
+
client = connectclient({ transport, now: Date.now(), ...clientid !== "" ? { id: clientid } : {} });
|
|
18743
|
+
await memory.setclient(client);
|
|
18744
|
+
await audit("protocol", `A new mcp client ${client.id} connected on the ${transport} transport and waits for the pairing approval; unpaired clients never dispatch tools.`, {});
|
|
18745
|
+
}
|
|
18746
|
+
const previous = mcpclientchains.get(client.id) ?? Promise.resolve();
|
|
18747
|
+
const task = previous.catch(() => void 0).then(async () => await routemcpframe(client, frame, config));
|
|
18748
|
+
mcpclientchains.set(client.id, task);
|
|
18749
|
+
return task;
|
|
18750
|
+
}
|
|
18751
|
+
async function relaystdin(raw) {
|
|
18752
|
+
const now = Date.now();
|
|
18753
|
+
let state = await memory.getmcpstate();
|
|
18754
|
+
if (state?.bridge !== void 0) {
|
|
18755
|
+
let frame;
|
|
18756
|
+
try {
|
|
18757
|
+
frame = parseframe(raw);
|
|
18758
|
+
} catch {
|
|
18759
|
+
frame = void 0;
|
|
18760
|
+
}
|
|
18761
|
+
if (frame !== void 0) {
|
|
18762
|
+
await memory.setmcpstate({ ...state, bridge: relayframe({ bridge: state.bridge, direction: "inbound", frame, now }) });
|
|
18763
|
+
state = await memory.getmcpstate();
|
|
18764
|
+
}
|
|
18765
|
+
}
|
|
18766
|
+
const response = await processmcpframe(raw, "", "stdio");
|
|
18767
|
+
if (state?.bridge !== void 0) await memory.setmcpstate({ ...state, bridge: relayframe({ bridge: state.bridge, direction: "outbound", frame: response, now: Date.now() }) });
|
|
18768
|
+
return response;
|
|
18769
|
+
}
|
|
18770
|
+
var mcpclientchains = /* @__PURE__ */ new Map();
|
|
18241
18771
|
chrome.runtime.onMessage.addListener((message, sender, sendresponse) => {
|
|
18242
18772
|
handlerequest(message, sender).then((value) => sendresponse({ ok: true, value })).catch((error) => sendresponse({ ok: false, error: error instanceof Error ? error.message : String(error) }));
|
|
18243
18773
|
return true;
|