@wenathlan/extension 1.1.37 → 1.1.38

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.38",
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"]);
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"]);
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,189 @@
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
+
2937
3120
  // extension/pagebridge.ts
2938
3121
  function stepoptions3(step) {
2939
3122
  if (!step.options) return {};
@@ -3043,6 +3226,7 @@
3043
3226
  var navstepkinds = /* @__PURE__ */ new Set(["waitload", "waiturl", "followlink", "spanav", "spawait", "rewritequery", "setfragment", "stopnav", "prefetch", "preconnect", "printpdf"]);
3044
3227
  var formkinds = /* @__PURE__ */ new Set(["fillform", "filllabel", "fillplaceholder", "detectfields", "generatevalues", "readerrors", "skiphoneypot", "detectlogin", "detecttemplate", "handoffcaptcha", "asksubmit", "submitform", "consentpassword", "attachfile"]);
3045
3228
  var wizardkinds = /* @__PURE__ */ new Set(["runwizard", "selectchain", "picktypeahead", "pickdate", "fillcard", "fillcode"]);
3229
+ var datastepkinds = /* @__PURE__ */ new Set(["scrapetable", "paginateextract"]);
3046
3230
  async function performstep(step, expectedorigin, rootdocument = document) {
3047
3231
  if (location.origin !== expectedorigin) return { ok: false, summary: "Page origin changed before action." };
3048
3232
  if (step.kind === "observe") return { ok: true, summary: "Observation completed." };
@@ -3091,6 +3275,7 @@
3091
3275
  let result;
3092
3276
  if (formkinds.has(step.kind)) result = runpageform(step, element, rootdocument);
3093
3277
  else if (wizardkinds.has(step.kind)) result = runpagewizard(step, element, rootdocument);
3278
+ else if (datastepkinds.has(step.kind)) result = runpagedata(step, element, rootdocument);
3094
3279
  else if (readkinds.has(step.kind)) result = runpageread(step, element, rootdocument);
3095
3280
  else if (controlkinds.has(step.kind)) result = runpagecontrol(step, element, rootdocument);
3096
3281
  else if (interactkinds.has(step.kind)) return await runinteractstep(step, expectedorigin, performstep);