@wenathlan/extension 1.1.36 → 1.1.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +272 -6
- package/dist/index.js.map +3 -3
- package/dist/memory.d.ts +39 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/policy.d.ts +17 -1
- package/dist/policy.d.ts.map +1 -1
- package/dist/protocol.d.ts +24 -1
- package/dist/protocol.d.ts.map +1 -1
- package/dist/types.d.ts +96 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/extension/dist/background.js +617 -8
- package/extension/dist/background.js.map +4 -4
- package/extension/dist/manifest.json +1 -1
- package/extension/dist/pagebridge.js +585 -5
- package/extension/dist/pagebridge.js.map +4 -4
- package/extension/dist/popup.html +1 -1
- package/extension/dist/popup.js +15 -2
- package/extension/dist/popup.js.map +2 -2
- package/extension/dist/sidepanel.html +1 -1
- package/extension/dist/sidepanel.js +185 -4
- package/extension/dist/sidepanel.js.map +3 -3
- package/extension/manifest.json +1 -1
- package/package.json +1 -1
|
@@ -435,20 +435,107 @@ var sessionmemory = class {
|
|
|
435
435
|
async setcontroltab(state) {
|
|
436
436
|
return this.adapter.set("controltab", state);
|
|
437
437
|
}
|
|
438
|
+
/** Stores one saved form profile under its reviewed name, replacing the previous profile of that name. */
|
|
439
|
+
async setprofile(profile) {
|
|
440
|
+
const records = (await this.getprofiles()).filter((item) => item.name !== profile.name);
|
|
441
|
+
await this.adapter.set("formprofiles", [profile, ...records]);
|
|
442
|
+
}
|
|
443
|
+
/** Returns one saved form profile by its reviewed name. */
|
|
444
|
+
async getprofile(name) {
|
|
445
|
+
return (await this.getprofiles()).find((item) => item.name === name);
|
|
446
|
+
}
|
|
447
|
+
/** Returns every saved form profile with its origin grants, newest first. */
|
|
448
|
+
async getprofiles() {
|
|
449
|
+
return await this.adapter.get("formprofiles") ?? [];
|
|
450
|
+
}
|
|
451
|
+
/** Removes one saved form profile by its reviewed name. */
|
|
452
|
+
async removeprofile(name) {
|
|
453
|
+
const records = (await this.getprofiles()).filter((item) => item.name !== name);
|
|
454
|
+
await this.adapter.set("formprofiles", records);
|
|
455
|
+
}
|
|
456
|
+
/** Records one wizard state with its step history. */
|
|
457
|
+
async addwizard(state) {
|
|
458
|
+
const records = await this.getwizards();
|
|
459
|
+
await this.adapter.set("wizards", [state, ...records]);
|
|
460
|
+
}
|
|
461
|
+
/** Returns every stored wizard state with its step history, newest first. */
|
|
462
|
+
async getwizards() {
|
|
463
|
+
return await this.adapter.get("wizards") ?? [];
|
|
464
|
+
}
|
|
465
|
+
/** Stores one submission ticket with its values hash, replacing the previous ticket of that id. */
|
|
466
|
+
async setticket(ticket) {
|
|
467
|
+
const records = (await this.gettickets()).filter((item) => item.id !== ticket.id);
|
|
468
|
+
await this.adapter.set("submittickets", [ticket, ...records]);
|
|
469
|
+
}
|
|
470
|
+
/** Returns every stored submission ticket with its values hash, newest first. */
|
|
471
|
+
async gettickets() {
|
|
472
|
+
return await this.adapter.get("submittickets") ?? [];
|
|
473
|
+
}
|
|
474
|
+
/** Records one collected error report for correction loops. */
|
|
475
|
+
async adderrorreport(report) {
|
|
476
|
+
const records = await this.geterrorreports();
|
|
477
|
+
await this.adapter.set("errorreports", [report, ...records]);
|
|
478
|
+
}
|
|
479
|
+
/** Returns every stored error report, newest first. */
|
|
480
|
+
async geterrorreports() {
|
|
481
|
+
return await this.adapter.get("errorreports") ?? [];
|
|
482
|
+
}
|
|
483
|
+
/** Records one typeahead pick observed when a reviewed suggestion entry was chosen. */
|
|
484
|
+
async addpick(pick) {
|
|
485
|
+
const records = await this.getpicks();
|
|
486
|
+
await this.adapter.set("typeaheadpicks", [pick, ...records]);
|
|
487
|
+
}
|
|
488
|
+
/** Returns every recorded typeahead pick, newest first. */
|
|
489
|
+
async getpicks() {
|
|
490
|
+
return await this.adapter.get("typeaheadpicks") ?? [];
|
|
491
|
+
}
|
|
492
|
+
/** Records one captcha handoff while the plan waits for the user. */
|
|
493
|
+
async addcaptcha(handoff) {
|
|
494
|
+
const records = await this.getcaptchas();
|
|
495
|
+
await this.adapter.set("captchas", [handoff, ...records]);
|
|
496
|
+
}
|
|
497
|
+
/** Returns every captcha handoff record with its resolution state, newest first. */
|
|
498
|
+
async getcaptchas() {
|
|
499
|
+
return await this.adapter.get("captchas") ?? [];
|
|
500
|
+
}
|
|
501
|
+
/** Resolves one captcha handoff by id once the user finished it. */
|
|
502
|
+
async resolvecaptcha(id, resolvedat) {
|
|
503
|
+
const records = await this.getcaptchas();
|
|
504
|
+
await this.adapter.set("captchas", records.map((handoff) => handoff.id === id && !handoff.resolved ? { ...handoff, resolved: true, resolvedat } : handoff));
|
|
505
|
+
}
|
|
506
|
+
/** Records one login or template detection for its origin. */
|
|
507
|
+
async adddetection(record2) {
|
|
508
|
+
const records = await this.getdetections();
|
|
509
|
+
await this.adapter.set("detections", [record2, ...records]);
|
|
510
|
+
}
|
|
511
|
+
/** Returns every stored login and template detection per origin, newest first. */
|
|
512
|
+
async getdetections() {
|
|
513
|
+
return await this.adapter.get("detections") ?? [];
|
|
514
|
+
}
|
|
515
|
+
/** Stores the reviewed one time code behind the consent gate of an active session. */
|
|
516
|
+
async setcodevalue(value) {
|
|
517
|
+
return this.adapter.set("codevalue", value);
|
|
518
|
+
}
|
|
519
|
+
/** Returns the reviewed one time code, if the user stored one behind the consent gate. */
|
|
520
|
+
async getcodevalue() {
|
|
521
|
+
return this.adapter.get("codevalue");
|
|
522
|
+
}
|
|
438
523
|
};
|
|
439
524
|
function randomid() {
|
|
440
525
|
return crypto.randomUUID();
|
|
441
526
|
}
|
|
442
527
|
|
|
443
528
|
// policy.ts
|
|
444
|
-
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"]);
|
|
529
|
+
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"]);
|
|
445
530
|
var interactionactions = /* @__PURE__ */ new Set(["focus", "scroll", "hover", "clickdeep", "rightclick", "doubleclick", "scrollpage", "scrollby", "scrollend", "scrolltop", "fullscreen", "zoomset", "movepointer", "clicktext", "clickaria", "clickname", "expanddetails", "pierceshadow", "retryaction"]);
|
|
446
|
-
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"]);
|
|
531
|
+
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"]);
|
|
447
532
|
var allowedactions = /* @__PURE__ */ new Set([...sensitiveactions, ...interactionactions, ...readactions]);
|
|
448
533
|
var watchactions = /* @__PURE__ */ new Set(["watchmutate", "watchbanner", "watchfocus", "watchtab"]);
|
|
449
|
-
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"]);
|
|
450
|
-
var valueactions = /* @__PURE__ */ new Set(["presskey", "drag", "drop", "upload", "readattribute", "removeattribute", "waittext", "evaluate", "zoomset", "tabactivate", "tabclose", "tabreload", "windowclose", "windowresize", "tabcreate", "windowcreate", "downloadfile", "typetime", "appendtext", "setvalue", "typeedit", "keyhold", "keyrelease", "chooseradio", "setslider", "setdate", "setcolor", "followlink", "setfragment", "handleauth", "navintent", "openclipboard", "checksafe", "reopentab", "spanav", "duplicatetab", "pintab", "mutetab", "movetab", "movetabwindow", "searchtabs", "badgetab", "attachmeta", "focuswindow", "maximizewindow", "minimizewindow", "restorewindow", "incognitowindow"]);
|
|
534
|
+
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"]);
|
|
535
|
+
var valueactions = /* @__PURE__ */ new Set(["presskey", "drag", "drop", "upload", "readattribute", "removeattribute", "waittext", "evaluate", "zoomset", "tabactivate", "tabclose", "tabreload", "windowclose", "windowresize", "tabcreate", "windowcreate", "downloadfile", "typetime", "appendtext", "setvalue", "typeedit", "keyhold", "keyrelease", "chooseradio", "setslider", "setdate", "setcolor", "followlink", "setfragment", "handleauth", "navintent", "openclipboard", "checksafe", "reopentab", "spanav", "duplicatetab", "pintab", "mutetab", "movetab", "movetabwindow", "searchtabs", "badgetab", "attachmeta", "focuswindow", "maximizewindow", "minimizewindow", "restorewindow", "incognitowindow", "asksubmit", "selectchain", "picktypeahead", "pickdate", "attachfile", "fillcode", "consentpassword"]);
|
|
451
536
|
var tabscommandactions = /* @__PURE__ */ new Set(["querytabs", "duplicatetab", "closepattern", "pintab", "mutetab", "movetab", "movetabwindow", "grouptabs", "colorgroup", "collapsegroup", "discardtab", "reloadtabs", "zoomin", "zoomout", "watchtab", "switchtab", "maximizewindow", "minimizewindow", "restorewindow", "focuswindow", "scratchwindow", "incognitowindow", "restoretab", "savelayout", "restorelayout", "findclones", "searchtabs", "badgetab", "attachmeta", "listaudio", "reopenrun", "snapshotsession"]);
|
|
537
|
+
var formactions = /* @__PURE__ */ new Set(["fillform", "filllabel", "fillplaceholder", "detectfields", "generatevalues", "saveprofiles", "asksubmit", "submitform", "readerrors", "retryform", "runwizard", "selectchain", "picktypeahead", "pickdate", "attachfile", "handoffcaptcha", "fillcard", "fillcode", "consentpassword", "skiphoneypot", "detectlogin", "detecttemplate"]);
|
|
538
|
+
var fieldkinds = ["text", "email", "phone", "date", "number", "select", "check", "radio", "file", "password", "card", "code"];
|
|
452
539
|
var layoutmutationactions = /* @__PURE__ */ new Set(["grouptabs", "colorgroup", "collapsegroup", "savelayout", "restorelayout"]);
|
|
453
540
|
var groupcolors = ["grey", "blue", "red", "yellow", "green", "pink", "purple", "cyan", "orange"];
|
|
454
541
|
function normalizeendpoint(value) {
|
|
@@ -492,6 +579,147 @@ function istabscommandkind(kind) {
|
|
|
492
579
|
function islayoutkind(kind) {
|
|
493
580
|
return layoutmutationactions.has(kind);
|
|
494
581
|
}
|
|
582
|
+
function isformkind(kind) {
|
|
583
|
+
return formactions.has(kind);
|
|
584
|
+
}
|
|
585
|
+
function validatefieldmatch(value) {
|
|
586
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return { allowed: false, reason: "A reviewed field match is required in options." };
|
|
587
|
+
const match = value;
|
|
588
|
+
if (match.mode !== "label" && match.mode !== "placeholder" && match.mode !== "arialabel" && match.mode !== "name") return { allowed: false, reason: "The reviewed field match mode must be label, placeholder, arialabel or name." };
|
|
589
|
+
const key = match.mode === "label" ? "label" : match.mode === "placeholder" ? "placeholder" : match.mode === "arialabel" ? "arialabel" : "name";
|
|
590
|
+
if (!isnonempty(match[key])) return { allowed: false, reason: `The reviewed ${match.mode} field match needs a non-empty ${key}.` };
|
|
591
|
+
return { allowed: true };
|
|
592
|
+
}
|
|
593
|
+
function validateformrecord(value) {
|
|
594
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return { allowed: false, reason: "A reviewed form record with entries is required in options." };
|
|
595
|
+
const record2 = value;
|
|
596
|
+
if (record2.form !== void 0 && !isnonempty(record2.form)) return { allowed: false, reason: "The reviewed form record form selector must be a non-empty string." };
|
|
597
|
+
if (!Array.isArray(record2.entries) || record2.entries.length === 0) return { allowed: false, reason: "The reviewed form record needs a non-empty list of entries." };
|
|
598
|
+
for (const item of record2.entries) {
|
|
599
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return { allowed: false, reason: "Every reviewed form record entry must be an object." };
|
|
600
|
+
const entry = item;
|
|
601
|
+
const matchcheck = validatefieldmatch(entry.match);
|
|
602
|
+
if (!matchcheck.allowed) return matchcheck;
|
|
603
|
+
if (typeof entry.kind !== "string" || !fieldkinds.includes(entry.kind)) return { allowed: false, reason: "Every reviewed form record entry needs a known field kind." };
|
|
604
|
+
if (typeof entry.value !== "string") return { allowed: false, reason: "Every reviewed form record entry needs a string value." };
|
|
605
|
+
if (entry.kind === "password") return { allowed: false, reason: "Password entries are refused inside form records; use consentpassword with a reviewed consent ref." };
|
|
606
|
+
}
|
|
607
|
+
return { allowed: true };
|
|
608
|
+
}
|
|
609
|
+
function validatevaluegen(value) {
|
|
610
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return { allowed: false, reason: "A reviewed valuegen rule with a field kind is required in options." };
|
|
611
|
+
const rule = value;
|
|
612
|
+
if (typeof rule.kind !== "string" || !fieldkinds.includes(rule.kind)) return { allowed: false, reason: "The reviewed valuegen kind must be a known field kind." };
|
|
613
|
+
if (rule.locale !== void 0 && !isnonempty(rule.locale)) return { allowed: false, reason: "The reviewed valuegen locale must be a non-empty string." };
|
|
614
|
+
if (rule.seed !== void 0 && (typeof rule.seed !== "number" || !Number.isFinite(rule.seed))) return { allowed: false, reason: "The reviewed valuegen seed must be a finite number." };
|
|
615
|
+
return { allowed: true };
|
|
616
|
+
}
|
|
617
|
+
function validatefieldpairs(options, mode) {
|
|
618
|
+
const pairs = options.fields;
|
|
619
|
+
if (!Array.isArray(pairs) || pairs.length === 0) return { allowed: false, reason: "A reviewed non-empty list of field pairs is required in options." };
|
|
620
|
+
for (const item of pairs) {
|
|
621
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return { allowed: false, reason: "Every reviewed field pair must be an object." };
|
|
622
|
+
const pair = item;
|
|
623
|
+
if (!isnonempty(pair[mode])) return { allowed: false, reason: `Every reviewed field pair needs a non-empty ${mode}.` };
|
|
624
|
+
if (typeof pair.value !== "string" || !pair.value.trim()) return { allowed: false, reason: "Every reviewed field pair needs a non-empty value." };
|
|
625
|
+
}
|
|
626
|
+
return { allowed: true };
|
|
627
|
+
}
|
|
628
|
+
function validatecardsegments(value) {
|
|
629
|
+
if (!Array.isArray(value) || value.length === 0) return { allowed: false, reason: "A reviewed non-empty list of card segments is required in options." };
|
|
630
|
+
for (const item of value) {
|
|
631
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return { allowed: false, reason: "Every reviewed card segment must be an object." };
|
|
632
|
+
const segment = item;
|
|
633
|
+
const matchcheck = validatefieldmatch(segment.match);
|
|
634
|
+
if (!matchcheck.allowed) return matchcheck;
|
|
635
|
+
if (typeof segment.value !== "string" || !segment.value.trim()) return { allowed: false, reason: "Every reviewed card segment needs a non-empty value." };
|
|
636
|
+
}
|
|
637
|
+
return { allowed: true };
|
|
638
|
+
}
|
|
639
|
+
function validateformgrammar(step, options) {
|
|
640
|
+
const kind = step.kind;
|
|
641
|
+
if (kind === "fillform" || kind === "saveprofiles" && options.formrecord !== void 0) {
|
|
642
|
+
const recordcheck = validateformrecord(options.formrecord);
|
|
643
|
+
if (!recordcheck.allowed) return recordcheck;
|
|
644
|
+
}
|
|
645
|
+
if (kind === "filllabel" || kind === "fillplaceholder") {
|
|
646
|
+
const paircheck = validatefieldpairs(options, kind === "filllabel" ? "label" : "placeholder");
|
|
647
|
+
if (!paircheck.allowed) return paircheck;
|
|
648
|
+
}
|
|
649
|
+
if (kind === "generatevalues" && options.valuegen !== void 0) {
|
|
650
|
+
const rulecheck = validatevaluegen(options.valuegen);
|
|
651
|
+
if (!rulecheck.allowed) return rulecheck;
|
|
652
|
+
}
|
|
653
|
+
if (kind === "saveprofiles" && !isnonempty(options.name)) return { allowed: false, reason: "A reviewed profile name is required in options." };
|
|
654
|
+
if (kind === "submitform" && !isnonempty(options.consentref)) return { allowed: false, reason: "A reviewed consent ref of an approved asksubmit ticket is required in options." };
|
|
655
|
+
if (kind === "retryform") {
|
|
656
|
+
const backoff = options.backoff;
|
|
657
|
+
if (!backoff || typeof backoff !== "object" || Array.isArray(backoff)) return { allowed: false, reason: "A reviewed backoff rule with wait and factor is required in options." };
|
|
658
|
+
const rule = backoff;
|
|
659
|
+
if (typeof rule.wait !== "number" || !Number.isFinite(rule.wait) || rule.wait <= 0) return { allowed: false, reason: "The reviewed retry backoff wait must be a positive number of milliseconds with no code ceiling." };
|
|
660
|
+
if (typeof rule.factor !== "number" || !Number.isFinite(rule.factor) || rule.factor < 1) return { allowed: false, reason: "The reviewed retry backoff factor must be one or greater with no code ceiling." };
|
|
661
|
+
if (options.attempts !== void 0 && (typeof options.attempts !== "number" || !Number.isInteger(options.attempts) || options.attempts < 1)) return { allowed: false, reason: "The reviewed retry attempts must be a positive integer with no code ceiling." };
|
|
662
|
+
}
|
|
663
|
+
if (kind === "runwizard" && options.steps !== void 0 && (typeof options.steps !== "number" || !Number.isInteger(options.steps) || options.steps < 1)) return { allowed: false, reason: "The reviewed wizard step count must be a positive integer with no code ceiling." };
|
|
664
|
+
if (kind === "selectchain") {
|
|
665
|
+
if (!isnonempty(options.child)) return { allowed: false, reason: "A reviewed child selector of the dependent control is required in options." };
|
|
666
|
+
if (!nonnegativeoption(options, "wait")) return { allowed: false, reason: "The reviewed dependent wait must be zero or a positive number of milliseconds." };
|
|
667
|
+
}
|
|
668
|
+
if (kind === "picktypeahead") {
|
|
669
|
+
if (!isnonempty(options.pick)) return { allowed: false, reason: "A reviewed suggestion entry to pick is required in options." };
|
|
670
|
+
if (!nonnegativeoption(options, "timeout")) return { allowed: false, reason: "The reviewed typeahead timeout must be zero or a positive number of milliseconds." };
|
|
671
|
+
}
|
|
672
|
+
if (kind === "pickdate" && !/^\d{4}-\d{2}-\d{2}$/.test(step.value ?? "")) return { allowed: false, reason: "The reviewed date must use the yyyy-mm-dd form." };
|
|
673
|
+
if (kind === "fillcard") {
|
|
674
|
+
const segmentcheck = validatecardsegments(options.segments);
|
|
675
|
+
if (!segmentcheck.allowed) return segmentcheck;
|
|
676
|
+
if (!nonnegativeoption(options, "pause")) return { allowed: false, reason: "The reviewed card typing pause must be zero or a positive number of milliseconds." };
|
|
677
|
+
}
|
|
678
|
+
if (kind === "fillcode" && !isnonempty(options.source)) return { allowed: false, reason: "A reviewed one time code source is required in options." };
|
|
679
|
+
if (kind === "consentpassword" && !isnonempty(options.consentref)) return { allowed: false, reason: "A reviewed consent ref is required in options before any password is filled." };
|
|
680
|
+
return { allowed: true };
|
|
681
|
+
}
|
|
682
|
+
function submitreviewgranted(steps, submitid) {
|
|
683
|
+
const position = steps.findIndex((candidate) => candidate.id === submitid);
|
|
684
|
+
const asked = steps.some((candidate, index) => candidate.kind === "asksubmit" && (position === -1 || index < position));
|
|
685
|
+
return asked ? { allowed: true } : { allowed: false, reason: "Form submission requires an asksubmit review step before it." };
|
|
686
|
+
}
|
|
687
|
+
function passwordconsentgranted(step) {
|
|
688
|
+
let options = {};
|
|
689
|
+
try {
|
|
690
|
+
options = parseoptions(step);
|
|
691
|
+
} catch {
|
|
692
|
+
options = {};
|
|
693
|
+
}
|
|
694
|
+
const consentref = options.consentref;
|
|
695
|
+
if (typeof consentref !== "string" || !consentref.trim()) return { allowed: false, reason: "A password fill requires a reviewed consent ref in options." };
|
|
696
|
+
return { allowed: true };
|
|
697
|
+
}
|
|
698
|
+
function luhnvalid(digits) {
|
|
699
|
+
let sum = 0;
|
|
700
|
+
let double = false;
|
|
701
|
+
for (let index = digits.length - 1; index >= 0; index -= 1) {
|
|
702
|
+
let value = Number.parseInt(digits[index] ?? "", 10);
|
|
703
|
+
if (!Number.isFinite(value)) return false;
|
|
704
|
+
if (double) {
|
|
705
|
+
value *= 2;
|
|
706
|
+
if (value > 9) value -= 9;
|
|
707
|
+
}
|
|
708
|
+
sum += value;
|
|
709
|
+
double = !double;
|
|
710
|
+
}
|
|
711
|
+
return sum % 10 === 0;
|
|
712
|
+
}
|
|
713
|
+
function generatedvalueallowed(value) {
|
|
714
|
+
const compact = value.replace(/[\s-]/g, "");
|
|
715
|
+
if (/^\d{13,19}$/.test(compact) && luhnvalid(compact) && !compact.startsWith("4111")) return { allowed: false, reason: "The generated value looks like a real card number and is refused; generated card values use the 4111 test prefix." };
|
|
716
|
+
if (/^\d{3}-\d{2}-\d{4}$/.test(value.trim())) return { allowed: false, reason: "The generated value looks like a personal identifier and is refused." };
|
|
717
|
+
return { allowed: true };
|
|
718
|
+
}
|
|
719
|
+
function profilegrantgranted(profile, origin) {
|
|
720
|
+
if (!profile.grants.includes(origin)) return { allowed: false, reason: `The saved profile ${profile.name} is not granted to ${origin}; add the origin to the profile grants first.` };
|
|
721
|
+
return { allowed: true };
|
|
722
|
+
}
|
|
495
723
|
function layoutmutationgranted(session, now) {
|
|
496
724
|
if (!session || session.stoppedat || session.expiresat <= now) return { allowed: false, reason: "Group and layout mutations stay inside the active session." };
|
|
497
725
|
return { allowed: true };
|
|
@@ -969,6 +1197,10 @@ function validatestep(step, origin) {
|
|
|
969
1197
|
const tabscheck = validatetabsgrammar(step, options);
|
|
970
1198
|
if (!tabscheck.allowed) return tabscheck;
|
|
971
1199
|
}
|
|
1200
|
+
if (isformkind(step.kind)) {
|
|
1201
|
+
const formcheck = validateformgrammar(step, options);
|
|
1202
|
+
if (!formcheck.allowed) return formcheck;
|
|
1203
|
+
}
|
|
972
1204
|
if (step.kind === "tabcreate") {
|
|
973
1205
|
if (options.background !== void 0 && typeof options.background !== "boolean") return { allowed: false, reason: "The reviewed background flag must be a boolean." };
|
|
974
1206
|
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." };
|
|
@@ -1010,6 +1242,15 @@ function canexecute(input) {
|
|
|
1010
1242
|
}
|
|
1011
1243
|
}
|
|
1012
1244
|
if (islayoutkind(input.step.kind) && !layoutmutationgranted(input.session, now).allowed) return { allowed: false, reason: "Group and layout mutations stay inside the active session." };
|
|
1245
|
+
if (input.step.kind === "submitform" || input.step.kind === "retryform") {
|
|
1246
|
+
if (!input.plan) return { allowed: false, reason: "Form submission requires an asksubmit review step before it." };
|
|
1247
|
+
const reviewgate = submitreviewgranted(input.plan.steps, input.step.id);
|
|
1248
|
+
if (!reviewgate.allowed) return reviewgate;
|
|
1249
|
+
}
|
|
1250
|
+
if (input.step.kind === "consentpassword") {
|
|
1251
|
+
const consentgate = passwordconsentgranted(input.step);
|
|
1252
|
+
if (!consentgate.allowed) return consentgate;
|
|
1253
|
+
}
|
|
1013
1254
|
if (input.step.kind === "openlink" || input.step.kind === "openprivate" || input.step.kind === "batchopen" || input.step.kind === "prefetch" || input.step.kind === "deeplink" || input.step.kind === "reopentab") {
|
|
1014
1255
|
let options = {};
|
|
1015
1256
|
try {
|
|
@@ -1091,9 +1332,20 @@ function tasktabs(progress, planid) {
|
|
|
1091
1332
|
if (!progress || progress.planid !== planid) return [];
|
|
1092
1333
|
return progress.tasktabs ?? [];
|
|
1093
1334
|
}
|
|
1335
|
+
function wizardcompletion(state) {
|
|
1336
|
+
if (state.steps <= 0) return 0;
|
|
1337
|
+
return Math.min(1, state.completed.filter(Boolean).length / state.steps);
|
|
1338
|
+
}
|
|
1339
|
+
function recordwizardstep(progress, planid, stepid, state, now) {
|
|
1340
|
+
const base = progress && progress.planid === planid ? progress : emptyprogress(planid, now);
|
|
1341
|
+
const executed = Math.min(state.index, state.steps);
|
|
1342
|
+
const done = executed >= state.steps;
|
|
1343
|
+
const outcome = { stepid, ok: done, summary: `Wizard step ${executed} of ${state.steps} ${done ? "completed the wizard" : "executed"}.`, details: { wizard: { index: state.index, steps: state.steps, completed: [...state.completed] } }, at: now };
|
|
1344
|
+
return recordoutcome(base, planid, outcome, now);
|
|
1345
|
+
}
|
|
1094
1346
|
|
|
1095
1347
|
// version.ts
|
|
1096
|
-
var packageversion = "1.1.
|
|
1348
|
+
var packageversion = "1.1.37";
|
|
1097
1349
|
|
|
1098
1350
|
// types.ts
|
|
1099
1351
|
var protocolversion = packageversion;
|
|
@@ -1134,6 +1386,11 @@ function parseproposal(value, origin) {
|
|
|
1134
1386
|
const options = parseoptions(step);
|
|
1135
1387
|
if (typeof options.stepid === "string" && !steps.some((candidate) => candidate.id === options.stepid)) throw new Error("A retry or frame wrapper references an unknown step id.");
|
|
1136
1388
|
}
|
|
1389
|
+
for (const step of steps) {
|
|
1390
|
+
if (step.kind !== "submitform" && step.kind !== "retryform") continue;
|
|
1391
|
+
const review = submitreviewgranted(steps, step.id);
|
|
1392
|
+
if (!review.allowed) throw new Error(review.reason);
|
|
1393
|
+
}
|
|
1137
1394
|
const createdat = Date.now();
|
|
1138
1395
|
const expiresat = typeof planinput.expiresat === "number" ? planinput.expiresat : createdat + 10 * 60 * 1e3;
|
|
1139
1396
|
const plan = {
|
|
@@ -1185,6 +1442,15 @@ function safetyresponse(input) {
|
|
|
1185
1442
|
function layoutreport(input) {
|
|
1186
1443
|
return { version: protocolversion, layouts: input.layouts };
|
|
1187
1444
|
}
|
|
1445
|
+
function formreportresponse(input) {
|
|
1446
|
+
return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, report: input.report });
|
|
1447
|
+
}
|
|
1448
|
+
function errorreportresponse(input) {
|
|
1449
|
+
return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, report: input.report });
|
|
1450
|
+
}
|
|
1451
|
+
function wizardreport(input) {
|
|
1452
|
+
return { version: protocolversion, ...input.sessionid ? { sessionid: input.sessionid } : {}, wizards: input.wizards, picks: input.picks };
|
|
1453
|
+
}
|
|
1188
1454
|
|
|
1189
1455
|
// extension/browsertabs.ts
|
|
1190
1456
|
var browserkinds = /* @__PURE__ */ new Set(["tablist", "tabcreate", "tabactivate", "tabclose", "tabreload", "tabsnapshot", "windowlist", "windowcreate", "windowclose", "zoomset", "windowresize", "downloadfile"]);
|
|
@@ -1840,6 +2106,113 @@ function authfor(auths, url) {
|
|
|
1840
2106
|
return auths.find((record2) => record2.origin === origin);
|
|
1841
2107
|
}
|
|
1842
2108
|
|
|
2109
|
+
// extension/pageforms.ts
|
|
2110
|
+
var firstnames = { en: ["alex", "jordan", "taylor", "morgan", "casey"], pt: ["ana", "bruno", "carla", "diego", "helena"] };
|
|
2111
|
+
var lastnames = { en: ["brooks", "carter", "diaz", "evans", "reyes"], pt: ["alves", "costa", "lima", "souza", "moraes"] };
|
|
2112
|
+
function localekey(locale) {
|
|
2113
|
+
const normalized = locale.toLowerCase();
|
|
2114
|
+
if (normalized.startsWith("pt")) return "pt";
|
|
2115
|
+
return "en";
|
|
2116
|
+
}
|
|
2117
|
+
function generatevalue(kind, rule) {
|
|
2118
|
+
const seed = typeof rule.seed === "number" && Number.isFinite(rule.seed) ? Math.abs(Math.floor(rule.seed)) : 1;
|
|
2119
|
+
const names = firstnames[localekey(rule.locale ?? "en")] ?? firstnames.en ?? ["alex"];
|
|
2120
|
+
const surnames = lastnames[localekey(rule.locale ?? "en")] ?? lastnames.en ?? ["brooks"];
|
|
2121
|
+
let state = seed * 1103515245 + 12345;
|
|
2122
|
+
const next = () => {
|
|
2123
|
+
state = (state * 1103515245 + 12345) % 2147483648;
|
|
2124
|
+
return state / 2147483648;
|
|
2125
|
+
};
|
|
2126
|
+
const pick = (items) => items[Math.floor(next() * items.length) % items.length] ?? items[0];
|
|
2127
|
+
const digits = (count) => Array.from({ length: count }, () => String(Math.floor(next() * 10))).join("");
|
|
2128
|
+
const person = `${pick(names)} ${pick(surnames)}`;
|
|
2129
|
+
switch (kind) {
|
|
2130
|
+
case "email":
|
|
2131
|
+
return `${person.replace(" ", ".")}${digits(2)}@example.com`;
|
|
2132
|
+
case "phone":
|
|
2133
|
+
return localekey(rule.locale ?? "en") === "pt" ? `+55 (11) 9${digits(4)}-${digits(4)}` : `+1 (555) 010-${digits(4)}`;
|
|
2134
|
+
case "date":
|
|
2135
|
+
return `${2024 + Math.floor(next() * 2)}-${String(1 + Math.floor(next() * 12)).padStart(2, "0")}-${String(1 + Math.floor(next() * 28)).padStart(2, "0")}`;
|
|
2136
|
+
case "number":
|
|
2137
|
+
return String(Math.floor(next() * 1e3));
|
|
2138
|
+
case "select":
|
|
2139
|
+
return `option ${1 + Math.floor(next() * 5)}`;
|
|
2140
|
+
case "check":
|
|
2141
|
+
return next() > 0.5 ? "true" : "false";
|
|
2142
|
+
case "radio":
|
|
2143
|
+
return `choice ${1 + Math.floor(next() * 4)}`;
|
|
2144
|
+
case "file":
|
|
2145
|
+
return `sample${digits(2)}.pdf`;
|
|
2146
|
+
case "password":
|
|
2147
|
+
return `pw-${digits(6)}-${pick(names)}`;
|
|
2148
|
+
case "card":
|
|
2149
|
+
return `4111 ${digits(4)} ${digits(4)} ${digits(4)}`;
|
|
2150
|
+
case "code":
|
|
2151
|
+
return digits(6);
|
|
2152
|
+
default:
|
|
2153
|
+
return person;
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
function valueshash(values) {
|
|
2157
|
+
const source = values.map((entry) => `${entry.label}=${entry.value}`).join("|");
|
|
2158
|
+
let hash = 5381;
|
|
2159
|
+
for (let index = 0; index < source.length; index += 1) hash = (hash * 33 ^ source.charCodeAt(index)) >>> 0;
|
|
2160
|
+
return hash.toString(16);
|
|
2161
|
+
}
|
|
2162
|
+
function parseformrecord(value) {
|
|
2163
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
2164
|
+
const record2 = value;
|
|
2165
|
+
if (!Array.isArray(record2.entries)) return null;
|
|
2166
|
+
const entries = [];
|
|
2167
|
+
for (const item of record2.entries) {
|
|
2168
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) continue;
|
|
2169
|
+
const entry = item;
|
|
2170
|
+
const match = entry.match;
|
|
2171
|
+
if (!match || typeof match !== "object" || Array.isArray(match)) continue;
|
|
2172
|
+
const shapes = match;
|
|
2173
|
+
if (typeof shapes.mode !== "string") continue;
|
|
2174
|
+
const fieldmatch = {
|
|
2175
|
+
mode: shapes.mode,
|
|
2176
|
+
...typeof shapes.label === "string" ? { label: shapes.label } : {},
|
|
2177
|
+
...typeof shapes.placeholder === "string" ? { placeholder: shapes.placeholder } : {},
|
|
2178
|
+
...typeof shapes.arialabel === "string" ? { arialabel: shapes.arialabel } : {},
|
|
2179
|
+
...typeof shapes.name === "string" ? { name: shapes.name } : {}
|
|
2180
|
+
};
|
|
2181
|
+
if (typeof entry.kind !== "string" || typeof entry.value !== "string") continue;
|
|
2182
|
+
entries.push({ match: fieldmatch, kind: entry.kind, value: entry.value });
|
|
2183
|
+
}
|
|
2184
|
+
if (entries.length === 0) return null;
|
|
2185
|
+
return { ...typeof record2.form === "string" && record2.form ? { form: record2.form } : {}, entries };
|
|
2186
|
+
}
|
|
2187
|
+
|
|
2188
|
+
// extension/pagewizards.ts
|
|
2189
|
+
function parsebackoff(step) {
|
|
2190
|
+
let options = {};
|
|
2191
|
+
try {
|
|
2192
|
+
options = parseoptions(step);
|
|
2193
|
+
} catch {
|
|
2194
|
+
options = {};
|
|
2195
|
+
}
|
|
2196
|
+
const backoff = options.backoff;
|
|
2197
|
+
if (!backoff || typeof backoff !== "object" || Array.isArray(backoff)) return null;
|
|
2198
|
+
const rule = backoff;
|
|
2199
|
+
const wait = rule.wait;
|
|
2200
|
+
const factor = rule.factor;
|
|
2201
|
+
if (typeof wait !== "number" || !Number.isFinite(wait) || wait <= 0) return null;
|
|
2202
|
+
if (typeof factor !== "number" || !Number.isFinite(factor) || factor < 1) return null;
|
|
2203
|
+
const attempts = typeof options.attempts === "number" && Number.isInteger(options.attempts) && options.attempts >= 1 ? options.attempts : 2;
|
|
2204
|
+
return { attempts, wait, factor };
|
|
2205
|
+
}
|
|
2206
|
+
function backoffwaits(attempts, wait, factor) {
|
|
2207
|
+
const windows = [];
|
|
2208
|
+
let current = wait;
|
|
2209
|
+
for (let index = 1; index < attempts; index += 1) {
|
|
2210
|
+
windows.push(current);
|
|
2211
|
+
current *= factor;
|
|
2212
|
+
}
|
|
2213
|
+
return windows;
|
|
2214
|
+
}
|
|
2215
|
+
|
|
1843
2216
|
// extension/background.ts
|
|
1844
2217
|
var sessionduration = 15 * 60 * 1e3;
|
|
1845
2218
|
var freshcheckkinds = /* @__PURE__ */ new Set(["focus", "inspect", "click", "type", "scroll", "select", "hover"]);
|
|
@@ -1849,6 +2222,7 @@ var observationstepkinds = /* @__PURE__ */ new Set(["a11ytree", "readvisible", "
|
|
|
1849
2222
|
var navigationstepkinds = /* @__PURE__ */ new Set(["openlink", "openprivate", "reloadcache", "stopnav", "waitload", "waiturl", "followlink", "spanav", "spawait", "rewritequery", "setfragment", "navlist", "navprofile", "detecthttp", "readredirects", "readfinalurl", "handleauth", "printpdf", "prefetch", "preconnect", "deeplink", "reopentab", "trailaudit", "pausenav", "navintent", "navrate", "openclipboard", "checksafe", "batchopen"]);
|
|
1850
2223
|
var pausenavkinds = /* @__PURE__ */ new Set(["openlink", "openprivate", "followlink", "spanav", "navlist", "openclipboard", "batchopen", "prefetch", "preconnect", "deeplink", "reopentab"]);
|
|
1851
2224
|
var ratecheckedkinds = /* @__PURE__ */ new Set(["openlink", "openprivate", "followlink", "spanav", "navlist", "openclipboard", "batchopen", "deeplink", "reopentab"]);
|
|
2225
|
+
var formfillkinds = /* @__PURE__ */ new Set(["fillform", "filllabel", "fillplaceholder", "fillcard", "fillcode", "attachfile", "saveprofiles", "runwizard", "selectchain", "picktypeahead", "pickdate"]);
|
|
1852
2226
|
var evidencepoll = 100;
|
|
1853
2227
|
var evidencesettle = 5e3;
|
|
1854
2228
|
var chromestorage = {
|
|
@@ -2025,7 +2399,11 @@ function stepauditkind(step, ok) {
|
|
|
2025
2399
|
}
|
|
2026
2400
|
if (step.kind === "dismissdialog") return "dialog";
|
|
2027
2401
|
if (step.kind === "keyhold" || step.kind === "keyrelease") return "hold";
|
|
2028
|
-
if (step.kind === "retryaction") return "retry";
|
|
2402
|
+
if (step.kind === "retryaction" || step.kind === "retryform") return "retry";
|
|
2403
|
+
if (step.kind === "submitform" || step.kind === "asksubmit") return "submit";
|
|
2404
|
+
if (step.kind === "consentpassword") return "consent";
|
|
2405
|
+
if (step.kind === "handoffcaptcha") return "handoff";
|
|
2406
|
+
if (formfillkinds.has(step.kind)) return "fill";
|
|
2029
2407
|
if (pointerkinds.has(step.kind)) return "pointer";
|
|
2030
2408
|
if (watchstepkinds.has(step.kind)) return "watch";
|
|
2031
2409
|
if (step.kind === "diffsnapshots") return "diff";
|
|
@@ -3067,6 +3445,146 @@ async function executetabscommand(step, session, plan, sessiontabid) {
|
|
|
3067
3445
|
return { ok: false, summary: "Unsupported tabs and windows command." };
|
|
3068
3446
|
}
|
|
3069
3447
|
}
|
|
3448
|
+
async function executesaveprofiles(step, session, origin) {
|
|
3449
|
+
const options = stepoptions2(step);
|
|
3450
|
+
const record2 = parseformrecord(options.formrecord);
|
|
3451
|
+
const name = typeof options.name === "string" ? options.name : "";
|
|
3452
|
+
if (!name || !record2) throw new Error("A reviewed profile name and form record are required.");
|
|
3453
|
+
const grants = session?.grants ?? (session ? [session.origin] : [origin]);
|
|
3454
|
+
const profile = { name, fields: record2.entries, grants, savedat: Date.now() };
|
|
3455
|
+
await memory.setprofile(profile);
|
|
3456
|
+
await audit("fill", `Form profile ${name} stored locally with ${profile.fields.length} field entries behind the origin grants of ${grants.join(", ")}; password entries are refused.`, { ...session ? { sessionid: session.id } : {} });
|
|
3457
|
+
return { ok: true, summary: `Stored the form profile ${name} locally with ${profile.fields.length} field entries.`, details: { profile: { name: profile.name, fields: profile.fields.length, grants: profile.grants } } };
|
|
3458
|
+
}
|
|
3459
|
+
async function executeasksubmit(step, session, plan, tabid2, origin) {
|
|
3460
|
+
const output = await dispatchpagestep(step, tabid2, origin, plan);
|
|
3461
|
+
const values = Array.isArray(output?.details?.values) ? output?.details?.values : [];
|
|
3462
|
+
const ticket = { id: randomid(), form: step.value ?? "", valueshash: valueshash(values), consentref: step.id, at: Date.now() };
|
|
3463
|
+
await memory.setticket(ticket);
|
|
3464
|
+
await audit("submit", `Asksubmit prompt opened for form ${ticket.form || "the reviewed form"} with the values hash ${ticket.valueshash}; the submission waits for the user approval.`, { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id });
|
|
3465
|
+
await refreshbadge();
|
|
3466
|
+
return { ok: true, summary: `Asksubmit prompt opened for form ${ticket.form || "the reviewed form"}; the submission waits for your approval.`, details: { ticket, values } };
|
|
3467
|
+
}
|
|
3468
|
+
async function executesubmitform(step, session, plan, tabid2, origin) {
|
|
3469
|
+
const consentref = typeof stepoptions2(step).consentref === "string" ? stepoptions2(step).consentref : "";
|
|
3470
|
+
const ticket = (await memory.gettickets()).find((item) => item.approved === true && (item.consentref === consentref || item.id === consentref));
|
|
3471
|
+
if (!ticket) throw new Error("No approved asksubmit ticket matches the reviewed consent ref; approve the submission in the review panel first.");
|
|
3472
|
+
const output = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The form submission returned no result." };
|
|
3473
|
+
await audit("submit", `Form ${ticket.form || "the reviewed form"} submitted through its owning form under ticket ${ticket.id} with values hash ${ticket.valueshash} and outcome ${output.ok ? "delivered" : "refused"}.`, { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id });
|
|
3474
|
+
return { ...output, details: { ...output.details ?? {}, ticket: { id: ticket.id, valueshash: ticket.valueshash, consentref: ticket.consentref } } };
|
|
3475
|
+
}
|
|
3476
|
+
async function executeretryform(step, session, plan, tabid2, origin) {
|
|
3477
|
+
const rule = parsebackoff(step);
|
|
3478
|
+
if (!rule) throw new Error("A reviewed backoff rule with wait and factor is required.");
|
|
3479
|
+
const windows = backoffwaits(rule.attempts, rule.wait, rule.factor);
|
|
3480
|
+
let attempts = 0;
|
|
3481
|
+
let output;
|
|
3482
|
+
while (attempts < rule.attempts) {
|
|
3483
|
+
attempts += 1;
|
|
3484
|
+
output = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The retried submission returned no result." };
|
|
3485
|
+
if (output.ok) break;
|
|
3486
|
+
const waitwindow = windows[attempts - 1];
|
|
3487
|
+
if (waitwindow !== void 0 && attempts < rule.attempts) await new Promise((resolve) => setTimeout(resolve, waitwindow));
|
|
3488
|
+
}
|
|
3489
|
+
await audit("retry", `Form submission retried ${attempts} time${attempts === 1 ? "" : "s"} with the reviewed backoff windows ${windows.join(", ") || "none"} milliseconds; outcome ${output?.ok ? "delivered" : "refused"}.`, { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id });
|
|
3490
|
+
return { ok: Boolean(output?.ok), summary: output?.summary ?? "The retried submission returned no result.", details: { attempts, windows, ok: Boolean(output?.ok) } };
|
|
3491
|
+
}
|
|
3492
|
+
async function executeconsentpassword(step, session, plan, tabid2, origin) {
|
|
3493
|
+
const consentref = typeof stepoptions2(step).consentref === "string" ? stepoptions2(step).consentref : "";
|
|
3494
|
+
const gate = passwordconsentgranted(step);
|
|
3495
|
+
if (!gate.allowed) throw new Error(gate.reason ?? "A password fill requires a reviewed consent ref.");
|
|
3496
|
+
const output = await dispatchpagestep(step, tabid2, origin, plan);
|
|
3497
|
+
await audit("consent", `Password field filled after the explicit consent ref ${consentref}; the value never appears in the audit trail.`, { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id });
|
|
3498
|
+
return output ?? { ok: false, summary: "The password fill returned no result." };
|
|
3499
|
+
}
|
|
3500
|
+
async function executeattachfile(step, session, plan, tabid2, origin) {
|
|
3501
|
+
const name = step.value ?? "";
|
|
3502
|
+
const artifacts = await memory.getartifacts();
|
|
3503
|
+
const artifact = artifacts.find((item) => item.name === name || item.id === name);
|
|
3504
|
+
if (!artifact) throw new Error(`No generated artifact named ${name} exists in the run store yet.`);
|
|
3505
|
+
const derived = { ...step, options: JSON.stringify({ ...stepoptions2(step), artifact: artifact.id, artifactname: artifact.name }) };
|
|
3506
|
+
const output = await dispatchpagestep(derived, tabid2, origin, plan);
|
|
3507
|
+
await audit("fill", `Artifact ${artifact.name} of kind ${artifact.kind} attached to the reviewed file input inside the form submission.`, { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id });
|
|
3508
|
+
return { ...output ?? { ok: false, summary: "The artifact attachment returned no result." }, details: { ...output?.details ?? {}, artifact } };
|
|
3509
|
+
}
|
|
3510
|
+
async function executecaptchahandoff(step, session, plan, tabid2, origin) {
|
|
3511
|
+
const output = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The captcha probe returned no result." };
|
|
3512
|
+
if (output.details?.captcha !== true) return { ok: true, summary: "No captcha was detected; the plan continues.", details: { captcha: false } };
|
|
3513
|
+
const handoff = { id: randomid(), origin, resolved: false, openedat: Date.now() };
|
|
3514
|
+
await memory.addcaptcha(handoff);
|
|
3515
|
+
if (session && !session.pausedat && !session.stoppedat) await memory.setsession({ ...session, pausedat: Date.now() });
|
|
3516
|
+
await audit("handoff", `Captcha detected on ${origin}; control handed back to the user and the plan pauses until the handoff ${handoff.id} resolves.`, { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id });
|
|
3517
|
+
await refreshbadge();
|
|
3518
|
+
return { ok: true, summary: "Captcha detected; control is yours and the plan waits until you resolve the handoff.", details: { captcha: true, handoff } };
|
|
3519
|
+
}
|
|
3520
|
+
async function executeformstep(step, session, plan, tabid2, origin) {
|
|
3521
|
+
switch (step.kind) {
|
|
3522
|
+
case "saveprofiles":
|
|
3523
|
+
return executesaveprofiles(step, session, origin);
|
|
3524
|
+
case "asksubmit":
|
|
3525
|
+
return executeasksubmit(step, session, plan, tabid2, origin);
|
|
3526
|
+
case "submitform":
|
|
3527
|
+
return executesubmitform(step, session, plan, tabid2, origin);
|
|
3528
|
+
case "retryform":
|
|
3529
|
+
return executeretryform(step, session, plan, tabid2, origin);
|
|
3530
|
+
case "consentpassword":
|
|
3531
|
+
return executeconsentpassword(step, session, plan, tabid2, origin);
|
|
3532
|
+
case "attachfile":
|
|
3533
|
+
return executeattachfile(step, session, plan, tabid2, origin);
|
|
3534
|
+
case "handoffcaptcha":
|
|
3535
|
+
return executecaptchahandoff(step, session, plan, tabid2, origin);
|
|
3536
|
+
case "fillcode": {
|
|
3537
|
+
const stored = await memory.getcodevalue();
|
|
3538
|
+
const source = typeof stepoptions2(step).source === "string" ? stepoptions2(step).source : "";
|
|
3539
|
+
const derived = stored !== void 0 && source === "reviewed" ? { ...step, value: stored } : step;
|
|
3540
|
+
const output = await dispatchpagestep(derived, tabid2, origin, plan);
|
|
3541
|
+
await audit("fill", `One time code typed from the reviewed source ${source}${stored !== void 0 ? " through the consent gated code entry" : ""}.`, { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id });
|
|
3542
|
+
return output ?? { ok: false, summary: "The one time code fill returned no result." };
|
|
3543
|
+
}
|
|
3544
|
+
default: {
|
|
3545
|
+
const output = await dispatchpagestep(step, tabid2, origin, plan);
|
|
3546
|
+
const extra = { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id };
|
|
3547
|
+
if (step.kind === "runwizard" && output?.details?.wizard && typeof output.details.wizard === "object") {
|
|
3548
|
+
const state = output.details.wizard;
|
|
3549
|
+
await memory.addwizard(state);
|
|
3550
|
+
await memory.setprogress(recordwizardstep(await memory.getprogress(), plan.id, step.id, state, Date.now()));
|
|
3551
|
+
await audit("fill", `Wizard advanced to step ${Math.min(state.index, state.steps)} of ${state.steps} with a completion share of ${Math.round(wizardcompletion(state) * 100)} percent.`, extra);
|
|
3552
|
+
}
|
|
3553
|
+
if (step.kind === "picktypeahead" && typeof output?.details?.pick === "string") {
|
|
3554
|
+
const pick = { field: step.target ?? "", query: step.value ?? "", pick: output.details.pick, at: Date.now() };
|
|
3555
|
+
await memory.addpick(pick);
|
|
3556
|
+
}
|
|
3557
|
+
if (step.kind === "readerrors" && Array.isArray(output?.details?.errors)) {
|
|
3558
|
+
const report = { form: step.target ?? "", errors: output?.details?.errors, at: Date.now() };
|
|
3559
|
+
await memory.adderrorreport(report);
|
|
3560
|
+
await audit("fill", `Collected ${report.errors.length} inline validation message${report.errors.length === 1 ? "" : "s"} for the correction loop.`, extra);
|
|
3561
|
+
}
|
|
3562
|
+
if ((step.kind === "detectlogin" || step.kind === "detecttemplate") && output?.ok) {
|
|
3563
|
+
const detected = step.kind === "detectlogin" ? output.details?.login === true : output.details?.template === "signup" || output.details?.template === "checkout";
|
|
3564
|
+
if (detected) {
|
|
3565
|
+
const kind = step.kind === "detectlogin" ? "login" : output.details?.template === "checkout" ? "checkout" : "signup";
|
|
3566
|
+
const markers = Array.isArray(output.details?.markers) ? output.details?.markers : [];
|
|
3567
|
+
const record2 = { origin, kind, markers, at: Date.now() };
|
|
3568
|
+
await memory.adddetection(record2);
|
|
3569
|
+
await audit("fill", `${kind} shape detected on ${origin} with markers ${markers.join(", ") || "none"}; sensitive work stays behind the consent gates.`, extra);
|
|
3570
|
+
}
|
|
3571
|
+
}
|
|
3572
|
+
if (step.kind === "skiphoneypot" && Array.isArray(output?.details?.skipped)) {
|
|
3573
|
+
await audit("fill", `Honeypot survey flagged ${output.details.skipped.length} field${output.details.skipped.length === 1 ? "" : "s"} as skipped so fills never trip them.`, extra);
|
|
3574
|
+
}
|
|
3575
|
+
if (step.kind === "fillform" || step.kind === "filllabel" || step.kind === "fillplaceholder") {
|
|
3576
|
+
const filled = typeof output?.details?.filled === "number" ? output.details.filled : 0;
|
|
3577
|
+
const skipped = Array.isArray(output?.details?.skipped) ? output.details.skipped.length : 0;
|
|
3578
|
+
await audit("fill", `Filled ${filled} reviewed field${filled === 1 ? "" : "s"}${skipped > 0 ? ` and skipped ${skipped} honeypot field${skipped === 1 ? "" : "s"}` : ""}.`, extra);
|
|
3579
|
+
}
|
|
3580
|
+
if (step.kind === "generatevalues") {
|
|
3581
|
+
const count = Array.isArray(output?.details?.values) ? output.details.values.length : 0;
|
|
3582
|
+
await audit("fill", `Generated ${count} realistic value${count === 1 ? "" : "s"} with the reviewed seed and locale; real looking card numbers and personal identifiers are refused.`, extra);
|
|
3583
|
+
}
|
|
3584
|
+
return output ?? { ok: false, summary: "The forms and data step returned no result." };
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3070
3588
|
async function enforcewindowreview(step, session, plan) {
|
|
3071
3589
|
const windowid = step.value && /^\d+$/.test(step.value) ? Number.parseInt(step.value, 10) : 0;
|
|
3072
3590
|
const progress = plan ? await memory.getprogress() : void 0;
|
|
@@ -3101,8 +3619,9 @@ async function togglecontroltab(enabled) {
|
|
|
3101
3619
|
async function refreshbadge() {
|
|
3102
3620
|
const queues = await memory.getnavqueues();
|
|
3103
3621
|
const badges = await memory.getbadges();
|
|
3622
|
+
const prompts = (await memory.gettickets()).filter((ticket) => ticket.approved === void 0).length;
|
|
3104
3623
|
const tasktabs2 = new Set(badges.map((badge) => badge.tabid)).size;
|
|
3105
|
-
const total = (queues?.prefetch ?? 0) + (queues?.batchopen ?? 0) + tasktabs2;
|
|
3624
|
+
const total = (queues?.prefetch ?? 0) + (queues?.batchopen ?? 0) + tasktabs2 + prompts;
|
|
3106
3625
|
await chrome.action.setBadgeText({ text: total > 0 ? String(total) : "" }).catch(() => {
|
|
3107
3626
|
});
|
|
3108
3627
|
}
|
|
@@ -3126,6 +3645,8 @@ async function executestep(stepid) {
|
|
|
3126
3645
|
}
|
|
3127
3646
|
if (istabscommandkind(step.kind)) {
|
|
3128
3647
|
output = await executetabscommand(step, session, plan, tab.id);
|
|
3648
|
+
} else if (isformkind(step.kind)) {
|
|
3649
|
+
output = await executeformstep(step, session, plan, tab.id, origin);
|
|
3129
3650
|
} else if (isbrowserkind(step.kind)) {
|
|
3130
3651
|
output = await runbrowseraction(step, tab.id, tab.windowId ?? chrome.windows.WINDOW_ID_CURRENT);
|
|
3131
3652
|
} else if (step.kind === "keyhold") {
|
|
@@ -3275,13 +3796,21 @@ async function handlerequest(message, sender) {
|
|
|
3275
3796
|
const closedtabs = await memory.getclosedtabs();
|
|
3276
3797
|
const controltab = await memory.getcontroltab();
|
|
3277
3798
|
const tabwatchevents = await memory.gettabwatchevents();
|
|
3799
|
+
const profiles = await memory.getprofiles();
|
|
3800
|
+
const tickets = await memory.gettickets();
|
|
3801
|
+
const wizards = await memory.getwizards();
|
|
3802
|
+
const picks = await memory.getpicks();
|
|
3803
|
+
const errorreports = await memory.geterrorreports();
|
|
3804
|
+
const captchas = await memory.getcaptchas();
|
|
3805
|
+
const detections = await memory.getdetections();
|
|
3806
|
+
const codeentry = await memory.getcodevalue();
|
|
3278
3807
|
const clones = clonetabs(tabs);
|
|
3279
3808
|
const taskgauge = tasktabgauge(tabs.filter((tab) => badges.some((badge) => badge.tabid === tab.tabid)).length, tasktabceiling(await memory.getsettings()));
|
|
3280
3809
|
const report = await buildtabreport(tabs);
|
|
3281
3810
|
const livetab = session ? await chrome.tabs.get(session.tabid).catch(() => void 0) : void 0;
|
|
3282
3811
|
const waitprofile = session ? waitprofiles.find((record2) => record2.origin === session.origin) : void 0;
|
|
3283
3812
|
const livestate = { phase: livetab?.status === "loading" ? "loading" : "complete", ...navrecords[0] ? { finalurl: navrecords[0].finalurl, redirects: navrecords[0].chain } : {} };
|
|
3284
|
-
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 };
|
|
3813
|
+
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 } : {} };
|
|
3285
3814
|
}
|
|
3286
3815
|
case "capabilities":
|
|
3287
3816
|
return refreshcapabilities();
|
|
@@ -3477,6 +4006,86 @@ async function handlerequest(message, sender) {
|
|
|
3477
4006
|
await audit("window", `The review panel closed window ${inputclose.windowid}${count > 0 ? ` while holding ${count} task tab${count === 1 ? "" : "s"} under explicit review` : ""}.`, { ...session ? { sessionid: session.id } : {} });
|
|
3478
4007
|
return { windowid: inputclose.windowid, closed: true };
|
|
3479
4008
|
}
|
|
4009
|
+
case "applyprofile": {
|
|
4010
|
+
const session = await memory.getsession();
|
|
4011
|
+
if (!session || session.stoppedat || session.expiresat <= Date.now()) throw new Error("Profile application stays inside the consent gate of an active session.");
|
|
4012
|
+
const inputprofile = message;
|
|
4013
|
+
const profile = await memory.getprofile(inputprofile.name ?? "");
|
|
4014
|
+
if (!profile) throw new Error(`No form profile named ${inputprofile.name ?? ""} is stored yet.`);
|
|
4015
|
+
const gate = profilegrantgranted(profile, session.origin);
|
|
4016
|
+
if (!gate.allowed) throw new Error(gate.reason);
|
|
4017
|
+
await audit("fill", `Form profile ${profile.name} applied as the reviewed field map for ${session.origin} under its origin grants.`, { sessionid: session.id });
|
|
4018
|
+
return { profile: { name: profile.name, fields: profile.fields, grants: profile.grants } };
|
|
4019
|
+
}
|
|
4020
|
+
case "approvesubmit": {
|
|
4021
|
+
const inputticket = message;
|
|
4022
|
+
const tickets = await memory.gettickets();
|
|
4023
|
+
const ticket = tickets.find((item) => item.id === inputticket.id);
|
|
4024
|
+
if (!ticket) throw new Error("No submission ticket matches the requested id.");
|
|
4025
|
+
const updated = { ...ticket, approved: inputticket.approved !== false };
|
|
4026
|
+
await memory.setticket(updated);
|
|
4027
|
+
const session = await memory.getsession();
|
|
4028
|
+
await audit("submit", `Submission ticket ${ticket.id} for form ${ticket.form} ${updated.approved ? "approved" : "declined"} by the user with values hash ${ticket.valueshash}.`, { ...session ? { sessionid: session.id } : {} });
|
|
4029
|
+
await refreshbadge();
|
|
4030
|
+
return updated;
|
|
4031
|
+
}
|
|
4032
|
+
case "resolvecaptcha": {
|
|
4033
|
+
const open = (await memory.getcaptchas()).find((item) => !item.resolved);
|
|
4034
|
+
if (!open) throw new Error("No open captcha handoff exists.");
|
|
4035
|
+
await memory.resolvecaptcha(open.id, Date.now());
|
|
4036
|
+
const session = await memory.getsession();
|
|
4037
|
+
if (session?.pausedat && !session.stoppedat) {
|
|
4038
|
+
const resumed = { id: session.id, tabid: session.tabid, origin: session.origin, startedat: session.startedat, expiresat: session.expiresat, ...session.grants ? { grants: session.grants } : {} };
|
|
4039
|
+
await memory.setsession(resumed);
|
|
4040
|
+
}
|
|
4041
|
+
await audit("handoff", `Captcha handoff ${open.id} resolved by the user after ${Date.now() - open.openedat} milliseconds; the plan continues.`, { ...session ? { sessionid: session.id } : {} });
|
|
4042
|
+
await refreshbadge();
|
|
4043
|
+
return { resolved: true, id: open.id };
|
|
4044
|
+
}
|
|
4045
|
+
case "storecode": {
|
|
4046
|
+
const session = await memory.getsession();
|
|
4047
|
+
if (!session || session.stoppedat || session.expiresat <= Date.now()) throw new Error("The one time code entry stays behind the consent gate of an active session.");
|
|
4048
|
+
const inputcode = message;
|
|
4049
|
+
if (!inputcode.code?.trim()) throw new Error("A non-empty one time code is required.");
|
|
4050
|
+
await memory.setcodevalue(inputcode.code.trim());
|
|
4051
|
+
await audit("consent", `A one time code was stored behind the consent gate of the active session; the value never appears in the audit trail.`, { sessionid: session.id });
|
|
4052
|
+
return { stored: true };
|
|
4053
|
+
}
|
|
4054
|
+
case "regeneratevalue": {
|
|
4055
|
+
const inputvalue = message;
|
|
4056
|
+
if (typeof inputvalue.field !== "string" || !inputvalue.field) throw new Error("A field kind is required to regenerate a value.");
|
|
4057
|
+
const rule = {};
|
|
4058
|
+
if (typeof inputvalue.locale === "string" && inputvalue.locale) rule.locale = inputvalue.locale;
|
|
4059
|
+
if (typeof inputvalue.seed === "number" && Number.isFinite(inputvalue.seed)) rule.seed = inputvalue.seed;
|
|
4060
|
+
const value = generatevalue(inputvalue.field, rule);
|
|
4061
|
+
const verdict = generatedvalueallowed(value);
|
|
4062
|
+
if (!verdict.allowed) throw new Error(verdict.reason ?? "The regenerated value was refused.");
|
|
4063
|
+
return { kind: inputvalue.field, value, locale: rule.locale ?? "en", seed: rule.seed ?? 1 };
|
|
4064
|
+
}
|
|
4065
|
+
case "removeprofile": {
|
|
4066
|
+
const inputprofile = message;
|
|
4067
|
+
const profile = await memory.getprofile(inputprofile.name ?? "");
|
|
4068
|
+
if (!profile) throw new Error(`No form profile named ${inputprofile.name ?? ""} is stored yet.`);
|
|
4069
|
+
await memory.removeprofile(profile.name);
|
|
4070
|
+
const session = await memory.getsession();
|
|
4071
|
+
await audit("fill", `Form profile ${profile.name} removed from local memory by the user.`, { ...session ? { sessionid: session.id } : {} });
|
|
4072
|
+
return { removed: true, name: profile.name };
|
|
4073
|
+
}
|
|
4074
|
+
case "formreport": {
|
|
4075
|
+
const plan = await memory.getplan();
|
|
4076
|
+
if (!plan) throw new Error("No plan is available for a form report envelope.");
|
|
4077
|
+
const outcome = (await memory.getoutcomes()).find((candidate) => plan.steps.some((step) => step.id === candidate.stepid && step.kind === "detectfields"));
|
|
4078
|
+
const report = outcome?.details?.report;
|
|
4079
|
+
if (!report) throw new Error("No form report has been captured yet.");
|
|
4080
|
+
return JSON.parse(formreportresponse({ report, plan }));
|
|
4081
|
+
}
|
|
4082
|
+
case "errorreport": {
|
|
4083
|
+
const plan = await memory.getplan();
|
|
4084
|
+
if (!plan) throw new Error("No plan is available for an error report envelope.");
|
|
4085
|
+
const report = (await memory.geterrorreports())[0];
|
|
4086
|
+
if (!report) throw new Error("No error report has been collected yet.");
|
|
4087
|
+
return JSON.parse(errorreportresponse({ report, plan }));
|
|
4088
|
+
}
|
|
3480
4089
|
case "stop": {
|
|
3481
4090
|
const session = await memory.getsession();
|
|
3482
4091
|
if (session) await memory.setsession({ ...session, stoppedat: Date.now() });
|