@wenathlan/extension 1.1.37 → 1.1.39
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/index.js +440 -8
- package/dist/index.js.map +2 -2
- package/dist/memory.d.ts +83 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/policy.d.ts +25 -1
- package/dist/policy.d.ts.map +1 -1
- package/dist/protocol.d.ts +46 -1
- package/dist/protocol.d.ts.map +1 -1
- package/dist/types.d.ts +209 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/extension/dist/background.js +1540 -14
- package/extension/dist/background.js.map +4 -4
- package/extension/dist/manifest.json +1 -1
- package/extension/dist/pagebridge.js +199 -2
- package/extension/dist/pagebridge.js.map +4 -4
- package/extension/dist/popup.html +1 -1
- package/extension/dist/popup.js +41 -2
- package/extension/dist/popup.js.map +2 -2
- package/extension/dist/sidepanel.html +1 -1
- package/extension/dist/sidepanel.js +340 -2
- package/extension/dist/sidepanel.js.map +4 -4
- package/extension/manifest.json +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// policy.ts
|
|
2
|
-
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"]);
|
|
2
|
+
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"]);
|
|
3
3
|
var interactionactions = /* @__PURE__ */ new Set(["focus", "scroll", "hover", "clickdeep", "rightclick", "doubleclick", "scrollpage", "scrollby", "scrollend", "scrolltop", "fullscreen", "zoomset", "movepointer", "clicktext", "clickaria", "clickname", "expanddetails", "pierceshadow", "retryaction"]);
|
|
4
|
-
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"]);
|
|
4
|
+
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"]);
|
|
5
5
|
var allowedactions = /* @__PURE__ */ new Set([...sensitiveactions, ...interactionactions, ...readactions]);
|
|
6
6
|
|
|
7
7
|
// extension/tabscommand.ts
|
|
@@ -28,6 +28,19 @@ function cardmask(value) {
|
|
|
28
28
|
return "\u2022".repeat(trimmed.length);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
// extension/datacommand.ts
|
|
32
|
+
function sortrows(rows, key, direction) {
|
|
33
|
+
const sign = direction === "desc" ? -1 : 1;
|
|
34
|
+
return [...rows].sort((left, right) => {
|
|
35
|
+
const a = left[key] ?? "";
|
|
36
|
+
const b = right[key] ?? "";
|
|
37
|
+
const numeric = Number(a);
|
|
38
|
+
const numericb = Number(b);
|
|
39
|
+
if (Number.isFinite(numeric) && Number.isFinite(numericb) && a.trim() !== "" && b.trim() !== "") return (numeric - numericb) * sign;
|
|
40
|
+
return a.localeCompare(b) * sign;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
31
44
|
// extension/sidepanel.ts
|
|
32
45
|
var objective = document.querySelector("#objective");
|
|
33
46
|
var localbutton = document.querySelector("#localplan");
|
|
@@ -48,6 +61,8 @@ var trailroot = document.querySelector("#trail");
|
|
|
48
61
|
var navigationroot = document.querySelector("#navigation");
|
|
49
62
|
var tabswindowsroot = document.querySelector("#tabswindows");
|
|
50
63
|
var formsroot = document.querySelector("#forms");
|
|
64
|
+
var datasetsroot = document.querySelector("#datasets");
|
|
65
|
+
var filesroot = document.querySelector("#files");
|
|
51
66
|
var statusnode = document.querySelector("#status");
|
|
52
67
|
var progressnode = document.querySelector("#planprogress");
|
|
53
68
|
var capabilitiestext = document.querySelector("#capabilitiestext");
|
|
@@ -150,6 +165,19 @@ function quietdetail(step, outcomes) {
|
|
|
150
165
|
node.textContent = `network quiet: ${samples.length} sample${samples.length === 1 ? "" : "s"} \xB7 quiet for ${Math.round(last?.quietfor ?? 0)} ms \xB7 idle threshold ${idle} ms \xB7 ${latest.ok ? "quiet reached" : "still busy"}`;
|
|
151
166
|
return node;
|
|
152
167
|
}
|
|
168
|
+
function loopdetail(step, progress) {
|
|
169
|
+
if (step.kind !== "looprows") return null;
|
|
170
|
+
const iterations = (progress?.outcomes ?? []).filter((outcome) => outcome.stepid === step.id && outcome.details?.iteration !== void 0);
|
|
171
|
+
if (iterations.length === 0) return null;
|
|
172
|
+
const node = document.createElement("p");
|
|
173
|
+
node.className = "timeline";
|
|
174
|
+
const latest = iterations[iterations.length - 1];
|
|
175
|
+
if (!latest) return null;
|
|
176
|
+
const variables = latest.details?.variables;
|
|
177
|
+
const shown = variables ? Object.entries(variables).slice(0, 4).map(([key, value]) => `${key}=${value}`).join(", ") : "";
|
|
178
|
+
node.textContent = `loop timeline: ${iterations.length} iteration${iterations.length === 1 ? "" : "s"} \xB7 ${String(latest.details?.variable ?? "row")} variables ${shown}`;
|
|
179
|
+
return node;
|
|
180
|
+
}
|
|
153
181
|
function navlistdetail(step, progress, outcomes) {
|
|
154
182
|
if (step.kind !== "navlist") return null;
|
|
155
183
|
const entries = (progress?.outcomes ?? []).filter((outcome) => outcome.stepid === step.id && outcome.details?.naventry !== void 0).map((outcome) => outcome.details?.naventry);
|
|
@@ -208,6 +236,8 @@ function stepitem(plan, step, completed, outcomes, retries, progress) {
|
|
|
208
236
|
if (timeline) item.append(timeline);
|
|
209
237
|
const navlist = navlistdetail(step, progress, outcomes);
|
|
210
238
|
if (navlist) item.append(navlist);
|
|
239
|
+
const loopvars = loopdetail(step, progress);
|
|
240
|
+
if (loopvars) item.append(loopvars);
|
|
211
241
|
const redirects = redirectdetail(step, outcomes);
|
|
212
242
|
if (redirects) item.append(redirects);
|
|
213
243
|
const preview = renderpreview(step);
|
|
@@ -965,6 +995,312 @@ function renderforms(context) {
|
|
|
965
995
|
}
|
|
966
996
|
formsroot.append(codecard);
|
|
967
997
|
}
|
|
998
|
+
function renderdatasets(context) {
|
|
999
|
+
if (!datasetsroot) return;
|
|
1000
|
+
datasetsroot.replaceChildren();
|
|
1001
|
+
const outcomes = context.outcomes ?? [];
|
|
1002
|
+
const active = context.session && !context.session.stoppedat && context.session.expiresat > Date.now();
|
|
1003
|
+
const extractsessions = context.extractsessions ?? [];
|
|
1004
|
+
for (const extract of extractsessions.slice(0, 4)) {
|
|
1005
|
+
const card = document.createElement("p");
|
|
1006
|
+
const interrupted2 = extract.done !== true;
|
|
1007
|
+
card.textContent = `extraction ${extract.name}: ${extract.pages.length} page${extract.pages.length === 1 ? "" : "s"} visited \xB7 ${extract.rows} row${extract.rows === 1 ? "" : "s"} collected \xB7 cursor ${extract.cursor} of ${extract.planned}${extract.done ? " \xB7 complete" : " \xB7 interrupted"}`;
|
|
1008
|
+
datasetsroot.append(card);
|
|
1009
|
+
}
|
|
1010
|
+
const interrupted = extractsessions.find((extract) => extract.done !== true);
|
|
1011
|
+
if (interrupted && active) {
|
|
1012
|
+
const prompt = document.createElement("div");
|
|
1013
|
+
prompt.className = "bannercard";
|
|
1014
|
+
prompt.textContent = `Extraction ${interrupted.name} was interrupted at cursor ${interrupted.cursor}; run its resumeextract reviewed step to continue from the stored cursor.`;
|
|
1015
|
+
datasetsroot.append(prompt);
|
|
1016
|
+
}
|
|
1017
|
+
const streams = context.streams ?? [];
|
|
1018
|
+
if (streams.length > 0) {
|
|
1019
|
+
const stream = streams[0];
|
|
1020
|
+
const card = document.createElement("p");
|
|
1021
|
+
card.textContent = `stream state: ${stream.name} chunk ${stream.chunk} of ${stream.chunks} \xB7 ${stream.written} row${stream.written === 1 ? "" : "s"} written${stream.done ? " \xB7 complete" : " \xB7 resumable"}`;
|
|
1022
|
+
datasetsroot.append(card);
|
|
1023
|
+
}
|
|
1024
|
+
const rules = (context.taskrules ?? [])[0];
|
|
1025
|
+
if (rules && rules.transforms.length > 0) {
|
|
1026
|
+
const transforms = document.createElement("p");
|
|
1027
|
+
transforms.textContent = `transform rules: ${rules.transforms.map((rule) => `${rule.sources.join("+")} \u2192 ${rule.target} (${rule.expression})`).join(" \xB7 ")}`;
|
|
1028
|
+
datasetsroot.append(transforms);
|
|
1029
|
+
}
|
|
1030
|
+
if (rules && rules.dedupekeys.length > 0) {
|
|
1031
|
+
const keys = document.createElement("p");
|
|
1032
|
+
keys.textContent = `dedupe keys: ${rules.dedupekeys.join(", ")}`;
|
|
1033
|
+
datasetsroot.append(keys);
|
|
1034
|
+
}
|
|
1035
|
+
const dedupeoutcome = [...outcomes].reverse().find((outcome) => outcome.details?.dedupe !== void 0);
|
|
1036
|
+
if (dedupeoutcome) {
|
|
1037
|
+
const dedupe = dedupeoutcome.details?.dedupe;
|
|
1038
|
+
const card = document.createElement("p");
|
|
1039
|
+
card.textContent = `last dedupe: removed ${dedupe.removed} duplicate row${dedupe.removed === 1 ? "" : "s"}, kept ${dedupe.kept} by ${dedupe.keys.join(", ")}`;
|
|
1040
|
+
datasetsroot.append(card);
|
|
1041
|
+
}
|
|
1042
|
+
for (const datasetvalue of (context.datasets ?? []).slice(0, 4)) {
|
|
1043
|
+
const card = document.createElement("div");
|
|
1044
|
+
card.className = "panel";
|
|
1045
|
+
const head = document.createElement("p");
|
|
1046
|
+
head.textContent = `dataset ${datasetvalue.name}: ${datasetvalue.rows.length} row${datasetvalue.rows.length === 1 ? "" : "s"} \xB7 ${datasetvalue.columns.length} column${datasetvalue.columns.length === 1 ? "" : "s"}${(context.imports ?? []).some((item) => item.id === datasetvalue.id) ? " \xB7 imported csv" : ""}`;
|
|
1047
|
+
card.append(head);
|
|
1048
|
+
const grid = document.createElement("table");
|
|
1049
|
+
const headerrow = document.createElement("tr");
|
|
1050
|
+
for (const column of datasetvalue.columns.slice(0, 6)) {
|
|
1051
|
+
const cell = document.createElement("th");
|
|
1052
|
+
cell.textContent = `${column.label || column.key} ${column.kind === "number" ? "#" : ""}`;
|
|
1053
|
+
cell.addEventListener("click", () => {
|
|
1054
|
+
const body2 = grid.querySelector("tbody");
|
|
1055
|
+
if (!body2) return;
|
|
1056
|
+
const sorted = sortrows(datasetvalue.rows.slice(0, 5), column.key, cell.dataset.sorted === "asc" ? "desc" : "asc");
|
|
1057
|
+
cell.dataset.sorted = cell.dataset.sorted === "asc" ? "desc" : "asc";
|
|
1058
|
+
body2.replaceChildren(...sorted.map((row) => {
|
|
1059
|
+
const line = document.createElement("tr");
|
|
1060
|
+
for (const columnspec of datasetvalue.columns.slice(0, 6)) {
|
|
1061
|
+
const value = document.createElement("td");
|
|
1062
|
+
value.textContent = row[columnspec.key] ?? "";
|
|
1063
|
+
line.append(value);
|
|
1064
|
+
}
|
|
1065
|
+
return line;
|
|
1066
|
+
}));
|
|
1067
|
+
});
|
|
1068
|
+
headerrow.append(cell);
|
|
1069
|
+
}
|
|
1070
|
+
grid.append(headerrow);
|
|
1071
|
+
const body = document.createElement("tbody");
|
|
1072
|
+
for (const row of datasetvalue.rows.slice(0, 5)) {
|
|
1073
|
+
const line = document.createElement("tr");
|
|
1074
|
+
for (const column of datasetvalue.columns.slice(0, 6)) {
|
|
1075
|
+
const cell = document.createElement("td");
|
|
1076
|
+
cell.textContent = row[column.key] ?? "";
|
|
1077
|
+
line.append(cell);
|
|
1078
|
+
}
|
|
1079
|
+
body.append(line);
|
|
1080
|
+
}
|
|
1081
|
+
grid.append(body);
|
|
1082
|
+
card.append(grid);
|
|
1083
|
+
if (active) {
|
|
1084
|
+
const actions = document.createElement("p");
|
|
1085
|
+
for (const format of ["csv", "json", "excel"]) {
|
|
1086
|
+
actions.append(" ", button(`Export ${format}`, async () => {
|
|
1087
|
+
const artifact = await request({ kind: "exportdataset", datasetid: datasetvalue.id, format });
|
|
1088
|
+
status(`Exported ${datasetvalue.name} to ${artifact.name} with checksum ${artifact.checksum}.`);
|
|
1089
|
+
await refresh();
|
|
1090
|
+
}));
|
|
1091
|
+
}
|
|
1092
|
+
card.append(actions);
|
|
1093
|
+
}
|
|
1094
|
+
datasetsroot.append(card);
|
|
1095
|
+
}
|
|
1096
|
+
const provenances = context.provenances ?? [];
|
|
1097
|
+
if (provenances.length > 0) {
|
|
1098
|
+
const list = document.createElement("ul");
|
|
1099
|
+
for (const record of provenances.slice(0, 6)) {
|
|
1100
|
+
const item = document.createElement("li");
|
|
1101
|
+
item.textContent = `${record.name}: rows ${record.rowstart}\u2013${record.rowend} \xB7 checksum ${record.checksum} \xB7 source ${record.url}`;
|
|
1102
|
+
list.append(item);
|
|
1103
|
+
}
|
|
1104
|
+
datasetsroot.append(list);
|
|
1105
|
+
}
|
|
1106
|
+
const sheetendpoints = context.sheetendpoints ?? [];
|
|
1107
|
+
if (sheetendpoints.length > 0) {
|
|
1108
|
+
const sheets = document.createElement("p");
|
|
1109
|
+
sheets.textContent = `sheet endpoints: ${sheetendpoints.map((config) => `${config.origin} ${config.granted ? "granted" : "not granted"}`).join(" \xB7 ")}`;
|
|
1110
|
+
datasetsroot.append(sheets);
|
|
1111
|
+
}
|
|
1112
|
+
if (active) {
|
|
1113
|
+
const importer = document.createElement("div");
|
|
1114
|
+
importer.className = "panel";
|
|
1115
|
+
const csvinput = document.createElement("textarea");
|
|
1116
|
+
csvinput.rows = 3;
|
|
1117
|
+
csvinput.placeholder = "Paste reviewed csv content for a fill loop (header line first).";
|
|
1118
|
+
const nameinput = document.createElement("input");
|
|
1119
|
+
nameinput.placeholder = "dataset name (optional)";
|
|
1120
|
+
const mappinginput = document.createElement("input");
|
|
1121
|
+
mappinginput.placeholder = "column mapping json (optional, csv header \u2192 target)";
|
|
1122
|
+
importer.append(csvinput, nameinput, mappinginput, " ", button("Import csv", async () => {
|
|
1123
|
+
let mapping = {};
|
|
1124
|
+
if (mappinginput.value.trim()) {
|
|
1125
|
+
try {
|
|
1126
|
+
mapping = JSON.parse(mappinginput.value);
|
|
1127
|
+
} catch {
|
|
1128
|
+
status("The column mapping must be a json object.", true);
|
|
1129
|
+
return;
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
const imported = await request({ kind: "importcsv", csv: csvinput.value, name: nameinput.value, mapping });
|
|
1133
|
+
status(`Imported ${imported.rows} rows as dataset ${imported.name} for fill loops.`);
|
|
1134
|
+
await refresh();
|
|
1135
|
+
}));
|
|
1136
|
+
datasetsroot.append(importer);
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
function renderfiles(context) {
|
|
1140
|
+
if (!filesroot) return;
|
|
1141
|
+
filesroot.replaceChildren();
|
|
1142
|
+
const active = context.session && !context.session.stoppedat && context.session.expiresat > Date.now();
|
|
1143
|
+
const downloads = context.downloads ?? [];
|
|
1144
|
+
if (downloads.length > 0) {
|
|
1145
|
+
const card = document.createElement("div");
|
|
1146
|
+
card.className = "panel";
|
|
1147
|
+
const head = document.createElement("p");
|
|
1148
|
+
const states = ["queued", "running", "paused", "complete", "failed"];
|
|
1149
|
+
head.textContent = `batch download queue: ${downloads.length} file${downloads.length === 1 ? "" : "s"} (${states.map((state) => `${downloads.filter((item) => item.state === state).length} ${state}`).filter((part) => !part.startsWith("0 ")).join(" \xB7 ") || "none"})`;
|
|
1150
|
+
card.append(head);
|
|
1151
|
+
const list = document.createElement("ul");
|
|
1152
|
+
for (const record of downloads.slice(0, 6)) {
|
|
1153
|
+
const item = document.createElement("li");
|
|
1154
|
+
item.textContent = `${record.filename} \xB7 ${record.state}${record.bytes !== void 0 ? ` \xB7 ${record.bytes} bytes` : ""}${record.checksum !== void 0 ? ` \xB7 checksum ${record.checksum}` : ""}${record.path !== void 0 ? ` \xB7 ${record.path}` : ""}`;
|
|
1155
|
+
if (active) {
|
|
1156
|
+
item.append(" ", button("Pause", () => request({ kind: "downloadaction", id: record.id, action: "pause" }).then(() => refresh()).then(() => status(`Paused the download of ${record.filename}.`)), record.state !== "running"));
|
|
1157
|
+
item.append(" ", button("Resume", () => request({ kind: "downloadaction", id: record.id, action: "resume" }).then(() => refresh()).then(() => status(`Resumed the download of ${record.filename}.`)), record.state !== "paused"));
|
|
1158
|
+
item.append(" ", button("Verify", () => request({ kind: "downloadaction", id: record.id, action: "verify" }).then((value) => {
|
|
1159
|
+
const output = value;
|
|
1160
|
+
status(output.summary);
|
|
1161
|
+
return refresh();
|
|
1162
|
+
})));
|
|
1163
|
+
}
|
|
1164
|
+
list.append(item);
|
|
1165
|
+
}
|
|
1166
|
+
card.append(list);
|
|
1167
|
+
filesroot.append(card);
|
|
1168
|
+
}
|
|
1169
|
+
const filters = context.mimefilters ?? [];
|
|
1170
|
+
if (filters.length > 0) {
|
|
1171
|
+
const filter = filters[0];
|
|
1172
|
+
const card = document.createElement("p");
|
|
1173
|
+
card.textContent = `mime interception: include ${filter.include.join(", ")} \xB7 exclude ${filter.exclude.join(", ") || "none"} \xB7 ${filter.default} default for unlisted mime types${context.session?.origin ? ` \xB7 armed inside the ${context.session.origin} origin grants` : ""}`;
|
|
1174
|
+
filesroot.append(card);
|
|
1175
|
+
}
|
|
1176
|
+
const consents = (context.clipconsents ?? []).filter((record) => record.approved === void 0);
|
|
1177
|
+
for (const consent of consents.slice(0, 4)) {
|
|
1178
|
+
const card = document.createElement("div");
|
|
1179
|
+
card.className = "bannercard";
|
|
1180
|
+
card.textContent = `Clipboard read consent ${consent.id} waits for your approval: step ${consent.stepid} on ${consent.origin} asked to read the clipboard \u2014 "${consent.prompt}".`;
|
|
1181
|
+
if (active) {
|
|
1182
|
+
card.append(" ", button("Approve read", async () => {
|
|
1183
|
+
await request({ kind: "approveclipconsent", id: consent.id, approved: true });
|
|
1184
|
+
status(`Clipboard read consent ${consent.id} approved; run the step again to read once.`);
|
|
1185
|
+
await refresh();
|
|
1186
|
+
}));
|
|
1187
|
+
card.append(" ", button("Decline", async () => {
|
|
1188
|
+
await request({ kind: "approveclipconsent", id: consent.id, approved: false });
|
|
1189
|
+
status(`Clipboard read consent ${consent.id} declined.`);
|
|
1190
|
+
await refresh();
|
|
1191
|
+
}));
|
|
1192
|
+
}
|
|
1193
|
+
filesroot.append(card);
|
|
1194
|
+
}
|
|
1195
|
+
const netlogs = context.netlogs ?? [];
|
|
1196
|
+
if (netlogs.length > 0) {
|
|
1197
|
+
const card = document.createElement("div");
|
|
1198
|
+
card.className = "panel";
|
|
1199
|
+
const head = document.createElement("p");
|
|
1200
|
+
const steps = [...new Set(netlogs.map((record) => record.stepid))];
|
|
1201
|
+
head.textContent = `network log: ${netlogs.length} record${netlogs.length === 1 ? "" : "s"} correlated with ${steps.length} step${steps.length === 1 ? "" : "s"} (${steps.slice(0, 4).join(", ")}${steps.length > 4 ? "\u2026" : ""})`;
|
|
1202
|
+
card.append(head);
|
|
1203
|
+
const list = document.createElement("ul");
|
|
1204
|
+
for (const record of netlogs.slice(0, 6)) {
|
|
1205
|
+
const item = document.createElement("li");
|
|
1206
|
+
item.textContent = `${record.method} ${record.url} \xB7 ${record.status} \xB7 ${record.timing}ms \xB7 request ${record.requestid ?? "?"} \xB7 step ${record.stepid}`;
|
|
1207
|
+
list.append(item);
|
|
1208
|
+
}
|
|
1209
|
+
card.append(list);
|
|
1210
|
+
if (active) card.append(button("Export netlog (header values redacted)", async () => {
|
|
1211
|
+
const exported = await request({ kind: "exportnetlog" });
|
|
1212
|
+
status(`Exported ${exported.records.length} netlog records; ${exported.redaction}.`);
|
|
1213
|
+
}));
|
|
1214
|
+
filesroot.append(card);
|
|
1215
|
+
}
|
|
1216
|
+
const quarantines = context.quarantines ?? [];
|
|
1217
|
+
if (quarantines.length > 0) {
|
|
1218
|
+
const card = document.createElement("div");
|
|
1219
|
+
card.className = "panel";
|
|
1220
|
+
const head = document.createElement("p");
|
|
1221
|
+
head.textContent = `quarantine: ${quarantines.length} file${quarantines.length === 1 ? "" : "s"} outside the downloads folder (${quarantines.filter((entry) => entry.scan === "pending").length} awaiting scan verdicts)`;
|
|
1222
|
+
card.append(head);
|
|
1223
|
+
const list = document.createElement("ul");
|
|
1224
|
+
for (const entry of quarantines.slice(0, 6)) {
|
|
1225
|
+
const item = document.createElement("li");
|
|
1226
|
+
item.textContent = `${entry.path} \xB7 scan ${entry.scan}${entry.release !== void 0 ? ` \xB7 released under ${entry.release}` : ""} \xB7 ${entry.reason}`;
|
|
1227
|
+
if (active && entry.release === void 0 && entry.scan === "clean") item.append(" ", button("Release", async () => {
|
|
1228
|
+
await request({ kind: "releasequarantine", id: entry.id });
|
|
1229
|
+
status(`Released ${entry.path} from quarantine under the clean scan verdict.`);
|
|
1230
|
+
await refresh();
|
|
1231
|
+
}));
|
|
1232
|
+
list.append(item);
|
|
1233
|
+
}
|
|
1234
|
+
card.append(list);
|
|
1235
|
+
filesroot.append(card);
|
|
1236
|
+
}
|
|
1237
|
+
const planid = context.plan?.id;
|
|
1238
|
+
const counters = (context.capturecounters ?? []).find((item) => item.taskid === planid);
|
|
1239
|
+
if (counters) {
|
|
1240
|
+
const card = document.createElement("p");
|
|
1241
|
+
card.textContent = `capture naming for task ${counters.taskid}: ${Object.entries(counters.counters).map(([step, sequence]) => `${step} \u2192 ${counters.taskid}-${step}-${sequence}`).join(" \xB7 ")}`;
|
|
1242
|
+
filesroot.append(card);
|
|
1243
|
+
}
|
|
1244
|
+
const rules = context.cleanuprules ?? [];
|
|
1245
|
+
const rulecard = document.createElement("div");
|
|
1246
|
+
rulecard.className = "panel";
|
|
1247
|
+
const rulehead = document.createElement("p");
|
|
1248
|
+
rulehead.textContent = `cleanup policy: ${rules.length > 0 ? rules.map((rule) => `older than ${rule.age}ms of kind ${rule.kind} keep ${rule.keep}`).join(" \xB7 ") : "no rule set stored yet"}`;
|
|
1249
|
+
rulecard.append(rulehead);
|
|
1250
|
+
const runs = context.cleanupruns ?? [];
|
|
1251
|
+
if (runs.length > 0) {
|
|
1252
|
+
const lastrun = runs[0];
|
|
1253
|
+
const runline = document.createElement("p");
|
|
1254
|
+
runline.textContent = `last sweep: removed ${lastrun.removed}, kept ${lastrun.kept} under ${lastrun.rules} rule${lastrun.rules === 1 ? "" : "s"}`;
|
|
1255
|
+
rulecard.append(runline);
|
|
1256
|
+
}
|
|
1257
|
+
if (active) {
|
|
1258
|
+
const ageinput = document.createElement("input");
|
|
1259
|
+
ageinput.placeholder = "age window in ms";
|
|
1260
|
+
const kindinput = document.createElement("input");
|
|
1261
|
+
kindinput.placeholder = "artifact kind (any matches all)";
|
|
1262
|
+
const keepinput = document.createElement("input");
|
|
1263
|
+
keepinput.placeholder = "keep policy: none, latest or all";
|
|
1264
|
+
rulecard.append(ageinput, kindinput, keepinput, " ", button("Add cleanup rule", async () => {
|
|
1265
|
+
const age = Number(ageinput.value);
|
|
1266
|
+
const rule = { age, kind: kindinput.value.trim() || "any", keep: keepinput.value.trim() || "none" };
|
|
1267
|
+
const stored = await request({ kind: "setcleanuprules", rules: [...rules, rule] });
|
|
1268
|
+
status(`Stored ${stored.rules} reviewed cleanup rule${stored.rules === 1 ? "" : "s"}; ages stay your choice with no code ceiling.`);
|
|
1269
|
+
await refresh();
|
|
1270
|
+
}));
|
|
1271
|
+
}
|
|
1272
|
+
filesroot.append(rulecard);
|
|
1273
|
+
const inventory = context.inventory ?? [];
|
|
1274
|
+
if (inventory.length > 0) {
|
|
1275
|
+
const card = document.createElement("p");
|
|
1276
|
+
card.textContent = `artifact inventory: ${inventory.slice(0, 5).map((entry) => `${entry.name} (${entry.kind}, ${entry.size} characters, ${Math.max(0, Math.round((Date.now() - entry.at) / 6e4))} minute${Math.round((Date.now() - entry.at) / 6e4) === 1 ? "" : "s"} old)`).join(" \xB7 ")}${inventory.length > 5 ? ` and ${inventory.length - 5} more` : ""}`;
|
|
1277
|
+
filesroot.append(card);
|
|
1278
|
+
}
|
|
1279
|
+
const screens = (context.clips ?? []).filter((entry) => entry.kind === "screen").slice(0, 2);
|
|
1280
|
+
for (const screen of screens) {
|
|
1281
|
+
const card = document.createElement("p");
|
|
1282
|
+
card.textContent = `copyscreen result: ${screen.length} characters of png data with payload hash ${screen.hash} routed to the clipboard destination.`;
|
|
1283
|
+
filesroot.append(card);
|
|
1284
|
+
}
|
|
1285
|
+
const hooks = context.scanhooks ?? [];
|
|
1286
|
+
if (active) {
|
|
1287
|
+
const hookcard = document.createElement("div");
|
|
1288
|
+
hookcard.className = "panel";
|
|
1289
|
+
const hookhead = document.createElement("p");
|
|
1290
|
+
hookhead.textContent = hooks.length > 0 ? `scan hooks: ${hooks.map((hook) => `${hook.scanner} at ${hook.origin} ${hook.granted ? "granted" : "not granted"}`).join(" \xB7 ")}` : "scan hooks: none configured; scan verdicts stay pending without one.";
|
|
1291
|
+
hookcard.append(hookhead);
|
|
1292
|
+
const scannerinput = document.createElement("input");
|
|
1293
|
+
scannerinput.placeholder = "scanner name";
|
|
1294
|
+
const endpointinput = document.createElement("input");
|
|
1295
|
+
endpointinput.placeholder = "https://scanner.example/verdict";
|
|
1296
|
+
hookcard.append(scannerinput, endpointinput, " ", button("Configure scan hook", async () => {
|
|
1297
|
+
await request({ kind: "configurescanhook", scanner: scannerinput.value, endpoint: endpointinput.value });
|
|
1298
|
+
status(`Scan hook ${scannerinput.value} configured; hook failures stay pending verdicts.`);
|
|
1299
|
+
await refresh();
|
|
1300
|
+
}));
|
|
1301
|
+
filesroot.append(hookcard);
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
968
1304
|
function rendercapabilities(report) {
|
|
969
1305
|
if (!capabilitiestext) return;
|
|
970
1306
|
if (!report) {
|
|
@@ -1012,6 +1348,8 @@ async function refresh() {
|
|
|
1012
1348
|
rendernavigation(context);
|
|
1013
1349
|
rendertabswindows(context);
|
|
1014
1350
|
renderforms(context);
|
|
1351
|
+
renderdatasets(context);
|
|
1352
|
+
renderfiles(context);
|
|
1015
1353
|
renderaudit(context.audit);
|
|
1016
1354
|
rendercapabilities(context.capabilities);
|
|
1017
1355
|
if (context.session?.pausedat) status("Session paused. Reviewed actions are blocked until resume.");
|