@wenathlan/extension 1.1.44 → 1.1.45
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 +5 -4
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +438 -4
- package/dist/index.js.map +4 -4
- package/dist/memory.d.ts +39 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/policy.d.ts +23 -1
- package/dist/policy.d.ts.map +1 -1
- package/dist/protocol.d.ts +31 -2
- package/dist/protocol.d.ts.map +1 -1
- package/dist/runtimeline.d.ts +122 -0
- package/dist/runtimeline.d.ts.map +1 -0
- package/dist/types.d.ts +134 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/extension/dist/background.js +488 -7
- package/extension/dist/background.js.map +4 -4
- package/extension/dist/manifest.json +1 -1
- package/extension/dist/pagebridge.js +205 -5
- package/extension/dist/pagebridge.js.map +4 -4
- 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 +152 -1
- package/extension/dist/sidepanel.js.map +2 -2
- package/extension/dist/style.css +4 -0
- package/extension/manifest.json +1 -1
- package/package.json +1 -1
|
@@ -1042,6 +1042,103 @@ var sessionmemory = class {
|
|
|
1042
1042
|
if (live.length !== records.length) await this.adapter.set("ratelimits", live);
|
|
1043
1043
|
return live;
|
|
1044
1044
|
}
|
|
1045
|
+
/** Stores one run timeline entry; the user configured timeline retention window expires the oldest entries while their level counts survive in the per run level summaries. */
|
|
1046
|
+
async addtimelineentry(entry) {
|
|
1047
|
+
const records = await this.gettimeline();
|
|
1048
|
+
const combined = [entry, ...records];
|
|
1049
|
+
const retention = (await this.getsettings())?.timelineretention;
|
|
1050
|
+
if (retention === void 0) {
|
|
1051
|
+
await this.adapter.set("timelineentries", combined);
|
|
1052
|
+
return;
|
|
1053
|
+
}
|
|
1054
|
+
const kept = combined.slice(0, retention);
|
|
1055
|
+
const expired = combined.slice(retention);
|
|
1056
|
+
if (expired.length > 0) {
|
|
1057
|
+
const expiredcounts = /* @__PURE__ */ new Map();
|
|
1058
|
+
for (const item of expired) {
|
|
1059
|
+
const counts = expiredcounts.get(item.runid) ?? {};
|
|
1060
|
+
counts[item.level] = (counts[item.level] ?? 0) + 1;
|
|
1061
|
+
expiredcounts.set(item.runid, counts);
|
|
1062
|
+
}
|
|
1063
|
+
for (const [runid, counts] of expiredcounts) await this.mergelevelsummary(runid, counts, Date.now());
|
|
1064
|
+
}
|
|
1065
|
+
await this.adapter.set("timelineentries", kept);
|
|
1066
|
+
}
|
|
1067
|
+
/** Returns every stored run timeline entry, newest first. */
|
|
1068
|
+
async gettimeline() {
|
|
1069
|
+
return await this.adapter.get("timelineentries") ?? [];
|
|
1070
|
+
}
|
|
1071
|
+
/** Returns the run timeline entries filtered by run, level and step id. */
|
|
1072
|
+
async listtimeline(filter) {
|
|
1073
|
+
const records = await this.gettimeline();
|
|
1074
|
+
return records.filter((item) => (filter.runid === void 0 || item.runid === filter.runid) && (filter.level === void 0 || item.level === filter.level) && (filter.stepid === void 0 || item.stepid === filter.stepid));
|
|
1075
|
+
}
|
|
1076
|
+
/** Stores one captured javascript error record with its stack frames, source url and line. */
|
|
1077
|
+
async adderrorrecord(record2) {
|
|
1078
|
+
const records = await this.adapter.get("errorrecords") ?? [];
|
|
1079
|
+
await this.adapter.set("errorrecords", [record2, ...records]);
|
|
1080
|
+
}
|
|
1081
|
+
/** Returns every stored error record, newest first. */
|
|
1082
|
+
async geterrorrecords() {
|
|
1083
|
+
return await this.adapter.get("errorrecords") ?? [];
|
|
1084
|
+
}
|
|
1085
|
+
/** Stores one captured unhandled rejection record with its reason and stack frames. */
|
|
1086
|
+
async addrejectionrecord(record2) {
|
|
1087
|
+
const records = await this.adapter.get("rejectionrecords") ?? [];
|
|
1088
|
+
await this.adapter.set("rejectionrecords", [record2, ...records]);
|
|
1089
|
+
}
|
|
1090
|
+
/** Returns every stored rejection record, newest first. */
|
|
1091
|
+
async getrejectionrecords() {
|
|
1092
|
+
return await this.adapter.get("rejectionrecords") ?? [];
|
|
1093
|
+
}
|
|
1094
|
+
/** Stores one captured long task entry with its duration, start time and attribution names. */
|
|
1095
|
+
async addlongtask(record2) {
|
|
1096
|
+
const records = await this.adapter.get("longtasks") ?? [];
|
|
1097
|
+
await this.adapter.set("longtasks", [record2, ...records]);
|
|
1098
|
+
}
|
|
1099
|
+
/** Returns every stored long task entry, newest first. */
|
|
1100
|
+
async getlongtasks() {
|
|
1101
|
+
return await this.adapter.get("longtasks") ?? [];
|
|
1102
|
+
}
|
|
1103
|
+
/** Stores one console diff result between two runs, replacing the previous one. */
|
|
1104
|
+
async addconsolediff(diff) {
|
|
1105
|
+
return this.adapter.set("consolediff", diff);
|
|
1106
|
+
}
|
|
1107
|
+
/** Returns the one stored console diff result. */
|
|
1108
|
+
async getdiff() {
|
|
1109
|
+
return this.adapter.get("consolediff");
|
|
1110
|
+
}
|
|
1111
|
+
/** Stores one log rotation target record with its overflow entry counts, replacing the previous record of that target and run. */
|
|
1112
|
+
async addrotationtarget(record2) {
|
|
1113
|
+
const records = (await this.adapter.get("rotationtargets") ?? []).filter((item) => !(item.target === record2.target && item.runid === record2.runid));
|
|
1114
|
+
await this.adapter.set("rotationtargets", [record2, ...records]);
|
|
1115
|
+
}
|
|
1116
|
+
/** Returns every stored rotation target record with its overflow entry counts, newest first. */
|
|
1117
|
+
async getrotationtargets() {
|
|
1118
|
+
return await this.adapter.get("rotationtargets") ?? [];
|
|
1119
|
+
}
|
|
1120
|
+
/** Stores one console capture consent decision per origin; the approved decision persists so console watching on that origin prompts once. */
|
|
1121
|
+
async setconsoleconsent(consent) {
|
|
1122
|
+
const records = (await this.adapter.get("consoleconsents") ?? []).filter((item) => item.id !== consent.id);
|
|
1123
|
+
await this.adapter.set("consoleconsents", [consent, ...records]);
|
|
1124
|
+
}
|
|
1125
|
+
/** Returns every console capture consent decision, newest first. */
|
|
1126
|
+
async getconsoleconsents() {
|
|
1127
|
+
return await this.adapter.get("consoleconsents") ?? [];
|
|
1128
|
+
}
|
|
1129
|
+
/** Merges expired entry counts into the per run level count summary that survives the retention window. */
|
|
1130
|
+
async mergelevelsummary(runid, counts, now) {
|
|
1131
|
+
const records = await this.getlevelsummaries();
|
|
1132
|
+
const existing = records.find((item) => item.runid === runid);
|
|
1133
|
+
const merged = { ...existing?.counts ?? {} };
|
|
1134
|
+
for (const [level, count] of Object.entries(counts)) merged[level] = (merged[level] ?? 0) + count;
|
|
1135
|
+
const updated = { runid, counts: merged, at: now };
|
|
1136
|
+
await this.adapter.set("levelsummaries", [updated, ...records.filter((item) => item.runid !== runid)]);
|
|
1137
|
+
}
|
|
1138
|
+
/** Returns every per run level count summary, newest first. */
|
|
1139
|
+
async getlevelsummaries() {
|
|
1140
|
+
return await this.adapter.get("levelsummaries") ?? [];
|
|
1141
|
+
}
|
|
1045
1142
|
};
|
|
1046
1143
|
function mediakindof(record2) {
|
|
1047
1144
|
if ("pages" in record2) return "pdf";
|
|
@@ -2043,10 +2140,107 @@ ${file.content}\r
|
|
|
2043
2140
|
return { chunks, boundary, bytes: chunks.reduce((total, chunk) => total + chunk.length, 0) };
|
|
2044
2141
|
}
|
|
2045
2142
|
|
|
2143
|
+
// runtimeline.ts
|
|
2144
|
+
var timelinekinds = ["watchconsole", "watcherrors", "watchtasks"];
|
|
2145
|
+
var loglevels = ["error", "warn", "info", "log", "debug", "trace"];
|
|
2146
|
+
var timelinesources = ["console", "error", "rejection", "resource", "longtask", "network"];
|
|
2147
|
+
function attachtimeline(input) {
|
|
2148
|
+
return { runid: input.runid, origin: input.origin, stepids: [...input.stepids], attachedat: input.now, entries: [] };
|
|
2149
|
+
}
|
|
2150
|
+
function spamdetect(entries, rule) {
|
|
2151
|
+
const collapsed = [];
|
|
2152
|
+
const counts = /* @__PURE__ */ new Map();
|
|
2153
|
+
for (const entry of entries) {
|
|
2154
|
+
if (rule.pattern !== "" && !entry.message.includes(rule.pattern)) {
|
|
2155
|
+
collapsed.push({ ...entry, repeat: 1 });
|
|
2156
|
+
continue;
|
|
2157
|
+
}
|
|
2158
|
+
const key = `${entry.level}|${entry.source}|${entry.message}`;
|
|
2159
|
+
const previous = collapsed[collapsed.length - 1];
|
|
2160
|
+
if (previous && previous.repeat !== void 0 && `${previous.level}|${previous.source}|${previous.message}` === key && entry.time - previous.time <= rule.windowsize) {
|
|
2161
|
+
previous.repeat += 1;
|
|
2162
|
+
continue;
|
|
2163
|
+
}
|
|
2164
|
+
collapsed.push({ ...entry, repeat: 1 });
|
|
2165
|
+
}
|
|
2166
|
+
for (const entry of collapsed) {
|
|
2167
|
+
if (entry.repeat > 1) counts.set(`${entry.level}|${entry.source}|${entry.message}`, entry.repeat);
|
|
2168
|
+
}
|
|
2169
|
+
const flagged = [...counts.entries()].filter(([, count]) => count > rule.collapse).map(([key, count]) => ({ message: key.split("|").slice(2).join("|"), count }));
|
|
2170
|
+
return { entries: collapsed, flagged };
|
|
2171
|
+
}
|
|
2172
|
+
function rotatelogs(entries, rule) {
|
|
2173
|
+
if (entries.length <= rule.maxentries) return { kept: [...entries], overflow: [] };
|
|
2174
|
+
const kept = entries.slice(entries.length - rule.maxentries);
|
|
2175
|
+
const overflow = entries.slice(0, entries.length - rule.maxentries);
|
|
2176
|
+
return { kept, overflow };
|
|
2177
|
+
}
|
|
2178
|
+
function timelinecounts(entries) {
|
|
2179
|
+
const counts = {};
|
|
2180
|
+
for (const level of loglevels) counts[level] = 0;
|
|
2181
|
+
for (const entry of entries) counts[entry.level] = (counts[entry.level] ?? 0) + 1;
|
|
2182
|
+
return counts;
|
|
2183
|
+
}
|
|
2184
|
+
function blockingduration(tasks, stepid, window2) {
|
|
2185
|
+
const inside = tasks.filter((task) => task.starttime >= window2.startedat && task.starttime <= window2.endedat);
|
|
2186
|
+
return { stepid, blocking: inside.reduce((total, task) => total + task.duration, 0), tasks: inside.length };
|
|
2187
|
+
}
|
|
2188
|
+
function netfailureentryof(input) {
|
|
2189
|
+
const exchange = input.exchange;
|
|
2190
|
+
if (exchange.errorclass === void 0 && exchange.status < 400) return null;
|
|
2191
|
+
return { id: input.id, runid: exchange.runid, stepid: exchange.stepid, url: exchange.url, status: exchange.status, errorclass: exchange.errorclass ?? "httperror", correlationid: exchange.correlationid, at: input.at };
|
|
2192
|
+
}
|
|
2193
|
+
function watcherdetached(input) {
|
|
2194
|
+
for (const navigation of input.navigations) {
|
|
2195
|
+
if (navigation >= input.startedat && navigation <= input.startedat + input.lifetime) return { detached: true, at: navigation };
|
|
2196
|
+
}
|
|
2197
|
+
return { detached: false };
|
|
2198
|
+
}
|
|
2199
|
+
function consolediff(input) {
|
|
2200
|
+
const base = input.baselines;
|
|
2201
|
+
const target = input.targetlines;
|
|
2202
|
+
const basemap = /* @__PURE__ */ new Map();
|
|
2203
|
+
for (const line of base) basemap.set(line, (basemap.get(line) ?? 0) + 1);
|
|
2204
|
+
const targetmap = /* @__PURE__ */ new Map();
|
|
2205
|
+
for (const line of target) targetmap.set(line, (targetmap.get(line) ?? 0) + 1);
|
|
2206
|
+
const lines = [];
|
|
2207
|
+
const added = [];
|
|
2208
|
+
const removed = [];
|
|
2209
|
+
const repeated = [];
|
|
2210
|
+
for (const [line, count] of targetmap) {
|
|
2211
|
+
const basecount = basemap.get(line) ?? 0;
|
|
2212
|
+
if (basecount === 0) {
|
|
2213
|
+
for (let index = 0; index < count; index += 1) {
|
|
2214
|
+
lines.push({ kind: "added", text: line });
|
|
2215
|
+
added.push(line);
|
|
2216
|
+
}
|
|
2217
|
+
continue;
|
|
2218
|
+
}
|
|
2219
|
+
const share = Math.min(basecount, count);
|
|
2220
|
+
for (let index = 0; index < share; index += 1) {
|
|
2221
|
+
lines.push({ kind: "repeated", text: line, count: share });
|
|
2222
|
+
repeated.push(line);
|
|
2223
|
+
}
|
|
2224
|
+
for (let index = share; index < count; index += 1) {
|
|
2225
|
+
lines.push({ kind: "added", text: line });
|
|
2226
|
+
added.push(line);
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
for (const [line, count] of basemap) {
|
|
2230
|
+
const targetcount = targetmap.get(line) ?? 0;
|
|
2231
|
+
const missing = Math.max(0, count - targetcount);
|
|
2232
|
+
for (let index = 0; index < missing; index += 1) {
|
|
2233
|
+
lines.push({ kind: "removed", text: line });
|
|
2234
|
+
removed.push(line);
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
return { base: input.baseid, target: input.targetid, lines, added: added.length, removed: removed.length, repeated: repeated.length, at: input.now };
|
|
2238
|
+
}
|
|
2239
|
+
|
|
2046
2240
|
// policy.ts
|
|
2047
2241
|
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"]);
|
|
2048
2242
|
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"]);
|
|
2049
|
-
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"]);
|
|
2243
|
+
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"]);
|
|
2050
2244
|
var allowedactions = /* @__PURE__ */ new Set([...sensitiveactions, ...interactionactions, ...readactions]);
|
|
2051
2245
|
var watchactions = /* @__PURE__ */ new Set(["watchmutate", "watchbanner", "watchfocus", "watchtab"]);
|
|
2052
2246
|
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"]);
|
|
@@ -2062,6 +2256,7 @@ var httpactions = /* @__PURE__ */ new Set(["fetchurl", "parsejson", "parsehtml",
|
|
|
2062
2256
|
var socketactions = /* @__PURE__ */ new Set(["opensocket", "sendmessage", "waitmessage", "subscribesse", "longpoll"]);
|
|
2063
2257
|
var netwatchactions = /* @__PURE__ */ new Set(["watchrequests", "readheaders", "capturebodies", "mapapi", "extractapi"]);
|
|
2064
2258
|
var controlactions = /* @__PURE__ */ new Set(["blockrequest", "mockresponse", "rewriteheaders", "setcookies", "readcookies", "clearcookies", "authflow", "saveapikey", "routeproxy", "postform", "postfiles"]);
|
|
2259
|
+
var debugactions = /* @__PURE__ */ new Set(["watchconsole", "watcherrors", "watchtasks"]);
|
|
2065
2260
|
var credentialheaders = /* @__PURE__ */ new Set(["authorization", "proxy-authorization", "cookie", "cookie2", "set-cookie", "api-key", "x-api-key", "x-auth-token", "x-session-token", "proxy-authorization"]);
|
|
2066
2261
|
var fieldkinds = ["text", "email", "phone", "date", "number", "select", "check", "radio", "file", "password", "card", "code"];
|
|
2067
2262
|
var layoutmutationactions = /* @__PURE__ */ new Set(["grouptabs", "colorgroup", "collapsegroup", "savelayout", "restorelayout"]);
|
|
@@ -2077,6 +2272,9 @@ function hostpattern(origin) {
|
|
|
2077
2272
|
if (parsed.protocol !== "https:") throw new Error("Only HTTPS origins can be granted.");
|
|
2078
2273
|
return `${parsed.origin}/*`;
|
|
2079
2274
|
}
|
|
2275
|
+
function isdebugkind(kind) {
|
|
2276
|
+
return debugactions.has(kind);
|
|
2277
|
+
}
|
|
2080
2278
|
function actionrisk(kind) {
|
|
2081
2279
|
if (!allowedactions.has(kind)) throw new Error("Unsupported browser action.");
|
|
2082
2280
|
if (sensitiveactions.has(kind)) return "sensitive";
|
|
@@ -3302,6 +3500,81 @@ function ratelimitbudgetallowed(wait, budget) {
|
|
|
3302
3500
|
if (wait !== void 0 && budget !== void 0 && wait > budget) return { allowed: false, reason: `The rate limit wait of ${wait} milliseconds exceeds the reviewed budget of ${budget} milliseconds; review a wider budget or submit later.` };
|
|
3303
3501
|
return { allowed: true };
|
|
3304
3502
|
}
|
|
3503
|
+
function timelinegate(session, tabid2, origin, now) {
|
|
3504
|
+
if (!session || session.stoppedat) return { allowed: false, reason: "No active browser session exists for the timeline capture." };
|
|
3505
|
+
if (session.expiresat <= now) return { allowed: false, reason: "The browser session has expired and cannot capture the timeline." };
|
|
3506
|
+
if (session.pausedat) return { allowed: false, reason: "The browser session is paused and cannot capture the timeline." };
|
|
3507
|
+
if (session.tabid !== tabid2) return { allowed: false, reason: `The timeline capture needs the run tab ${session.tabid} and refuses tab ${tabid2}.` };
|
|
3508
|
+
if (!origingranted(session, origin)) return { allowed: false, reason: `The timeline capture of ${origin} needs the session origin grants first.` };
|
|
3509
|
+
return { allowed: true };
|
|
3510
|
+
}
|
|
3511
|
+
function consoleconsentcovers(origin, consents) {
|
|
3512
|
+
if (consents.some((consent) => consent.origin === origin && consent.approved === true)) return { allowed: true };
|
|
3513
|
+
return { allowed: false, reason: `Console capture on ${origin} needs the reviewed console consent first; approve the prompt in the review panel.` };
|
|
3514
|
+
}
|
|
3515
|
+
function stackgate(session, origin) {
|
|
3516
|
+
if (!origingranted(session, origin)) return { allowed: false, reason: `Stack capture of ${origin} stays outside the session origin grants.` };
|
|
3517
|
+
return { allowed: true };
|
|
3518
|
+
}
|
|
3519
|
+
function debugwaitbudgetallowed(watchwindow, wait) {
|
|
3520
|
+
if (watchwindow !== void 0 && (typeof watchwindow !== "number" || !Number.isFinite(watchwindow) || watchwindow < 0)) return { allowed: false, reason: "The debug watch window must be zero or a positive number of milliseconds." };
|
|
3521
|
+
if (wait !== void 0 && (typeof wait !== "number" || !Number.isFinite(wait) || wait < 0)) return { allowed: false, reason: "The reviewed debug wait budget must be zero or a positive number of milliseconds." };
|
|
3522
|
+
if (watchwindow !== void 0 && wait !== void 0 && watchwindow > wait) return { allowed: false, reason: `The debug watch window of ${watchwindow} milliseconds exceeds the reviewed wait budget of ${wait} milliseconds; review a wider budget or a shorter window.` };
|
|
3523
|
+
return { allowed: true };
|
|
3524
|
+
}
|
|
3525
|
+
function validatetimelinegrammar(step, options) {
|
|
3526
|
+
const kind = step.kind;
|
|
3527
|
+
let watchwindow;
|
|
3528
|
+
if (options.watch !== void 0) {
|
|
3529
|
+
const watch = options.watch;
|
|
3530
|
+
if (!watch || typeof watch !== "object" || Array.isArray(watch)) return { allowed: false, reason: "The reviewed debug watch window must be an object." };
|
|
3531
|
+
const reviewed = watch;
|
|
3532
|
+
if (reviewed.window !== void 0) {
|
|
3533
|
+
if (typeof reviewed.window !== "number" || !Number.isFinite(reviewed.window) || reviewed.window < 0) return { allowed: false, reason: "The reviewed debug watch window must be zero or a positive number of milliseconds." };
|
|
3534
|
+
watchwindow = reviewed.window;
|
|
3535
|
+
}
|
|
3536
|
+
}
|
|
3537
|
+
const budgetcheck = debugwaitbudgetallowed(watchwindow, typeof options.wait === "number" ? options.wait : void 0);
|
|
3538
|
+
if (!budgetcheck.allowed) return budgetcheck;
|
|
3539
|
+
if (options.level !== void 0 && !loglevels.includes(options.level)) return { allowed: false, reason: `The reviewed level floor must be one of ${loglevels.join(", ")}.` };
|
|
3540
|
+
if (options.sources !== void 0) {
|
|
3541
|
+
if (!Array.isArray(options.sources) || options.sources.length === 0 || !options.sources.every((source) => timelinesources.includes(source))) return { allowed: false, reason: `The reviewed source filters must be a non-empty list of the reviewed timeline sources: ${timelinesources.join(", ")}.` };
|
|
3542
|
+
}
|
|
3543
|
+
if (kind === "watchconsole") {
|
|
3544
|
+
if (options.redact === void 0 || !Array.isArray(options.redact) || options.redact.length === 0 || !options.redact.every((pattern) => isnonempty(pattern))) return { allowed: false, reason: "Console capture requires a reviewed non-empty redaction pattern list before any console text is captured." };
|
|
3545
|
+
if (options.depth !== void 0 && (typeof options.depth !== "number" || !Number.isInteger(options.depth) || options.depth < 1)) return { allowed: false, reason: "The reviewed serialization depth bound must be a positive integer with no code ceiling." };
|
|
3546
|
+
if (options.spam !== void 0) {
|
|
3547
|
+
const rule = spamruleof(options.spam);
|
|
3548
|
+
if (!rule) return { allowed: false, reason: "The reviewed spam rule needs a pattern, a window size and a collapse threshold." };
|
|
3549
|
+
if (rule.collapse < 1) return { allowed: false, reason: "The reviewed spam collapse threshold must be a positive integer of user configured value with no code ceiling." };
|
|
3550
|
+
}
|
|
3551
|
+
if (options.rotation !== void 0) {
|
|
3552
|
+
const rule = rotationruleof(options.rotation);
|
|
3553
|
+
if (!rule) return { allowed: false, reason: "The reviewed rotation rule needs a max entry count and an overflow target." };
|
|
3554
|
+
}
|
|
3555
|
+
}
|
|
3556
|
+
if (kind === "watchtasks") {
|
|
3557
|
+
if (options.threshold !== void 0 && (typeof options.threshold !== "number" || !Number.isFinite(options.threshold) || options.threshold < 0)) return { allowed: false, reason: "The reviewed long task threshold must be zero or a positive number of milliseconds with no code ceiling." };
|
|
3558
|
+
}
|
|
3559
|
+
return { allowed: true };
|
|
3560
|
+
}
|
|
3561
|
+
function spamruleof(value) {
|
|
3562
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
3563
|
+
const entry = value;
|
|
3564
|
+
const pattern = typeof entry.pattern === "string" ? entry.pattern : "";
|
|
3565
|
+
const windowsize = typeof entry.windowsize === "number" && Number.isFinite(entry.windowsize) && entry.windowsize >= 0 ? entry.windowsize : void 0;
|
|
3566
|
+
const collapse = typeof entry.collapse === "number" && Number.isInteger(entry.collapse) ? entry.collapse : void 0;
|
|
3567
|
+
if (windowsize === void 0 || collapse === void 0) return void 0;
|
|
3568
|
+
return { pattern, windowsize, collapse };
|
|
3569
|
+
}
|
|
3570
|
+
function rotationruleof(value) {
|
|
3571
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
3572
|
+
const entry = value;
|
|
3573
|
+
const maxentries = typeof entry.maxentries === "number" && Number.isInteger(entry.maxentries) && entry.maxentries >= 1 ? entry.maxentries : void 0;
|
|
3574
|
+
const overflowtarget = typeof entry.overflowtarget === "string" && entry.overflowtarget.trim() ? entry.overflowtarget.trim() : void 0;
|
|
3575
|
+
if (maxentries === void 0 || overflowtarget === void 0) return void 0;
|
|
3576
|
+
return { maxentries, overflowtarget };
|
|
3577
|
+
}
|
|
3305
3578
|
function controltarget(step) {
|
|
3306
3579
|
let options = {};
|
|
3307
3580
|
try {
|
|
@@ -3711,6 +3984,10 @@ function validatestep(step, origin) {
|
|
|
3711
3984
|
const controlcheck = validatecontrolgrammar(step, options);
|
|
3712
3985
|
if (!controlcheck.allowed) return controlcheck;
|
|
3713
3986
|
}
|
|
3987
|
+
if (isdebugkind(step.kind)) {
|
|
3988
|
+
const timelinecheck = validatetimelinegrammar(step, options);
|
|
3989
|
+
if (!timelinecheck.allowed) return timelinecheck;
|
|
3990
|
+
}
|
|
3714
3991
|
if (step.kind === "tabcreate") {
|
|
3715
3992
|
if (options.background !== void 0 && typeof options.background !== "boolean") return { allowed: false, reason: "The reviewed background flag must be a boolean." };
|
|
3716
3993
|
if (options.window !== void 0 && (typeof options.window !== "number" || !Number.isInteger(options.window) || options.window < 0)) return { allowed: false, reason: "The reviewed target window id must be a non-negative integer." };
|
|
@@ -3812,6 +4089,10 @@ function canexecute(input) {
|
|
|
3812
4089
|
const watchgatecheck = watchgate(input.session, input.settings, now);
|
|
3813
4090
|
if (!watchgatecheck.allowed) return watchgatecheck;
|
|
3814
4091
|
}
|
|
4092
|
+
if (isdebugkind(input.step.kind)) {
|
|
4093
|
+
const timelinegatecheck = timelinegate(input.session, input.tabid, input.origin, now);
|
|
4094
|
+
if (!timelinegatecheck.allowed) return timelinegatecheck;
|
|
4095
|
+
}
|
|
3815
4096
|
if (iscontrolkind(input.step.kind)) {
|
|
3816
4097
|
const controlgate = sessiongate({ session: input.session, tabid: input.tabid, origin: input.origin, now, action: "control the network" });
|
|
3817
4098
|
if (!controlgate.allowed) return controlgate;
|
|
@@ -4040,9 +4321,14 @@ function recordupload(progress, planid, stepid, entry, now) {
|
|
|
4040
4321
|
const outcome = { stepid, ok: true, summary: `The multipart upload moved chunk ${entry.chunk} of ${entry.chunks} with ${entry.uploaded} of ${entry.bytes} bytes sent.`, details: { upload: entry }, at: now };
|
|
4041
4322
|
return recordoutcome(base, planid, outcome, now);
|
|
4042
4323
|
}
|
|
4324
|
+
function recordtimeline(progress, planid, stepid, entry, now) {
|
|
4325
|
+
const base = progress && progress.planid === planid ? progress : emptyprogress(planid, now);
|
|
4326
|
+
const outcome = { stepid, ok: true, summary: `Captured ${entry.entries} timeline entr${entry.entries === 1 ? "y" : "ies"} with ${entry.collapsed} collapsed repeat${entry.collapsed === 1 ? "" : "s"}, ${entry.errors} error${entry.errors === 1 ? "" : "s"}, ${entry.rejections} rejection${entry.rejections === 1 ? "" : "s"} and ${entry.longtasks} long task${entry.longtasks === 1 ? "" : "s"}.`, details: { timeline: entry }, at: now };
|
|
4327
|
+
return recordoutcome(base, planid, outcome, now);
|
|
4328
|
+
}
|
|
4043
4329
|
|
|
4044
4330
|
// version.ts
|
|
4045
|
-
var packageversion = "1.1.
|
|
4331
|
+
var packageversion = "1.1.45";
|
|
4046
4332
|
|
|
4047
4333
|
// types.ts
|
|
4048
4334
|
var protocolversion = packageversion;
|
|
@@ -4100,6 +4386,23 @@ function parseproposal(value, origin, grants) {
|
|
|
4100
4386
|
const bypass = proxy && typeof proxy === "object" && !Array.isArray(proxy) ? proxy.bypass : void 0;
|
|
4101
4387
|
if (!Array.isArray(bypass) || bypass.length === 0) throw new Error("Proxy routes without a bypass list are refused.");
|
|
4102
4388
|
}
|
|
4389
|
+
if (step.kind === "watchconsole" || step.kind === "watcherrors" || step.kind === "watchtasks") {
|
|
4390
|
+
let debugoptions = {};
|
|
4391
|
+
try {
|
|
4392
|
+
debugoptions = parseoptions(step);
|
|
4393
|
+
} catch {
|
|
4394
|
+
debugoptions = {};
|
|
4395
|
+
}
|
|
4396
|
+
const granted = covered.some((pattern) => {
|
|
4397
|
+
try {
|
|
4398
|
+
return new URL(origin).origin === new URL(pattern).origin;
|
|
4399
|
+
} catch {
|
|
4400
|
+
return false;
|
|
4401
|
+
}
|
|
4402
|
+
});
|
|
4403
|
+
if (!granted) throw new Error(`The ${step.kind} capture of ${origin} targets an origin outside the grants.`);
|
|
4404
|
+
if (debugoptions.level !== void 0 && !loglevels.includes(debugoptions.level)) throw new Error(`The reviewed level floor must be one of ${loglevels.join(", ")}.`);
|
|
4405
|
+
}
|
|
4103
4406
|
const evaluation = validatestep(step, origin);
|
|
4104
4407
|
if (!evaluation.allowed) throw new Error(evaluation.reason);
|
|
4105
4408
|
const target = outboundtarget(step);
|
|
@@ -4174,7 +4477,7 @@ function requestbody(input) {
|
|
|
4174
4477
|
return JSON.stringify({ version: protocolversion, objective: input.objective, session: input.session, observation: input.observation, capabilities: input.capabilities });
|
|
4175
4478
|
}
|
|
4176
4479
|
function outcomeresponse(input) {
|
|
4177
|
-
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 } : {} });
|
|
4480
|
+
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 } : {} });
|
|
4178
4481
|
}
|
|
4179
4482
|
function mapresponse(input) {
|
|
4180
4483
|
return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, map: input.map });
|
|
@@ -4270,6 +4573,12 @@ function controlreport(input) {
|
|
|
4270
4573
|
});
|
|
4271
4574
|
return { version: protocolversion, blocks: input.blocks, mocks, rewrites: input.rewrites, cookies: input.cookies, proxies: input.proxies, ratelimits: input.ratelimits };
|
|
4272
4575
|
}
|
|
4576
|
+
function timelinereport(input) {
|
|
4577
|
+
return { version: protocolversion, entries: input.entries, errors: input.errors, rejections: input.rejections, longtasks: input.longtasks, levelcounts: input.levelcounts };
|
|
4578
|
+
}
|
|
4579
|
+
function consolediffreport(input) {
|
|
4580
|
+
return { version: protocolversion, diff: input.diff };
|
|
4581
|
+
}
|
|
4273
4582
|
|
|
4274
4583
|
// capture.ts
|
|
4275
4584
|
var capturekinds = ["shotview", "shotfullpage", "shotelement", "shotregion", "contactsheet"];
|
|
@@ -5958,7 +6267,7 @@ function stepoptions2(step) {
|
|
|
5958
6267
|
}
|
|
5959
6268
|
async function refreshcapabilities() {
|
|
5960
6269
|
const report = await readcapabilities();
|
|
5961
|
-
const withmedia = { ...report, captures: [...capturekinds], media: [...mediakinds], http: [...httpkinds], netwatch: [...socketkinds, ...netwatchkinds], control: [...controlkinds] };
|
|
6270
|
+
const withmedia = { ...report, captures: [...capturekinds], media: [...mediakinds], http: [...httpkinds], netwatch: [...socketkinds, ...netwatchkinds], control: [...controlkinds], debug: [...timelinekinds] };
|
|
5962
6271
|
await memory.setcapabilities(withmedia);
|
|
5963
6272
|
return withmedia;
|
|
5964
6273
|
}
|
|
@@ -6381,6 +6690,17 @@ async function tracktabupdate(tabid2, changeinfo) {
|
|
|
6381
6690
|
if (status === "loading" && url) {
|
|
6382
6691
|
navbuffers.set(tabid2, [{ event: "beforenavigate", url, timestamp: now }]);
|
|
6383
6692
|
lastknownurls.set(tabid2, url);
|
|
6693
|
+
for (const [id, watcher] of [...activetimelinewatchers.entries()]) {
|
|
6694
|
+
const detached = watcherdetached({ startedat: watcher.startedat, lifetime: watcher.lifetime, navigations: [now] });
|
|
6695
|
+
if (!detached.detached) continue;
|
|
6696
|
+
const session = await memory.getsession();
|
|
6697
|
+
if (!session || session.tabid !== tabid2) continue;
|
|
6698
|
+
watcher.cancelled = true;
|
|
6699
|
+
await memory.closewatch(id, now).catch(() => {
|
|
6700
|
+
});
|
|
6701
|
+
await audit("timeline", `Watcher ${id} detached when the run tab navigated to ${url} at ${detached.at}; every page hook of the destroyed context is gone with it.`, { sessionid: session.id });
|
|
6702
|
+
activetimelinewatchers.delete(id);
|
|
6703
|
+
}
|
|
6384
6704
|
return;
|
|
6385
6705
|
}
|
|
6386
6706
|
const buffer = navbuffers.get(tabid2) ?? [];
|
|
@@ -8909,6 +9229,11 @@ async function executenetwatchstep(step, session, plan, tabid2, origin) {
|
|
|
8909
9229
|
observed.push(exchange);
|
|
8910
9230
|
}
|
|
8911
9231
|
const failed = observed.filter((exchange) => exchange.errorclass !== void 0).length;
|
|
9232
|
+
for (const exchange of observed) {
|
|
9233
|
+
const failure = netfailureentryof({ id: randomid(), exchange, at: Date.now() });
|
|
9234
|
+
if (!failure) continue;
|
|
9235
|
+
await memory.addtimelineentry({ id: failure.id, runid: failure.runid, stepid: failure.stepid, time: failure.at, level: "error", source: "network", message: `Request ${failure.correlationid} of ${failure.url} failed with the ${failure.errorclass} class${failure.status > 0 ? ` at status ${failure.status}` : ""}.` });
|
|
9236
|
+
}
|
|
8912
9237
|
await refreshbadge();
|
|
8913
9238
|
await audit("watch", `Observed ${observed.length} request${observed.length === 1 ? "" : "s"} of the run tab over the reviewed ${window2} millisecond window, derived from the page timing buffers with ${failed} marked failed; headers and bodies stay out of this observation.`, extra);
|
|
8914
9239
|
return { ok: true, summary: `Observed ${observed.length} request${observed.length === 1 ? "" : "s"} of the run tab${failed > 0 ? ` with ${failed} failed` : ""}.`, details: { network: { exchanges: observed.length, channelstate: "none", messages: 0 }, observed: observed.map((exchange) => ({ correlationid: exchange.correlationid, method: exchange.method, url: exchange.url, status: exchange.status, statusclass: exchange.statusclass, ...exchange.errorclass !== void 0 ? { errorclass: exchange.errorclass } : {}, bytes: exchange.bytes, duration: exchange.timing })), derivation: "The request lifecycle derives from the page performance and navigation buffers; the timing buffers expose no header names, body bytes or subresource status codes." } };
|
|
@@ -9026,6 +9351,115 @@ async function executenetwatchstep(step, session, plan, tabid2, origin) {
|
|
|
9026
9351
|
void origin;
|
|
9027
9352
|
throw new Error("Unsupported request observation kind.");
|
|
9028
9353
|
}
|
|
9354
|
+
var activetimelinewatchers = /* @__PURE__ */ new Map();
|
|
9355
|
+
function timelinedetail(entry, runid) {
|
|
9356
|
+
if (!entry || typeof entry !== "object") return null;
|
|
9357
|
+
const record2 = entry;
|
|
9358
|
+
if (typeof record2.stepid !== "string" || typeof record2.time !== "number" || typeof record2.message !== "string") return null;
|
|
9359
|
+
if (typeof record2.level !== "string" || !loglevels.includes(record2.level)) return null;
|
|
9360
|
+
if (typeof record2.source !== "string" || !timelinesources.includes(record2.source)) return null;
|
|
9361
|
+
return { id: randomid(), runid, stepid: record2.stepid, time: record2.time, level: record2.level, source: record2.source, message: record2.message };
|
|
9362
|
+
}
|
|
9363
|
+
async function executetimelinestep(step, session, plan, tabid2, origin) {
|
|
9364
|
+
const options = stepoptions2(step);
|
|
9365
|
+
const watch = options.watch && typeof options.watch === "object" && !Array.isArray(options.watch) ? options.watch : {};
|
|
9366
|
+
const watchwindow = typeof watch.window === "number" && Number.isFinite(watch.window) && watch.window >= 0 ? watch.window : 0;
|
|
9367
|
+
const extra = { sessionid: session.id, planid: plan.id, stepid: step.id };
|
|
9368
|
+
if (step.kind === "watchconsole") {
|
|
9369
|
+
const consents = await memory.getconsoleconsents();
|
|
9370
|
+
const consent = consoleconsentcovers(origin, consents);
|
|
9371
|
+
if (!consent.allowed) {
|
|
9372
|
+
const record2 = { id: randomid(), prompt: `Console capture on ${origin} for the reviewed ${watchwindow} millisecond window of step ${step.id}.`, origin, stepid: step.id, at: Date.now() };
|
|
9373
|
+
await memory.setconsoleconsent(record2);
|
|
9374
|
+
await refreshbadge();
|
|
9375
|
+
throw new Error(`${consent.reason} The prompt is open in the review panel; approve it and run the step again.`);
|
|
9376
|
+
}
|
|
9377
|
+
}
|
|
9378
|
+
if (step.kind === "watcherrors") {
|
|
9379
|
+
const stackgatecheck = stackgate(session, origin);
|
|
9380
|
+
if (!stackgatecheck.allowed) throw new Error(stackgatecheck.reason ?? "Stack capture stays outside the session origin grants.");
|
|
9381
|
+
}
|
|
9382
|
+
const startedat = Date.now();
|
|
9383
|
+
const bound = attachtimeline({ runid: plan.id, origin, stepids: plan.steps.map((item) => item.id), now: startedat });
|
|
9384
|
+
const watchid = randomid();
|
|
9385
|
+
const registration = { watchid, kind: step.kind, stepid: step.id, sessionid: session.id, origin, scopes: [], events: [], startedat, lifetime: watchwindow };
|
|
9386
|
+
await memory.addwatch(registration);
|
|
9387
|
+
await audit("timeline", `Watcher ${step.kind} attached under id ${watchid} on ${origin} for the reviewed window of ${watchwindow} milliseconds inside the run tab ${tabid2}.`, extra);
|
|
9388
|
+
activetimelinewatchers.set(watchid, { runid: plan.id, startedat, lifetime: watchwindow, cancelled: false });
|
|
9389
|
+
let output;
|
|
9390
|
+
try {
|
|
9391
|
+
output = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The watch returned no result." };
|
|
9392
|
+
} catch (error) {
|
|
9393
|
+
activetimelinewatchers.delete(watchid);
|
|
9394
|
+
await memory.closewatch(watchid, Date.now());
|
|
9395
|
+
await audit("timeline", `Watcher ${watchid} detached when the run tab navigated or closed inside the reviewed window of ${watchwindow} milliseconds.`, extra);
|
|
9396
|
+
return { ok: false, summary: `The ${step.kind} watcher detached when the tab navigated or closed inside the reviewed window; run it again on the settled page (${error instanceof Error ? error.message : String(error)}).` };
|
|
9397
|
+
}
|
|
9398
|
+
const watcherstate = activetimelinewatchers.get(watchid);
|
|
9399
|
+
activetimelinewatchers.delete(watchid);
|
|
9400
|
+
await memory.closewatch(watchid, Date.now());
|
|
9401
|
+
await audit("timeline", `Watcher ${watchid} detached cleanly after its reviewed window of ${watchwindow} milliseconds.`, extra);
|
|
9402
|
+
if (watcherstate?.cancelled) {
|
|
9403
|
+
await audit("timeline", `Watcher ${watchid} cancelled on run cancel or the killswitch; the captured window is discarded.`, extra);
|
|
9404
|
+
return { ok: false, summary: `The ${step.kind} watcher was cancelled with the run; the captured window is discarded.` };
|
|
9405
|
+
}
|
|
9406
|
+
const captured = detailarray(output.details, "entries").map((entry) => timelinedetail(entry, plan.id)).filter((entry) => entry !== null);
|
|
9407
|
+
bound.entries.push(...captured);
|
|
9408
|
+
let collapsed = 0;
|
|
9409
|
+
let flagged = [];
|
|
9410
|
+
let stored = bound.entries;
|
|
9411
|
+
if (step.kind === "watchconsole") {
|
|
9412
|
+
const rule = spamruleof(options.spam);
|
|
9413
|
+
if (rule) {
|
|
9414
|
+
const outcome = spamdetect(bound.entries, rule);
|
|
9415
|
+
stored = outcome.entries;
|
|
9416
|
+
collapsed = bound.entries.length - stored.length;
|
|
9417
|
+
flagged = outcome.flagged;
|
|
9418
|
+
}
|
|
9419
|
+
}
|
|
9420
|
+
const rotation = rotationruleof(options.rotation);
|
|
9421
|
+
let rotationtarget;
|
|
9422
|
+
if (rotation) {
|
|
9423
|
+
const rotated = rotatelogs(stored, rotation);
|
|
9424
|
+
stored = rotated.kept;
|
|
9425
|
+
rotationtarget = { target: rotation.overflowtarget, runid: plan.id, entries: rotated.overflow.length, at: Date.now() };
|
|
9426
|
+
}
|
|
9427
|
+
const errorids = [];
|
|
9428
|
+
const rejectionids = [];
|
|
9429
|
+
const longtasks = [];
|
|
9430
|
+
for (const entry of detailarray(output.details, "errors")) {
|
|
9431
|
+
if (!entry || typeof entry !== "object") continue;
|
|
9432
|
+
const record2 = entry;
|
|
9433
|
+
const id = randomid();
|
|
9434
|
+
errorids.push(id);
|
|
9435
|
+
const capturederror = { id, runid: plan.id, stepid: step.id, message: typeof record2.message === "string" ? record2.message : "", frames: Array.isArray(record2.frames) ? record2.frames : [], sourceurl: typeof record2.sourceurl === "string" ? record2.sourceurl : "", line: typeof record2.line === "number" ? record2.line : 0, at: Date.now() };
|
|
9436
|
+
await memory.adderrorrecord(capturederror);
|
|
9437
|
+
}
|
|
9438
|
+
for (const entry of detailarray(output.details, "rejections")) {
|
|
9439
|
+
if (!entry || typeof entry !== "object") continue;
|
|
9440
|
+
const record2 = entry;
|
|
9441
|
+
const id = randomid();
|
|
9442
|
+
rejectionids.push(id);
|
|
9443
|
+
const capturedrejection = { id, runid: plan.id, stepid: step.id, reason: typeof record2.reason === "string" ? record2.reason : "", frames: Array.isArray(record2.frames) ? record2.frames : [], at: Date.now() };
|
|
9444
|
+
await memory.addrejectionrecord(capturedrejection);
|
|
9445
|
+
}
|
|
9446
|
+
for (const entry of detailarray(output.details, "longtasks")) {
|
|
9447
|
+
if (!entry || typeof entry !== "object") continue;
|
|
9448
|
+
const record2 = entry;
|
|
9449
|
+
const capturedtask = { id: randomid(), runid: plan.id, stepid: step.id, duration: typeof record2.duration === "number" ? record2.duration : 0, starttime: typeof record2.starttime === "number" ? record2.starttime : 0, attributions: Array.isArray(record2.attributions) ? record2.attributions.filter((name) => typeof name === "string") : [], at: Date.now() };
|
|
9450
|
+
longtasks.push(capturedtask);
|
|
9451
|
+
await memory.addlongtask(capturedtask);
|
|
9452
|
+
}
|
|
9453
|
+
for (const entry of stored) await memory.addtimelineentry(entry);
|
|
9454
|
+
if (rotationtarget) await memory.addrotationtarget(rotationtarget);
|
|
9455
|
+
const blocking = blockingduration(longtasks, step.id, { startedat, endedat: Date.now() });
|
|
9456
|
+
const evidence = { entries: stored.length, collapsed, errors: errorids.length, rejections: rejectionids.length, longtasks: longtasks.length };
|
|
9457
|
+
await memory.setprogress(recordtimeline(await memory.getprogress(), plan.id, step.id, evidence, Date.now()));
|
|
9458
|
+
await refreshbadge();
|
|
9459
|
+
const counts = timelinecounts(stored);
|
|
9460
|
+
await audit("timeline", `Captured ${stored.length} timeline entr${stored.length === 1 ? "y" : "ies"} of ${origin} for the reviewed window of ${watchwindow} milliseconds${collapsed > 0 ? ` with ${collapsed} collapsed repeat${collapsed === 1 ? "" : "s"}` : ""}${errorids.length > 0 ? `, ${errorids.length} error${errorids.length === 1 ? "" : "s"}` : ""}${rejectionids.length > 0 ? `, ${rejectionids.length} rejection${rejectionids.length === 1 ? "" : "s"}` : ""}${longtasks.length > 0 ? ` and ${longtasks.length} long task${longtasks.length === 1 ? "" : "s"}` : ""}; console, error and task watching derives from page-injected listeners and the performance buffers.`, extra);
|
|
9461
|
+
return { ok: Boolean(output.ok), summary: output.summary, details: { ...output.details ?? {}, timeline: { entries: stored.length, levels: counts, collapsed }, entries: stored, errorids, rejectionids, longtasks: longtasks.map((task) => ({ ...task, blocking: blocking.blocking })), ...flagged.length > 0 ? { flagged } : {}, ...rotationtarget ? { rotation: rotationtarget } : {} } };
|
|
9462
|
+
}
|
|
9029
9463
|
var activerules = /* @__PURE__ */ new Map();
|
|
9030
9464
|
var activeauthflows = /* @__PURE__ */ new Map();
|
|
9031
9465
|
function rulesetof(runid) {
|
|
@@ -9415,11 +9849,12 @@ async function refreshbadge() {
|
|
|
9415
9849
|
const media = (await memory.getmediarecords()).length + (await memory.getimagebatches()).length;
|
|
9416
9850
|
const recordingprompts = (await memory.getrecordingconsents()).filter((record2) => record2.approved === void 0).length;
|
|
9417
9851
|
const fetchprompts = (await memory.getfetchconsents()).filter((consent) => consent.approved === void 0).length;
|
|
9852
|
+
const consoleprompts = (await memory.getconsoleconsents()).filter((consent) => consent.approved === void 0).length;
|
|
9418
9853
|
const observedrequests = (await memory.getexchanges()).length;
|
|
9419
9854
|
const livechannels = (await memory.getchannels()).filter((channel) => channel.state === "open" || channel.state === "connecting").length;
|
|
9420
9855
|
const activerulescount = [...activerules.values()].reduce((total2, ruleset) => total2 + 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);
|
|
9421
9856
|
const tasktabs2 = new Set(badges.map((badge) => badge.tabid)).size;
|
|
9422
|
-
const total = (queues?.prefetch ?? 0) + (queues?.batchopen ?? 0) + tasktabs2 + prompts + consents + quarantined + datasets + captures + media + recordingprompts + fetchprompts + observedrequests + livechannels + activerulescount;
|
|
9857
|
+
const total = (queues?.prefetch ?? 0) + (queues?.batchopen ?? 0) + tasktabs2 + prompts + consents + quarantined + datasets + captures + media + recordingprompts + fetchprompts + consoleprompts + observedrequests + livechannels + activerulescount;
|
|
9423
9858
|
await chrome.action.setBadgeText({ text: total > 0 ? String(total) : "" }).catch(() => {
|
|
9424
9859
|
});
|
|
9425
9860
|
}
|
|
@@ -9487,6 +9922,9 @@ async function executestep(stepid) {
|
|
|
9487
9922
|
output = await executenetwatchstep(step, session, plan, tab.id, origin);
|
|
9488
9923
|
} else if (iscontrolkind(step.kind)) {
|
|
9489
9924
|
output = await executenetcontrolstep(step, session, plan, tab.id, origin);
|
|
9925
|
+
} else if (isdebugkind(step.kind)) {
|
|
9926
|
+
if (!session || !plan || plan.state !== "approved") throw new Error("Debugging kinds refuse to run outside an approved session plan.");
|
|
9927
|
+
output = await executetimelinestep(step, session, plan, tab.id, origin);
|
|
9490
9928
|
} else {
|
|
9491
9929
|
if (step.target && freshcheckkinds.has(step.kind)) {
|
|
9492
9930
|
const fresh = await snapshot(tab.id);
|
|
@@ -9706,6 +10144,8 @@ async function handlerequest(message, sender) {
|
|
|
9706
10144
|
const apikeys = (await memory.getapikeys()).map((ref) => ({ name: ref.name, origins: ref.origins, header: ref.header, createdat: ref.createdat, ...ref.lastuse !== void 0 ? { lastuse: ref.lastuse } : {} }));
|
|
9707
10145
|
const traffic = controlreport({ blocks: await memory.getblockrules(), mocks: await memory.getmockspecs(), rewrites: await memory.getheaderules(), cookies: await memory.getcookieops(), proxies: await memory.getproxyroutes(), ratelimits: await memory.getratelimits(Date.now()) });
|
|
9708
10146
|
const tokens = authreport({ tokens: await memory.listtokens() });
|
|
10147
|
+
const timelineentries = await memory.gettimeline();
|
|
10148
|
+
const timeline = timelinereport({ entries: timelineentries, errors: await memory.geterrorrecords(), rejections: await memory.getrejectionrecords(), longtasks: await memory.getlongtasks(), levelcounts: timelinecounts(timelineentries) });
|
|
9709
10149
|
const authflows = [...activeauthflows.values()].map((active) => ({ provider: active.flow.provider, redirectorigin: active.flow.redirectorigin, scopes: active.flow.scopes, stepid: active.stepid, tabid: active.tabid ?? 0, stage: active.cancelled ? "cancelled" : "consent" }));
|
|
9710
10150
|
const runsettings = await memory.getsettings();
|
|
9711
10151
|
const scanhooks = [];
|
|
@@ -9718,7 +10158,7 @@ async function handlerequest(message, sender) {
|
|
|
9718
10158
|
const livetab = session ? await chrome.tabs.get(session.tabid).catch(() => void 0) : void 0;
|
|
9719
10159
|
const waitprofile = session ? waitprofiles.find((record2) => record2.origin === session.origin) : void 0;
|
|
9720
10160
|
const livestate = { phase: livetab?.status === "loading" ? "loading" : "complete", ...navrecords[0] ? { finalurl: navrecords[0].finalurl, redirects: navrecords[0].chain } : {} };
|
|
9721
|
-
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, socketsactive: activesockets.size, 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), ...stitchprogress.size > 0 ? { stitchprogress: [...stitchprogress.values()] } : {} };
|
|
10161
|
+
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(), socketsactive: activesockets.size, 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), ...stitchprogress.size > 0 ? { stitchprogress: [...stitchprogress.values()] } : {} };
|
|
9722
10162
|
}
|
|
9723
10163
|
case "capabilities":
|
|
9724
10164
|
return refreshcapabilities();
|
|
@@ -9763,7 +10203,8 @@ async function handlerequest(message, sender) {
|
|
|
9763
10203
|
const capture = outcome.details?.capture;
|
|
9764
10204
|
const media = outcome.details?.media;
|
|
9765
10205
|
const network = outcome.details?.network;
|
|
9766
|
-
|
|
10206
|
+
const timeline = outcome.details?.timeline;
|
|
10207
|
+
return JSON.parse(outcomeresponse({ outcome, plan, ...resolved ? { resolvedtarget: resolved } : {}, ...capture ? { capture } : {}, ...media ? { media } : {}, ...network ? { network } : {}, ...timeline ? { timeline } : {} }));
|
|
9767
10208
|
}
|
|
9768
10209
|
case "map": {
|
|
9769
10210
|
const plan = await memory.getplan();
|
|
@@ -10374,6 +10815,39 @@ async function handlerequest(message, sender) {
|
|
|
10374
10815
|
await audit("configure", `The user set the captured body retention to ${retention === void 0 ? "keep every body" : retention} record${retention === 1 ? "" : "s"}; the exchange metadata always survives.`);
|
|
10375
10816
|
return { bodyretention: retention };
|
|
10376
10817
|
}
|
|
10818
|
+
case "settimelineretention": {
|
|
10819
|
+
const inputretention = message;
|
|
10820
|
+
const settings = await memory.getsettings();
|
|
10821
|
+
const retention = typeof inputretention.retention === "number" && Number.isInteger(inputretention.retention) && inputretention.retention >= 0 ? inputretention.retention : void 0;
|
|
10822
|
+
await memory.setsettings({ ...settings, ...retention !== void 0 ? { timelineretention: retention } : {} });
|
|
10823
|
+
await audit("configure", `The user set the timeline retention to ${retention === void 0 ? "keep every entry" : retention} entr${retention === 1 ? "y" : "ies"}; the level count summaries always survive.`);
|
|
10824
|
+
return { timelineretention: retention };
|
|
10825
|
+
}
|
|
10826
|
+
case "approveconsoleconsent": {
|
|
10827
|
+
const inputapprove = message;
|
|
10828
|
+
const records = await memory.getconsoleconsents();
|
|
10829
|
+
const record2 = records.find((item) => item.id === inputapprove.id);
|
|
10830
|
+
if (!record2) throw new Error("No console capture prompt matches the id.");
|
|
10831
|
+
const decided = { ...record2, approved: true, usedat: Date.now() };
|
|
10832
|
+
await memory.setconsoleconsent(decided);
|
|
10833
|
+
await audit("consent", `Console capture on ${record2.origin} approved from the review panel; the decision persists for that origin.`, { stepid: record2.stepid });
|
|
10834
|
+
await refreshbadge();
|
|
10835
|
+
return { approved: true, origin: record2.origin };
|
|
10836
|
+
}
|
|
10837
|
+
case "consolediff": {
|
|
10838
|
+
const inputdiff = message;
|
|
10839
|
+
const base = inputdiff.base?.trim();
|
|
10840
|
+
const target = inputdiff.target?.trim();
|
|
10841
|
+
if (!base || !target) throw new Error("Console diffing needs the two reviewed run ids.");
|
|
10842
|
+
if (base === target) throw new Error("Console diffing needs two different run ids.");
|
|
10843
|
+
const baselines = (await memory.listtimeline({ runid: base })).filter((entry) => entry.source === "console").map((entry) => entry.message);
|
|
10844
|
+
const targetlines = (await memory.listtimeline({ runid: target })).filter((entry) => entry.source === "console").map((entry) => entry.message);
|
|
10845
|
+
if (baselines.length === 0 && targetlines.length === 0) throw new Error("Neither run stored console output yet; run watchconsole on both runs first.");
|
|
10846
|
+
const diff = consolediff({ baseid: base, targetid: target, baselines, targetlines, now: Date.now() });
|
|
10847
|
+
await memory.addconsolediff(diff);
|
|
10848
|
+
await audit("diff", `Diffed the console output of runs ${base} and ${target}: ${diff.added} added, ${diff.removed} removed and ${diff.repeated} repeated line${diff.added + diff.removed + diff.repeated === 1 ? "" : "s"}.`);
|
|
10849
|
+
return consolediffreport({ diff });
|
|
10850
|
+
}
|
|
10377
10851
|
case "trafficreport": {
|
|
10378
10852
|
const plan = await memory.getplan();
|
|
10379
10853
|
if (!plan) throw new Error("No plan is available for a traffic control envelope.");
|
|
@@ -10464,6 +10938,13 @@ async function handlerequest(message, sender) {
|
|
|
10464
10938
|
controller.abort();
|
|
10465
10939
|
activefetches.delete(id);
|
|
10466
10940
|
}
|
|
10941
|
+
for (const [id, watcher] of [...activetimelinewatchers.entries()]) {
|
|
10942
|
+
watcher.cancelled = true;
|
|
10943
|
+
await memory.closewatch(id, Date.now()).catch(() => {
|
|
10944
|
+
});
|
|
10945
|
+
await audit("timeline", `Watcher ${id} cancelled on run cancel or the killswitch; the captured window is discarded.`, { ...session ? { sessionid: session.id } : {}, ...watcher.runid ? { planid: watcher.runid } : {} });
|
|
10946
|
+
activetimelinewatchers.delete(id);
|
|
10947
|
+
}
|
|
10467
10948
|
const stoppedplan = await memory.getplan();
|
|
10468
10949
|
if (stoppedplan) {
|
|
10469
10950
|
await closechannelsforrun(stoppedplan.id).catch(() => {
|