@jevitate/cli 0.5.0 → 0.6.0
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/dist/author-journey-cli.d.ts +2 -0
- package/dist/author-journey-cli.js +93 -6
- package/dist/author-journey-cli.js.map +1 -1
- package/dist/bin.js +1759 -664
- package/dist/build-info.generated.js +2 -2
- package/dist/demo-aspect-api.js +2 -0
- package/dist/demo-aspect-api.js.map +1 -1
- package/dist/explore-author.d.ts +21 -3
- package/dist/explore-author.js +64 -32
- package/dist/explore-author.js.map +1 -1
- package/dist/explore-cli.js +55 -88
- package/dist/explore-cli.js.map +1 -1
- package/dist/goal-run-flags.d.ts +120 -0
- package/dist/goal-run-flags.js +220 -0
- package/dist/goal-run-flags.js.map +1 -0
- package/dist/mcp-cli-tools.js +36 -2
- package/dist/mcp-cli-tools.js.map +1 -1
- package/dist/signal-triage.js +5 -1
- package/dist/signal-triage.js.map +1 -1
- package/dist/usability-capture.js +5 -1
- package/dist/usability-capture.js.map +1 -1
- package/package.json +9 -9
- package/skills/jevitate-explore/SKILL.md +7 -0
package/dist/bin.js
CHANGED
|
@@ -4003,7 +4003,7 @@ function handleIntersectionResults(result, left, right) {
|
|
|
4003
4003
|
const unrecKeys = /* @__PURE__ */ new Map();
|
|
4004
4004
|
let unrecIssue;
|
|
4005
4005
|
const keyIssues = /* @__PURE__ */ new Map();
|
|
4006
|
-
const
|
|
4006
|
+
const collect7 = (iss, side2) => {
|
|
4007
4007
|
let keys;
|
|
4008
4008
|
if (iss.code === "unrecognized_keys" && !iss.path?.length) {
|
|
4009
4009
|
unrecIssue ?? (unrecIssue = iss);
|
|
@@ -4024,11 +4024,11 @@ function handleIntersectionResults(result, left, right) {
|
|
|
4024
4024
|
return true;
|
|
4025
4025
|
};
|
|
4026
4026
|
for (const iss of left.issues) {
|
|
4027
|
-
if (!
|
|
4027
|
+
if (!collect7(iss, "l"))
|
|
4028
4028
|
result.issues.push(iss);
|
|
4029
4029
|
}
|
|
4030
4030
|
for (const iss of right.issues) {
|
|
4031
|
-
if (!
|
|
4031
|
+
if (!collect7(iss, "r"))
|
|
4032
4032
|
result.issues.push(iss);
|
|
4033
4033
|
}
|
|
4034
4034
|
const bothKeys = [...unrecKeys].filter(([, f]) => f.l && f.r).map(([k]) => k);
|
|
@@ -26829,6 +26829,43 @@ var clock = {
|
|
|
26829
26829
|
sleep: (ms) => new Promise((resolve14) => current.setTimeout(() => resolve14(), ms))
|
|
26830
26830
|
};
|
|
26831
26831
|
|
|
26832
|
+
// ../domain/dist/external-scheme.js
|
|
26833
|
+
var IN_BROWSER_SCHEMES = /* @__PURE__ */ new Set([
|
|
26834
|
+
"http",
|
|
26835
|
+
"https",
|
|
26836
|
+
"ws",
|
|
26837
|
+
"wss",
|
|
26838
|
+
"data",
|
|
26839
|
+
"blob",
|
|
26840
|
+
"about",
|
|
26841
|
+
"file",
|
|
26842
|
+
"javascript",
|
|
26843
|
+
"filesystem",
|
|
26844
|
+
"chrome",
|
|
26845
|
+
"chrome-extension",
|
|
26846
|
+
"chrome-error",
|
|
26847
|
+
"chrome-untrusted",
|
|
26848
|
+
"devtools",
|
|
26849
|
+
"edge",
|
|
26850
|
+
"view-source",
|
|
26851
|
+
"moz-extension",
|
|
26852
|
+
"resource"
|
|
26853
|
+
]);
|
|
26854
|
+
var SCHEME = /^([a-z][a-z0-9+.-]*):/i;
|
|
26855
|
+
function urlSchemeOf(url2) {
|
|
26856
|
+
const m = SCHEME.exec(url2.trim());
|
|
26857
|
+
return m?.[1]?.toLowerCase();
|
|
26858
|
+
}
|
|
26859
|
+
function externalSchemeOf(url2) {
|
|
26860
|
+
const scheme = urlSchemeOf(url2);
|
|
26861
|
+
if (scheme === void 0 || IN_BROWSER_SCHEMES.has(scheme))
|
|
26862
|
+
return void 0;
|
|
26863
|
+
return scheme;
|
|
26864
|
+
}
|
|
26865
|
+
function isExternalSchemeUrl(url2) {
|
|
26866
|
+
return externalSchemeOf(url2) !== void 0;
|
|
26867
|
+
}
|
|
26868
|
+
|
|
26832
26869
|
// ../daemon/dist/profile-manager.js
|
|
26833
26870
|
var ProfileManager = class {
|
|
26834
26871
|
rootDir;
|
|
@@ -34378,10 +34415,8 @@ async function evaluateAssertionOnce(actor, a) {
|
|
|
34378
34415
|
const page = actor.ability(BrowseTheWebToken).session.page;
|
|
34379
34416
|
return page.url().includes(a.text);
|
|
34380
34417
|
}
|
|
34381
|
-
case "textIncludes":
|
|
34382
|
-
|
|
34383
|
-
return text2 !== null && textIncludesCI(text2, a.text);
|
|
34384
|
-
}
|
|
34418
|
+
case "textIncludes":
|
|
34419
|
+
return textIncludesOnAnyMatch(actor, a.target, a.text);
|
|
34385
34420
|
case "count": {
|
|
34386
34421
|
const n3 = await actor.asks(CountOf.target(descriptorToTarget(a.target)));
|
|
34387
34422
|
return (a.min === void 0 || n3 >= a.min) && (a.max === void 0 || n3 <= a.max);
|
|
@@ -34390,6 +34425,29 @@ async function evaluateAssertionOnce(actor, a) {
|
|
|
34390
34425
|
return await actor.asks(ValueOf.target(descriptorToTarget(a.target))) === a.value;
|
|
34391
34426
|
}
|
|
34392
34427
|
}
|
|
34428
|
+
var MAX_TEXT_MATCHES = 50;
|
|
34429
|
+
async function textIncludesOnAnyMatch(actor, target, text2) {
|
|
34430
|
+
const t = descriptorToTarget(target);
|
|
34431
|
+
const locator = t.resolve(actor.ability(BrowseTheWebToken).session.page);
|
|
34432
|
+
const n3 = await locator.count();
|
|
34433
|
+
if (n3 === 0)
|
|
34434
|
+
return false;
|
|
34435
|
+
if (n3 === 1) {
|
|
34436
|
+
if (!await locator.isVisible().catch(() => false))
|
|
34437
|
+
return false;
|
|
34438
|
+
const read = await actor.asks(TextOf.target(t));
|
|
34439
|
+
return read !== null && textIncludesCI(read, text2);
|
|
34440
|
+
}
|
|
34441
|
+
for (let i = 0; i < Math.min(n3, MAX_TEXT_MATCHES); i++) {
|
|
34442
|
+
const el = locator.nth(i);
|
|
34443
|
+
if (!await el.isVisible().catch(() => false))
|
|
34444
|
+
continue;
|
|
34445
|
+
const read = await el.innerText({ timeout: 1e3 }).catch(() => null);
|
|
34446
|
+
if (read !== null && textIncludesCI(read, text2))
|
|
34447
|
+
return true;
|
|
34448
|
+
}
|
|
34449
|
+
return false;
|
|
34450
|
+
}
|
|
34393
34451
|
async function readAssertionText(actor, a) {
|
|
34394
34452
|
if (a.kind === "textIncludes")
|
|
34395
34453
|
return actor.asks(TextOf.target(descriptorToTarget(a.target))).catch(() => null);
|
|
@@ -35895,6 +35953,68 @@ function srOnlyLabelOf(node2) {
|
|
|
35895
35953
|
return { none: true };
|
|
35896
35954
|
}
|
|
35897
35955
|
|
|
35956
|
+
// ../explore/dist/composer-evidence.js
|
|
35957
|
+
var MESSAGE_FIELD = /\b(reply|message|ask|chat|inquiry|prompt|say|talk|conversation)\b/i;
|
|
35958
|
+
var MESSAGE_INPUT_TYPES = /* @__PURE__ */ new Set(["", "text", "search"]);
|
|
35959
|
+
var SUBMIT_NAME = /\b(send|submit|reply|ask|post)\b|[→➤➔↑]|^\s*(go|ok)\s*$/i;
|
|
35960
|
+
var PAIRED_SUBMIT_DISTANCE = 6;
|
|
35961
|
+
function composerEvidence(node2, args) {
|
|
35962
|
+
const el = node2;
|
|
35963
|
+
const norm5 = (s3) => s3 === null ? "" : s3.replace(/\s+/g, " ").trim();
|
|
35964
|
+
const parentOf = (a) => a.parentElement ?? (a.parentNode instanceof ShadowRoot ? a.parentNode.host : null);
|
|
35965
|
+
const rendered = (a) => a.getClientRects().length > 0 || a.offsetParent !== null;
|
|
35966
|
+
const roots = [document];
|
|
35967
|
+
const own2 = el.getRootNode();
|
|
35968
|
+
if (own2 instanceof ShadowRoot)
|
|
35969
|
+
roots.push(own2);
|
|
35970
|
+
const all = (sel) => roots.flatMap((r) => Array.from(r.querySelectorAll(sel)));
|
|
35971
|
+
if (all('[role="log"]').some(rendered))
|
|
35972
|
+
return true;
|
|
35973
|
+
for (const live of all('[aria-live="polite"], [aria-live="assertive"]')) {
|
|
35974
|
+
const role = norm5(live.getAttribute("role")).toLowerCase();
|
|
35975
|
+
if (role === "status" || role === "alert" || role === "timer" || role === "progressbar")
|
|
35976
|
+
continue;
|
|
35977
|
+
if (rendered(live) && live.children.length >= 2)
|
|
35978
|
+
return true;
|
|
35979
|
+
}
|
|
35980
|
+
const MESSAGE_ITEM = /(^|[\s_-])(message|msg|bubble|chat-?message|chat-?bubble)s?($|[\s_-])/i;
|
|
35981
|
+
const itemLike = (a) => MESSAGE_ITEM.test(a.getAttribute("class") ?? "") || Array.from(a.attributes).some((at3) => at3.name.startsWith("data-") && MESSAGE_ITEM.test(`${at3.name.slice(5)} ${at3.value}`));
|
|
35982
|
+
const parents = /* @__PURE__ */ new Map();
|
|
35983
|
+
for (const item of all("[class], [data-role], [data-type], [data-message-id], [data-message-author-role]")) {
|
|
35984
|
+
if (item === el || item.contains(el) || !itemLike(item) || item.parentElement === null || !rendered(item))
|
|
35985
|
+
continue;
|
|
35986
|
+
const n3 = (parents.get(item.parentElement) ?? 0) + 1;
|
|
35987
|
+
if (n3 >= 2)
|
|
35988
|
+
return true;
|
|
35989
|
+
parents.set(item.parentElement, n3);
|
|
35990
|
+
}
|
|
35991
|
+
const submit2 = new RegExp(args.submit, "i");
|
|
35992
|
+
const up4 = (a) => {
|
|
35993
|
+
const out = [];
|
|
35994
|
+
for (let cur = a; cur !== null; cur = parentOf(cur))
|
|
35995
|
+
out.push(cur);
|
|
35996
|
+
return out;
|
|
35997
|
+
};
|
|
35998
|
+
const fieldPath = up4(el);
|
|
35999
|
+
for (const b2 of all('button, [role="button"], input[type="submit"], input[type="button"]')) {
|
|
36000
|
+
if (b2 === el || !rendered(b2))
|
|
36001
|
+
continue;
|
|
36002
|
+
const name = norm5(b2.getAttribute("aria-label")) || norm5(b2.textContent) || norm5(b2.value ?? null) || norm5(b2.getAttribute("title"));
|
|
36003
|
+
if (!submit2.test(name))
|
|
36004
|
+
continue;
|
|
36005
|
+
const bp = up4(b2);
|
|
36006
|
+
for (let i = 0; i < fieldPath.length && i <= args.distance; i++) {
|
|
36007
|
+
const j = bp.indexOf(fieldPath[i]);
|
|
36008
|
+
if (j >= 0) {
|
|
36009
|
+
if (i + j <= args.distance)
|
|
36010
|
+
return true;
|
|
36011
|
+
break;
|
|
36012
|
+
}
|
|
36013
|
+
}
|
|
36014
|
+
}
|
|
36015
|
+
return false;
|
|
36016
|
+
}
|
|
36017
|
+
|
|
35898
36018
|
// ../explore/dist/temporal-value.js
|
|
35899
36019
|
var TEMPORAL_FORMATS = {
|
|
35900
36020
|
time: "HH:MM (24-hour, e.g. 08:00 or 17:30)",
|
|
@@ -36034,6 +36154,24 @@ function inWireFormat(field3, value) {
|
|
|
36034
36154
|
}
|
|
36035
36155
|
|
|
36036
36156
|
// ../explore/dist/snapshot.js
|
|
36157
|
+
var NAVIGATION_RETRIES = 3;
|
|
36158
|
+
var NAVIGATION_WAIT_MS = 15e3;
|
|
36159
|
+
var PageNavigatingError = class extends Error {
|
|
36160
|
+
url;
|
|
36161
|
+
attempts;
|
|
36162
|
+
name = "PageNavigatingError";
|
|
36163
|
+
constructor(url2, attempts, cause) {
|
|
36164
|
+
super(`the page kept navigating: its controls could not be read in ${attempts} attempts (last at ${redactUrl(url2)})`, { cause });
|
|
36165
|
+
this.url = url2;
|
|
36166
|
+
this.attempts = attempts;
|
|
36167
|
+
}
|
|
36168
|
+
};
|
|
36169
|
+
function isNavigationInterruption(e) {
|
|
36170
|
+
const message2 = e instanceof Error ? e.message : String(e);
|
|
36171
|
+
if (/Target page, context or browser has been closed|Target closed/i.test(message2))
|
|
36172
|
+
return false;
|
|
36173
|
+
return /Execution context was destroyed|Cannot find context with specified id/.test(message2);
|
|
36174
|
+
}
|
|
36037
36175
|
var SNAPSHOT_BUDGET_MS = 3e4;
|
|
36038
36176
|
var LIST_OPTION_CAP = 25;
|
|
36039
36177
|
var LIST_OPTION_ROLES = /* @__PURE__ */ new Set(["option", "menuitem", "menuitemradio", "menuitemcheckbox", "treeitem"]);
|
|
@@ -36360,6 +36498,25 @@ function readCheapNames(els) {
|
|
|
36360
36498
|
});
|
|
36361
36499
|
}
|
|
36362
36500
|
async function snapshot(page, opts) {
|
|
36501
|
+
const retries = opts?.navigationRetries ?? NAVIGATION_RETRIES;
|
|
36502
|
+
const deadline = clock.now() + (opts?.navigationWaitMs ?? NAVIGATION_WAIT_MS);
|
|
36503
|
+
const awaitNavigation = opts?.awaitNavigation ?? (async (remainingMs) => {
|
|
36504
|
+
await page.waitForLoadState("load", { timeout: Math.max(1, remainingMs) }).catch(() => void 0);
|
|
36505
|
+
});
|
|
36506
|
+
for (let attempt2 = 1; ; attempt2 += 1) {
|
|
36507
|
+
try {
|
|
36508
|
+
return await readSnapshot(page, opts);
|
|
36509
|
+
} catch (e) {
|
|
36510
|
+
if (!isNavigationInterruption(e))
|
|
36511
|
+
throw e;
|
|
36512
|
+
const remaining = deadline - clock.now();
|
|
36513
|
+
if (attempt2 > retries || remaining <= 0 || page.isClosed())
|
|
36514
|
+
throw new PageNavigatingError(page.url(), attempt2, e);
|
|
36515
|
+
await awaitNavigation(remaining);
|
|
36516
|
+
}
|
|
36517
|
+
}
|
|
36518
|
+
}
|
|
36519
|
+
async function readSnapshot(page, opts) {
|
|
36363
36520
|
const maxCandidates = opts?.maxCandidates ?? DEFAULT_BOUNDS.maxCandidates;
|
|
36364
36521
|
const url2 = page.url();
|
|
36365
36522
|
const viewport = page.viewportSize();
|
|
@@ -36420,6 +36577,8 @@ async function snapshot(page, opts) {
|
|
|
36420
36577
|
continue;
|
|
36421
36578
|
const value = raw.valueBearing && !isSecretField(raw.inputType, raw.autocomplete) ? await handle.evaluate(readControlValue) : null;
|
|
36422
36579
|
const facts = { ...raw, value };
|
|
36580
|
+
const textEntry = raw.tag === "textarea" || raw.tag === "input" && MESSAGE_INPUT_TYPES.has(raw.inputType ?? "") || raw.role === "textbox";
|
|
36581
|
+
const conversational = textEntry && MESSAGE_FIELD.test(raw.name) ? await handle.evaluate(composerEvidence, { submit: SUBMIT_NAME.source, distance: PAIRED_SUBMIT_DISTANCE }).catch(() => false) : null;
|
|
36423
36582
|
const computed = await computeDescriptor(page, handle, { primaryOnly: true });
|
|
36424
36583
|
const control = {
|
|
36425
36584
|
index: controls.length,
|
|
@@ -36449,11 +36608,14 @@ async function snapshot(page, opts) {
|
|
|
36449
36608
|
step: facts.step,
|
|
36450
36609
|
landmark: facts.landmark,
|
|
36451
36610
|
...facts.richText ? { richText: true } : {},
|
|
36611
|
+
...conversational === null ? {} : { conversational },
|
|
36452
36612
|
...facts.clippedOffscreen ? { clippedOffscreen: true } : {}
|
|
36453
36613
|
};
|
|
36454
36614
|
controls.push(redactControl(control, opts?.secrets ?? []));
|
|
36455
36615
|
keptFacts.push(facts);
|
|
36456
|
-
} catch {
|
|
36616
|
+
} catch (e) {
|
|
36617
|
+
if (isNavigationInterruption(e))
|
|
36618
|
+
throw e;
|
|
36457
36619
|
} finally {
|
|
36458
36620
|
await handle.dispose().catch(() => void 0);
|
|
36459
36621
|
}
|
|
@@ -36560,15 +36722,179 @@ var FirstPartyOrigins = class {
|
|
|
36560
36722
|
}
|
|
36561
36723
|
};
|
|
36562
36724
|
|
|
36725
|
+
// ../explore/dist/rpc-status.js
|
|
36726
|
+
var RPC_CONTENT = /^\s*application\/(?:grpc-web(?:-text)?(?:\+[\w.-]+)?|connect\+[\w.-]+)\s*(?:;|$)/i;
|
|
36727
|
+
var GRPC_CODE_NAMES = [
|
|
36728
|
+
"ok",
|
|
36729
|
+
"canceled",
|
|
36730
|
+
"unknown",
|
|
36731
|
+
"invalid_argument",
|
|
36732
|
+
"deadline_exceeded",
|
|
36733
|
+
"not_found",
|
|
36734
|
+
"already_exists",
|
|
36735
|
+
"permission_denied",
|
|
36736
|
+
"resource_exhausted",
|
|
36737
|
+
"failed_precondition",
|
|
36738
|
+
"aborted",
|
|
36739
|
+
"out_of_range",
|
|
36740
|
+
"unimplemented",
|
|
36741
|
+
"internal",
|
|
36742
|
+
"unavailable",
|
|
36743
|
+
"data_loss",
|
|
36744
|
+
"unauthenticated"
|
|
36745
|
+
];
|
|
36746
|
+
var HTTP_EQUIVALENT = [200, 499, 500, 400, 504, 404, 409, 403, 429, 400, 409, 400, 501, 500, 503, 500, 401];
|
|
36747
|
+
var MAX_RPC_BODY_BYTES = 8 * 1024 * 1024;
|
|
36748
|
+
function statusOf2(protocol, code) {
|
|
36749
|
+
if (!Number.isInteger(code) || code < 0)
|
|
36750
|
+
return null;
|
|
36751
|
+
return { protocol, code, name: GRPC_CODE_NAMES[code] ?? `code_${code}` };
|
|
36752
|
+
}
|
|
36753
|
+
function rpcHttpEquivalent(code) {
|
|
36754
|
+
return HTTP_EQUIVALENT[code] ?? 500;
|
|
36755
|
+
}
|
|
36756
|
+
function effectiveStatus(r) {
|
|
36757
|
+
if (r.status === null)
|
|
36758
|
+
return null;
|
|
36759
|
+
if (r.rpcStatus === void 0 || r.rpcStatus.code === 0)
|
|
36760
|
+
return r.status;
|
|
36761
|
+
return r.status >= 200 && r.status < 300 ? rpcHttpEquivalent(r.rpcStatus.code) : r.status;
|
|
36762
|
+
}
|
|
36763
|
+
function describeStatusWithRpc(r) {
|
|
36764
|
+
if (r.status === null)
|
|
36765
|
+
return "no response";
|
|
36766
|
+
if (r.rpcStatus === void 0 || r.rpcStatus.code === 0)
|
|
36767
|
+
return String(r.status);
|
|
36768
|
+
return `${r.status} (grpc-status ${r.rpcStatus.code} ${r.rpcStatus.name})`;
|
|
36769
|
+
}
|
|
36770
|
+
function protocolOf(contentType) {
|
|
36771
|
+
if (!RPC_CONTENT.test(contentType))
|
|
36772
|
+
return null;
|
|
36773
|
+
return /connect\+/i.test(contentType) ? "connect" : "grpc-web";
|
|
36774
|
+
}
|
|
36775
|
+
function rpcStatusFromHeaders(headers, contentType = headers["content-type"] ?? "") {
|
|
36776
|
+
const raw = headers["grpc-status"];
|
|
36777
|
+
if (raw === void 0 || !/^\s*\d+\s*$/.test(raw))
|
|
36778
|
+
return null;
|
|
36779
|
+
return statusOf2(/connect\+/i.test(contentType) ? "connect" : "grpc-web", Number(raw.trim()));
|
|
36780
|
+
}
|
|
36781
|
+
function decodeGrpcWebText(body) {
|
|
36782
|
+
const text2 = Buffer.from(body.buffer, body.byteOffset, body.byteLength).toString("latin1").replace(/\s+/g, "");
|
|
36783
|
+
const chunks = text2.match(/[^=]+={0,2}|={1,2}/g) ?? [];
|
|
36784
|
+
return Buffer.concat(chunks.map((c) => Buffer.from(c, "base64")));
|
|
36785
|
+
}
|
|
36786
|
+
function lastFrameWith(bytes, bit) {
|
|
36787
|
+
let found = null;
|
|
36788
|
+
let at3 = 0;
|
|
36789
|
+
while (at3 + 5 <= bytes.length) {
|
|
36790
|
+
const flag = bytes[at3] ?? 0;
|
|
36791
|
+
const len = ((bytes[at3 + 1] ?? 0) << 24 >>> 0) + ((bytes[at3 + 2] ?? 0) << 16) + ((bytes[at3 + 3] ?? 0) << 8) + (bytes[at3 + 4] ?? 0);
|
|
36792
|
+
const end = at3 + 5 + len;
|
|
36793
|
+
if (end > bytes.length)
|
|
36794
|
+
break;
|
|
36795
|
+
if ((flag & bit) !== 0)
|
|
36796
|
+
found = bytes.subarray(at3 + 5, end);
|
|
36797
|
+
at3 = end;
|
|
36798
|
+
}
|
|
36799
|
+
return found;
|
|
36800
|
+
}
|
|
36801
|
+
function parseGrpcWebTrailer(body, contentType) {
|
|
36802
|
+
const bytes = /grpc-web-text/i.test(contentType) ? decodeGrpcWebText(body) : body;
|
|
36803
|
+
const trailer = lastFrameWith(bytes, 128);
|
|
36804
|
+
if (trailer === null)
|
|
36805
|
+
return null;
|
|
36806
|
+
for (const line of Buffer.from(trailer.buffer, trailer.byteOffset, trailer.byteLength).toString("latin1").split(/\r?\n/)) {
|
|
36807
|
+
const m = /^\s*grpc-status\s*:\s*(\d+)\s*$/i.exec(line);
|
|
36808
|
+
if (m !== null)
|
|
36809
|
+
return Number(m[1]);
|
|
36810
|
+
}
|
|
36811
|
+
return null;
|
|
36812
|
+
}
|
|
36813
|
+
function parseConnectUnaryError(body) {
|
|
36814
|
+
try {
|
|
36815
|
+
const parsed = JSON.parse(body);
|
|
36816
|
+
const code = parsed !== null && typeof parsed === "object" && "code" in parsed ? parsed.code : null;
|
|
36817
|
+
return typeof code === "string" && /^[a-z_]+$/.test(code) ? code : null;
|
|
36818
|
+
} catch {
|
|
36819
|
+
return null;
|
|
36820
|
+
}
|
|
36821
|
+
}
|
|
36822
|
+
function parseConnectEndStream(body) {
|
|
36823
|
+
const end = lastFrameWith(body, 2);
|
|
36824
|
+
if (end === null)
|
|
36825
|
+
return null;
|
|
36826
|
+
try {
|
|
36827
|
+
const parsed = JSON.parse(Buffer.from(end.buffer, end.byteOffset, end.byteLength).toString("utf8"));
|
|
36828
|
+
if (parsed === null || typeof parsed !== "object")
|
|
36829
|
+
return null;
|
|
36830
|
+
const error62 = parsed.error;
|
|
36831
|
+
if (error62 === void 0 || error62 === null)
|
|
36832
|
+
return 0;
|
|
36833
|
+
const name = typeof error62 === "object" ? error62.code : void 0;
|
|
36834
|
+
if (typeof name !== "string")
|
|
36835
|
+
return 2;
|
|
36836
|
+
const code = GRPC_CODE_NAMES.indexOf(name);
|
|
36837
|
+
return code <= 0 ? 2 : code;
|
|
36838
|
+
} catch {
|
|
36839
|
+
return null;
|
|
36840
|
+
}
|
|
36841
|
+
}
|
|
36842
|
+
function rpcStatusFromBody(body, contentType) {
|
|
36843
|
+
const protocol = protocolOf(contentType);
|
|
36844
|
+
if (protocol === null)
|
|
36845
|
+
return null;
|
|
36846
|
+
const code = protocol === "connect" ? parseConnectEndStream(body) : parseGrpcWebTrailer(body, contentType);
|
|
36847
|
+
return code === null ? null : statusOf2(protocol, code);
|
|
36848
|
+
}
|
|
36849
|
+
var reads = /* @__PURE__ */ new WeakMap();
|
|
36850
|
+
function rpcStatusOfResponse(response) {
|
|
36851
|
+
const known = reads.get(response);
|
|
36852
|
+
if (known !== void 0)
|
|
36853
|
+
return known;
|
|
36854
|
+
const read = (async () => {
|
|
36855
|
+
const headers = response.headers();
|
|
36856
|
+
const contentType = headers["content-type"] ?? "";
|
|
36857
|
+
const fromHeader = rpcStatusFromHeaders(headers, contentType);
|
|
36858
|
+
if (fromHeader !== null)
|
|
36859
|
+
return fromHeader;
|
|
36860
|
+
if (protocolOf(contentType) === null)
|
|
36861
|
+
return null;
|
|
36862
|
+
const declared = Number(headers["content-length"] ?? "0");
|
|
36863
|
+
if (Number.isFinite(declared) && declared > MAX_RPC_BODY_BYTES)
|
|
36864
|
+
return null;
|
|
36865
|
+
const body = await response.body();
|
|
36866
|
+
return body.length > MAX_RPC_BODY_BYTES ? null : rpcStatusFromBody(body, contentType);
|
|
36867
|
+
})().catch(() => null);
|
|
36868
|
+
reads.set(response, read);
|
|
36869
|
+
return read;
|
|
36870
|
+
}
|
|
36871
|
+
|
|
36563
36872
|
// ../explore/dist/http-5xx.js
|
|
36564
|
-
function http5xxSignalOf(response, firstParty, headers, method) {
|
|
36565
|
-
const
|
|
36873
|
+
function http5xxSignalOf(response, firstParty, headers, method, rpc) {
|
|
36874
|
+
const httpStatus = response.status();
|
|
36875
|
+
const status = effectiveStatus({ status: httpStatus, ...rpc === void 0 || rpc === null ? {} : { rpcStatus: rpc } }) ?? httpStatus;
|
|
36566
36876
|
if (status < 500)
|
|
36567
36877
|
return null;
|
|
36568
36878
|
if (firstParty !== void 0 && firstParty.thirdParty(response.url(), headers) !== null)
|
|
36569
36879
|
return null;
|
|
36570
36880
|
const url2 = redactUrl(response.url());
|
|
36571
|
-
|
|
36881
|
+
const rpcNote = status === httpStatus || rpc === void 0 || rpc === null ? "" : ` (HTTP ${httpStatus}, grpc-status ${rpc.code} ${rpc.name})`;
|
|
36882
|
+
return { kind: "http-5xx", detail: `${status} ${url2}${rpcNote}`, url: url2, status, ...method === void 0 ? {} : { method: method.toUpperCase() } };
|
|
36883
|
+
}
|
|
36884
|
+
async function rpc5xxSignalOf(response, firstParty, headers, method) {
|
|
36885
|
+
if (response.status() >= 500)
|
|
36886
|
+
return null;
|
|
36887
|
+
let rpcCandidate = false;
|
|
36888
|
+
try {
|
|
36889
|
+
const h = response.headers();
|
|
36890
|
+
rpcCandidate = RPC_CONTENT.test(h["content-type"] ?? "") || h["grpc-status"] !== void 0;
|
|
36891
|
+
} catch {
|
|
36892
|
+
return null;
|
|
36893
|
+
}
|
|
36894
|
+
if (!rpcCandidate)
|
|
36895
|
+
return null;
|
|
36896
|
+
const rpc = await rpcStatusOfResponse(response);
|
|
36897
|
+
return rpc === null || rpc.code === 0 ? null : http5xxSignalOf(response, firstParty, headers, method, rpc);
|
|
36572
36898
|
}
|
|
36573
36899
|
function requestHeadersOf(r) {
|
|
36574
36900
|
try {
|
|
@@ -36594,15 +36920,24 @@ var Http5xxOracle = class {
|
|
|
36594
36920
|
});
|
|
36595
36921
|
page.on("response", (response) => {
|
|
36596
36922
|
const request = response.request();
|
|
36923
|
+
const isDocument = request.isNavigationRequest() && request.frame() === page.mainFrame();
|
|
36924
|
+
const pageUrl = redactUrl(page.url());
|
|
36925
|
+
const record2 = (signal2) => {
|
|
36926
|
+
this.#observed.push({
|
|
36927
|
+
signal: signal2,
|
|
36928
|
+
method: request.method().toUpperCase(),
|
|
36929
|
+
pageUrl: isDocument ? signal2.url : pageUrl,
|
|
36930
|
+
startedAt: this.#started.get(request) ?? this.#now()
|
|
36931
|
+
});
|
|
36932
|
+
};
|
|
36597
36933
|
const signal = http5xxSignalOf(response, this.#firstParty, requestHeadersOf(request));
|
|
36598
|
-
if (signal
|
|
36934
|
+
if (signal !== null) {
|
|
36935
|
+
record2(signal);
|
|
36599
36936
|
return;
|
|
36600
|
-
|
|
36601
|
-
this.#
|
|
36602
|
-
|
|
36603
|
-
|
|
36604
|
-
pageUrl: isDocument ? signal.url : redactUrl(page.url()),
|
|
36605
|
-
startedAt: this.#started.get(request) ?? this.#now()
|
|
36937
|
+
}
|
|
36938
|
+
void rpc5xxSignalOf(response, this.#firstParty, requestHeadersOf(request)).then((s3) => {
|
|
36939
|
+
if (s3 !== null)
|
|
36940
|
+
record2(s3);
|
|
36606
36941
|
});
|
|
36607
36942
|
});
|
|
36608
36943
|
}
|
|
@@ -36764,15 +37099,26 @@ var PageSignalCollector = class {
|
|
|
36764
37099
|
responseSeen.add(response.request());
|
|
36765
37100
|
noteResponse(response.status(), redactUrl(response.url()));
|
|
36766
37101
|
const request = response.request();
|
|
36767
|
-
const
|
|
36768
|
-
|
|
37102
|
+
const headers = firstParty === void 0 ? void 0 : requestHeadersOf(request);
|
|
37103
|
+
const push = (signal2) => {
|
|
37104
|
+
const started = requestStarted.get(request);
|
|
37105
|
+
if (started !== void 0)
|
|
37106
|
+
this.startedAt.set(signal2, started);
|
|
37107
|
+
this.buffer.push(signal2);
|
|
37108
|
+
};
|
|
37109
|
+
const signal = http5xxSignalOf(response, firstParty, headers, methodOf(request));
|
|
37110
|
+
if (signal !== null) {
|
|
37111
|
+
push(signal);
|
|
36769
37112
|
return;
|
|
36770
|
-
|
|
36771
|
-
|
|
36772
|
-
|
|
36773
|
-
|
|
37113
|
+
}
|
|
37114
|
+
void rpc5xxSignalOf(response, firstParty, headers, methodOf(request)).then((s3) => {
|
|
37115
|
+
if (s3 !== null)
|
|
37116
|
+
push(s3);
|
|
37117
|
+
});
|
|
36774
37118
|
});
|
|
36775
37119
|
page.on("requestfailed", (request) => {
|
|
37120
|
+
if (isExternalSchemeUrl(request.url()))
|
|
37121
|
+
return;
|
|
36776
37122
|
const errorText = request.failure()?.errorText ?? "request failed";
|
|
36777
37123
|
if (errorText === ERR_ABORTED) {
|
|
36778
37124
|
if (responseSeen.has(request))
|
|
@@ -36997,6 +37343,7 @@ function readPageTiming() {
|
|
|
36997
37343
|
}
|
|
36998
37344
|
async function measurePageTiming(page, window2) {
|
|
36999
37345
|
const apiPrefixes = window2.apiPrefixes ?? [];
|
|
37346
|
+
const waitedMs2 = Math.max(0, window2.waitedMs ?? 0);
|
|
37000
37347
|
const side2 = await page.evaluate(readPageTiming).catch(() => ({ docId: null, nav: null, lcp: null }));
|
|
37001
37348
|
const now2 = window2.settleEndedAt;
|
|
37002
37349
|
const all = [
|
|
@@ -37010,7 +37357,8 @@ async function measurePageTiming(page, window2) {
|
|
|
37010
37357
|
route: normalizeRoute(redactUrl(page.url())),
|
|
37011
37358
|
kind,
|
|
37012
37359
|
...newDocument && side2.nav !== null ? { navigation: side2.nav } : {},
|
|
37013
|
-
...window2.actionAt !== null ? { settleMs: Math.max(0, window2.settleEndedAt - window2.actionAt) } : {},
|
|
37360
|
+
...window2.actionAt !== null ? { settleMs: Math.max(0, window2.settleEndedAt - window2.actionAt - waitedMs2) } : {},
|
|
37361
|
+
...window2.actionAt !== null && waitedMs2 > 0 ? { waitedMs: waitedMs2 } : {},
|
|
37014
37362
|
settled: window2.settle.settled,
|
|
37015
37363
|
requests: {
|
|
37016
37364
|
count: window2.completed.length,
|
|
@@ -37183,7 +37531,22 @@ function hangRoute(url2) {
|
|
|
37183
37531
|
return normalizeRoute(redactUrl(url2));
|
|
37184
37532
|
}
|
|
37185
37533
|
|
|
37534
|
+
// ../explore/dist/clock-bound.js
|
|
37535
|
+
async function clockBounded(work, ms, onTimeout) {
|
|
37536
|
+
let timer;
|
|
37537
|
+
const bound = new Promise((resolve14) => {
|
|
37538
|
+
timer = clock.setTimeout(() => resolve14(onTimeout), Math.max(1, ms));
|
|
37539
|
+
});
|
|
37540
|
+
try {
|
|
37541
|
+
return await Promise.race([work, bound]);
|
|
37542
|
+
} finally {
|
|
37543
|
+
if (timer !== void 0)
|
|
37544
|
+
clock.clearTimeout(timer);
|
|
37545
|
+
}
|
|
37546
|
+
}
|
|
37547
|
+
|
|
37186
37548
|
// ../explore/dist/page-monitor.js
|
|
37549
|
+
var RPC_TRAILER_READ_MS = 2e3;
|
|
37187
37550
|
var INTERACTIVE_SELECTOR2 = "a[href],button,input:not([type=hidden]),select,textarea,[role=button],[role=link],[role=textbox],[role=checkbox],[role=combobox],[contenteditable=true]";
|
|
37188
37551
|
function hasEnabledControl(selector) {
|
|
37189
37552
|
for (const el of Array.from(document.querySelectorAll(selector))) {
|
|
@@ -37352,6 +37715,21 @@ var RequestCapture = class {
|
|
|
37352
37715
|
return this.#truncated;
|
|
37353
37716
|
}
|
|
37354
37717
|
};
|
|
37718
|
+
var ACTION_CAUSED_MS = 1500;
|
|
37719
|
+
var isRead = (r) => ["GET", "HEAD"].includes(r.method.toUpperCase());
|
|
37720
|
+
var RECURRING_WINDOW_MS = 3e4;
|
|
37721
|
+
var RECURRING_MIN_COMPLETIONS = 2;
|
|
37722
|
+
function isRecurringRead(r, recent) {
|
|
37723
|
+
const method = r.method.toUpperCase();
|
|
37724
|
+
if (!isRead(r))
|
|
37725
|
+
return false;
|
|
37726
|
+
const path2 = pathOf2(r.url);
|
|
37727
|
+
let n3 = 0;
|
|
37728
|
+
for (const c of recent)
|
|
37729
|
+
if (c.method.toUpperCase() === method && pathOf2(c.url) === path2 && ++n3 >= RECURRING_MIN_COMPLETIONS)
|
|
37730
|
+
return true;
|
|
37731
|
+
return false;
|
|
37732
|
+
}
|
|
37355
37733
|
function pathOf2(url2) {
|
|
37356
37734
|
try {
|
|
37357
37735
|
return new URL(url2).pathname;
|
|
@@ -37375,6 +37753,9 @@ var PageMonitor = class {
|
|
|
37375
37753
|
#wakers = /* @__PURE__ */ new Set();
|
|
37376
37754
|
#statuses = /* @__PURE__ */ new WeakMap();
|
|
37377
37755
|
#contentTypes = /* @__PURE__ */ new WeakMap();
|
|
37756
|
+
/** #378: the RPC-status read of a gRPC-web / Connect response (its end waits for it). */
|
|
37757
|
+
#rpcReads = /* @__PURE__ */ new WeakMap();
|
|
37758
|
+
#rpcStatuses = /* @__PURE__ */ new WeakMap();
|
|
37378
37759
|
#now;
|
|
37379
37760
|
#lastNetworkActivity;
|
|
37380
37761
|
/** The last main-frame navigation (activity no request filter can discount). */
|
|
@@ -37392,6 +37773,8 @@ var PageMonitor = class {
|
|
|
37392
37773
|
this.#lastNetworkActivity = now2();
|
|
37393
37774
|
this.#lastNavigation = now2();
|
|
37394
37775
|
page.on("request", (r) => {
|
|
37776
|
+
if (isExternalSchemeUrl(r.url()))
|
|
37777
|
+
return;
|
|
37395
37778
|
const startedAt = this.#now();
|
|
37396
37779
|
if (r.isNavigationRequest() && r.frame() === page.mainFrame())
|
|
37397
37780
|
this.#documentNavStartedAt = startedAt;
|
|
@@ -37445,6 +37828,7 @@ var PageMonitor = class {
|
|
|
37445
37828
|
const abortedAfterResponse = failed && status !== null;
|
|
37446
37829
|
const endedAt = this.#now();
|
|
37447
37830
|
const contentType = this.#contentTypes.get(r) ?? null;
|
|
37831
|
+
const rpcStatus = this.#rpcStatuses.get(r);
|
|
37448
37832
|
this.#completed.push({
|
|
37449
37833
|
...started,
|
|
37450
37834
|
status,
|
|
@@ -37466,7 +37850,8 @@ var PageMonitor = class {
|
|
|
37466
37850
|
resourceType: started.resourceType,
|
|
37467
37851
|
contentType,
|
|
37468
37852
|
startedAt: started.startedAt,
|
|
37469
|
-
...started.requestContentType === void 0 ? {} : { requestContentType: started.requestContentType }
|
|
37853
|
+
...started.requestContentType === void 0 ? {} : { requestContentType: started.requestContentType },
|
|
37854
|
+
...rpcStatus === void 0 ? {} : { rpcStatus }
|
|
37470
37855
|
});
|
|
37471
37856
|
}
|
|
37472
37857
|
} else {
|
|
@@ -37476,13 +37861,28 @@ var PageMonitor = class {
|
|
|
37476
37861
|
if (!ignored)
|
|
37477
37862
|
this.#touch();
|
|
37478
37863
|
};
|
|
37479
|
-
|
|
37480
|
-
|
|
37864
|
+
const finish = (r, failed) => {
|
|
37865
|
+
const read = this.#rpcReads.get(r);
|
|
37866
|
+
if (read === void 0) {
|
|
37867
|
+
end(r, failed);
|
|
37868
|
+
return;
|
|
37869
|
+
}
|
|
37870
|
+
this.#rpcReads.delete(r);
|
|
37871
|
+
void clockBounded(read, RPC_TRAILER_READ_MS, null).then((s3) => {
|
|
37872
|
+
if (s3 !== null)
|
|
37873
|
+
this.#rpcStatuses.set(r, s3);
|
|
37874
|
+
end(r, failed);
|
|
37875
|
+
});
|
|
37876
|
+
};
|
|
37877
|
+
page.on("requestfinished", (r) => finish(r, false));
|
|
37878
|
+
page.on("requestfailed", (r) => finish(r, true));
|
|
37481
37879
|
page.on("response", (res) => {
|
|
37482
37880
|
this.#statuses.set(res.request(), res.status());
|
|
37483
37881
|
const type = res.headers()["content-type"] ?? "";
|
|
37484
37882
|
if (type !== "")
|
|
37485
37883
|
this.#contentTypes.set(res.request(), type);
|
|
37884
|
+
if (RPC_CONTENT.test(type) || res.headers()["grpc-status"] !== void 0)
|
|
37885
|
+
this.#rpcReads.set(res.request(), rpcStatusOfResponse(res));
|
|
37486
37886
|
if (STREAMING_CONTENT.test(type)) {
|
|
37487
37887
|
this.#background.set(res.request(), "stream");
|
|
37488
37888
|
this.#touch();
|
|
@@ -37541,6 +37941,20 @@ var PageMonitor = class {
|
|
|
37541
37941
|
if (this.#ignore(r.url()))
|
|
37542
37942
|
this.#background.set(r, "ignored");
|
|
37543
37943
|
}
|
|
37944
|
+
/**
|
|
37945
|
+
* #383: pending work minus the page's own polling — an in-flight read (GET/HEAD) whose method and
|
|
37946
|
+
* path already completed {@link RECURRING_MIN_COMPLETIONS} times in the last
|
|
37947
|
+
* {@link RECURRING_WINDOW_MS} is a periodic poll (a balance, a notification count), not the app
|
|
37948
|
+
* working on what the run did; so is a read the page started on its own more than
|
|
37949
|
+
* {@link ACTION_CAUSED_MS} after the run's last action. A write always counts. What a quiet `wait`
|
|
37950
|
+
* asks (#241).
|
|
37951
|
+
*/
|
|
37952
|
+
pendingWork() {
|
|
37953
|
+
const now2 = this.#now();
|
|
37954
|
+
const recent = this.#completed.filter((c) => c.endedAt >= now2 - RECURRING_WINDOW_MS);
|
|
37955
|
+
const since = this.#lastActionAt;
|
|
37956
|
+
return this.pending().filter((r) => !isRecurringRead(r, recent) && !(isRead(r) && since !== null && r.startedAt > since + ACTION_CAUSED_MS));
|
|
37957
|
+
}
|
|
37544
37958
|
/** Requests that are pending WORK: long-lived connections and background requests excluded. */
|
|
37545
37959
|
pending() {
|
|
37546
37960
|
return [...this.#inflight.entries()].filter(([r, info]) => !STREAM_TYPES.has(info.resourceType) && !this.#background.has(r)).map(([, info]) => info);
|
|
@@ -37614,15 +38028,40 @@ var PageMonitor = class {
|
|
|
37614
38028
|
// ---- the timing window: from one perception to the next (owner ruling 6) ----
|
|
37615
38029
|
#windowStart = null;
|
|
37616
38030
|
#actionAt = null;
|
|
38031
|
+
/** #383: the run's most recent action, kept across windows (what "caused by the run" is measured from). */
|
|
38032
|
+
#lastActionAt = null;
|
|
37617
38033
|
#lastDocId = null;
|
|
38034
|
+
/** #368: time spent since the action inside a wait the run chose (a reply wait, a job wait). */
|
|
38035
|
+
#waitedMs = 0;
|
|
38036
|
+
#waitDepth = 0;
|
|
37618
38037
|
/** Called by `act()` when it dispatches a page-changing action: the start of a transition. */
|
|
37619
38038
|
markAction() {
|
|
37620
38039
|
this.#actionAt = this.#now();
|
|
38040
|
+
this.#lastActionAt = this.#actionAt;
|
|
38041
|
+
this.#waitedMs = 0;
|
|
38042
|
+
}
|
|
38043
|
+
/**
|
|
38044
|
+
* #368: runs an EXPLICIT wait the run chose (a chat reply wait `--reply-wait-ms`, a job wait
|
|
38045
|
+
* `--job-wait-ms`, a `wait` decision) and books its duration as waiting, not as the page's render:
|
|
38046
|
+
* the transition's `settleMs` excludes it, so a 30s reply wait never reads as a 30s render (and
|
|
38047
|
+
* never feeds the host-health render trend). Nested waits count once.
|
|
38048
|
+
*/
|
|
38049
|
+
async explicitWait(wait) {
|
|
38050
|
+
const started = this.#now();
|
|
38051
|
+
this.#waitDepth += 1;
|
|
38052
|
+
try {
|
|
38053
|
+
return await wait();
|
|
38054
|
+
} finally {
|
|
38055
|
+
this.#waitDepth -= 1;
|
|
38056
|
+
if (this.#waitDepth === 0)
|
|
38057
|
+
this.#waitedMs += Math.max(0, this.#now() - started);
|
|
38058
|
+
}
|
|
37621
38059
|
}
|
|
37622
38060
|
/** The open timing window (since the previous perception, or since the monitor started). */
|
|
37623
38061
|
window() {
|
|
37624
38062
|
const start = this.#windowStart ?? 0;
|
|
37625
|
-
|
|
38063
|
+
const actionAt = this.#actionAt !== null && this.#actionAt >= start ? this.#actionAt : null;
|
|
38064
|
+
return { start, actionAt, lastDocId: this.#lastDocId, waitedMs: actionAt === null ? 0 : this.#waitedMs };
|
|
37626
38065
|
}
|
|
37627
38066
|
/**
|
|
37628
38067
|
* Closes the window at `at` (the perception just read the page) and forgets requests that ended
|
|
@@ -37634,6 +38073,7 @@ var PageMonitor = class {
|
|
|
37634
38073
|
closeWindow(at3, docId) {
|
|
37635
38074
|
this.#windowStart = at3;
|
|
37636
38075
|
this.#actionAt = null;
|
|
38076
|
+
this.#waitedMs = 0;
|
|
37637
38077
|
if (docId !== null)
|
|
37638
38078
|
this.#lastDocId = docId;
|
|
37639
38079
|
const keepFrom = at3 - RECENT_REQUESTS_MS;
|
|
@@ -37766,18 +38206,267 @@ function monitorFor(page) {
|
|
|
37766
38206
|
return m;
|
|
37767
38207
|
}
|
|
37768
38208
|
|
|
37769
|
-
// ../explore/dist/
|
|
37770
|
-
|
|
37771
|
-
|
|
37772
|
-
|
|
37773
|
-
|
|
37774
|
-
|
|
37775
|
-
|
|
37776
|
-
|
|
37777
|
-
|
|
37778
|
-
if (
|
|
37779
|
-
|
|
38209
|
+
// ../explore/dist/status.js
|
|
38210
|
+
var EMPTY_STATUS = { alerts: [], notices: [], invalid: [] };
|
|
38211
|
+
var STATUS_CHARS = 200;
|
|
38212
|
+
var STATUS_ITEMS = 5;
|
|
38213
|
+
function readStatusInPage(limits) {
|
|
38214
|
+
const norm5 = (s3) => (s3 ?? "").replace(/\s+/g, " ").trim();
|
|
38215
|
+
const cut3 = (s3) => s3.length > limits.chars ? `${s3.slice(0, limits.chars)}\u2026` : s3;
|
|
38216
|
+
const shown = (el) => {
|
|
38217
|
+
const h = el;
|
|
38218
|
+
if (h.closest("[hidden],[aria-hidden=true]") !== null)
|
|
38219
|
+
return false;
|
|
38220
|
+
const st = window.getComputedStyle(h);
|
|
38221
|
+
const r = h.getBoundingClientRect();
|
|
38222
|
+
return st.display !== "none" && st.visibility !== "hidden" && r.width > 0 && r.height > 0;
|
|
38223
|
+
};
|
|
38224
|
+
const w = window;
|
|
38225
|
+
if (w.__jevInvalid === void 0) {
|
|
38226
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
38227
|
+
w.__jevInvalid = seen;
|
|
38228
|
+
document.addEventListener("invalid", (e) => {
|
|
38229
|
+
if (e.target instanceof Element)
|
|
38230
|
+
seen.add(e.target);
|
|
38231
|
+
}, true);
|
|
38232
|
+
}
|
|
38233
|
+
const refused = w.__jevInvalid;
|
|
38234
|
+
const alerts = [];
|
|
38235
|
+
const notices = [];
|
|
38236
|
+
const live = document.querySelectorAll("[role=alert],[role=status],[aria-live=polite],[aria-live=assertive],output");
|
|
38237
|
+
for (const el of Array.from(live)) {
|
|
38238
|
+
if (!shown(el))
|
|
38239
|
+
continue;
|
|
38240
|
+
const text2 = cut3(norm5(el.innerText));
|
|
38241
|
+
if (text2 === "")
|
|
38242
|
+
continue;
|
|
38243
|
+
if ([...alerts, ...notices].some((a) => a.includes(text2) || text2.includes(a)))
|
|
38244
|
+
continue;
|
|
38245
|
+
const assertive = el.getAttribute("role") === "alert" || el.getAttribute("aria-live") === "assertive";
|
|
38246
|
+
const into = assertive ? alerts : notices;
|
|
38247
|
+
if (into.length < limits.items)
|
|
38248
|
+
into.push(text2);
|
|
38249
|
+
}
|
|
38250
|
+
const labelOf2 = (el) => {
|
|
38251
|
+
const aria = norm5(el.getAttribute("aria-label"));
|
|
38252
|
+
if (aria !== "")
|
|
38253
|
+
return aria;
|
|
38254
|
+
const labels = el.labels;
|
|
38255
|
+
if (labels && labels.length > 0)
|
|
38256
|
+
return norm5(labels[0].textContent);
|
|
38257
|
+
const owner = el.closest("label");
|
|
38258
|
+
if (owner !== null)
|
|
38259
|
+
return norm5(owner.textContent);
|
|
38260
|
+
return norm5(el.getAttribute("placeholder")) || norm5(el.getAttribute("name")) || el.tagName.toLowerCase();
|
|
38261
|
+
};
|
|
38262
|
+
const describedBy = (el) => {
|
|
38263
|
+
const ids = `${el.getAttribute("aria-errormessage") ?? ""} ${el.getAttribute("aria-describedby") ?? ""}`.trim();
|
|
38264
|
+
if (ids === "")
|
|
38265
|
+
return "";
|
|
38266
|
+
return norm5(ids.split(/\s+/).map((id) => document.getElementById(id)).filter((n3) => n3 !== null && shown(n3)).map((n3) => n3.innerText).join(" "));
|
|
38267
|
+
};
|
|
38268
|
+
const invalid = [];
|
|
38269
|
+
const fields = document.querySelectorAll("input:not([type=hidden]),select,textarea,[aria-invalid=true]");
|
|
38270
|
+
for (const el of Array.from(fields)) {
|
|
38271
|
+
if (!shown(el))
|
|
38272
|
+
continue;
|
|
38273
|
+
const f = el;
|
|
38274
|
+
const ariaInvalid = norm5(el.getAttribute("aria-invalid")).toLowerCase() === "true";
|
|
38275
|
+
const nativeInvalid = typeof f.checkValidity === "function" && f.validity !== void 0 && !f.validity.valid;
|
|
38276
|
+
const reached2 = refused.has(el) || typeof f.value === "string" && f.value !== "";
|
|
38277
|
+
if (!ariaInvalid && !(nativeInvalid && reached2))
|
|
38278
|
+
continue;
|
|
38279
|
+
const message2 = norm5(nativeInvalid ? f.validationMessage : "") || describedBy(el) || "marked invalid";
|
|
38280
|
+
invalid.push({ name: cut3(labelOf2(el)), message: cut3(message2) });
|
|
38281
|
+
if (invalid.length >= limits.items)
|
|
38282
|
+
break;
|
|
38283
|
+
}
|
|
38284
|
+
return { alerts, notices, invalid };
|
|
38285
|
+
}
|
|
38286
|
+
async function readPageStatus(page) {
|
|
38287
|
+
return page.evaluate(readStatusInPage, { chars: STATUS_CHARS, items: STATUS_ITEMS }).catch(() => EMPTY_STATUS);
|
|
38288
|
+
}
|
|
38289
|
+
function statusDelta(before, now2) {
|
|
38290
|
+
const alerts = now2.alerts.filter((a) => !before.alerts.includes(a));
|
|
38291
|
+
const notices = now2.notices.filter((a) => !before.notices.includes(a));
|
|
38292
|
+
const invalid = now2.invalid.filter((f) => !before.invalid.some((b2) => b2.name === f.name && b2.message === f.message));
|
|
38293
|
+
return { alerts, notices, invalid };
|
|
38294
|
+
}
|
|
38295
|
+
function describeStatus(s3) {
|
|
38296
|
+
return [
|
|
38297
|
+
...s3.alerts.map((a) => `alert "${a}"`),
|
|
38298
|
+
...s3.notices.map((a) => `status "${a}"`),
|
|
38299
|
+
...s3.invalid.map((f) => `invalid field "${f.name}": "${f.message}"`)
|
|
38300
|
+
].join("; ");
|
|
38301
|
+
}
|
|
38302
|
+
function isEmptyStatus(s3) {
|
|
38303
|
+
return s3.alerts.length === 0 && s3.notices.length === 0 && s3.invalid.length === 0;
|
|
38304
|
+
}
|
|
38305
|
+
var IN_PROGRESS_WORDS = "simulating|processing|running|generating|drafting|fetching|rendering|publishing|analy[sz]ing|computing|calculating|loading|preparing|working|thinking|uploading|importing|exporting|syncing|saving|submitting|creating|building|training|indexing|queued|in progress";
|
|
38306
|
+
function inProgressInPage(words3) {
|
|
38307
|
+
const norm5 = (s3) => (s3 ?? "").replace(/\s+/g, " ").trim();
|
|
38308
|
+
const cut3 = (s3) => s3.length > 80 ? `${s3.slice(0, 80)}\u2026` : s3;
|
|
38309
|
+
const shown = (el) => {
|
|
38310
|
+
const h = el;
|
|
38311
|
+
if (h.closest("[hidden],[aria-hidden=true]") !== null)
|
|
38312
|
+
return false;
|
|
38313
|
+
const st = window.getComputedStyle(h);
|
|
38314
|
+
const r = h.getBoundingClientRect();
|
|
38315
|
+
return st.display !== "none" && st.visibility !== "hidden" && r.width > 0 && r.height > 0;
|
|
38316
|
+
};
|
|
38317
|
+
const loose = new RegExp(`\\b(?:${words3})\\b`, "i");
|
|
38318
|
+
const trailing = new RegExp(`^\\W*(?:${words3})\\b[^.!?]{0,60}(?:\u2026|\\.\\.\\.)\\s*$`, "i");
|
|
38319
|
+
const job = /\b(?:is|are)\s+(?:still\s+|already\s+|currently\s+|now\s+)?(?:running|processing|in progress|being (?:processed|generated|simulated|prepared))\b|\bstill (?:running|working|processing)\b|^\W*in progress\W*$/i;
|
|
38320
|
+
for (const el of Array.from(document.querySelectorAll('[aria-busy="true"]'))) {
|
|
38321
|
+
if (shown(el))
|
|
38322
|
+
return `aria-busy region${norm5(el.innerText) === "" ? "" : ` "${cut3(norm5(el.innerText))}"`}`;
|
|
38323
|
+
}
|
|
38324
|
+
const live = document.querySelectorAll("[role=status],[aria-live=polite],[aria-live=assertive],[role=progressbar],output");
|
|
38325
|
+
for (const el of Array.from(live)) {
|
|
38326
|
+
if (!shown(el))
|
|
38327
|
+
continue;
|
|
38328
|
+
const text2 = norm5(el.innerText || el.getAttribute("aria-valuetext") || el.getAttribute("aria-label"));
|
|
38329
|
+
if (text2 !== "" && loose.test(text2))
|
|
38330
|
+
return `status "${cut3(text2)}"`;
|
|
38331
|
+
if (text2 !== "" && /^\W*[a-z]{3,}ing\b[^.!?]{0,60}(?:…|\.\.\.)\s*$/i.test(text2))
|
|
38332
|
+
return `status "${cut3(text2)}"`;
|
|
38333
|
+
}
|
|
38334
|
+
const body = document.body ? document.body.innerText : "";
|
|
38335
|
+
for (const raw of body.split(/\n+/)) {
|
|
38336
|
+
const line = norm5(raw);
|
|
38337
|
+
if (line === "" || line.length > 200)
|
|
38338
|
+
continue;
|
|
38339
|
+
if (trailing.test(line) || job.test(line))
|
|
38340
|
+
return `status text "${cut3(line)}"`;
|
|
38341
|
+
}
|
|
38342
|
+
return null;
|
|
38343
|
+
}
|
|
38344
|
+
async function readInProgressStatus(page) {
|
|
38345
|
+
return page.evaluate(inProgressInPage, IN_PROGRESS_WORDS).catch(() => null);
|
|
38346
|
+
}
|
|
38347
|
+
async function busyOverlay(page) {
|
|
38348
|
+
const busy = await page.evaluate(visibleBusyIndicator).catch(() => null);
|
|
38349
|
+
return busy ?? await readInProgressStatus(page);
|
|
38350
|
+
}
|
|
38351
|
+
function workAcknowledgedInPage(words3) {
|
|
38352
|
+
const norm5 = (s3) => (s3 ?? "").replace(/\s+/g, " ").trim();
|
|
38353
|
+
const shown = (el) => {
|
|
38354
|
+
const h = el;
|
|
38355
|
+
if (h.closest("[hidden],[aria-hidden=true]") !== null)
|
|
38356
|
+
return false;
|
|
38357
|
+
const st = window.getComputedStyle(h);
|
|
38358
|
+
const r = h.getBoundingClientRect();
|
|
38359
|
+
return st.display !== "none" && st.visibility !== "hidden" && r.width > 0 && r.height > 0;
|
|
38360
|
+
};
|
|
38361
|
+
const label2 = (el) => el instanceof HTMLInputElement ? norm5(el.value) : norm5(el.getAttribute("aria-label") ?? el.innerText ?? el.textContent);
|
|
38362
|
+
const disabled = (el) => el.disabled === true || el.getAttribute("aria-disabled") === "true";
|
|
38363
|
+
const controls = Array.from(document.querySelectorAll("button,[role=button],input[type=button],input[type=submit]")).filter(shown);
|
|
38364
|
+
const progress = new RegExp(`^\\W*(?:${words3})\\b[^.!?]{0,60}(?:\u2026|\\.\\.\\.)?\\s*$`, "i");
|
|
38365
|
+
let cancel2 = null;
|
|
38366
|
+
let busyControl = null;
|
|
38367
|
+
for (const el of controls) {
|
|
38368
|
+
const n3 = label2(el);
|
|
38369
|
+
if (cancel2 === null && !disabled(el) && /^\W*(?:cancel|stop|abort)\b/i.test(n3))
|
|
38370
|
+
cancel2 = `an enabled "${n3.slice(0, 40)}" control`;
|
|
38371
|
+
if (busyControl === null && disabled(el) && n3 !== "" && progress.test(n3))
|
|
38372
|
+
busyControl = `a disabled "${n3.slice(0, 40)}" control`;
|
|
38373
|
+
}
|
|
38374
|
+
let bar = null;
|
|
38375
|
+
for (const el of Array.from(document.querySelectorAll("[role=progressbar][aria-valuenow],progress[value]"))) {
|
|
38376
|
+
if (!shown(el))
|
|
38377
|
+
continue;
|
|
38378
|
+
const now2 = Number(el.getAttribute("aria-valuenow") ?? el.value);
|
|
38379
|
+
const max = Number(el.getAttribute("aria-valuemax") ?? el.max) || 100;
|
|
38380
|
+
if (Number.isFinite(now2) && now2 < max) {
|
|
38381
|
+
bar = `a progress bar at ${Math.round(now2 / max * 100)}%`;
|
|
38382
|
+
break;
|
|
38383
|
+
}
|
|
37780
38384
|
}
|
|
38385
|
+
return { busyControl, cancel: cancel2, bar };
|
|
38386
|
+
}
|
|
38387
|
+
async function readWorkingStatus(page) {
|
|
38388
|
+
const ack = await page.evaluate(workAcknowledgedInPage, IN_PROGRESS_WORDS).catch(() => null);
|
|
38389
|
+
if (ack === null)
|
|
38390
|
+
return null;
|
|
38391
|
+
if (ack.busyControl !== null)
|
|
38392
|
+
return ack.cancel === null ? ack.busyControl : `${ack.busyControl} and ${ack.cancel}`;
|
|
38393
|
+
const extra = ack.cancel ?? ack.bar;
|
|
38394
|
+
if (extra === null)
|
|
38395
|
+
return null;
|
|
38396
|
+
const status = await readInProgressStatus(page);
|
|
38397
|
+
return status === null ? null : `${status} with ${extra}`;
|
|
38398
|
+
}
|
|
38399
|
+
var MAX_DOCUMENTED_WAIT_MS = 30 * 6e4;
|
|
38400
|
+
var NUMBER_WORDS = {
|
|
38401
|
+
a: 1,
|
|
38402
|
+
an: 1,
|
|
38403
|
+
one: 1,
|
|
38404
|
+
two: 2,
|
|
38405
|
+
three: 3,
|
|
38406
|
+
four: 4,
|
|
38407
|
+
five: 5,
|
|
38408
|
+
six: 6,
|
|
38409
|
+
seven: 7,
|
|
38410
|
+
eight: 8,
|
|
38411
|
+
nine: 9,
|
|
38412
|
+
ten: 10,
|
|
38413
|
+
fifteen: 15,
|
|
38414
|
+
twenty: 20,
|
|
38415
|
+
thirty: 30,
|
|
38416
|
+
forty: 40,
|
|
38417
|
+
sixty: 60,
|
|
38418
|
+
ninety: 90,
|
|
38419
|
+
"a couple of": 3,
|
|
38420
|
+
"a couple": 3,
|
|
38421
|
+
"a few": 5,
|
|
38422
|
+
few: 5,
|
|
38423
|
+
several: 10
|
|
38424
|
+
};
|
|
38425
|
+
var UNIT_MS = [
|
|
38426
|
+
[/^(?:seconds?|secs?|s)$/i, 1e3],
|
|
38427
|
+
[/^(?:minutes?|mins?|m)$/i, 6e4],
|
|
38428
|
+
[/^(?:hours?|hrs?|h)$/i, 36e5]
|
|
38429
|
+
];
|
|
38430
|
+
var WAIT_TALK = /\b(?:take|takes|taking|took|wait|waiting|ready|done|finish|finishes|complete|completes|usually|typically|normally|should|may|might|can|up to|less than|under|within|about|around|roughly|approximately|expect)\b/i;
|
|
38431
|
+
var DURATION = /\b(\d+(?:\.\d+)?|an?|one|two|three|four|five|six|seven|eight|nine|ten|fifteen|twenty|thirty|forty|sixty|ninety|a couple(?: of)?|a few|few|several)(?:\s*(?:-|–|to)\s*(\d+(?:\.\d+)?))?\s*(seconds?|secs?|minutes?|mins?|hours?|hrs?)\b/gi;
|
|
38432
|
+
function documentedWaitMs(line) {
|
|
38433
|
+
if (!WAIT_TALK.test(line) || /\bago\b/i.test(line))
|
|
38434
|
+
return null;
|
|
38435
|
+
let longest = null;
|
|
38436
|
+
for (const m of line.matchAll(DURATION)) {
|
|
38437
|
+
const [, first, upper, unit] = m;
|
|
38438
|
+
const per = UNIT_MS.find(([re]) => re.test(unit ?? ""))?.[1];
|
|
38439
|
+
if (per === void 0 || first === void 0)
|
|
38440
|
+
continue;
|
|
38441
|
+
const n3 = Number(upper ?? first) || NUMBER_WORDS[first.toLowerCase()] || NaN;
|
|
38442
|
+
if (!Number.isFinite(n3) || n3 <= 0)
|
|
38443
|
+
continue;
|
|
38444
|
+
longest = Math.max(longest ?? 0, n3 * per);
|
|
38445
|
+
}
|
|
38446
|
+
return longest === null ? null : Math.min(longest, MAX_DOCUMENTED_WAIT_MS);
|
|
38447
|
+
}
|
|
38448
|
+
function durationLinesInPage() {
|
|
38449
|
+
const body = document.body ? document.body.innerText : "";
|
|
38450
|
+
const out = [];
|
|
38451
|
+
for (const raw of body.split(/\n+/)) {
|
|
38452
|
+
const line = raw.replace(/\s+/g, " ").trim();
|
|
38453
|
+
if (line === "" || line.length > 300)
|
|
38454
|
+
continue;
|
|
38455
|
+
if (/\b(?:seconds?|secs?|minutes?|mins?|hours?|hrs?)\b/i.test(line))
|
|
38456
|
+
out.push(line);
|
|
38457
|
+
if (out.length >= 10)
|
|
38458
|
+
break;
|
|
38459
|
+
}
|
|
38460
|
+
return out;
|
|
38461
|
+
}
|
|
38462
|
+
async function readDocumentedWait(page) {
|
|
38463
|
+
const lines = await page.evaluate(durationLinesInPage).catch(() => []);
|
|
38464
|
+
for (const line of lines) {
|
|
38465
|
+
const ms = documentedWaitMs(line);
|
|
38466
|
+
if (ms !== null)
|
|
38467
|
+
return { text: line.length > 120 ? `${line.slice(0, 120)}\u2026` : line, ms };
|
|
38468
|
+
}
|
|
38469
|
+
return null;
|
|
37781
38470
|
}
|
|
37782
38471
|
|
|
37783
38472
|
// ../explore/dist/perceive.js
|
|
@@ -37836,7 +38525,8 @@ async function perceive(page, opts = {}) {
|
|
|
37836
38525
|
const requestBoundMs = opts.requestBoundMs ?? ceiling / 2;
|
|
37837
38526
|
const monitor = monitorFor(page);
|
|
37838
38527
|
monitor.configure(opts.settleConfig);
|
|
37839
|
-
const
|
|
38528
|
+
const ignoreDeclared = textMatcher(opts.hangConfig?.ignoreNoProgress);
|
|
38529
|
+
const ignoreNoProgress = (desc) => ignoreDeclared(desc) || ignoreDeclared(/"(.*)"/.exec(desc)?.[1] ?? desc);
|
|
37840
38530
|
let responsive = await probeResponsive(page, hangProbeMs);
|
|
37841
38531
|
if (!responsive && documentPending(monitor.pending())) {
|
|
37842
38532
|
const deadline = clock.now() + ceiling;
|
|
@@ -37947,10 +38637,44 @@ async function perceive(page, opts = {}) {
|
|
|
37947
38637
|
actionAt: win.actionAt,
|
|
37948
38638
|
settle: settle2,
|
|
37949
38639
|
settleEndedAt,
|
|
38640
|
+
waitedMs: win.waitedMs,
|
|
37950
38641
|
...opts.timingConfig?.apiPrefixes === void 0 ? {} : { apiPrefixes: opts.timingConfig.apiPrefixes }
|
|
37951
38642
|
});
|
|
37952
38643
|
monitor.closeWindow(settleEndedAt, docId);
|
|
37953
|
-
const
|
|
38644
|
+
const read = () => snapshot(page, {
|
|
38645
|
+
...snapOpts,
|
|
38646
|
+
navigationWaitMs: Math.max(1, ceiling),
|
|
38647
|
+
awaitNavigation: async (remainingMs) => {
|
|
38648
|
+
await monitor.waitSettled({ quietMs, ceilingMs: Math.max(1, remainingMs) });
|
|
38649
|
+
}
|
|
38650
|
+
});
|
|
38651
|
+
let snap;
|
|
38652
|
+
try {
|
|
38653
|
+
snap = await read();
|
|
38654
|
+
if (snap.controls.length === 0 && stuckBusy === null) {
|
|
38655
|
+
const left = () => ceiling - (clock.now() - started);
|
|
38656
|
+
if (left() > 0 && await busyOverlay(page) !== null) {
|
|
38657
|
+
while (left() > 0 && !page.isClosed() && await busyOverlay(page) !== null)
|
|
38658
|
+
await clock.sleep(Math.min(250, Math.max(1, left())));
|
|
38659
|
+
if (left() > 0 && !page.isClosed()) {
|
|
38660
|
+
settle2 = await monitor.waitSettled({ quietMs, ceilingMs: Math.max(1, left()) });
|
|
38661
|
+
snap = await read();
|
|
38662
|
+
}
|
|
38663
|
+
}
|
|
38664
|
+
}
|
|
38665
|
+
} catch (e) {
|
|
38666
|
+
if (!(e instanceof PageNavigatingError))
|
|
38667
|
+
throw e;
|
|
38668
|
+
const url2 = redactUrl(page.url());
|
|
38669
|
+
return {
|
|
38670
|
+
rendered: false,
|
|
38671
|
+
snapshot: { url: page.url(), controls: [], truncated: false, signature: contentHash({ url: url2, navigating: true }) },
|
|
38672
|
+
settle: settle2,
|
|
38673
|
+
timing,
|
|
38674
|
+
hang: null,
|
|
38675
|
+
reason: `${e.message} within ${ceiling}ms`
|
|
38676
|
+
};
|
|
38677
|
+
}
|
|
37954
38678
|
const evidence = pendingEvidence(settle2.pending, settleEndedAt);
|
|
37955
38679
|
const kind = classifyHang({
|
|
37956
38680
|
responsive: true,
|
|
@@ -38013,9 +38737,8 @@ function affordedOp(c) {
|
|
|
38013
38737
|
return "type";
|
|
38014
38738
|
return "click";
|
|
38015
38739
|
}
|
|
38016
|
-
var MESSAGE_INPUT_TYPES = /* @__PURE__ */ new Set(["", "text", "search"]);
|
|
38017
38740
|
function sendable(c) {
|
|
38018
|
-
if (!c.enabled || !MESSAGE_FIELD.test(c.name))
|
|
38741
|
+
if (!c.enabled || !MESSAGE_FIELD.test(c.name) || c.conversational !== true)
|
|
38019
38742
|
return false;
|
|
38020
38743
|
if (c.tag === "textarea")
|
|
38021
38744
|
return true;
|
|
@@ -38023,7 +38746,6 @@ function sendable(c) {
|
|
|
38023
38746
|
return MESSAGE_INPUT_TYPES.has(c.inputType ?? "");
|
|
38024
38747
|
return c.role === "textbox";
|
|
38025
38748
|
}
|
|
38026
|
-
var MESSAGE_FIELD = /\b(reply|message|ask|chat|inquiry|prompt|say|talk|conversation)\b/i;
|
|
38027
38749
|
var TARGET_FREE_ACTIONS = [
|
|
38028
38750
|
{ op: "wait", description: "wait for the page to finish updating" },
|
|
38029
38751
|
{ op: "scroll_down", description: "scroll down to reveal more of the page" },
|
|
@@ -38418,7 +39140,6 @@ var SEND_REQUEST_SLACK_MS = 5e3;
|
|
|
38418
39140
|
var REPLY_QUIET_MS = 1e3;
|
|
38419
39141
|
var MIN_REPLY_CHARS = 12;
|
|
38420
39142
|
var REPLY_KEEP_CHARS = 1500;
|
|
38421
|
-
var SUBMIT_NAME = /\b(send|submit|reply|ask|post)\b|[→➤➔↑]|^\s*(go|ok)\s*$/i;
|
|
38422
39143
|
var BUSY_TEXT = /^(thinking|typing|loading|generating|sending|working|searching|preparing|processing|analy[sz]ing|drafting|please wait|one moment)\b.{0,60}$/i;
|
|
38423
39144
|
var PENDING_STATUS = /\b(waiting for|confirmation pending|pending confirmation|may take (a few |several )?(seconds|minutes|a moment)|in progress|still working|hang tight|please wait)\b/i;
|
|
38424
39145
|
var TRAILING_STATUS = /^.{0,60}(…|\.\.\.)$/;
|
|
@@ -38562,6 +39283,9 @@ async function readPageHeadings(page, secrets = []) {
|
|
|
38562
39283
|
return { heading: redactPageText(h.heading, secrets), title: redactPageText(h.title, secrets) };
|
|
38563
39284
|
}
|
|
38564
39285
|
async function waitForReply(page, opts) {
|
|
39286
|
+
return monitorFor(page).explicitWait(() => listenForReply(page, opts));
|
|
39287
|
+
}
|
|
39288
|
+
async function listenForReply(page, opts) {
|
|
38565
39289
|
const idleMs = opts.timeoutMs ?? REPLY_WAIT_MS;
|
|
38566
39290
|
const ceilingMs = Math.max(idleMs, opts.ceilingMs ?? REPLY_CEILING_MS);
|
|
38567
39291
|
const quietMs = opts.quietMs ?? REPLY_QUIET_MS;
|
|
@@ -38584,7 +39308,7 @@ async function waitForReply(page, opts) {
|
|
|
38584
39308
|
});
|
|
38585
39309
|
for (; ; ) {
|
|
38586
39310
|
if (remaining() <= 0)
|
|
38587
|
-
return result(false, "ceiling");
|
|
39311
|
+
return result(false, clock.now() - lastActivity >= idleMs ? "idle" : "ceiling");
|
|
38588
39312
|
const text2 = await readPageText(page, secrets);
|
|
38589
39313
|
const fresh = newTurnText(opts.baseline, text2, sent);
|
|
38590
39314
|
const t = clock.now();
|
|
@@ -38622,6 +39346,9 @@ async function waitForReply(page, opts) {
|
|
|
38622
39346
|
}
|
|
38623
39347
|
}
|
|
38624
39348
|
async function waitForChange(page, timeoutMs) {
|
|
39349
|
+
return monitorFor(page).explicitWait(() => watchForChange(page, timeoutMs));
|
|
39350
|
+
}
|
|
39351
|
+
async function watchForChange(page, timeoutMs) {
|
|
38625
39352
|
const before = await readPageText(page);
|
|
38626
39353
|
const changed = page.waitForFunction((b2) => (document.body ? document.body.innerText : "") !== b2, before, {
|
|
38627
39354
|
timeout: Math.max(1, timeoutMs),
|
|
@@ -38633,7 +39360,7 @@ async function waitForChange(page, timeoutMs) {
|
|
|
38633
39360
|
return clockBounded(changed, Math.max(1, timeoutMs), false);
|
|
38634
39361
|
}
|
|
38635
39362
|
async function stillBusy(page) {
|
|
38636
|
-
if (monitorFor(page).
|
|
39363
|
+
if (monitorFor(page).pendingWork().length > 0)
|
|
38637
39364
|
return true;
|
|
38638
39365
|
return await page.evaluate(visibleBusyIndicator).catch(() => null) !== null;
|
|
38639
39366
|
}
|
|
@@ -38946,28 +39673,55 @@ function exactLiterals(goal) {
|
|
|
38946
39673
|
const re = new RegExp(String.raw`\bexactly\s*[:=]?\s*(?:${quoted}|(?!["“'‘\u0060])(\S.{0,499}?)(?=\.(?:\s|$)|[\n;]|\s*$))`, "giu");
|
|
38947
39674
|
return [...goal.matchAll(re)].map((m) => (m[1] ?? m[2] ?? "").trim()).filter((v) => v !== "");
|
|
38948
39675
|
}
|
|
39676
|
+
var INSTRUCTION_VERB_LIST = "add create make set enter type fill write test check verify ensure confirm click open go navigate visit connect disconnect save submit send ask select choose change edit update delete remove upload try run start finish complete configure enable disable sign log invite import export publish";
|
|
39677
|
+
var VALUE_SPAN_MARKER = String.raw`\b(?:(?:an?|the|some|one)\s+)?(?:(?:short|brief|quick|simple|one-line|one-sentence|few-word)\s+)?(?:description|summary|explanation|bio|blurb|overview|pitch)\s+(?:of|about|for)\b|\b(?:describing|saying|explaining|mentioning|stating|about|that\s+says)\b`;
|
|
39678
|
+
var VALUE_SPAN = String.raw`\s*[:,]?\s*((?:\([^()\n]{0,300}\)|[^,;:.()\n])+?)(?=\s*(?:[,;:\n]|\.(?:\s|$))|\s+(?:and\s+)?then\s|\s+and\s+(?:${INSTRUCTION_VERB_LIST.replace(/ /g, "|")})\b|\s*$)`;
|
|
39679
|
+
function goalValueSpans(goal) {
|
|
39680
|
+
const spans = [];
|
|
39681
|
+
const cut3 = (re, s3) => s3.replace(re, (m, ...g) => {
|
|
39682
|
+
const span = typeof g[0] === "string" ? g[0] : "";
|
|
39683
|
+
if (span.trim() === "")
|
|
39684
|
+
return m;
|
|
39685
|
+
spans.push(span.trim());
|
|
39686
|
+
const at3 = m.lastIndexOf(span);
|
|
39687
|
+
return `${m.slice(0, at3)} ; ${m.slice(at3 + span.length)}`;
|
|
39688
|
+
});
|
|
39689
|
+
let prose = cut3(/["“'‘\u0060]([^"”'’\u0060\n]{1,200})["”'’\u0060]/gu, goal);
|
|
39690
|
+
for (const literal2 of exactLiterals(goal)) {
|
|
39691
|
+
spans.push(literal2);
|
|
39692
|
+
prose = prose.split(literal2).join(" ; ");
|
|
39693
|
+
}
|
|
39694
|
+
prose = cut3(new RegExp(String.raw`(?:${VALUE_SPAN_MARKER})${VALUE_SPAN}`, "giu"), prose);
|
|
39695
|
+
prose = cut3(/\(([^()\n]{1,300})\)/gu, prose);
|
|
39696
|
+
return { spans, prose };
|
|
39697
|
+
}
|
|
38949
39698
|
function echoesGoal(value, goal) {
|
|
38950
39699
|
const v = words2(value);
|
|
38951
39700
|
const g = words2(goal);
|
|
38952
39701
|
if (v.length === 0 || g.length === 0)
|
|
38953
39702
|
return null;
|
|
38954
39703
|
const flat = v.join(" ");
|
|
38955
|
-
const goalFlat = ` ${g.join(" ")} `;
|
|
38956
39704
|
if (flat === g.join(" "))
|
|
38957
39705
|
return "it is the goal text";
|
|
38958
|
-
|
|
39706
|
+
const { spans, prose } = goalValueSpans(goal);
|
|
39707
|
+
if (spans.some((q) => words2(q).join(" ") === flat))
|
|
38959
39708
|
return null;
|
|
39709
|
+
const valueWords = new Set(spans.flatMap(words2));
|
|
39710
|
+
const instructionWords = new Set(words2(prose).filter((w) => !valueWords.has(w) && !ECHO_STOP_WORDS.has(w)));
|
|
39711
|
+
const goalFlat = ` ${g.join(" ")} `;
|
|
38960
39712
|
for (let i = 0; i < v.length; i++) {
|
|
38961
39713
|
let j = i;
|
|
38962
39714
|
while (j < v.length && goalFlat.includes(` ${v.slice(i, j + 1).join(" ")} `))
|
|
38963
39715
|
j++;
|
|
38964
|
-
const
|
|
38965
|
-
|
|
39716
|
+
const runWords = v.slice(i, j);
|
|
39717
|
+
const run = runWords.join(" ");
|
|
39718
|
+
const instructional = new Set(runWords.filter((w) => instructionWords.has(w))).size >= 2;
|
|
39719
|
+
if (j - i >= 5 && run.length >= GOAL_ECHO_RUN_CHARS && instructional)
|
|
38966
39720
|
return `it copies the goal ("${run.slice(0, 60)}")`;
|
|
38967
39721
|
}
|
|
38968
39722
|
const vs = new Set(v.filter((w) => !ECHO_STOP_WORDS.has(w)));
|
|
38969
39723
|
if (vs.size >= ECHO_MIN_WORDS) {
|
|
38970
|
-
const gs =
|
|
39724
|
+
const gs = instructionWords;
|
|
38971
39725
|
const overlap = [...vs].filter((w) => gs.has(w)).length / vs.size;
|
|
38972
39726
|
if (overlap > GOAL_ECHO_OVERLAP)
|
|
38973
39727
|
return `it restates the goal (${Math.round(overlap * 100)}% of its words are the goal's)`;
|
|
@@ -38976,7 +39730,7 @@ function echoesGoal(value, goal) {
|
|
|
38976
39730
|
}
|
|
38977
39731
|
var INSTRUCTION_MIN_WORDS = 4;
|
|
38978
39732
|
var INSTRUCTION_COVERAGE = 0.8;
|
|
38979
|
-
var INSTRUCTION_VERBS = new Set(
|
|
39733
|
+
var INSTRUCTION_VERBS = new Set(INSTRUCTION_VERB_LIST.split(" "));
|
|
38980
39734
|
var CLAUSE_OPENERS = /* @__PURE__ */ new Set(["and", "then", "also", "please", "next", "finally", "first"]);
|
|
38981
39735
|
var VALUE_MARKERS = /* @__PURE__ */ new Set([":", "named", "called", "titled", "saying", "says", "reading", "reads", "text", "message", "content", "description", "title", "label", "value", "as"]);
|
|
38982
39736
|
function copiesGoalInstruction(value, goal) {
|
|
@@ -38984,9 +39738,10 @@ function copiesGoalInstruction(value, goal) {
|
|
|
38984
39738
|
if (v.length < INSTRUCTION_MIN_WORDS)
|
|
38985
39739
|
return null;
|
|
38986
39740
|
const flatValue = v.join(" ");
|
|
38987
|
-
|
|
39741
|
+
const { spans, prose: instructionProse } = goalValueSpans(goal);
|
|
39742
|
+
if (spans.some((q) => ` ${words2(q).join(" ")} `.includes(` ${flatValue} `)))
|
|
38988
39743
|
return null;
|
|
38989
|
-
const prose =
|
|
39744
|
+
const prose = instructionProse.toLowerCase().match(/[\p{L}\p{N}$]+(?:['’][\p{L}]+)?|:/gu) ?? [];
|
|
38990
39745
|
const runAt = (i) => {
|
|
38991
39746
|
let best = { start: -1, len: 0 };
|
|
38992
39747
|
for (let s3 = 0; s3 < prose.length; s3++) {
|
|
@@ -39055,6 +39810,13 @@ var FieldValueLog = class _FieldValueLog {
|
|
|
39055
39810
|
#used = /* @__PURE__ */ new Map();
|
|
39056
39811
|
/** What the latest submit sent, per field — undone by a reload (#184). */
|
|
39057
39812
|
#lastBatch = /* @__PURE__ */ new Map();
|
|
39813
|
+
/**
|
|
39814
|
+
* #366: the values submitted into each field's CURRENT instance — forgotten when the field left
|
|
39815
|
+
* the page and came back empty (a confirm dialog closed and reopened): a new instance holds none.
|
|
39816
|
+
*/
|
|
39817
|
+
#live = /* @__PURE__ */ new Map();
|
|
39818
|
+
/** #366: fields with live values that were absent from the latest page view. */
|
|
39819
|
+
#gone = /* @__PURE__ */ new Set();
|
|
39058
39820
|
static key(label2) {
|
|
39059
39821
|
return bareLabel(label2).toLowerCase();
|
|
39060
39822
|
}
|
|
@@ -39068,27 +39830,54 @@ var FieldValueLog = class _FieldValueLog {
|
|
|
39068
39830
|
this.#lastBatch.clear();
|
|
39069
39831
|
for (const [k, v] of this.#pending) {
|
|
39070
39832
|
this.#lastBatch.set(k, v);
|
|
39071
|
-
const
|
|
39072
|
-
|
|
39073
|
-
list.
|
|
39074
|
-
|
|
39833
|
+
for (const memory of [this.#used, this.#live]) {
|
|
39834
|
+
const list = memory.get(k) ?? [];
|
|
39835
|
+
if (!list.some((u) => sameValue(u, v)))
|
|
39836
|
+
list.push(v);
|
|
39837
|
+
memory.set(k, list);
|
|
39838
|
+
}
|
|
39839
|
+
this.#gone.delete(k);
|
|
39075
39840
|
}
|
|
39076
39841
|
this.#pending.clear();
|
|
39077
39842
|
}
|
|
39843
|
+
/**
|
|
39844
|
+
* #366: the text fields on the page now (label and current value). A field with submitted values
|
|
39845
|
+
* that left the page and is back EMPTY is a new instance (a dialog reopened, a form remounted):
|
|
39846
|
+
* its live values are forgotten, so the same value may be typed into it again. `used` keeps them.
|
|
39847
|
+
*/
|
|
39848
|
+
observe(fields) {
|
|
39849
|
+
const present2 = /* @__PURE__ */ new Map();
|
|
39850
|
+
for (const f of fields) {
|
|
39851
|
+
const k = _FieldValueLog.key(f.label);
|
|
39852
|
+
present2.set(k, (present2.get(k) ?? false) || (f.value ?? "").trim() === "");
|
|
39853
|
+
}
|
|
39854
|
+
for (const k of [...this.#live.keys()]) {
|
|
39855
|
+
const empty = present2.get(k);
|
|
39856
|
+
if (empty === void 0)
|
|
39857
|
+
this.#gone.add(k);
|
|
39858
|
+
else if (this.#gone.has(k)) {
|
|
39859
|
+
this.#gone.delete(k);
|
|
39860
|
+
if (empty)
|
|
39861
|
+
this.#live.delete(k);
|
|
39862
|
+
}
|
|
39863
|
+
}
|
|
39864
|
+
}
|
|
39078
39865
|
/**
|
|
39079
39866
|
* The page was reloaded (#184): the last submit did not take, so what it sent is a retry, not a
|
|
39080
39867
|
* used item — retyping the same value is allowed again.
|
|
39081
39868
|
*/
|
|
39082
39869
|
reloaded() {
|
|
39083
|
-
for (const
|
|
39084
|
-
const
|
|
39085
|
-
|
|
39086
|
-
|
|
39087
|
-
|
|
39088
|
-
|
|
39089
|
-
|
|
39090
|
-
|
|
39091
|
-
|
|
39870
|
+
for (const memory of [this.#used, this.#live]) {
|
|
39871
|
+
for (const [k, list] of memory) {
|
|
39872
|
+
const last = this.#lastBatch.get(k);
|
|
39873
|
+
if (last === void 0)
|
|
39874
|
+
continue;
|
|
39875
|
+
const rest = list.filter((u) => !sameValue(u, last));
|
|
39876
|
+
if (rest.length === 0)
|
|
39877
|
+
memory.delete(k);
|
|
39878
|
+
else
|
|
39879
|
+
memory.set(k, rest);
|
|
39880
|
+
}
|
|
39092
39881
|
}
|
|
39093
39882
|
this.#lastBatch.clear();
|
|
39094
39883
|
this.#pending.clear();
|
|
@@ -39097,6 +39886,10 @@ var FieldValueLog = class _FieldValueLog {
|
|
|
39097
39886
|
used(label2) {
|
|
39098
39887
|
return this.#used.get(_FieldValueLog.key(label2)) ?? [];
|
|
39099
39888
|
}
|
|
39889
|
+
/** #366: the values already submitted into `label`'s current instance (see `observe`), oldest first. */
|
|
39890
|
+
liveUsed(label2) {
|
|
39891
|
+
return this.#live.get(_FieldValueLog.key(label2)) ?? [];
|
|
39892
|
+
}
|
|
39100
39893
|
};
|
|
39101
39894
|
var VALUE_END = String.raw`(?=\s*(?:[,;\n]|\s\.|\.(?:\s|$)|\s+(?:and|then)\s)|\s*$)`;
|
|
39102
39895
|
var WORD_VALUE_END = String.raw`(?=\s*(?:[,;\n]|\s\.|\.(?:\s|$)|\s+(?:and|then|to|in|into|on|for|with)\s)|\s*$)`;
|
|
@@ -39189,6 +39982,12 @@ function typedPassages(goal) {
|
|
|
39189
39982
|
return [...goal.matchAll(re)].map((m) => (m[1] ?? "").trim()).filter((p) => /\n/.test(p) || words2(p).length >= PASSAGE_MIN_WORDS);
|
|
39190
39983
|
}
|
|
39191
39984
|
var PASSAGE_MIN_WORDS = 6;
|
|
39985
|
+
function promptAsksFor(value, texts) {
|
|
39986
|
+
const verb = String.raw`\b(?:type|enter|write|input)\s+(?:in\s+)?(?:the\s+(?:word|text|phrase|name|code)\s+)?`;
|
|
39987
|
+
const quoted = new RegExp(String.raw`${verb}["“'‘\u0060]([^"”'’\u0060\n]{1,80})["”'’\u0060]`, "giu");
|
|
39988
|
+
const bare = new RegExp(String.raw`${verb}([^\s"“”'‘’\u0060]{1,80}?)[.,:;!]?(?=\s+(?:to|below|here|in|into|and|if|for|then)\b|[^\S\n]*(?:\n|$)|[.,:;!]\s)`, "giu");
|
|
39989
|
+
return texts.some((t) => [...t.matchAll(quoted), ...t.matchAll(bare)].some((m) => sameValue(m[1] ?? "", value)));
|
|
39990
|
+
}
|
|
39192
39991
|
var CONTEXT_CEILING = 4e3;
|
|
39193
39992
|
var FillHelper = class {
|
|
39194
39993
|
gen;
|
|
@@ -39232,6 +40031,17 @@ var FillHelper = class {
|
|
|
39232
40031
|
};
|
|
39233
40032
|
const several = goalListsSeveral(input2.goal);
|
|
39234
40033
|
const isUsed = (v) => used.some((u) => sameValue(u, v));
|
|
40034
|
+
const live = req.liveUsed === void 0 ? used : req.liveUsed.map((u) => redactContext(u, secrets).slice(0, 200));
|
|
40035
|
+
const repeats = (v) => {
|
|
40036
|
+
if (!isUsed(v))
|
|
40037
|
+
return false;
|
|
40038
|
+
if (promptAsksFor(v, [input2.fieldLabel, redactContext(req.prompt ?? "", secrets)]))
|
|
40039
|
+
return false;
|
|
40040
|
+
if (live.some((u) => sameValue(u, v)))
|
|
40041
|
+
return true;
|
|
40042
|
+
const stated = field3 === void 0 ? [] : valuesStatedInGoal(input2.goal, input2.fieldLabel, field3);
|
|
40043
|
+
return stated.some((s3) => sameValue(s3, v)) && stated.some((s3) => !isUsed(s3));
|
|
40044
|
+
};
|
|
39235
40045
|
if (field3 !== void 0) {
|
|
39236
40046
|
const stated = valuesStatedInGoal(input2.goal, input2.fieldLabel, field3);
|
|
39237
40047
|
const pick3 = several ? stated.find((v) => !isUsed(v)) : stated.length === 1 ? stated[0] : void 0;
|
|
@@ -39252,7 +40062,7 @@ var FillHelper = class {
|
|
|
39252
40062
|
};
|
|
39253
40063
|
const text2 = generated !== null && identity !== null && checkFieldValue(generated, field3, input2.fieldLabel, input2.goal) === null && !copied(generated) ? uniqueIdentity(generated, identity, this.#identityToken) : generated;
|
|
39254
40064
|
if (field3 !== void 0 && text2 !== null) {
|
|
39255
|
-
const rejected = checkFieldValue(text2, field3, input2.fieldLabel, input2.goal) ?? (several &&
|
|
40065
|
+
const rejected = checkFieldValue(text2, field3, input2.fieldLabel, input2.goal) ?? (several && repeats(text2) ? `repeats ${JSON.stringify(text2.trim().slice(0, 80))}, already submitted into this field \u2014 the goal lists several items: use the next one` : null);
|
|
39256
40066
|
if (rejected !== null)
|
|
39257
40067
|
return { text: null, rejected, source: "model" };
|
|
39258
40068
|
const value = field3.tag === "input" ? inWireFormat(field3, text2.trim()) : text2;
|
|
@@ -41199,6 +42009,26 @@ var SaveProgress = class {
|
|
|
41199
42009
|
|
|
41200
42010
|
// ../explore/dist/side-effects.js
|
|
41201
42011
|
import { createHash as createHash3 } from "node:crypto";
|
|
42012
|
+
var BOOKKEEPING_RPC = /^(?:(?:Record|Track|Log|Report|Send|Emit|Capture|Ingest)\w*(?:Event|Events|Analytics|Telemetry|Metric|Metrics|Impression|Impressions|PageView|PageViews)|Mark\w*(?:Read|Seen|Viewed|Opened)|Ping|Heartbeat|KeepAlive)$/;
|
|
42013
|
+
var BOOKKEEPING_SEGMENT = /^(?:analytics|telemetry|beacons?|metrics|rum)$/i;
|
|
42014
|
+
var READ_MARKER_SEGMENT = /^(?:mark[-_]?(?:as[-_]?)?)?(?:read|seen|viewed)$/i;
|
|
42015
|
+
function isBookkeepingRequest(r) {
|
|
42016
|
+
if (r.resourceType === "ping")
|
|
42017
|
+
return true;
|
|
42018
|
+
const rpc = rpcMethodOf(r.path);
|
|
42019
|
+
if (rpc !== null)
|
|
42020
|
+
return BOOKKEEPING_RPC.test(rpc.method);
|
|
42021
|
+
const segments3 = pathOf4(r.path).split("/").filter((x) => x !== "");
|
|
42022
|
+
if (segments3.some((x) => BOOKKEEPING_SEGMENT.test(x)))
|
|
42023
|
+
return true;
|
|
42024
|
+
const last = segments3[segments3.length - 1];
|
|
42025
|
+
return last !== void 0 && segments3.length > 1 && READ_MARKER_SEGMENT.test(last);
|
|
42026
|
+
}
|
|
42027
|
+
function screenState(controls, visibleText) {
|
|
42028
|
+
const offered = JSON.stringify(controls.map((c) => [c.role, c.name, c.enabled]));
|
|
42029
|
+
return digest(`${offered}
|
|
42030
|
+
${visibleText.replace(/\s+/g, " ").replace(/\d+/g, "#").trim()}`);
|
|
42031
|
+
}
|
|
41202
42032
|
function requestSignature(method, path2) {
|
|
41203
42033
|
return `${method.toUpperCase()} ${urlTemplate(path2)}`;
|
|
41204
42034
|
}
|
|
@@ -41235,9 +42065,12 @@ var SideEffectGuard = class {
|
|
|
41235
42065
|
this.#firstParty = opts.firstParty ?? new FirstPartyOrigins(this.#origins);
|
|
41236
42066
|
this.#ignored = opts.ignoreRequests ?? (() => false);
|
|
41237
42067
|
}
|
|
41238
|
-
/**
|
|
41239
|
-
|
|
41240
|
-
|
|
42068
|
+
/**
|
|
42069
|
+
* Is this request the app's own side effect (#274/#284/#374): first-party, not declared
|
|
42070
|
+
* background, and not bookkeeping (analytics, a beacon, a read marker)?
|
|
42071
|
+
*/
|
|
42072
|
+
#ours(r) {
|
|
42073
|
+
return this.#firstParty.thirdParty(r.url) === null && !this.#ignored(r.url) && !isBookkeepingRequest({ path: pathOf4(r.url), ...r.resourceType === void 0 ? {} : { resourceType: r.resourceType } });
|
|
41241
42074
|
}
|
|
41242
42075
|
/** How a request is named (#194): path on an allowed origin, else origin + path. */
|
|
41243
42076
|
#name(url2) {
|
|
@@ -41252,8 +42085,9 @@ var SideEffectGuard = class {
|
|
|
41252
42085
|
/**
|
|
41253
42086
|
* A click on `id` (a bare string = an element with no known context) is about to be dispatched:
|
|
41254
42087
|
* watch what it sends. `label` only names it in reasons and drives the sign-in / back rules.
|
|
42088
|
+
* `state` (#380) is its region's state as it is clicked (`screenState`), when the caller read it.
|
|
41255
42089
|
*/
|
|
41256
|
-
beginClick(id, label2, route, at3) {
|
|
42090
|
+
beginClick(id, label2, route, at3, state) {
|
|
41257
42091
|
const identity = identityOf(id);
|
|
41258
42092
|
this.#closeOpen();
|
|
41259
42093
|
if (BACK_NAME.test(label2)) {
|
|
@@ -41261,14 +42095,19 @@ var SideEffectGuard = class {
|
|
|
41261
42095
|
if (f.route === route)
|
|
41262
42096
|
this.#fired.delete(k);
|
|
41263
42097
|
}
|
|
41264
|
-
this.#open = { key: recordKey(route, identity), element: identity.element, label: label2, route, at: at3, capture: this.#monitor.startCapture(), values: this.#valuesKey() };
|
|
42098
|
+
this.#open = { key: recordKey(route, identity), element: identity.element, label: label2, route, at: at3, capture: this.#monitor.startCapture(), values: this.#valuesKey(), ...state === void 0 ? {} : { before: state } };
|
|
41265
42099
|
}
|
|
41266
42100
|
/**
|
|
41267
42101
|
* The click's window closes (the next perception has settled the page): the writes it fired are
|
|
41268
|
-
* the ones that finished since, plus those still in flight that started after it.
|
|
42102
|
+
* the ones that finished since, plus those still in flight that started after it. `state` (#380)
|
|
42103
|
+
* is the clicked control's region state the window closed on, when the caller read it.
|
|
41269
42104
|
*/
|
|
41270
|
-
settle() {
|
|
41271
|
-
this.#closeOpen();
|
|
42105
|
+
settle(state) {
|
|
42106
|
+
this.#closeOpen(state);
|
|
42107
|
+
}
|
|
42108
|
+
/** Whether a click's window is open (the caller reads the region state for `settle` only then). */
|
|
42109
|
+
clickOpen() {
|
|
42110
|
+
return this.#open !== null;
|
|
41272
42111
|
}
|
|
41273
42112
|
/**
|
|
41274
42113
|
* The most recently closed click (#130a): did it send ANY request (of any method), by the time its
|
|
@@ -41277,7 +42116,7 @@ var SideEffectGuard = class {
|
|
|
41277
42116
|
lastClick() {
|
|
41278
42117
|
return this.#lastClick;
|
|
41279
42118
|
}
|
|
41280
|
-
#closeOpen() {
|
|
42119
|
+
#closeOpen(after) {
|
|
41281
42120
|
const o = this.#open;
|
|
41282
42121
|
if (o === null)
|
|
41283
42122
|
return;
|
|
@@ -41286,18 +42125,25 @@ var SideEffectGuard = class {
|
|
|
41286
42125
|
const requests = o.capture.requests();
|
|
41287
42126
|
const unfinished = this.#monitor.unfinished();
|
|
41288
42127
|
const inflightAny = unfinished.some((r) => r.startedAt >= o.at);
|
|
41289
|
-
const done = requests.filter((r) => this.#write(r) && this.#ours(r
|
|
41290
|
-
|
|
41291
|
-
path: this.#name(r.url),
|
|
41292
|
-
|
|
41293
|
-
|
|
41294
|
-
}));
|
|
41295
|
-
const inflight = unfinished.filter((r) => r.startedAt >= o.at && this.#write({ ...r, path: pathOf4(r.url) }) && this.#ours(r.url));
|
|
42128
|
+
const done = requests.filter((r) => this.#write(r) && this.#ours(r)).map((r) => {
|
|
42129
|
+
const status = effectiveStatus(r);
|
|
42130
|
+
return { method: r.method.toUpperCase(), path: this.#name(r.url), status, rejected: status !== null && status >= 400 || status === null && r.failed };
|
|
42131
|
+
});
|
|
42132
|
+
const inflight = unfinished.filter((r) => r.startedAt >= o.at && this.#write({ ...r, path: pathOf4(r.url) }) && this.#ours(r));
|
|
41296
42133
|
const pending = inflight.map((r) => ({ method: r.method.toUpperCase(), path: this.#name(r.url), status: null, rejected: false }));
|
|
41297
42134
|
this.#lastClick = { requestSent: requests.length > 0 || inflightAny, writes: [...done, ...pending] };
|
|
41298
42135
|
if (done.length + pending.length === 0)
|
|
41299
42136
|
return;
|
|
41300
|
-
this.#fired.set(o.key, {
|
|
42137
|
+
this.#fired.set(o.key, {
|
|
42138
|
+
label: o.label,
|
|
42139
|
+
route: o.route,
|
|
42140
|
+
element: o.element,
|
|
42141
|
+
writes: [...done, ...pending],
|
|
42142
|
+
inflight,
|
|
42143
|
+
values: o.values,
|
|
42144
|
+
...o.before === void 0 ? {} : { before: o.before },
|
|
42145
|
+
...after === void 0 ? {} : { after }
|
|
42146
|
+
});
|
|
41301
42147
|
}
|
|
41302
42148
|
/** Writes fired by this run's clicks that are still in flight now. */
|
|
41303
42149
|
inflight() {
|
|
@@ -41336,7 +42182,10 @@ var SideEffectGuard = class {
|
|
|
41336
42182
|
}
|
|
41337
42183
|
/**
|
|
41338
42184
|
* May `key` be clicked on `route` now? `page` is what the page shows: its control names and alerts
|
|
41339
|
-
* (a visible retry affordance, or an error alert, re-allows it).
|
|
42185
|
+
* (a visible retry affordance, or an error alert, re-allows it). `state` (#380) is the control's region state
|
|
42186
|
+
* now (`screenState`): one that differs from both the state the earlier click was made in and the
|
|
42187
|
+
* one its window closed on has moved on, and re-allows a click whose writes all finished. Omit it
|
|
42188
|
+
* (a paid / destructive control) and only the rules above apply.
|
|
41340
42189
|
*/
|
|
41341
42190
|
check(id, route, page) {
|
|
41342
42191
|
const identity = identityOf(id);
|
|
@@ -41367,11 +42216,15 @@ var SideEffectGuard = class {
|
|
|
41367
42216
|
return { refuse: false };
|
|
41368
42217
|
if (f.values !== this.#valuesKey())
|
|
41369
42218
|
return { refuse: false };
|
|
42219
|
+
const known = page.state !== void 0 && f.before !== void 0 && f.after !== void 0;
|
|
42220
|
+
if (known && page.state !== f.before && page.state !== f.after && f.writes.every((w) => w.status !== null))
|
|
42221
|
+
return { refuse: false };
|
|
42222
|
+
const unchanged = known ? " (its part of the page has not moved on since)" : "";
|
|
41370
42223
|
const retyped = this.#values.size > 0 ? " (the inputs hold the same values as when it was sent \u2014 nothing new would be submitted)" : "";
|
|
41371
42224
|
return {
|
|
41372
42225
|
refuse: true,
|
|
41373
42226
|
inflight: false,
|
|
41374
|
-
reason: `repeated side effect refused: "${f.label}" already sent ${what} on this page and the page does not offer a retry \u2014 clicking it again would repeat that action${retyped}`
|
|
42227
|
+
reason: `repeated side effect refused: "${f.label}" already sent ${what} on this page and the page does not offer a retry \u2014 clicking it again would repeat that action${retyped}${unchanged}`
|
|
41375
42228
|
};
|
|
41376
42229
|
}
|
|
41377
42230
|
};
|
|
@@ -41458,7 +42311,7 @@ var SideEffectLog = class {
|
|
|
41458
42311
|
continue;
|
|
41459
42312
|
const m = this.#owner(r.startedAt);
|
|
41460
42313
|
if (m !== void 0)
|
|
41461
|
-
push(m, r.method, r.url, r
|
|
42314
|
+
push(m, r.method, r.url, effectiveStatus(r), r.startedAt ?? m.at);
|
|
41462
42315
|
}
|
|
41463
42316
|
for (const r of monitor.unfinished()) {
|
|
41464
42317
|
const path2 = pathOf4(r.url);
|
|
@@ -42608,265 +43461,6 @@ function demoOverlayFor(enabled, secrets) {
|
|
|
42608
43461
|
return enabled === true ? new DemoOverlay(secrets) : null;
|
|
42609
43462
|
}
|
|
42610
43463
|
|
|
42611
|
-
// ../explore/dist/status.js
|
|
42612
|
-
var EMPTY_STATUS = { alerts: [], notices: [], invalid: [] };
|
|
42613
|
-
var STATUS_CHARS = 200;
|
|
42614
|
-
var STATUS_ITEMS = 5;
|
|
42615
|
-
function readStatusInPage(limits) {
|
|
42616
|
-
const norm5 = (s3) => (s3 ?? "").replace(/\s+/g, " ").trim();
|
|
42617
|
-
const cut3 = (s3) => s3.length > limits.chars ? `${s3.slice(0, limits.chars)}\u2026` : s3;
|
|
42618
|
-
const shown = (el) => {
|
|
42619
|
-
const h = el;
|
|
42620
|
-
if (h.closest("[hidden],[aria-hidden=true]") !== null)
|
|
42621
|
-
return false;
|
|
42622
|
-
const st = window.getComputedStyle(h);
|
|
42623
|
-
const r = h.getBoundingClientRect();
|
|
42624
|
-
return st.display !== "none" && st.visibility !== "hidden" && r.width > 0 && r.height > 0;
|
|
42625
|
-
};
|
|
42626
|
-
const w = window;
|
|
42627
|
-
if (w.__jevInvalid === void 0) {
|
|
42628
|
-
const seen = /* @__PURE__ */ new WeakSet();
|
|
42629
|
-
w.__jevInvalid = seen;
|
|
42630
|
-
document.addEventListener("invalid", (e) => {
|
|
42631
|
-
if (e.target instanceof Element)
|
|
42632
|
-
seen.add(e.target);
|
|
42633
|
-
}, true);
|
|
42634
|
-
}
|
|
42635
|
-
const refused = w.__jevInvalid;
|
|
42636
|
-
const alerts = [];
|
|
42637
|
-
const notices = [];
|
|
42638
|
-
const live = document.querySelectorAll("[role=alert],[role=status],[aria-live=polite],[aria-live=assertive],output");
|
|
42639
|
-
for (const el of Array.from(live)) {
|
|
42640
|
-
if (!shown(el))
|
|
42641
|
-
continue;
|
|
42642
|
-
const text2 = cut3(norm5(el.innerText));
|
|
42643
|
-
if (text2 === "")
|
|
42644
|
-
continue;
|
|
42645
|
-
if ([...alerts, ...notices].some((a) => a.includes(text2) || text2.includes(a)))
|
|
42646
|
-
continue;
|
|
42647
|
-
const assertive = el.getAttribute("role") === "alert" || el.getAttribute("aria-live") === "assertive";
|
|
42648
|
-
const into = assertive ? alerts : notices;
|
|
42649
|
-
if (into.length < limits.items)
|
|
42650
|
-
into.push(text2);
|
|
42651
|
-
}
|
|
42652
|
-
const labelOf2 = (el) => {
|
|
42653
|
-
const aria = norm5(el.getAttribute("aria-label"));
|
|
42654
|
-
if (aria !== "")
|
|
42655
|
-
return aria;
|
|
42656
|
-
const labels = el.labels;
|
|
42657
|
-
if (labels && labels.length > 0)
|
|
42658
|
-
return norm5(labels[0].textContent);
|
|
42659
|
-
const owner = el.closest("label");
|
|
42660
|
-
if (owner !== null)
|
|
42661
|
-
return norm5(owner.textContent);
|
|
42662
|
-
return norm5(el.getAttribute("placeholder")) || norm5(el.getAttribute("name")) || el.tagName.toLowerCase();
|
|
42663
|
-
};
|
|
42664
|
-
const describedBy = (el) => {
|
|
42665
|
-
const ids = `${el.getAttribute("aria-errormessage") ?? ""} ${el.getAttribute("aria-describedby") ?? ""}`.trim();
|
|
42666
|
-
if (ids === "")
|
|
42667
|
-
return "";
|
|
42668
|
-
return norm5(ids.split(/\s+/).map((id) => document.getElementById(id)).filter((n3) => n3 !== null && shown(n3)).map((n3) => n3.innerText).join(" "));
|
|
42669
|
-
};
|
|
42670
|
-
const invalid = [];
|
|
42671
|
-
const fields = document.querySelectorAll("input:not([type=hidden]),select,textarea,[aria-invalid=true]");
|
|
42672
|
-
for (const el of Array.from(fields)) {
|
|
42673
|
-
if (!shown(el))
|
|
42674
|
-
continue;
|
|
42675
|
-
const f = el;
|
|
42676
|
-
const ariaInvalid = norm5(el.getAttribute("aria-invalid")).toLowerCase() === "true";
|
|
42677
|
-
const nativeInvalid = typeof f.checkValidity === "function" && f.validity !== void 0 && !f.validity.valid;
|
|
42678
|
-
const reached2 = refused.has(el) || typeof f.value === "string" && f.value !== "";
|
|
42679
|
-
if (!ariaInvalid && !(nativeInvalid && reached2))
|
|
42680
|
-
continue;
|
|
42681
|
-
const message2 = norm5(nativeInvalid ? f.validationMessage : "") || describedBy(el) || "marked invalid";
|
|
42682
|
-
invalid.push({ name: cut3(labelOf2(el)), message: cut3(message2) });
|
|
42683
|
-
if (invalid.length >= limits.items)
|
|
42684
|
-
break;
|
|
42685
|
-
}
|
|
42686
|
-
return { alerts, notices, invalid };
|
|
42687
|
-
}
|
|
42688
|
-
async function readPageStatus(page) {
|
|
42689
|
-
return page.evaluate(readStatusInPage, { chars: STATUS_CHARS, items: STATUS_ITEMS }).catch(() => EMPTY_STATUS);
|
|
42690
|
-
}
|
|
42691
|
-
function statusDelta(before, now2) {
|
|
42692
|
-
const alerts = now2.alerts.filter((a) => !before.alerts.includes(a));
|
|
42693
|
-
const notices = now2.notices.filter((a) => !before.notices.includes(a));
|
|
42694
|
-
const invalid = now2.invalid.filter((f) => !before.invalid.some((b2) => b2.name === f.name && b2.message === f.message));
|
|
42695
|
-
return { alerts, notices, invalid };
|
|
42696
|
-
}
|
|
42697
|
-
function describeStatus(s3) {
|
|
42698
|
-
return [
|
|
42699
|
-
...s3.alerts.map((a) => `alert "${a}"`),
|
|
42700
|
-
...s3.notices.map((a) => `status "${a}"`),
|
|
42701
|
-
...s3.invalid.map((f) => `invalid field "${f.name}": "${f.message}"`)
|
|
42702
|
-
].join("; ");
|
|
42703
|
-
}
|
|
42704
|
-
function isEmptyStatus(s3) {
|
|
42705
|
-
return s3.alerts.length === 0 && s3.notices.length === 0 && s3.invalid.length === 0;
|
|
42706
|
-
}
|
|
42707
|
-
var IN_PROGRESS_WORDS = "simulating|processing|running|generating|drafting|fetching|rendering|publishing|analy[sz]ing|computing|calculating|loading|preparing|working|thinking|uploading|importing|exporting|syncing|saving|submitting|creating|building|training|indexing|queued|in progress";
|
|
42708
|
-
function inProgressInPage(words3) {
|
|
42709
|
-
const norm5 = (s3) => (s3 ?? "").replace(/\s+/g, " ").trim();
|
|
42710
|
-
const cut3 = (s3) => s3.length > 80 ? `${s3.slice(0, 80)}\u2026` : s3;
|
|
42711
|
-
const shown = (el) => {
|
|
42712
|
-
const h = el;
|
|
42713
|
-
if (h.closest("[hidden],[aria-hidden=true]") !== null)
|
|
42714
|
-
return false;
|
|
42715
|
-
const st = window.getComputedStyle(h);
|
|
42716
|
-
const r = h.getBoundingClientRect();
|
|
42717
|
-
return st.display !== "none" && st.visibility !== "hidden" && r.width > 0 && r.height > 0;
|
|
42718
|
-
};
|
|
42719
|
-
const loose = new RegExp(`\\b(?:${words3})\\b`, "i");
|
|
42720
|
-
const trailing = new RegExp(`^\\W*(?:${words3})\\b[^.!?]{0,60}(?:\u2026|\\.\\.\\.)\\s*$`, "i");
|
|
42721
|
-
const job = /\b(?:is|are)\s+(?:still\s+|already\s+|currently\s+|now\s+)?(?:running|processing|in progress|being (?:processed|generated|simulated|prepared))\b|\bstill (?:running|working|processing)\b|^\W*in progress\W*$/i;
|
|
42722
|
-
for (const el of Array.from(document.querySelectorAll('[aria-busy="true"]'))) {
|
|
42723
|
-
if (shown(el))
|
|
42724
|
-
return `aria-busy region${norm5(el.innerText) === "" ? "" : ` "${cut3(norm5(el.innerText))}"`}`;
|
|
42725
|
-
}
|
|
42726
|
-
const live = document.querySelectorAll("[role=status],[aria-live=polite],[aria-live=assertive],[role=progressbar],output");
|
|
42727
|
-
for (const el of Array.from(live)) {
|
|
42728
|
-
if (!shown(el))
|
|
42729
|
-
continue;
|
|
42730
|
-
const text2 = norm5(el.innerText || el.getAttribute("aria-valuetext") || el.getAttribute("aria-label"));
|
|
42731
|
-
if (text2 !== "" && loose.test(text2))
|
|
42732
|
-
return `status "${cut3(text2)}"`;
|
|
42733
|
-
if (text2 !== "" && /^\W*[a-z]{3,}ing\b[^.!?]{0,60}(?:…|\.\.\.)\s*$/i.test(text2))
|
|
42734
|
-
return `status "${cut3(text2)}"`;
|
|
42735
|
-
}
|
|
42736
|
-
const body = document.body ? document.body.innerText : "";
|
|
42737
|
-
for (const raw of body.split(/\n+/)) {
|
|
42738
|
-
const line = norm5(raw);
|
|
42739
|
-
if (line === "" || line.length > 200)
|
|
42740
|
-
continue;
|
|
42741
|
-
if (trailing.test(line) || job.test(line))
|
|
42742
|
-
return `status text "${cut3(line)}"`;
|
|
42743
|
-
}
|
|
42744
|
-
return null;
|
|
42745
|
-
}
|
|
42746
|
-
async function readInProgressStatus(page) {
|
|
42747
|
-
return page.evaluate(inProgressInPage, IN_PROGRESS_WORDS).catch(() => null);
|
|
42748
|
-
}
|
|
42749
|
-
function workAcknowledgedInPage(words3) {
|
|
42750
|
-
const norm5 = (s3) => (s3 ?? "").replace(/\s+/g, " ").trim();
|
|
42751
|
-
const shown = (el) => {
|
|
42752
|
-
const h = el;
|
|
42753
|
-
if (h.closest("[hidden],[aria-hidden=true]") !== null)
|
|
42754
|
-
return false;
|
|
42755
|
-
const st = window.getComputedStyle(h);
|
|
42756
|
-
const r = h.getBoundingClientRect();
|
|
42757
|
-
return st.display !== "none" && st.visibility !== "hidden" && r.width > 0 && r.height > 0;
|
|
42758
|
-
};
|
|
42759
|
-
const label2 = (el) => el instanceof HTMLInputElement ? norm5(el.value) : norm5(el.getAttribute("aria-label") ?? el.innerText ?? el.textContent);
|
|
42760
|
-
const disabled = (el) => el.disabled === true || el.getAttribute("aria-disabled") === "true";
|
|
42761
|
-
const controls = Array.from(document.querySelectorAll("button,[role=button],input[type=button],input[type=submit]")).filter(shown);
|
|
42762
|
-
const progress = new RegExp(`^\\W*(?:${words3})\\b[^.!?]{0,60}(?:\u2026|\\.\\.\\.)?\\s*$`, "i");
|
|
42763
|
-
let cancel2 = null;
|
|
42764
|
-
let busyControl = null;
|
|
42765
|
-
for (const el of controls) {
|
|
42766
|
-
const n3 = label2(el);
|
|
42767
|
-
if (cancel2 === null && !disabled(el) && /^\W*(?:cancel|stop|abort)\b/i.test(n3))
|
|
42768
|
-
cancel2 = `an enabled "${n3.slice(0, 40)}" control`;
|
|
42769
|
-
if (busyControl === null && disabled(el) && n3 !== "" && progress.test(n3))
|
|
42770
|
-
busyControl = `a disabled "${n3.slice(0, 40)}" control`;
|
|
42771
|
-
}
|
|
42772
|
-
let bar = null;
|
|
42773
|
-
for (const el of Array.from(document.querySelectorAll("[role=progressbar][aria-valuenow],progress[value]"))) {
|
|
42774
|
-
if (!shown(el))
|
|
42775
|
-
continue;
|
|
42776
|
-
const now2 = Number(el.getAttribute("aria-valuenow") ?? el.value);
|
|
42777
|
-
const max = Number(el.getAttribute("aria-valuemax") ?? el.max) || 100;
|
|
42778
|
-
if (Number.isFinite(now2) && now2 < max) {
|
|
42779
|
-
bar = `a progress bar at ${Math.round(now2 / max * 100)}%`;
|
|
42780
|
-
break;
|
|
42781
|
-
}
|
|
42782
|
-
}
|
|
42783
|
-
return { busyControl, cancel: cancel2, bar };
|
|
42784
|
-
}
|
|
42785
|
-
async function readWorkingStatus(page) {
|
|
42786
|
-
const ack = await page.evaluate(workAcknowledgedInPage, IN_PROGRESS_WORDS).catch(() => null);
|
|
42787
|
-
if (ack === null)
|
|
42788
|
-
return null;
|
|
42789
|
-
if (ack.busyControl !== null)
|
|
42790
|
-
return ack.cancel === null ? ack.busyControl : `${ack.busyControl} and ${ack.cancel}`;
|
|
42791
|
-
const extra = ack.cancel ?? ack.bar;
|
|
42792
|
-
if (extra === null)
|
|
42793
|
-
return null;
|
|
42794
|
-
const status = await readInProgressStatus(page);
|
|
42795
|
-
return status === null ? null : `${status} with ${extra}`;
|
|
42796
|
-
}
|
|
42797
|
-
var MAX_DOCUMENTED_WAIT_MS = 30 * 6e4;
|
|
42798
|
-
var NUMBER_WORDS = {
|
|
42799
|
-
a: 1,
|
|
42800
|
-
an: 1,
|
|
42801
|
-
one: 1,
|
|
42802
|
-
two: 2,
|
|
42803
|
-
three: 3,
|
|
42804
|
-
four: 4,
|
|
42805
|
-
five: 5,
|
|
42806
|
-
six: 6,
|
|
42807
|
-
seven: 7,
|
|
42808
|
-
eight: 8,
|
|
42809
|
-
nine: 9,
|
|
42810
|
-
ten: 10,
|
|
42811
|
-
fifteen: 15,
|
|
42812
|
-
twenty: 20,
|
|
42813
|
-
thirty: 30,
|
|
42814
|
-
forty: 40,
|
|
42815
|
-
sixty: 60,
|
|
42816
|
-
ninety: 90,
|
|
42817
|
-
"a couple of": 3,
|
|
42818
|
-
"a couple": 3,
|
|
42819
|
-
"a few": 5,
|
|
42820
|
-
few: 5,
|
|
42821
|
-
several: 10
|
|
42822
|
-
};
|
|
42823
|
-
var UNIT_MS = [
|
|
42824
|
-
[/^(?:seconds?|secs?|s)$/i, 1e3],
|
|
42825
|
-
[/^(?:minutes?|mins?|m)$/i, 6e4],
|
|
42826
|
-
[/^(?:hours?|hrs?|h)$/i, 36e5]
|
|
42827
|
-
];
|
|
42828
|
-
var WAIT_TALK = /\b(?:take|takes|taking|took|wait|waiting|ready|done|finish|finishes|complete|completes|usually|typically|normally|should|may|might|can|up to|less than|under|within|about|around|roughly|approximately|expect)\b/i;
|
|
42829
|
-
var DURATION = /\b(\d+(?:\.\d+)?|an?|one|two|three|four|five|six|seven|eight|nine|ten|fifteen|twenty|thirty|forty|sixty|ninety|a couple(?: of)?|a few|few|several)(?:\s*(?:-|–|to)\s*(\d+(?:\.\d+)?))?\s*(seconds?|secs?|minutes?|mins?|hours?|hrs?)\b/gi;
|
|
42830
|
-
function documentedWaitMs(line) {
|
|
42831
|
-
if (!WAIT_TALK.test(line) || /\bago\b/i.test(line))
|
|
42832
|
-
return null;
|
|
42833
|
-
let longest = null;
|
|
42834
|
-
for (const m of line.matchAll(DURATION)) {
|
|
42835
|
-
const [, first, upper, unit] = m;
|
|
42836
|
-
const per = UNIT_MS.find(([re]) => re.test(unit ?? ""))?.[1];
|
|
42837
|
-
if (per === void 0 || first === void 0)
|
|
42838
|
-
continue;
|
|
42839
|
-
const n3 = Number(upper ?? first) || NUMBER_WORDS[first.toLowerCase()] || NaN;
|
|
42840
|
-
if (!Number.isFinite(n3) || n3 <= 0)
|
|
42841
|
-
continue;
|
|
42842
|
-
longest = Math.max(longest ?? 0, n3 * per);
|
|
42843
|
-
}
|
|
42844
|
-
return longest === null ? null : Math.min(longest, MAX_DOCUMENTED_WAIT_MS);
|
|
42845
|
-
}
|
|
42846
|
-
function durationLinesInPage() {
|
|
42847
|
-
const body = document.body ? document.body.innerText : "";
|
|
42848
|
-
const out = [];
|
|
42849
|
-
for (const raw of body.split(/\n+/)) {
|
|
42850
|
-
const line = raw.replace(/\s+/g, " ").trim();
|
|
42851
|
-
if (line === "" || line.length > 300)
|
|
42852
|
-
continue;
|
|
42853
|
-
if (/\b(?:seconds?|secs?|minutes?|mins?|hours?|hrs?)\b/i.test(line))
|
|
42854
|
-
out.push(line);
|
|
42855
|
-
if (out.length >= 10)
|
|
42856
|
-
break;
|
|
42857
|
-
}
|
|
42858
|
-
return out;
|
|
42859
|
-
}
|
|
42860
|
-
async function readDocumentedWait(page) {
|
|
42861
|
-
const lines = await page.evaluate(durationLinesInPage).catch(() => []);
|
|
42862
|
-
for (const line of lines) {
|
|
42863
|
-
const ms = documentedWaitMs(line);
|
|
42864
|
-
if (ms !== null)
|
|
42865
|
-
return { text: line.length > 120 ? `${line.slice(0, 120)}\u2026` : line, ms };
|
|
42866
|
-
}
|
|
42867
|
-
return null;
|
|
42868
|
-
}
|
|
42869
|
-
|
|
42870
43464
|
// ../explore/dist/read-only.js
|
|
42871
43465
|
var CHANGE_VERBS = /\b(?:create|add|save|submit|send|post|publish|update|edit|modify|rename|delete|remove|revoke|rotate|buy|purchase|upgrade|downgrade|subscribe|unsubscribe|invite|enable|disable|activate|deactivate|sign ?up|register|archive|restore|import|upload|transfer|assign|ask|reply|fill (?:in|out))\b/i;
|
|
42872
43466
|
var ASKED_ABOUT = /\b(?:how (?:do|can|would|should|to) (?:i |you |we |users? |one )?|how to |whether (?:i |you |we )?(?:can |could )?|(?:what happens |what if )?(?:if|when|after|before) (?:i |you |we )?|(?:can|could) (?:i|you|we) )\w+/gi;
|
|
@@ -43139,6 +43733,60 @@ function buildCrashReport(failure3, signals, heapSamples, opts = {}) {
|
|
|
43139
43733
|
};
|
|
43140
43734
|
}
|
|
43141
43735
|
|
|
43736
|
+
// ../explore/dist/loop-cycle.js
|
|
43737
|
+
var CYCLE_REPEATS = 4;
|
|
43738
|
+
var CYCLE_MAX_PERIOD = 2;
|
|
43739
|
+
var LoopCycleDetector = class {
|
|
43740
|
+
repeats;
|
|
43741
|
+
maxPeriod;
|
|
43742
|
+
#window = [];
|
|
43743
|
+
constructor(repeats = CYCLE_REPEATS, maxPeriod = CYCLE_MAX_PERIOD) {
|
|
43744
|
+
this.repeats = repeats;
|
|
43745
|
+
this.maxPeriod = maxPeriod;
|
|
43746
|
+
if (!Number.isInteger(repeats) || repeats < 2)
|
|
43747
|
+
throw new Error(`LoopCycleDetector repeats must be an integer >= 2, got ${String(repeats)}`);
|
|
43748
|
+
if (!Number.isInteger(maxPeriod) || maxPeriod < 1)
|
|
43749
|
+
throw new Error(`LoopCycleDetector maxPeriod must be a positive integer, got ${String(maxPeriod)}`);
|
|
43750
|
+
}
|
|
43751
|
+
/** The steps the current window holds (since the last progress). */
|
|
43752
|
+
get size() {
|
|
43753
|
+
return this.#window.length;
|
|
43754
|
+
}
|
|
43755
|
+
/** Forget the window (a step made progress, or the run's situation changed). */
|
|
43756
|
+
reset() {
|
|
43757
|
+
this.#window = [];
|
|
43758
|
+
}
|
|
43759
|
+
/** Records one executed step; returns the cycle once the window is one, else null. */
|
|
43760
|
+
note(step) {
|
|
43761
|
+
if (step.progress) {
|
|
43762
|
+
this.reset();
|
|
43763
|
+
return null;
|
|
43764
|
+
}
|
|
43765
|
+
const span = this.repeats * this.maxPeriod;
|
|
43766
|
+
this.#window.push(step);
|
|
43767
|
+
if (this.#window.length > span)
|
|
43768
|
+
this.#window.splice(0, this.#window.length - span);
|
|
43769
|
+
if (this.#window.length < span)
|
|
43770
|
+
return null;
|
|
43771
|
+
const labels = /* @__PURE__ */ new Map();
|
|
43772
|
+
const states = /* @__PURE__ */ new Set();
|
|
43773
|
+
for (const s3 of this.#window) {
|
|
43774
|
+
if (!labels.has(s3.action))
|
|
43775
|
+
labels.set(s3.action, s3.label);
|
|
43776
|
+
states.add(s3.state);
|
|
43777
|
+
}
|
|
43778
|
+
if (labels.size > this.maxPeriod || states.size > this.maxPeriod)
|
|
43779
|
+
return null;
|
|
43780
|
+
if (labels.size === 1 && states.size === 1)
|
|
43781
|
+
return null;
|
|
43782
|
+
return { labels: [...labels.values()], states: states.size, period: Math.max(labels.size, states.size), steps: span };
|
|
43783
|
+
}
|
|
43784
|
+
};
|
|
43785
|
+
function describeCycle(v, repeats = CYCLE_REPEATS) {
|
|
43786
|
+
const loop = v.labels.length === 1 ? `${v.labels[0]} over and over` : v.labels.join(" \u2194 ");
|
|
43787
|
+
return `no progress: the run went round a loop \u2014 ${loop} \u2014 ${repeats} times (${v.steps} steps between the same ${v.states} page states, no request sent, nothing new on the page)`;
|
|
43788
|
+
}
|
|
43789
|
+
|
|
43142
43790
|
// ../explore/dist/action-delta.js
|
|
43143
43791
|
var DELTA_SNAPSHOT_TIMEOUT_MS = 1500;
|
|
43144
43792
|
var VOLATILITY_GAP_MS = 1e3;
|
|
@@ -44080,6 +44728,9 @@ var stateBesides = (snap, key2) => JSON.stringify([snap.url, snap.controls.map((
|
|
|
44080
44728
|
var MAX_EARLY_BLOCKED_REFUSALS = 2;
|
|
44081
44729
|
var JOB_WAIT_SLICE_MS = 6e4;
|
|
44082
44730
|
async function waitOutJob(page, budgetMs, stillWorking = async (p) => await readInProgressStatus(p) !== null) {
|
|
44731
|
+
return monitorFor(page).explicitWait(() => sitOutJob(page, budgetMs, stillWorking));
|
|
44732
|
+
}
|
|
44733
|
+
async function sitOutJob(page, budgetMs, stillWorking) {
|
|
44083
44734
|
const started = clock.now();
|
|
44084
44735
|
const url2 = safeUrl(page);
|
|
44085
44736
|
let delay = 1e3;
|
|
@@ -44138,6 +44789,24 @@ var actionIdentityOf = (c) => ({
|
|
|
44138
44789
|
element: keyOf(c),
|
|
44139
44790
|
context: JSON.stringify([c.form ?? null, c.container ?? null, c.scope ?? null, c.heading ?? null])
|
|
44140
44791
|
});
|
|
44792
|
+
function regionOf(el) {
|
|
44793
|
+
const REGION = 'dialog,[role=dialog],[role=alertdialog],form,[role=form],[role=region],[role=group],[role=tabpanel],section,article,li,tr,[role=row],[role=listitem],[class*="card" i],[data-card]';
|
|
44794
|
+
const LANDMARK = "main,[role=main]";
|
|
44795
|
+
const region = el.parentElement?.closest(REGION) ?? el.closest(LANDMARK);
|
|
44796
|
+
if (region === null || region === void 0 || region === document.body || region === document.documentElement)
|
|
44797
|
+
return null;
|
|
44798
|
+
const CONTROL = "a[href],button,input,select,textarea,summary,[role=button],[role=link],[role=tab],[role=checkbox],[role=switch],[role=menuitem],[role=option]";
|
|
44799
|
+
const controls = [...region.querySelectorAll(CONTROL)].filter((c) => c.offsetParent !== null || c.getClientRects().length > 0).map((c) => {
|
|
44800
|
+
const h = c;
|
|
44801
|
+
const name = (c.getAttribute("aria-label") ?? (h.type === "submit" || h.type === "button" ? h.value : "") ?? "") || (c.innerText ?? c.textContent ?? "");
|
|
44802
|
+
return { role: c.getAttribute("role") ?? c.tagName.toLowerCase(), name: name.replace(/\s+/g, " ").trim(), enabled: !h.disabled && c.getAttribute("aria-disabled") !== "true" };
|
|
44803
|
+
});
|
|
44804
|
+
return { text: region.innerText ?? region.textContent ?? "", controls };
|
|
44805
|
+
}
|
|
44806
|
+
async function readRegionState(page, control, secrets) {
|
|
44807
|
+
const region = await descriptorToLocator(page, control.descriptor).first().evaluate(regionOf, void 0, { timeout: 1e3 }).catch(() => null);
|
|
44808
|
+
return region === null ? void 0 : screenState(region.controls, redactPageText(region.text, secrets));
|
|
44809
|
+
}
|
|
44141
44810
|
function noReply(r) {
|
|
44142
44811
|
const s3 = Math.round(r.waitedMs / 1e3);
|
|
44143
44812
|
if (r.endedBy === "ceiling")
|
|
@@ -44259,6 +44928,9 @@ async function createRunContext(cfg) {
|
|
|
44259
44928
|
ctx.movingScrolls = 0;
|
|
44260
44929
|
ctx.movingScrollsSignature = null;
|
|
44261
44930
|
ctx.scrollStreakSignatures = /* @__PURE__ */ new Set();
|
|
44931
|
+
ctx.cycles = new LoopCycleDetector();
|
|
44932
|
+
ctx.cycleAction = null;
|
|
44933
|
+
ctx.cycleMark = clock.now();
|
|
44262
44934
|
ctx.lastChanceGiven = false;
|
|
44263
44935
|
ctx.lastChanceTurn = false;
|
|
44264
44936
|
ctx.fixtureAttached = false;
|
|
@@ -44293,6 +44965,7 @@ async function createRunContext(cfg) {
|
|
|
44293
44965
|
ctx.lastReportNotFound = false;
|
|
44294
44966
|
ctx.lastAbsenceUncovered = null;
|
|
44295
44967
|
ctx.settledClick = null;
|
|
44968
|
+
ctx.clickedRegion = null;
|
|
44296
44969
|
ctx.wroteOk = false;
|
|
44297
44970
|
ctx.writeGoal = goalAsksToWrite(cfg.goal);
|
|
44298
44971
|
ctx.vetoes = new VetoedAnswers();
|
|
@@ -44310,6 +44983,7 @@ async function createRunContext(cfg) {
|
|
|
44310
44983
|
ctx.idleSteps = 0;
|
|
44311
44984
|
ctx.idleSince = null;
|
|
44312
44985
|
ctx.busyWaitedMs = 0;
|
|
44986
|
+
ctx.replyWaitedMs = 0;
|
|
44313
44987
|
ctx.awaitingReply = false;
|
|
44314
44988
|
ctx.lastTurn = null;
|
|
44315
44989
|
ctx.turnWrites = /* @__PURE__ */ new Set();
|
|
@@ -44334,13 +45008,17 @@ async function createRunContext(cfg) {
|
|
|
44334
45008
|
ctx.scrollsSinceMutation = 0;
|
|
44335
45009
|
ctx.blockers = {
|
|
44336
45010
|
failClosed: null,
|
|
44337
|
-
target: null
|
|
45011
|
+
target: null,
|
|
45012
|
+
latest: null
|
|
44338
45013
|
};
|
|
44339
45014
|
ctx.blockingCause = () => {
|
|
44340
45015
|
if (ctx.blockers.failClosed !== null)
|
|
44341
45016
|
return ctx.blockers.failClosed;
|
|
44342
|
-
|
|
44343
|
-
|
|
45017
|
+
const { target, latest } = ctx.blockers;
|
|
45018
|
+
if (latest !== null && (target === null || latest.step > target.step))
|
|
45019
|
+
return latest.text;
|
|
45020
|
+
if (target !== null)
|
|
45021
|
+
return target.text;
|
|
44344
45022
|
const alert = ctx.status.alerts[0];
|
|
44345
45023
|
if (alert !== void 0)
|
|
44346
45024
|
return `the page shows alert ${quote(alert)}`;
|
|
@@ -44356,12 +45034,23 @@ async function createRunContext(cfg) {
|
|
|
44356
45034
|
if (r !== "target not enabled" && r !== "target not visible")
|
|
44357
45035
|
return r;
|
|
44358
45036
|
const name = quote(c.name || c.summary, 120);
|
|
44359
|
-
ctx.blockers.target = { key: keyOf(c), text: `${r === "target not enabled" ? "target disabled" : "target not visible"} \u2014 ${name}
|
|
45037
|
+
ctx.blockers.target = { key: keyOf(c), text: `${r === "target not enabled" ? "target disabled" : "target not visible"} \u2014 ${name}`, step: ctx.transcript.nextStep };
|
|
44360
45038
|
return r === "target not enabled" ? `${r}: ${name} is disabled \u2014 its label may say what it needs first` : `${r}: ${name}`;
|
|
44361
45039
|
};
|
|
44362
45040
|
ctx.cleared = (c) => {
|
|
44363
45041
|
if (ctx.blockers.target?.key === keyOf(c))
|
|
44364
45042
|
ctx.blockers.target = null;
|
|
45043
|
+
if (ctx.blockers.latest?.key === keyOf(c))
|
|
45044
|
+
ctx.blockers.latest = null;
|
|
45045
|
+
};
|
|
45046
|
+
ctx.noteFailure = (op, c, reason, refused) => {
|
|
45047
|
+
const why = (reason ?? "?").replace(/^typed value rejected:\s*/, "");
|
|
45048
|
+
ctx.blockers.latest = {
|
|
45049
|
+
key: keyOf(c),
|
|
45050
|
+
text: `${op} ${quote(c.name || c.summary, 80)} ${refused ? "rejected" : "failed"}: ${quote(why, 200).slice(1, -1)}`,
|
|
45051
|
+
reason: reason ?? "?",
|
|
45052
|
+
step: ctx.transcript.nextStep
|
|
45053
|
+
};
|
|
44365
45054
|
};
|
|
44366
45055
|
ctx.noteFailedAct = async (c, reason) => {
|
|
44367
45056
|
const v = ctx.failedActs.fail(keyOf(c), quote(c.name || c.summary, 80), reason ?? "?");
|
|
@@ -44456,6 +45145,7 @@ async function createRunContext(cfg) {
|
|
|
44456
45145
|
ctx.jobWaitMs = cfg.jobWaitMs ?? ctx.replyCeilingMs;
|
|
44457
45146
|
ctx.jobWaitExplicit = cfg.jobWaitMs !== void 0;
|
|
44458
45147
|
ctx.jobWaitedMs = 0;
|
|
45148
|
+
ctx.overlayWaitedMs = 0;
|
|
44459
45149
|
ctx.hangWorkWaitedMs = 0;
|
|
44460
45150
|
ctx.documentedBudgetMs = 0;
|
|
44461
45151
|
ctx.noteMutation = (label2, descriptor, before, at3, input2, linkFromRoute = null, clickFromRoute = null) => {
|
|
@@ -44484,6 +45174,8 @@ async function createRunContext(cfg) {
|
|
|
44484
45174
|
}
|
|
44485
45175
|
|
|
44486
45176
|
// ../explore/dist/goal-loop/step.js
|
|
45177
|
+
var TARGET_OPS = /* @__PURE__ */ new Set(["click", "type", "send", "select", "upload", "edit_text"]);
|
|
45178
|
+
var CYCLE_OPS = /* @__PURE__ */ new Set([...TARGET_OPS, "scroll_up", "scroll_down", "reload"]);
|
|
44487
45179
|
function newStep(ctx, input2) {
|
|
44488
45180
|
const { cfg } = ctx;
|
|
44489
45181
|
const { snap, perception, decision } = input2;
|
|
@@ -44492,6 +45184,14 @@ function newStep(ctx, input2) {
|
|
|
44492
45184
|
const target = extra.control === void 0 ? decision.control : extra.control;
|
|
44493
45185
|
if (!actOk && extra.origin === "engine")
|
|
44494
45186
|
ctx.refusedSinceMutation += 1;
|
|
45187
|
+
if (!actOk && target !== null && TARGET_OPS.has(op))
|
|
45188
|
+
ctx.noteFailure(op, target, reason, extra.origin === "engine");
|
|
45189
|
+
if (CYCLE_OPS.has(op)) {
|
|
45190
|
+
ctx.cycleAction = {
|
|
45191
|
+
action: `${op} ${target === null ? "" : keyOf(target)} ${actOk ? "ok" : "failed"}`,
|
|
45192
|
+
label: target === null ? op.replace("_", " ") : `${op} ${quote(target.name || target.summary, 60)}`
|
|
45193
|
+
};
|
|
45194
|
+
}
|
|
44495
45195
|
if (op === "type" || op === "send")
|
|
44496
45196
|
ctx.auth.noteTyped(target, snap.url, actOk, target !== null && ctx.isBound(target));
|
|
44497
45197
|
if ((op === "type" || op === "send") && actOk && target !== null && (ctx.isBound(target) || isCredentialField(target)))
|
|
@@ -44586,11 +45286,11 @@ async function handleReport(ctx, step) {
|
|
|
44586
45286
|
const { cfg } = ctx;
|
|
44587
45287
|
const { snap, record: record2 } = step;
|
|
44588
45288
|
if (ctx.replyGoal) {
|
|
44589
|
-
if (ctx.awaitingReply && ctx.lastTurn !== null && ctx.
|
|
45289
|
+
if (ctx.awaitingReply && ctx.lastTurn !== null && ctx.replyWaitedMs < ctx.replyWaitMs) {
|
|
44590
45290
|
const t0 = ctx.now();
|
|
44591
|
-
const listen = ctx.replyWaitMs - ctx.
|
|
45291
|
+
const listen = ctx.replyWaitMs - ctx.replyWaitedMs;
|
|
44592
45292
|
const reply = await waitForReply(ctx.page, { secrets: ctx.secrets, ...ctx.lastTurn, timeoutMs: listen, ceilingMs: listen, quietMs: ctx.replyQuietMs });
|
|
44593
|
-
ctx.
|
|
45293
|
+
ctx.replyWaitedMs += ctx.now() - t0;
|
|
44594
45294
|
if (reply.received) {
|
|
44595
45295
|
ctx.conversation.latestReply = reply.text;
|
|
44596
45296
|
ctx.replies.add(snap.url, reply.text);
|
|
@@ -44752,7 +45452,7 @@ async function handleBlocked(ctx, step) {
|
|
|
44752
45452
|
return "continue";
|
|
44753
45453
|
}
|
|
44754
45454
|
const untried = modelControls.filter((c) => c.enabled);
|
|
44755
|
-
if (ctx.actionAttempts === 0 && untried.length > 0 && ctx.reportRejections === 0) {
|
|
45455
|
+
if (ctx.actionAttempts === 0 && untried.length > 0 && ctx.reportRejections === 0 && !await (ctx.cfg.successChecksHoldNow?.() ?? Promise.resolve(false)).catch(() => false)) {
|
|
44756
45456
|
ctx.earlyBlocked += 1;
|
|
44757
45457
|
if (ctx.earlyBlocked <= MAX_EARLY_BLOCKED_REFUSALS) {
|
|
44758
45458
|
const nav = [...untried.filter((c) => (c.landmark ?? null) !== null), ...untried.filter((c) => (c.landmark ?? null) === null)];
|
|
@@ -44782,11 +45482,14 @@ async function handleWaitOrScroll(ctx, step) {
|
|
|
44782
45482
|
const { snap, decision, record: record2 } = step;
|
|
44783
45483
|
let changed;
|
|
44784
45484
|
let note;
|
|
44785
|
-
if (decision.op === "wait" && ctx.awaitingReply && ctx.lastTurn !== null
|
|
45485
|
+
if (decision.op === "wait" && ctx.awaitingReply && ctx.lastTurn !== null) {
|
|
45486
|
+
const left = ctx.replyCeilingMs - ctx.replyWaitedMs;
|
|
45487
|
+
if (left <= 0)
|
|
45488
|
+
return replyCeilingSpent(ctx, step, false);
|
|
44786
45489
|
const t0 = ctx.now();
|
|
44787
|
-
const listen = Math.min(
|
|
45490
|
+
const listen = Math.min(left, 2e4);
|
|
44788
45491
|
const reply = await waitForReply(ctx.page, { secrets: ctx.secrets, ...ctx.lastTurn, timeoutMs: listen, ceilingMs: listen, quietMs: ctx.replyQuietMs });
|
|
44789
|
-
ctx.
|
|
45492
|
+
ctx.replyWaitedMs += ctx.now() - t0;
|
|
44790
45493
|
if (reply.received) {
|
|
44791
45494
|
ctx.conversation.latestReply = reply.text;
|
|
44792
45495
|
ctx.replies.add(snap.url, reply.text);
|
|
@@ -44798,6 +45501,10 @@ async function handleWaitOrScroll(ctx, step) {
|
|
|
44798
45501
|
changed = !idle;
|
|
44799
45502
|
ctx.quietWaits = idle ? ctx.quietWaits + 1 : 0;
|
|
44800
45503
|
record2(true, note, reply.received ? { reply } : {});
|
|
45504
|
+
if (!reply.received && ctx.replyWaitedMs >= ctx.replyCeilingMs) {
|
|
45505
|
+
ctx.history.push(note);
|
|
45506
|
+
return replyCeilingSpent(ctx, step, true);
|
|
45507
|
+
}
|
|
44801
45508
|
} else if (decision.op === "wait" && ctx.jobWaitedMs < ctx.jobWaitMs && await readInProgressStatus(ctx.page) !== null) {
|
|
44802
45509
|
const job = await readInProgressStatus(ctx.page) ?? "an in-progress status";
|
|
44803
45510
|
const w = await waitOutJob(ctx.page, Math.min(ctx.jobWaitMs - ctx.jobWaitedMs, JOB_WAIT_SLICE_MS));
|
|
@@ -44868,6 +45575,17 @@ async function handleWaitOrScroll(ctx, step) {
|
|
|
44868
45575
|
}
|
|
44869
45576
|
return "continue";
|
|
44870
45577
|
}
|
|
45578
|
+
function replyCeilingSpent(ctx, step, recorded) {
|
|
45579
|
+
const reason = `stuck: the reply wait is spent \u2014 ${Math.round(ctx.replyWaitedMs / 1e3)}s waited in total for the reply, the ${Math.round(ctx.replyCeilingMs / 1e3)}s reply ceiling (raise --reply-ceiling-ms for a slower reply)`;
|
|
45580
|
+
if (!recorded)
|
|
45581
|
+
step.record(false, reason, { origin: "engine" });
|
|
45582
|
+
ctx.history.push(reason);
|
|
45583
|
+
ctx.lastActedOp = step.decision.op;
|
|
45584
|
+
ctx.statusAfter = "waiting";
|
|
45585
|
+
ctx.incomplete = reason;
|
|
45586
|
+
ctx.stop = "no-progress";
|
|
45587
|
+
return "stop";
|
|
45588
|
+
}
|
|
44871
45589
|
|
|
44872
45590
|
// ../explore/dist/goal-loop/handle-reload.js
|
|
44873
45591
|
async function handleReload(ctx, step) {
|
|
@@ -45276,7 +45994,8 @@ async function handleMessage(ctx, step, op, forcedNote) {
|
|
|
45276
45994
|
ctx.replies.add(snap.url, reply.text);
|
|
45277
45995
|
}
|
|
45278
45996
|
ctx.awaitingReply = !reply.received;
|
|
45279
|
-
ctx.busyWaitedMs =
|
|
45997
|
+
ctx.busyWaitedMs = 0;
|
|
45998
|
+
ctx.replyWaitedMs = reply.waitedMs;
|
|
45280
45999
|
ctx.lastTurn = { baseline, sent: message2, sentAt: at3, background: sendBackground };
|
|
45281
46000
|
ctx.offerBaseline = before;
|
|
45282
46001
|
ctx.history.push(`sent ${quote(message2)} via ${via?.kind === "click" ? `"${via.control.name}"` : "Enter"} \u2192 ` + (reply.received ? `reply: ${quote(reply.text, 300)}` : noReply(reply)));
|
|
@@ -45415,10 +46134,14 @@ async function handleClick(ctx, step) {
|
|
|
45415
46134
|
const quickReply = ctx.offeredKeys.has(keyOf(control)) && control.role === "button" && control.name.length <= 60 && !/[→›»]/.test(control.name);
|
|
45416
46135
|
const turn = submits || quickReply;
|
|
45417
46136
|
const identity = actionIdentityOf(control);
|
|
45418
|
-
const
|
|
45419
|
-
|
|
45420
|
-
|
|
45421
|
-
|
|
46137
|
+
const pageNow = { controlNames: snap.controls.map((c) => c.name), alerts: ctx.status.alerts };
|
|
46138
|
+
let repeat = ctx.sideEffects.check(identity, safePath(snap.url), pageNow);
|
|
46139
|
+
const stateless = ctx.safety.riskOf(control) !== null;
|
|
46140
|
+
const readState = () => stateless ? Promise.resolve(void 0) : readRegionState(ctx.page, control, ctx.secrets);
|
|
46141
|
+
if (repeat.refuse && !repeat.inflight && !stateless) {
|
|
46142
|
+
const state = await readState();
|
|
46143
|
+
repeat = ctx.sideEffects.check(identity, safePath(snap.url), { ...pageNow, ...state === void 0 ? {} : { state } });
|
|
46144
|
+
}
|
|
45422
46145
|
if (repeat.refuse) {
|
|
45423
46146
|
let note = repeat.reason;
|
|
45424
46147
|
if (repeat.inflight) {
|
|
@@ -45431,7 +46154,9 @@ async function handleClick(ctx, step) {
|
|
|
45431
46154
|
return "continue";
|
|
45432
46155
|
}
|
|
45433
46156
|
const baseline = turn ? await readPageText(ctx.page, ctx.secrets) : "";
|
|
45434
|
-
|
|
46157
|
+
const before = await readState();
|
|
46158
|
+
ctx.clickedRegion = readState;
|
|
46159
|
+
ctx.sideEffects.beginClick(identity, control.name || control.summary, safePath(snap.url), ctx.now(), before);
|
|
45435
46160
|
const r = await act(cfg.actor, { op: "click", control });
|
|
45436
46161
|
let reply;
|
|
45437
46162
|
let message2;
|
|
@@ -45476,7 +46201,8 @@ async function handleClick(ctx, step) {
|
|
|
45476
46201
|
ctx.replies.add(snap.url, reply.text);
|
|
45477
46202
|
}
|
|
45478
46203
|
ctx.awaitingReply = !reply.received;
|
|
45479
|
-
ctx.busyWaitedMs =
|
|
46204
|
+
ctx.busyWaitedMs = 0;
|
|
46205
|
+
ctx.replyWaitedMs = reply.waitedMs;
|
|
45480
46206
|
ctx.lastTurn = { baseline, sent: message2, sentAt: at3, background: turnBackground };
|
|
45481
46207
|
ctx.offerBaseline = new Set(keys.keys());
|
|
45482
46208
|
ctx.history.push(`clicked ${control.name}${quickReply ? " (a quick reply)" : ""} \u2192 ` + (reply.received ? `reply: ${quote(reply.text, 300)}` : noReply(reply)));
|
|
@@ -45520,7 +46246,14 @@ async function handleFill(ctx, step) {
|
|
|
45520
46246
|
secrets: ctx.secrets,
|
|
45521
46247
|
// A text field's value is field-scoped and checked before it is typed (#71); in an
|
|
45522
46248
|
// add-another flow it is the next item, not one already submitted into this field (#123).
|
|
45523
|
-
...decision.op === "type" ? {
|
|
46249
|
+
...decision.op === "type" ? {
|
|
46250
|
+
field: { tag: control.tag, inputType: control.inputType },
|
|
46251
|
+
alreadyUsed: ctx.valueLog.used(control.name || control.summary),
|
|
46252
|
+
// #366: only a repeat into this same live field is refused (a reopened dialog's field is new),
|
|
46253
|
+
// and never a literal the field's own prompt asks for ("Type CONFIRM to continue").
|
|
46254
|
+
liveUsed: ctx.valueLog.liveUsed(control.name || control.summary),
|
|
46255
|
+
prompt: [control.scope, control.heading, step.visibleText].filter((t) => typeof t === "string" && t !== "").join("\n")
|
|
46256
|
+
} : {}
|
|
45524
46257
|
}));
|
|
45525
46258
|
} catch (e) {
|
|
45526
46259
|
const reason = `value generation unavailable: ${firstLine2(e)}`;
|
|
@@ -45622,7 +46355,9 @@ async function perceiveStep(ctx) {
|
|
|
45622
46355
|
ctx.history.push(dialogHistoryLine(d));
|
|
45623
46356
|
ctx.transcript.attachDialogs(ctx.transcript.nextStep - 1, dialogs);
|
|
45624
46357
|
}
|
|
45625
|
-
ctx.
|
|
46358
|
+
const region = ctx.clickedRegion;
|
|
46359
|
+
ctx.clickedRegion = null;
|
|
46360
|
+
ctx.sideEffects.settle(ctx.sideEffects.clickOpen() && region !== null ? await region() : void 0);
|
|
45626
46361
|
{
|
|
45627
46362
|
const lc = ctx.sideEffects.lastClick();
|
|
45628
46363
|
if (lc !== null && lc !== ctx.settledClick) {
|
|
@@ -45813,6 +46548,43 @@ async function checkSettled(ctx, step) {
|
|
|
45813
46548
|
}
|
|
45814
46549
|
}
|
|
45815
46550
|
if (!perception.rendered) {
|
|
46551
|
+
const overlay = perception.hang === null ? await busyOverlay(ctx.page) : null;
|
|
46552
|
+
if (overlay !== null && ctx.overlayWaitedMs < ctx.jobWaitMs) {
|
|
46553
|
+
const w = await waitOutJob(ctx.page, Math.min(ctx.jobWaitMs - ctx.overlayWaitedMs, JOB_WAIT_SLICE_MS), async (p) => await busyOverlay(p) !== null);
|
|
46554
|
+
ctx.overlayWaitedMs += clock.now() - step.perceiveStartedAt;
|
|
46555
|
+
const note = `no visible control while the page shows ${overlay} \u2014 the app is still working; waited ${(w.waitedMs / 1e3).toFixed(1)}s (${w.cleared ? "the overlay cleared" : `still in progress; ${Math.round(ctx.overlayWaitedMs / 1e3)}s of the ${Math.round(ctx.jobWaitMs / 1e3)}s job-wait budget used`})`;
|
|
46556
|
+
ctx.history.push(note);
|
|
46557
|
+
ctx.transcript.record({
|
|
46558
|
+
op: "wait",
|
|
46559
|
+
control: null,
|
|
46560
|
+
confidence: null,
|
|
46561
|
+
chosenBy: "strategy",
|
|
46562
|
+
strategy: "job-wait",
|
|
46563
|
+
actOk: true,
|
|
46564
|
+
reason: note,
|
|
46565
|
+
snapshot: snap,
|
|
46566
|
+
timing: perception.timing
|
|
46567
|
+
});
|
|
46568
|
+
return "continue";
|
|
46569
|
+
}
|
|
46570
|
+
if (overlay !== null) {
|
|
46571
|
+
const reason = `stuck: the page still shows ${overlay} (no visible control) after ${Math.round(ctx.overlayWaitedMs / 1e3)}s of waiting \u2014 past the ${Math.round(ctx.jobWaitMs / 1e3)}s job-wait budget (a status that never completes); raise --job-wait-ms if this job legitimately takes longer`;
|
|
46572
|
+
ctx.transcript.record({
|
|
46573
|
+
op: "wait",
|
|
46574
|
+
control: null,
|
|
46575
|
+
confidence: null,
|
|
46576
|
+
chosenBy: "strategy",
|
|
46577
|
+
strategy: "job-wait",
|
|
46578
|
+
actOk: false,
|
|
46579
|
+
reason,
|
|
46580
|
+
snapshot: snap,
|
|
46581
|
+
timing: perception.timing
|
|
46582
|
+
});
|
|
46583
|
+
ctx.history.push(reason);
|
|
46584
|
+
ctx.incomplete = reason;
|
|
46585
|
+
ctx.stop = "no-progress";
|
|
46586
|
+
return "stop";
|
|
46587
|
+
}
|
|
45816
46588
|
ctx.transcript.record({
|
|
45817
46589
|
op: "wait",
|
|
45818
46590
|
control: null,
|
|
@@ -45826,10 +46598,12 @@ async function checkSettled(ctx, step) {
|
|
|
45826
46598
|
ctx.stop = "blocked";
|
|
45827
46599
|
return "stop";
|
|
45828
46600
|
}
|
|
46601
|
+
ctx.overlayWaitedMs = 0;
|
|
45829
46602
|
return "next";
|
|
45830
46603
|
}
|
|
45831
46604
|
|
|
45832
46605
|
// ../explore/dist/goal-loop/progress.js
|
|
46606
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
45833
46607
|
async function checkProgress(ctx, step) {
|
|
45834
46608
|
const { cfg } = ctx;
|
|
45835
46609
|
const { snap } = step;
|
|
@@ -45842,11 +46616,12 @@ async function checkProgress(ctx, step) {
|
|
|
45842
46616
|
}
|
|
45843
46617
|
ctx.statusAfter = null;
|
|
45844
46618
|
}
|
|
45845
|
-
const
|
|
45846
|
-
|
|
46619
|
+
const scrolled = ctx.lastActedOp === "scroll_down" || ctx.lastActedOp === "scroll_up";
|
|
46620
|
+
const scrolledMoved = scrolled && ctx.lastScrollMoved;
|
|
46621
|
+
if (!scrolled) {
|
|
45847
46622
|
ctx.movingScrolls = 0;
|
|
45848
46623
|
ctx.scrollStreakSignatures.clear();
|
|
45849
|
-
} else {
|
|
46624
|
+
} else if (scrolledMoved) {
|
|
45850
46625
|
if (ctx.scrollStreakSignatures.size === 0 && ctx.movingScrollsSignature !== null)
|
|
45851
46626
|
ctx.scrollStreakSignatures.add(ctx.movingScrollsSignature);
|
|
45852
46627
|
if (ctx.scrollStreakSignatures.has(snap.signature))
|
|
@@ -45920,11 +46695,36 @@ async function checkProgress(ctx, step) {
|
|
|
45920
46695
|
return "stop";
|
|
45921
46696
|
}
|
|
45922
46697
|
}
|
|
46698
|
+
const cycle = await noteCycle(ctx, snap);
|
|
46699
|
+
if (cycle !== null) {
|
|
46700
|
+
ctx.history.push(cycle);
|
|
46701
|
+
ctx.incomplete = cycle;
|
|
46702
|
+
ctx.stop = "no-progress";
|
|
46703
|
+
return "stop";
|
|
46704
|
+
}
|
|
45923
46705
|
if (ctx.noProgress.streak === 0)
|
|
45924
46706
|
ctx.lastChanceGiven = false;
|
|
45925
46707
|
ctx.seen.add(snap.signature);
|
|
45926
46708
|
return "next";
|
|
45927
46709
|
}
|
|
46710
|
+
async function noteCycle(ctx, snap) {
|
|
46711
|
+
const step = ctx.cycleAction;
|
|
46712
|
+
ctx.cycleAction = null;
|
|
46713
|
+
const mark = ctx.cycleMark;
|
|
46714
|
+
ctx.cycleMark = ctx.now();
|
|
46715
|
+
if (step === null)
|
|
46716
|
+
return null;
|
|
46717
|
+
const monitor = monitorFor(ctx.page);
|
|
46718
|
+
const background = backgroundEndpoints(monitor, mark, ctx.turnWrites);
|
|
46719
|
+
const wrote = writesStartedSince(monitor, mark, ctx.isWrite).some((k) => !background.has(k));
|
|
46720
|
+
const text2 = await readPageText(ctx.page).catch(() => "");
|
|
46721
|
+
const viewport = ctx.page.viewportSize();
|
|
46722
|
+
const pt = { x: (viewport?.width ?? 0) / 2, y: (viewport?.height ?? 0) / 2 };
|
|
46723
|
+
const scroll = await ctx.page.evaluate(scrollPositionAt, pt).catch(() => null);
|
|
46724
|
+
const state = `${snap.signature}|${createHash4("sha1").update(text2).digest("hex").slice(0, 16)}|${scroll ?? "?"}`;
|
|
46725
|
+
const verdict = ctx.cycles.note({ ...step, state, progress: wrote });
|
|
46726
|
+
return verdict === null ? null : describeCycle(verdict);
|
|
46727
|
+
}
|
|
45928
46728
|
|
|
45929
46729
|
// ../explore/dist/goal-loop/page-view.js
|
|
45930
46730
|
async function viewPage(ctx, step) {
|
|
@@ -45960,6 +46760,7 @@ async function viewPage(ctx, step) {
|
|
|
45960
46760
|
ctx.save.reset();
|
|
45961
46761
|
}
|
|
45962
46762
|
ctx.lastPath = path2;
|
|
46763
|
+
ctx.valueLog.observe(snap.controls.filter((c) => c.tag === "input" || c.tag === "textarea" || c.richText === true).map((c) => ({ label: c.name || c.summary, value: c.value })));
|
|
45963
46764
|
if (ctx.listsSeveral && ctx.prevSignature !== null && ctx.prevSignature !== snap.signature) {
|
|
45964
46765
|
const next = ctx.nextFrom.get(snap.signature);
|
|
45965
46766
|
if (next !== void 0) {
|
|
@@ -46128,12 +46929,19 @@ async function finishRun(ctx) {
|
|
|
46128
46929
|
if (ctx.lastReportNotFound && ctx.answer === void 0 && (ctx.stop === "no-progress" || ctx.stop === "blocked") && ctx.failure === void 0) {
|
|
46129
46930
|
ctx.incomplete = answerNotFoundReason(ctx.observed.pages());
|
|
46130
46931
|
}
|
|
46932
|
+
if (ctx.awaitingReply && ctx.answer === void 0 && ctx.failure === void 0 && (ctx.stop === "no-progress" || ctx.stop === "blocked" || ctx.stop === "exhausted")) {
|
|
46933
|
+
const sent = ctx.lastTurn?.sent;
|
|
46934
|
+
const missing = `no reply within ${Math.round(ctx.replyWaitedMs / 1e3)}s to the last message sent${sent === void 0 || sent === "" ? "" : ` (${quote(sent, 80)})`}`;
|
|
46935
|
+
ctx.incomplete = `${missing}; ${incompleteReason(ctx.stop, ctx.incomplete, ctx.failure, ctx.hang, ctx.tracker)}`;
|
|
46936
|
+
}
|
|
46131
46937
|
await ctx.readOnly?.disarm();
|
|
46132
46938
|
ctx.page.off("request", ctx.onRequestSeen);
|
|
46133
46939
|
ctx.page.off("response", ctx.onDocumentResponse);
|
|
46134
46940
|
const finished = ctx.recorder.tryFinish({ intent: cfg.goal });
|
|
46135
46941
|
const cause = ctx.blockingCause();
|
|
46136
|
-
const
|
|
46942
|
+
const latest = ctx.blockers.latest;
|
|
46943
|
+
const reasonCause = cause !== null && latest !== null && cause === latest.text && ctx.incomplete !== null && ctx.incomplete.includes(latest.reason) ? null : cause;
|
|
46944
|
+
const finalOutcome = ctx.stop === "done" && ctx.outcome !== null && finished.ok ? ctx.outcome : { status: "incomplete", reason: withCause(incompleteReason(ctx.stop, ctx.incomplete, ctx.failure, ctx.hang, ctx.tracker), ctx.stop, reasonCause) };
|
|
46137
46945
|
if (!finished.ok) {
|
|
46138
46946
|
ctx.failure = ctx.failure ?? { kind: "exception", message: `recording rejected: ${finished.reason}` };
|
|
46139
46947
|
ctx.stop = "crashed";
|
|
@@ -46238,6 +47046,8 @@ async function explore(cfg) {
|
|
|
46238
47046
|
const settled = await checkSettled(ctx, seen);
|
|
46239
47047
|
if (settled === "stop")
|
|
46240
47048
|
break;
|
|
47049
|
+
if (settled === "continue")
|
|
47050
|
+
continue;
|
|
46241
47051
|
const progressed = await checkProgress(ctx, seen);
|
|
46242
47052
|
if (progressed === "stop")
|
|
46243
47053
|
break;
|
|
@@ -46551,18 +47361,18 @@ function nearMissHint(requests, method, pathGlob2) {
|
|
|
46551
47361
|
return null;
|
|
46552
47362
|
const groups = /* @__PURE__ */ new Map();
|
|
46553
47363
|
for (const r of candidates) {
|
|
46554
|
-
const
|
|
47364
|
+
const status = describeStatusWithRpc(r);
|
|
47365
|
+
const key2 = `${r.method} ${r.path} ${status}`;
|
|
46555
47366
|
const g = groups.get(key2);
|
|
46556
47367
|
if (g === void 0)
|
|
46557
|
-
groups.set(key2, { method: r.method, path: r.path, status
|
|
47368
|
+
groups.set(key2, { method: r.method, path: r.path, status, count: 1 });
|
|
46558
47369
|
else
|
|
46559
47370
|
g.count += 1;
|
|
46560
47371
|
}
|
|
46561
47372
|
const top = [...groups.values()].sort((a, b2) => b2.count - a.count)[0];
|
|
46562
47373
|
if (top === void 0)
|
|
46563
47374
|
return null;
|
|
46564
|
-
|
|
46565
|
-
return `saw ${top.method} ${top.path} \u2192 ${statusText} (${top.count}\xD7)`;
|
|
47375
|
+
return `saw ${top.method} ${top.path} \u2192 ${top.status} (${top.count}\xD7)`;
|
|
46566
47376
|
}
|
|
46567
47377
|
function evaluateNetworkCheck(check2, requests, truncated = false) {
|
|
46568
47378
|
const spec = describeCheck(check2);
|
|
@@ -46593,8 +47403,11 @@ function evaluateNetworkCheck(check2, requests, truncated = false) {
|
|
|
46593
47403
|
detail: `expected ${describeStatus2(check2.status)}, but the ${inFlight} matching request(s) were sent and are still awaiting a response`
|
|
46594
47404
|
};
|
|
46595
47405
|
}
|
|
46596
|
-
const seen = answered.map(
|
|
46597
|
-
const bad = answered.filter((r) =>
|
|
47406
|
+
const seen = answered.map(describeStatusWithRpc);
|
|
47407
|
+
const bad = answered.filter((r) => {
|
|
47408
|
+
const status = effectiveStatus(r);
|
|
47409
|
+
return status === null || !statusMatches(status, check2.status);
|
|
47410
|
+
});
|
|
46598
47411
|
const waiting = inFlight === 0 ? "" : ` (${inFlight} more still awaiting a response)`;
|
|
46599
47412
|
return bad.length === 0 ? { check: spec, passed: true, detail: `${answered.length} matching request(s), status ${[...new Set(seen)].join(", ")}${waiting}` } : {
|
|
46600
47413
|
check: spec,
|
|
@@ -46664,13 +47477,7 @@ async function connectCodeOf(response) {
|
|
|
46664
47477
|
const buf = await response.body().catch(() => null);
|
|
46665
47478
|
if (buf === null || buf.length > MAX_BODY_BYTES)
|
|
46666
47479
|
return null;
|
|
46667
|
-
|
|
46668
|
-
const body = JSON.parse(buf.toString("utf8"));
|
|
46669
|
-
const code = body !== null && typeof body === "object" && "code" in body ? body.code : null;
|
|
46670
|
-
return typeof code === "string" && /^[a-z_]+$/.test(code) ? code : null;
|
|
46671
|
-
} catch {
|
|
46672
|
-
return null;
|
|
46673
|
-
}
|
|
47480
|
+
return parseConnectUnaryError(buf.toString("utf8"));
|
|
46674
47481
|
}
|
|
46675
47482
|
function pageOf(actor) {
|
|
46676
47483
|
return actor.ability(BrowseTheWebToken).session.page;
|
|
@@ -48062,8 +48869,11 @@ async function adjudicatedRun(cfg, checks, page, capture2, declared) {
|
|
|
48062
48869
|
eachSawNot[i] = true;
|
|
48063
48870
|
else if (eachSawNot[i])
|
|
48064
48871
|
eachHeldAt[i] = settledSteps;
|
|
48065
|
-
else if (settledSteps === 1)
|
|
48872
|
+
else if (settledSteps === 1) {
|
|
48066
48873
|
eachHeldAtStart[i] = true;
|
|
48874
|
+
if (allowVacuous)
|
|
48875
|
+
eachHeldAt[i] = 1;
|
|
48876
|
+
}
|
|
48067
48877
|
}
|
|
48068
48878
|
return;
|
|
48069
48879
|
}
|
|
@@ -48074,12 +48884,15 @@ async function adjudicatedRun(cfg, checks, page, capture2, declared) {
|
|
|
48074
48884
|
sawNotHolding = true;
|
|
48075
48885
|
else if (sawNotHolding)
|
|
48076
48886
|
heldAtStep = settledSteps;
|
|
48077
|
-
else if (settledSteps === 1)
|
|
48887
|
+
else if (settledSteps === 1) {
|
|
48078
48888
|
heldAtStart = true;
|
|
48889
|
+
if (allowVacuous)
|
|
48890
|
+
heldAtStep = 1;
|
|
48891
|
+
}
|
|
48079
48892
|
},
|
|
48080
48893
|
...stopWhenHeld ? {
|
|
48081
48894
|
successMetNow: async () => {
|
|
48082
|
-
if (settledSteps < 2)
|
|
48895
|
+
if (settledSteps < 2 && !allowVacuous)
|
|
48083
48896
|
return null;
|
|
48084
48897
|
if (pageChecks.length > 0 && (each ? eachHeldAt.some((at3) => at3 === null) : heldAtStep === null))
|
|
48085
48898
|
return null;
|
|
@@ -48114,6 +48927,11 @@ async function adjudicatedRun(cfg, checks, page, capture2, declared) {
|
|
|
48114
48927
|
...answerRequired ? { requireAnswer: true } : {},
|
|
48115
48928
|
// #235: only `reloadThen` checks — the in-run check below evaluates none of them.
|
|
48116
48929
|
...checks.every((c) => c.kind === "reloadThen") ? { successCheckDeferred: true } : {},
|
|
48930
|
+
// #377: the checks as they read now (no held/each transition rule) — a `blocked` while
|
|
48931
|
+
// they hold is not refused as "nothing tried yet".
|
|
48932
|
+
...checks.every((c) => c.kind === "reloadThen") ? {} : {
|
|
48933
|
+
successChecksHoldNow: () => evaluateChecks(cfg, checks.filter((c) => c.kind !== "reloadThen"), page, capture2, scope()).then((rs) => rs.every((r) => r.passed), () => false)
|
|
48934
|
+
},
|
|
48117
48935
|
successCheck: () => evaluateChecks(cfg, checks.filter((c) => c.kind !== "reloadThen"), page, capture2, scope()).then((rs) => rs.every((r) => each && isPageCheck(r, pageChecks) ? (
|
|
48118
48936
|
// #337: under `each` a page check counts once IT went from not holding to holding.
|
|
48119
48937
|
eachHeldAt[pageIndexOf(r)] !== null || r.passed && eachSawNot[pageIndexOf(r)] === true
|
|
@@ -48927,39 +49745,6 @@ var CampaignSpecSchema = external_exports.object({
|
|
|
48927
49745
|
jobs: external_exports.array(CampaignJobSchema).min(1).max(CAMPAIGN_LIMITS.maxJobs)
|
|
48928
49746
|
}).strict();
|
|
48929
49747
|
|
|
48930
|
-
// ../explore/dist/authoring/value-capturing-generation-port.js
|
|
48931
|
-
var ValueCapturingGenerationPort = class {
|
|
48932
|
-
inner;
|
|
48933
|
-
captured = [];
|
|
48934
|
-
constructor(inner) {
|
|
48935
|
-
this.inner = inner;
|
|
48936
|
-
}
|
|
48937
|
-
async generate(kind, input2) {
|
|
48938
|
-
const result = await this.inner.generate(kind, input2);
|
|
48939
|
-
const text2 = extractText(result.output);
|
|
48940
|
-
if (text2 !== void 0)
|
|
48941
|
-
this.captured.push(text2);
|
|
48942
|
-
return result;
|
|
48943
|
-
}
|
|
48944
|
-
capturedValues(recording) {
|
|
48945
|
-
const fillSteps = flattenBaseFillSteps(recording);
|
|
48946
|
-
if (fillSteps.length !== this.captured.length) {
|
|
48947
|
-
throw new Error(`ValueCapturingGenerationPort: recorded ${this.captured.length} generate() call(s) but the recording has ${fillSteps.length} fill/select step(s) \u2014 cannot correlate values 1:1`);
|
|
48948
|
-
}
|
|
48949
|
-
const values2 = /* @__PURE__ */ new Map();
|
|
48950
|
-
fillSteps.forEach(({ ref }, i) => values2.set(`${ref.page}:${ref.step}`, this.captured[i]));
|
|
48951
|
-
return values2;
|
|
48952
|
-
}
|
|
48953
|
-
};
|
|
48954
|
-
function extractText(output3) {
|
|
48955
|
-
if (output3 && typeof output3 === "object" && "text" in output3) {
|
|
48956
|
-
const text2 = output3.text;
|
|
48957
|
-
if (typeof text2 === "string")
|
|
48958
|
-
return text2;
|
|
48959
|
-
}
|
|
48960
|
-
return void 0;
|
|
48961
|
-
}
|
|
48962
|
-
|
|
48963
49748
|
// ../explore/dist/authoring/auto-decide.js
|
|
48964
49749
|
function autoDecidePostdoc(base, diff2) {
|
|
48965
49750
|
const baseFillSteps = flattenBaseFillSteps(base);
|
|
@@ -48997,6 +49782,21 @@ function pickAutoName(columnClass, originalIndex, used) {
|
|
|
48997
49782
|
}
|
|
48998
49783
|
|
|
48999
49784
|
// ../explore/dist/authoring/author-journey.js
|
|
49785
|
+
function goalResultDiagnostics(r) {
|
|
49786
|
+
const failure3 = r.run.failure ?? r.failure;
|
|
49787
|
+
const reason = r.reason ?? failure3?.message;
|
|
49788
|
+
return {
|
|
49789
|
+
outcome: r.outcome,
|
|
49790
|
+
...reason === void 0 ? {} : { reason },
|
|
49791
|
+
...r.run.stop === void 0 ? {} : { stop: r.run.stop },
|
|
49792
|
+
...r.run.outcome === void 0 ? {} : { runOutcome: r.run.outcome },
|
|
49793
|
+
...failure3 === void 0 ? {} : { failure: { kind: failure3.kind, message: failure3.message } },
|
|
49794
|
+
...r.checks === void 0 ? {} : { checks: r.checks },
|
|
49795
|
+
...r.run.decisions === void 0 ? {} : { decisions: r.run.decisions },
|
|
49796
|
+
...r.run.actions === void 0 ? {} : { actions: r.run.actions },
|
|
49797
|
+
...r.finalUrl === void 0 ? {} : { finalUrl: r.finalUrl }
|
|
49798
|
+
};
|
|
49799
|
+
}
|
|
49000
49800
|
async function authorJourney(req) {
|
|
49001
49801
|
const takes = req.takes ?? 1;
|
|
49002
49802
|
if (takes < 1)
|
|
@@ -49011,45 +49811,43 @@ async function authorJourney(req) {
|
|
|
49011
49811
|
if (reload !== void 0) {
|
|
49012
49812
|
throw new Error(`authorJourney: ${describeCheck(reload)} can't be authored into a Journey yet \u2014 use a page check or a requestMade/responseStatus check`);
|
|
49013
49813
|
}
|
|
49014
|
-
const
|
|
49015
|
-
const
|
|
49016
|
-
const discovery = await runGoalBasedMission({
|
|
49017
|
-
goal: req.goal,
|
|
49018
|
-
...mission,
|
|
49019
|
-
allowlist: req.allowlist,
|
|
49020
|
-
startUrl: req.startUrl,
|
|
49021
|
-
bounds: req.bounds,
|
|
49022
|
-
actor: req.actor,
|
|
49023
|
-
judge: req.judgment,
|
|
49024
|
-
gen: discoveryGeneration,
|
|
49025
|
-
...req.safety === void 0 ? {} : { safety: req.safety }
|
|
49026
|
-
});
|
|
49814
|
+
const runTake = req.runTake ?? inProcessTake(req);
|
|
49815
|
+
const discovery = await runTake({ index: 0, goal: req.goal, successChecks: checks });
|
|
49027
49816
|
if (discovery.outcome !== "succeeded") {
|
|
49028
|
-
|
|
49817
|
+
const why = discovery.diagnostics.reason;
|
|
49818
|
+
return {
|
|
49819
|
+
outcome: "not-reached",
|
|
49820
|
+
reason: `discovery mission ${discovery.outcome}${why === void 0 ? "" : `: ${why}`}`,
|
|
49821
|
+
discovery: discovery.diagnostics,
|
|
49822
|
+
takes: { requested: takes, run: 1, succeeded: 0 }
|
|
49823
|
+
};
|
|
49029
49824
|
}
|
|
49030
|
-
const authoringTakes = [
|
|
49031
|
-
|
|
49032
|
-
];
|
|
49825
|
+
const authoringTakes = [authoringTake(discovery.recording)];
|
|
49826
|
+
const corroborating = [];
|
|
49033
49827
|
for (let i = 1; i < takes; i++) {
|
|
49034
|
-
const
|
|
49035
|
-
|
|
49036
|
-
goal: req.goal,
|
|
49037
|
-
...mission,
|
|
49038
|
-
allowlist: req.allowlist,
|
|
49039
|
-
startUrl: req.startUrl,
|
|
49040
|
-
bounds: req.bounds,
|
|
49041
|
-
actor: req.actor,
|
|
49042
|
-
judge: req.judgment,
|
|
49043
|
-
gen: replayGeneration,
|
|
49044
|
-
...req.safety === void 0 ? {} : { safety: req.safety }
|
|
49045
|
-
});
|
|
49828
|
+
const replay = await runTake({ index: i, goal: req.goal, successChecks: checks });
|
|
49829
|
+
corroborating.push(replay.diagnostics);
|
|
49046
49830
|
if (replay.outcome !== "succeeded")
|
|
49047
49831
|
continue;
|
|
49048
|
-
authoringTakes.push(
|
|
49832
|
+
authoringTakes.push(authoringTake(replay.recording));
|
|
49049
49833
|
}
|
|
49050
49834
|
const diff2 = diffTakes(authoringTakes);
|
|
49051
|
-
const
|
|
49052
|
-
const
|
|
49835
|
+
const base = authoringTakes[0];
|
|
49836
|
+
const secretSteps = secretFillSteps(base.recording);
|
|
49837
|
+
const secretParams = [];
|
|
49838
|
+
const decisions = autoDecidePostdoc(base.recording, diff2).map((d) => {
|
|
49839
|
+
const key2 = `${d.step.page}:${d.step.step}`;
|
|
49840
|
+
if (!secretSteps.has(key2))
|
|
49841
|
+
return d;
|
|
49842
|
+
const name = `secret${secretParams.length + 1}`;
|
|
49843
|
+
secretParams.push({
|
|
49844
|
+
name,
|
|
49845
|
+
secret: true,
|
|
49846
|
+
description: "a value code typed during authoring (a --secret-field/--totp binding, or one holding a --secret) \u2014 never kept in the Journey; pass it with --param at replay"
|
|
49847
|
+
});
|
|
49848
|
+
return { step: d.step, classify: "variable", name };
|
|
49849
|
+
});
|
|
49850
|
+
const materializedRecording = applyPostdoc(base, diff2, decisions);
|
|
49053
49851
|
const parameterizedRecording = checks.reduce((r, c) => c.kind === "page" ? appendSuccessAssertion(r, c.assertion) : r, materializedRecording);
|
|
49054
49852
|
const networkChecks = checks.flatMap((c) => c.kind === "requestMade" ? [{ kind: "requestMade", method: c.method, pathGlob: c.pathGlob }] : c.kind === "responseStatus" ? [{ kind: "responseStatus", method: c.method, pathGlob: c.pathGlob, status: { ...c.status } }] : []);
|
|
49055
49853
|
const metadata = {
|
|
@@ -49059,9 +49857,58 @@ async function authorJourney(req) {
|
|
|
49059
49857
|
params: deriveParamSchema(parameterizedRecording).required,
|
|
49060
49858
|
authoredBy: "jev-driven",
|
|
49061
49859
|
createdAtIso: clock.nowIso(),
|
|
49062
|
-
...networkChecks.length === 0 ? {} : { networkChecks }
|
|
49860
|
+
...networkChecks.length === 0 ? {} : { networkChecks },
|
|
49861
|
+
...secretParams.length === 0 ? {} : { parameters: secretParams }
|
|
49862
|
+
};
|
|
49863
|
+
return {
|
|
49864
|
+
outcome: "authored",
|
|
49865
|
+
journey: { metadata, recording: parameterizedRecording },
|
|
49866
|
+
discovery: discovery.diagnostics,
|
|
49867
|
+
takes: { requested: takes, run: takes, succeeded: authoringTakes.length },
|
|
49868
|
+
...corroborating.length === 0 ? {} : { corroboratingTakes: corroborating }
|
|
49869
|
+
};
|
|
49870
|
+
}
|
|
49871
|
+
function inProcessTake(req) {
|
|
49872
|
+
const { actor, judgment, generation } = req;
|
|
49873
|
+
if (actor === void 0 || judgment === void 0 || generation === void 0) {
|
|
49874
|
+
throw new Error("authorJourney: actor, judgment and generation are required without runTake");
|
|
49875
|
+
}
|
|
49876
|
+
return async ({ goal, successChecks }) => {
|
|
49877
|
+
const r = await runGoalBasedMission({
|
|
49878
|
+
goal,
|
|
49879
|
+
successChecks,
|
|
49880
|
+
allowlist: req.allowlist,
|
|
49881
|
+
startUrl: req.startUrl,
|
|
49882
|
+
bounds: req.bounds,
|
|
49883
|
+
actor,
|
|
49884
|
+
judge: judgment,
|
|
49885
|
+
gen: generation,
|
|
49886
|
+
...req.safety === void 0 ? {} : { safety: req.safety }
|
|
49887
|
+
});
|
|
49888
|
+
return { outcome: r.outcome, recording: r.recording, diagnostics: goalResultDiagnostics(r) };
|
|
49063
49889
|
};
|
|
49064
|
-
|
|
49890
|
+
}
|
|
49891
|
+
var SECRET_PLACEHOLDER = "\0jevitate:code-typed-secret";
|
|
49892
|
+
function authoringTake(recording) {
|
|
49893
|
+
const values2 = /* @__PURE__ */ new Map();
|
|
49894
|
+
recording.pages.forEach((page, p) => page.steps.forEach(({ step }, i) => {
|
|
49895
|
+
if (step.kind !== "fill" && step.kind !== "select")
|
|
49896
|
+
return;
|
|
49897
|
+
const v = step.value;
|
|
49898
|
+
if ("var" in v)
|
|
49899
|
+
return;
|
|
49900
|
+
values2.set(`${p}:${i}`, v.redacted ? SECRET_PLACEHOLDER : v.value);
|
|
49901
|
+
}));
|
|
49902
|
+
return { recording, values: values2 };
|
|
49903
|
+
}
|
|
49904
|
+
function secretFillSteps(recording) {
|
|
49905
|
+
const keys = /* @__PURE__ */ new Set();
|
|
49906
|
+
for (const { ref } of flattenBaseFillSteps(recording)) {
|
|
49907
|
+
const { step } = recording.pages[ref.page].steps[ref.step];
|
|
49908
|
+
if ((step.kind === "fill" || step.kind === "select") && "redacted" in step.value && step.value.redacted)
|
|
49909
|
+
keys.add(`${ref.page}:${ref.step}`);
|
|
49910
|
+
}
|
|
49911
|
+
return keys;
|
|
49065
49912
|
}
|
|
49066
49913
|
function appendSuccessAssertion(recording, check2) {
|
|
49067
49914
|
if (recording.pages.length === 0)
|
|
@@ -51991,7 +52838,7 @@ async function runOneReplay(params) {
|
|
|
51991
52838
|
}
|
|
51992
52839
|
|
|
51993
52840
|
// ../explore/dist/adversarial/markup-canary.js
|
|
51994
|
-
import { createHash as
|
|
52841
|
+
import { createHash as createHash5, randomBytes as randomBytes2 } from "node:crypto";
|
|
51995
52842
|
var CanaryTokens = class {
|
|
51996
52843
|
prefix;
|
|
51997
52844
|
#n = 0;
|
|
@@ -52049,7 +52896,7 @@ async function renderedCanariesSettled(page, prefix, authorized, expected, waitM
|
|
|
52049
52896
|
return seen;
|
|
52050
52897
|
}
|
|
52051
52898
|
function markupFingerprint(route, field3, payload) {
|
|
52052
|
-
return
|
|
52899
|
+
return createHash5("sha256").update(`markup-injection|${route}|${field3}|${payload}`).digest("hex").slice(0, 16);
|
|
52053
52900
|
}
|
|
52054
52901
|
|
|
52055
52902
|
// ../explore/dist/missions/adversarial-hunt/helpers.js
|
|
@@ -52379,7 +53226,7 @@ function startHunt(ctx, params) {
|
|
|
52379
53226
|
}
|
|
52380
53227
|
|
|
52381
53228
|
// ../explore/dist/adversarial/identity.js
|
|
52382
|
-
import { createHash as
|
|
53229
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
52383
53230
|
var READ_TIMEOUT_MS2 = 2e3;
|
|
52384
53231
|
function bounded3(p) {
|
|
52385
53232
|
p.catch(() => void 0);
|
|
@@ -52394,7 +53241,7 @@ var AUTH_NAME = /sess|auth|token|jwt|\bsid\b|_sid|sid_|login|user|account|identi
|
|
|
52394
53241
|
var AUTH_PATH = /log-?in|log-?out|sign-?in|sign-?up|sign-?out|register|auth|session|verify|oauth|sso|token|impersonat|switch-?user|demo/i;
|
|
52395
53242
|
var MAX_VALUE_CHARS2 = 16384;
|
|
52396
53243
|
function hash2(text2) {
|
|
52397
|
-
return
|
|
53244
|
+
return createHash6("sha256").update(text2).digest("hex").slice(0, 16);
|
|
52398
53245
|
}
|
|
52399
53246
|
var JWT = /^[A-Za-z0-9_-]+\.([A-Za-z0-9_-]+)\.[A-Za-z0-9_-]*$/;
|
|
52400
53247
|
var SUBJECT_CLAIMS = ["sub", "user_id", "userId", "uid", "email", "tenant", "tenant_id", "tenantId", "org", "org_id", "orgId"];
|
|
@@ -54227,6 +55074,7 @@ var RENDER_BASELINE_RENDERS = 3;
|
|
|
54227
55074
|
var RENDER_TREND_RENDERS = 3;
|
|
54228
55075
|
var RENDER_SLOWDOWN_FACTOR = 5;
|
|
54229
55076
|
var RENDER_SLOWDOWN_FLOOR_MS = 3e3;
|
|
55077
|
+
var RENDER_CORROBORATING_LOAD_PER_CORE = LAG_CORROBORATING_LOAD_PER_CORE;
|
|
54230
55078
|
var STARVATION_WINDOW_MS = 15e3;
|
|
54231
55079
|
var HOST_SAMPLE_INTERVAL_MS = 2e3;
|
|
54232
55080
|
var DEGRADED_STEP_MAJORITY = 0.5;
|
|
@@ -54333,14 +55181,21 @@ var HostHealthSampler = class {
|
|
|
54333
55181
|
if (!this.#attribute)
|
|
54334
55182
|
return null;
|
|
54335
55183
|
const since = this.#now() - STARVATION_WINDOW_MS;
|
|
55184
|
+
let corroborating = null;
|
|
54336
55185
|
for (let i = this.#samples.length - 1; i >= 0; i--) {
|
|
54337
55186
|
const s3 = this.#samples[i];
|
|
54338
55187
|
if (s3.at < since)
|
|
54339
55188
|
break;
|
|
54340
55189
|
if (s3.starved !== null)
|
|
54341
55190
|
return s3.starved;
|
|
55191
|
+
const load = s3.host.loadPerCore;
|
|
55192
|
+
if (load !== void 0 && load >= RENDER_CORROBORATING_LOAD_PER_CORE)
|
|
55193
|
+
corroborating = Math.max(corroborating ?? 0, load);
|
|
54342
55194
|
}
|
|
54343
|
-
|
|
55195
|
+
if (corroborating === null)
|
|
55196
|
+
return null;
|
|
55197
|
+
const trend = this.#renderTrend();
|
|
55198
|
+
return trend === null ? null : `${trend} at load ${fmt3(corroborating)}/core`;
|
|
54344
55199
|
}
|
|
54345
55200
|
/**
|
|
54346
55201
|
* One recorded transcript step (every strategy's runner routes its entries here): counts it as
|
|
@@ -54458,8 +55313,8 @@ function degradedEnvironmentOutcome(outcome, health, opts = {}) {
|
|
|
54458
55313
|
}
|
|
54459
55314
|
|
|
54460
55315
|
// src/build-info.generated.ts
|
|
54461
|
-
var GENERATED_COMMIT = "
|
|
54462
|
-
var GENERATED_BUILT_AT = "2026-10-
|
|
55316
|
+
var GENERATED_COMMIT = "b0b27ab";
|
|
55317
|
+
var GENERATED_BUILT_AT = "2026-10-05T02:04:30.795Z";
|
|
54463
55318
|
|
|
54464
55319
|
// src/version.ts
|
|
54465
55320
|
import { readFileSync as readFileSync5 } from "node:fs";
|
|
@@ -54481,10 +55336,10 @@ function readCliVersion() {
|
|
|
54481
55336
|
|
|
54482
55337
|
// src/engine.ts
|
|
54483
55338
|
function bundledCommit() {
|
|
54484
|
-
return true ? "
|
|
55339
|
+
return true ? "b0b27ab" : void 0;
|
|
54485
55340
|
}
|
|
54486
55341
|
function bundledBuiltAt() {
|
|
54487
|
-
return true ? "2026-10-
|
|
55342
|
+
return true ? "2026-10-05T02:04:30.795Z" : void 0;
|
|
54488
55343
|
}
|
|
54489
55344
|
function currentEngineInfo() {
|
|
54490
55345
|
return {
|
|
@@ -70125,7 +70980,7 @@ async function gateJourney(siteGate, recording, opts) {
|
|
|
70125
70980
|
|
|
70126
70981
|
// src/mission-fixtures.ts
|
|
70127
70982
|
import { spawn } from "node:child_process";
|
|
70128
|
-
import { createHash as
|
|
70983
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
70129
70984
|
import { readFileSync as readFileSync11 } from "node:fs";
|
|
70130
70985
|
|
|
70131
70986
|
// src/fixture-auth.ts
|
|
@@ -70540,7 +71395,7 @@ function substituteDeep(v, sub) {
|
|
|
70540
71395
|
return v;
|
|
70541
71396
|
}
|
|
70542
71397
|
function sha256(text2) {
|
|
70543
|
-
return
|
|
71398
|
+
return createHash7("sha256").update(text2).digest("hex");
|
|
70544
71399
|
}
|
|
70545
71400
|
function canonical2(v) {
|
|
70546
71401
|
if (Array.isArray(v)) return `[${v.map(canonical2).join(",")}]`;
|
|
@@ -72682,6 +73537,7 @@ function observeBrowserSignals(page, sink, now2, onDrop) {
|
|
|
72682
73537
|
const method = call(req, "method");
|
|
72683
73538
|
const failure3 = call(req, "failure");
|
|
72684
73539
|
if (typeof url2 !== "string") return;
|
|
73540
|
+
if (isExternalSchemeUrl(url2)) return;
|
|
72685
73541
|
const why = typeof failure3?.errorText === "string" ? failure3.errorText : "failed";
|
|
72686
73542
|
push({ source: "requestfailed", level: "error", text: `${typeof method === "string" ? method : "GET"} ${redactUrl(url2)} \u2014 ${why}` });
|
|
72687
73543
|
});
|
|
@@ -75280,7 +76136,7 @@ function renderReportMarkdown(input2) {
|
|
|
75280
76136
|
...engines.length === 0 ? [] : [`Engine commit(s): ${engines.map((c) => `\`${c}\``).join(", ")}.`],
|
|
75281
76137
|
""
|
|
75282
76138
|
];
|
|
75283
|
-
const
|
|
76139
|
+
const statusOf3 = new Map(input2.diff?.entries.map((e) => [e.key, e.status]) ?? []);
|
|
75284
76140
|
if (input2.diff !== void 0) {
|
|
75285
76141
|
out.push("## Diff against baseline", "", "| status | count |", "| --- | --- |", ...DIFF_STATUSES.map((s3) => `| ${s3} | ${input2.diff?.summary[s3] ?? 0} |`), "", "| status | key | severity | title | baseline | current |", "| --- | --- | --- | --- | --- | --- |", ...input2.diff.entries.map((e) => `| ${e.status} | \`${e.key}\` | ${e.defect.severity} | ${cell(e.defect.title)} | ${e.baseline.seen}/${e.baseline.of} | ${e.current.seen}/${e.current.of} |`), "");
|
|
75286
76142
|
}
|
|
@@ -75288,12 +76144,12 @@ function renderReportMarkdown(input2) {
|
|
|
75288
76144
|
if (hard.length === 0)
|
|
75289
76145
|
out.push("None.", "");
|
|
75290
76146
|
for (const d of hard)
|
|
75291
|
-
out.push(...defectSection(d,
|
|
76147
|
+
out.push(...defectSection(d, statusOf3.get(d.key)));
|
|
75292
76148
|
out.push("## Advisory findings", "", "Advisory findings (UX, 4xx-correlated console errors, Jev flags) never gate on their own.", "");
|
|
75293
76149
|
if (advisory.length === 0)
|
|
75294
76150
|
out.push("None.", "");
|
|
75295
76151
|
for (const d of advisory)
|
|
75296
|
-
out.push(...defectSection(d,
|
|
76152
|
+
out.push(...defectSection(d, statusOf3.get(d.key)));
|
|
75297
76153
|
out.push("## Runs", "", "| run | mode | target | started | outcome | build |", "| --- | --- | --- | --- | --- | --- |", ...input2.runs.map((r) => `| \`${r.runId}\` | ${r.mode} | ${r.target ?? ""} | ${r.startedAt ?? ""} | ${r.missionOutcome ?? ""}${r.goalOutcome === void 0 ? "" : ` (goal: ${r.goalOutcome})`} | ${r.targetBuild ?? ""} |`), "");
|
|
75298
76154
|
return out.join("\n");
|
|
75299
76155
|
}
|
|
@@ -76609,7 +77465,10 @@ async function runAuthorJourney(opts) {
|
|
|
76609
77465
|
browserPortFactory: opts.browserPortFactory,
|
|
76610
77466
|
browser: opts.browser,
|
|
76611
77467
|
...opts.storageState !== void 0 ? { storageState: opts.storageState } : {},
|
|
76612
|
-
...opts.safety === void 0 ? {} : { safety: opts.safety }
|
|
77468
|
+
...opts.safety === void 0 ? {} : { safety: opts.safety },
|
|
77469
|
+
...opts.goalRun === void 0 ? {} : { goalRun: opts.goalRun },
|
|
77470
|
+
...opts.outDir === void 0 ? {} : { outDir: opts.outDir },
|
|
77471
|
+
...opts.usage === void 0 ? {} : { usage: opts.usage }
|
|
76613
77472
|
});
|
|
76614
77473
|
if (result.outcome === "authored") {
|
|
76615
77474
|
const journey = opts.storageState !== void 0 && result.journey.metadata.requiresAuth !== true ? { ...result.journey, metadata: { ...result.journey.metadata, requiresAuth: true } } : result.journey;
|
|
@@ -76624,34 +77483,58 @@ async function authorViaBrowser(args) {
|
|
|
76624
77483
|
}
|
|
76625
77484
|
const judge = args.judge;
|
|
76626
77485
|
const gen = args.gen;
|
|
76627
|
-
const
|
|
76628
|
-
const
|
|
76629
|
-
|
|
76630
|
-
|
|
76631
|
-
|
|
76632
|
-
|
|
76633
|
-
|
|
77486
|
+
const g = args.goalRun ?? {};
|
|
77487
|
+
const target = g.target ?? (args.safety === void 0 ? void 0 : { safety: args.safety });
|
|
77488
|
+
return authorJourney({
|
|
77489
|
+
goal: args.goal,
|
|
77490
|
+
...args.successAssertion === void 0 ? {} : { successAssertion: args.successAssertion },
|
|
77491
|
+
...args.successChecks === void 0 ? {} : { successChecks: args.successChecks },
|
|
77492
|
+
allowlist: args.allowlist,
|
|
77493
|
+
startUrl: args.url,
|
|
77494
|
+
bounds: args.bounds,
|
|
77495
|
+
takes: args.takes,
|
|
77496
|
+
journeyId: args.journeyId,
|
|
77497
|
+
journeyName: args.journeyName,
|
|
77498
|
+
runTake: async ({ goal, successChecks }) => {
|
|
77499
|
+
const r = await runExploration({
|
|
77500
|
+
...g,
|
|
77501
|
+
...target === void 0 ? {} : { target },
|
|
77502
|
+
url: args.url,
|
|
77503
|
+
goal,
|
|
77504
|
+
successChecks,
|
|
77505
|
+
allowlist: args.allowlist,
|
|
77506
|
+
judge,
|
|
77507
|
+
gen,
|
|
77508
|
+
...args.usage === void 0 ? {} : { usage: args.usage },
|
|
77509
|
+
bounds: args.bounds,
|
|
77510
|
+
...args.outDir === void 0 ? {} : { outDir: args.outDir },
|
|
77511
|
+
...args.browserPortFactory === void 0 ? {} : { browserPortFactory: args.browserPortFactory },
|
|
77512
|
+
...args.browser === void 0 ? {} : { browser: args.browser },
|
|
77513
|
+
...args.storageState === void 0 ? {} : { storageState: args.storageState }
|
|
77514
|
+
});
|
|
77515
|
+
return goalRunTake(r, g);
|
|
77516
|
+
}
|
|
76634
77517
|
});
|
|
76635
|
-
|
|
76636
|
-
|
|
76637
|
-
|
|
76638
|
-
|
|
76639
|
-
|
|
76640
|
-
|
|
76641
|
-
|
|
76642
|
-
|
|
76643
|
-
|
|
76644
|
-
|
|
76645
|
-
|
|
76646
|
-
|
|
76647
|
-
|
|
76648
|
-
|
|
76649
|
-
|
|
76650
|
-
|
|
76651
|
-
|
|
76652
|
-
|
|
76653
|
-
|
|
76654
|
-
}
|
|
77518
|
+
}
|
|
77519
|
+
function goalRunTake(r, g = {}) {
|
|
77520
|
+
const reason = r.reason ?? r.failure?.message;
|
|
77521
|
+
const recordingPath = r.recordingPaths[0];
|
|
77522
|
+
const diagnostics = {
|
|
77523
|
+
outcome: r.goalOutcome,
|
|
77524
|
+
...reason === void 0 ? {} : { reason },
|
|
77525
|
+
stop: r.stop,
|
|
77526
|
+
runOutcome: r.runOutcome,
|
|
77527
|
+
...r.failure === void 0 ? {} : { failure: { kind: r.failure.kind, message: r.failure.message } },
|
|
77528
|
+
checks: r.checks,
|
|
77529
|
+
decisions: r.decisions,
|
|
77530
|
+
actions: r.actions,
|
|
77531
|
+
finalUrl: r.finalUrl,
|
|
77532
|
+
resultPath: r.resultPath,
|
|
77533
|
+
transcriptPath: r.transcriptPath,
|
|
77534
|
+
recordingPaths: r.recordingPaths,
|
|
77535
|
+
...g.screenshots === void 0 || recordingPath === void 0 ? {} : { screenshotsDir: screenshotsDirFor(g.screenshots, recordingPath) }
|
|
77536
|
+
};
|
|
77537
|
+
return { outcome: r.goalOutcome, recording: r.recording, diagnostics };
|
|
76655
77538
|
}
|
|
76656
77539
|
|
|
76657
77540
|
// src/explore-coverage.ts
|
|
@@ -81181,7 +82064,7 @@ function loadUxMaxFindingsPerPage(path2 = resolveDataDir(["config.json"])) {
|
|
|
81181
82064
|
}
|
|
81182
82065
|
|
|
81183
82066
|
// src/usability-capture.ts
|
|
81184
|
-
import { createHash as
|
|
82067
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
81185
82068
|
import { mkdirSync as mkdirSync8 } from "node:fs";
|
|
81186
82069
|
import { join as join22 } from "node:path";
|
|
81187
82070
|
var SECRET_INPUT_SELECTOR = [
|
|
@@ -81272,7 +82155,7 @@ function payloadDigest(method, endpoint, body, secrets) {
|
|
|
81272
82155
|
}
|
|
81273
82156
|
try {
|
|
81274
82157
|
const canon = JSON.stringify(canonical3(value));
|
|
81275
|
-
return
|
|
82158
|
+
return createHash8("sha256").update(`${method.toUpperCase()} ${endpoint}
|
|
81276
82159
|
${canon}`).digest("hex").slice(0, 32);
|
|
81277
82160
|
} catch {
|
|
81278
82161
|
return void 0;
|
|
@@ -81322,6 +82205,7 @@ var UsabilityCapture = class {
|
|
|
81322
82205
|
}
|
|
81323
82206
|
#onRequest = (r) => {
|
|
81324
82207
|
if (!KEPT_TYPES.has(r.resourceType())) return;
|
|
82208
|
+
if (isExternalSchemeUrl(r.url())) return;
|
|
81325
82209
|
const endpoint = this.#redact(endpointOf(r.method(), r.url()));
|
|
81326
82210
|
const payloadKey = WRITE_METHODS4.has(r.method().toUpperCase()) ? payloadDigest(r.method(), endpoint, postDataOf(r), this.#opts.secrets) : void 0;
|
|
81327
82211
|
const rec = {
|
|
@@ -84352,7 +85236,7 @@ async function runCheck(opts) {
|
|
|
84352
85236
|
const entry = d.keys.map((k) => entryOf.get(k)).find((e) => e !== void 0) ?? entryOf.get(d.key);
|
|
84353
85237
|
return entry === void 0 ? true : !entry.inBaseline;
|
|
84354
85238
|
};
|
|
84355
|
-
const
|
|
85239
|
+
const statusOf3 = (d) => d.keys.map((k) => entryOf.get(k)?.status).find((s3) => s3 !== void 0);
|
|
84356
85240
|
const gating = defects.filter(isGating);
|
|
84357
85241
|
const itemReports = executed.map(({ item, ex, durationMs }) => {
|
|
84358
85242
|
const base = {
|
|
@@ -84408,7 +85292,7 @@ async function runCheck(opts) {
|
|
|
84408
85292
|
cases.push({ suite: "budget", classname: "jevitate.budget", name: "total budget", timeSec: 0, status: "error", type: "budget-exceeded", message: exceeded });
|
|
84409
85293
|
}
|
|
84410
85294
|
const findings = defects.map((d) => {
|
|
84411
|
-
const status =
|
|
85295
|
+
const status = statusOf3(d);
|
|
84412
85296
|
return {
|
|
84413
85297
|
key: d.key,
|
|
84414
85298
|
title: d.title,
|
|
@@ -84458,7 +85342,7 @@ async function runCheck(opts) {
|
|
|
84458
85342
|
...opts.targetBuild === void 0 ? {} : { targetBuild: opts.targetBuild },
|
|
84459
85343
|
suiteUri: opts.suiteUri ?? opts.suite.path,
|
|
84460
85344
|
automationId: `jevitate-check/${opts.suite.name}/`,
|
|
84461
|
-
findings: defects.map((d) => ({ defect: d, gating: gating.includes(d), ...
|
|
85345
|
+
findings: defects.map((d) => ({ defect: d, gating: gating.includes(d), ...statusOf3(d) === void 0 ? {} : { status: statusOf3(d) } }))
|
|
84462
85346
|
});
|
|
84463
85347
|
await writeFile12(sarifPath, `${JSON.stringify(sarif, null, 2)}
|
|
84464
85348
|
`, "utf8");
|
|
@@ -85183,7 +86067,7 @@ import { userInfo } from "node:os";
|
|
|
85183
86067
|
import { InvalidArgumentError as InvalidArgumentError3 } from "commander";
|
|
85184
86068
|
|
|
85185
86069
|
// src/run-key-preflight.ts
|
|
85186
|
-
import { createHash as
|
|
86070
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
85187
86071
|
var NO_KEY_VERIFY_ENV = "JEVITATE_NO_KEY_VERIFY";
|
|
85188
86072
|
var InvalidCredentialError = class extends MissingCredentialError {
|
|
85189
86073
|
constructor(feature, invalid) {
|
|
@@ -85202,7 +86086,7 @@ var InvalidCredentialError = class extends MissingCredentialError {
|
|
|
85202
86086
|
};
|
|
85203
86087
|
var cache = /* @__PURE__ */ new Map();
|
|
85204
86088
|
function cacheKey(key2, value) {
|
|
85205
|
-
return `${key2}:${
|
|
86089
|
+
return `${key2}:${createHash9("sha256").update(value).digest("hex")}`;
|
|
85206
86090
|
}
|
|
85207
86091
|
function keyPreflightDisabled(env) {
|
|
85208
86092
|
return /^(?:1|true|yes)$/i.test((env[NO_KEY_VERIFY_ENV] ?? "").trim());
|
|
@@ -85475,7 +86359,7 @@ var GitSourceManager = class {
|
|
|
85475
86359
|
};
|
|
85476
86360
|
|
|
85477
86361
|
// ../sources/dist/hash.js
|
|
85478
|
-
import { createHash as
|
|
86362
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
85479
86363
|
function canonicalJson(value) {
|
|
85480
86364
|
if (value === null)
|
|
85481
86365
|
return "null";
|
|
@@ -85494,7 +86378,7 @@ function canonicalJson(value) {
|
|
|
85494
86378
|
}
|
|
85495
86379
|
function canonicalJourneyHash(file2) {
|
|
85496
86380
|
const validated = SharedJourneyFileSchema.parse(file2);
|
|
85497
|
-
const hex3 =
|
|
86381
|
+
const hex3 = createHash10("sha256").update(canonicalJson(validated), "utf8").digest("hex");
|
|
85498
86382
|
return `sha256:${hex3}`;
|
|
85499
86383
|
}
|
|
85500
86384
|
|
|
@@ -86243,7 +87127,7 @@ import { existsSync as realExistsSync } from "node:fs";
|
|
|
86243
87127
|
import { readFile as readFile13, writeFile as writeFile18, mkdir as mkdir17 } from "node:fs/promises";
|
|
86244
87128
|
import { homedir as realHomedir } from "node:os";
|
|
86245
87129
|
import { join as join38, dirname as dirname14 } from "node:path";
|
|
86246
|
-
import { createHash as
|
|
87130
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
86247
87131
|
function detectRuntimes(deps = {}) {
|
|
86248
87132
|
const existsSync33 = deps.existsSync ?? realExistsSync;
|
|
86249
87133
|
const homedir4 = deps.homedir ?? realHomedir;
|
|
@@ -86267,7 +87151,7 @@ function resolveInstallTargetPaths(deps = {}) {
|
|
|
86267
87151
|
};
|
|
86268
87152
|
}
|
|
86269
87153
|
function sha2562(s3) {
|
|
86270
|
-
return
|
|
87154
|
+
return createHash11("sha256").update(s3, "utf8").digest("hex");
|
|
86271
87155
|
}
|
|
86272
87156
|
async function planFileInstall(targetPath, newContent, lastInstalledHash, opts = {}) {
|
|
86273
87157
|
let current2;
|
|
@@ -89741,6 +90625,209 @@ function registerCampaignCommands(program2, deps, buildProgram2) {
|
|
|
89741
90625
|
});
|
|
89742
90626
|
}
|
|
89743
90627
|
|
|
90628
|
+
// src/goal-run-flags.ts
|
|
90629
|
+
import { Option } from "commander";
|
|
90630
|
+
var collect6 = (v, prev) => [...prev, v];
|
|
90631
|
+
var GOAL_RUN_OPTIONS = {
|
|
90632
|
+
successWhen: () => new Option(
|
|
90633
|
+
"--success-when <when>",
|
|
90634
|
+
"when the --success page checks must hold: final (default; on the final page) | held (on the final page, or all together at any settled step \u2014 a one-time secret, a toast) | each (each went from not holding to holding at some settled step, in any order \u2014 checks on different pages; the run stops once all have). reloadThen is always final"
|
|
90635
|
+
),
|
|
90636
|
+
allowVacuousChecks: () => new Option(
|
|
90637
|
+
"--allow-vacuous-checks",
|
|
90638
|
+
"downgrade a vacuous --success check to a warning. By default a check satisfied before the run's first action \u2014 a page check that held on the seed page and never changed (an empty result container), a requestMade/responseStatus matched only by a page-load or polling request \u2014 FAILS: it cannot verify the goal"
|
|
90639
|
+
),
|
|
90640
|
+
actionDeltas: () => new Option(
|
|
90641
|
+
"--action-deltas",
|
|
90642
|
+
"opt-in (#303; every --strategy, not --feature): record what each action changed on the page \u2014 an accessibility snapshot before and after, announcements, the action's requests \u2014 redacted, with a code verdict per step (no-change | relevant-change | inconclusive) used by the goal loop's no-progress check and a persistence re-check after writes (goal), and as defect evidence (adversarial, coverage); adds `delta` to every transcript step (and Recording step, goal) and `actionDeltas` to the result. Costs about 50-100 ms per action on a small page, 0.3-0.5 s on a large one"
|
|
90643
|
+
),
|
|
90644
|
+
secret: () => new Option(
|
|
90645
|
+
"--secret <value|env:VAR>",
|
|
90646
|
+
"REDACTION ONLY: a secret/PII value kept out of every model call and artifact (repeatable); env:VAR reads it from the environment (preferred: a literal is visible in the process list and shell history). It is never typed into a field \u2014 to log in, bind it with --secret-field (or start from --storage-state)"
|
|
90647
|
+
).default([]).argParser(collect6),
|
|
90648
|
+
secretField: () => new Option(
|
|
90649
|
+
"--secret-field <binding>",
|
|
90650
|
+
"goal/usability strategy: '<label|testId|type|id|name>=<value>=env:<VAR>' (repeatable), e.g. 'label=Password=env:APP_PASSWORD'. When the run types into a matching field, code types $VAR itself; the model sees only \xABsecret:VAR\xBB and the Recording {redacted:true}. A value delivered during the run (an emailed code): '<descriptor>=cmd:<command>' runs the command when the field is typed and types its stdout (needs --allow-secret-cmd)"
|
|
90651
|
+
).default([]).argParser(collect6),
|
|
90652
|
+
totp: () => new Option(
|
|
90653
|
+
"--totp <binding>",
|
|
90654
|
+
"goal/usability strategy: '<descriptor>=env:<VAR>' with $VAR a base32 TOTP seed (repeatable), e.g. 'label=Authentication code=env:APP_TOTP_SEED'. The 6-digit code is computed locally (RFC 6238) when the field is typed; the seed never reaches a model or disk"
|
|
90655
|
+
).default([]).argParser(collect6),
|
|
90656
|
+
typeFixture: () => new Option(
|
|
90657
|
+
"--type-fixture <binding>",
|
|
90658
|
+
"goal strategy: '<label|testId|type|id|name>=<value>=<file>' (repeatable), e.g. 'label=Paste your text=./fixtures/import.txt'. When the run types into a matching field, code types the file's exact text verbatim (line breaks kept, never paraphrased or capped); the model sees only \xABfixture:<file name>\xBB. Recorded as typed unless it holds a --secret"
|
|
90659
|
+
).default([]).argParser(collect6),
|
|
90660
|
+
fixture: () => new Option("--fixture <path>", "local file the upload op attaches to a file input (goal and usability strategies); must exist"),
|
|
90661
|
+
saveStorageState: () => new Option(
|
|
90662
|
+
"--save-storage-state <file>",
|
|
90663
|
+
"write the context's storageState (cookies + origin storage) here when the run ends; mode 0600, contents never logged. Useful with a rotating refresh token: --storage-state's file goes stale after one authenticated run refreshes it, so point --save-storage-state at the SAME file (or a new one) to keep it usable for the next run. Written on every exit path -- a crash or a SIGTERM/SIGINT kill included (#159), not only a clean end -- but never over a good file with a session that already looks lost/logged-out; the last known-good state is used instead, or nothing is written if none was ever captured."
|
|
90664
|
+
),
|
|
90665
|
+
replyWaitMs: () => new Option(
|
|
90666
|
+
"--reply-wait-ms <ms>",
|
|
90667
|
+
"conversational pages: how long to keep waiting for a reply while the page shows no sign of working on one (goal and usability; default 60000). While a request the message started is in flight, a busy indicator shows, or the reply is still growing, the wait continues up to --reply-ceiling-ms"
|
|
90668
|
+
).argParser(positiveIntArg),
|
|
90669
|
+
replyQuietMs: () => new Option(
|
|
90670
|
+
"--reply-quiet-ms <ms>",
|
|
90671
|
+
"conversational pages: how long a reply must hold still (no new text, no busy sign) before it is read as complete (goal and usability; default 1000). Raise it for an assistant that answers in several parts (a sentence, then a card a moment later)"
|
|
90672
|
+
).argParser(positiveIntArg),
|
|
90673
|
+
replyCeilingMs: () => new Option(
|
|
90674
|
+
"--reply-ceiling-ms <ms>",
|
|
90675
|
+
"conversational pages: hard ceiling on the TOTAL wait for one sent message's reply \u2014 the send's own wait plus every later 'wait' \u2014 however busy the page stays; once spent the run ends naming the missing reply (default 180000; never below --reply-wait-ms) (default 180000; never below --reply-wait-ms)"
|
|
90676
|
+
).argParser(positiveIntArg),
|
|
90677
|
+
replyMaxChars: () => new Option("--reply-max-chars <n>", "conversational pages: cap on each generated chat message (goal and usability; default 300)").argParser(
|
|
90678
|
+
intArg({ min: 20, max: 2e3 })
|
|
90679
|
+
),
|
|
90680
|
+
jobWaitMs: () => new Option(
|
|
90681
|
+
"--job-wait-ms <ms>",
|
|
90682
|
+
`goal and usability: while the page shows an in-progress status ("Simulating\u2026", aria-busy, a job "is running"), waits keep waiting with backoff \u2014 and a model 'blocked' is deferred \u2014 up to this budget (default: --reply-ceiling-ms, 180000); it also bounds a busy indicator the app visibly keeps working behind (live progress, a job poll) before it is a hang, and a wait the page documents ("usually takes a minute") can raise it`
|
|
90683
|
+
).argParser(positiveIntArg),
|
|
90684
|
+
deny: () => new Option(
|
|
90685
|
+
"--deny <pattern>",
|
|
90686
|
+
"a control no mission may click (repeatable): an accessible-name regex (/Archive/i or Archive) or a descriptor role=button;name=Archive. Session-ending (Sign out), destructive (Delete, Revoke, Rotate) and paid (Buy, Run simulation, Generate, Send invite) controls are refused by default"
|
|
90687
|
+
).default([]).argParser(collect6),
|
|
90688
|
+
paid: () => new Option(
|
|
90689
|
+
"--paid <pattern>",
|
|
90690
|
+
"an app control that costs money or credits (repeatable; same syntax as --deny), e.g. /^(Analyze|Draft|Improve)\\b/i: treated like the built-in paid vocabulary \u2014 the budget guard sees it, hang replays never repeat it, and a goal that asks for it may still click it"
|
|
90691
|
+
).default([]).argParser(collect6),
|
|
90692
|
+
allowDestructive: () => new Option(
|
|
90693
|
+
"--allow-destructive",
|
|
90694
|
+
"let missions click session-ending, destructive and paid controls (a --deny pattern still holds). A goal run already may click one its goal asks for"
|
|
90695
|
+
),
|
|
90696
|
+
dialogs: () => new Option(
|
|
90697
|
+
"--dialogs <policy>",
|
|
90698
|
+
"native window.confirm/prompt dialogs: dismiss (default) or accept. accept still dismisses one whose message names a session-ending, destructive or paid action the run may not take (without --allow-destructive or a goal asking for it); every dialog is logged"
|
|
90699
|
+
),
|
|
90700
|
+
readRpc: () => new Option(
|
|
90701
|
+
"--read-rpc <glob>",
|
|
90702
|
+
"a POST request that only READS (repeatable): an RPC-method glob (Estimate*, pkg.Service/Preview*) or a path glob (/api/search*). gRPC-web/Connect Get*/List*/Search*/Find*/Watch*/Stream*/Count*/Describe*/Read* methods are reads already. Reads are never guarded or reported as duplicate writes"
|
|
90703
|
+
).default([]).argParser(collect6),
|
|
90704
|
+
hangReplays: () => new Option("--hang-replays <n>", "fresh-context replays that confirm a hang (default 2; 0 = don't replay, the hang is reported unconfirmed)").argParser(
|
|
90705
|
+
nonNegativeIntArg
|
|
90706
|
+
),
|
|
90707
|
+
settleIgnore: () => new Option("--settle-ignore <pattern>", "a request URL pattern the target marks as background (never pending work; repeatable, * wildcard)").default([]).argParser(collect6),
|
|
90708
|
+
longPollMs: () => new Option("--long-poll-ms <n>", "a request pending this long on an interactive page is a long-poll (default 5000)").argParser(nonNegativeIntArg),
|
|
90709
|
+
apiPrefix: () => new Option("--api-prefix <path>", "a path prefix whose requests are the app's API in the timing summary (repeatable), e.g. /api/").default([]).argParser(collect6),
|
|
90710
|
+
ignoreNoProgress: () => new Option("--ignore-no-progress <pattern>", "a route / action label / busy indicator where ui-no-progress is expected (repeatable, * wildcard)").default([]).argParser(collect6),
|
|
90711
|
+
allowSecretCmd: () => new Option(
|
|
90712
|
+
"--allow-secret-cmd",
|
|
90713
|
+
"opt-in: a --secret-field <descriptor>=cmd:<command> may run its command (in a shell, at type time, 60s timeout) and type its output (operator-declared only; refused otherwise)"
|
|
90714
|
+
).default(false),
|
|
90715
|
+
secretCmdAttempts: () => new Option(
|
|
90716
|
+
"--secret-cmd-attempts <n>",
|
|
90717
|
+
"#359: how many times one cmd: secret field's command may run in this run (default 3); past it, typing that field fails without running the command again (read-the-code commands usually have side effects)"
|
|
90718
|
+
).argParser(positiveIntArg)
|
|
90719
|
+
};
|
|
90720
|
+
var GoalRunFlagError = class extends Error {
|
|
90721
|
+
constructor(code, message2) {
|
|
90722
|
+
super(message2);
|
|
90723
|
+
this.code = code;
|
|
90724
|
+
this.name = "GoalRunFlagError";
|
|
90725
|
+
}
|
|
90726
|
+
code;
|
|
90727
|
+
};
|
|
90728
|
+
var asError = (err) => err instanceof GoalRunFlagError ? err : new GoalRunFlagError(
|
|
90729
|
+
err instanceof SessionFileInProjectError || err instanceof TargetConfigError ? err.code : "E_EXPLORE_ARGS",
|
|
90730
|
+
err instanceof Error ? err.message : String(err)
|
|
90731
|
+
);
|
|
90732
|
+
function dialogsFromFlags(o) {
|
|
90733
|
+
if (o.dialogs === void 0) return void 0;
|
|
90734
|
+
if (o.dialogs !== "dismiss" && o.dialogs !== "accept") {
|
|
90735
|
+
throw new GoalRunFlagError("E_EXPLORE_ARGS", `--dialogs must be dismiss or accept, got ${JSON.stringify(o.dialogs)}`);
|
|
90736
|
+
}
|
|
90737
|
+
return o.dialogs;
|
|
90738
|
+
}
|
|
90739
|
+
function successWhenFromFlags(o) {
|
|
90740
|
+
if (o.successWhen === void 0) return void 0;
|
|
90741
|
+
if (o.successWhen !== "held" && o.successWhen !== "final" && o.successWhen !== "each") {
|
|
90742
|
+
throw new GoalRunFlagError("E_EXPLORE_ARGS", `--success-when must be "final", "held" or "each", got ${JSON.stringify(o.successWhen)}`);
|
|
90743
|
+
}
|
|
90744
|
+
return o.successWhen;
|
|
90745
|
+
}
|
|
90746
|
+
function conversationFromFlags(o) {
|
|
90747
|
+
const conversation = {
|
|
90748
|
+
...o.replyWaitMs === void 0 ? {} : { replyWaitMs: Number(o.replyWaitMs) },
|
|
90749
|
+
...o.replyQuietMs === void 0 ? {} : { replyQuietMs: Number(o.replyQuietMs) },
|
|
90750
|
+
...o.replyCeilingMs === void 0 ? {} : { replyCeilingMs: Number(o.replyCeilingMs) },
|
|
90751
|
+
...o.replyMaxChars === void 0 ? {} : { replyMaxChars: Number(o.replyMaxChars) },
|
|
90752
|
+
...o.jobWaitMs === void 0 ? {} : { jobWaitMs: Number(o.jobWaitMs) }
|
|
90753
|
+
};
|
|
90754
|
+
if (conversation.replyWaitMs !== void 0 && !(Number.isInteger(conversation.replyWaitMs) && conversation.replyWaitMs > 0) || conversation.replyCeilingMs !== void 0 && !(Number.isInteger(conversation.replyCeilingMs) && conversation.replyCeilingMs > 0) || conversation.replyMaxChars !== void 0 && !(Number.isInteger(conversation.replyMaxChars) && conversation.replyMaxChars >= 20 && conversation.replyMaxChars <= 2e3)) {
|
|
90755
|
+
throw new GoalRunFlagError("E_EXPLORE_ARGS", "--reply-wait-ms and --reply-ceiling-ms must be positive integers; --reply-max-chars an integer in 20..2000");
|
|
90756
|
+
}
|
|
90757
|
+
if (conversation.jobWaitMs !== void 0 && !(Number.isInteger(conversation.jobWaitMs) && conversation.jobWaitMs > 0)) {
|
|
90758
|
+
throw new GoalRunFlagError("E_EXPLORE_ARGS", "--job-wait-ms must be a positive integer");
|
|
90759
|
+
}
|
|
90760
|
+
return conversation;
|
|
90761
|
+
}
|
|
90762
|
+
function secretFieldsFromFlags(o, env = process.env) {
|
|
90763
|
+
try {
|
|
90764
|
+
return [
|
|
90765
|
+
...o.secretField.map((s3) => parseSecretField(s3, "value", env, { allowCmd: o.allowSecretCmd === true })),
|
|
90766
|
+
...o.totp.map((s3) => parseSecretField(s3, "totp", env))
|
|
90767
|
+
];
|
|
90768
|
+
} catch (err) {
|
|
90769
|
+
throw asError(err);
|
|
90770
|
+
}
|
|
90771
|
+
}
|
|
90772
|
+
function targetFlagsFromFlags(o, dialogs) {
|
|
90773
|
+
return {
|
|
90774
|
+
settleIgnore: o.settleIgnore ?? [],
|
|
90775
|
+
ignoreNoProgress: o.ignoreNoProgress ?? [],
|
|
90776
|
+
apiPrefixes: o.apiPrefix ?? [],
|
|
90777
|
+
deny: o.deny ?? [],
|
|
90778
|
+
paid: o.paid ?? [],
|
|
90779
|
+
readRpc: o.readRpc ?? [],
|
|
90780
|
+
...o.allowDestructive === true ? { allowDestructive: true } : {},
|
|
90781
|
+
...dialogs === void 0 ? {} : { dialogs },
|
|
90782
|
+
...o.allowWrites === true ? { allowWrites: true } : {},
|
|
90783
|
+
allowWrite: o.allowWrite ?? [],
|
|
90784
|
+
...o.hangReplayWrites === true ? { hangReplayWrites: true } : {},
|
|
90785
|
+
...o.longPollMs === void 0 ? {} : { longPollMs: Number(o.longPollMs) }
|
|
90786
|
+
};
|
|
90787
|
+
}
|
|
90788
|
+
function resolveGoalRunFlags(o, url2, deps = {}) {
|
|
90789
|
+
const env = deps.env ?? process.env;
|
|
90790
|
+
try {
|
|
90791
|
+
const dialogs = dialogsFromFlags(o);
|
|
90792
|
+
const successWhen = successWhenFromFlags(o);
|
|
90793
|
+
if (o.saveStorageState !== void 0) assertSessionFileOutsideProject(o.saveStorageState, "--save-storage-state");
|
|
90794
|
+
const resolved = resolveSecretArgs(o.secret, env, "--secret");
|
|
90795
|
+
if (resolved.literals > 0) deps.warn?.(LITERAL_SECRET_WARNING);
|
|
90796
|
+
const screenshots = parseScreenshotsArg(o.screenshots);
|
|
90797
|
+
const conversation = conversationFromFlags(o);
|
|
90798
|
+
validateDenyPatterns(o.deny);
|
|
90799
|
+
validateDenyPatterns(o.paid, "--paid");
|
|
90800
|
+
const emulation = emulationFromFlags(o);
|
|
90801
|
+
const secretFields = secretFieldsFromFlags(o, env);
|
|
90802
|
+
const typeFixtures = o.typeFixture.length === 0 ? [] : loadTypeFixtures(o.typeFixture);
|
|
90803
|
+
let target;
|
|
90804
|
+
try {
|
|
90805
|
+
target = resolveTargetConfig(loadTargetsFile(deps.targetsConfigPath), new URL(url2).origin, targetFlagsFromFlags(o, dialogs));
|
|
90806
|
+
} catch (err) {
|
|
90807
|
+
if (!(err instanceof TypeError)) throw err;
|
|
90808
|
+
}
|
|
90809
|
+
return {
|
|
90810
|
+
...successWhen === void 0 ? {} : { successWhen },
|
|
90811
|
+
...o.allowVacuousChecks === true ? { allowVacuousChecks: true } : {},
|
|
90812
|
+
...o.actionDeltas === true ? { actionDeltas: true } : {},
|
|
90813
|
+
...resolved.secrets.length === 0 ? {} : { secrets: resolved.secrets },
|
|
90814
|
+
...secretFields.length === 0 ? {} : { secretFields },
|
|
90815
|
+
...secretFields.some((f) => f.kind === "cmd") ? { secretCommand: secretCommandRunner() } : {},
|
|
90816
|
+
...o.secretCmdAttempts === void 0 ? {} : { secretCommandAttempts: o.secretCmdAttempts },
|
|
90817
|
+
...typeFixtures.length === 0 ? {} : { typeFixtures },
|
|
90818
|
+
...o.fixture === void 0 ? {} : { fixture: o.fixture },
|
|
90819
|
+
...o.saveStorageState === void 0 ? {} : { saveStorageState: o.saveStorageState },
|
|
90820
|
+
...screenshots === void 0 ? {} : { screenshots },
|
|
90821
|
+
...emulation === void 0 ? {} : { emulation },
|
|
90822
|
+
...target === void 0 ? {} : { target },
|
|
90823
|
+
...o.hangReplays === void 0 ? {} : { hangReplays: o.hangReplays },
|
|
90824
|
+
...Object.keys(conversation).length === 0 ? {} : { conversation }
|
|
90825
|
+
};
|
|
90826
|
+
} catch (err) {
|
|
90827
|
+
throw asError(err);
|
|
90828
|
+
}
|
|
90829
|
+
}
|
|
90830
|
+
|
|
89744
90831
|
// src/explore-cli.ts
|
|
89745
90832
|
var SWEEP_OWNED = /* @__PURE__ */ new Set([
|
|
89746
90833
|
"fromJourney",
|
|
@@ -89819,16 +90906,7 @@ function registerExploreCommands(program2, deps, buildProgram2) {
|
|
|
89819
90906
|
].join(" "),
|
|
89820
90907
|
(v, prev) => [...prev, v],
|
|
89821
90908
|
[]
|
|
89822
|
-
).option(
|
|
89823
|
-
"--success-when <when>",
|
|
89824
|
-
"when the --success page checks must hold: final (default; on the final page) | held (on the final page, or all together at any settled step \u2014 a one-time secret, a toast) | each (each went from not holding to holding at some settled step, in any order \u2014 checks on different pages; the run stops once all have). reloadThen is always final"
|
|
89825
|
-
).option(
|
|
89826
|
-
"--allow-vacuous-checks",
|
|
89827
|
-
"downgrade a vacuous --success check to a warning. By default a check satisfied before the run's first action \u2014 a page check that held on the seed page and never changed (an empty result container), a requestMade/responseStatus matched only by a page-load or polling request \u2014 FAILS: it cannot verify the goal"
|
|
89828
|
-
).option(
|
|
89829
|
-
"--action-deltas",
|
|
89830
|
-
"opt-in (#303; every --strategy, not --feature): record what each action changed on the page \u2014 an accessibility snapshot before and after, announcements, the action's requests \u2014 redacted, with a code verdict per step (no-change | relevant-change | inconclusive) used by the goal loop's no-progress check and a persistence re-check after writes (goal), and as defect evidence (adversarial, coverage); adds `delta` to every transcript step (and Recording step, goal) and `actionDeltas` to the result. Costs about 50-100 ms per action on a small page, 0.3-0.5 s on a large one"
|
|
89831
|
-
).option("--feature <name>", "run the capability-scoped feature-testing mission (instead of --goal/--success)").option(
|
|
90909
|
+
).addOption(GOAL_RUN_OPTIONS.successWhen()).addOption(GOAL_RUN_OPTIONS.allowVacuousChecks()).addOption(GOAL_RUN_OPTIONS.actionDeltas()).option("--feature <name>", "run the capability-scoped feature-testing mission (instead of --goal/--success)").option(
|
|
89832
90910
|
"--route <glob>",
|
|
89833
90911
|
"in-scope route glob (repeatable), e.g. /thread/** \u2014 for --feature it replaces the default scope (the start URL's route and everything under it); it widens --strategy adversarial/coverage/exploratory beyond the start URL's route",
|
|
89834
90912
|
(v, prev) => [...prev, v],
|
|
@@ -89841,30 +90919,7 @@ function registerExploreCommands(program2, deps, buildProgram2) {
|
|
|
89841
90919
|
"authorized origin (repeatable); REPLACES the default allowlist when given (the URL's own origin is used only when --allow is omitted entirely) -- include the URL's own origin explicitly if you still need it",
|
|
89842
90920
|
(v, prev) => [...prev, v],
|
|
89843
90921
|
[]
|
|
89844
|
-
).option(
|
|
89845
|
-
"--secret <value|env:VAR>",
|
|
89846
|
-
"REDACTION ONLY: a secret/PII value kept out of every model call and artifact (repeatable); env:VAR reads it from the environment (preferred: a literal is visible in the process list and shell history). It is never typed into a field \u2014 to log in, bind it with --secret-field (or start from --storage-state)",
|
|
89847
|
-
(v, prev) => [...prev, v],
|
|
89848
|
-
[]
|
|
89849
|
-
).option(
|
|
89850
|
-
"--secret-field <binding>",
|
|
89851
|
-
"goal/usability strategy: '<label|testId|type|id|name>=<value>=env:<VAR>' (repeatable), e.g. 'label=Password=env:APP_PASSWORD'. When the run types into a matching field, code types $VAR itself; the model sees only \xABsecret:VAR\xBB and the Recording {redacted:true}. A value delivered during the run (an emailed code): '<descriptor>=cmd:<command>' runs the command when the field is typed and types its stdout (needs --allow-secret-cmd)",
|
|
89852
|
-
(v, prev) => [...prev, v],
|
|
89853
|
-
[]
|
|
89854
|
-
).option(
|
|
89855
|
-
"--totp <binding>",
|
|
89856
|
-
"goal/usability strategy: '<descriptor>=env:<VAR>' with $VAR a base32 TOTP seed (repeatable), e.g. 'label=Authentication code=env:APP_TOTP_SEED'. The 6-digit code is computed locally (RFC 6238) when the field is typed; the seed never reaches a model or disk",
|
|
89857
|
-
(v, prev) => [...prev, v],
|
|
89858
|
-
[]
|
|
89859
|
-
).option(
|
|
89860
|
-
"--type-fixture <binding>",
|
|
89861
|
-
"goal strategy: '<label|testId|type|id|name>=<value>=<file>' (repeatable), e.g. 'label=Paste your text=./fixtures/import.txt'. When the run types into a matching field, code types the file's exact text verbatim (line breaks kept, never paraphrased or capped); the model sees only \xABfixture:<file name>\xBB. Recorded as typed unless it holds a --secret",
|
|
89862
|
-
(v, prev) => [...prev, v],
|
|
89863
|
-
[]
|
|
89864
|
-
).option(
|
|
89865
|
-
"--fixture <path>",
|
|
89866
|
-
"local file the upload op attaches to a file input (goal and usability strategies); must exist"
|
|
89867
|
-
).option(
|
|
90922
|
+
).addOption(GOAL_RUN_OPTIONS.secret()).addOption(GOAL_RUN_OPTIONS.secretField()).addOption(GOAL_RUN_OPTIONS.totp()).addOption(GOAL_RUN_OPTIONS.typeFixture()).addOption(GOAL_RUN_OPTIONS.fixture()).option(
|
|
89868
90923
|
"--storage-state <file>",
|
|
89869
90924
|
"Playwright storageState JSON to start the session authenticated (deterministic login pre-step); must exist"
|
|
89870
90925
|
).option(
|
|
@@ -89872,50 +90927,11 @@ function registerExploreCommands(program2, deps, buildProgram2) {
|
|
|
89872
90927
|
"multi-actor mission (#147, goal only; repeatable): the FIRST actor is the primary (the only one the model drives, from its own storageState); every other actor is an observer in its OWN fresh context that only runs the --invariants' cross-actor checks (capture + probe as:/deniedAs) \u2014 never clicks or types. Replaces --storage-state",
|
|
89873
90928
|
(v, prev) => [...prev, v],
|
|
89874
90929
|
[]
|
|
89875
|
-
).option(
|
|
89876
|
-
"--save-storage-state <file>",
|
|
89877
|
-
"write the context's storageState (cookies + origin storage) here when the run ends; mode 0600, contents never logged. Useful with a rotating refresh token: --storage-state's file goes stale after one authenticated run refreshes it, so point --save-storage-state at the SAME file (or a new one) to keep it usable for the next run. Written on every exit path -- a crash or a SIGTERM/SIGINT kill included (#159), not only a clean end -- but never over a good file with a session that already looks lost/logged-out; the last known-good state is used instead, or nothing is written if none was ever captured."
|
|
89878
|
-
).option("--max-actions <n>", "hard cap on executed actions", positiveIntArg).option("--max-decisions <n>", "hard cap on model decisions", positiveIntArg).option(
|
|
90930
|
+
).addOption(GOAL_RUN_OPTIONS.saveStorageState()).option("--max-actions <n>", "hard cap on executed actions", positiveIntArg).option("--max-decisions <n>", "hard cap on model decisions", positiveIntArg).option(
|
|
89879
90931
|
"--stall-timeout <seconds>",
|
|
89880
90932
|
"--strategy coverage/exploratory and --feature: end the run inconclusive (stalled) when no step completes within this many seconds (default 120)",
|
|
89881
90933
|
positiveNumberArg
|
|
89882
|
-
).option(
|
|
89883
|
-
"--reply-wait-ms <ms>",
|
|
89884
|
-
"conversational pages: how long to keep waiting for a reply while the page shows no sign of working on one (goal and usability; default 60000). While a request the message started is in flight, a busy indicator shows, or the reply is still growing, the wait continues up to --reply-ceiling-ms",
|
|
89885
|
-
positiveIntArg
|
|
89886
|
-
).option(
|
|
89887
|
-
"--reply-quiet-ms <ms>",
|
|
89888
|
-
"conversational pages: how long a reply must hold still (no new text, no busy sign) before it is read as complete (goal and usability; default 1000). Raise it for an assistant that answers in several parts (a sentence, then a card a moment later)",
|
|
89889
|
-
positiveIntArg
|
|
89890
|
-
).option(
|
|
89891
|
-
"--reply-ceiling-ms <ms>",
|
|
89892
|
-
"conversational pages: hard ceiling on one reply wait, however busy the page stays (default 180000; never below --reply-wait-ms)",
|
|
89893
|
-
positiveIntArg
|
|
89894
|
-
).option(
|
|
89895
|
-
"--reply-max-chars <n>",
|
|
89896
|
-
"conversational pages: cap on each generated chat message (goal and usability; default 300)",
|
|
89897
|
-
intArg({ min: 20, max: 2e3 })
|
|
89898
|
-
).option(
|
|
89899
|
-
"--job-wait-ms <ms>",
|
|
89900
|
-
`goal and usability: while the page shows an in-progress status ("Simulating\u2026", aria-busy, a job "is running"), waits keep waiting with backoff \u2014 and a model 'blocked' is deferred \u2014 up to this budget (default: --reply-ceiling-ms, 180000); it also bounds a busy indicator the app visibly keeps working behind (live progress, a job poll) before it is a hang, and a wait the page documents ("usually takes a minute") can raise it`,
|
|
89901
|
-
positiveIntArg
|
|
89902
|
-
).option(
|
|
89903
|
-
"--deny <pattern>",
|
|
89904
|
-
"a control no mission may click (repeatable): an accessible-name regex (/Archive/i or Archive) or a descriptor role=button;name=Archive. Session-ending (Sign out), destructive (Delete, Revoke, Rotate) and paid (Buy, Run simulation, Generate, Send invite) controls are refused by default",
|
|
89905
|
-
(v, prev) => [...prev, v],
|
|
89906
|
-
[]
|
|
89907
|
-
).option(
|
|
89908
|
-
"--paid <pattern>",
|
|
89909
|
-
"an app control that costs money or credits (repeatable; same syntax as --deny), e.g. /^(Analyze|Draft|Improve)\\b/i: treated like the built-in paid vocabulary \u2014 the budget guard sees it, hang replays never repeat it, and a goal that asks for it may still click it",
|
|
89910
|
-
(v, prev) => [...prev, v],
|
|
89911
|
-
[]
|
|
89912
|
-
).option(
|
|
89913
|
-
"--allow-destructive",
|
|
89914
|
-
"let missions click session-ending, destructive and paid controls (a --deny pattern still holds). A goal run already may click one its goal asks for"
|
|
89915
|
-
).option(
|
|
89916
|
-
"--dialogs <policy>",
|
|
89917
|
-
"native window.confirm/prompt dialogs: dismiss (default) or accept. accept still dismisses one whose message names a session-ending, destructive or paid action the run may not take (without --allow-destructive or a goal asking for it); every dialog is logged"
|
|
89918
|
-
).option(
|
|
90934
|
+
).addOption(GOAL_RUN_OPTIONS.replyWaitMs()).addOption(GOAL_RUN_OPTIONS.replyQuietMs()).addOption(GOAL_RUN_OPTIONS.replyCeilingMs()).addOption(GOAL_RUN_OPTIONS.replyMaxChars()).addOption(GOAL_RUN_OPTIONS.jobWaitMs()).addOption(GOAL_RUN_OPTIONS.deny()).addOption(GOAL_RUN_OPTIONS.paid()).addOption(GOAL_RUN_OPTIONS.allowDestructive()).addOption(GOAL_RUN_OPTIONS.dialogs()).option(
|
|
89919
90935
|
"--allow-writes",
|
|
89920
90936
|
"let a find-out goal (no --success check, ended by report) change the app. By default it is read-only: controls that start a write flow (checkout, upgrade, create, save, submit\u2026) are refused and the write requests an action fires are blocked, unless the goal itself asks for a change"
|
|
89921
90937
|
).option(
|
|
@@ -89923,33 +90939,13 @@ function registerExploreCommands(program2, deps, buildProgram2) {
|
|
|
89923
90939
|
"a write-request path a read-only find-out goal never blocks (repeatable; ** spans segments; a glob starting with https:// matches origin + path, e.g. https://abc.supabase.co/rest/v1/**), beyond the built-in auth-refresh ones (**/refresh*, **/token*, **/oauth/**, **/auth/**/refresh*). The app's background writes outside an action always pass",
|
|
89924
90940
|
(v, prev) => [...prev, v],
|
|
89925
90941
|
[]
|
|
89926
|
-
).option(
|
|
89927
|
-
"--read-rpc <glob>",
|
|
89928
|
-
"a POST request that only READS (repeatable): an RPC-method glob (Estimate*, pkg.Service/Preview*) or a path glob (/api/search*). gRPC-web/Connect Get*/List*/Search*/Find*/Watch*/Stream*/Count*/Describe*/Read* methods are reads already. Reads are never guarded or reported as duplicate writes",
|
|
89929
|
-
(v, prev) => [...prev, v],
|
|
89930
|
-
[]
|
|
89931
|
-
).option("--real", "use live Jev + OpenRouter gateways (requires keys)", false).option("--fake-ai", "use deterministic fake gateways (pipeline smoke only)", false).option("--out <dir>", "directory to write the emitted Recording").option(
|
|
90942
|
+
).addOption(GOAL_RUN_OPTIONS.readRpc()).option("--real", "use live Jev + OpenRouter gateways (requires keys)", false).option("--fake-ai", "use deterministic fake gateways (pipeline smoke only)", false).option("--out <dir>", "directory to write the emitted Recording").option(
|
|
89932
90943
|
"--file-issues",
|
|
89933
90944
|
"file findings as issues (needs a repo: --issue-repo or ~/.jevitate/filing.json); default: drafts only"
|
|
89934
|
-
).option("--issue-repo <owner/name>", "the system-under-test repo findings for THIS target are filed to").
|
|
90945
|
+
).option("--issue-repo <owner/name>", "the system-under-test repo findings for THIS target are filed to").addOption(GOAL_RUN_OPTIONS.hangReplays()).option(
|
|
89935
90946
|
"--hang-replay-writes",
|
|
89936
90947
|
"let hang replays re-send a paid/destructive write the run sent (default: such a hang is reported inconclusive, never replayed)"
|
|
89937
|
-
).option(
|
|
89938
|
-
"--settle-ignore <pattern>",
|
|
89939
|
-
"a request URL pattern the target marks as background (never pending work; repeatable, * wildcard)",
|
|
89940
|
-
(v, prev) => [...prev, v],
|
|
89941
|
-
[]
|
|
89942
|
-
).option("--long-poll-ms <n>", "a request pending this long on an interactive page is a long-poll (default 5000)", nonNegativeIntArg).option(
|
|
89943
|
-
"--api-prefix <path>",
|
|
89944
|
-
"a path prefix whose requests are the app's API in the timing summary (repeatable), e.g. /api/",
|
|
89945
|
-
(v, prev) => [...prev, v],
|
|
89946
|
-
[]
|
|
89947
|
-
).option(
|
|
89948
|
-
"--ignore-no-progress <pattern>",
|
|
89949
|
-
"a route / action label / busy indicator where ui-no-progress is expected (repeatable, * wildcard)",
|
|
89950
|
-
(v, prev) => [...prev, v],
|
|
89951
|
-
[]
|
|
89952
|
-
).option("--jevitate-repo <owner/name>", "where jevitate engine findings are filed (default matt-cochran/jevitate)").option(
|
|
90948
|
+
).addOption(GOAL_RUN_OPTIONS.settleIgnore()).addOption(GOAL_RUN_OPTIONS.longPollMs()).addOption(GOAL_RUN_OPTIONS.apiPrefix()).addOption(GOAL_RUN_OPTIONS.ignoreNoProgress()).option("--jevitate-repo <owner/name>", "where jevitate engine findings are filed (default matt-cochran/jevitate)").option(
|
|
89953
90949
|
"--min-control-coverage <ratio>",
|
|
89954
90950
|
"adversarial: share of the target's controls (0..1) a run must exercise before 'found nothing' is clean (default 0.25); below it the run is inconclusive",
|
|
89955
90951
|
ratioArg
|
|
@@ -89966,15 +90962,7 @@ function registerExploreCommands(program2, deps, buildProgram2) {
|
|
|
89966
90962
|
"backend log source (repeatable; every strategy, incl. usability): file:<path> (tailed from its current end) | docker:<container> (docker logs -f --since 0s) | cmd:<command> (needs --allow-log-cmd). Read-only, operator-declared, never the model's choice. Error/warning lines are correlated to the step they landed during and attached to its transcript evidence, redacted",
|
|
89967
90963
|
(v, prev) => [...prev, v],
|
|
89968
90964
|
[]
|
|
89969
|
-
).option(
|
|
89970
|
-
"--allow-secret-cmd",
|
|
89971
|
-
"opt-in: a --secret-field <descriptor>=cmd:<command> may run its command (in a shell, at type time, 60s timeout) and type its output (operator-declared only; refused otherwise)",
|
|
89972
|
-
false
|
|
89973
|
-
).option(
|
|
89974
|
-
"--secret-cmd-attempts <n>",
|
|
89975
|
-
"#359: how many times one cmd: secret field's command may run in this run (default 3); past it, typing that field fails without running the command again (read-the-code commands usually have side effects)",
|
|
89976
|
-
positiveIntArg
|
|
89977
|
-
).option(
|
|
90965
|
+
).addOption(GOAL_RUN_OPTIONS.allowSecretCmd()).addOption(GOAL_RUN_OPTIONS.secretCmdAttempts()).option(
|
|
89978
90966
|
"--allow-log-cmd",
|
|
89979
90967
|
"opt-in: a --log-source cmd:<command> may run as a subprocess (operator-declared only; refused otherwise)",
|
|
89980
90968
|
false
|
|
@@ -90063,8 +91051,12 @@ function registerExploreCommands(program2, deps, buildProgram2) {
|
|
|
90063
91051
|
emitExplore(fail("E_EXPLORE_ARGS", `unknown strategy ${JSON.stringify(o.strategy)} (one of ${EXPLORE_STRATEGIES.join(", ")})`));
|
|
90064
91052
|
return;
|
|
90065
91053
|
}
|
|
90066
|
-
|
|
90067
|
-
|
|
91054
|
+
let dialogs;
|
|
91055
|
+
try {
|
|
91056
|
+
dialogs = dialogsFromFlags(o);
|
|
91057
|
+
} catch (err) {
|
|
91058
|
+
if (!(err instanceof GoalRunFlagError)) throw err;
|
|
91059
|
+
emitExplore(fail(err.code, err.message));
|
|
90068
91060
|
return;
|
|
90069
91061
|
}
|
|
90070
91062
|
if (o.saveStorageState !== void 0) {
|
|
@@ -90248,19 +91240,12 @@ function registerExploreCommands(program2, deps, buildProgram2) {
|
|
|
90248
91240
|
return;
|
|
90249
91241
|
}
|
|
90250
91242
|
setKillSwitchOutput(o.json === true ? "envelope" : "human");
|
|
90251
|
-
|
|
90252
|
-
|
|
90253
|
-
|
|
90254
|
-
|
|
90255
|
-
|
|
90256
|
-
|
|
90257
|
-
};
|
|
90258
|
-
if (conversation.replyWaitMs !== void 0 && !(Number.isInteger(conversation.replyWaitMs) && conversation.replyWaitMs > 0) || conversation.replyCeilingMs !== void 0 && !(Number.isInteger(conversation.replyCeilingMs) && conversation.replyCeilingMs > 0) || conversation.replyMaxChars !== void 0 && !(Number.isInteger(conversation.replyMaxChars) && conversation.replyMaxChars >= 20 && conversation.replyMaxChars <= 2e3)) {
|
|
90259
|
-
emitExplore(fail("E_EXPLORE_ARGS", "--reply-wait-ms and --reply-ceiling-ms must be positive integers; --reply-max-chars an integer in 20..2000"));
|
|
90260
|
-
return;
|
|
90261
|
-
}
|
|
90262
|
-
if (conversation.jobWaitMs !== void 0 && !(Number.isInteger(conversation.jobWaitMs) && conversation.jobWaitMs > 0)) {
|
|
90263
|
-
emitExplore(fail("E_EXPLORE_ARGS", "--job-wait-ms must be a positive integer"));
|
|
91243
|
+
let conversation;
|
|
91244
|
+
try {
|
|
91245
|
+
conversation = conversationFromFlags(o);
|
|
91246
|
+
} catch (err) {
|
|
91247
|
+
if (!(err instanceof GoalRunFlagError)) throw err;
|
|
91248
|
+
emitExplore(fail(err.code, err.message));
|
|
90264
91249
|
return;
|
|
90265
91250
|
}
|
|
90266
91251
|
try {
|
|
@@ -90301,20 +91286,11 @@ function registerExploreCommands(program2, deps, buildProgram2) {
|
|
|
90301
91286
|
let target;
|
|
90302
91287
|
if (o.url !== void 0) {
|
|
90303
91288
|
try {
|
|
90304
|
-
target = resolveTargetConfig(
|
|
90305
|
-
|
|
90306
|
-
|
|
90307
|
-
|
|
90308
|
-
|
|
90309
|
-
paid: o.paid,
|
|
90310
|
-
readRpc: o.readRpc,
|
|
90311
|
-
...o.allowDestructive === true ? { allowDestructive: true } : {},
|
|
90312
|
-
...o.dialogs === void 0 ? {} : { dialogs: o.dialogs },
|
|
90313
|
-
...o.allowWrites === true ? { allowWrites: true } : {},
|
|
90314
|
-
allowWrite: o.allowWrite,
|
|
90315
|
-
...o.hangReplayWrites === true ? { hangReplayWrites: true } : {},
|
|
90316
|
-
...o.longPollMs === void 0 ? {} : { longPollMs: Number(o.longPollMs) }
|
|
90317
|
-
});
|
|
91289
|
+
target = resolveTargetConfig(
|
|
91290
|
+
loadTargetsFile(deps.explore?.targetsConfigPath),
|
|
91291
|
+
new URL(setupRefFreeUrl(o.url)).origin,
|
|
91292
|
+
targetFlagsFromFlags(o, dialogs)
|
|
91293
|
+
);
|
|
90318
91294
|
} catch (err) {
|
|
90319
91295
|
if (err instanceof TargetConfigError) {
|
|
90320
91296
|
emitExplore(fail(err.code, err.message));
|
|
@@ -90451,12 +91427,9 @@ function registerExploreCommands(program2, deps, buildProgram2) {
|
|
|
90451
91427
|
return;
|
|
90452
91428
|
}
|
|
90453
91429
|
try {
|
|
90454
|
-
secretFields =
|
|
90455
|
-
...o.secretField.map((s3) => parseSecretField(s3, "value", process.env, { allowCmd: o.allowSecretCmd === true })),
|
|
90456
|
-
...o.totp.map((s3) => parseSecretField(s3, "totp", process.env))
|
|
90457
|
-
];
|
|
91430
|
+
secretFields = secretFieldsFromFlags(o);
|
|
90458
91431
|
} catch (err) {
|
|
90459
|
-
if (!(err instanceof
|
|
91432
|
+
if (!(err instanceof GoalRunFlagError)) throw err;
|
|
90460
91433
|
emitExplore(fail(err.code, err.message));
|
|
90461
91434
|
return;
|
|
90462
91435
|
}
|
|
@@ -90791,11 +91764,14 @@ function registerExploreCommands(program2, deps, buildProgram2) {
|
|
|
90791
91764
|
emitExplore(fail("E_EXPLORE_ASSERTION", String(err instanceof Error ? err.message : err)));
|
|
90792
91765
|
return;
|
|
90793
91766
|
}
|
|
90794
|
-
|
|
90795
|
-
|
|
91767
|
+
let successWhen;
|
|
91768
|
+
try {
|
|
91769
|
+
successWhen = successWhenFromFlags(o);
|
|
91770
|
+
} catch (err) {
|
|
91771
|
+
if (!(err instanceof GoalRunFlagError)) throw err;
|
|
91772
|
+
emitExplore(fail(err.code, err.message));
|
|
90796
91773
|
return;
|
|
90797
91774
|
}
|
|
90798
|
-
const successWhen = o.successWhen === "held" || o.successWhen === "final" || o.successWhen === "each" ? o.successWhen : void 0;
|
|
90799
91775
|
const allowlist = resolveExploreAllowlist(setupRefFreeUrl(o.url), o.allow);
|
|
90800
91776
|
let fx;
|
|
90801
91777
|
try {
|
|
@@ -91010,8 +91986,63 @@ function registerVerifyFixCommands(program2, deps) {
|
|
|
91010
91986
|
|
|
91011
91987
|
// src/author-journey-cli.ts
|
|
91012
91988
|
import { existsSync as existsSync24 } from "node:fs";
|
|
91989
|
+
var AUTHOR_GOAL_RUN_FLAGS = [
|
|
91990
|
+
"successWhen",
|
|
91991
|
+
"allowVacuousChecks",
|
|
91992
|
+
"actionDeltas",
|
|
91993
|
+
"secret",
|
|
91994
|
+
"secretField",
|
|
91995
|
+
"totp",
|
|
91996
|
+
"typeFixture",
|
|
91997
|
+
"fixture",
|
|
91998
|
+
"saveStorageState",
|
|
91999
|
+
"replyWaitMs",
|
|
92000
|
+
"replyQuietMs",
|
|
92001
|
+
"replyCeilingMs",
|
|
92002
|
+
"replyMaxChars",
|
|
92003
|
+
"jobWaitMs",
|
|
92004
|
+
"deny",
|
|
92005
|
+
"paid",
|
|
92006
|
+
"allowDestructive",
|
|
92007
|
+
"dialogs",
|
|
92008
|
+
"readRpc",
|
|
92009
|
+
"hangReplays",
|
|
92010
|
+
"settleIgnore",
|
|
92011
|
+
"longPollMs",
|
|
92012
|
+
"apiPrefix",
|
|
92013
|
+
"ignoreNoProgress",
|
|
92014
|
+
"allowSecretCmd",
|
|
92015
|
+
"secretCmdAttempts"
|
|
92016
|
+
];
|
|
92017
|
+
var innerOfReloadThen = (spec) => spec.replace(/^\s*reloadThen:/, "");
|
|
92018
|
+
function takeLines(label2, d) {
|
|
92019
|
+
const lines = [` ${label2}: ${d.outcome}${d.stop === void 0 ? "" : ` (stop: ${d.stop})`}${d.actions === void 0 ? "" : ` \xB7 ${d.actions} action(s), ${d.decisions ?? 0} decision(s)`}`];
|
|
92020
|
+
if (d.reason !== void 0) lines.push(` reason: ${d.reason}`);
|
|
92021
|
+
for (const c of d.checks ?? []) lines.push(` check ${c.passed ? "held" : "FAILED"}: ${c.check}${c.passed ? "" : ` \u2014 ${c.detail}`}`);
|
|
92022
|
+
if (d.resultPath !== void 0) lines.push(` result: ${d.resultPath}`);
|
|
92023
|
+
if (d.transcriptPath !== void 0) lines.push(` transcript: ${d.transcriptPath}`);
|
|
92024
|
+
for (const r of d.recordingPaths ?? []) lines.push(` recording: ${r}`);
|
|
92025
|
+
if (d.screenshotsDir !== void 0) lines.push(` screenshots: ${d.screenshotsDir}`);
|
|
92026
|
+
return lines;
|
|
92027
|
+
}
|
|
92028
|
+
function formatAuthorJourneyHuman(data) {
|
|
92029
|
+
const r = data;
|
|
92030
|
+
const lines = [];
|
|
92031
|
+
if (r.outcome === "authored") {
|
|
92032
|
+
const m = r.journey.metadata;
|
|
92033
|
+
lines.push(`AUTHORED: journey ${m.id} (unpromoted; \`jevitate journey promote ${m.id}\` makes it runnable)`);
|
|
92034
|
+
if (m.params.length > 0) lines.push(` params: ${m.params.join(", ")}`);
|
|
92035
|
+
} else {
|
|
92036
|
+
lines.push(`NOT REACHED: ${r.reason}`);
|
|
92037
|
+
}
|
|
92038
|
+
if (r.takes !== void 0) lines.push(` takes: ${r.takes.succeeded}/${r.takes.run} reached the goal (${r.takes.requested} requested)`);
|
|
92039
|
+
if (r.discovery !== void 0) lines.push(...takeLines("discovery", r.discovery));
|
|
92040
|
+
if (r.outcome === "authored") (r.corroboratingTakes ?? []).forEach((d, i) => lines.push(...takeLines(`take ${i + 2}`, d)));
|
|
92041
|
+
return `${lines.join("\n")}
|
|
92042
|
+
`;
|
|
92043
|
+
}
|
|
91013
92044
|
function registerAuthorJourneyCommands(program2, deps) {
|
|
91014
|
-
withBrowserLaunchFlags(
|
|
92045
|
+
const cmd = withBrowserLaunchFlags(
|
|
91015
92046
|
program2.command("explore-author-journey").description("Jev-driving authors a promotable Journey (authoring plane); never auto-promoted")
|
|
91016
92047
|
).option("--url <url>", "target URL (must be an authorized origin)").option("--goal <text>", "natural-language goal").option(
|
|
91017
92048
|
"--success <spec>",
|
|
@@ -91026,7 +92057,19 @@ function registerAuthorJourneyCommands(program2, deps) {
|
|
|
91026
92057
|
"authorized origin (repeatable); REPLACES the default allowlist when given (the URL's own origin is used only when --allow is omitted entirely) -- include the URL's own origin explicitly if you still need it",
|
|
91027
92058
|
(v, prev) => [...prev, v],
|
|
91028
92059
|
[]
|
|
91029
|
-
).option("--max-actions <n>", "hard cap on executed actions", positiveIntArg).option("--max-decisions <n>", "hard cap on model decisions", positiveIntArg).option("--real", "use live Jev + OpenRouter gateways (requires keys)", false).option("--fake-ai", "use deterministic fake gateways (pipeline smoke only)", false).option("--json", "emit a JSON envelope")
|
|
92060
|
+
).option("--max-actions <n>", "hard cap on executed actions", positiveIntArg).option("--max-decisions <n>", "hard cap on model decisions", positiveIntArg).option("--out <dir>", "directory each take's result, transcript and Recording are written to (default: .jevitate/logs/<date>)").option("--real", "use live Jev + OpenRouter gateways (requires keys)", false).option("--fake-ai", "use deterministic fake gateways (pipeline smoke only)", false).option("--json", "emit a JSON envelope");
|
|
92061
|
+
for (const name of AUTHOR_GOAL_RUN_FLAGS) cmd.addOption(GOAL_RUN_OPTIONS[name]());
|
|
92062
|
+
withScreenshotsFlag(withEmulationFlags(cmd)).addHelpText(
|
|
92063
|
+
"after",
|
|
92064
|
+
[
|
|
92065
|
+
"",
|
|
92066
|
+
"Each take is a full goal run (the same runner and run-shaping flags as `jevitate explore --strategy goal`),",
|
|
92067
|
+
"writing its own result, transcript and Recording (under --out). A take that does not reach the goal",
|
|
92068
|
+
"is reported with its stop reason, its checks' verdicts and those paths. A field code types itself",
|
|
92069
|
+
"(--secret-field/--totp) becomes a secret Journey parameter (secret1, \u2026), passed with --param at replay.",
|
|
92070
|
+
"A reloadThen: check is refused before any browser opens: author with its inner check instead."
|
|
92071
|
+
].join("\n")
|
|
92072
|
+
).action(async function() {
|
|
91030
92073
|
const o = this.opts();
|
|
91031
92074
|
if (o.storageState !== void 0 && !existsSync24(o.storageState)) {
|
|
91032
92075
|
emitJson(program2, fail("E_EXPLORE_ARGS", `storage state not found: ${o.storageState}`));
|
|
@@ -91042,7 +92085,7 @@ function registerAuthorJourneyCommands(program2, deps) {
|
|
|
91042
92085
|
const check2 = parseSuccessSpec(spec);
|
|
91043
92086
|
if (check2.kind === "reloadThen") {
|
|
91044
92087
|
throw new Error(
|
|
91045
|
-
|
|
92088
|
+
`--success ${JSON.stringify(spec)}: a reloadThen check can't be authored into a Journey (a Journey has no reload step); nothing was run. Author with its inner check (--success ${JSON.stringify(innerOfReloadThen(spec))}) and prove persistence with \`jevitate explore --success ${JSON.stringify(spec)}\`, or use a requestMade/responseStatus check`
|
|
91046
92089
|
);
|
|
91047
92090
|
}
|
|
91048
92091
|
successChecks.push(check2);
|
|
@@ -91051,6 +92094,17 @@ function registerAuthorJourneyCommands(program2, deps) {
|
|
|
91051
92094
|
emitJson(program2, fail("E_EXPLORE_ASSERTION", String(err instanceof Error ? err.message : err)));
|
|
91052
92095
|
return;
|
|
91053
92096
|
}
|
|
92097
|
+
let goalRun;
|
|
92098
|
+
try {
|
|
92099
|
+
goalRun = resolveGoalRunFlags(o, o.url, {
|
|
92100
|
+
targetsConfigPath: deps.explore?.targetsConfigPath,
|
|
92101
|
+
warn: (text2) => program2.configureOutput().writeErr?.(text2)
|
|
92102
|
+
});
|
|
92103
|
+
} catch (err) {
|
|
92104
|
+
if (!(err instanceof GoalRunFlagError)) throw err;
|
|
92105
|
+
emitJson(program2, fail(err.code, err.message));
|
|
92106
|
+
return;
|
|
92107
|
+
}
|
|
91054
92108
|
const allowlist = allowWithExtensions(o.url, resolveExploreAllowlist(o.url, o.allow), browserLaunchFromFlags(o));
|
|
91055
92109
|
const bounds2 = {};
|
|
91056
92110
|
if (o.maxActions !== void 0) bounds2.maxActions = Number(o.maxActions);
|
|
@@ -91083,19 +92137,24 @@ function registerAuthorJourneyCommands(program2, deps) {
|
|
|
91083
92137
|
bounds: Object.keys(bounds2).length > 0 ? bounds2 : void 0,
|
|
91084
92138
|
browserPortFactory: deps.explore?.browserPortFactory,
|
|
91085
92139
|
browser: browserLaunchFromFlags(o),
|
|
91086
|
-
...o.storageState !== void 0 ? { storageState: o.storageState } : {}
|
|
92140
|
+
...o.storageState !== void 0 ? { storageState: o.storageState } : {},
|
|
92141
|
+
goalRun,
|
|
92142
|
+
...o.out === void 0 ? {} : { outDir: o.out },
|
|
92143
|
+
usage: authorUsage
|
|
91087
92144
|
}).then((r) => ({ ...r, usage: authorUsage.snapshot() }));
|
|
91088
92145
|
const envelope = ok(result);
|
|
91089
92146
|
if (o.json) {
|
|
91090
92147
|
emitJson(program2, envelope);
|
|
91091
92148
|
if (result.outcome !== "authored") process.exitCode = 1;
|
|
91092
92149
|
} else {
|
|
91093
|
-
|
|
92150
|
+
writeHumanResult(program2, result, formatAuthorJourneyHuman);
|
|
91094
92151
|
process.exitCode = result.outcome === "authored" ? 0 : 1;
|
|
91095
92152
|
}
|
|
91096
92153
|
} catch (err) {
|
|
91097
92154
|
if (err instanceof UnauthorizedExploreTargetError) {
|
|
91098
92155
|
emitJson(program2, fail("E_UNAUTHORIZED_EXPLORE_TARGET", err.message));
|
|
92156
|
+
} else if (err instanceof FixtureNotFoundError) {
|
|
92157
|
+
emitJson(program2, fail("E_EXPLORE_FIXTURE", err.message));
|
|
91099
92158
|
} else {
|
|
91100
92159
|
emitJson(program2, fail("E_AUTHOR_JOURNEY", String(err instanceof Error ? err.message : err)));
|
|
91101
92160
|
}
|
|
@@ -91456,6 +92515,8 @@ async function demoAspect(opts) {
|
|
|
91456
92515
|
successAssertion: opts.successAssertion,
|
|
91457
92516
|
allowlist: env.allowedOrigins,
|
|
91458
92517
|
journeysDir: join48(work, "explored"),
|
|
92518
|
+
// #369: the discovery take's own artifacts stay in the scratch dir with the rest.
|
|
92519
|
+
outDir: join48(work, "explored-runs"),
|
|
91459
92520
|
journeyId: id,
|
|
91460
92521
|
journeyName: aspect,
|
|
91461
92522
|
takes: 1,
|
|
@@ -92536,7 +93597,7 @@ async function promoteMissionTarget(ctx, id) {
|
|
|
92536
93597
|
|
|
92537
93598
|
// src/mission-queue-cli.ts
|
|
92538
93599
|
import { readFileSync as readFileSync29 } from "node:fs";
|
|
92539
|
-
import { Option } from "commander";
|
|
93600
|
+
import { Option as Option2 } from "commander";
|
|
92540
93601
|
|
|
92541
93602
|
// src/mcp-api.ts
|
|
92542
93603
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -93704,7 +94765,7 @@ var CLI_TOOL_SPECS = [
|
|
|
93704
94765
|
},
|
|
93705
94766
|
{
|
|
93706
94767
|
name: "author_journey",
|
|
93707
|
-
description: "`jevitate explore-author-journey`: explore url toward goal (checked by success) several takes, and author an UNPROMOTED Journey from the verified path (promote_journey makes it runnable). Needs real or fakeAi.",
|
|
94768
|
+
description: "`jevitate explore-author-journey`: explore url toward goal (checked by success) several takes, and author an UNPROMOTED Journey from the verified path (promote_journey makes it runnable). Each take is run_exploration's goal run (same run-shaping arguments); a not-reached result carries the discovery take's stop reason, checks and result/transcript/Recording paths. Needs real or fakeAi.",
|
|
93708
94769
|
command: {
|
|
93709
94770
|
path: "explore-author-journey",
|
|
93710
94771
|
params: {
|
|
@@ -93719,9 +94780,43 @@ var CLI_TOOL_SPECS = [
|
|
|
93719
94780
|
maxActions: n2("--max-actions"),
|
|
93720
94781
|
maxDecisions: n2("--max-decisions"),
|
|
93721
94782
|
storageState: session("--storage-state"),
|
|
94783
|
+
out: path("--out"),
|
|
94784
|
+
// #369: every take is explore's goal run — the same run-shaping arguments as run_exploration.
|
|
94785
|
+
successWhen: s2("--success-when", { enum: ["final", "held", "each"] }),
|
|
94786
|
+
allowVacuousChecks: b("--allow-vacuous-checks"),
|
|
94787
|
+
actionDeltas: b("--action-deltas"),
|
|
94788
|
+
typeFixture: { kind: "bound-paths", flag: "--type-fixture" },
|
|
94789
|
+
fixture: path("--fixture"),
|
|
94790
|
+
saveStorageState: session("--save-storage-state"),
|
|
94791
|
+
replyWaitMs: n2("--reply-wait-ms"),
|
|
94792
|
+
replyQuietMs: n2("--reply-quiet-ms"),
|
|
94793
|
+
replyCeilingMs: n2("--reply-ceiling-ms"),
|
|
94794
|
+
replyMaxChars: n2("--reply-max-chars"),
|
|
94795
|
+
jobWaitMs: n2("--job-wait-ms"),
|
|
94796
|
+
deny: many("--deny"),
|
|
94797
|
+
paid: many("--paid"),
|
|
94798
|
+
allowDestructive: b("--allow-destructive"),
|
|
94799
|
+
dialogs: s2("--dialogs", { enum: ["dismiss", "accept"] }),
|
|
94800
|
+
readRpc: many("--read-rpc"),
|
|
94801
|
+
hangReplays: n2("--hang-replays"),
|
|
94802
|
+
settleIgnore: many("--settle-ignore"),
|
|
94803
|
+
longPollMs: n2("--long-poll-ms"),
|
|
94804
|
+
apiPrefix: many("--api-prefix"),
|
|
94805
|
+
ignoreNoProgress: many("--ignore-no-progress"),
|
|
94806
|
+
screenshots: { kind: "screenshots", flag: "--screenshots" },
|
|
94807
|
+
...EMULATION,
|
|
93722
94808
|
...AI
|
|
93723
94809
|
},
|
|
93724
|
-
omitted: {
|
|
94810
|
+
omitted: {
|
|
94811
|
+
"--journeys-dir": OMIT.storeDir,
|
|
94812
|
+
...BROWSER_FLAGS,
|
|
94813
|
+
...JSON_FLAG,
|
|
94814
|
+
"--secret": OMIT.envSecret,
|
|
94815
|
+
"--secret-field": OMIT.envSecret,
|
|
94816
|
+
"--totp": OMIT.envSecret,
|
|
94817
|
+
"--allow-secret-cmd": OMIT.envSecret,
|
|
94818
|
+
"--secret-cmd-attempts": OMIT.envSecret
|
|
94819
|
+
}
|
|
93725
94820
|
}
|
|
93726
94821
|
},
|
|
93727
94822
|
{
|
|
@@ -94886,7 +95981,7 @@ next: jevitate mission run \u2014 then jevitate mission result ${id}
|
|
|
94886
95981
|
}
|
|
94887
95982
|
function registerMissionQueueCommands(program2, mission, deps) {
|
|
94888
95983
|
withEmulationFlags(
|
|
94889
|
-
mission.command("queue <target>").description("enqueue an exploration mission against a PROMOTED target \u2014 only queues; `mission run` drains (MCP queue_exploration)").addOption(new
|
|
95984
|
+
mission.command("queue <target>").description("enqueue an exploration mission against a PROMOTED target \u2014 only queues; `mission run` drains (MCP queue_exploration)").addOption(new Option2("--strategy <strategy>", `${MISSION_STRATEGIES.join(" | ")} (required)`).choices(MISSION_STRATEGIES)).option("--goal <text>", "goal-based: the objective (exactly one of --goal/--feature/--route)").option("--feature <name>", "feature: the capability to test (goal-based: the objective)").option("--route <glob>", "coverage/exploratory/adversarial/feature: an in-scope route glob, e.g. /thread/** (goal-based: the objective)").option("--success <spec>", "goal-based: the independent success check (required there), e.g. urlIncludes:/done \u2014 the `explore --success` page-check forms").option("--max-actions <n>", "budget: max actions (bounded by the queue's ceiling)", positiveIntArg).option("--max-decisions <n>", "budget: max decisions", positiveIntArg).option("--max-candidates <n>", "budget: max candidates", positiveIntArg).option("--invariants <file>", "app-declared invariants JSON file (the `explore --invariants` format; probes GET/HEAD on the target's origins; no authFrom.secret)").option("--record-video", "record a video of the run (headless too), written next to its result; listed as videoPaths").option("--screenshots [mode]", "masked screenshots + index.md next to the result: screens (default, one per distinct screen) | steps (one per step)").option("--evidence-video", "per defect: a captioned evidence clip of its minimal repro + before/at screenshots (defects[].evidence)").option("--persona <name>", "run as this persona: its session in ~/.jevitate/targets.json (personas) for the target's origin \u2014 a name, never a path"),
|
|
94890
95985
|
// #329: a queued mission persists only viewport/device; --geolocation is not offered here.
|
|
94891
95986
|
{ geolocation: false }
|
|
94892
95987
|
).option("--dir <path>", QUEUE_DIR_HELP).option("--targets-dir <path>", "mission targets directory (default: ~/.jevitate/missions/targets)").option("--json", "emit a JSON envelope").action(async function(target) {
|
|
@@ -95809,7 +96904,7 @@ result ${o.result}
|
|
|
95809
96904
|
|
|
95810
96905
|
// src/inbox-cli.ts
|
|
95811
96906
|
import { readFileSync as readFileSync32 } from "node:fs";
|
|
95812
|
-
import { Option as
|
|
96907
|
+
import { Option as Option3 } from "commander";
|
|
95813
96908
|
var INBOX_DIR_HELP = "inbox store directory (default: ~/.jevitate/inbox \u2014 the dir `jevitate mcp` and `jevitate ui` use)";
|
|
95814
96909
|
var REDACTED = "***REDACTED***";
|
|
95815
96910
|
function withInboxFlags(cmd) {
|
|
@@ -95945,7 +97040,7 @@ function registerInboxCommands(program2, deps) {
|
|
|
95945
97040
|
if (args !== void 0) await run(this, "queue_retrieval", args, formatQueuedHuman, "cannot queue the item");
|
|
95946
97041
|
});
|
|
95947
97042
|
queueFlags(
|
|
95948
|
-
inbox.command("queue-action").description("ask a human for a decision \u2014 an 'approval' item by default; only queues (MCP queue_action)").addOption(new
|
|
97043
|
+
inbox.command("queue-action").description("ask a human for a decision \u2014 an 'approval' item by default; only queues (MCP queue_action)").addOption(new Option3("--kind <kind>", "approval (default) | handback | review").choices(["approval", "handback", "review"]))
|
|
95949
97044
|
).action(async function() {
|
|
95950
97045
|
const args = queueArgs(this);
|
|
95951
97046
|
if (args !== void 0) await run(this, "queue_action", args, formatQueuedHuman, "cannot queue the item");
|