@wenathlan/extension 1.1.45 → 1.1.47

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.
@@ -2,7 +2,7 @@
2
2
  "manifest_version": 3,
3
3
  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnOEjO8Z0PDgQyfvawGcaO2j+o0GLCFTLNj7TkYC/Avo9l2NenMRq7gp90Nfd7E9MViv/OMcCKSYZ5unv12QPRtv31C+a5UQWDFAOP/cH5mwMd6hsayElrSoW8ta+FwFqmr9dIFkn7cQEU3YhZr4Gcbs+ycUHOxVgDA4NBKB0rQ6e9VW5LvTw0isRYUrqM+M72vKxHk9zUIYYn/LGPvottKBYi2GLr0PHSeC2UE+Shmq7vcFIXj6hDjvD4kLJ5sKoUllEcZ1TPuBcnHUQ9ndKA5iktXDQOIJCUJmi7a0YJ2PGg7fvpYfT9k0ai/qZ+pIoRfoOEwE01bPoDn7NjeYnNQIDAQAB",
4
4
  "name": "Devthink",
5
- "version": "1.1.45",
5
+ "version": "1.1.47",
6
6
  "description": "A consent-first bridge for reviewed browser-agent tasks.",
7
7
  "permissions": [
8
8
  "activeTab",
@@ -1,5 +1,37 @@
1
1
  "use strict";
2
2
  (() => {
3
+ // cdpbus.ts
4
+ function breakpointinputof(value) {
5
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
6
+ const entry = value;
7
+ const url = typeof entry.url === "string" && entry.url.trim() ? entry.url.trim() : void 0;
8
+ const line = typeof entry.line === "number" && Number.isInteger(entry.line) && entry.line >= 0 ? entry.line : void 0;
9
+ if (url === void 0 || line === void 0) return void 0;
10
+ const column = typeof entry.column === "number" && Number.isInteger(entry.column) && entry.column >= 0 ? entry.column : void 0;
11
+ const condition = typeof entry.condition === "string" && entry.condition.trim() ? entry.condition.trim() : void 0;
12
+ return { url, line, ...column !== void 0 ? { column } : {}, ...condition !== void 0 ? { condition } : {} };
13
+ }
14
+ function stepmodeof(value) {
15
+ const modes = ["stepover", "stepinto", "stepout", "resume"];
16
+ return typeof value === "string" && modes.includes(value) ? value : void 0;
17
+ }
18
+ function watchexpressionof(value) {
19
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
20
+ const entry = value;
21
+ const expression = typeof entry.expression === "string" && entry.expression.trim() ? entry.expression.trim() : void 0;
22
+ if (expression === void 0) return void 0;
23
+ const scope = typeof entry.scope === "string" && entry.scope.trim() ? entry.scope.trim() : "topframe";
24
+ return { expression, scope };
25
+ }
26
+ function overrideinputof(value) {
27
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
28
+ const entry = value;
29
+ const urlpattern = typeof entry.urlpattern === "string" && entry.urlpattern.trim() ? entry.urlpattern.trim() : void 0;
30
+ const source = typeof entry.source === "string" ? entry.source : void 0;
31
+ if (urlpattern === void 0 || source === void 0 || source.trim().length === 0) return void 0;
32
+ return { urlpattern, source };
33
+ }
34
+
3
35
  // runtimeline.ts
4
36
  var loglevels = ["error", "warn", "info", "log", "debug", "trace"];
5
37
  function levelrank(level) {
@@ -88,9 +120,9 @@
88
120
  }
89
121
 
90
122
  // policy.ts
91
- 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"]);
92
- 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"]);
93
- 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"]);
123
+ 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"]);
124
+ 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"]);
125
+ 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"]);
94
126
  var allowedactions = /* @__PURE__ */ new Set([...sensitiveactions, ...interactionactions, ...readactions]);
95
127
  function parseoptions(step) {
96
128
  if (step.options === void 0) return {};
@@ -2446,6 +2478,206 @@
2446
2478
  }
2447
2479
 
2448
2480
  // extension/pagedebug.ts
2481
+ var harnesskey = "__devthinkcdp";
2482
+ function readharness() {
2483
+ return globalThis[harnesskey];
2484
+ }
2485
+ function writeharness(harness) {
2486
+ if (harness === void 0) delete globalThis[harnesskey];
2487
+ else globalThis[harnesskey] = harness;
2488
+ }
2489
+ var instrumentedmethods = /* @__PURE__ */ new Set(["Runtime.evaluate", "Log.enable", "Debugger.enable", "DOM.enable", "Network.enable", "Page.enable", "DOM.getSnapshot", "Page.getNavigationHistory"]);
2490
+ function cdpstepoptions(step) {
2491
+ let options = {};
2492
+ try {
2493
+ options = parseoptions(step);
2494
+ } catch {
2495
+ options = {};
2496
+ }
2497
+ const teardown = options.teardown && typeof options.teardown === "object" && !Array.isArray(options.teardown) && Array.isArray(options.teardown.revertsteps) ? { revertsteps: options.teardown.revertsteps.filter((item) => typeof item === "string"), resumepolicy: String(options.teardown.resumepolicy ?? "ask") } : void 0;
2498
+ const command = options.command && typeof options.command === "object" && !Array.isArray(options.command) ? options.command : void 0;
2499
+ const watch = options.watch && typeof options.watch === "object" && !Array.isArray(options.watch) ? options.watch : {};
2500
+ const breakpoint = breakpointinputof(options.breakpoint);
2501
+ const expression = watchexpressionof(options.expression);
2502
+ const override = overrideinputof(options.override);
2503
+ return {
2504
+ domains: Array.isArray(options.domains) ? options.domains.filter((domain) => typeof domain === "string") : [],
2505
+ ...teardown !== void 0 ? { teardown } : {},
2506
+ ...command !== void 0 && typeof command.method === "string" ? { command: { method: command.method, ...command.params && typeof command.params === "object" && !Array.isArray(command.params) ? { params: command.params } : {}, ...typeof command.resultpath === "string" ? { resultpath: command.resultpath } : {} } } : {},
2507
+ events: Array.isArray(options.events) ? options.events.flatMap((rule) => {
2508
+ const parsed = rule && typeof rule === "object" && !Array.isArray(rule) ? rule : void 0;
2509
+ if (!parsed || typeof parsed.domain !== "string" || typeof parsed.event !== "string") return [];
2510
+ return [{ domain: parsed.domain, event: parsed.event, ...typeof parsed.match === "string" ? { match: parsed.match } : {} }];
2511
+ }) : [],
2512
+ watchwindow: typeof watch.window === "number" && Number.isFinite(watch.window) && watch.window >= 0 ? watch.window : 0,
2513
+ ...breakpoint !== void 0 ? { breakpoint } : {},
2514
+ ...options.mode !== void 0 && stepmodeof(options.mode) !== void 0 ? { mode: options.mode } : {},
2515
+ ...expression !== void 0 ? { expression } : {},
2516
+ ...override !== void 0 ? { override } : {}
2517
+ };
2518
+ }
2519
+ function domstate() {
2520
+ return { url: location.href, title: document.title, nodes: document.querySelectorAll("*").length, forms: document.forms.length };
2521
+ }
2522
+ async function runcdpstep(step) {
2523
+ const options = cdpstepoptions(step);
2524
+ if (step.kind === "attachcdp") {
2525
+ const existing = readharness();
2526
+ if (existing) {
2527
+ for (const detach of existing.hooks) detach();
2528
+ }
2529
+ const harness = { domains: options.domains, breakpoints: [], overrides: [], events: [], hooks: [] };
2530
+ if (options.domains.includes("Log") || options.domains.includes("Runtime")) {
2531
+ for (const level of loglevels) {
2532
+ const original = console[level];
2533
+ const hooked = (...args) => {
2534
+ try {
2535
+ original.apply(console, args);
2536
+ } catch {
2537
+ }
2538
+ harness.events.push({ domain: "Log", event: "entryAdded", payload: args.map((arg) => serializearg(arg, 2)).join(" "), at: Date.now() });
2539
+ };
2540
+ console[level] = hooked;
2541
+ harness.hooks.push(() => {
2542
+ console[level] = original;
2543
+ });
2544
+ }
2545
+ }
2546
+ writeharness(harness);
2547
+ const iframes = [...document.querySelectorAll("iframe[src]")].map((frame) => frame.src).filter((src) => src.startsWith("https://"));
2548
+ const serviceworker = "serviceWorker" in navigator && navigator.serviceWorker.controller ? navigator.serviceWorker.controller.scriptURL : void 0;
2549
+ return { ok: true, summary: `Attached the instrumented devtools harness with the reviewed domains ${options.domains.join(", ")} enabled.`, details: { attached: true, domains: [...harness.domains], targets: { iframes, ...serviceworker !== void 0 ? { serviceworker } : {} }, derivation: "The chrome devtools protocol needs the debugger permission, which the manifest gate forbids; the session runs through the page-instrumented harness injected by the scripting api and the iframe and service worker targets derive from the page frame list and controller state." } };
2550
+ }
2551
+ if (step.kind === "detachcdp") {
2552
+ const harness = readharness();
2553
+ if (!harness) return { ok: false, summary: "No instrumented devtools harness is attached to this page." };
2554
+ for (const detach of harness.hooks) detach();
2555
+ const reverted = { breakpoints: harness.breakpoints.length, overrides: harness.overrides.length };
2556
+ writeharness(void 0);
2557
+ return { ok: true, summary: `Detached the instrumented devtools harness cleanly after reverting ${reverted.breakpoints} breakpoint${reverted.breakpoints === 1 ? "" : "s"} and ${reverted.overrides} override${reverted.overrides === 1 ? "" : "s"}.`, details: { detached: true, ...reverted } };
2558
+ }
2559
+ if (step.kind === "cdpcmd") {
2560
+ const harness = readharness();
2561
+ if (!harness) return { ok: false, summary: "No instrumented devtools harness is attached to this page." };
2562
+ const method = options.command?.method ?? "";
2563
+ const params = options.command?.params ?? {};
2564
+ if (!instrumentedmethods.has(method)) return { ok: false, summary: `The reviewed command ${method} reports the uninstrumented error class: the page harness implements ${[...instrumentedmethods].join(", ")} only.`, details: { method, errorclass: "uninstrumented" } };
2565
+ const started = Date.now();
2566
+ try {
2567
+ if (method === "Runtime.evaluate") {
2568
+ const expression = typeof params.expression === "string" ? params.expression : "";
2569
+ const probeurl = typeof params.url === "string" ? params.url : "inline";
2570
+ const scope = params.scope && typeof params.scope === "object" && !Array.isArray(params.scope) ? params.scope : {};
2571
+ const override = harness.overrides.find((spec) => overridematch(spec.urlpattern, probeurl));
2572
+ const source = override !== void 0 && overridematch(override.urlpattern, probeurl) ? override.source : expression;
2573
+ const value = new Function(...Object.keys(scope), `"use strict"; return (${source});`)(...Object.values(scope));
2574
+ let tripped;
2575
+ for (const breakpoint of harness.breakpoints) {
2576
+ if (breakpoint.url !== probeurl) continue;
2577
+ const conditionok = breakpoint.condition === void 0 ? true : Boolean(new Function(...Object.keys(scope), `"use strict"; return (${breakpoint.condition});`)(...Object.values(scope)));
2578
+ if (!conditionok) continue;
2579
+ breakpoint.hits += 1;
2580
+ tripped = { reason: "breakpoint", hitbreakpoint: breakpoint.id, frames: stackframes(new Error().stack ?? ""), scope, cursor: breakpoint.line, lines: Math.max(1, source.split("\n").length) };
2581
+ harness.paused = tripped;
2582
+ break;
2583
+ }
2584
+ const serialized = serializearg(value, 3);
2585
+ harness.events.push({ domain: "Runtime", event: "executionContextDestroyed", payload: serialized.slice(0, 200), at: Date.now() });
2586
+ return { ok: true, summary: `The reviewed command ${method} returned in ${Date.now() - started} milliseconds${tripped !== void 0 ? " and paused the run on the reviewed breakpoint" : ""}.`, details: { method, duration: Date.now() - started, result: { value: serialized }, ...tripped !== void 0 ? { paused: { reason: tripped.reason, hitbreakpoint: tripped.hitbreakpoint, frames: tripped.frames } } : {} } };
2587
+ }
2588
+ if (method === "DOM.getSnapshot") {
2589
+ const state = domstate();
2590
+ return { ok: true, summary: `The reviewed command ${method} returned the dom snapshot of ${state.nodes} nodes in ${Date.now() - started} milliseconds.`, details: { method, duration: Date.now() - started, result: state } };
2591
+ }
2592
+ if (method === "Page.getNavigationHistory") {
2593
+ const state = domstate();
2594
+ return { ok: true, summary: `The reviewed command ${method} returned the page navigation history in ${Date.now() - started} milliseconds.`, details: { method, duration: Date.now() - started, result: { url: state.url, title: state.title } } };
2595
+ }
2596
+ return { ok: true, summary: `The reviewed command ${method} enabled its domain through the instrumented harness in ${Date.now() - started} milliseconds.`, details: { method, duration: Date.now() - started, result: {} } };
2597
+ } catch (error) {
2598
+ return { ok: false, summary: `The reviewed command ${method} failed with the evaluationerror class: ${error instanceof Error ? error.message : String(error)}.`, details: { method, duration: Date.now() - started, errorclass: "evaluationerror" } };
2599
+ }
2600
+ }
2601
+ if (step.kind === "watchcdp") {
2602
+ const harness = readharness();
2603
+ if (!harness) return { ok: false, summary: "No instrumented devtools harness is attached to this page." };
2604
+ const started = Date.now();
2605
+ const navigation = performance.getEntriesByType("navigation")[0];
2606
+ if (harness.domains.includes("Page") && navigation !== void 0 && navigation.loadEventStart > 0) harness.events.push({ domain: "Page", event: "loadEventFired", payload: location.href, at: started });
2607
+ await wait4(options.watchwindow);
2608
+ const observed = harness.events.filter((event) => event.at >= started);
2609
+ return { ok: true, summary: `Observed ${observed.length} domain event${observed.length === 1 ? "" : "s"} for the reviewed window of ${options.watchwindow} milliseconds.`, details: { events: observed, watchwindow: options.watchwindow, derivation: "Domain events derive from the instrumented console hooks and the page performance navigation buffer because no debugger permission exists in the manifest." } };
2610
+ }
2611
+ if (step.kind === "setbreakpoint") {
2612
+ const harness = readharness();
2613
+ if (!harness) return { ok: false, summary: "No instrumented devtools harness is attached to this page." };
2614
+ if (!options.breakpoint) return { ok: false, summary: "The breakpoint input is absent." };
2615
+ const id = `bp-${options.breakpoint.url}-${options.breakpoint.line}-${options.breakpoint.column ?? 0}`;
2616
+ const registered = { id, ...options.breakpoint, hits: 0 };
2617
+ harness.breakpoints.push(registered);
2618
+ return { ok: true, summary: `Registered the reviewed breakpoint at ${options.breakpoint.url}:${options.breakpoint.line}${options.breakpoint.condition !== void 0 ? ` under the condition ${options.breakpoint.condition}` : ""}.`, details: { breakpoint: registered } };
2619
+ }
2620
+ if (step.kind === "stepcode") {
2621
+ const harness = readharness();
2622
+ if (!harness) return { ok: false, summary: "No instrumented devtools harness is attached to this page." };
2623
+ const mode = stepmodeof(options.mode);
2624
+ if (mode === void 0) return { ok: false, summary: "The step code mode is absent." };
2625
+ if (harness.paused === void 0) return { ok: false, summary: "No paused instrumented probe exists to step through; pause on a reviewed breakpoint first." };
2626
+ if (mode === "resume" || mode === "stepout") {
2627
+ const reason = harness.paused.reason;
2628
+ const frames = harness.paused.frames;
2629
+ delete harness.paused;
2630
+ return { ok: true, summary: `The ${mode} mode ${mode === "resume" ? "resumed" : "stepped out of"} the paused probe after ${frames.length} call frame${frames.length === 1 ? "" : "s"}.`, details: { mode, paused: false, reason } };
2631
+ }
2632
+ harness.paused.cursor += 1;
2633
+ const state = domstate();
2634
+ return { ok: true, summary: `The ${mode} mode advanced to line ${harness.paused.cursor} of the paused probe and captured the pause state with ${harness.paused.frames.length} call frame${harness.paused.frames.length === 1 ? "" : "s"} and the dom state.`, details: { mode, paused: true, pausestate: { reason: harness.paused.reason, ...harness.paused.hitbreakpoint !== void 0 ? { hitbreakpoint: harness.paused.hitbreakpoint } : {}, frames: harness.paused.frames, cursor: harness.paused.cursor, dom: state } } };
2635
+ }
2636
+ if (step.kind === "watchexpr") {
2637
+ const harness = readharness();
2638
+ if (!harness) return { ok: false, summary: "No instrumented devtools harness is attached to this page." };
2639
+ if (!options.expression) return { ok: false, summary: "The watch expression input is absent." };
2640
+ if (harness.paused === void 0) return { ok: false, summary: "No paused instrumented probe exists to evaluate the watch expression in; pause on a reviewed breakpoint first." };
2641
+ try {
2642
+ const value = new Function(...Object.keys(harness.paused.scope), `"use strict"; return (${options.expression.expression});`)(...Object.values(harness.paused.scope));
2643
+ return { ok: true, summary: `Evaluated the reviewed watch expression at the pause in the ${options.expression.scope} scope.`, details: { expression: options.expression.expression, scope: options.expression.scope, value: serializearg(value, 3) } };
2644
+ } catch (error) {
2645
+ return { ok: false, summary: `The reviewed watch expression failed with the evaluationerror class: ${error instanceof Error ? error.message : String(error)}.`, details: { errorclass: "evaluationerror" } };
2646
+ }
2647
+ }
2648
+ if (step.kind === "overridescript") {
2649
+ const harness = readharness();
2650
+ if (!harness) return { ok: false, summary: "No instrumented devtools harness is attached to this page." };
2651
+ if (!options.override) return { ok: false, summary: "The script override input is absent." };
2652
+ const id = `ov-${options.override.urlpattern}`;
2653
+ harness.overrides = harness.overrides.filter((spec) => spec.id !== id);
2654
+ harness.overrides.push({ id, urlpattern: options.override.urlpattern, source: options.override.source });
2655
+ try {
2656
+ new Function(options.override.source)();
2657
+ return { ok: true, summary: `Applied the reviewed script fixture for ${options.override.urlpattern} on the current document and on later instrumented evaluations of the pattern.`, details: { override: { id, urlpattern: options.override.urlpattern, applied: true } } };
2658
+ } catch (error) {
2659
+ return { ok: false, summary: `The reviewed script fixture failed with the evaluationerror class: ${error instanceof Error ? error.message : String(error)}.`, details: { errorclass: "evaluationerror" } };
2660
+ }
2661
+ }
2662
+ return { ok: false, summary: "The devtools step is not part of the instrumented family." };
2663
+ }
2664
+ function overridematch(urlpattern, url) {
2665
+ const patternmatch = /^(https:\/\/[^/]+|inline)(\/.*)?$/.exec(urlpattern);
2666
+ const urlmatch = /^(https:\/\/[^/]+|inline)(\/.*)?$/.exec(url);
2667
+ if (!patternmatch || !urlmatch) return false;
2668
+ if (patternmatch[1] !== urlmatch[1]) return false;
2669
+ const patternpath = (patternmatch[2] ?? "/").split("/").filter((segment) => segment.length > 0);
2670
+ const urlpath = (urlmatch[2] ?? "/").split("/").filter((segment) => segment.length > 0);
2671
+ const walk = (patternindex, urlindex) => {
2672
+ if (patternindex >= patternpath.length) return urlindex >= urlpath.length;
2673
+ const segment = patternpath[patternindex];
2674
+ if (segment === "**") return walk(patternindex + 1, urlindex) || urlindex < urlpath.length && walk(patternindex, urlindex + 1);
2675
+ if (urlindex >= urlpath.length) return false;
2676
+ if (segment !== "*" && segment !== urlpath[urlindex]) return false;
2677
+ return walk(patternindex + 1, urlindex + 1);
2678
+ };
2679
+ return walk(0, 0);
2680
+ }
2449
2681
  function debugwatchoptions(step) {
2450
2682
  let options = {};
2451
2683
  try {
@@ -2556,6 +2788,143 @@
2556
2788
  return { ok: true, summary, details: { entries, console: consoleentries, errors, rejections, resources, longtasks, watchwindow: options.window, depth: options.depth, derivation: "Console, error and task watching derives from page-injected listeners and the performance buffers through the scripting api; no debugger permission exists in the manifest." } };
2557
2789
  }
2558
2790
 
2791
+ // extension/pageprofile.ts
2792
+ function profilestepoptions(step) {
2793
+ let options = {};
2794
+ try {
2795
+ options = parseoptions(step);
2796
+ } catch {
2797
+ options = {};
2798
+ }
2799
+ const watch = options.watch && typeof options.watch === "object" && !Array.isArray(options.watch) ? options.watch : {};
2800
+ const flow = options.flow && typeof options.flow === "object" && !Array.isArray(options.flow) ? options.flow : void 0;
2801
+ const heap = options.heap && typeof options.heap === "object" && !Array.isArray(options.heap) ? options.heap : {};
2802
+ const growth = options.growth && typeof options.growth === "object" && !Array.isArray(options.growth) ? options.growth : void 0;
2803
+ const profile = options.profile && typeof options.profile === "object" && !Array.isArray(options.profile) ? options.profile : {};
2804
+ const trace = options.trace && typeof options.trace === "object" && !Array.isArray(options.trace) ? options.trace : {};
2805
+ return {
2806
+ ...flow !== void 0 && typeof flow.prefix === "string" && Array.isArray(flow.steps) && Array.isArray(flow.metrics) ? { flow: { prefix: flow.prefix, steps: flow.steps.filter((item) => typeof item === "string"), metrics: flow.metrics.filter((item) => typeof item === "string") } } : {},
2807
+ watchwindow: typeof watch.window === "number" && Number.isFinite(watch.window) && watch.window >= 0 ? watch.window : 0,
2808
+ heapinterval: typeof heap.interval === "number" && Number.isFinite(heap.interval) && heap.interval >= 0 ? heap.interval : 0,
2809
+ ...growth !== void 0 && typeof growth.slope === "number" ? { growth: { slope: growth.slope, interval: typeof growth.interval === "number" && Number.isFinite(growth.interval) && growth.interval >= 0 ? growth.interval : 0 } } : {},
2810
+ duration: typeof profile.duration === "number" && Number.isFinite(profile.duration) && profile.duration >= 0 ? profile.duration : 0,
2811
+ threshold: typeof options.threshold === "number" && Number.isFinite(options.threshold) && options.threshold >= 0 ? options.threshold : 0,
2812
+ categories: Array.isArray(trace.categories) ? trace.categories.filter((category) => typeof category === "string") : [],
2813
+ ...typeof trace.exporttarget === "string" ? { exporttarget: trace.exporttarget } : {},
2814
+ ...typeof trace.traceid === "string" ? { traceid: trace.traceid } : {},
2815
+ scripts: Array.isArray(options.scripts) ? options.scripts.filter((url) => typeof url === "string") : []
2816
+ };
2817
+ }
2818
+ function categoryof(entry, initiator) {
2819
+ if (entry.entryType === "navigation") return "navigation";
2820
+ if (entry.entryType === "paint" || entry.entryType === "largest-contentful-paint" || entry.entryType === "layout-shift") return "painting";
2821
+ if (entry.entryType === "resource") return initiator === "fetch" || initiator === "xmlhttprequest" ? "network" : "loading";
2822
+ return "scripting";
2823
+ }
2824
+ async function collectentries(watchwindow, types) {
2825
+ const rows = [];
2826
+ for (const type of ["navigation", "paint", "mark", "measure", "resource", "longtask"]) {
2827
+ for (const entry of performance.getEntriesByType(type)) {
2828
+ rows.push({ name: entry.name, type: entry.entryType, start: entry.startTime, duration: entry.duration, ...type === "resource" ? { initiator: entry.initiatorType } : {} });
2829
+ }
2830
+ }
2831
+ if (types.length > 0) {
2832
+ await new Promise((resolve) => {
2833
+ const observer = new PerformanceObserver((list) => {
2834
+ for (const entry of list.getEntries()) rows.push({ name: entry.name, type: entry.entryType, start: entry.startTime, duration: entry.duration });
2835
+ });
2836
+ observer.observe({ entryTypes: types, buffered: true });
2837
+ window.setTimeout(() => {
2838
+ observer.disconnect();
2839
+ resolve();
2840
+ }, Math.max(0, watchwindow));
2841
+ });
2842
+ } else {
2843
+ await new Promise((resolve) => window.setTimeout(resolve, Math.max(0, watchwindow)));
2844
+ }
2845
+ return rows;
2846
+ }
2847
+ function heapsample() {
2848
+ const memory = performance.memory;
2849
+ return { usedbytes: memory?.usedJSHeapSize ?? 0, limitbytes: memory?.jsHeapSizeLimit ?? memory?.totalJSHeapSize ?? 0, nodecount: document.querySelectorAll("*").length };
2850
+ }
2851
+ async function runprofilestep(step) {
2852
+ const options = profilestepoptions(step);
2853
+ if (step.kind === "measureflow") {
2854
+ if (!options.flow || options.watchwindow <= 0) return { ok: false, summary: "The reviewed flow spec with its watch window is absent." };
2855
+ const started = performance.now();
2856
+ for (const stepid of options.flow.steps) performance.mark(`${options.flow.prefix}:${stepid}:start`);
2857
+ const entries = await collectentries(options.watchwindow, ["largest-contentful-paint", "first-input", "event", "longtask"]);
2858
+ for (const stepid of options.flow.steps) performance.mark(`${options.flow.prefix}:${stepid}:end`);
2859
+ for (const stepid of options.flow.steps) performance.measure(`${options.flow.prefix}:${stepid}`, `${options.flow.prefix}:${stepid}:start`, `${options.flow.prefix}:${stepid}:end`);
2860
+ return { ok: true, summary: `Marked the start and end of ${options.flow.steps.length} step${options.flow.steps.length === 1 ? "" : "s"} of the flow ${options.flow.prefix} and collected ${entries.length} performance entr${entries.length === 1 ? "y" : "ies"} for the reviewed window of ${options.watchwindow} milliseconds.`, details: { entries, watchwindow: options.watchwindow, started, derivation: "Flow measurement derives from the performance timeline buffers and the injected marks through the scripting api; no debugger permission exists in the manifest." } };
2861
+ }
2862
+ if (step.kind === "heapshot") {
2863
+ const sample = heapsample();
2864
+ return { ok: true, summary: `Captured the on demand heap sample of ${sample.usedbytes} used bytes against the ${sample.limitbytes} byte limit with ${sample.nodecount} dom node${sample.nodecount === 1 ? "" : "s"}.`, details: { ...sample, derivation: "Heap bytes derive from the page performance memory buffer and the node count from the dom because no heap profiler exists without the debugger permission." } };
2865
+ }
2866
+ if (step.kind === "trackmemory") {
2867
+ if (!options.growth) return { ok: false, summary: "The reviewed growth slope is absent." };
2868
+ const sample = heapsample();
2869
+ return { ok: true, summary: `Took the heap sample of ${sample.usedbytes} used bytes beside the step for the growth tracking of slope ${options.growth.slope} bytes per millisecond.`, details: { ...sample, slope: options.growth.slope, interval: options.growth.interval, derivation: "Growth samples derive from the page performance memory buffer beside every step of the run." } };
2870
+ }
2871
+ if (step.kind === "profilecpu") {
2872
+ if (options.duration <= 0) return { ok: false, summary: "The reviewed cpu profile duration is absent." };
2873
+ const started = performance.now();
2874
+ const entries = await collectentries(options.duration, ["longtask", "event", "first-input"]);
2875
+ const samples = entries.filter((entry) => entry.type === "longtask" || entry.type === "event" || entry.type === "first-input").map((entry) => ({ name: entry.name || entry.type, time: entry.duration }));
2876
+ return { ok: true, summary: `Profiled the cpu window of ${options.duration} milliseconds with ${samples.length} sample${samples.length === 1 ? "" : "s"} from the long task and event timing buffers.`, details: { samples, duration: options.duration, started, derivation: "Cpu samples derive from the long task attribution and event timing buffers because no sampling profiler exists without the debugger permission." } };
2877
+ }
2878
+ if (step.kind === "watchshifts") {
2879
+ if (options.watchwindow <= 0) return { ok: false, summary: "The reviewed layout shift window is absent." };
2880
+ const shifts = [];
2881
+ await new Promise((resolve) => {
2882
+ const observer = new PerformanceObserver((list) => {
2883
+ for (const entry of list.getEntries()) {
2884
+ const shift = entry;
2885
+ const selectors = (shift.sources ?? []).flatMap((source) => source.node instanceof Element ? [source.node.tagName.toLowerCase() + (source.node.id ? `#${source.node.id}` : "")] : []);
2886
+ const score = typeof shift.value === "number" ? shift.value : 0;
2887
+ if (options.threshold > 0 && score < options.threshold) continue;
2888
+ shifts.push({ score, starttime: shift.startTime, selectors });
2889
+ }
2890
+ });
2891
+ observer.observe({ entryTypes: ["layout-shift"], buffered: true });
2892
+ window.setTimeout(() => {
2893
+ observer.disconnect();
2894
+ resolve();
2895
+ }, options.watchwindow);
2896
+ });
2897
+ return { ok: true, summary: `Watched ${shifts.length} layout shift${shifts.length === 1 ? "" : "s"} for the reviewed window of ${options.watchwindow} milliseconds${options.threshold > 0 ? ` with the score threshold ${options.threshold}` : ""}.`, details: { shifts, watchwindow: options.watchwindow, derivation: "Layout shifts derive from the performance layout-shift buffer with the impacted element selectors of the shift sources." } };
2898
+ }
2899
+ if (step.kind === "traceload") {
2900
+ if (options.categories.length === 0 || options.watchwindow <= 0) return { ok: false, summary: "The reviewed trace categories or window are absent." };
2901
+ const started = performance.now();
2902
+ const entries = await collectentries(options.watchwindow, ["largest-contentful-paint", "first-input", "event", "longtask", "layout-shift"]);
2903
+ const events4 = entries.filter((entry) => options.categories.includes(categoryof({ name: entry.name, entryType: entry.type, startTime: entry.start, duration: entry.duration }, entry.initiator))).map((entry) => ({ name: entry.name, category: categoryof({ name: entry.name, entryType: entry.type, startTime: entry.start, duration: entry.duration }, entry.initiator), offset: Math.round(entry.start - started) }));
2904
+ return { ok: true, summary: `Recorded ${events4.length} trace event${events4.length === 1 ? "" : "s"} of the reviewed categories ${options.categories.join(", ")} for the window of ${options.watchwindow} milliseconds and derived the exportable trace file.`, details: { events: events4, categories: options.categories, watchwindow: options.watchwindow, started, ...options.exporttarget !== void 0 ? { exporttarget: options.exporttarget } : {}, derivation: "The trace file derives from the performance timeline entries of the reviewed categories; it is not the devtools binary trace format because no debugger permission exists in the manifest." } };
2905
+ }
2906
+ if (step.kind === "capturesourcemaps") {
2907
+ const scripts = [];
2908
+ for (const element of document.querySelectorAll("script[src]")) {
2909
+ const src = element.src;
2910
+ if (!src.startsWith(location.origin)) continue;
2911
+ if (options.scripts.length > 0 && !options.scripts.includes(src)) continue;
2912
+ let mapurl;
2913
+ try {
2914
+ const response = await fetch(src, { credentials: "same-origin" });
2915
+ const source = await response.text();
2916
+ const match = /[#@]\s*sourceMappingURL=(\S+)/.exec(source);
2917
+ if (match !== null && match[1] !== void 0) mapurl = new URL(match[1], src).toString();
2918
+ } catch {
2919
+ }
2920
+ scripts.push({ url: src, ...mapurl !== void 0 ? { mapurl } : {} });
2921
+ }
2922
+ const withmaps = scripts.filter((script) => script.mapurl !== void 0);
2923
+ return { ok: true, summary: `Read the sourceMappingURL declarations of ${scripts.length} same origin script${scripts.length === 1 ? "" : "s"} of ${location.origin} and found ${withmaps.length} map declaration${withmaps.length === 1 ? "" : "s"}; the script sources stay in the page bridge and only the map urls leave it.`, details: { scripts, origin: location.origin, derivation: "Source map declarations are read by re-fetching the loaded same origin scripts of the page; cross origin scripts stay outside the capture and no map content enters the page bridge." } };
2924
+ }
2925
+ return { ok: false, summary: "The profiling step is not part of the instrumented family." };
2926
+ }
2927
+
2559
2928
  // extension/pageforms.ts
2560
2929
  function matchfield(fields, match) {
2561
2930
  const key = match.mode === "label" ? "label" : match.mode === "placeholder" ? "placeholder" : match.mode === "arialabel" ? "arialabel" : "name";
@@ -3429,6 +3798,8 @@
3429
3798
  var detectionkinds = /* @__PURE__ */ new Set(["detectlists", "detecttables", "detectinfinitescroll", "detectvirtual", "detectlazy", "detectsticky", "detectscrolllock", "countpages", "classifypage", "fingerprintsection", "readscrollpos"]);
3430
3799
  var watchstepkinds = /* @__PURE__ */ new Set(["watchmutate", "watchbanner", "watchfocus", "waitquiet", "readjson", "diffsnapshots", "deriveselector"]);
3431
3800
  var debugstepkinds = /* @__PURE__ */ new Set(["watchconsole", "watcherrors", "watchtasks"]);
3801
+ var cdpstepkinds = /* @__PURE__ */ new Set(["attachcdp", "detachcdp", "cdpcmd", "watchcdp", "setbreakpoint", "stepcode", "watchexpr", "overridescript"]);
3802
+ var profilestepkinds = /* @__PURE__ */ new Set(["measureflow", "heapshot", "trackmemory", "profilecpu", "watchshifts", "traceload", "capturesourcemaps"]);
3432
3803
  var navstepkinds = /* @__PURE__ */ new Set(["waitload", "waiturl", "followlink", "spanav", "spawait", "rewritequery", "setfragment", "stopnav", "prefetch", "preconnect", "printpdf"]);
3433
3804
  var formkinds = /* @__PURE__ */ new Set(["fillform", "filllabel", "fillplaceholder", "detectfields", "generatevalues", "readerrors", "skiphoneypot", "detectlogin", "detecttemplate", "handoffcaptcha", "asksubmit", "submitform", "consentpassword", "attachfile"]);
3434
3805
  var wizardkinds = /* @__PURE__ */ new Set(["runwizard", "selectchain", "picktypeahead", "pickdate", "fillcard", "fillcode"]);
@@ -3495,6 +3866,8 @@
3495
3866
  else if (detectionkinds.has(step.kind)) result = runpagedetection(step, element, rootdocument);
3496
3867
  else if (watchstepkinds.has(step.kind)) result = runpagewatch(step, element, rootdocument);
3497
3868
  else if (debugstepkinds.has(step.kind)) return await rundebugwatch(step);
3869
+ else if (cdpstepkinds.has(step.kind)) return await runcdpstep(step);
3870
+ else if (profilestepkinds.has(step.kind)) return await runprofilestep(step);
3498
3871
  else if (mutatingkinds.has(step.kind)) result = runpageaction(step, element);
3499
3872
  else {
3500
3873
  if (!element) return { ok: false, summary: "Action target is no longer available." };