@wenathlan/extension 1.1.59 → 1.1.61
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 -3
- package/dist/cli.js +19 -2
- package/dist/environments.d.ts +96 -0
- package/dist/environments.d.ts.map +1 -0
- package/dist/immutablelog.d.ts +73 -0
- package/dist/immutablelog.d.ts.map +1 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1092 -1
- package/dist/index.js.map +4 -4
- package/dist/maskinputs.d.ts +52 -0
- package/dist/maskinputs.d.ts.map +1 -0
- package/dist/memory.d.ts +119 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/originpolicy.d.ts +150 -0
- package/dist/originpolicy.d.ts.map +1 -0
- package/dist/policy.d.ts +87 -1
- package/dist/policy.d.ts.map +1 -1
- package/dist/protocol.d.ts +121 -2
- package/dist/protocol.d.ts.map +1 -1
- package/dist/runstate.d.ts +127 -0
- package/dist/runstate.d.ts.map +1 -0
- package/dist/sandboxframe.d.ts +45 -0
- package/dist/sandboxframe.d.ts.map +1 -0
- package/dist/types.d.ts +312 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/extension/dist/background.js +1629 -50
- package/extension/dist/background.js.map +4 -4
- package/extension/dist/manifest.json +16 -2
- package/extension/dist/offscreen.html +7 -0
- package/extension/dist/offscreen.js +87 -0
- package/extension/dist/offscreen.js.map +7 -0
- package/extension/dist/pagebridge.js +98 -28
- package/extension/dist/pagebridge.js.map +3 -3
- package/extension/dist/popup.html +1 -1
- package/extension/dist/popup.js +77 -0
- package/extension/dist/popup.js.map +2 -2
- package/extension/dist/sandbox.html +28 -0
- package/extension/dist/sidepanel.html +1 -1
- package/extension/dist/sidepanel.js +572 -0
- package/extension/dist/sidepanel.js.map +4 -4
- package/extension/manifest.json +16 -2
- package/package.json +1 -1
|
@@ -71,6 +71,51 @@ function regexruleof(value) {
|
|
|
71
71
|
return { pattern: candidate.pattern, flags: candidate.flags, groups };
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
// originpolicy.ts
|
|
75
|
+
function stepoptions(step) {
|
|
76
|
+
if (!step.options) return {};
|
|
77
|
+
try {
|
|
78
|
+
const parsed = JSON.parse(step.options);
|
|
79
|
+
return Boolean(parsed) && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
80
|
+
} catch {
|
|
81
|
+
return {};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
var paymentkinds = /* @__PURE__ */ new Set(["fillcard", "fillcode"]);
|
|
85
|
+
var credentialkinds = /* @__PURE__ */ new Set(["consentpassword", "saveapikey", "handleauth", "authflow"]);
|
|
86
|
+
var deletekinds = /* @__PURE__ */ new Set(["discardtab", "closepattern", "clearcookies", "removeattribute", "cleanupartifacts"]);
|
|
87
|
+
var publishkinds = /* @__PURE__ */ new Set(["postform", "postfiles", "sendmessage", "submitform", "submitsearch", "writeclipboard"]);
|
|
88
|
+
var defaultsensitivekinds = /* @__PURE__ */ new Set(["attachfile", "uploadfile", "uploadfiles", "downloadfile", "downloadimages", "batchdownload", "pausedownload", "resumedownload", "quarantinedownload", "evaluate"]);
|
|
89
|
+
function sensitiveclassesof(step) {
|
|
90
|
+
const options2 = stepoptions(step);
|
|
91
|
+
const fields = Array.isArray(options2.fields) ? options2.fields.filter((item) => Boolean(item) && typeof item === "object") : [];
|
|
92
|
+
const names = [...fields.map((field) => typeof field.name === "string" ? field.name : ""), typeof options2.field === "string" ? options2.field : "", typeof options2.target === "string" ? options2.target : ""].map((name) => name.toLowerCase());
|
|
93
|
+
const carries = (shape) => names.some((name) => name.includes(shape));
|
|
94
|
+
const classes = /* @__PURE__ */ new Set();
|
|
95
|
+
if (paymentkinds.has(step.kind) || carries("card") || carries("cvc") || carries("cvv")) classes.add("payment");
|
|
96
|
+
const credentialshape = carries("password") || carries("token") || carries("secret") || carries("apikey") || carries("passphrase");
|
|
97
|
+
const submits = step.kind === "submitform" || step.kind === "postform" || step.kind === "submitsearch" || step.kind === "fillform" || step.kind === "filllabel" || step.kind === "fillplaceholder";
|
|
98
|
+
if (credentialkinds.has(step.kind) || submits && credentialshape) classes.add("credential");
|
|
99
|
+
if (deletekinds.has(step.kind)) classes.add("delete");
|
|
100
|
+
if (publishkinds.has(step.kind) || step.kind === "callrest" || step.kind === "callgraphql") {
|
|
101
|
+
const verb = typeof options2.method === "string" ? options2.method.trim().toUpperCase() : typeof options2.verb === "string" ? options2.verb.trim().toUpperCase() : "";
|
|
102
|
+
if (step.kind === "callrest" || step.kind === "callgraphql") {
|
|
103
|
+
if (verb !== "" && !["GET", "HEAD", "OPTIONS"].includes(verb)) classes.add("publish");
|
|
104
|
+
} else classes.add("publish");
|
|
105
|
+
}
|
|
106
|
+
const bydefault = defaultsensitivekinds.has(step.kind);
|
|
107
|
+
const list = [...classes];
|
|
108
|
+
if (list.length === 0 && !bydefault) return { classes: [], bydefault: false, sensitive: false, reason: `The ${step.kind} kind carries no sensitive class and no default sensitive grade.` };
|
|
109
|
+
return { classes: list, bydefault, sensitive: true, reason: `The ${step.kind} kind grades sensitive${list.length > 0 ? ` through the ${list.join(", ")} class${list.length === 1 ? "" : "es"}` : ""}${bydefault ? " by default" : ""}.` };
|
|
110
|
+
}
|
|
111
|
+
function denydefaultnotice(origin) {
|
|
112
|
+
return `The denydefault posture refuses ${origin} until the user adds the origin to the automation allowlist; no step dispatches without the grant.`;
|
|
113
|
+
}
|
|
114
|
+
function profilesummary(profile) {
|
|
115
|
+
if (profile === void 0) return "No origin profile exists for this origin yet; sensitive steps route through their fresh consent prompts.";
|
|
116
|
+
return `The origin profile of ${profile.origin} grants ${profile.grants.length} kind${profile.grants.length === 1 ? "" : "s"} and denies ${profile.denials.length} kind${profile.denials.length === 1 ? "" : "s"} the user reviewed.`;
|
|
117
|
+
}
|
|
118
|
+
|
|
74
119
|
// policy.ts
|
|
75
120
|
var sensitiveactions = /* @__PURE__ */ new Set(["click", "type", "navigate", "select", "presskey", "drag", "drop", "upload", "clear", "check", "uncheck", "toggle", "submit", "reload", "back", "forward", "writestorage", "setattribute", "removeattribute", "evaluate", "tabcreate", "tabactivate", "tabclose", "tabreload", "windowcreate", "windowclose", "windowresize", "downloadfile", "clickpoint", "shiftclick", "dismissdialog", "enterframe", "typetime", "appendtext", "setvalue", "typeedit", "keyhold", "keyrelease", "submitsearch", "selectmulti", "chooseradio", "setslider", "setdate", "setcolor", "openlink", "openprivate", "reloadcache", "stopnav", "followlink", "spanav", "rewritequery", "setfragment", "navlist", "navprofile", "handleauth", "printpdf", "prefetch", "preconnect", "deeplink", "reopentab", "pausenav", "navrate", "openclipboard", "batchopen", "duplicatetab", "closepattern", "pintab", "mutetab", "movetab", "movetabwindow", "grouptabs", "colorgroup", "collapsegroup", "discardtab", "reloadtabs", "zoomin", "zoomout", "switchtab", "maximizewindow", "minimizewindow", "restorewindow", "focuswindow", "scratchwindow", "incognitowindow", "restoretab", "restorelayout", "reopenrun", "badgetab", "fillform", "filllabel", "fillplaceholder", "submitform", "retryform", "runwizard", "selectchain", "picktypeahead", "pickdate", "attachfile", "fillcard", "fillcode", "consentpassword", "exportcsv", "exportjson", "exportexcel", "copytable", "pushsheets", "streamdisk", "paginateextract", "resumeextract", "batchdownload", "pausedownload", "resumedownload", "interceptmime", "readclipboard", "writeclipboard", "copyscreen", "quarantinedownload", "scanvirus", "cleanupartifacts", "recordscreen", "captureaudio", "downloadimages", "callrest", "callgraphql", "sendmessage", "blockrequest", "mockresponse", "rewriteheaders", "setcookies", "clearcookies", "authflow", "saveapikey", "routeproxy", "postform", "postfiles", "attachcdp", "detachcdp", "cdpcmd", "overridescript", "heapshot", "profilecpu", "capturesourcemaps", "emulatedevice", "emulatenetwork", "emulatelocate", "setuseragent", "overridepermission", "restoresession", "exportsessions", "importsessions", "runworkflow", "visitrule", "urlrule", "menurule", "keyrule", "buttonrule", "cronrule", "intervalrule", "urllistrule", "webhookrule", "eventrule"]);
|
|
76
121
|
var interactionactions = /* @__PURE__ */ new Set(["focus", "scroll", "hover", "clickdeep", "rightclick", "doubleclick", "scrollpage", "scrollby", "scrollend", "scrolltop", "fullscreen", "zoomset", "movepointer", "clicktext", "clickaria", "clickname", "expanddetails", "pierceshadow", "retryaction", "capturebodies", "setbreakpoint", "stepcode", "watchexpr", "loop", "repeatuntil", "whileloop", "foreach", "parallel", "trycatch"]);
|
|
@@ -319,6 +364,12 @@ function redoedit(model) {
|
|
|
319
364
|
return { ...next, redo: redo.slice(0, -1), undo: [...model.undo ?? [], current] };
|
|
320
365
|
}
|
|
321
366
|
|
|
367
|
+
// progress.ts
|
|
368
|
+
function environmentof(progress, planid, stepid) {
|
|
369
|
+
if (!progress || progress.planid !== planid) return void 0;
|
|
370
|
+
return (progress.environments ?? {})[stepid];
|
|
371
|
+
}
|
|
372
|
+
|
|
322
373
|
// extension/tabscommand.ts
|
|
323
374
|
function switcherlist(tabs, recency, filter) {
|
|
324
375
|
const needle = filter.trim().toLowerCase();
|
|
@@ -395,6 +446,8 @@ var triggersroot = document.querySelector("#triggers");
|
|
|
395
446
|
var agentprotocolroot = document.querySelector("#agentprotocol");
|
|
396
447
|
var modelsroot = document.querySelector("#models");
|
|
397
448
|
var agentsroot = document.querySelector("#agents");
|
|
449
|
+
var environmentsroot = document.querySelector("#environments");
|
|
450
|
+
var securityroot = document.querySelector("#security");
|
|
398
451
|
var commandinput = document.querySelector("#command");
|
|
399
452
|
var commandbutton = document.querySelector("#commandsend");
|
|
400
453
|
var intentbadge = document.querySelector("#intentbadge");
|
|
@@ -585,6 +638,13 @@ function stepitem(plan, step, completed, outcomes, retries, progress) {
|
|
|
585
638
|
const item = document.createElement("li");
|
|
586
639
|
const done = completed.includes(step.id);
|
|
587
640
|
item.textContent = `${done ? "\u2713" : ""} ${step.summary}${holddetail(step)}`;
|
|
641
|
+
const environmentbadge = progress?.planid === plan.id ? environmentof(progress, plan.id, step.id) ?? step.environment : step.environment;
|
|
642
|
+
if (environmentbadge !== void 0) {
|
|
643
|
+
const badge = document.createElement("span");
|
|
644
|
+
badge.className = "muted";
|
|
645
|
+
badge.textContent = ` [${environmentbadge}]`;
|
|
646
|
+
item.append(badge);
|
|
647
|
+
}
|
|
588
648
|
const outcome = renderoutcome(step, outcomes);
|
|
589
649
|
if (outcome) item.append(outcome);
|
|
590
650
|
const timeline = step.kind === "retryaction" ? retrydetail(step, retries) : quietdetail(step, outcomes);
|
|
@@ -2883,6 +2943,8 @@ async function refresh() {
|
|
|
2883
2943
|
renderagentprotocol(context);
|
|
2884
2944
|
rendermodels(context);
|
|
2885
2945
|
renderagents(context);
|
|
2946
|
+
renderenvironments(context);
|
|
2947
|
+
rendersecurity(context);
|
|
2886
2948
|
renderconsolediff();
|
|
2887
2949
|
renderaudit(context.audit);
|
|
2888
2950
|
rendercapabilities(context.capabilities);
|
|
@@ -5543,6 +5605,489 @@ function rendermodels(context) {
|
|
|
5543
5605
|
}
|
|
5544
5606
|
modelsroot.append(guardbox);
|
|
5545
5607
|
}
|
|
5608
|
+
function rendersecurity(context) {
|
|
5609
|
+
if (!securityroot) return;
|
|
5610
|
+
securityroot.replaceChildren();
|
|
5611
|
+
const view = context.security;
|
|
5612
|
+
if (!view) {
|
|
5613
|
+
securityroot.textContent = "The security state is unknown.";
|
|
5614
|
+
return;
|
|
5615
|
+
}
|
|
5616
|
+
const head = document.createElement("p");
|
|
5617
|
+
head.textContent = `Posture ${view.posture}: the allowlist holds ${view.allowlist.length} exact origin${view.allowlist.length === 1 ? "" : "s"} with no wildcard expansion, ${view.profiles.length} origin profile${view.profiles.length === 1 ? "" : "s"}, ${view.windows.filter((window2) => window2.state === "active").length} active consent window${view.windows.filter((window2) => window2.state === "active").length === 1 ? "" : "s"} and ${view.revocations.length} recorded revocation${view.revocations.length === 1 ? "" : "s"}.`;
|
|
5618
|
+
securityroot.append(head);
|
|
5619
|
+
const allowbox = document.createElement("details");
|
|
5620
|
+
allowbox.className = "sessiongroup";
|
|
5621
|
+
const allowsummary = document.createElement("summary");
|
|
5622
|
+
allowsummary.textContent = "Automation allowlist (denydefault)";
|
|
5623
|
+
allowbox.append(allowsummary);
|
|
5624
|
+
if (view.allowlist.length === 0) {
|
|
5625
|
+
const none = document.createElement("p");
|
|
5626
|
+
none.textContent = "No origin is granted yet; the denydefault posture refuses every ungranted origin.";
|
|
5627
|
+
allowbox.append(none);
|
|
5628
|
+
}
|
|
5629
|
+
for (const entry of view.allowlist) {
|
|
5630
|
+
const line = document.createElement("p");
|
|
5631
|
+
line.textContent = `${entry.origin} \xB7 profile workspace ${entry.profileid} \xB7 granted ${new Date(entry.grantedat).toLocaleString()}`;
|
|
5632
|
+
line.append(" ", button("Remove", async () => {
|
|
5633
|
+
await request({ kind: "security", allowlist: { remove: { origin: entry.origin } } });
|
|
5634
|
+
await refresh();
|
|
5635
|
+
}));
|
|
5636
|
+
allowbox.append(line);
|
|
5637
|
+
}
|
|
5638
|
+
const origininput = document.createElement("input");
|
|
5639
|
+
origininput.placeholder = "https://exact.example";
|
|
5640
|
+
origininput.setAttribute("aria-label", "Exact origin to grant");
|
|
5641
|
+
const addorigin = button("Grant exact origin", async () => {
|
|
5642
|
+
const origin = origininput.value.trim();
|
|
5643
|
+
if (origin === "") throw new Error("The allowlist grant needs its exact origin.");
|
|
5644
|
+
await request({ kind: "security", allowlist: { add: { origin } } });
|
|
5645
|
+
origininput.value = "";
|
|
5646
|
+
await refresh();
|
|
5647
|
+
});
|
|
5648
|
+
allowbox.append(origininput, " ", addorigin);
|
|
5649
|
+
const allownote = document.createElement("p");
|
|
5650
|
+
allownote.className = "muted";
|
|
5651
|
+
allownote.textContent = "The allowlist binds every grant to one exact origin with no wildcard expansion; the active tab grant counts as exactly one explicit single origin grant, and grants never outlive their named boundary.";
|
|
5652
|
+
allowbox.append(allownote);
|
|
5653
|
+
securityroot.append(allowbox);
|
|
5654
|
+
const profilebox = document.createElement("details");
|
|
5655
|
+
profilebox.className = "sessiongroup";
|
|
5656
|
+
const profilesummarynode = document.createElement("summary");
|
|
5657
|
+
profilesummarynode.textContent = "Origin profiles";
|
|
5658
|
+
profilebox.append(profilesummarynode);
|
|
5659
|
+
if (view.profiles.length === 0) {
|
|
5660
|
+
const none = document.createElement("p");
|
|
5661
|
+
none.textContent = profilesummary(void 0);
|
|
5662
|
+
profilebox.append(none);
|
|
5663
|
+
}
|
|
5664
|
+
for (const profile of view.profiles) {
|
|
5665
|
+
const line = document.createElement("p");
|
|
5666
|
+
line.textContent = `${profile.origin}: ${profile.grants.length} granted kind${profile.grants.length === 1 ? "" : "s"}${profile.denials.length > 0 ? `, ${profile.denials.length} denied kind${profile.denials.length === 1 ? "" : "s"}` : ""}.`;
|
|
5667
|
+
profilebox.append(line);
|
|
5668
|
+
}
|
|
5669
|
+
if (context.session?.origin !== void 0) {
|
|
5670
|
+
const kindinput = document.createElement("input");
|
|
5671
|
+
kindinput.placeholder = "action kind such as submitform";
|
|
5672
|
+
kindinput.setAttribute("aria-label", "Action kind for the origin profile");
|
|
5673
|
+
profilebox.append(kindinput, " ", button("Grant kind on active origin", async () => {
|
|
5674
|
+
const kind = kindinput.value.trim();
|
|
5675
|
+
if (kind === "") throw new Error("The origin profile decision needs its action kind.");
|
|
5676
|
+
await request({ kind: "security", profile: { origin: context.session?.origin, kind, decision: "grant" } });
|
|
5677
|
+
await refresh();
|
|
5678
|
+
}), " ", button("Deny kind on active origin", async () => {
|
|
5679
|
+
const kind = kindinput.value.trim();
|
|
5680
|
+
if (kind === "") throw new Error("The origin profile decision needs its action kind.");
|
|
5681
|
+
await request({ kind: "security", profile: { origin: context.session?.origin, kind, decision: "deny" } });
|
|
5682
|
+
await refresh();
|
|
5683
|
+
}));
|
|
5684
|
+
}
|
|
5685
|
+
securityroot.append(profilebox);
|
|
5686
|
+
const windowbox = document.createElement("details");
|
|
5687
|
+
windowbox.className = "sessiongroup";
|
|
5688
|
+
const windowsummary = document.createElement("summary");
|
|
5689
|
+
windowsummary.textContent = "Consent windows";
|
|
5690
|
+
windowbox.append(windowsummary);
|
|
5691
|
+
const now = Date.now();
|
|
5692
|
+
if (view.windows.length === 0) {
|
|
5693
|
+
const none = document.createElement("p");
|
|
5694
|
+
none.textContent = "No consent window exists yet; every sensitive step waits for its prompt.";
|
|
5695
|
+
windowbox.append(none);
|
|
5696
|
+
}
|
|
5697
|
+
for (const window2 of view.windows) {
|
|
5698
|
+
const remaining = window2.state === "active" ? Math.max(0, window2.expiresat - now) : 0;
|
|
5699
|
+
const line = document.createElement("p");
|
|
5700
|
+
line.textContent = `${window2.origin} \xB7 ${window2.state === "active" ? `active, ${Math.ceil(remaining / 1e3)}s of the ${window2.boundary} boundary left` : `closed at ${new Date(window2.closedat ?? window2.expiresat).toLocaleString()}`} \xB7 ${window2.kinds.length} kind${window2.kinds.length === 1 ? "" : "s"} covered.`;
|
|
5701
|
+
if (window2.state === "active") {
|
|
5702
|
+
const durationinput2 = document.createElement("input");
|
|
5703
|
+
durationinput2.placeholder = "duration in ms";
|
|
5704
|
+
durationinput2.setAttribute("aria-label", "Renewal duration in milliseconds");
|
|
5705
|
+
line.append(" ", durationinput2, " ", button("Renew", async () => {
|
|
5706
|
+
const duration = Number(durationinput2.value.trim());
|
|
5707
|
+
const offer = Number.isFinite(duration) && duration > 0 ? duration : view.promptduration;
|
|
5708
|
+
if (offer === void 0) throw new Error("The renewal prompt needs its duration in milliseconds; no grant ever defaults to unlimited.");
|
|
5709
|
+
await request({ kind: "security", consent: { renew: { windowid: window2.id, duration: offer } } });
|
|
5710
|
+
await refresh();
|
|
5711
|
+
}));
|
|
5712
|
+
}
|
|
5713
|
+
windowbox.append(line);
|
|
5714
|
+
}
|
|
5715
|
+
const durationinput = document.createElement("input");
|
|
5716
|
+
durationinput.placeholder = "duration in ms";
|
|
5717
|
+
durationinput.setAttribute("aria-label", "Consent window duration in milliseconds");
|
|
5718
|
+
const offered = view.promptduration !== void 0 ? String(view.promptduration) : "";
|
|
5719
|
+
if (offered !== "") durationinput.value = offered;
|
|
5720
|
+
windowbox.append(durationinput, " ", button("Open consent window", async () => {
|
|
5721
|
+
const duration = Number(durationinput.value.trim());
|
|
5722
|
+
if (!Number.isFinite(duration) || duration <= 0) throw new Error("The consent prompt needs its duration in milliseconds; no grant ever defaults to unlimited.");
|
|
5723
|
+
await request({ kind: "security", consent: { open: { duration } } });
|
|
5724
|
+
await refresh();
|
|
5725
|
+
}));
|
|
5726
|
+
const windownote = document.createElement("p");
|
|
5727
|
+
windownote.className = "muted";
|
|
5728
|
+
windownote.textContent = "Every consent window scopes to one session and one origin and names its boundary; a window past its boundary suspends the run mid step and the executor refuses to resume without a new explicit prompt.";
|
|
5729
|
+
windowbox.append(windownote);
|
|
5730
|
+
securityroot.append(windowbox);
|
|
5731
|
+
const promptbox = document.createElement("details");
|
|
5732
|
+
promptbox.className = "sessiongroup";
|
|
5733
|
+
const promptsummary = document.createElement("summary");
|
|
5734
|
+
promptsummary.textContent = "Consent prompts of the pending plan";
|
|
5735
|
+
promptbox.append(promptsummary);
|
|
5736
|
+
const prompts = view.prompts ?? [];
|
|
5737
|
+
if (prompts.length === 0) {
|
|
5738
|
+
const none = document.createElement("p");
|
|
5739
|
+
none.textContent = "No sensitive step of the current plan waits for a fresh consent prompt.";
|
|
5740
|
+
promptbox.append(none);
|
|
5741
|
+
}
|
|
5742
|
+
for (const pending of prompts) {
|
|
5743
|
+
const classification = context.plan?.steps.find((step) => step.id === pending.stepid) !== void 0 ? sensitiveclassesof(context.plan.steps.find((step) => step.id === pending.stepid)) : { classes: [], bydefault: false };
|
|
5744
|
+
const line = document.createElement("p");
|
|
5745
|
+
line.textContent = `${pending.prompt}${classification.classes.length > 0 ? ` Class badge: ${classification.classes.join(", ")}.` : classification.bydefault ? " Class badge: sensitive by default." : ""}`;
|
|
5746
|
+
promptbox.append(line);
|
|
5747
|
+
}
|
|
5748
|
+
if (context.session?.origin !== void 0 && prompts.length > 0) {
|
|
5749
|
+
promptbox.append(button("Consent to every sensitive class", async () => {
|
|
5750
|
+
const classes = /* @__PURE__ */ new Set();
|
|
5751
|
+
for (const pending of prompts) for (const step of context.plan?.steps ?? []) if (step.id === pending.stepid) for (const classname of sensitiveclassesof(step).classes) classes.add(classname);
|
|
5752
|
+
if (classes.size === 0) throw new Error("The pending steps grade sensitive by default; open their consent window instead.");
|
|
5753
|
+
await request({ kind: "security", consent: { classes: { origin: context.session?.origin, classes: [...classes] } } });
|
|
5754
|
+
await refresh();
|
|
5755
|
+
}));
|
|
5756
|
+
}
|
|
5757
|
+
securityroot.append(promptbox);
|
|
5758
|
+
const consentbox = document.createElement("div");
|
|
5759
|
+
consentbox.className = "actions";
|
|
5760
|
+
const revoke = button("Revoke consent mid run", async () => {
|
|
5761
|
+
const plan = context.plan;
|
|
5762
|
+
if (!plan) throw new Error("The revocation needs its run; no plan is open.");
|
|
5763
|
+
const halted = plan.steps.map((step) => `${step.id} (${step.kind})`);
|
|
5764
|
+
const confirmnode = document.createElement("p");
|
|
5765
|
+
confirmnode.textContent = `Confirm the revokerun: the pending step and every queued step halt without executing${halted.length > 0 ? ` (${halted.length} step${halted.length === 1 ? "" : "s"} of the plan: ${halted.slice(0, 5).join(", ")}${halted.length > 5 ? ", \u2026" : ""})` : ""}.`;
|
|
5766
|
+
confirmnode.append(" ", button("Confirm revokerun", async () => {
|
|
5767
|
+
await request({ kind: "security", revoke: { runid: plan.id } });
|
|
5768
|
+
await refresh();
|
|
5769
|
+
}), " ", button("Cancel", async () => {
|
|
5770
|
+
confirmnode.remove();
|
|
5771
|
+
}));
|
|
5772
|
+
securityroot.prepend(confirmnode);
|
|
5773
|
+
});
|
|
5774
|
+
consentbox.append(revoke);
|
|
5775
|
+
const revocations = view.revocations;
|
|
5776
|
+
if (revocations.length > 0) consentbox.append(document.createElement("span"), document.createTextNode(`Last revocation: ${revocations[0]?.runid} halted ${revocations[0]?.haltedstepids.length} step${revocations[0]?.haltedstepids.length === 1 ? "" : "s"}.`));
|
|
5777
|
+
securityroot.append(consentbox);
|
|
5778
|
+
const maskbox = document.createElement("details");
|
|
5779
|
+
maskbox.className = "sessiongroup";
|
|
5780
|
+
const masksummary = document.createElement("summary");
|
|
5781
|
+
masksummary.textContent = "Mask rules for sensitive field shapes";
|
|
5782
|
+
maskbox.append(masksummary);
|
|
5783
|
+
const shapeinput = document.createElement("input");
|
|
5784
|
+
shapeinput.placeholder = "field shape such as accountnumber";
|
|
5785
|
+
shapeinput.setAttribute("aria-label", "Field shape to mask");
|
|
5786
|
+
maskbox.append(shapeinput, " ", button("Add mask shape", async () => {
|
|
5787
|
+
const shape = shapeinput.value.trim();
|
|
5788
|
+
if (shape === "") throw new Error("The mask rule needs its field shape.");
|
|
5789
|
+
await request({ kind: "security", mask: { add: { shapes: [shape] } } });
|
|
5790
|
+
await refresh();
|
|
5791
|
+
}));
|
|
5792
|
+
if (view.maskrules.length === 0) {
|
|
5793
|
+
const none = document.createElement("p");
|
|
5794
|
+
none.textContent = "No custom mask rule exists; the documented password, token, card and secret shapes always apply.";
|
|
5795
|
+
maskbox.append(none);
|
|
5796
|
+
}
|
|
5797
|
+
for (const rule of view.maskrules) {
|
|
5798
|
+
const line = document.createElement("p");
|
|
5799
|
+
line.textContent = `${rule.shapes.join(", ")}${rule.origin !== void 0 ? ` scoped to ${rule.origin}` : " global"} \xB7 values show as [redacted] fields in step details.`;
|
|
5800
|
+
line.append(" ", button("Remove", async () => {
|
|
5801
|
+
await request({ kind: "security", mask: { remove: { id: rule.id } } });
|
|
5802
|
+
await refresh();
|
|
5803
|
+
}));
|
|
5804
|
+
maskbox.append(line);
|
|
5805
|
+
}
|
|
5806
|
+
securityroot.append(maskbox);
|
|
5807
|
+
const chainbox = document.createElement("details");
|
|
5808
|
+
chainbox.className = "sessiongroup";
|
|
5809
|
+
const chainsummary = document.createElement("summary");
|
|
5810
|
+
chainsummary.textContent = "Run log chain verification";
|
|
5811
|
+
chainbox.append(chainsummary);
|
|
5812
|
+
if (view.chain.length === 0) {
|
|
5813
|
+
const none = document.createElement("p");
|
|
5814
|
+
none.textContent = "No run log exists yet; the session start writes the consentscope grant into the immutable log.";
|
|
5815
|
+
chainbox.append(none);
|
|
5816
|
+
}
|
|
5817
|
+
for (const chain of view.chain) {
|
|
5818
|
+
const line = document.createElement("p");
|
|
5819
|
+
line.textContent = `${chain.runid}: ${chain.valid ? "chain verified" : `chain broken at entry ${chain.brokenat ?? "unknown"}`} \xB7 ${chain.entries} entries${chain.sealhash !== void 0 ? ` \xB7 seal hash ${chain.sealhash.slice(0, 12)}\u2026 at ${new Date(chain.sealedat ?? 0).toLocaleString()}` : " \xB7 unsealed"}.`;
|
|
5820
|
+
line.append(" ", button("Read log", async () => {
|
|
5821
|
+
const read = await request({ kind: "security", read: { runid: chain.runid } });
|
|
5822
|
+
status(read.verification ?? `The log of ${chain.runid} read through the audit accessor.`);
|
|
5823
|
+
}), " ", button("Export audit file", async () => {
|
|
5824
|
+
const exported = await request({ kind: "security", export: { runid: chain.runid } });
|
|
5825
|
+
status(`The verified run log of ${chain.runid} was exported as an audit file with ${exported.export?.entries ?? 0} entries.`);
|
|
5826
|
+
}));
|
|
5827
|
+
chainbox.append(line);
|
|
5828
|
+
}
|
|
5829
|
+
const chainnote = document.createElement("p");
|
|
5830
|
+
chainnote.className = "muted";
|
|
5831
|
+
chainnote.textContent = "The immutable log appends only: every entry chains through its loghash to its predecessor, the session close seals the chain with a final hash, and the audit accessor refuses reads of a chain with a broken link.";
|
|
5832
|
+
chainbox.append(chainnote);
|
|
5833
|
+
securityroot.append(chainbox);
|
|
5834
|
+
if (view.sessionorigin !== void 0 && !view.allowlist.some((entry) => entry.origin === view.sessionorigin)) {
|
|
5835
|
+
const notice = document.createElement("p");
|
|
5836
|
+
notice.textContent = denydefaultnotice(view.sessionorigin);
|
|
5837
|
+
securityroot.append(notice);
|
|
5838
|
+
}
|
|
5839
|
+
}
|
|
5840
|
+
function renderenvironments(context) {
|
|
5841
|
+
if (!environmentsroot) return;
|
|
5842
|
+
environmentsroot.replaceChildren();
|
|
5843
|
+
const view = context.environments;
|
|
5844
|
+
if (!view) {
|
|
5845
|
+
environmentsroot.textContent = "The execution environment state is unknown.";
|
|
5846
|
+
return;
|
|
5847
|
+
}
|
|
5848
|
+
const head = document.createElement("p");
|
|
5849
|
+
head.textContent = `Steps by environment: ${view.report.environments.length} executed \xB7 offscreen ${view.offscreengranted ? "granted" : "not granted"} \xB7 parse offload ${view.parseoffload ? "on" : "off"} \xB7 workers ${view.report.workers} \xB7 sandbox origins ${view.sandboxorigins?.length ?? 0} configured.`;
|
|
5850
|
+
environmentsroot.append(head);
|
|
5851
|
+
const keepalive = view.report.keepalive;
|
|
5852
|
+
const keepbox = document.createElement("div");
|
|
5853
|
+
keepbox.className = "actions";
|
|
5854
|
+
const beatinfo = document.createElement("span");
|
|
5855
|
+
beatinfo.textContent = keepalive ? `Keepalive ${keepalive.state}: ${keepalive.beats} beat${keepalive.beats === 1 ? "" : "s"}, port ${keepalive.portopen ? "open" : "closed"}, last beat ${new Date(keepalive.lastbeatat).toLocaleTimeString()}${view.workerpoolsize !== void 0 ? ` \xB7 pool size ${view.workerpoolsize}` : ""}.` : "No run state holds a keepalive port.";
|
|
5856
|
+
keepbox.append(beatinfo);
|
|
5857
|
+
if (keepalive?.state === "active") {
|
|
5858
|
+
keepbox.append(button("Beat now", async () => {
|
|
5859
|
+
await request({ kind: "runstate", beat: true });
|
|
5860
|
+
await refresh();
|
|
5861
|
+
}), button("Close run state", async () => {
|
|
5862
|
+
await request({ kind: "runstate", stop: true });
|
|
5863
|
+
await refresh();
|
|
5864
|
+
}));
|
|
5865
|
+
}
|
|
5866
|
+
environmentsroot.append(keepbox);
|
|
5867
|
+
const keepnote = document.createElement("p");
|
|
5868
|
+
keepnote.className = "muted";
|
|
5869
|
+
keepnote.textContent = "Keepalive runs only during active reviewed plans: the port opens behind an approved plan of an active session and closes at every terminal state.";
|
|
5870
|
+
environmentsroot.append(keepnote);
|
|
5871
|
+
if (view.restartnotice !== void 0 || view.recovery !== void 0) {
|
|
5872
|
+
const notice = document.createElement("p");
|
|
5873
|
+
notice.textContent = view.restartnotice ?? view.recovery?.reason ?? "";
|
|
5874
|
+
if (view.recovery?.recoverable === true) notice.append(" ", button("Resume pending step", async () => {
|
|
5875
|
+
const result = await request({ kind: "runstate", recover: true });
|
|
5876
|
+
status(result.resumed ?? result.recovery?.reason ?? "The recovery finished.");
|
|
5877
|
+
await refresh();
|
|
5878
|
+
}));
|
|
5879
|
+
environmentsroot.append(notice);
|
|
5880
|
+
}
|
|
5881
|
+
if (view.zombies.length > 0) {
|
|
5882
|
+
const warning = document.createElement("p");
|
|
5883
|
+
warning.textContent = `Zombie run${view.zombies.length === 1 ? "" : "s"} whose heartbeat fell silent: ${view.zombies.join(", ")}.`;
|
|
5884
|
+
warning.append(" ", button("Reap zombie runs", async () => {
|
|
5885
|
+
const result = await request({ kind: "runstate", reap: true });
|
|
5886
|
+
status(`The reaper closed ${result.reaped?.length ?? 0} zombie run${result.reaped?.length === 1 ? "" : "s"}.`);
|
|
5887
|
+
await refresh();
|
|
5888
|
+
}));
|
|
5889
|
+
environmentsroot.append(warning);
|
|
5890
|
+
}
|
|
5891
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
5892
|
+
for (const entry of view.report.environments) grouped.set(entry.environment, [...grouped.get(entry.environment) ?? [], entry.stepid]);
|
|
5893
|
+
const groupbox = document.createElement("details");
|
|
5894
|
+
groupbox.className = "sessiongroup";
|
|
5895
|
+
const groupsummary = document.createElement("summary");
|
|
5896
|
+
groupsummary.textContent = "Steps grouped by execution environment";
|
|
5897
|
+
groupbox.append(groupsummary);
|
|
5898
|
+
if (grouped.size === 0) {
|
|
5899
|
+
const none = document.createElement("p");
|
|
5900
|
+
none.textContent = "No step of the open run has executed yet.";
|
|
5901
|
+
groupbox.append(none);
|
|
5902
|
+
}
|
|
5903
|
+
for (const [environment, stepids] of grouped) {
|
|
5904
|
+
const line = document.createElement("p");
|
|
5905
|
+
line.textContent = `${environment}: ${stepids.length} step${stepids.length === 1 ? "" : "s"}${view.report.turnarounds.filter((entry) => stepids.includes(entry.stepid)).length > 0 ? ` \xB7 worker turnaround ${view.report.turnarounds.filter((entry) => stepids.includes(entry.stepid)).map((entry) => `${entry.stepid}: ${entry.milliseconds} ms`).join(", ")}` : ""}`;
|
|
5906
|
+
groupbox.append(line);
|
|
5907
|
+
}
|
|
5908
|
+
environmentsroot.append(groupbox);
|
|
5909
|
+
const grantbox = document.createElement("div");
|
|
5910
|
+
grantbox.className = "actions";
|
|
5911
|
+
const grantinfo = document.createElement("span");
|
|
5912
|
+
grantinfo.textContent = view.grants !== void 0 ? `Session environment grants: ${view.grants.length > 0 ? view.grants.join(", ") : "the documented default posture"}.` : "Session environment grants: the documented default posture (no grant list configured).";
|
|
5913
|
+
grantbox.append(grantinfo);
|
|
5914
|
+
const allenvironments = ["pagecontext", "isolatedworld", "offscreenworker", "sandboxframe"];
|
|
5915
|
+
for (const environment of allenvironments) {
|
|
5916
|
+
const active = view.grants?.includes(environment) ?? false;
|
|
5917
|
+
grantbox.append(" ", button(`${active ? "Revoke" : "Grant"} ${environment}`, async () => {
|
|
5918
|
+
const current = view.grants ?? [];
|
|
5919
|
+
const next = active ? current.filter((entry) => entry !== environment) : [...current, environment];
|
|
5920
|
+
await request({ kind: "environments", grants: next });
|
|
5921
|
+
await refresh();
|
|
5922
|
+
}));
|
|
5923
|
+
}
|
|
5924
|
+
environmentsroot.append(grantbox);
|
|
5925
|
+
const grantnote = document.createElement("p");
|
|
5926
|
+
grantnote.className = "muted";
|
|
5927
|
+
grantnote.textContent = "Environment grants join the origin grants of the session record: a configured list narrows every step to its entries and the request links to the same consent flow the origins use.";
|
|
5928
|
+
environmentsroot.append(grantnote);
|
|
5929
|
+
const capabilitybox = document.createElement("div");
|
|
5930
|
+
capabilitybox.className = "actions";
|
|
5931
|
+
const capabilityinfo = document.createElement("span");
|
|
5932
|
+
capabilityinfo.textContent = `Offscreen capability: ${view.offscreengranted ? "granted" : "absent \u2014 heavy parses keep their inline fallback inside the page"}.`;
|
|
5933
|
+
capabilitybox.append(capabilityinfo);
|
|
5934
|
+
if (!view.offscreengranted) capabilitybox.append(" ", button("Request offscreen capability", async () => {
|
|
5935
|
+
const result = await request({ kind: "environments", requestcapability: true });
|
|
5936
|
+
status(result.requested === true ? "The offscreen capability was granted; the worker pool may spawn." : "The capability request stayed refused; the inline fallback holds.");
|
|
5937
|
+
await refresh();
|
|
5938
|
+
}));
|
|
5939
|
+
else capabilitybox.append(" ", button("Close offscreen document", async () => {
|
|
5940
|
+
await request({ kind: "environments", offscreenclose: true });
|
|
5941
|
+
await refresh();
|
|
5942
|
+
}));
|
|
5943
|
+
environmentsroot.append(capabilitybox);
|
|
5944
|
+
const optionsbox = document.createElement("details");
|
|
5945
|
+
optionsbox.className = "sessiongroup";
|
|
5946
|
+
const optionssummary = document.createElement("summary");
|
|
5947
|
+
optionssummary.textContent = "Environment options";
|
|
5948
|
+
optionsbox.append(optionssummary);
|
|
5949
|
+
const offloadrow = document.createElement("div");
|
|
5950
|
+
offloadrow.className = "actions";
|
|
5951
|
+
offloadrow.append(button(`Parse offload: ${view.parseoffload ? "on" : "off"}`, async () => {
|
|
5952
|
+
await request({ kind: "environments", settings: { parseoffload: !view.parseoffload } });
|
|
5953
|
+
await refresh();
|
|
5954
|
+
}));
|
|
5955
|
+
const poolinput = document.createElement("input");
|
|
5956
|
+
poolinput.type = "number";
|
|
5957
|
+
poolinput.min = "1";
|
|
5958
|
+
poolinput.placeholder = "worker pool size (no cap)";
|
|
5959
|
+
poolinput.value = view.workerpoolsize !== void 0 ? String(view.workerpoolsize) : "";
|
|
5960
|
+
offloadrow.append(" ", poolinput, " ", button("Set pool size", async () => {
|
|
5961
|
+
const value = poolinput.value.trim();
|
|
5962
|
+
await request({ kind: "environments", settings: { ...value !== "" ? { workerpoolsize: Number(value) } : {} } });
|
|
5963
|
+
await refresh();
|
|
5964
|
+
}));
|
|
5965
|
+
optionsbox.append(offloadrow);
|
|
5966
|
+
const origininput = document.createElement("input");
|
|
5967
|
+
origininput.type = "url";
|
|
5968
|
+
origininput.placeholder = "https://origin.example to allow sandbox renders";
|
|
5969
|
+
const originrow = document.createElement("div");
|
|
5970
|
+
originrow.className = "actions";
|
|
5971
|
+
originrow.append(origininput, " ", button("Add sandbox origin", async () => {
|
|
5972
|
+
const value = origininput.value.trim();
|
|
5973
|
+
if (value === "") return;
|
|
5974
|
+
const next = [...view.sandboxorigins ?? [], value];
|
|
5975
|
+
await request({ kind: "environments", settings: { sandboxorigins: next } });
|
|
5976
|
+
await refresh();
|
|
5977
|
+
}));
|
|
5978
|
+
optionsbox.append(originrow);
|
|
5979
|
+
if ((view.sandboxorigins ?? []).length > 0) {
|
|
5980
|
+
const originlist = document.createElement("ul");
|
|
5981
|
+
originlist.className = "audit";
|
|
5982
|
+
for (const origin of view.sandboxorigins ?? []) {
|
|
5983
|
+
const item = document.createElement("li");
|
|
5984
|
+
item.textContent = origin;
|
|
5985
|
+
item.append(" ", button("Remove", async () => {
|
|
5986
|
+
await request({ kind: "environments", settings: { sandboxorigins: (view.sandboxorigins ?? []).filter((entry) => entry !== origin) } });
|
|
5987
|
+
await refresh();
|
|
5988
|
+
}));
|
|
5989
|
+
originlist.append(item);
|
|
5990
|
+
}
|
|
5991
|
+
optionsbox.append(originlist);
|
|
5992
|
+
}
|
|
5993
|
+
const intervalinput = document.createElement("input");
|
|
5994
|
+
intervalinput.type = "number";
|
|
5995
|
+
intervalinput.min = "1";
|
|
5996
|
+
intervalinput.placeholder = "keepalive interval ms (roadmap: 30000)";
|
|
5997
|
+
const intervalrow = document.createElement("div");
|
|
5998
|
+
intervalrow.className = "actions";
|
|
5999
|
+
intervalrow.append(intervalinput, " ", button("Set interval", async () => {
|
|
6000
|
+
const value = intervalinput.value.trim();
|
|
6001
|
+
if (value === "") return;
|
|
6002
|
+
await request({ kind: "environments", settings: { keepaliveinterval: Number(value) } });
|
|
6003
|
+
await refresh();
|
|
6004
|
+
}));
|
|
6005
|
+
optionsbox.append(intervalrow);
|
|
6006
|
+
environmentsroot.append(optionsbox);
|
|
6007
|
+
if (view.registry.length > 0) {
|
|
6008
|
+
const registrybox = document.createElement("details");
|
|
6009
|
+
registrybox.className = "sessiongroup";
|
|
6010
|
+
const registrysummary = document.createElement("summary");
|
|
6011
|
+
registrysummary.textContent = "Executor registry";
|
|
6012
|
+
registrybox.append(registrysummary);
|
|
6013
|
+
const registrylist = document.createElement("ul");
|
|
6014
|
+
registrylist.className = "audit";
|
|
6015
|
+
for (const adapter of view.registry) {
|
|
6016
|
+
const item = document.createElement("li");
|
|
6017
|
+
item.textContent = `${adapter.environment} \u2192 ${adapter.adapter}: ${adapter.description}`;
|
|
6018
|
+
registrylist.append(item);
|
|
6019
|
+
}
|
|
6020
|
+
registrybox.append(registrylist);
|
|
6021
|
+
environmentsroot.append(registrybox);
|
|
6022
|
+
}
|
|
6023
|
+
if (view.report.offscreen.length > 0) {
|
|
6024
|
+
const offbox = document.createElement("details");
|
|
6025
|
+
offbox.className = "sessiongroup";
|
|
6026
|
+
const offsummary = document.createElement("summary");
|
|
6027
|
+
offsummary.textContent = `Offscreen documents (${view.report.offscreen.length})`;
|
|
6028
|
+
offbox.append(offsummary);
|
|
6029
|
+
const offlist = document.createElement("ul");
|
|
6030
|
+
offlist.className = "audit";
|
|
6031
|
+
for (const entry of view.report.offscreen) {
|
|
6032
|
+
const item = document.createElement("li");
|
|
6033
|
+
item.textContent = `${entry.document} for the run ${entry.runid}: ${entry.reasons.join(", ")}${entry.closedat !== void 0 ? ` \xB7 closed ${new Date(entry.closedat).toLocaleTimeString()}` : " \xB7 open"} \u2014 ${entry.justification}`;
|
|
6034
|
+
offlist.append(item);
|
|
6035
|
+
}
|
|
6036
|
+
offbox.append(offlist);
|
|
6037
|
+
environmentsroot.append(offbox);
|
|
6038
|
+
}
|
|
6039
|
+
if (view.workerevents.length > 0) {
|
|
6040
|
+
const workerbox = document.createElement("details");
|
|
6041
|
+
workerbox.className = "sessiongroup";
|
|
6042
|
+
const workersummary = document.createElement("summary");
|
|
6043
|
+
workersummary.textContent = `Worker activity (${view.workerevents.length})`;
|
|
6044
|
+
workerbox.append(workersummary);
|
|
6045
|
+
const workerlist = document.createElement("ul");
|
|
6046
|
+
workerlist.className = "audit";
|
|
6047
|
+
for (const event of view.workerevents.slice(0, 10)) {
|
|
6048
|
+
const item = document.createElement("li");
|
|
6049
|
+
item.textContent = `${event.kind} ${event.workers} worker${event.workers === 1 ? "" : "s"}: ${event.reason}`;
|
|
6050
|
+
workerlist.append(item);
|
|
6051
|
+
}
|
|
6052
|
+
workerbox.append(workerlist);
|
|
6053
|
+
environmentsroot.append(workerbox);
|
|
6054
|
+
}
|
|
6055
|
+
if (view.urls.length > 0) {
|
|
6056
|
+
const urlbox = document.createElement("details");
|
|
6057
|
+
urlbox.className = "sessiongroup";
|
|
6058
|
+
const urlsummary = document.createElement("summary");
|
|
6059
|
+
urlsummary.textContent = `Url history of the open run (${view.urls.length})`;
|
|
6060
|
+
urlbox.append(urlsummary);
|
|
6061
|
+
const urllist = document.createElement("ol");
|
|
6062
|
+
urllist.className = "audit";
|
|
6063
|
+
for (const entry of view.urls) {
|
|
6064
|
+
const item = document.createElement("li");
|
|
6065
|
+
item.textContent = `${new Date(entry.at).toLocaleTimeString()} ${entry.url} (step ${entry.stepid})`;
|
|
6066
|
+
urllist.append(item);
|
|
6067
|
+
}
|
|
6068
|
+
urlbox.append(urllist);
|
|
6069
|
+
environmentsroot.append(urlbox);
|
|
6070
|
+
}
|
|
6071
|
+
const locksbox = document.createElement("details");
|
|
6072
|
+
locksbox.className = "sessiongroup";
|
|
6073
|
+
const lockssummary = document.createElement("summary");
|
|
6074
|
+
lockssummary.textContent = `Run locks (${view.locks.length})`;
|
|
6075
|
+
locksbox.append(lockssummary);
|
|
6076
|
+
if (view.locks.length === 0) {
|
|
6077
|
+
const none = document.createElement("p");
|
|
6078
|
+
none.textContent = "No session holds a run lock; one session never carries two concurrent runs.";
|
|
6079
|
+
locksbox.append(none);
|
|
6080
|
+
}
|
|
6081
|
+
const locklist = document.createElement("ul");
|
|
6082
|
+
locklist.className = "audit";
|
|
6083
|
+
for (const lock of view.locks) {
|
|
6084
|
+
const item = document.createElement("li");
|
|
6085
|
+
item.textContent = `Session ${lock.sessionid} \u2192 run ${lock.runid} (${lock.holder})${lock.expiresat !== void 0 ? ` \xB7 expires ${new Date(lock.expiresat).toLocaleTimeString()}` : " \xB7 no expiry"}`;
|
|
6086
|
+
locklist.append(item);
|
|
6087
|
+
}
|
|
6088
|
+
locksbox.append(locklist);
|
|
6089
|
+
environmentsroot.append(locksbox);
|
|
6090
|
+
}
|
|
5546
6091
|
function renderagents(context) {
|
|
5547
6092
|
if (!agentsroot) return;
|
|
5548
6093
|
agentsroot.replaceChildren();
|
|
@@ -6430,4 +6975,31 @@ function renderagents(context) {
|
|
|
6430
6975
|
agentsroot.append(eventbox);
|
|
6431
6976
|
}
|
|
6432
6977
|
}
|
|
6978
|
+
{
|
|
6979
|
+
let sandboxframe;
|
|
6980
|
+
chrome.runtime.onMessage.addListener((message, _sender, sendresponse) => {
|
|
6981
|
+
if (!message || message.kind !== "environments" || message.action !== "sandboxhost" || !message.render?.nonce) return false;
|
|
6982
|
+
try {
|
|
6983
|
+
if (!sandboxframe) {
|
|
6984
|
+
sandboxframe = document.createElement("iframe");
|
|
6985
|
+
sandboxframe.src = chrome.runtime.getURL("sandbox.html");
|
|
6986
|
+
sandboxframe.style.display = "none";
|
|
6987
|
+
sandboxframe.setAttribute("aria-hidden", "true");
|
|
6988
|
+
document.body.append(sandboxframe);
|
|
6989
|
+
}
|
|
6990
|
+
sandboxframe.contentWindow?.postMessage({ channel: "devthinksandbox", type: "render", nonce: message.render.nonce, markup: message.render.markup ?? "" }, "*");
|
|
6991
|
+
const waitfor = (event) => {
|
|
6992
|
+
const data = event.data;
|
|
6993
|
+
if (data?.channel !== "devthinksandbox" || data.type !== "renderresult" || data.nonce !== message.render?.nonce) return;
|
|
6994
|
+
window.removeEventListener("message", waitfor);
|
|
6995
|
+
sendresponse({ ok: data.ok !== false, text: data.text ?? "", summary: data.summary ?? "The sandbox frame returned its render result." });
|
|
6996
|
+
};
|
|
6997
|
+
window.addEventListener("message", waitfor);
|
|
6998
|
+
return true;
|
|
6999
|
+
} catch {
|
|
7000
|
+
sendresponse({ ok: false, text: "", summary: "The sandbox host could not open the sandboxed page." });
|
|
7001
|
+
return true;
|
|
7002
|
+
}
|
|
7003
|
+
});
|
|
7004
|
+
}
|
|
6433
7005
|
//# sourceMappingURL=sidepanel.js.map
|