@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.
@@ -2,7 +2,7 @@
2
2
  "manifest_version": 3,
3
3
  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnOEjO8Z0PDgQyfvawGcaO2j+o0GLCFTLNj7TkYC/Avo9l2NenMRq7gp90Nfd7E9MViv/OMcCKSYZ5unv12QPRtv31C+a5UQWDFAOP/cH5mwMd6hsayElrSoW8ta+FwFqmr9dIFkn7cQEU3YhZr4Gcbs+ycUHOxVgDA4NBKB0rQ6e9VW5LvTw0isRYUrqM+M72vKxHk9zUIYYn/LGPvottKBYi2GLr0PHSeC2UE+Shmq7vcFIXj6hDjvD4kLJ5sKoUllEcZ1TPuBcnHUQ9ndKA5iktXDQOIJCUJmi7a0YJ2PGg7fvpYfT9k0ai/qZ+pIoRfoOEwE01bPoDn7NjeYnNQIDAQAB",
4
4
  "name": "Devthink",
5
- "version": "1.1.37",
5
+ "version": "1.1.39",
6
6
  "description": "A consent-first bridge for reviewed browser-agent tasks.",
7
7
  "permissions": [
8
8
  "activeTab",
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  (() => {
3
3
  // policy.ts
4
- 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"]);
4
+ 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"]);
5
5
  var interactionactions = /* @__PURE__ */ new Set(["focus", "scroll", "hover", "clickdeep", "rightclick", "doubleclick", "scrollpage", "scrollby", "scrollend", "scrolltop", "fullscreen", "zoomset", "movepointer", "clicktext", "clickaria", "clickname", "expanddetails", "pierceshadow", "retryaction"]);
6
- 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"]);
6
+ 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"]);
7
7
  var allowedactions = /* @__PURE__ */ new Set([...sensitiveactions, ...interactionactions, ...readactions]);
8
8
  function parseoptions(step) {
9
9
  if (step.options === void 0) return {};
@@ -2934,6 +2934,196 @@
2934
2934
  }
2935
2935
  }
2936
2936
 
2937
+ // extension/pagedata.ts
2938
+ function normalizeheader(label) {
2939
+ const slug = label.trim().toLowerCase().replace(/[^\p{L}\p{N}]+/gu, "-").replace(/^-+|-+$/g, "");
2940
+ return slug || "column";
2941
+ }
2942
+ function columnspecof(label, used = /* @__PURE__ */ new Set()) {
2943
+ const base = normalizeheader(label);
2944
+ let key = base;
2945
+ let suffix = 2;
2946
+ while (used.has(key)) {
2947
+ key = `${base}${suffix}`;
2948
+ suffix += 1;
2949
+ }
2950
+ used.add(key);
2951
+ return { key, label: label.trim(), kind: "text", normalized: label.trim().toLowerCase() };
2952
+ }
2953
+ function classifycolumn(values) {
2954
+ const present = values.filter((value) => value.trim().length > 0);
2955
+ if (present.length === 0) return "text";
2956
+ return present.every((value) => Number.isFinite(Number(value.replace(/,/g, ".")))) ? "number" : "text";
2957
+ }
2958
+ function expandspans(rows) {
2959
+ const filled = [];
2960
+ const pending = /* @__PURE__ */ new Map();
2961
+ for (let index = 0; index < rows.length; index += 1) {
2962
+ const row = filled[index] ?? (filled[index] = []);
2963
+ let column = 0;
2964
+ for (const cell of rows[index].cells) {
2965
+ while (row[column] !== void 0 || pending.has(`${index},${column}`)) {
2966
+ if (row[column] === void 0) row[column] = pending.get(`${index},${column}`);
2967
+ column += 1;
2968
+ }
2969
+ row[column] = cell.text;
2970
+ for (let spanrow = 0; spanrow < Math.max(1, cell.rowspan); spanrow += 1) {
2971
+ for (let spancol = 0; spancol < Math.max(1, cell.colspan); spancol += 1) {
2972
+ if (spanrow === 0 && spancol === 0) continue;
2973
+ pending.set(`${index + spanrow},${column + spancol}`, cell.text);
2974
+ }
2975
+ }
2976
+ column += Math.max(1, cell.colspan);
2977
+ }
2978
+ }
2979
+ for (const [key, value] of pending) {
2980
+ const [rowpart, columnpart] = key.split(",");
2981
+ const rowindex = Number.parseInt(rowpart ?? "0", 10);
2982
+ const columnindex = Number.parseInt(columnpart ?? "0", 10);
2983
+ const target = filled[rowindex] ?? (filled[rowindex] = []);
2984
+ if (target[columnindex] === void 0) target[columnindex] = value;
2985
+ }
2986
+ const width = filled.reduce((largest, row) => Math.max(largest, row.length), 0);
2987
+ return filled.map((row) => Array.from({ length: width }, (_, column) => row[column] ?? ""));
2988
+ }
2989
+ function readgrid(rows) {
2990
+ const headerindex = rows.findIndex((row) => row.header || (row.cells[0]?.header ?? false));
2991
+ const useheader = headerindex !== -1 ? headerindex : 0;
2992
+ const hasheader = headerindex !== -1 || rows.length > 0;
2993
+ const grid = expandspans(rows);
2994
+ const headercells = hasheader ? grid[useheader] ?? [] : [];
2995
+ const used = /* @__PURE__ */ new Set();
2996
+ const columns = Array.from({ length: headercells.length || (grid[0]?.length ?? 0) }, (_, index) => columnspecof(headercells[index] ?? `column${index + 1}`, used));
2997
+ const bodyrows = grid.filter((_, index) => hasheader ? index !== useheader : true).filter((line) => line.some((value) => value.trim().length > 0)).map((line) => {
2998
+ const row = {};
2999
+ columns.forEach((column, index) => {
3000
+ row[column.key] = line[index] ?? "";
3001
+ });
3002
+ return row;
3003
+ });
3004
+ for (const column of columns) column.kind = classifycolumn(bodyrows.map((row) => row[column.key] ?? ""));
3005
+ const children = [];
3006
+ rows.forEach((row, rowindex) => {
3007
+ if (hasheader && rowindex === useheader) return;
3008
+ const parentrow = hasheader && rowindex > useheader ? rowindex - 1 : rowindex;
3009
+ row.cells.forEach((cell) => {
3010
+ if (!cell.nested) return;
3011
+ const child = readgrid(cell.nested.rows);
3012
+ children.push({ parentrow, selector: cell.nested.selector, columns: child.columns, rows: child.rows });
3013
+ });
3014
+ });
3015
+ return { columns, rows: bodyrows, children };
3016
+ }
3017
+ function rowsfresh(previous, current) {
3018
+ if (current.length === 0) return false;
3019
+ const known = new Set(previous.map((row) => JSON.stringify(row)));
3020
+ return current.some((row) => !known.has(JSON.stringify(row)));
3021
+ }
3022
+ function mergedatasets(datasets) {
3023
+ const columns = [];
3024
+ const seen = /* @__PURE__ */ new Set();
3025
+ for (const dataset of datasets) {
3026
+ for (const column of dataset.columns) {
3027
+ if (seen.has(column.key)) continue;
3028
+ seen.add(column.key);
3029
+ columns.push(column);
3030
+ }
3031
+ }
3032
+ const rows = datasets.flatMap((dataset) => dataset.rows.map((row) => {
3033
+ const merged = {};
3034
+ for (const column of columns) merged[column.key] = row[column.key] ?? "";
3035
+ return merged;
3036
+ }));
3037
+ return { columns, rows };
3038
+ }
3039
+ function collectrowshapes(table) {
3040
+ return [...table.rows].map((row) => ({
3041
+ header: [...row.cells].every((cell) => cell.tagName === "TH") && row.cells.length > 0,
3042
+ cells: [...row.cells].map((cell) => {
3043
+ const nested = cell.querySelector("table");
3044
+ return {
3045
+ text: clean(nested ? `${nested.rows.length} rows` : cell.textContent ?? ""),
3046
+ header: cell.tagName === "TH",
3047
+ rowspan: cell.rowSpan,
3048
+ colspan: cell.colSpan,
3049
+ ...nested instanceof HTMLTableElement ? { nested: { selector: elementselector(nested), rows: collectrowshapes(nested) } } : {}
3050
+ };
3051
+ })
3052
+ }));
3053
+ }
3054
+ function readtable(target, root, fallbackselector) {
3055
+ const table = target instanceof HTMLTableElement ? target : fallbackselector ? root.querySelector(fallbackselector) : root.querySelector("table");
3056
+ if (!table) return null;
3057
+ return readgrid(collectrowshapes(table));
3058
+ }
3059
+ async function runpagedata(step, target, root = document) {
3060
+ let options = {};
3061
+ try {
3062
+ options = parseoptions(step);
3063
+ } catch {
3064
+ options = {};
3065
+ }
3066
+ if (step.kind === "scrapetable") {
3067
+ const grid = readtable(target, root, step.target);
3068
+ if (!grid) return { ok: false, summary: "The reviewed table selector matches no table element." };
3069
+ const rowlimit = typeof options.rowlimit === "number" && Number.isInteger(options.rowlimit) && options.rowlimit > 0 ? options.rowlimit : grid.rows.length;
3070
+ const limited = grid.rows.slice(0, rowlimit);
3071
+ return {
3072
+ ok: true,
3073
+ summary: `Scraped ${limited.length} row${limited.length === 1 ? "" : "s"} into ${grid.columns.length} normalized column${grid.columns.length === 1 ? "" : "s"}${grid.children.length > 0 ? ` with ${grid.children.length} nested child table${grid.children.length === 1 ? "" : "s"}` : ""}.`,
3074
+ details: { grid: { columns: grid.columns, rows: limited, children: grid.children }, rows: limited.length, columns: grid.columns.length }
3075
+ };
3076
+ }
3077
+ if (step.kind === "paginateextract") {
3078
+ const nextselector = typeof options.next === "string" ? options.next : "";
3079
+ const pages = typeof options.pages === "number" && Number.isInteger(options.pages) && options.pages > 0 ? options.pages : 1;
3080
+ const wait5 = typeof options.wait === "number" && Number.isFinite(options.wait) && options.wait > 0 ? options.wait : 0;
3081
+ const cursor = typeof options.cursor === "number" && Number.isInteger(options.cursor) && options.cursor > 0 ? options.cursor : 0;
3082
+ const grids = [];
3083
+ let previous = [];
3084
+ for (let page = 0; page < pages + cursor; page += 1) {
3085
+ if (page < cursor) {
3086
+ const control = root.querySelector(nextselector);
3087
+ if (!control) break;
3088
+ control.click();
3089
+ await new Promise((resolve) => window.setTimeout(resolve, 0));
3090
+ continue;
3091
+ }
3092
+ if (page > cursor) {
3093
+ const control = root.querySelector(nextselector);
3094
+ if (!control) break;
3095
+ control.click();
3096
+ const deadline = Date.now() + wait5;
3097
+ let fresh = false;
3098
+ while (!fresh && Date.now() < deadline) {
3099
+ await new Promise((resolve) => window.setTimeout(resolve, Math.min(100, Math.max(16, deadline - Date.now()))));
3100
+ const probe = readtable(target, root, step.target);
3101
+ fresh = probe !== null && rowsfresh(previous, probe.rows);
3102
+ }
3103
+ }
3104
+ const grid = readtable(target, root, step.target);
3105
+ if (!grid) return { ok: false, summary: "The reviewed table selector matches no table element." };
3106
+ grids.push(grid);
3107
+ previous = grid.rows;
3108
+ }
3109
+ const merged = mergedatasets(grids);
3110
+ const hasnext = Boolean(root.querySelector(nextselector));
3111
+ return {
3112
+ ok: grids.length > 0,
3113
+ summary: grids.length > 0 ? `Followed ${grids.length} page${grids.length === 1 ? "" : "s"} of the reviewed table into ${merged.rows.length} row${merged.rows.length === 1 ? "" : "s"}${hasnext ? "; a next control remains" : ""}.` : "No page of the reviewed table was extracted.",
3114
+ details: { grid: { columns: merged.columns, rows: merged.rows, children: grids.flatMap((grid) => grid.children) }, pages: grids.length, rows: merged.rows.length, next: hasnext }
3115
+ };
3116
+ }
3117
+ return { ok: false, summary: "Unsupported data step." };
3118
+ }
3119
+
3120
+ // extension/datacommand.ts
3121
+ function checksum(value) {
3122
+ let hash = 5381;
3123
+ for (let index = 0; index < value.length; index += 1) hash = (hash * 33 ^ value.charCodeAt(index)) >>> 0;
3124
+ return `fnv1a-${hash.toString(16)}`;
3125
+ }
3126
+
2937
3127
  // extension/pagebridge.ts
2938
3128
  function stepoptions3(step) {
2939
3129
  if (!step.options) return {};
@@ -3043,6 +3233,7 @@
3043
3233
  var navstepkinds = /* @__PURE__ */ new Set(["waitload", "waiturl", "followlink", "spanav", "spawait", "rewritequery", "setfragment", "stopnav", "prefetch", "preconnect", "printpdf"]);
3044
3234
  var formkinds = /* @__PURE__ */ new Set(["fillform", "filllabel", "fillplaceholder", "detectfields", "generatevalues", "readerrors", "skiphoneypot", "detectlogin", "detecttemplate", "handoffcaptcha", "asksubmit", "submitform", "consentpassword", "attachfile"]);
3045
3235
  var wizardkinds = /* @__PURE__ */ new Set(["runwizard", "selectchain", "picktypeahead", "pickdate", "fillcard", "fillcode"]);
3236
+ var datastepkinds = /* @__PURE__ */ new Set(["scrapetable", "paginateextract"]);
3046
3237
  async function performstep(step, expectedorigin, rootdocument = document) {
3047
3238
  if (location.origin !== expectedorigin) return { ok: false, summary: "Page origin changed before action." };
3048
3239
  if (step.kind === "observe") return { ok: true, summary: "Observation completed." };
@@ -3070,6 +3261,11 @@
3070
3261
  return { ok: true, summary: "History forward requested." };
3071
3262
  }
3072
3263
  if (navstepkinds.has(step.kind)) return await runpagenav(step, rootdocument);
3264
+ if (step.kind === "writeclipboard") {
3265
+ const text = step.value ?? "";
3266
+ await navigator.clipboard.writeText(text);
3267
+ return { ok: true, summary: `Wrote ${text.length} reviewed character${text.length === 1 ? "" : "s"} to the clipboard with payload hash ${checksum(text)}.`, details: { length: text.length, hash: checksum(text), destination: "clipboard" } };
3268
+ }
3073
3269
  if (step.kind === "scrollpage") {
3074
3270
  const options = stepoptions3(step);
3075
3271
  window.scrollBy({ left: typeof options.x === "number" ? options.x : 0, top: typeof options.y === "number" ? options.y : 600, behavior: "auto" });
@@ -3091,6 +3287,7 @@
3091
3287
  let result;
3092
3288
  if (formkinds.has(step.kind)) result = runpageform(step, element, rootdocument);
3093
3289
  else if (wizardkinds.has(step.kind)) result = runpagewizard(step, element, rootdocument);
3290
+ else if (datastepkinds.has(step.kind)) result = runpagedata(step, element, rootdocument);
3094
3291
  else if (readkinds.has(step.kind)) result = runpageread(step, element, rootdocument);
3095
3292
  else if (controlkinds.has(step.kind)) result = runpagecontrol(step, element, rootdocument);
3096
3293
  else if (interactkinds.has(step.kind)) return await runinteractstep(step, expectedorigin, performstep);