@wenathlan/extension 1.1.44 → 1.1.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/dist/cdpbus.d.ts +104 -0
- package/dist/cdpbus.d.ts.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +937 -6
- package/dist/index.js.map +4 -4
- package/dist/memory.d.ts +75 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/policy.d.ts +39 -1
- package/dist/policy.d.ts.map +1 -1
- package/dist/protocol.d.ts +63 -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 +271 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/extension/dist/background.js +1252 -9
- package/extension/dist/background.js.map +4 -4
- package/extension/dist/manifest.json +1 -1
- package/extension/dist/pagebridge.js +439 -7
- package/extension/dist/pagebridge.js.map +4 -4
- package/extension/dist/popup.html +1 -1
- package/extension/dist/popup.js +18 -3
- package/extension/dist/popup.js.map +2 -2
- package/extension/dist/sidepanel.html +1 -1
- package/extension/dist/sidepanel.js +281 -3
- package/extension/dist/sidepanel.js.map +2 -2
- package/extension/dist/style.css +6 -0
- package/extension/manifest.json +1 -1
- package/package.json +1 -1
|
@@ -1042,6 +1042,205 @@ 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
|
+
/** Stores one devtools session record with its enabled domains and detach state, replacing the previous record of its id. */
|
|
1130
|
+
async setcdpsession(session) {
|
|
1131
|
+
const records = (await this.adapter.get("cdpsessions") ?? []).filter((item) => item.id !== session.id);
|
|
1132
|
+
await this.adapter.set("cdpsessions", [session, ...records]);
|
|
1133
|
+
}
|
|
1134
|
+
/** Returns every stored devtools session record, newest first. */
|
|
1135
|
+
async getcdpsessions() {
|
|
1136
|
+
return await this.adapter.get("cdpsessions") ?? [];
|
|
1137
|
+
}
|
|
1138
|
+
/** Stores one raw command outcome with its duration and error class. */
|
|
1139
|
+
async addcdpcommand(command) {
|
|
1140
|
+
const records = await this.adapter.get("cdpcommands") ?? [];
|
|
1141
|
+
await this.adapter.set("cdpcommands", [command, ...records]);
|
|
1142
|
+
}
|
|
1143
|
+
/** Returns every stored raw command outcome, newest first. */
|
|
1144
|
+
async getcdpcommands() {
|
|
1145
|
+
return await this.adapter.get("cdpcommands") ?? [];
|
|
1146
|
+
}
|
|
1147
|
+
/** Stores one domain event rule with its match filter, replacing the previous rule of its id. */
|
|
1148
|
+
async setcdpeventrule(rule) {
|
|
1149
|
+
const records = (await this.adapter.get("cdpeventrules") ?? []).filter((item) => item.id !== rule.id);
|
|
1150
|
+
await this.adapter.set("cdpeventrules", [rule, ...records]);
|
|
1151
|
+
}
|
|
1152
|
+
/** Returns every stored domain event rule, newest first. */
|
|
1153
|
+
async getcdpeventrules() {
|
|
1154
|
+
return await this.adapter.get("cdpeventrules") ?? [];
|
|
1155
|
+
}
|
|
1156
|
+
/** Stores one breakpoint record with its condition and hit counter, replacing the previous record of its id. */
|
|
1157
|
+
async addbreakpoint(spec) {
|
|
1158
|
+
const records = (await this.adapter.get("breakpoints") ?? []).filter((item) => item.id !== spec.id);
|
|
1159
|
+
await this.adapter.set("breakpoints", [spec, ...records]);
|
|
1160
|
+
}
|
|
1161
|
+
/** Returns every stored breakpoint record, newest first. */
|
|
1162
|
+
async getbreakpoints() {
|
|
1163
|
+
return await this.adapter.get("breakpoints") ?? [];
|
|
1164
|
+
}
|
|
1165
|
+
/** Stores one pause state capture; the user configured pause retention window expires the call frames and the dom snapshot reference of the oldest captures while the pause reason and hit breakpoint survive. */
|
|
1166
|
+
async addpause(pause) {
|
|
1167
|
+
const records = await this.getpauses();
|
|
1168
|
+
const combined = [pause, ...records.filter((item) => item.id !== pause.id)];
|
|
1169
|
+
const retention = (await this.getsettings())?.pauseretention;
|
|
1170
|
+
if (retention === void 0) {
|
|
1171
|
+
await this.adapter.set("pauses", combined);
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
1174
|
+
const kept = combined.slice(0, retention);
|
|
1175
|
+
const expired = combined.slice(retention).map((item) => {
|
|
1176
|
+
if (item.framesexpired === true) return item;
|
|
1177
|
+
const faded = { ...item, callframes: [], framesexpired: true };
|
|
1178
|
+
delete faded.domsnapshotid;
|
|
1179
|
+
return faded;
|
|
1180
|
+
});
|
|
1181
|
+
await this.adapter.set("pauses", [...kept, ...expired]);
|
|
1182
|
+
}
|
|
1183
|
+
/** Returns every stored pause state capture, newest first. */
|
|
1184
|
+
async getpauses() {
|
|
1185
|
+
return await this.adapter.get("pauses") ?? [];
|
|
1186
|
+
}
|
|
1187
|
+
/** Returns the pause state captures of one run, newest first. */
|
|
1188
|
+
async listpauses(runid) {
|
|
1189
|
+
const records = await this.getpauses();
|
|
1190
|
+
return records.filter((item) => item.runid === runid);
|
|
1191
|
+
}
|
|
1192
|
+
/** Stores one watch expression with its per pause values, replacing the previous expression of its id. */
|
|
1193
|
+
async setwatchexpression(expression) {
|
|
1194
|
+
const records = (await this.adapter.get("watchexpressions") ?? []).filter((item) => item.id !== expression.id);
|
|
1195
|
+
await this.adapter.set("watchexpressions", [expression, ...records]);
|
|
1196
|
+
}
|
|
1197
|
+
/** Returns every stored watch expression, newest first. */
|
|
1198
|
+
async getwatchexpressions() {
|
|
1199
|
+
return await this.adapter.get("watchexpressions") ?? [];
|
|
1200
|
+
}
|
|
1201
|
+
/** Stores one script override with its review provenance, replacing the previous override of its id. */
|
|
1202
|
+
async addscriptoverride(spec) {
|
|
1203
|
+
const records = (await this.adapter.get("scriptoverrides") ?? []).filter((item) => item.id !== spec.id);
|
|
1204
|
+
await this.adapter.set("scriptoverrides", [spec, ...records]);
|
|
1205
|
+
}
|
|
1206
|
+
/** Returns every stored script override, newest first. */
|
|
1207
|
+
async getscriptoverrides() {
|
|
1208
|
+
return await this.adapter.get("scriptoverrides") ?? [];
|
|
1209
|
+
}
|
|
1210
|
+
/** Stores one debugger consent decision per origin with the consented domain list, replacing the previous decision of its id. */
|
|
1211
|
+
async setdebuggergrant(grant) {
|
|
1212
|
+
const records = (await this.adapter.get("debuggergrants") ?? []).filter((item) => item.id !== grant.id);
|
|
1213
|
+
await this.adapter.set("debuggergrants", [grant, ...records]);
|
|
1214
|
+
}
|
|
1215
|
+
/** Returns every debugger consent decision, newest first. */
|
|
1216
|
+
async getdebuggergrants() {
|
|
1217
|
+
return await this.adapter.get("debuggergrants") ?? [];
|
|
1218
|
+
}
|
|
1219
|
+
/** Revokes every approved debugger consent of one origin: the revoke time stamps the records so the next attach needs a new reviewed prompt. */
|
|
1220
|
+
async revokedebuggergrants(origin, at) {
|
|
1221
|
+
const records = await this.getdebuggergrants();
|
|
1222
|
+
let revoked = 0;
|
|
1223
|
+
const updated = records.map((grant) => {
|
|
1224
|
+
if (grant.origin !== origin || grant.revokedat !== void 0) return grant;
|
|
1225
|
+
revoked += 1;
|
|
1226
|
+
return { ...grant, revokedat: at };
|
|
1227
|
+
});
|
|
1228
|
+
await this.adapter.set("debuggergrants", updated);
|
|
1229
|
+
return revoked;
|
|
1230
|
+
}
|
|
1231
|
+
/** Merges expired entry counts into the per run level count summary that survives the retention window. */
|
|
1232
|
+
async mergelevelsummary(runid, counts, now) {
|
|
1233
|
+
const records = await this.getlevelsummaries();
|
|
1234
|
+
const existing = records.find((item) => item.runid === runid);
|
|
1235
|
+
const merged = { ...existing?.counts ?? {} };
|
|
1236
|
+
for (const [level, count] of Object.entries(counts)) merged[level] = (merged[level] ?? 0) + count;
|
|
1237
|
+
const updated = { runid, counts: merged, at: now };
|
|
1238
|
+
await this.adapter.set("levelsummaries", [updated, ...records.filter((item) => item.runid !== runid)]);
|
|
1239
|
+
}
|
|
1240
|
+
/** Returns every per run level count summary, newest first. */
|
|
1241
|
+
async getlevelsummaries() {
|
|
1242
|
+
return await this.adapter.get("levelsummaries") ?? [];
|
|
1243
|
+
}
|
|
1045
1244
|
};
|
|
1046
1245
|
function mediakindof(record2) {
|
|
1047
1246
|
if ("pages" in record2) return "pdf";
|
|
@@ -1908,6 +2107,126 @@ function ratelimitwait(state, now) {
|
|
|
1908
2107
|
return Math.max(0, state.resetat - now);
|
|
1909
2108
|
}
|
|
1910
2109
|
|
|
2110
|
+
// cdpbus.ts
|
|
2111
|
+
var cdpkinds = ["attachcdp", "detachcdp", "cdpcmd", "watchcdp", "setbreakpoint", "stepcode", "watchexpr", "overridescript"];
|
|
2112
|
+
var cdpdomains = ["Runtime", "Log", "Debugger", "DOM", "Network", "Page"];
|
|
2113
|
+
function methoddomain(method) {
|
|
2114
|
+
const match = /^([A-Z][A-Za-z]*)\.([a-zA-Z][A-Za-z0-9]*)$/.exec(method.trim());
|
|
2115
|
+
return match?.[1];
|
|
2116
|
+
}
|
|
2117
|
+
function cdpallowlistof(value) {
|
|
2118
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
2119
|
+
const entry = value;
|
|
2120
|
+
const domains = Array.isArray(entry.domains) ? entry.domains.filter((domain) => typeof domain === "string" && cdpdomains.includes(domain)) : [];
|
|
2121
|
+
if (domains.length === 0) return void 0;
|
|
2122
|
+
if (entry.methods === void 0) return { domains };
|
|
2123
|
+
const methods = Array.isArray(entry.methods) ? entry.methods.filter((method) => typeof method === "string" && methoddomain(method) !== void 0 && domains.includes(methoddomain(method))) : [];
|
|
2124
|
+
if (methods.length === 0) return void 0;
|
|
2125
|
+
return { domains, methods };
|
|
2126
|
+
}
|
|
2127
|
+
function allowlistcovers(allowlist, method) {
|
|
2128
|
+
const domain = methoddomain(method);
|
|
2129
|
+
if (domain === void 0) return false;
|
|
2130
|
+
if (!allowlist.domains.includes(domain)) return false;
|
|
2131
|
+
if (allowlist.methods !== void 0 && !allowlist.methods.includes(method)) return false;
|
|
2132
|
+
return true;
|
|
2133
|
+
}
|
|
2134
|
+
function attachcdpsession(input) {
|
|
2135
|
+
return { id: input.id, runid: input.runid, stepid: input.stepid, tabid: input.tabid, origin: input.origin, attachedat: input.now, domains: [...new Set(input.domains)], debuggerversion: input.debuggerversion };
|
|
2136
|
+
}
|
|
2137
|
+
function detachcdpsession(session, at, userdetached = false) {
|
|
2138
|
+
return { ...session, detachedat: at, ...userdetached ? { userdetached: true } : {} };
|
|
2139
|
+
}
|
|
2140
|
+
function sendcdpcommand(input) {
|
|
2141
|
+
const domain = methoddomain(input.method);
|
|
2142
|
+
if (domain === void 0) return { ...input, method: input.method.trim(), domain: "", duration: input.duration, ...input.errorclass !== void 0 ? { errorclass: input.errorclass } : { errorclass: "malformedmethod" }, at: input.at };
|
|
2143
|
+
return { ...input, method: input.method.trim(), domain, duration: input.duration, ...input.errorclass !== void 0 ? { errorclass: input.errorclass } : {}, at: input.at };
|
|
2144
|
+
}
|
|
2145
|
+
function serializecdpcommand(queue, command) {
|
|
2146
|
+
return [...queue, command];
|
|
2147
|
+
}
|
|
2148
|
+
function cdpeventruleof(value) {
|
|
2149
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
2150
|
+
const entry = value;
|
|
2151
|
+
const domain = typeof entry.domain === "string" && cdpdomains.includes(entry.domain) ? entry.domain : void 0;
|
|
2152
|
+
const event = typeof entry.event === "string" && entry.event.trim() ? entry.event.trim() : void 0;
|
|
2153
|
+
if (domain === void 0 || event === void 0) return void 0;
|
|
2154
|
+
const match = typeof entry.match === "string" && entry.match.trim() ? entry.match.trim() : void 0;
|
|
2155
|
+
return { domain, event, ...match !== void 0 ? { match } : {} };
|
|
2156
|
+
}
|
|
2157
|
+
function watchcdpevents(rules, events) {
|
|
2158
|
+
const matched = [];
|
|
2159
|
+
const counts = {};
|
|
2160
|
+
for (const domain of cdpdomains) counts[domain] = 0;
|
|
2161
|
+
for (const event of events) {
|
|
2162
|
+
for (const rule of rules) {
|
|
2163
|
+
if (rule.domain !== event.domain || rule.event !== event.event) continue;
|
|
2164
|
+
if (rule.match !== void 0 && !(event.payload ?? "").includes(rule.match)) continue;
|
|
2165
|
+
matched.push({ ruleid: rule.id, domain: event.domain, event: event.event, ...event.payload !== void 0 ? { payload: event.payload } : {} });
|
|
2166
|
+
counts[event.domain] = (counts[event.domain] ?? 0) + 1;
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
return { matched, counts };
|
|
2170
|
+
}
|
|
2171
|
+
function breakpointinputof(value) {
|
|
2172
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
2173
|
+
const entry = value;
|
|
2174
|
+
const url = typeof entry.url === "string" && entry.url.trim() ? entry.url.trim() : void 0;
|
|
2175
|
+
const line = typeof entry.line === "number" && Number.isInteger(entry.line) && entry.line >= 0 ? entry.line : void 0;
|
|
2176
|
+
if (url === void 0 || line === void 0) return void 0;
|
|
2177
|
+
const column = typeof entry.column === "number" && Number.isInteger(entry.column) && entry.column >= 0 ? entry.column : void 0;
|
|
2178
|
+
const condition = typeof entry.condition === "string" && entry.condition.trim() ? entry.condition.trim() : void 0;
|
|
2179
|
+
return { url, line, ...column !== void 0 ? { column } : {}, ...condition !== void 0 ? { condition } : {} };
|
|
2180
|
+
}
|
|
2181
|
+
function capturepause(input) {
|
|
2182
|
+
return { id: input.id, runid: input.runid, stepid: input.stepid, reason: input.reason, callframes: [...input.callframes], ...input.hitbreakpoint !== void 0 ? { hitbreakpoint: input.hitbreakpoint } : {}, ...input.domsnapshotid !== void 0 ? { domsnapshotid: input.domsnapshotid } : {}, at: input.at };
|
|
2183
|
+
}
|
|
2184
|
+
function stepmodeof(value) {
|
|
2185
|
+
const modes = ["stepover", "stepinto", "stepout", "resume"];
|
|
2186
|
+
return typeof value === "string" && modes.includes(value) ? value : void 0;
|
|
2187
|
+
}
|
|
2188
|
+
function watchexpressionof(value) {
|
|
2189
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
2190
|
+
const entry = value;
|
|
2191
|
+
const expression = typeof entry.expression === "string" && entry.expression.trim() ? entry.expression.trim() : void 0;
|
|
2192
|
+
if (expression === void 0) return void 0;
|
|
2193
|
+
const scope = typeof entry.scope === "string" && entry.scope.trim() ? entry.scope.trim() : "topframe";
|
|
2194
|
+
return { expression, scope };
|
|
2195
|
+
}
|
|
2196
|
+
function recordwatchvalue(expression, pauseid, value, at) {
|
|
2197
|
+
return { ...expression, values: [...expression.values, { pauseid, value, at }] };
|
|
2198
|
+
}
|
|
2199
|
+
function overrideinputof(value) {
|
|
2200
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
2201
|
+
const entry = value;
|
|
2202
|
+
const urlpattern = typeof entry.urlpattern === "string" && entry.urlpattern.trim() ? entry.urlpattern.trim() : void 0;
|
|
2203
|
+
const source = typeof entry.source === "string" ? entry.source : void 0;
|
|
2204
|
+
if (urlpattern === void 0 || source === void 0 || source.trim().length === 0) return void 0;
|
|
2205
|
+
return { urlpattern, source };
|
|
2206
|
+
}
|
|
2207
|
+
function teardownplanof(value) {
|
|
2208
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
2209
|
+
const entry = value;
|
|
2210
|
+
const revertsteps = Array.isArray(entry.revertsteps) ? entry.revertsteps.filter((step) => typeof step === "string" && step.trim().length > 0) : [];
|
|
2211
|
+
const policy = entry.resumepolicy;
|
|
2212
|
+
if (revertsteps.length === 0) return void 0;
|
|
2213
|
+
if (policy !== void 0 && policy !== "resume" && policy !== "pause" && policy !== "ask") return void 0;
|
|
2214
|
+
return { revertsteps, resumepolicy: policy ?? "ask" };
|
|
2215
|
+
}
|
|
2216
|
+
function teardowncdpsession(input) {
|
|
2217
|
+
const revertedbreakpoints = input.breakpoints.filter((spec) => spec.revertedat === void 0).map((spec) => spec.id);
|
|
2218
|
+
const revertedoverrides = input.overrides.filter((spec) => spec.revertedat === void 0).map((spec) => spec.id);
|
|
2219
|
+
const resumepolicy = input.userdetached ? "pause" : input.plan?.resumepolicy ?? "ask";
|
|
2220
|
+
return {
|
|
2221
|
+
session: detachcdpsession(input.session, input.at, input.userdetached),
|
|
2222
|
+
revertedbreakpoints,
|
|
2223
|
+
revertedoverrides,
|
|
2224
|
+
resumepolicy,
|
|
2225
|
+
paused: input.userdetached || resumepolicy === "pause",
|
|
2226
|
+
keepsalive: input.userdetached
|
|
2227
|
+
};
|
|
2228
|
+
}
|
|
2229
|
+
|
|
1911
2230
|
// netauth.ts
|
|
1912
2231
|
function oauthflowof(value) {
|
|
1913
2232
|
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
@@ -2043,10 +2362,107 @@ ${file.content}\r
|
|
|
2043
2362
|
return { chunks, boundary, bytes: chunks.reduce((total, chunk) => total + chunk.length, 0) };
|
|
2044
2363
|
}
|
|
2045
2364
|
|
|
2365
|
+
// runtimeline.ts
|
|
2366
|
+
var timelinekinds = ["watchconsole", "watcherrors", "watchtasks"];
|
|
2367
|
+
var loglevels = ["error", "warn", "info", "log", "debug", "trace"];
|
|
2368
|
+
var timelinesources = ["console", "error", "rejection", "resource", "longtask", "network", "cdp"];
|
|
2369
|
+
function attachtimeline(input) {
|
|
2370
|
+
return { runid: input.runid, origin: input.origin, stepids: [...input.stepids], attachedat: input.now, entries: [] };
|
|
2371
|
+
}
|
|
2372
|
+
function spamdetect(entries, rule) {
|
|
2373
|
+
const collapsed = [];
|
|
2374
|
+
const counts = /* @__PURE__ */ new Map();
|
|
2375
|
+
for (const entry of entries) {
|
|
2376
|
+
if (rule.pattern !== "" && !entry.message.includes(rule.pattern)) {
|
|
2377
|
+
collapsed.push({ ...entry, repeat: 1 });
|
|
2378
|
+
continue;
|
|
2379
|
+
}
|
|
2380
|
+
const key = `${entry.level}|${entry.source}|${entry.message}`;
|
|
2381
|
+
const previous = collapsed[collapsed.length - 1];
|
|
2382
|
+
if (previous && previous.repeat !== void 0 && `${previous.level}|${previous.source}|${previous.message}` === key && entry.time - previous.time <= rule.windowsize) {
|
|
2383
|
+
previous.repeat += 1;
|
|
2384
|
+
continue;
|
|
2385
|
+
}
|
|
2386
|
+
collapsed.push({ ...entry, repeat: 1 });
|
|
2387
|
+
}
|
|
2388
|
+
for (const entry of collapsed) {
|
|
2389
|
+
if (entry.repeat > 1) counts.set(`${entry.level}|${entry.source}|${entry.message}`, entry.repeat);
|
|
2390
|
+
}
|
|
2391
|
+
const flagged = [...counts.entries()].filter(([, count]) => count > rule.collapse).map(([key, count]) => ({ message: key.split("|").slice(2).join("|"), count }));
|
|
2392
|
+
return { entries: collapsed, flagged };
|
|
2393
|
+
}
|
|
2394
|
+
function rotatelogs(entries, rule) {
|
|
2395
|
+
if (entries.length <= rule.maxentries) return { kept: [...entries], overflow: [] };
|
|
2396
|
+
const kept = entries.slice(entries.length - rule.maxentries);
|
|
2397
|
+
const overflow = entries.slice(0, entries.length - rule.maxentries);
|
|
2398
|
+
return { kept, overflow };
|
|
2399
|
+
}
|
|
2400
|
+
function timelinecounts(entries) {
|
|
2401
|
+
const counts = {};
|
|
2402
|
+
for (const level of loglevels) counts[level] = 0;
|
|
2403
|
+
for (const entry of entries) counts[entry.level] = (counts[entry.level] ?? 0) + 1;
|
|
2404
|
+
return counts;
|
|
2405
|
+
}
|
|
2406
|
+
function blockingduration(tasks, stepid, window2) {
|
|
2407
|
+
const inside = tasks.filter((task) => task.starttime >= window2.startedat && task.starttime <= window2.endedat);
|
|
2408
|
+
return { stepid, blocking: inside.reduce((total, task) => total + task.duration, 0), tasks: inside.length };
|
|
2409
|
+
}
|
|
2410
|
+
function netfailureentryof(input) {
|
|
2411
|
+
const exchange = input.exchange;
|
|
2412
|
+
if (exchange.errorclass === void 0 && exchange.status < 400) return null;
|
|
2413
|
+
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 };
|
|
2414
|
+
}
|
|
2415
|
+
function watcherdetached(input) {
|
|
2416
|
+
for (const navigation of input.navigations) {
|
|
2417
|
+
if (navigation >= input.startedat && navigation <= input.startedat + input.lifetime) return { detached: true, at: navigation };
|
|
2418
|
+
}
|
|
2419
|
+
return { detached: false };
|
|
2420
|
+
}
|
|
2421
|
+
function consolediff(input) {
|
|
2422
|
+
const base = input.baselines;
|
|
2423
|
+
const target = input.targetlines;
|
|
2424
|
+
const basemap = /* @__PURE__ */ new Map();
|
|
2425
|
+
for (const line of base) basemap.set(line, (basemap.get(line) ?? 0) + 1);
|
|
2426
|
+
const targetmap = /* @__PURE__ */ new Map();
|
|
2427
|
+
for (const line of target) targetmap.set(line, (targetmap.get(line) ?? 0) + 1);
|
|
2428
|
+
const lines = [];
|
|
2429
|
+
const added = [];
|
|
2430
|
+
const removed = [];
|
|
2431
|
+
const repeated = [];
|
|
2432
|
+
for (const [line, count] of targetmap) {
|
|
2433
|
+
const basecount = basemap.get(line) ?? 0;
|
|
2434
|
+
if (basecount === 0) {
|
|
2435
|
+
for (let index = 0; index < count; index += 1) {
|
|
2436
|
+
lines.push({ kind: "added", text: line });
|
|
2437
|
+
added.push(line);
|
|
2438
|
+
}
|
|
2439
|
+
continue;
|
|
2440
|
+
}
|
|
2441
|
+
const share = Math.min(basecount, count);
|
|
2442
|
+
for (let index = 0; index < share; index += 1) {
|
|
2443
|
+
lines.push({ kind: "repeated", text: line, count: share });
|
|
2444
|
+
repeated.push(line);
|
|
2445
|
+
}
|
|
2446
|
+
for (let index = share; index < count; index += 1) {
|
|
2447
|
+
lines.push({ kind: "added", text: line });
|
|
2448
|
+
added.push(line);
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
for (const [line, count] of basemap) {
|
|
2452
|
+
const targetcount = targetmap.get(line) ?? 0;
|
|
2453
|
+
const missing = Math.max(0, count - targetcount);
|
|
2454
|
+
for (let index = 0; index < missing; index += 1) {
|
|
2455
|
+
lines.push({ kind: "removed", text: line });
|
|
2456
|
+
removed.push(line);
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
return { base: input.baseid, target: input.targetid, lines, added: added.length, removed: removed.length, repeated: repeated.length, at: input.now };
|
|
2460
|
+
}
|
|
2461
|
+
|
|
2046
2462
|
// policy.ts
|
|
2047
|
-
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
|
-
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"]);
|
|
2463
|
+
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"]);
|
|
2464
|
+
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"]);
|
|
2465
|
+
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"]);
|
|
2050
2466
|
var allowedactions = /* @__PURE__ */ new Set([...sensitiveactions, ...interactionactions, ...readactions]);
|
|
2051
2467
|
var watchactions = /* @__PURE__ */ new Set(["watchmutate", "watchbanner", "watchfocus", "watchtab"]);
|
|
2052
2468
|
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 +2478,8 @@ var httpactions = /* @__PURE__ */ new Set(["fetchurl", "parsejson", "parsehtml",
|
|
|
2062
2478
|
var socketactions = /* @__PURE__ */ new Set(["opensocket", "sendmessage", "waitmessage", "subscribesse", "longpoll"]);
|
|
2063
2479
|
var netwatchactions = /* @__PURE__ */ new Set(["watchrequests", "readheaders", "capturebodies", "mapapi", "extractapi"]);
|
|
2064
2480
|
var controlactions = /* @__PURE__ */ new Set(["blockrequest", "mockresponse", "rewriteheaders", "setcookies", "readcookies", "clearcookies", "authflow", "saveapikey", "routeproxy", "postform", "postfiles"]);
|
|
2481
|
+
var debugactions = /* @__PURE__ */ new Set(["watchconsole", "watcherrors", "watchtasks"]);
|
|
2482
|
+
var cdpactions = /* @__PURE__ */ new Set(["attachcdp", "detachcdp", "cdpcmd", "watchcdp", "setbreakpoint", "stepcode", "watchexpr", "overridescript"]);
|
|
2065
2483
|
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
2484
|
var fieldkinds = ["text", "email", "phone", "date", "number", "select", "check", "radio", "file", "password", "card", "code"];
|
|
2067
2485
|
var layoutmutationactions = /* @__PURE__ */ new Set(["grouptabs", "colorgroup", "collapsegroup", "savelayout", "restorelayout"]);
|
|
@@ -2077,6 +2495,12 @@ function hostpattern(origin) {
|
|
|
2077
2495
|
if (parsed.protocol !== "https:") throw new Error("Only HTTPS origins can be granted.");
|
|
2078
2496
|
return `${parsed.origin}/*`;
|
|
2079
2497
|
}
|
|
2498
|
+
function isdebugkind(kind) {
|
|
2499
|
+
return debugactions.has(kind);
|
|
2500
|
+
}
|
|
2501
|
+
function iscdpkind(kind) {
|
|
2502
|
+
return cdpactions.has(kind);
|
|
2503
|
+
}
|
|
2080
2504
|
function actionrisk(kind) {
|
|
2081
2505
|
if (!allowedactions.has(kind)) throw new Error("Unsupported browser action.");
|
|
2082
2506
|
if (sensitiveactions.has(kind)) return "sensitive";
|
|
@@ -3302,6 +3726,202 @@ function ratelimitbudgetallowed(wait, budget) {
|
|
|
3302
3726
|
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
3727
|
return { allowed: true };
|
|
3304
3728
|
}
|
|
3729
|
+
function timelinegate(session, tabid2, origin, now) {
|
|
3730
|
+
if (!session || session.stoppedat) return { allowed: false, reason: "No active browser session exists for the timeline capture." };
|
|
3731
|
+
if (session.expiresat <= now) return { allowed: false, reason: "The browser session has expired and cannot capture the timeline." };
|
|
3732
|
+
if (session.pausedat) return { allowed: false, reason: "The browser session is paused and cannot capture the timeline." };
|
|
3733
|
+
if (session.tabid !== tabid2) return { allowed: false, reason: `The timeline capture needs the run tab ${session.tabid} and refuses tab ${tabid2}.` };
|
|
3734
|
+
if (!origingranted(session, origin)) return { allowed: false, reason: `The timeline capture of ${origin} needs the session origin grants first.` };
|
|
3735
|
+
return { allowed: true };
|
|
3736
|
+
}
|
|
3737
|
+
function consoleconsentcovers(origin, consents) {
|
|
3738
|
+
if (consents.some((consent) => consent.origin === origin && consent.approved === true)) return { allowed: true };
|
|
3739
|
+
return { allowed: false, reason: `Console capture on ${origin} needs the reviewed console consent first; approve the prompt in the review panel.` };
|
|
3740
|
+
}
|
|
3741
|
+
function stackgate(session, origin) {
|
|
3742
|
+
if (!origingranted(session, origin)) return { allowed: false, reason: `Stack capture of ${origin} stays outside the session origin grants.` };
|
|
3743
|
+
return { allowed: true };
|
|
3744
|
+
}
|
|
3745
|
+
function debugwaitbudgetallowed(watchwindow, wait) {
|
|
3746
|
+
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." };
|
|
3747
|
+
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." };
|
|
3748
|
+
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.` };
|
|
3749
|
+
return { allowed: true };
|
|
3750
|
+
}
|
|
3751
|
+
function validatetimelinegrammar(step, options) {
|
|
3752
|
+
const kind = step.kind;
|
|
3753
|
+
let watchwindow;
|
|
3754
|
+
if (options.watch !== void 0) {
|
|
3755
|
+
const watch = options.watch;
|
|
3756
|
+
if (!watch || typeof watch !== "object" || Array.isArray(watch)) return { allowed: false, reason: "The reviewed debug watch window must be an object." };
|
|
3757
|
+
const reviewed = watch;
|
|
3758
|
+
if (reviewed.window !== void 0) {
|
|
3759
|
+
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." };
|
|
3760
|
+
watchwindow = reviewed.window;
|
|
3761
|
+
}
|
|
3762
|
+
}
|
|
3763
|
+
const budgetcheck = debugwaitbudgetallowed(watchwindow, typeof options.wait === "number" ? options.wait : void 0);
|
|
3764
|
+
if (!budgetcheck.allowed) return budgetcheck;
|
|
3765
|
+
if (options.level !== void 0 && !loglevels.includes(options.level)) return { allowed: false, reason: `The reviewed level floor must be one of ${loglevels.join(", ")}.` };
|
|
3766
|
+
if (options.sources !== void 0) {
|
|
3767
|
+
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(", ")}.` };
|
|
3768
|
+
}
|
|
3769
|
+
if (kind === "watchconsole") {
|
|
3770
|
+
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." };
|
|
3771
|
+
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." };
|
|
3772
|
+
if (options.spam !== void 0) {
|
|
3773
|
+
const rule = spamruleof(options.spam);
|
|
3774
|
+
if (!rule) return { allowed: false, reason: "The reviewed spam rule needs a pattern, a window size and a collapse threshold." };
|
|
3775
|
+
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." };
|
|
3776
|
+
}
|
|
3777
|
+
if (options.rotation !== void 0) {
|
|
3778
|
+
const rule = rotationruleof(options.rotation);
|
|
3779
|
+
if (!rule) return { allowed: false, reason: "The reviewed rotation rule needs a max entry count and an overflow target." };
|
|
3780
|
+
}
|
|
3781
|
+
}
|
|
3782
|
+
if (kind === "watchtasks") {
|
|
3783
|
+
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." };
|
|
3784
|
+
}
|
|
3785
|
+
return { allowed: true };
|
|
3786
|
+
}
|
|
3787
|
+
function spamruleof(value) {
|
|
3788
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
3789
|
+
const entry = value;
|
|
3790
|
+
const pattern = typeof entry.pattern === "string" ? entry.pattern : "";
|
|
3791
|
+
const windowsize = typeof entry.windowsize === "number" && Number.isFinite(entry.windowsize) && entry.windowsize >= 0 ? entry.windowsize : void 0;
|
|
3792
|
+
const collapse = typeof entry.collapse === "number" && Number.isInteger(entry.collapse) ? entry.collapse : void 0;
|
|
3793
|
+
if (windowsize === void 0 || collapse === void 0) return void 0;
|
|
3794
|
+
return { pattern, windowsize, collapse };
|
|
3795
|
+
}
|
|
3796
|
+
function rotationruleof(value) {
|
|
3797
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
3798
|
+
const entry = value;
|
|
3799
|
+
const maxentries = typeof entry.maxentries === "number" && Number.isInteger(entry.maxentries) && entry.maxentries >= 1 ? entry.maxentries : void 0;
|
|
3800
|
+
const overflowtarget = typeof entry.overflowtarget === "string" && entry.overflowtarget.trim() ? entry.overflowtarget.trim() : void 0;
|
|
3801
|
+
if (maxentries === void 0 || overflowtarget === void 0) return void 0;
|
|
3802
|
+
return { maxentries, overflowtarget };
|
|
3803
|
+
}
|
|
3804
|
+
function debuggate(session, tabid2, origin, now) {
|
|
3805
|
+
if (!session || session.stoppedat) return { allowed: false, reason: "No active browser session exists for the devtools protocol step." };
|
|
3806
|
+
if (session.expiresat <= now) return { allowed: false, reason: "The browser session has expired and cannot run a devtools protocol step." };
|
|
3807
|
+
if (session.pausedat) return { allowed: false, reason: "The browser session is paused and cannot run a devtools protocol step." };
|
|
3808
|
+
if (session.tabid !== tabid2) return { allowed: false, reason: `The devtools protocol step needs the run tab ${session.tabid} and refuses tab ${tabid2}.` };
|
|
3809
|
+
if (!origingranted(session, origin)) return { allowed: false, reason: `The devtools protocol step on ${origin} needs the session origin grants first.` };
|
|
3810
|
+
return { allowed: true };
|
|
3811
|
+
}
|
|
3812
|
+
function debuggerconsentcovers(origin, domains, grants) {
|
|
3813
|
+
const needed = [...new Set(domains)];
|
|
3814
|
+
const covering = grants.find((grant) => grant.origin === origin && grant.approved === true && grant.revokedat === void 0 && needed.every((domain) => grant.domains.includes(domain)));
|
|
3815
|
+
if (covering) return { allowed: true };
|
|
3816
|
+
if (grants.some((grant) => grant.origin === origin && grant.revokedat !== void 0)) return { allowed: false, reason: `The debugger consent on ${origin} was revoked; approve a new prompt before the devtools protocol runs again.` };
|
|
3817
|
+
return { allowed: false, reason: `The devtools protocol on ${origin} needs the reviewed debugger consent for ${needed.join(", ")} first; approve the prompt with the domain allowlist shown in the review panel.` };
|
|
3818
|
+
}
|
|
3819
|
+
function validatebreakpointcondition(condition) {
|
|
3820
|
+
const expression = condition.trim();
|
|
3821
|
+
if (expression.length === 0) return { allowed: false, reason: "The breakpoint condition must not be empty." };
|
|
3822
|
+
if (/(?<![=!<>])=(?!=)/.test(expression)) return { allowed: false, reason: "Breakpoint conditions refuse assignment because the reviewed grammar is comparison only." };
|
|
3823
|
+
if (/[A-Za-z_$][\w$]*\s*\(/.test(expression)) return { allowed: false, reason: "Breakpoint conditions refuse calls because the reviewed grammar is comparison only." };
|
|
3824
|
+
const literal = /^(?:-?\d+(?:\.\d+)?|"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|true|false|null)$/;
|
|
3825
|
+
const tokens = expression.match(/(?:[A-Za-z_$][\w$]*|-?\d+(?:\.\d+)?|"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|===|!==|==|!=|>=|<=|&&|\|\||[!.<>()+\-*\/%])/g);
|
|
3826
|
+
if (tokens === null || tokens.join("") !== expression.replace(/\s+/g, "")) return { allowed: false, reason: "The breakpoint condition must use the reviewed expression grammar of member chains, literals, comparisons, logic operators, negation and parentheses." };
|
|
3827
|
+
const identifierlike = /^(?:true|false|null)$/;
|
|
3828
|
+
for (const token of tokens) {
|
|
3829
|
+
if (literal.test(token) || identifierlike.test(token)) continue;
|
|
3830
|
+
if (["===", "!==", "==", "!=", ">=", "<=", "&&", "||", "!", ".", "(", ")", "<", ">", "+", "-", "*", "/", "%"].includes(token)) continue;
|
|
3831
|
+
if (/^[A-Za-z_$][\w$]*$/.test(token)) continue;
|
|
3832
|
+
return { allowed: false, reason: `The token ${token} of the breakpoint condition stays outside the reviewed expression grammar.` };
|
|
3833
|
+
}
|
|
3834
|
+
return { allowed: true };
|
|
3835
|
+
}
|
|
3836
|
+
function breakpointbudgetallowed(active, ceiling) {
|
|
3837
|
+
if (ceiling === void 0) return { allowed: true };
|
|
3838
|
+
if (typeof ceiling !== "number" || !Number.isInteger(ceiling) || ceiling < 0) return { allowed: false, reason: "The reviewed breakpoint ceiling must be zero or a positive integer of user configured value with no code ceiling." };
|
|
3839
|
+
if (active >= ceiling) return { allowed: false, reason: `The run already holds ${active} active breakpoint${active === 1 ? "" : "s"} and the reviewed breakpoint ceiling is ${ceiling}; revert one or review a wider ceiling.` };
|
|
3840
|
+
return { allowed: true };
|
|
3841
|
+
}
|
|
3842
|
+
function breakpointceilingof(settings) {
|
|
3843
|
+
return settings?.breakpointceiling;
|
|
3844
|
+
}
|
|
3845
|
+
function validatecdpgrammar(step, options) {
|
|
3846
|
+
const kind = step.kind;
|
|
3847
|
+
if (kind === "attachcdp") {
|
|
3848
|
+
if (!Array.isArray(options.domains) || options.domains.length === 0 || !options.domains.every((domain) => typeof domain === "string" && cdpdomains.includes(domain))) return { allowed: false, reason: `The attach needs a non-empty enabled domain list of the reviewed domain grammar: ${cdpdomains.join(", ")}.` };
|
|
3849
|
+
if (teardownplanof(options.teardown) === void 0) return { allowed: false, reason: "Every attach needs a reviewed teardown plan with its revert steps and resume policy before approval." };
|
|
3850
|
+
if (options.allowlist !== void 0) {
|
|
3851
|
+
const allowlist = cdpallowlistof(options.allowlist);
|
|
3852
|
+
if (!allowlist || !allowlist.domains.every((domain) => options.domains.includes(domain))) return { allowed: false, reason: "The reviewed method allowlist must stay inside the enabled domains of the attach." };
|
|
3853
|
+
}
|
|
3854
|
+
const budgetcheck = debugwaitbudgetallowed(typeof options.wait === "number" ? options.wait : void 0, void 0);
|
|
3855
|
+
if (!budgetcheck.allowed) return budgetcheck;
|
|
3856
|
+
return { allowed: true };
|
|
3857
|
+
}
|
|
3858
|
+
if (kind === "detachcdp") return { allowed: true };
|
|
3859
|
+
if (kind === "cdpcmd") {
|
|
3860
|
+
const command = options.command && typeof options.command === "object" && !Array.isArray(options.command) ? options.command : void 0;
|
|
3861
|
+
if (!command || typeof command.method !== "string" || methoddomain(command.method) === void 0) return { allowed: false, reason: "The raw command needs a reviewed method of the Domain.method form." };
|
|
3862
|
+
if (command.params !== void 0 && (typeof command.params !== "object" || Array.isArray(command.params))) return { allowed: false, reason: "The raw command params must be a JSON object." };
|
|
3863
|
+
if (command.resultpath !== void 0 && typeof command.resultpath !== "string") return { allowed: false, reason: "The reviewed result path must be a dotted path string." };
|
|
3864
|
+
return { allowed: true };
|
|
3865
|
+
}
|
|
3866
|
+
if (kind === "watchcdp") {
|
|
3867
|
+
if (!Array.isArray(options.events) || options.events.length === 0 || !options.events.every((rule) => cdpeventruleof(rule) !== void 0)) return { allowed: false, reason: "The event watch needs a non-empty reviewed list of domain event rules of the reviewed domain grammar." };
|
|
3868
|
+
let watchwindow;
|
|
3869
|
+
if (options.watch !== void 0) {
|
|
3870
|
+
const watch = options.watch;
|
|
3871
|
+
if (!watch || typeof watch !== "object" || Array.isArray(watch)) return { allowed: false, reason: "The reviewed event watch window must be an object." };
|
|
3872
|
+
const reviewed = watch;
|
|
3873
|
+
if (reviewed.window !== void 0) {
|
|
3874
|
+
if (typeof reviewed.window !== "number" || !Number.isFinite(reviewed.window) || reviewed.window < 0) return { allowed: false, reason: "The reviewed event watch window must be zero or a positive number of milliseconds." };
|
|
3875
|
+
watchwindow = reviewed.window;
|
|
3876
|
+
}
|
|
3877
|
+
}
|
|
3878
|
+
if (watchwindow === void 0) return { allowed: false, reason: "The event watch needs a reviewed lifetime window before any domain event is observed." };
|
|
3879
|
+
const budgetcheck = debugwaitbudgetallowed(watchwindow, typeof options.wait === "number" ? options.wait : void 0);
|
|
3880
|
+
if (!budgetcheck.allowed) return budgetcheck;
|
|
3881
|
+
return { allowed: true };
|
|
3882
|
+
}
|
|
3883
|
+
if (kind === "setbreakpoint") {
|
|
3884
|
+
const breakpoint = breakpointinputof(options.breakpoint);
|
|
3885
|
+
if (!breakpoint) return { allowed: false, reason: "The breakpoint needs a reviewed script url and a zero based line." };
|
|
3886
|
+
if (!ishttpsurl(breakpoint.url)) return { allowed: false, reason: "The breakpoint script url must be a reviewed HTTPS url." };
|
|
3887
|
+
if (breakpoint.condition !== void 0) {
|
|
3888
|
+
const conditioncheck = validatebreakpointcondition(breakpoint.condition);
|
|
3889
|
+
if (!conditioncheck.allowed) return conditioncheck;
|
|
3890
|
+
}
|
|
3891
|
+
return { allowed: true };
|
|
3892
|
+
}
|
|
3893
|
+
if (kind === "stepcode") {
|
|
3894
|
+
if (stepmodeof(options.mode) === void 0) return { allowed: false, reason: "The step code mode must be one of stepover, stepinto, stepout or resume." };
|
|
3895
|
+
return { allowed: true };
|
|
3896
|
+
}
|
|
3897
|
+
if (kind === "watchexpr") {
|
|
3898
|
+
if (watchexpressionof(options.expression) === void 0) return { allowed: false, reason: "The watch expression needs the reviewed expression text." };
|
|
3899
|
+
if (options.reviewed !== true) return { allowed: false, reason: "Watch expressions must be reviewed before evaluation; set the explicit reviewed flag on the step." };
|
|
3900
|
+
return { allowed: true };
|
|
3901
|
+
}
|
|
3902
|
+
if (kind === "overridescript") {
|
|
3903
|
+
const override = overrideinputof(options.override);
|
|
3904
|
+
if (!override) return { allowed: false, reason: "The script override needs a reviewed url pattern and its full fixture source." };
|
|
3905
|
+
if (patternorigin(override.urlpattern) === void 0) return { allowed: false, reason: "Script overrides without a named https origin pattern are refused." };
|
|
3906
|
+
if (options.reviewed !== true) return { allowed: false, reason: "The full fixture source must be reviewed before the script override runs; set the explicit reviewed flag on the step." };
|
|
3907
|
+
return { allowed: true };
|
|
3908
|
+
}
|
|
3909
|
+
return { allowed: true };
|
|
3910
|
+
}
|
|
3911
|
+
function planallowlist(steps) {
|
|
3912
|
+
const attach = steps.find((step) => step.kind === "attachcdp");
|
|
3913
|
+
if (!attach) return void 0;
|
|
3914
|
+
let options = {};
|
|
3915
|
+
try {
|
|
3916
|
+
options = parseoptions(attach);
|
|
3917
|
+
} catch {
|
|
3918
|
+
options = {};
|
|
3919
|
+
}
|
|
3920
|
+
const domains = Array.isArray(options.domains) ? options.domains.filter((domain) => typeof domain === "string" && cdpdomains.includes(domain)) : [];
|
|
3921
|
+
if (domains.length === 0) return void 0;
|
|
3922
|
+
const gated = cdpallowlistof(options.allowlist);
|
|
3923
|
+
return { domains, ...gated?.methods !== void 0 ? { methods: gated.methods } : {} };
|
|
3924
|
+
}
|
|
3305
3925
|
function controltarget(step) {
|
|
3306
3926
|
let options = {};
|
|
3307
3927
|
try {
|
|
@@ -3711,6 +4331,14 @@ function validatestep(step, origin) {
|
|
|
3711
4331
|
const controlcheck = validatecontrolgrammar(step, options);
|
|
3712
4332
|
if (!controlcheck.allowed) return controlcheck;
|
|
3713
4333
|
}
|
|
4334
|
+
if (isdebugkind(step.kind)) {
|
|
4335
|
+
const timelinecheck = validatetimelinegrammar(step, options);
|
|
4336
|
+
if (!timelinecheck.allowed) return timelinecheck;
|
|
4337
|
+
}
|
|
4338
|
+
if (iscdpkind(step.kind)) {
|
|
4339
|
+
const cdpcheck = validatecdpgrammar(step, options);
|
|
4340
|
+
if (!cdpcheck.allowed) return cdpcheck;
|
|
4341
|
+
}
|
|
3714
4342
|
if (step.kind === "tabcreate") {
|
|
3715
4343
|
if (options.background !== void 0 && typeof options.background !== "boolean") return { allowed: false, reason: "The reviewed background flag must be a boolean." };
|
|
3716
4344
|
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 +4440,50 @@ function canexecute(input) {
|
|
|
3812
4440
|
const watchgatecheck = watchgate(input.session, input.settings, now);
|
|
3813
4441
|
if (!watchgatecheck.allowed) return watchgatecheck;
|
|
3814
4442
|
}
|
|
4443
|
+
if (isdebugkind(input.step.kind)) {
|
|
4444
|
+
const timelinegatecheck = timelinegate(input.session, input.tabid, input.origin, now);
|
|
4445
|
+
if (!timelinegatecheck.allowed) return timelinegatecheck;
|
|
4446
|
+
}
|
|
4447
|
+
if (iscdpkind(input.step.kind)) {
|
|
4448
|
+
const debuggatecheck = debuggate(input.session, input.tabid, input.origin, now);
|
|
4449
|
+
if (!debuggatecheck.allowed) return debuggatecheck;
|
|
4450
|
+
if (!input.plan) return { allowed: false, reason: "The devtools protocol steps need an approved plan." };
|
|
4451
|
+
const allowlist = planallowlist(input.plan.steps);
|
|
4452
|
+
if (input.step.kind !== "attachcdp") {
|
|
4453
|
+
if (allowlist === void 0) return { allowed: false, reason: "The devtools protocol step needs the attachcdp step of the same plan with its enabled domains first." };
|
|
4454
|
+
if (input.step.kind === "cdpcmd") {
|
|
4455
|
+
let cdpoptions2 = {};
|
|
4456
|
+
try {
|
|
4457
|
+
cdpoptions2 = parseoptions(input.step);
|
|
4458
|
+
} catch {
|
|
4459
|
+
cdpoptions2 = {};
|
|
4460
|
+
}
|
|
4461
|
+
const command = cdpoptions2.command && typeof cdpoptions2.command === "object" && !Array.isArray(cdpoptions2.command) ? cdpoptions2.command : void 0;
|
|
4462
|
+
const method = typeof command?.method === "string" ? command.method : "";
|
|
4463
|
+
if (methoddomain(method) === void 0 || !allowlistcovers(allowlist, method)) return { allowed: false, reason: `The raw command ${method || ""} stays outside the enabled domain allowlist of the plan attach; review the attach domains or the method gates.` };
|
|
4464
|
+
}
|
|
4465
|
+
}
|
|
4466
|
+
let cdpoptions = {};
|
|
4467
|
+
try {
|
|
4468
|
+
cdpoptions = parseoptions(input.step);
|
|
4469
|
+
} catch {
|
|
4470
|
+
cdpoptions = {};
|
|
4471
|
+
}
|
|
4472
|
+
if (input.step.kind === "setbreakpoint") {
|
|
4473
|
+
const breakpoint = breakpointinputof(cdpoptions.breakpoint);
|
|
4474
|
+
if (breakpoint) {
|
|
4475
|
+
const targetgate = origincheck(input.session, breakpoint.url);
|
|
4476
|
+
if (!targetgate.allowed) return targetgate;
|
|
4477
|
+
}
|
|
4478
|
+
}
|
|
4479
|
+
if (input.step.kind === "overridescript") {
|
|
4480
|
+
const override = overrideinputof(cdpoptions.override);
|
|
4481
|
+
if (override) {
|
|
4482
|
+
const targetgate = origincheck(input.session, override.urlpattern);
|
|
4483
|
+
if (!targetgate.allowed) return targetgate;
|
|
4484
|
+
}
|
|
4485
|
+
}
|
|
4486
|
+
}
|
|
3815
4487
|
if (iscontrolkind(input.step.kind)) {
|
|
3816
4488
|
const controlgate = sessiongate({ session: input.session, tabid: input.tabid, origin: input.origin, now, action: "control the network" });
|
|
3817
4489
|
if (!controlgate.allowed) return controlgate;
|
|
@@ -4040,9 +4712,20 @@ function recordupload(progress, planid, stepid, entry, now) {
|
|
|
4040
4712
|
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
4713
|
return recordoutcome(base, planid, outcome, now);
|
|
4042
4714
|
}
|
|
4715
|
+
function recordtimeline(progress, planid, stepid, entry, now) {
|
|
4716
|
+
const base = progress && progress.planid === planid ? progress : emptyprogress(planid, now);
|
|
4717
|
+
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 };
|
|
4718
|
+
return recordoutcome(base, planid, outcome, now);
|
|
4719
|
+
}
|
|
4720
|
+
function recordcdp(progress, planid, stepid, entry, now) {
|
|
4721
|
+
const base = progress && progress.planid === planid ? progress : emptyprogress(planid, now);
|
|
4722
|
+
const summary = entry.family === "command" ? `The reviewed ${entry.method ?? "raw"} command of the ${entry.domain ?? "unknown"} domain returned in ${entry.duration ?? 0} millisecond${(entry.duration ?? 0) === 1 ? "" : "s"}${entry.errorclass !== void 0 ? ` with the ${entry.errorclass} error class` : ""}.` : `The devtools ${entry.family} step ran${entry.domain !== void 0 ? ` on the ${entry.domain} domain` : ""}${entry.events !== void 0 ? ` and matched ${entry.events} event${entry.events === 1 ? "" : "s"}` : ""}${entry.hits !== void 0 ? ` with ${entry.hits} hit${entry.hits === 1 ? "" : "s"}` : ""}${entry.frames !== void 0 ? ` capturing ${entry.frames} call frame${entry.frames === 1 ? "" : "s"}` : ""}.`;
|
|
4723
|
+
const outcome = { stepid, ok: entry.errorclass === void 0, summary, details: { cdp: entry }, at: now };
|
|
4724
|
+
return recordoutcome(base, planid, outcome, now);
|
|
4725
|
+
}
|
|
4043
4726
|
|
|
4044
4727
|
// version.ts
|
|
4045
|
-
var packageversion = "1.1.
|
|
4728
|
+
var packageversion = "1.1.46";
|
|
4046
4729
|
|
|
4047
4730
|
// types.ts
|
|
4048
4731
|
var protocolversion = packageversion;
|
|
@@ -4067,6 +4750,20 @@ function parseproposal(value, origin, grants) {
|
|
|
4067
4750
|
const expiresat = typeof planinput.expiresat === "number" ? planinput.expiresat : createdat + 10 * 60 * 1e3;
|
|
4068
4751
|
if (expiresat <= createdat) throw new Error("Plan expiry must be in the future.");
|
|
4069
4752
|
const planwindow = expiresat - createdat;
|
|
4753
|
+
const attachinput = stepsinput.map((input) => record(input)).find((candidate) => candidate.kind === "attachcdp");
|
|
4754
|
+
let planallowlist2;
|
|
4755
|
+
if (attachinput !== void 0) {
|
|
4756
|
+
const attachoptions = (() => {
|
|
4757
|
+
try {
|
|
4758
|
+
return parseoptions({ id: "attach", kind: "attachcdp", summary: "attach", risk: "sensitive", ...typeof attachinput.options === "string" ? { options: attachinput.options } : {} });
|
|
4759
|
+
} catch {
|
|
4760
|
+
return {};
|
|
4761
|
+
}
|
|
4762
|
+
})();
|
|
4763
|
+
const domains = Array.isArray(attachoptions.domains) ? attachoptions.domains.filter((domain) => typeof domain === "string" && cdpdomains.includes(domain)) : [];
|
|
4764
|
+
const gated = cdpallowlistof(attachoptions.allowlist);
|
|
4765
|
+
planallowlist2 = domains.length > 0 ? { domains, ...gated?.methods !== void 0 ? { methods: gated.methods } : {} } : void 0;
|
|
4766
|
+
}
|
|
4070
4767
|
const steps = stepsinput.map((input, index) => {
|
|
4071
4768
|
const candidate = record(input);
|
|
4072
4769
|
const kind = text(candidate.kind, `step ${index + 1} kind`);
|
|
@@ -4100,6 +4797,53 @@ function parseproposal(value, origin, grants) {
|
|
|
4100
4797
|
const bypass = proxy && typeof proxy === "object" && !Array.isArray(proxy) ? proxy.bypass : void 0;
|
|
4101
4798
|
if (!Array.isArray(bypass) || bypass.length === 0) throw new Error("Proxy routes without a bypass list are refused.");
|
|
4102
4799
|
}
|
|
4800
|
+
if (step.kind === "watchconsole" || step.kind === "watcherrors" || step.kind === "watchtasks") {
|
|
4801
|
+
let debugoptions = {};
|
|
4802
|
+
try {
|
|
4803
|
+
debugoptions = parseoptions(step);
|
|
4804
|
+
} catch {
|
|
4805
|
+
debugoptions = {};
|
|
4806
|
+
}
|
|
4807
|
+
const granted = covered.some((pattern) => {
|
|
4808
|
+
try {
|
|
4809
|
+
return new URL(origin).origin === new URL(pattern).origin;
|
|
4810
|
+
} catch {
|
|
4811
|
+
return false;
|
|
4812
|
+
}
|
|
4813
|
+
});
|
|
4814
|
+
if (!granted) throw new Error(`The ${step.kind} capture of ${origin} targets an origin outside the grants.`);
|
|
4815
|
+
if (debugoptions.level !== void 0 && !loglevels.includes(debugoptions.level)) throw new Error(`The reviewed level floor must be one of ${loglevels.join(", ")}.`);
|
|
4816
|
+
}
|
|
4817
|
+
if (iscdpkind(step.kind)) {
|
|
4818
|
+
const granted = covered.some((pattern) => {
|
|
4819
|
+
try {
|
|
4820
|
+
return new URL(origin).origin === new URL(pattern).origin;
|
|
4821
|
+
} catch {
|
|
4822
|
+
return false;
|
|
4823
|
+
}
|
|
4824
|
+
});
|
|
4825
|
+
if (!granted) throw new Error(`The ${step.kind} step of ${origin} targets an origin outside the grants.`);
|
|
4826
|
+
let cdpoptions = {};
|
|
4827
|
+
try {
|
|
4828
|
+
cdpoptions = parseoptions(step);
|
|
4829
|
+
} catch {
|
|
4830
|
+
cdpoptions = {};
|
|
4831
|
+
}
|
|
4832
|
+
if (step.kind === "attachcdp") {
|
|
4833
|
+
if (teardownplanof(cdpoptions.teardown) === void 0) throw new Error("Attach steps without a reviewed teardown plan are refused.");
|
|
4834
|
+
const domains = Array.isArray(cdpoptions.domains) ? cdpoptions.domains.filter((domain) => typeof domain === "string" && cdpdomains.includes(domain)) : [];
|
|
4835
|
+
if (domains.length === 0) throw new Error("Attach steps need a non-empty enabled domain list of the reviewed domain grammar.");
|
|
4836
|
+
const gated = cdpallowlistof(cdpoptions.allowlist);
|
|
4837
|
+
if (cdpoptions.allowlist !== void 0 && (!gated || !gated.domains.every((domain) => domains.includes(domain)))) throw new Error("The reviewed method allowlist must stay inside the enabled domains of the attach.");
|
|
4838
|
+
}
|
|
4839
|
+
if (step.kind === "cdpcmd") {
|
|
4840
|
+
const command = cdpoptions.command && typeof cdpoptions.command === "object" && !Array.isArray(cdpoptions.command) ? cdpoptions.command : void 0;
|
|
4841
|
+
const method = typeof command?.method === "string" ? command.method : "";
|
|
4842
|
+
if (methoddomain(method) === void 0) throw new Error("Raw commands need a reviewed method of the Domain.method form.");
|
|
4843
|
+
if (planallowlist2 === void 0) throw new Error("Raw command steps need the attachcdp step of the same plan with its enabled domains first.");
|
|
4844
|
+
if (!allowlistcovers(planallowlist2, method)) throw new Error(`The raw command ${method} stays outside the enabled domain allowlist of the plan attach.`);
|
|
4845
|
+
}
|
|
4846
|
+
}
|
|
4103
4847
|
const evaluation = validatestep(step, origin);
|
|
4104
4848
|
if (!evaluation.allowed) throw new Error(evaluation.reason);
|
|
4105
4849
|
const target = outboundtarget(step);
|
|
@@ -4174,7 +4918,7 @@ function requestbody(input) {
|
|
|
4174
4918
|
return JSON.stringify({ version: protocolversion, objective: input.objective, session: input.session, observation: input.observation, capabilities: input.capabilities });
|
|
4175
4919
|
}
|
|
4176
4920
|
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 } : {} });
|
|
4921
|
+
return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, outcome: input.outcome, ...input.resolvedtarget ? { resolvedtarget: input.resolvedtarget } : {}, ...input.capture ? { capture: input.capture } : {}, ...input.media ? { media: input.media } : {}, ...input.transport ? { transport: input.transport } : {}, ...input.network ? { network: input.network } : {}, ...input.control ? { control: input.control } : {}, ...input.timeline ? { timeline: input.timeline } : {}, ...input.cdp ? { cdp: input.cdp } : {} });
|
|
4178
4922
|
}
|
|
4179
4923
|
function mapresponse(input) {
|
|
4180
4924
|
return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, map: input.map });
|
|
@@ -4270,6 +5014,25 @@ function controlreport(input) {
|
|
|
4270
5014
|
});
|
|
4271
5015
|
return { version: protocolversion, blocks: input.blocks, mocks, rewrites: input.rewrites, cookies: input.cookies, proxies: input.proxies, ratelimits: input.ratelimits };
|
|
4272
5016
|
}
|
|
5017
|
+
function timelinereport(input) {
|
|
5018
|
+
return { version: protocolversion, entries: input.entries, errors: input.errors, rejections: input.rejections, longtasks: input.longtasks, levelcounts: input.levelcounts };
|
|
5019
|
+
}
|
|
5020
|
+
function consolediffreport(input) {
|
|
5021
|
+
return { version: protocolversion, diff: input.diff };
|
|
5022
|
+
}
|
|
5023
|
+
function cdpreport(input) {
|
|
5024
|
+
const overrides = input.overrides.map((spec) => {
|
|
5025
|
+
const { source, ...metadata } = spec;
|
|
5026
|
+
void source;
|
|
5027
|
+
return metadata;
|
|
5028
|
+
});
|
|
5029
|
+
const grants = input.grants.map((grant) => {
|
|
5030
|
+
const { prompt, ...metadata } = grant;
|
|
5031
|
+
void prompt;
|
|
5032
|
+
return metadata;
|
|
5033
|
+
});
|
|
5034
|
+
return { version: protocolversion, sessions: input.sessions, commands: input.commands, events: input.events, breakpoints: input.breakpoints, pauses: input.pauses, watches: input.watches, overrides, grants };
|
|
5035
|
+
}
|
|
4273
5036
|
|
|
4274
5037
|
// capture.ts
|
|
4275
5038
|
var capturekinds = ["shotview", "shotfullpage", "shotelement", "shotregion", "contactsheet"];
|
|
@@ -5958,7 +6721,7 @@ function stepoptions2(step) {
|
|
|
5958
6721
|
}
|
|
5959
6722
|
async function refreshcapabilities() {
|
|
5960
6723
|
const report = await readcapabilities();
|
|
5961
|
-
const withmedia = { ...report, captures: [...capturekinds], media: [...mediakinds], http: [...httpkinds], netwatch: [...socketkinds, ...netwatchkinds], control: [...controlkinds] };
|
|
6724
|
+
const withmedia = { ...report, captures: [...capturekinds], media: [...mediakinds], http: [...httpkinds], netwatch: [...socketkinds, ...netwatchkinds], control: [...controlkinds], debug: [...timelinekinds, ...cdpkinds] };
|
|
5962
6725
|
await memory.setcapabilities(withmedia);
|
|
5963
6726
|
return withmedia;
|
|
5964
6727
|
}
|
|
@@ -6381,6 +7144,22 @@ async function tracktabupdate(tabid2, changeinfo) {
|
|
|
6381
7144
|
if (status === "loading" && url) {
|
|
6382
7145
|
navbuffers.set(tabid2, [{ event: "beforenavigate", url, timestamp: now }]);
|
|
6383
7146
|
lastknownurls.set(tabid2, url);
|
|
7147
|
+
for (const [id, watcher] of [...activetimelinewatchers.entries()]) {
|
|
7148
|
+
const detached = watcherdetached({ startedat: watcher.startedat, lifetime: watcher.lifetime, navigations: [now] });
|
|
7149
|
+
if (!detached.detached) continue;
|
|
7150
|
+
const session = await memory.getsession();
|
|
7151
|
+
if (!session || session.tabid !== tabid2) continue;
|
|
7152
|
+
watcher.cancelled = true;
|
|
7153
|
+
await memory.closewatch(id, now).catch(() => {
|
|
7154
|
+
});
|
|
7155
|
+
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 });
|
|
7156
|
+
activetimelinewatchers.delete(id);
|
|
7157
|
+
}
|
|
7158
|
+
for (const [runid, active] of [...activecdpsessions.entries()]) {
|
|
7159
|
+
if (active.session.detachedat !== void 0 || active.session.tabid !== tabid2) continue;
|
|
7160
|
+
await detachcdpforrun(runid, `the run tab navigated to ${url}`).catch(() => {
|
|
7161
|
+
});
|
|
7162
|
+
}
|
|
6384
7163
|
return;
|
|
6385
7164
|
}
|
|
6386
7165
|
const buffer = navbuffers.get(tabid2) ?? [];
|
|
@@ -8909,6 +9688,11 @@ async function executenetwatchstep(step, session, plan, tabid2, origin) {
|
|
|
8909
9688
|
observed.push(exchange);
|
|
8910
9689
|
}
|
|
8911
9690
|
const failed = observed.filter((exchange) => exchange.errorclass !== void 0).length;
|
|
9691
|
+
for (const exchange of observed) {
|
|
9692
|
+
const failure = netfailureentryof({ id: randomid(), exchange, at: Date.now() });
|
|
9693
|
+
if (!failure) continue;
|
|
9694
|
+
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}` : ""}.` });
|
|
9695
|
+
}
|
|
8912
9696
|
await refreshbadge();
|
|
8913
9697
|
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
9698
|
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 +9810,343 @@ async function executenetwatchstep(step, session, plan, tabid2, origin) {
|
|
|
9026
9810
|
void origin;
|
|
9027
9811
|
throw new Error("Unsupported request observation kind.");
|
|
9028
9812
|
}
|
|
9813
|
+
var activetimelinewatchers = /* @__PURE__ */ new Map();
|
|
9814
|
+
function timelinedetail(entry, runid) {
|
|
9815
|
+
if (!entry || typeof entry !== "object") return null;
|
|
9816
|
+
const record2 = entry;
|
|
9817
|
+
if (typeof record2.stepid !== "string" || typeof record2.time !== "number" || typeof record2.message !== "string") return null;
|
|
9818
|
+
if (typeof record2.level !== "string" || !loglevels.includes(record2.level)) return null;
|
|
9819
|
+
if (typeof record2.source !== "string" || !timelinesources.includes(record2.source)) return null;
|
|
9820
|
+
return { id: randomid(), runid, stepid: record2.stepid, time: record2.time, level: record2.level, source: record2.source, message: record2.message };
|
|
9821
|
+
}
|
|
9822
|
+
async function executetimelinestep(step, session, plan, tabid2, origin) {
|
|
9823
|
+
const options = stepoptions2(step);
|
|
9824
|
+
const watch = options.watch && typeof options.watch === "object" && !Array.isArray(options.watch) ? options.watch : {};
|
|
9825
|
+
const watchwindow = typeof watch.window === "number" && Number.isFinite(watch.window) && watch.window >= 0 ? watch.window : 0;
|
|
9826
|
+
const extra = { sessionid: session.id, planid: plan.id, stepid: step.id };
|
|
9827
|
+
if (step.kind === "watchconsole") {
|
|
9828
|
+
const consents = await memory.getconsoleconsents();
|
|
9829
|
+
const consent = consoleconsentcovers(origin, consents);
|
|
9830
|
+
if (!consent.allowed) {
|
|
9831
|
+
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() };
|
|
9832
|
+
await memory.setconsoleconsent(record2);
|
|
9833
|
+
await refreshbadge();
|
|
9834
|
+
throw new Error(`${consent.reason} The prompt is open in the review panel; approve it and run the step again.`);
|
|
9835
|
+
}
|
|
9836
|
+
}
|
|
9837
|
+
if (step.kind === "watcherrors") {
|
|
9838
|
+
const stackgatecheck = stackgate(session, origin);
|
|
9839
|
+
if (!stackgatecheck.allowed) throw new Error(stackgatecheck.reason ?? "Stack capture stays outside the session origin grants.");
|
|
9840
|
+
}
|
|
9841
|
+
const startedat = Date.now();
|
|
9842
|
+
const bound = attachtimeline({ runid: plan.id, origin, stepids: plan.steps.map((item) => item.id), now: startedat });
|
|
9843
|
+
const watchid = randomid();
|
|
9844
|
+
const registration = { watchid, kind: step.kind, stepid: step.id, sessionid: session.id, origin, scopes: [], events: [], startedat, lifetime: watchwindow };
|
|
9845
|
+
await memory.addwatch(registration);
|
|
9846
|
+
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);
|
|
9847
|
+
activetimelinewatchers.set(watchid, { runid: plan.id, startedat, lifetime: watchwindow, cancelled: false });
|
|
9848
|
+
let output;
|
|
9849
|
+
try {
|
|
9850
|
+
output = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The watch returned no result." };
|
|
9851
|
+
} catch (error) {
|
|
9852
|
+
activetimelinewatchers.delete(watchid);
|
|
9853
|
+
await memory.closewatch(watchid, Date.now());
|
|
9854
|
+
await audit("timeline", `Watcher ${watchid} detached when the run tab navigated or closed inside the reviewed window of ${watchwindow} milliseconds.`, extra);
|
|
9855
|
+
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)}).` };
|
|
9856
|
+
}
|
|
9857
|
+
const watcherstate = activetimelinewatchers.get(watchid);
|
|
9858
|
+
activetimelinewatchers.delete(watchid);
|
|
9859
|
+
await memory.closewatch(watchid, Date.now());
|
|
9860
|
+
await audit("timeline", `Watcher ${watchid} detached cleanly after its reviewed window of ${watchwindow} milliseconds.`, extra);
|
|
9861
|
+
if (watcherstate?.cancelled) {
|
|
9862
|
+
await audit("timeline", `Watcher ${watchid} cancelled on run cancel or the killswitch; the captured window is discarded.`, extra);
|
|
9863
|
+
return { ok: false, summary: `The ${step.kind} watcher was cancelled with the run; the captured window is discarded.` };
|
|
9864
|
+
}
|
|
9865
|
+
const captured = detailarray(output.details, "entries").map((entry) => timelinedetail(entry, plan.id)).filter((entry) => entry !== null);
|
|
9866
|
+
bound.entries.push(...captured);
|
|
9867
|
+
let collapsed = 0;
|
|
9868
|
+
let flagged = [];
|
|
9869
|
+
let stored = bound.entries;
|
|
9870
|
+
if (step.kind === "watchconsole") {
|
|
9871
|
+
const rule = spamruleof(options.spam);
|
|
9872
|
+
if (rule) {
|
|
9873
|
+
const outcome = spamdetect(bound.entries, rule);
|
|
9874
|
+
stored = outcome.entries;
|
|
9875
|
+
collapsed = bound.entries.length - stored.length;
|
|
9876
|
+
flagged = outcome.flagged;
|
|
9877
|
+
}
|
|
9878
|
+
}
|
|
9879
|
+
const rotation = rotationruleof(options.rotation);
|
|
9880
|
+
let rotationtarget;
|
|
9881
|
+
if (rotation) {
|
|
9882
|
+
const rotated = rotatelogs(stored, rotation);
|
|
9883
|
+
stored = rotated.kept;
|
|
9884
|
+
rotationtarget = { target: rotation.overflowtarget, runid: plan.id, entries: rotated.overflow.length, at: Date.now() };
|
|
9885
|
+
}
|
|
9886
|
+
const errorids = [];
|
|
9887
|
+
const rejectionids = [];
|
|
9888
|
+
const longtasks = [];
|
|
9889
|
+
for (const entry of detailarray(output.details, "errors")) {
|
|
9890
|
+
if (!entry || typeof entry !== "object") continue;
|
|
9891
|
+
const record2 = entry;
|
|
9892
|
+
const id = randomid();
|
|
9893
|
+
errorids.push(id);
|
|
9894
|
+
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() };
|
|
9895
|
+
await memory.adderrorrecord(capturederror);
|
|
9896
|
+
}
|
|
9897
|
+
for (const entry of detailarray(output.details, "rejections")) {
|
|
9898
|
+
if (!entry || typeof entry !== "object") continue;
|
|
9899
|
+
const record2 = entry;
|
|
9900
|
+
const id = randomid();
|
|
9901
|
+
rejectionids.push(id);
|
|
9902
|
+
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() };
|
|
9903
|
+
await memory.addrejectionrecord(capturedrejection);
|
|
9904
|
+
}
|
|
9905
|
+
for (const entry of detailarray(output.details, "longtasks")) {
|
|
9906
|
+
if (!entry || typeof entry !== "object") continue;
|
|
9907
|
+
const record2 = entry;
|
|
9908
|
+
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() };
|
|
9909
|
+
longtasks.push(capturedtask);
|
|
9910
|
+
await memory.addlongtask(capturedtask);
|
|
9911
|
+
}
|
|
9912
|
+
for (const entry of stored) await memory.addtimelineentry(entry);
|
|
9913
|
+
if (rotationtarget) await memory.addrotationtarget(rotationtarget);
|
|
9914
|
+
const blocking = blockingduration(longtasks, step.id, { startedat, endedat: Date.now() });
|
|
9915
|
+
const evidence = { entries: stored.length, collapsed, errors: errorids.length, rejections: rejectionids.length, longtasks: longtasks.length };
|
|
9916
|
+
await memory.setprogress(recordtimeline(await memory.getprogress(), plan.id, step.id, evidence, Date.now()));
|
|
9917
|
+
await refreshbadge();
|
|
9918
|
+
const counts = timelinecounts(stored);
|
|
9919
|
+
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);
|
|
9920
|
+
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 } : {} } };
|
|
9921
|
+
}
|
|
9922
|
+
var cdpderivation = "devthink instrumented harness 1.1.46 (no chrome.debugger permission)";
|
|
9923
|
+
var activecdpsessions = /* @__PURE__ */ new Map();
|
|
9924
|
+
async function detachcdpforrun(runid, reason, userdetached = false) {
|
|
9925
|
+
const active = activecdpsessions.get(runid);
|
|
9926
|
+
if (!active) return;
|
|
9927
|
+
const at = Date.now();
|
|
9928
|
+
const decision = teardowncdpsession({ session: active.session, breakpoints: active.breakpoints, overrides: active.overrides, plan: active.teardown, userdetached, at });
|
|
9929
|
+
for (const id of decision.revertedbreakpoints) {
|
|
9930
|
+
const spec = active.breakpoints.find((candidate) => candidate.id === id);
|
|
9931
|
+
if (!spec) continue;
|
|
9932
|
+
const reverted = { ...spec, revertedat: at };
|
|
9933
|
+
await memory.addbreakpoint(reverted).catch(() => void 0);
|
|
9934
|
+
await audit("debugger", `Reverted the breakpoint ${id} of ${spec.url}:${spec.line} with ${spec.hits} hit${spec.hits === 1 ? "" : "s"} at ${reason}${spec.condition !== void 0 ? "; the condition stays reviewed in the record" : ""}.`, { stepid: spec.stepid }).catch(() => void 0);
|
|
9935
|
+
}
|
|
9936
|
+
for (const id of decision.revertedoverrides) {
|
|
9937
|
+
const spec = active.overrides.find((candidate) => candidate.id === id);
|
|
9938
|
+
if (!spec) continue;
|
|
9939
|
+
const reverted = { ...spec, revertedat: at };
|
|
9940
|
+
await memory.addscriptoverride(reverted).catch(() => void 0);
|
|
9941
|
+
await audit("debugger", `Reverted the script override ${id} of ${spec.urlpattern} with ${spec.hits} applied evaluation${spec.hits === 1 ? "" : "s"} at ${reason}; the fixture source never enters the audit trail.`, { stepid: spec.stepid }).catch(() => void 0);
|
|
9942
|
+
}
|
|
9943
|
+
for (const rule of active.rules) {
|
|
9944
|
+
if (rule.closedat !== void 0) continue;
|
|
9945
|
+
await memory.setcdpeventrule({ ...rule, closedat: at }).catch(() => void 0);
|
|
9946
|
+
}
|
|
9947
|
+
await memory.setcdpsession(decision.session).catch(() => void 0);
|
|
9948
|
+
await audit("debugger", `Session ${active.session.id} of ${active.session.origin} detached at ${reason} with the reviewed domains ${active.session.domains.join(", ")} enabled${decision.keepsalive ? "; the session record stays alive for review because the user detached the debugger" : ""}.`, { sessionid: active.session.id, planid: runid, stepid: active.session.stepid }).catch(() => void 0);
|
|
9949
|
+
if (!userdetached) {
|
|
9950
|
+
const revoked = await memory.revokedebuggergrants(active.session.origin, at).catch(() => 0);
|
|
9951
|
+
if (revoked > 0) await audit("debugger", `Revoked ${revoked} debugger consent record${revoked === 1 ? "" : "s"} of ${active.session.origin} at ${reason}; the next attach needs a new reviewed prompt.`, { planid: runid }).catch(() => void 0);
|
|
9952
|
+
}
|
|
9953
|
+
if (decision.paused) await audit("pause", `The devtools teardown at ${reason} paused the run for review; the resume policy is ${decision.resumepolicy}.`, { planid: runid }).catch(() => void 0);
|
|
9954
|
+
activecdpsessions.delete(runid);
|
|
9955
|
+
await refreshbadge().catch(() => void 0);
|
|
9956
|
+
}
|
|
9957
|
+
function cdpstateof(runid, session, teardown) {
|
|
9958
|
+
const existing = activecdpsessions.get(runid);
|
|
9959
|
+
if (existing) return existing;
|
|
9960
|
+
const created = { session, queue: [], rules: [], breakpoints: [], overrides: [], ...teardown !== void 0 ? { teardown } : {}, cancelled: false };
|
|
9961
|
+
activecdpsessions.set(runid, created);
|
|
9962
|
+
return created;
|
|
9963
|
+
}
|
|
9964
|
+
function pauseframes(entry) {
|
|
9965
|
+
if (!Array.isArray(entry)) return [];
|
|
9966
|
+
return entry.flatMap((frame) => {
|
|
9967
|
+
if (!frame || typeof frame !== "object") return [];
|
|
9968
|
+
const record2 = frame;
|
|
9969
|
+
if (typeof record2.url !== "string" || typeof record2.line !== "number") return [];
|
|
9970
|
+
return [{ url: record2.url, line: record2.line, ...typeof record2.column === "number" ? { column: record2.column } : {}, ...typeof record2.functionname === "string" ? { functionname: record2.functionname } : {} }];
|
|
9971
|
+
});
|
|
9972
|
+
}
|
|
9973
|
+
async function executecdpstep(step, session, plan, tabid2, origin) {
|
|
9974
|
+
const options = stepoptions2(step);
|
|
9975
|
+
const extra = { sessionid: session.id, planid: plan.id, stepid: step.id };
|
|
9976
|
+
const debuggatecheck = debuggate(session, tabid2, origin, Date.now());
|
|
9977
|
+
if (!debuggatecheck.allowed) throw new Error(debuggatecheck.reason ?? "The devtools protocol step stays outside the debug gate.");
|
|
9978
|
+
const grants = await memory.getdebuggergrants();
|
|
9979
|
+
const domains = Array.isArray(options.domains) ? options.domains.filter((domain) => typeof domain === "string") : [];
|
|
9980
|
+
if (step.kind === "attachcdp") {
|
|
9981
|
+
const consent = debuggerconsentcovers(origin, domains, grants);
|
|
9982
|
+
if (!consent.allowed) {
|
|
9983
|
+
const pending = grants.find((grant) => grant.origin === origin && grant.approved === void 0 && grant.domains.join(",") === domains.join(","));
|
|
9984
|
+
if (!pending) {
|
|
9985
|
+
const record2 = { id: randomid(), prompt: `Debugger attach on ${origin} with the reviewed domains ${domains.join(", ")} enabled for run ${plan.id}.`, origin, domains, consentedat: Date.now() };
|
|
9986
|
+
await memory.setdebuggergrant(record2);
|
|
9987
|
+
await refreshbadge();
|
|
9988
|
+
}
|
|
9989
|
+
throw new Error(`${consent.reason} The prompt is open in the review panel with the domain allowlist shown; approve it and run the step again.`);
|
|
9990
|
+
}
|
|
9991
|
+
} else {
|
|
9992
|
+
const consent = debuggerconsentcovers(origin, planallowlist(plan.steps)?.domains ?? [], grants);
|
|
9993
|
+
if (!consent.allowed) throw new Error(consent.reason ?? "The reviewed debugger consent is missing.");
|
|
9994
|
+
}
|
|
9995
|
+
const active = activecdpsessions.get(plan.id);
|
|
9996
|
+
if (step.kind !== "attachcdp" && (!active || active.session.detachedat !== void 0)) throw new Error("No attached devtools session covers this run; run the attachcdp step first.");
|
|
9997
|
+
if (step.kind === "attachcdp") {
|
|
9998
|
+
const teardown = teardownplanof(options.teardown);
|
|
9999
|
+
const sessionid = randomid();
|
|
10000
|
+
const output = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The attach returned no result." };
|
|
10001
|
+
if (!output.ok) return output;
|
|
10002
|
+
const record2 = attachcdpsession({ id: sessionid, runid: plan.id, stepid: step.id, tabid: tabid2, origin, domains, now: Date.now(), debuggerversion: cdpderivation });
|
|
10003
|
+
cdpstateof(plan.id, record2, teardown);
|
|
10004
|
+
await memory.setcdpsession(record2);
|
|
10005
|
+
await memory.setprogress(recordcdp(await memory.getprogress(), plan.id, step.id, { family: "attach", domains: domains.length }, Date.now()));
|
|
10006
|
+
await audit("debugger", `Attached the devtools session ${sessionid} to the run tab ${tabid2} of ${origin} with the reviewed domains ${domains.join(", ")} enabled and the debugger version ${cdpderivation}; the teardown plan reverts ${teardown?.revertsteps.length ?? 0} step${(teardown?.revertsteps.length ?? 0) === 1 ? "" : "s"} with the ${teardown?.resumepolicy ?? "ask"} resume policy.`, extra);
|
|
10007
|
+
await refreshbadge();
|
|
10008
|
+
return { ok: true, summary: output.summary, details: { ...output.details ?? {}, session: record2, cdp: { sessionid, state: "attached", commandids: [] } } };
|
|
10009
|
+
}
|
|
10010
|
+
if (step.kind === "detachcdp") {
|
|
10011
|
+
if (!active) throw new Error("No attached devtools session covers this run.");
|
|
10012
|
+
const before = { breakpoints: active.breakpoints.filter((spec) => spec.revertedat === void 0).length, overrides: active.overrides.filter((spec) => spec.revertedat === void 0).length };
|
|
10013
|
+
await detachcdpforrun(plan.id, "the reviewed detachcdp step");
|
|
10014
|
+
await memory.setprogress(recordcdp(await memory.getprogress(), plan.id, step.id, { family: "detach", hits: before.breakpoints + before.overrides }, Date.now()));
|
|
10015
|
+
return { ok: true, summary: `Detached the devtools session ${active.session.id} cleanly after reverting ${before.breakpoints} breakpoint${before.breakpoints === 1 ? "" : "s"} and ${before.overrides} override${before.overrides === 1 ? "" : "s"}; the detach is confirmed.`, details: { detached: true, ...before, cdp: { sessionid: active.session.id, state: "detached", commandids: active.queue.map((command) => command.id) } } };
|
|
10016
|
+
}
|
|
10017
|
+
if (step.kind === "cdpcmd") {
|
|
10018
|
+
if (!active) throw new Error("No attached devtools session covers this run.");
|
|
10019
|
+
const command = options.command && typeof options.command === "object" && !Array.isArray(options.command) ? options.command : {};
|
|
10020
|
+
const method = typeof command.method === "string" ? command.method : "";
|
|
10021
|
+
const planlist = planallowlist(plan.steps);
|
|
10022
|
+
const allowlist = { domains: active.session.domains, ...planlist?.methods !== void 0 ? { methods: planlist.methods } : {} };
|
|
10023
|
+
if (!allowlistcovers(allowlist, method)) throw new Error(`The raw command ${method} stays outside the enabled domain allowlist of the run attach.`);
|
|
10024
|
+
const output = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The command returned no result." };
|
|
10025
|
+
const duration = typeof output.details?.duration === "number" ? output.details.duration : 0;
|
|
10026
|
+
const errorclass = typeof output.details?.errorclass === "string" ? output.details.errorclass : output.ok ? void 0 : "protocolerror";
|
|
10027
|
+
const record2 = sendcdpcommand({ id: randomid(), sessionid: active.session.id, runid: plan.id, stepid: step.id, method, ...command.params && typeof command.params === "object" && !Array.isArray(command.params) ? { params: command.params } : {}, ...typeof command.resultpath === "string" ? { resultpath: command.resultpath } : {}, duration, ...errorclass !== void 0 ? { errorclass } : {}, at: Date.now() });
|
|
10028
|
+
active.queue = serializecdpcommand(active.queue, record2);
|
|
10029
|
+
await memory.addcdpcommand(record2);
|
|
10030
|
+
await memory.setprogress(recordcdp(await memory.getprogress(), plan.id, step.id, { family: "command", method, domain: record2.domain, duration, ...errorclass !== void 0 ? { errorclass } : {} }, Date.now()));
|
|
10031
|
+
const pausedetails = output.details?.paused;
|
|
10032
|
+
let pauseid;
|
|
10033
|
+
if (pausedetails && typeof pausedetails === "object") {
|
|
10034
|
+
pauseid = randomid();
|
|
10035
|
+
const pause = capturepause({ id: pauseid, runid: plan.id, stepid: step.id, reason: typeof pausedetails.reason === "string" ? pausedetails.reason : "breakpoint", callframes: pauseframes(pausedetails.frames), ...typeof pausedetails.hitbreakpoint === "string" ? { hitbreakpoint: pausedetails.hitbreakpoint } : {}, at: Date.now() });
|
|
10036
|
+
await memory.addpause(pause);
|
|
10037
|
+
await audit("debugger", `The run paused on breakpoint ${pause.hitbreakpoint ?? "unknown"} with ${pause.callframes.length} call frame${pause.callframes.length === 1 ? "" : "s"} captured.`, extra);
|
|
10038
|
+
}
|
|
10039
|
+
await audit("debugger", `Sent the reviewed command ${method} of the ${record2.domain} domain to session ${active.session.id}; it returned in ${duration} millisecond${duration === 1 ? "" : "s"}${errorclass !== void 0 ? ` with the ${errorclass} error class` : ""}; the params never enter the audit trail.`, extra);
|
|
10040
|
+
await refreshbadge();
|
|
10041
|
+
if (!output.ok) return { ok: false, summary: output.summary, details: { ...output.details ?? {}, command: record2, cdp: { sessionid: active.session.id, state: "attached", commandids: active.queue.map((item) => item.id) } } };
|
|
10042
|
+
return { ok: true, summary: output.summary, details: { ...output.details ?? {}, command: record2, ...pauseid !== void 0 ? { pauseid } : {}, cdp: { sessionid: active.session.id, state: "attached", commandids: active.queue.map((item) => item.id) } } };
|
|
10043
|
+
}
|
|
10044
|
+
if (step.kind === "watchcdp") {
|
|
10045
|
+
if (!active) throw new Error("No attached devtools session covers this run.");
|
|
10046
|
+
const rules = [];
|
|
10047
|
+
for (const rule of Array.isArray(options.events) ? options.events : []) {
|
|
10048
|
+
const parsed = rule && typeof rule === "object" && !Array.isArray(rule) ? rule : void 0;
|
|
10049
|
+
const normalized = parsed !== void 0 ? await Promise.resolve(parsed) : void 0;
|
|
10050
|
+
const base = normalized !== void 0 ? { domain: String(normalized.domain ?? ""), event: String(normalized.event ?? ""), ...typeof normalized.match === "string" ? { match: normalized.match } : {} } : void 0;
|
|
10051
|
+
if (!base || !base.domain || !base.event) continue;
|
|
10052
|
+
const record2 = { id: randomid(), sessionid: active.session.id, runid: plan.id, stepid: step.id, domain: base.domain, event: base.event, ...base.match !== void 0 ? { match: base.match } : {}, events: 0, registeredat: Date.now() };
|
|
10053
|
+
rules.push(record2);
|
|
10054
|
+
active.rules.push(record2);
|
|
10055
|
+
await memory.setcdpeventrule(record2);
|
|
10056
|
+
}
|
|
10057
|
+
if (rules.length === 0) throw new Error("The event watch needs at least one reviewed domain event rule.");
|
|
10058
|
+
const output = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The event watch returned no result." };
|
|
10059
|
+
const observed = detailarray(output.details, "events").flatMap((event) => {
|
|
10060
|
+
if (!event || typeof event !== "object") return [];
|
|
10061
|
+
const record2 = event;
|
|
10062
|
+
if (typeof record2.domain !== "string" || typeof record2.event !== "string") return [];
|
|
10063
|
+
return [{ domain: record2.domain, event: record2.event, ...typeof record2.payload === "string" ? { payload: record2.payload } : {} }];
|
|
10064
|
+
});
|
|
10065
|
+
const matched = watchcdpevents(rules, observed);
|
|
10066
|
+
for (const rule of active.rules) {
|
|
10067
|
+
const count = matched.matched.filter((item) => item.ruleid === rule.id).length;
|
|
10068
|
+
if (count > 0) rule.events += count;
|
|
10069
|
+
await memory.setcdpeventrule({ ...rule });
|
|
10070
|
+
}
|
|
10071
|
+
for (const item of matched.matched) {
|
|
10072
|
+
await memory.addtimelineentry({ id: randomid(), runid: plan.id, stepid: step.id, time: Date.now(), level: "info", source: "cdp", message: `${item.domain}.${item.event}${item.payload !== void 0 ? `: ${item.payload}` : ""}` });
|
|
10073
|
+
}
|
|
10074
|
+
await memory.setprogress(recordcdp(await memory.getprogress(), plan.id, step.id, { family: "watch", events: matched.matched.length, ...rules[0] !== void 0 ? { domain: rules[0].domain } : {} }, Date.now()));
|
|
10075
|
+
await audit("debugger", `Watched ${rules.length} reviewed domain event rule${rules.length === 1 ? "" : "s"} of session ${active.session.id} for the reviewed window and forwarded ${matched.matched.length} matched event${matched.matched.length === 1 ? "" : "s"} into the run timeline; the subscription closes at run end or on step cancel.`, extra);
|
|
10076
|
+
await refreshbadge();
|
|
10077
|
+
return { ok: Boolean(output.ok), summary: output.summary, details: { ...output.details ?? {}, eventcounts: matched.counts, matched: matched.matched, cdp: { sessionid: active.session.id, state: "attached", commandids: [] } } };
|
|
10078
|
+
}
|
|
10079
|
+
if (step.kind === "setbreakpoint") {
|
|
10080
|
+
if (!active) throw new Error("No attached devtools session covers this run.");
|
|
10081
|
+
const input = breakpointinputof(options.breakpoint);
|
|
10082
|
+
if (!input) throw new Error("The breakpoint input is absent.");
|
|
10083
|
+
const settings = await memory.getsettings();
|
|
10084
|
+
const budgetcheck = breakpointbudgetallowed(active.breakpoints.filter((spec) => spec.revertedat === void 0).length, breakpointceilingof(settings));
|
|
10085
|
+
if (!budgetcheck.allowed) throw new Error(budgetcheck.reason ?? "The breakpoint ceiling refused the registration.");
|
|
10086
|
+
const output = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The breakpoint registration returned no result." };
|
|
10087
|
+
if (!output.ok) return output;
|
|
10088
|
+
const registered = output.details?.breakpoint;
|
|
10089
|
+
const id = typeof registered?.id === "string" ? registered.id : randomid();
|
|
10090
|
+
const record2 = { id, runid: plan.id, stepid: step.id, url: input.url, line: input.line, ...input.column !== void 0 ? { column: input.column } : {}, ...input.condition !== void 0 ? { condition: input.condition } : {}, hits: 0, registeredat: Date.now() };
|
|
10091
|
+
active.breakpoints.push(record2);
|
|
10092
|
+
await memory.addbreakpoint(record2);
|
|
10093
|
+
await memory.setprogress(recordcdp(await memory.getprogress(), plan.id, step.id, { family: "breakpoint", hits: 0 }, Date.now()));
|
|
10094
|
+
await audit("debugger", `Registered the reviewed breakpoint ${id} at ${input.url}:${input.line}${input.condition !== void 0 ? ` under the reviewed condition` : ""} of session ${active.session.id}; the run pauses when an instrumented probe of the run hits it.`, extra);
|
|
10095
|
+
return { ok: true, summary: output.summary, details: { ...output.details ?? {}, breakpoint: record2, cdp: { sessionid: active.session.id, state: "attached", commandids: [] } } };
|
|
10096
|
+
}
|
|
10097
|
+
if (step.kind === "stepcode") {
|
|
10098
|
+
if (!active) throw new Error("No attached devtools session covers this run.");
|
|
10099
|
+
const output = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The step returned no result." };
|
|
10100
|
+
const pausedetails = output.details?.pausestate;
|
|
10101
|
+
let pauseid;
|
|
10102
|
+
if (pausedetails && typeof pausedetails === "object") {
|
|
10103
|
+
pauseid = randomid();
|
|
10104
|
+
const domversion = await memory.nextobservationversion().catch(() => void 0);
|
|
10105
|
+
const pause = capturepause({ id: pauseid, runid: plan.id, stepid: step.id, reason: typeof pausedetails.reason === "string" ? pausedetails.reason : "step", callframes: pauseframes(pausedetails.frames), ...typeof pausedetails.hitbreakpoint === "string" ? { hitbreakpoint: pausedetails.hitbreakpoint } : {}, ...domversion !== void 0 ? { domsnapshotid: String(domversion) } : {}, at: Date.now() });
|
|
10106
|
+
await memory.addpause(pause);
|
|
10107
|
+
}
|
|
10108
|
+
await memory.setprogress(recordcdp(await memory.getprogress(), plan.id, step.id, { family: "step", frames: pauseframes(pausedetails?.frames).length }, Date.now()));
|
|
10109
|
+
await audit("debugger", `Stepped the paused probe of session ${active.session.id} with the ${stepmodeof(options.mode) ?? "reviewed"} mode and captured the pause state${pauseid !== void 0 ? ` as ${pauseid} with the dom snapshot through the page bridge` : ""}.`, extra);
|
|
10110
|
+
return { ok: Boolean(output.ok), summary: output.summary, details: { ...output.details ?? {}, ...pauseid !== void 0 ? { pauseid } : {}, cdp: { sessionid: active.session.id, state: "attached", commandids: [] } } };
|
|
10111
|
+
}
|
|
10112
|
+
if (step.kind === "watchexpr") {
|
|
10113
|
+
if (!active) throw new Error("No attached devtools session covers this run.");
|
|
10114
|
+
const input = watchexpressionof(options.expression);
|
|
10115
|
+
if (!input) throw new Error("The watch expression input is absent.");
|
|
10116
|
+
const output = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The watch expression returned no result." };
|
|
10117
|
+
const stored = (await memory.getwatchexpressions()).find((item) => item.stepid === step.id);
|
|
10118
|
+
const base = stored ?? { id: randomid(), runid: plan.id, stepid: step.id, expression: input.expression, scope: input.scope, reviewed: true, values: [], at: Date.now() };
|
|
10119
|
+
if (output.ok) {
|
|
10120
|
+
const value = typeof output.details?.value === "string" ? output.details.value : "";
|
|
10121
|
+
const pauses = await memory.listpauses(plan.id);
|
|
10122
|
+
const pauseid = pauses[0]?.id ?? "nopause";
|
|
10123
|
+
const updated = recordwatchvalue(base, pauseid, value, Date.now());
|
|
10124
|
+
await memory.setwatchexpression(updated);
|
|
10125
|
+
await memory.addtimelineentry({ id: randomid(), runid: plan.id, stepid: step.id, time: Date.now(), level: "debug", source: "cdp", message: `Watch expression ${input.expression} evaluated to ${value} at pause ${pauseid} in the ${input.scope} scope.` });
|
|
10126
|
+
}
|
|
10127
|
+
await memory.setprogress(recordcdp(await memory.getprogress(), plan.id, step.id, { family: "watch", events: base.values.length }, Date.now()));
|
|
10128
|
+
await audit("debugger", `Evaluated the reviewed watch expression of step ${step.id} at the pause in the ${input.scope} scope; the value stores with its pause scope in the timeline and the expression text stays reviewed.`, extra);
|
|
10129
|
+
return { ok: Boolean(output.ok), summary: output.summary, details: { ...output.details ?? {}, cdp: { sessionid: active.session.id, state: "attached", commandids: [] } } };
|
|
10130
|
+
}
|
|
10131
|
+
if (step.kind === "overridescript") {
|
|
10132
|
+
if (!active) throw new Error("No attached devtools session covers this run.");
|
|
10133
|
+
const override = options.override && typeof options.override === "object" && !Array.isArray(options.override) ? options.override : void 0;
|
|
10134
|
+
const urlpattern = typeof override?.urlpattern === "string" ? override.urlpattern : "";
|
|
10135
|
+
const source = typeof override?.source === "string" ? override.source : "";
|
|
10136
|
+
if (!urlpattern || !source) throw new Error("The script override input is absent.");
|
|
10137
|
+
const output = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The override returned no result." };
|
|
10138
|
+
if (!output.ok) return output;
|
|
10139
|
+
const applied = output.details?.override;
|
|
10140
|
+
const id = typeof applied?.id === "string" ? applied.id : randomid();
|
|
10141
|
+
const record2 = { id, runid: plan.id, stepid: step.id, urlpattern, source, reviewed: true, reviewedat: Date.now(), hits: 0, appliedat: Date.now() };
|
|
10142
|
+
active.overrides.push(record2);
|
|
10143
|
+
await memory.addscriptoverride(record2);
|
|
10144
|
+
await memory.setprogress(recordcdp(await memory.getprogress(), plan.id, step.id, { family: "override", hits: 0 }, Date.now()));
|
|
10145
|
+
await audit("debugger", `Applied the reviewed script override ${id} of ${urlpattern} on new document evaluation through the page instrumentation; the fixture reverts at run end and the source never enters the audit trail.`, extra);
|
|
10146
|
+
return { ok: true, summary: output.summary, details: { ...output.details ?? {}, override: { id, urlpattern, appliedat: record2.appliedat, reviewed: true }, cdp: { sessionid: active.session.id, state: "attached", commandids: [] } } };
|
|
10147
|
+
}
|
|
10148
|
+
return { ok: false, summary: "The devtools step is not part of the instrumented family." };
|
|
10149
|
+
}
|
|
9029
10150
|
var activerules = /* @__PURE__ */ new Map();
|
|
9030
10151
|
var activeauthflows = /* @__PURE__ */ new Map();
|
|
9031
10152
|
function rulesetof(runid) {
|
|
@@ -9415,11 +10536,13 @@ async function refreshbadge() {
|
|
|
9415
10536
|
const media = (await memory.getmediarecords()).length + (await memory.getimagebatches()).length;
|
|
9416
10537
|
const recordingprompts = (await memory.getrecordingconsents()).filter((record2) => record2.approved === void 0).length;
|
|
9417
10538
|
const fetchprompts = (await memory.getfetchconsents()).filter((consent) => consent.approved === void 0).length;
|
|
10539
|
+
const consoleprompts = (await memory.getconsoleconsents()).filter((consent) => consent.approved === void 0).length;
|
|
10540
|
+
const debuggerprompts = (await memory.getdebuggergrants()).filter((grant) => grant.approved === void 0).length;
|
|
9418
10541
|
const observedrequests = (await memory.getexchanges()).length;
|
|
9419
10542
|
const livechannels = (await memory.getchannels()).filter((channel) => channel.state === "open" || channel.state === "connecting").length;
|
|
9420
10543
|
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
10544
|
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;
|
|
10545
|
+
const total = (queues?.prefetch ?? 0) + (queues?.batchopen ?? 0) + tasktabs2 + prompts + consents + quarantined + datasets + captures + media + recordingprompts + fetchprompts + consoleprompts + debuggerprompts + observedrequests + livechannels + activerulescount;
|
|
9423
10546
|
await chrome.action.setBadgeText({ text: total > 0 ? String(total) : "" }).catch(() => {
|
|
9424
10547
|
});
|
|
9425
10548
|
}
|
|
@@ -9487,6 +10610,12 @@ async function executestep(stepid) {
|
|
|
9487
10610
|
output = await executenetwatchstep(step, session, plan, tab.id, origin);
|
|
9488
10611
|
} else if (iscontrolkind(step.kind)) {
|
|
9489
10612
|
output = await executenetcontrolstep(step, session, plan, tab.id, origin);
|
|
10613
|
+
} else if (isdebugkind(step.kind)) {
|
|
10614
|
+
if (!session || !plan || plan.state !== "approved") throw new Error("Debugging kinds refuse to run outside an approved session plan.");
|
|
10615
|
+
output = await executetimelinestep(step, session, plan, tab.id, origin);
|
|
10616
|
+
} else if (iscdpkind(step.kind)) {
|
|
10617
|
+
if (!session || !plan || plan.state !== "approved") throw new Error("Devtools protocol kinds refuse to run outside an approved session plan.");
|
|
10618
|
+
output = await executecdpstep(step, session, plan, tab.id, origin);
|
|
9490
10619
|
} else {
|
|
9491
10620
|
if (step.target && freshcheckkinds.has(step.kind)) {
|
|
9492
10621
|
const fresh = await snapshot(tab.id);
|
|
@@ -9544,6 +10673,8 @@ async function executestep(stepid) {
|
|
|
9544
10673
|
});
|
|
9545
10674
|
await revertcontrolsforrun(plan.id, "plan completion").catch(() => {
|
|
9546
10675
|
});
|
|
10676
|
+
await detachcdpforrun(plan.id, "plan completion").catch(() => {
|
|
10677
|
+
});
|
|
9547
10678
|
const done = { ...plan, state: "completed", completedat: Date.now() };
|
|
9548
10679
|
await memory.setplan(done);
|
|
9549
10680
|
await audit("complete", "Every reviewed step of the approved plan has executed.", { ...session ? { sessionid: session.id } : {}, planid: done.id });
|
|
@@ -9706,6 +10837,8 @@ async function handlerequest(message, sender) {
|
|
|
9706
10837
|
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
10838
|
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
10839
|
const tokens = authreport({ tokens: await memory.listtokens() });
|
|
10840
|
+
const timelineentries = await memory.gettimeline();
|
|
10841
|
+
const timeline = timelinereport({ entries: timelineentries, errors: await memory.geterrorrecords(), rejections: await memory.getrejectionrecords(), longtasks: await memory.getlongtasks(), levelcounts: timelinecounts(timelineentries) });
|
|
9709
10842
|
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
10843
|
const runsettings = await memory.getsettings();
|
|
9711
10844
|
const scanhooks = [];
|
|
@@ -9718,7 +10851,7 @@ async function handlerequest(message, sender) {
|
|
|
9718
10851
|
const livetab = session ? await chrome.tabs.get(session.tabid).catch(() => void 0) : void 0;
|
|
9719
10852
|
const waitprofile = session ? waitprofiles.find((record2) => record2.origin === session.origin) : void 0;
|
|
9720
10853
|
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()] } : {} };
|
|
10854
|
+
return { config: await memory.getconfig(), session, plan, progress: plan && progress?.planid === plan.id ? progress : void 0, diagnostic: await memory.getdiagnostic(), audit: await memory.getaudit(), capabilities: await refreshcapabilities(), outcomes: await memory.getoutcomes(), holds: heldkeysreport({ tabid: session?.tabid ?? 0, holds }), dialogs: await memory.getdialogs(), retries: await memory.getretries(), ...signals ? { signals: signalsreport({ signals }) } : { signals: signalsreport({}) }, banners: await memory.getbanners(), mutationevents: await memory.getmutationevents(), focusevents: await memory.getfocusevents(), diffs: await memory.getdiffs(), selectors: await memory.getselectors(), ...a11y ? { a11y } : {}, ...reader ? { reader } : {}, ...map ? { map } : {}, trail: trailreport({ ...session ? { sessionid: session.id } : {}, trail }), navrecords, ratestates, safeties, curated, waitprofiles, auths, navcontrol, navqueues, artifacts, navstate: livestate, ...waitprofile ? { waitprofile } : {}, offline: !navigator.onLine, tabs, windows, layouts: layoutreport({ layouts }), tabgroups, tabmetas, badges, snapshots, closedtabs, tabwatchevents, clones, tasktabgauge: taskgauge, ...controltab ? { controltab } : {}, tabreport: report, profiles, tickets, wizards: wizardreport({ ...session ? { sessionid: session.id } : {}, wizards, picks }), picks, errorreports, captchas, detections, ...codeentry !== void 0 ? { codeentry: true } : {}, datasets, imports, extractsessions, streams, exports, provenances, taskrules, sheetendpoints: sheetgrants, downloads, netlogs, clipconsents, clips, quarantines, cleanuprules, cleanupruns, capturecounters, inventory, mimefilters, scanhooks, captures: capturemetadata, capturepairs, capturepolicy: runsettings?.capturepolicy ?? "manual", media: mediarecords, imagebatches, recordingconsents, recordingactive: [...activerecordings.values()].map((active) => ({ id: active.record.id, kind: active.record.kind, scope: active.record.scope, startedat: active.record.startedat, stopat: active.stopat })), recordingwindow: runsettings?.recordingwindow, calls, endpoints, fetchconsents, apikeys, callretention: runsettings?.callretention, fetchesactive: activefetches.size, exchanges, channels, subscriptions, apimap, messages: messagecount, webrequestgrant: runsettings?.webrequestgrant === true, bodyretention: runsettings?.bodyretention, timelineretention: runsettings?.timelineretention, timeline, consoleconsents: await memory.getconsoleconsents(), rotationtargets: await memory.getrotationtargets(), levelsummaries: await memory.getlevelsummaries(), cdpsessions: await memory.getcdpsessions(), cdpcommands: await memory.getcdpcommands(), cdpeventrules: await memory.getcdpeventrules(), breakpoints: await memory.getbreakpoints(), pauses: await memory.getpauses(), watchexpressions: await memory.getwatchexpressions(), scriptoverrides: await memory.getscriptoverrides(), debuggergrants: await memory.getdebuggergrants(), pauseretention: runsettings?.pauseretention, breakpointceiling: runsettings?.breakpointceiling, cdpattached: [...activecdpsessions.values()].filter((active) => active.session.detachedat === void 0).length, 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
10855
|
}
|
|
9723
10856
|
case "capabilities":
|
|
9724
10857
|
return refreshcapabilities();
|
|
@@ -9763,7 +10896,8 @@ async function handlerequest(message, sender) {
|
|
|
9763
10896
|
const capture = outcome.details?.capture;
|
|
9764
10897
|
const media = outcome.details?.media;
|
|
9765
10898
|
const network = outcome.details?.network;
|
|
9766
|
-
|
|
10899
|
+
const timeline = outcome.details?.timeline;
|
|
10900
|
+
return JSON.parse(outcomeresponse({ outcome, plan, ...resolved ? { resolvedtarget: resolved } : {}, ...capture ? { capture } : {}, ...media ? { media } : {}, ...network ? { network } : {}, ...timeline ? { timeline } : {} }));
|
|
9767
10901
|
}
|
|
9768
10902
|
case "map": {
|
|
9769
10903
|
const plan = await memory.getplan();
|
|
@@ -10374,6 +11508,39 @@ async function handlerequest(message, sender) {
|
|
|
10374
11508
|
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
11509
|
return { bodyretention: retention };
|
|
10376
11510
|
}
|
|
11511
|
+
case "settimelineretention": {
|
|
11512
|
+
const inputretention = message;
|
|
11513
|
+
const settings = await memory.getsettings();
|
|
11514
|
+
const retention = typeof inputretention.retention === "number" && Number.isInteger(inputretention.retention) && inputretention.retention >= 0 ? inputretention.retention : void 0;
|
|
11515
|
+
await memory.setsettings({ ...settings, ...retention !== void 0 ? { timelineretention: retention } : {} });
|
|
11516
|
+
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.`);
|
|
11517
|
+
return { timelineretention: retention };
|
|
11518
|
+
}
|
|
11519
|
+
case "approveconsoleconsent": {
|
|
11520
|
+
const inputapprove = message;
|
|
11521
|
+
const records = await memory.getconsoleconsents();
|
|
11522
|
+
const record2 = records.find((item) => item.id === inputapprove.id);
|
|
11523
|
+
if (!record2) throw new Error("No console capture prompt matches the id.");
|
|
11524
|
+
const decided = { ...record2, approved: true, usedat: Date.now() };
|
|
11525
|
+
await memory.setconsoleconsent(decided);
|
|
11526
|
+
await audit("consent", `Console capture on ${record2.origin} approved from the review panel; the decision persists for that origin.`, { stepid: record2.stepid });
|
|
11527
|
+
await refreshbadge();
|
|
11528
|
+
return { approved: true, origin: record2.origin };
|
|
11529
|
+
}
|
|
11530
|
+
case "consolediff": {
|
|
11531
|
+
const inputdiff = message;
|
|
11532
|
+
const base = inputdiff.base?.trim();
|
|
11533
|
+
const target = inputdiff.target?.trim();
|
|
11534
|
+
if (!base || !target) throw new Error("Console diffing needs the two reviewed run ids.");
|
|
11535
|
+
if (base === target) throw new Error("Console diffing needs two different run ids.");
|
|
11536
|
+
const baselines = (await memory.listtimeline({ runid: base })).filter((entry) => entry.source === "console").map((entry) => entry.message);
|
|
11537
|
+
const targetlines = (await memory.listtimeline({ runid: target })).filter((entry) => entry.source === "console").map((entry) => entry.message);
|
|
11538
|
+
if (baselines.length === 0 && targetlines.length === 0) throw new Error("Neither run stored console output yet; run watchconsole on both runs first.");
|
|
11539
|
+
const diff = consolediff({ baseid: base, targetid: target, baselines, targetlines, now: Date.now() });
|
|
11540
|
+
await memory.addconsolediff(diff);
|
|
11541
|
+
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"}.`);
|
|
11542
|
+
return consolediffreport({ diff });
|
|
11543
|
+
}
|
|
10377
11544
|
case "trafficreport": {
|
|
10378
11545
|
const plan = await memory.getplan();
|
|
10379
11546
|
if (!plan) throw new Error("No plan is available for a traffic control envelope.");
|
|
@@ -10458,12 +11625,81 @@ async function handlerequest(message, sender) {
|
|
|
10458
11625
|
await refreshbadge();
|
|
10459
11626
|
return { closed: true, id: inputclose.id ?? "" };
|
|
10460
11627
|
}
|
|
11628
|
+
case "setpauseretention": {
|
|
11629
|
+
const inputretention = message;
|
|
11630
|
+
const settings = await memory.getsettings();
|
|
11631
|
+
const retention = typeof inputretention.retention === "number" && Number.isInteger(inputretention.retention) && inputretention.retention >= 0 ? inputretention.retention : void 0;
|
|
11632
|
+
await memory.setsettings({ ...settings, ...retention !== void 0 ? { pauseretention: retention } : {} });
|
|
11633
|
+
await audit("configure", `The user set the pause capture retention to ${retention === void 0 ? "keep every capture" : `${retention} capture${retention === 1 ? "" : "s"}`}; the pause reason and hit breakpoint always survive.`);
|
|
11634
|
+
return { pauseretention: retention };
|
|
11635
|
+
}
|
|
11636
|
+
case "setbreakpointceiling": {
|
|
11637
|
+
const inputceiling = message;
|
|
11638
|
+
const settings = await memory.getsettings();
|
|
11639
|
+
const ceiling = typeof inputceiling.ceiling === "number" && Number.isInteger(inputceiling.ceiling) && inputceiling.ceiling >= 0 ? inputceiling.ceiling : void 0;
|
|
11640
|
+
await memory.setsettings({ ...settings, ...ceiling !== void 0 ? { breakpointceiling: ceiling } : {} });
|
|
11641
|
+
await audit("configure", `The user set the breakpoint ceiling to ${ceiling === void 0 ? "no ceiling" : `${ceiling} breakpoint${ceiling === 1 ? "" : "s"} per run`}; an absent value never refuses a breakpoint.`);
|
|
11642
|
+
return { breakpointceiling: ceiling };
|
|
11643
|
+
}
|
|
11644
|
+
case "approvedebuggerconsent": {
|
|
11645
|
+
const inputapprove = message;
|
|
11646
|
+
const records = await memory.getdebuggergrants();
|
|
11647
|
+
const record2 = records.find((item) => item.id === inputapprove.id);
|
|
11648
|
+
if (!record2) throw new Error("No debugger consent prompt matches the id.");
|
|
11649
|
+
const decided = { ...record2, approved: true, usedat: Date.now() };
|
|
11650
|
+
await memory.setdebuggergrant(decided);
|
|
11651
|
+
await audit("debugger", `Debugger consent on ${record2.origin} approved from the review panel with the domain allowlist ${record2.domains.join(", ")} shown; the decision covers those domains only.`, { stepid: record2.id });
|
|
11652
|
+
await refreshbadge();
|
|
11653
|
+
return { approved: true, origin: record2.origin, domains: record2.domains };
|
|
11654
|
+
}
|
|
11655
|
+
case "revokedebuggerconsent": {
|
|
11656
|
+
const session = await memory.getsession();
|
|
11657
|
+
const plan = await memory.getplan();
|
|
11658
|
+
const origin = session?.origin;
|
|
11659
|
+
if (!origin) throw new Error("No active session origin covers a debugger consent revoke.");
|
|
11660
|
+
const revoked = await memory.revokedebuggergrants(origin, Date.now());
|
|
11661
|
+
if (plan) await detachcdpforrun(plan.id, "the user detached the debugger", true);
|
|
11662
|
+
if (session) await memory.setsession({ ...session, pausedat: session.pausedat ?? Date.now() });
|
|
11663
|
+
await audit("debugger", `The user detached the debugger: ${revoked} consent record${revoked === 1 ? "" : "s"} of ${origin} revoked, every breakpoint and override reverted, the session record stays alive for review and the run paused for review before continuing.`, { ...session ? { sessionid: session.id } : {}, ...plan ? { planid: plan.id } : {} });
|
|
11664
|
+
await refreshbadge();
|
|
11665
|
+
return { revoked, paused: true };
|
|
11666
|
+
}
|
|
11667
|
+
case "revertcdpoverride": {
|
|
11668
|
+
const inputrevert = message;
|
|
11669
|
+
const plan = await memory.getplan();
|
|
11670
|
+
if (!plan) throw new Error("No plan is available for a script override revert.");
|
|
11671
|
+
const active = activecdpsessions.get(plan.id);
|
|
11672
|
+
const stored = (await memory.getscriptoverrides()).find((spec) => spec.id === inputrevert.id);
|
|
11673
|
+
if (!stored) throw new Error(`No script override matches ${inputrevert.id ?? ""}.`);
|
|
11674
|
+
if (stored.revertedat !== void 0) throw new Error("The script override already reverted.");
|
|
11675
|
+
const reverted = { ...stored, revertedat: Date.now() };
|
|
11676
|
+
if (active) {
|
|
11677
|
+
const index = active.overrides.findIndex((spec) => spec.id === stored.id);
|
|
11678
|
+
if (index >= 0) active.overrides[index] = reverted;
|
|
11679
|
+
}
|
|
11680
|
+
await memory.addscriptoverride(reverted);
|
|
11681
|
+
await audit("debugger", `Reverted the script override ${reverted.id} of ${reverted.urlpattern} from the review panel; later evaluations run the original source again and the fixture source never entered the audit trail.`, { planid: plan.id, stepid: reverted.stepid });
|
|
11682
|
+
await refreshbadge();
|
|
11683
|
+
return { reverted: true, id: reverted.id };
|
|
11684
|
+
}
|
|
11685
|
+
case "cdpreport": {
|
|
11686
|
+
const plan = await memory.getplan();
|
|
11687
|
+
if (!plan) throw new Error("No plan is available for a devtools protocol envelope.");
|
|
11688
|
+
return cdpreport({ sessions: await memory.getcdpsessions(), commands: await memory.getcdpcommands(), events: await memory.getcdpeventrules(), breakpoints: await memory.getbreakpoints(), pauses: await memory.getpauses(), watches: await memory.getwatchexpressions(), overrides: await memory.getscriptoverrides(), grants: await memory.getdebuggergrants() });
|
|
11689
|
+
}
|
|
10461
11690
|
case "stop": {
|
|
10462
11691
|
const session = await memory.getsession();
|
|
10463
11692
|
for (const [id, controller] of [...activefetches.entries()]) {
|
|
10464
11693
|
controller.abort();
|
|
10465
11694
|
activefetches.delete(id);
|
|
10466
11695
|
}
|
|
11696
|
+
for (const [id, watcher] of [...activetimelinewatchers.entries()]) {
|
|
11697
|
+
watcher.cancelled = true;
|
|
11698
|
+
await memory.closewatch(id, Date.now()).catch(() => {
|
|
11699
|
+
});
|
|
11700
|
+
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 } : {} });
|
|
11701
|
+
activetimelinewatchers.delete(id);
|
|
11702
|
+
}
|
|
10467
11703
|
const stoppedplan = await memory.getplan();
|
|
10468
11704
|
if (stoppedplan) {
|
|
10469
11705
|
await closechannelsforrun(stoppedplan.id).catch(() => {
|
|
@@ -10472,12 +11708,19 @@ async function handlerequest(message, sender) {
|
|
|
10472
11708
|
});
|
|
10473
11709
|
await revertcontrolsforrun(stoppedplan.id, "run cancel").catch(() => {
|
|
10474
11710
|
});
|
|
11711
|
+
await detachcdpforrun(stoppedplan.id, "run cancel").catch(() => {
|
|
11712
|
+
});
|
|
10475
11713
|
} else {
|
|
10476
11714
|
await closechannelsforrun("none").catch(() => {
|
|
10477
11715
|
});
|
|
10478
11716
|
await revertcontrolsforrun("none", "run cancel").catch(() => {
|
|
10479
11717
|
});
|
|
10480
11718
|
}
|
|
11719
|
+
for (const [runid, active] of [...activecdpsessions.entries()]) {
|
|
11720
|
+
active.cancelled = true;
|
|
11721
|
+
await detachcdpforrun(runid, "run cancel").catch(() => {
|
|
11722
|
+
});
|
|
11723
|
+
}
|
|
10481
11724
|
for (const [id, active] of [...activerecordings.entries()]) {
|
|
10482
11725
|
const finished = finishrecording(active.record, Date.now());
|
|
10483
11726
|
await memory.addmedia(finished).catch(() => {
|