@jevitate/cli 0.24.0 → 0.25.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/acceptance-audit-approval-script.d.ts +6 -0
- package/dist/acceptance-audit-approval-script.js +20 -0
- package/dist/acceptance-audit-approval-script.js.map +1 -0
- package/dist/acceptance-audit-cli.d.ts +2 -0
- package/dist/acceptance-audit-cli.js +34 -0
- package/dist/acceptance-audit-cli.js.map +1 -0
- package/dist/acceptance-audit-render.d.ts +4 -0
- package/dist/acceptance-audit-render.js +12 -0
- package/dist/acceptance-audit-render.js.map +1 -0
- package/dist/acceptance-audit-schema.d.ts +32 -0
- package/dist/acceptance-audit-schema.js +19 -0
- package/dist/acceptance-audit-schema.js.map +1 -0
- package/dist/acceptance-audit-suite.d.ts +14 -0
- package/dist/acceptance-audit-suite.js +34 -0
- package/dist/acceptance-audit-suite.js.map +1 -0
- package/dist/acceptance-audit.d.ts +16 -0
- package/dist/acceptance-audit.js +36 -0
- package/dist/acceptance-audit.js.map +1 -0
- package/dist/bin.js +1008 -447
- package/dist/build-info.generated.js +3 -3
- package/dist/explore-goal.d.ts +3 -1
- package/dist/explore-goal.js +1 -0
- package/dist/explore-goal.js.map +1 -1
- package/dist/goal-run-flags.js +2 -2
- package/dist/goal-run-flags.js.map +1 -1
- package/dist/journeeze-hint.d.ts +35 -0
- package/dist/journeeze-hint.js +77 -0
- package/dist/journeeze-hint.js.map +1 -0
- package/dist/journey-cli/review.js +16 -2
- package/dist/journey-cli/review.js.map +1 -1
- package/dist/journey-cli/verify.js +33 -7
- package/dist/journey-cli/verify.js.map +1 -1
- package/dist/journey-verify.d.ts +23 -0
- package/dist/journey-verify.js +34 -0
- package/dist/journey-verify.js.map +1 -1
- package/dist/mcp-cli-tools.js +2 -1
- package/dist/mcp-cli-tools.js.map +1 -1
- package/dist/program.js +2 -0
- package/dist/program.js.map +1 -1
- package/dist/report-cli.js +21 -2
- package/dist/report-cli.js.map +1 -1
- package/dist/ux-usability/done-adjudication.d.ts +2 -2
- package/package.json +10 -10
- package/skills/jevitate-promotion-ready/SKILL.md +5 -1
- package/skills/jevitate-record/SKILL.md +3 -2
package/dist/bin.js
CHANGED
|
@@ -3631,8 +3631,8 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
|
|
|
3631
3631
|
const _normalized = cached(() => normalizeDef(def));
|
|
3632
3632
|
const memo2 = globalConfig.memoizer;
|
|
3633
3633
|
const generateFastpass = (shape3) => {
|
|
3634
|
-
const
|
|
3635
|
-
const syms =
|
|
3634
|
+
const normalized2 = _normalized.value;
|
|
3635
|
+
const syms = normalized2.symbolKeys;
|
|
3636
3636
|
const doc = new Doc(["payload", "ctx"], { shape: shape3, inst, memo: memo2, syms });
|
|
3637
3637
|
const parseStr = (k) => `shape[${k}]._zod.run({ value: input[${k}], issues: [] }, ctx)`;
|
|
3638
3638
|
const prefixStr = (id, k) => `
|
|
@@ -3650,11 +3650,11 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
|
|
|
3650
3650
|
doc.write(`const input = payload.value;`);
|
|
3651
3651
|
const ids = /* @__PURE__ */ Object.create(null);
|
|
3652
3652
|
let counter = 0;
|
|
3653
|
-
for (const key2 of
|
|
3653
|
+
for (const key2 of normalized2.allKeys) {
|
|
3654
3654
|
ids[key2] = `key_${counter++}`;
|
|
3655
3655
|
}
|
|
3656
3656
|
doc.write(memo2 ? `const newResult = memo.alloc(inst, payload, {}, ctx);` : `const newResult = {};`);
|
|
3657
|
-
for (const key2 of
|
|
3657
|
+
for (const key2 of normalized2.allKeys) {
|
|
3658
3658
|
if (key2 === "__proto__")
|
|
3659
3659
|
continue;
|
|
3660
3660
|
const id = ids[key2];
|
|
@@ -19483,23 +19483,23 @@ function fromJSONSchema(schema, params) {
|
|
|
19483
19483
|
if (typeof schema === "boolean") {
|
|
19484
19484
|
return schema ? z.any() : z.never();
|
|
19485
19485
|
}
|
|
19486
|
-
let
|
|
19486
|
+
let normalized2;
|
|
19487
19487
|
try {
|
|
19488
|
-
|
|
19488
|
+
normalized2 = JSON.parse(JSON.stringify(schema));
|
|
19489
19489
|
} catch {
|
|
19490
19490
|
throw new Error("fromJSONSchema input is not valid JSON (possibly cyclic); use $defs/$ref for recursive schemas");
|
|
19491
19491
|
}
|
|
19492
|
-
const version2 = detectVersion(
|
|
19493
|
-
const defs =
|
|
19492
|
+
const version2 = detectVersion(normalized2, params?.defaultTarget);
|
|
19493
|
+
const defs = normalized2.$defs || normalized2.definitions || {};
|
|
19494
19494
|
const ctx = {
|
|
19495
19495
|
version: version2,
|
|
19496
19496
|
defs,
|
|
19497
19497
|
refs: /* @__PURE__ */ new Map(),
|
|
19498
19498
|
processing: /* @__PURE__ */ new Set(),
|
|
19499
|
-
rootSchema:
|
|
19499
|
+
rootSchema: normalized2,
|
|
19500
19500
|
registry: params?.registry ?? globalRegistry
|
|
19501
19501
|
};
|
|
19502
|
-
return convertSchema(
|
|
19502
|
+
return convertSchema(normalized2, ctx);
|
|
19503
19503
|
}
|
|
19504
19504
|
|
|
19505
19505
|
// ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/visit.js
|
|
@@ -21423,15 +21423,15 @@ function asNumber(value) {
|
|
|
21423
21423
|
return numericValue;
|
|
21424
21424
|
}
|
|
21425
21425
|
function normalizeObject(obj, normalizer) {
|
|
21426
|
-
const
|
|
21426
|
+
const normalized2 = {};
|
|
21427
21427
|
for (const u in obj) {
|
|
21428
21428
|
if (hasOwnProperty(obj, u)) {
|
|
21429
21429
|
const v = obj[u];
|
|
21430
21430
|
if (v === void 0 || v === null) continue;
|
|
21431
|
-
|
|
21431
|
+
normalized2[normalizer(u)] = asNumber(v);
|
|
21432
21432
|
}
|
|
21433
21433
|
}
|
|
21434
|
-
return
|
|
21434
|
+
return normalized2;
|
|
21435
21435
|
}
|
|
21436
21436
|
function formatOffset(offset2, format) {
|
|
21437
21437
|
const hours = Math.trunc(Math.abs(offset2 / 60)), minutes = Math.trunc(Math.abs(offset2 % 60)), sign = offset2 >= 0 ? "+" : "-";
|
|
@@ -22432,7 +22432,7 @@ var Duration = class _Duration {
|
|
|
22432
22432
|
* @private
|
|
22433
22433
|
*/
|
|
22434
22434
|
static normalizeUnit(unit) {
|
|
22435
|
-
const
|
|
22435
|
+
const normalized2 = {
|
|
22436
22436
|
year: "years",
|
|
22437
22437
|
years: "years",
|
|
22438
22438
|
quarter: "quarters",
|
|
@@ -22452,8 +22452,8 @@ var Duration = class _Duration {
|
|
|
22452
22452
|
millisecond: "milliseconds",
|
|
22453
22453
|
milliseconds: "milliseconds"
|
|
22454
22454
|
}[unit ? unit.toLowerCase() : unit];
|
|
22455
|
-
if (!
|
|
22456
|
-
return
|
|
22455
|
+
if (!normalized2) throw new InvalidUnitError(unit);
|
|
22456
|
+
return normalized2;
|
|
22457
22457
|
}
|
|
22458
22458
|
/**
|
|
22459
22459
|
* Check if an object is a Duration. Works across context boundaries
|
|
@@ -24356,7 +24356,7 @@ var orderedWeekUnits = [
|
|
|
24356
24356
|
];
|
|
24357
24357
|
var orderedOrdinalUnits = ["year", "ordinal", "hour", "minute", "second", "millisecond"];
|
|
24358
24358
|
function normalizeUnit(unit) {
|
|
24359
|
-
const
|
|
24359
|
+
const normalized2 = {
|
|
24360
24360
|
year: "year",
|
|
24361
24361
|
years: "year",
|
|
24362
24362
|
month: "month",
|
|
@@ -24382,8 +24382,8 @@ function normalizeUnit(unit) {
|
|
|
24382
24382
|
weekyears: "weekYear",
|
|
24383
24383
|
ordinal: "ordinal"
|
|
24384
24384
|
}[unit.toLowerCase()];
|
|
24385
|
-
if (!
|
|
24386
|
-
return
|
|
24385
|
+
if (!normalized2) throw new InvalidUnitError(unit);
|
|
24386
|
+
return normalized2;
|
|
24387
24387
|
}
|
|
24388
24388
|
function normalizeUnitWithLocalWeeks(unit) {
|
|
24389
24389
|
switch (unit.toLowerCase()) {
|
|
@@ -24682,9 +24682,9 @@ var DateTime = class _DateTime {
|
|
|
24682
24682
|
return _DateTime.invalid(unsupportedZone(zoneToUse));
|
|
24683
24683
|
}
|
|
24684
24684
|
const loc = Locale.fromObject(opts);
|
|
24685
|
-
const
|
|
24686
|
-
const { minDaysInFirstWeek, startOfWeek } = usesLocalWeekValues(
|
|
24687
|
-
const tsNow = Settings.now(), offsetProvis = !isUndefined(opts.specificOffset) ? opts.specificOffset : zoneToUse.offset(tsNow), containsOrdinal = !isUndefined(
|
|
24685
|
+
const normalized2 = normalizeObject(obj, normalizeUnitWithLocalWeeks);
|
|
24686
|
+
const { minDaysInFirstWeek, startOfWeek } = usesLocalWeekValues(normalized2, loc);
|
|
24687
|
+
const tsNow = Settings.now(), offsetProvis = !isUndefined(opts.specificOffset) ? opts.specificOffset : zoneToUse.offset(tsNow), containsOrdinal = !isUndefined(normalized2.ordinal), containsGregorYear = !isUndefined(normalized2.year), containsGregorMD = !isUndefined(normalized2.month) || !isUndefined(normalized2.day), containsGregor = containsGregorYear || containsGregorMD, definiteWeekDef = normalized2.weekYear || normalized2.weekNumber;
|
|
24688
24688
|
if ((containsGregor || containsOrdinal) && definiteWeekDef) {
|
|
24689
24689
|
throw new ConflictingSpecificationError(
|
|
24690
24690
|
"Can't mix weekYear/weekNumber units with year/month/day or ordinals"
|
|
@@ -24693,7 +24693,7 @@ var DateTime = class _DateTime {
|
|
|
24693
24693
|
if (containsGregorMD && containsOrdinal) {
|
|
24694
24694
|
throw new ConflictingSpecificationError("Can't mix ordinal dates with month/day");
|
|
24695
24695
|
}
|
|
24696
|
-
const useWeekData = definiteWeekDef ||
|
|
24696
|
+
const useWeekData = definiteWeekDef || normalized2.weekday && !containsGregor;
|
|
24697
24697
|
let units, defaultValues, objNow = tsToObj(tsNow, offsetProvis);
|
|
24698
24698
|
if (useWeekData) {
|
|
24699
24699
|
units = orderedWeekUnits;
|
|
@@ -24709,29 +24709,29 @@ var DateTime = class _DateTime {
|
|
|
24709
24709
|
}
|
|
24710
24710
|
let foundFirst = false;
|
|
24711
24711
|
for (const u of units) {
|
|
24712
|
-
const v =
|
|
24712
|
+
const v = normalized2[u];
|
|
24713
24713
|
if (!isUndefined(v)) {
|
|
24714
24714
|
foundFirst = true;
|
|
24715
24715
|
} else if (foundFirst) {
|
|
24716
|
-
|
|
24716
|
+
normalized2[u] = defaultValues[u];
|
|
24717
24717
|
} else {
|
|
24718
|
-
|
|
24718
|
+
normalized2[u] = objNow[u];
|
|
24719
24719
|
}
|
|
24720
24720
|
}
|
|
24721
|
-
const higherOrderInvalid = useWeekData ? hasInvalidWeekData(
|
|
24721
|
+
const higherOrderInvalid = useWeekData ? hasInvalidWeekData(normalized2, minDaysInFirstWeek, startOfWeek) : containsOrdinal ? hasInvalidOrdinalData(normalized2) : hasInvalidGregorianData(normalized2), invalid = higherOrderInvalid || hasInvalidTimeData(normalized2);
|
|
24722
24722
|
if (invalid) {
|
|
24723
24723
|
return _DateTime.invalid(invalid);
|
|
24724
24724
|
}
|
|
24725
|
-
const gregorian = useWeekData ? weekToGregorian(
|
|
24725
|
+
const gregorian = useWeekData ? weekToGregorian(normalized2, minDaysInFirstWeek, startOfWeek) : containsOrdinal ? ordinalToGregorian(normalized2) : normalized2, [tsFinal, offsetFinal] = objToTS(gregorian, offsetProvis, zoneToUse), inst = new _DateTime({
|
|
24726
24726
|
ts: tsFinal,
|
|
24727
24727
|
zone: zoneToUse,
|
|
24728
24728
|
o: offsetFinal,
|
|
24729
24729
|
loc
|
|
24730
24730
|
});
|
|
24731
|
-
if (
|
|
24731
|
+
if (normalized2.weekday && containsGregor && obj.weekday !== inst.weekday) {
|
|
24732
24732
|
return _DateTime.invalid(
|
|
24733
24733
|
"mismatched weekday",
|
|
24734
|
-
`you can't specify both a weekday of ${
|
|
24734
|
+
`you can't specify both a weekday of ${normalized2.weekday} and a date of ${inst.toISO()}`
|
|
24735
24735
|
);
|
|
24736
24736
|
}
|
|
24737
24737
|
if (!inst.isValid) {
|
|
@@ -25380,9 +25380,9 @@ var DateTime = class _DateTime {
|
|
|
25380
25380
|
*/
|
|
25381
25381
|
set(values2) {
|
|
25382
25382
|
if (!this.isValid) return this;
|
|
25383
|
-
const
|
|
25384
|
-
const { minDaysInFirstWeek, startOfWeek } = usesLocalWeekValues(
|
|
25385
|
-
const settingWeekStuff = !isUndefined(
|
|
25383
|
+
const normalized2 = normalizeObject(values2, normalizeUnitWithLocalWeeks);
|
|
25384
|
+
const { minDaysInFirstWeek, startOfWeek } = usesLocalWeekValues(normalized2, this.loc);
|
|
25385
|
+
const settingWeekStuff = !isUndefined(normalized2.weekYear) || !isUndefined(normalized2.weekNumber) || !isUndefined(normalized2.weekday), containsOrdinal = !isUndefined(normalized2.ordinal), containsGregorYear = !isUndefined(normalized2.year), containsGregorMD = !isUndefined(normalized2.month) || !isUndefined(normalized2.day), containsGregor = containsGregorYear || containsGregorMD, definiteWeekDef = normalized2.weekYear || normalized2.weekNumber;
|
|
25386
25386
|
if ((containsGregor || containsOrdinal) && definiteWeekDef) {
|
|
25387
25387
|
throw new ConflictingSpecificationError(
|
|
25388
25388
|
"Can't mix weekYear/weekNumber units with year/month/day or ordinals"
|
|
@@ -25394,15 +25394,15 @@ var DateTime = class _DateTime {
|
|
|
25394
25394
|
let mixed;
|
|
25395
25395
|
if (settingWeekStuff) {
|
|
25396
25396
|
mixed = weekToGregorian(
|
|
25397
|
-
{ ...gregorianToWeek(this.c, minDaysInFirstWeek, startOfWeek), ...
|
|
25397
|
+
{ ...gregorianToWeek(this.c, minDaysInFirstWeek, startOfWeek), ...normalized2 },
|
|
25398
25398
|
minDaysInFirstWeek,
|
|
25399
25399
|
startOfWeek
|
|
25400
25400
|
);
|
|
25401
|
-
} else if (!isUndefined(
|
|
25402
|
-
mixed = ordinalToGregorian({ ...gregorianToOrdinal(this.c), ...
|
|
25401
|
+
} else if (!isUndefined(normalized2.ordinal)) {
|
|
25402
|
+
mixed = ordinalToGregorian({ ...gregorianToOrdinal(this.c), ...normalized2 });
|
|
25403
25403
|
} else {
|
|
25404
|
-
mixed = { ...this.toObject(), ...
|
|
25405
|
-
if (isUndefined(
|
|
25404
|
+
mixed = { ...this.toObject(), ...normalized2 };
|
|
25405
|
+
if (isUndefined(normalized2.day)) {
|
|
25406
25406
|
mixed.day = Math.min(daysInMonth(mixed.year, mixed.month), mixed.day);
|
|
25407
25407
|
}
|
|
25408
25408
|
}
|
|
@@ -28136,8 +28136,8 @@ function idFromDigest(bytes) {
|
|
|
28136
28136
|
function extensionIdForPath(absDir, platform2 = process.platform) {
|
|
28137
28137
|
if (platform2 !== "win32")
|
|
28138
28138
|
return idFromDigest(Buffer.from(absDir, "utf8"));
|
|
28139
|
-
const
|
|
28140
|
-
return idFromDigest(Buffer.from(
|
|
28139
|
+
const normalized2 = /^[a-z]:/.test(absDir) ? absDir[0].toUpperCase() + absDir.slice(1) : absDir;
|
|
28140
|
+
return idFromDigest(Buffer.from(normalized2, "utf16le"));
|
|
28141
28141
|
}
|
|
28142
28142
|
function extensionIdForKey(base64Key) {
|
|
28143
28143
|
return idFromDigest(Buffer.from(base64Key.replace(/\s+/g, ""), "base64"));
|
|
@@ -37215,8 +37215,8 @@ function isSecretField(inputType, autocomplete, hints) {
|
|
|
37215
37215
|
const raw = list2[i];
|
|
37216
37216
|
if (typeof raw !== "string" || raw === "")
|
|
37217
37217
|
continue;
|
|
37218
|
-
const
|
|
37219
|
-
if (credential.test(
|
|
37218
|
+
const normalized2 = raw.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/([A-Za-z])([0-9])/g, "$1 $2").replace(/[_\-.:[\]/]+/g, " ").toLowerCase();
|
|
37219
|
+
if (credential.test(normalized2))
|
|
37220
37220
|
return true;
|
|
37221
37221
|
}
|
|
37222
37222
|
return false;
|
|
@@ -38641,8 +38641,8 @@ async function readSnapshot(page, opts) {
|
|
|
38641
38641
|
// ../explore/dist/settle-config.js
|
|
38642
38642
|
var DEFAULT_LONG_POLL_MS = 5e3;
|
|
38643
38643
|
function globToRegExp2(glob) {
|
|
38644
|
-
const
|
|
38645
|
-
return new RegExp(`^${
|
|
38644
|
+
const escaped2 = glob.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
38645
|
+
return new RegExp(`^${escaped2}$`);
|
|
38646
38646
|
}
|
|
38647
38647
|
function urlMatcher(patterns) {
|
|
38648
38648
|
const compiled = (patterns ?? []).map((p) => ({ full: p.includes("://"), re: globToRegExp2(p) }));
|
|
@@ -39577,6 +39577,31 @@ function visibleBusyIndicatorContainer() {
|
|
|
39577
39577
|
}
|
|
39578
39578
|
return null;
|
|
39579
39579
|
}
|
|
39580
|
+
function visibleBusyIndicatorText() {
|
|
39581
|
+
const shown2 = (el) => {
|
|
39582
|
+
const r = el.getBoundingClientRect();
|
|
39583
|
+
const s3 = window.getComputedStyle(el);
|
|
39584
|
+
return r.width > 0 && r.height > 0 && s3.visibility !== "hidden" && s3.display !== "none" && s3.opacity !== "0";
|
|
39585
|
+
};
|
|
39586
|
+
const progressbar = '[role="progressbar"]:not([aria-valuenow])';
|
|
39587
|
+
const selectors = ['[aria-busy="true"]', progressbar, '[class*="spinner" i]', '[class*="animate-spin" i]'];
|
|
39588
|
+
const notificationContainer = '[role="alert"], [data-sonner-toast], [class*="toast" i], [class*="snackbar" i], [class*="notification" i]';
|
|
39589
|
+
const timerLabel = /timer|countdown|auto.?close/i;
|
|
39590
|
+
const isNotificationTimer = (el) => el.closest(notificationContainer) !== null || timerLabel.test(`${el.getAttribute("aria-label") ?? ""} ${el.getAttribute("title") ?? ""}`);
|
|
39591
|
+
const words3 = (el) => el === null ? "" : (el.getAttribute("aria-label") ?? el.innerText ?? el.textContent ?? "").replace(/\s+/g, " ").trim();
|
|
39592
|
+
for (const sel of selectors) {
|
|
39593
|
+
for (const el of Array.from(document.querySelectorAll(sel))) {
|
|
39594
|
+
if (!shown2(el))
|
|
39595
|
+
continue;
|
|
39596
|
+
if (sel === progressbar && isNotificationTimer(el))
|
|
39597
|
+
continue;
|
|
39598
|
+
const own2 = words3(el);
|
|
39599
|
+
const text2 = own2 !== "" ? own2 : words3(el.closest('[role="status"], [aria-live]:not([aria-live="off"])'));
|
|
39600
|
+
return text2 === "" ? null : text2.slice(0, 200);
|
|
39601
|
+
}
|
|
39602
|
+
}
|
|
39603
|
+
return null;
|
|
39604
|
+
}
|
|
39580
39605
|
function pendingEvidence(pending, now2) {
|
|
39581
39606
|
return [...pending].sort((a, b2) => a.startedAt - b2.startedAt).map((r) => ({ endpoint: endpointOf(r.method, r.url), url: redactUrl(r.url), ageMs: Math.max(0, now2 - r.startedAt) }));
|
|
39582
39607
|
}
|
|
@@ -40828,7 +40853,8 @@ async function perceive(page, opts = {}) {
|
|
|
40828
40853
|
const busySince = clock.now();
|
|
40829
40854
|
const remaining = Math.max(1, ceiling - (clock.now() - started));
|
|
40830
40855
|
const gone = await clockBounded(page.waitForFunction(visibleBusyIndicator, true, { timeout: remaining, polling: "raf" }).then(() => true, (e) => !(e instanceof Error && e.name === "TimeoutError")), remaining, false);
|
|
40831
|
-
|
|
40856
|
+
const busyText = gone ? null : await page.evaluate(visibleBusyIndicatorText).catch(() => null);
|
|
40857
|
+
if (!gone && !ignoreNoProgress(busy) && !(busyText !== null && ignoreDeclared(busyText)) && !ignoreNoProgress(hangRoute(page.url()))) {
|
|
40832
40858
|
stuckBusy = busy;
|
|
40833
40859
|
stuckBusyContainer = await page.evaluate(visibleBusyIndicatorContainer).catch(() => null);
|
|
40834
40860
|
const now2 = await page.evaluate(visibleBusyIndicator).catch(() => null);
|
|
@@ -44111,6 +44137,7 @@ var MAX_REPEAT_TYPE_SIGNALS = 3;
|
|
|
44111
44137
|
var MAX_QUIET_WAITS = 3;
|
|
44112
44138
|
var MAX_MOVING_SCROLLS = 12;
|
|
44113
44139
|
var LAST_CHANCE_NOTE = "no progress: the last steps left the page unchanged and you have seen the whole page \u2014 act on a visible control, report the answer, or say done/blocked now";
|
|
44140
|
+
var LAST_CHANCE_NO_WRITE_NOTE = "no progress: the last steps left the page unchanged and you have seen the whole page, but no write of this run has succeeded yet \u2014 the goal asks for a change the app must receive: go back and press the control that submits it (Import, Save, Send\u2026), or say blocked if the app offers none";
|
|
44114
44141
|
|
|
44115
44142
|
// ../explore/dist/run-depth.js
|
|
44116
44143
|
var OPEN_ENDED_MIN_ACTIONS = 12;
|
|
@@ -44985,11 +45012,11 @@ function hostProbe(signals = createResourceSignals(), thresholds = DEFAULT_PRESS
|
|
|
44985
45012
|
function initHost(ctx, cfg) {
|
|
44986
45013
|
ctx.probeHost = cfg.hostProbe ?? hostProbe();
|
|
44987
45014
|
ctx.judgeHost = async () => cfg.hostHealth === void 0 ? { host: await ctx.probeHost(), starved: null } : cfg.hostHealth.judge();
|
|
44988
|
-
ctx.degradedStop = (
|
|
44989
|
-
cfg.hostHealth?.markDegraded({ finding:
|
|
45015
|
+
ctx.degradedStop = (finding4, detail, starved) => {
|
|
45016
|
+
cfg.hostHealth?.markDegraded({ finding: finding4, detail, step: Math.max(0, ctx.transcript.entries().length - 1) }, starved);
|
|
44990
45017
|
ctx.failure = {
|
|
44991
45018
|
kind: "degraded-environment",
|
|
44992
|
-
message: `environment-degraded ${
|
|
45019
|
+
message: `environment-degraded ${finding4} (${detail}) while the host was starved: ${starved} \u2014 not an app finding`
|
|
44993
45020
|
};
|
|
44994
45021
|
ctx.stop = "inconclusive";
|
|
44995
45022
|
};
|
|
@@ -45400,6 +45427,7 @@ function initLoopState(ctx) {
|
|
|
45400
45427
|
ctx.fixtureAttached = false;
|
|
45401
45428
|
ctx.hang = void 0;
|
|
45402
45429
|
ctx.outcome = null;
|
|
45430
|
+
ctx.actionableControl = null;
|
|
45403
45431
|
ctx.incomplete = null;
|
|
45404
45432
|
ctx.endedOnRejectedDone = false;
|
|
45405
45433
|
}
|
|
@@ -46766,7 +46794,7 @@ function busyIndicatorOf(hang) {
|
|
|
46766
46794
|
return null;
|
|
46767
46795
|
if (hang.element !== void 0 && hang.element !== "")
|
|
46768
46796
|
return hang.element;
|
|
46769
|
-
const m = /^a busy indicator \((.+)\) never went away within \d+ms(?: \(still so after .*\))?$/.exec(hang.detail.trim());
|
|
46797
|
+
const m = /^a busy indicator \((.+)\) never went away within \d+ms(?: \((?:still so after|the page stopped showing work) .*\))?$/.exec(hang.detail.trim());
|
|
46770
46798
|
return m?.[1] ?? null;
|
|
46771
46799
|
}
|
|
46772
46800
|
function legacySelectorForm(indicator) {
|
|
@@ -46987,8 +47015,8 @@ async function recordCoverageHang(p) {
|
|
|
46987
47015
|
...p.perceive === void 0 ? {} : { perceive: p.perceive },
|
|
46988
47016
|
...p.safety === void 0 ? {} : { safety: p.safety }
|
|
46989
47017
|
});
|
|
46990
|
-
const
|
|
46991
|
-
p.found.set(fingerprint2, { ...
|
|
47018
|
+
const finding4 = hangFinding(hang, p.steps, index, reproduction);
|
|
47019
|
+
p.found.set(fingerprint2, { ...finding4, repro: { ...finding4.repro, recording: p.recording } });
|
|
46992
47020
|
return "recorded";
|
|
46993
47021
|
}
|
|
46994
47022
|
|
|
@@ -48178,6 +48206,7 @@ function initGuards(ctx, cfg) {
|
|
|
48178
48206
|
ctx.jobWaitedMs = 0;
|
|
48179
48207
|
ctx.overlayWaitedMs = 0;
|
|
48180
48208
|
ctx.hangWorkWaitedMs = 0;
|
|
48209
|
+
ctx.hangWorkSince = null;
|
|
48181
48210
|
ctx.documentedBudgetMs = 0;
|
|
48182
48211
|
}
|
|
48183
48212
|
|
|
@@ -48418,86 +48447,6 @@ function unreadShown(ctx, step) {
|
|
|
48418
48447
|
return ctx.observed.unreadShownIn(shownOn(step));
|
|
48419
48448
|
}
|
|
48420
48449
|
|
|
48421
|
-
// ../explore/dist/goal-loop/handle-report.js
|
|
48422
|
-
async function handleReport(ctx, step) {
|
|
48423
|
-
const { cfg } = ctx;
|
|
48424
|
-
const { snap, record: record2 } = step;
|
|
48425
|
-
if (ctx.replyGoal) {
|
|
48426
|
-
if (ctx.awaitingReply && ctx.lastTurn !== null && ctx.replyWaitedMs < ctx.replyWaitMs) {
|
|
48427
|
-
const t0 = ctx.now();
|
|
48428
|
-
const listen = ctx.replyWaitMs - ctx.replyWaitedMs;
|
|
48429
|
-
const reply = await waitForReply(ctx.page, { secrets: ctx.secrets, ...ctx.lastTurn, timeoutMs: listen, ceilingMs: listen, quietMs: ctx.replyQuietMs });
|
|
48430
|
-
ctx.replyWaitedMs += ctx.now() - t0;
|
|
48431
|
-
if (reply.received) {
|
|
48432
|
-
ctx.conversation.latestReply = reply.text;
|
|
48433
|
-
ctx.replies.add(snap.url, reply.text);
|
|
48434
|
-
ctx.awaitingReply = false;
|
|
48435
|
-
ctx.busyWaitedMs = 0;
|
|
48436
|
-
ctx.history.push(`waited for the reply \u2192 reply: ${quote(reply.text, 300)}`);
|
|
48437
|
-
}
|
|
48438
|
-
}
|
|
48439
|
-
ctx.noteReplyText(snap.url, await readPageText(ctx.page, ctx.secrets));
|
|
48440
|
-
}
|
|
48441
|
-
const replyPages = ctx.replyGoal ? ctx.replies.pages() : null;
|
|
48442
|
-
if (replyPages === null)
|
|
48443
|
-
await readBackProbe(ctx, step);
|
|
48444
|
-
const verdict = replyPages !== null && replyPages.length === 0 ? {
|
|
48445
|
-
accept: false,
|
|
48446
|
-
reason: ctx.preSend === null ? "no reply observed: no message was sent yet \u2014 text on the page before the conversation is not a reply" : `no reply observed: no new message appeared after the send within the reply wait (${Math.round(ctx.replyWaitMs / 1e3)}s)`,
|
|
48447
|
-
answer: null
|
|
48448
|
-
} : await reportAnswer(cfg.gen, {
|
|
48449
|
-
goal: cfg.goal,
|
|
48450
|
-
url: snap.url,
|
|
48451
|
-
pages: replyPages ?? ctx.observed.pages(),
|
|
48452
|
-
history: ctx.history,
|
|
48453
|
-
secrets: ctx.secrets,
|
|
48454
|
-
judge: cfg.judge,
|
|
48455
|
-
vetoes: ctx.vetoes,
|
|
48456
|
-
// #238: "none exists" is an answer only on observed pages that cover the app enough.
|
|
48457
|
-
...replyPages === null ? { topNav: ctx.observed.topNavigation(), ownInputs: ctx.observed.ownInputs(), unreadWrites: ctx.observed.unreadWrites() } : {}
|
|
48458
|
-
}).then((v) => v.accept && ctx.writeGoal && !ctx.wroteOk && v.answer.absent !== true ? { accept: false, reason: UNSAVED_WRITE_REASON, answer: v.answer } : v).catch((e) => ({ accept: false, reason: `no answer could be generated: ${firstLine(e)}`, answer: null }));
|
|
48459
|
-
if (verdict.accept) {
|
|
48460
|
-
const deferred = deferEnding(ctx, step, "report");
|
|
48461
|
-
if (deferred !== null) {
|
|
48462
|
-
ctx.deferredAnswer = verdict.answer;
|
|
48463
|
-
record2(false, deferred, { answer: verdict.answer, origin: "engine" });
|
|
48464
|
-
return "continue";
|
|
48465
|
-
}
|
|
48466
|
-
}
|
|
48467
|
-
if (verdict.accept) {
|
|
48468
|
-
const on = replyPages === null ? "the observed pages" : "the reply observed after the send";
|
|
48469
|
-
record2(true, `report accepted: answer grounded on ${on} (${verdict.answer.evidence.length} claim(s))`, {
|
|
48470
|
-
answer: verdict.answer
|
|
48471
|
-
});
|
|
48472
|
-
ctx.answer = verdict.answer;
|
|
48473
|
-
ctx.outcome = { status: "completed", verifiedBy: "grounded-answer" };
|
|
48474
|
-
ctx.stop = "done";
|
|
48475
|
-
return "stop";
|
|
48476
|
-
}
|
|
48477
|
-
if (verdict.unconfirmedWrite === true && !ctx.readBackAsked) {
|
|
48478
|
-
ctx.readBackAsked = true;
|
|
48479
|
-
ctx.history.push(`report deferred: ${verdict.reason}`);
|
|
48480
|
-
record2(false, `report deferred: ${verdict.reason}`, { answer: verdict.answer, origin: "engine" });
|
|
48481
|
-
return "continue";
|
|
48482
|
-
}
|
|
48483
|
-
ctx.reportRejections += 1;
|
|
48484
|
-
for (const e of verdict.answer?.evidence ?? [])
|
|
48485
|
-
if (e.grounded)
|
|
48486
|
-
ctx.reportClaims.push(e);
|
|
48487
|
-
ctx.lastReportNotFound = verdict.answer === null && verdict.reason === NO_ANSWER_REASON || verdict.notAnswer === true;
|
|
48488
|
-
ctx.lastAbsenceUncovered = verdict.absenceUncovered === true ? verdict.reason : null;
|
|
48489
|
-
ctx.history.push(`report rejected: ${verdict.reason} \u2014 find the answer on the page before reporting`);
|
|
48490
|
-
record2(false, `report rejected (${ctx.reportRejections}/${MAX_REPORT_REJECTIONS}): ${verdict.reason}`, {
|
|
48491
|
-
answer: verdict.answer
|
|
48492
|
-
});
|
|
48493
|
-
if (ctx.reportRejections >= MAX_REPORT_REJECTIONS && deferEnding(ctx, step, "report") === null) {
|
|
48494
|
-
ctx.incomplete = `the model reported an answer ${ctx.reportRejections} times, but ${verdict.reason}`;
|
|
48495
|
-
ctx.stop = "blocked";
|
|
48496
|
-
return "stop";
|
|
48497
|
-
}
|
|
48498
|
-
return "continue";
|
|
48499
|
-
}
|
|
48500
|
-
|
|
48501
48450
|
// ../explore/dist/goal-loop/handle-done.js
|
|
48502
48451
|
var NO_WRITE_YET = "no write of this run has succeeded yet \u2014 the goal asks for a change the app must receive: go back and press the control that submits it (Import, Save, Send\u2026)";
|
|
48503
48452
|
async function unreadWriteHold(ctx, step, outcome) {
|
|
@@ -48607,7 +48556,146 @@ async function handleDone(ctx, step) {
|
|
|
48607
48556
|
return "next";
|
|
48608
48557
|
}
|
|
48609
48558
|
|
|
48559
|
+
// ../explore/dist/goal-loop/handle-report.js
|
|
48560
|
+
async function handleReport(ctx, step) {
|
|
48561
|
+
const { cfg } = ctx;
|
|
48562
|
+
const { snap, record: record2 } = step;
|
|
48563
|
+
if (ctx.replyGoal) {
|
|
48564
|
+
if (ctx.awaitingReply && ctx.lastTurn !== null && ctx.replyWaitedMs < ctx.replyWaitMs) {
|
|
48565
|
+
const t0 = ctx.now();
|
|
48566
|
+
const listen = ctx.replyWaitMs - ctx.replyWaitedMs;
|
|
48567
|
+
const reply = await waitForReply(ctx.page, { secrets: ctx.secrets, ...ctx.lastTurn, timeoutMs: listen, ceilingMs: listen, quietMs: ctx.replyQuietMs });
|
|
48568
|
+
ctx.replyWaitedMs += ctx.now() - t0;
|
|
48569
|
+
if (reply.received) {
|
|
48570
|
+
ctx.conversation.latestReply = reply.text;
|
|
48571
|
+
ctx.replies.add(snap.url, reply.text);
|
|
48572
|
+
ctx.awaitingReply = false;
|
|
48573
|
+
ctx.busyWaitedMs = 0;
|
|
48574
|
+
ctx.history.push(`waited for the reply \u2192 reply: ${quote(reply.text, 300)}`);
|
|
48575
|
+
}
|
|
48576
|
+
}
|
|
48577
|
+
ctx.noteReplyText(snap.url, await readPageText(ctx.page, ctx.secrets));
|
|
48578
|
+
}
|
|
48579
|
+
const replyPages = ctx.replyGoal ? ctx.replies.pages() : null;
|
|
48580
|
+
if (replyPages === null)
|
|
48581
|
+
await readBackProbe(ctx, step);
|
|
48582
|
+
const verdict = replyPages !== null && replyPages.length === 0 ? {
|
|
48583
|
+
accept: false,
|
|
48584
|
+
reason: ctx.preSend === null ? "no reply observed: no message was sent yet \u2014 text on the page before the conversation is not a reply" : `no reply observed: no new message appeared after the send within the reply wait (${Math.round(ctx.replyWaitMs / 1e3)}s)`,
|
|
48585
|
+
answer: null
|
|
48586
|
+
} : await reportAnswer(cfg.gen, {
|
|
48587
|
+
goal: cfg.goal,
|
|
48588
|
+
url: snap.url,
|
|
48589
|
+
pages: replyPages ?? ctx.observed.pages(),
|
|
48590
|
+
history: ctx.history,
|
|
48591
|
+
secrets: ctx.secrets,
|
|
48592
|
+
judge: cfg.judge,
|
|
48593
|
+
vetoes: ctx.vetoes,
|
|
48594
|
+
// #238: "none exists" is an answer only on observed pages that cover the app enough.
|
|
48595
|
+
...replyPages === null ? { topNav: ctx.observed.topNavigation(), ownInputs: ctx.observed.ownInputs(), unreadWrites: ctx.observed.unreadWrites() } : {}
|
|
48596
|
+
}).then((v) => v.accept && ctx.writeGoal && !ctx.wroteOk && v.answer.absent !== true ? { accept: false, reason: UNSAVED_WRITE_REASON, answer: v.answer } : v).catch((e) => ({ accept: false, reason: `no answer could be generated: ${firstLine(e)}`, answer: null }));
|
|
48597
|
+
if (verdict.accept) {
|
|
48598
|
+
const deferred = deferEnding(ctx, step, "report");
|
|
48599
|
+
if (deferred !== null) {
|
|
48600
|
+
ctx.deferredAnswer = verdict.answer;
|
|
48601
|
+
record2(false, deferred, { answer: verdict.answer, origin: "engine" });
|
|
48602
|
+
return "continue";
|
|
48603
|
+
}
|
|
48604
|
+
}
|
|
48605
|
+
if (verdict.accept) {
|
|
48606
|
+
const on = replyPages === null ? "the observed pages" : "the reply observed after the send";
|
|
48607
|
+
record2(true, `report accepted: answer grounded on ${on} (${verdict.answer.evidence.length} claim(s))`, {
|
|
48608
|
+
answer: verdict.answer
|
|
48609
|
+
});
|
|
48610
|
+
ctx.answer = verdict.answer;
|
|
48611
|
+
ctx.outcome = { status: "completed", verifiedBy: "grounded-answer" };
|
|
48612
|
+
ctx.stop = "done";
|
|
48613
|
+
return "stop";
|
|
48614
|
+
}
|
|
48615
|
+
if (verdict.unconfirmedWrite === true && !ctx.readBackAsked) {
|
|
48616
|
+
ctx.readBackAsked = true;
|
|
48617
|
+
ctx.history.push(`report deferred: ${verdict.reason}`);
|
|
48618
|
+
record2(false, `report deferred: ${verdict.reason}`, { answer: verdict.answer, origin: "engine" });
|
|
48619
|
+
return "continue";
|
|
48620
|
+
}
|
|
48621
|
+
ctx.reportRejections += 1;
|
|
48622
|
+
for (const e of verdict.answer?.evidence ?? [])
|
|
48623
|
+
if (e.grounded)
|
|
48624
|
+
ctx.reportClaims.push(e);
|
|
48625
|
+
ctx.lastReportNotFound = verdict.answer === null && verdict.reason === NO_ANSWER_REASON || verdict.notAnswer === true;
|
|
48626
|
+
ctx.lastAbsenceUncovered = verdict.absenceUncovered === true ? verdict.reason : null;
|
|
48627
|
+
const noWrite = ctx.writeGoal && !ctx.wroteOk && verdict.reason !== UNSAVED_WRITE_REASON;
|
|
48628
|
+
const why = noWrite ? `${verdict.reason}; ${NO_WRITE_YET}` : verdict.reason;
|
|
48629
|
+
ctx.history.push(`report rejected: ${why} \u2014 ${noWrite ? "make the change first, then report" : "find the answer on the page before reporting"}`);
|
|
48630
|
+
record2(false, `report rejected (${ctx.reportRejections}/${MAX_REPORT_REJECTIONS}): ${why}`, {
|
|
48631
|
+
answer: verdict.answer
|
|
48632
|
+
});
|
|
48633
|
+
if (ctx.reportRejections >= MAX_REPORT_REJECTIONS && deferEnding(ctx, step, "report") === null) {
|
|
48634
|
+
ctx.incomplete = `the model reported an answer ${ctx.reportRejections} times, but ${verdict.reason}`;
|
|
48635
|
+
ctx.stop = "blocked";
|
|
48636
|
+
return "stop";
|
|
48637
|
+
}
|
|
48638
|
+
return "continue";
|
|
48639
|
+
}
|
|
48640
|
+
|
|
48610
48641
|
// ../explore/dist/goal-loop/handle-blocked.js
|
|
48642
|
+
var RECOVERY_TURNS = /* @__PURE__ */ new WeakMap();
|
|
48643
|
+
function takeRecoveryTurn(ctx) {
|
|
48644
|
+
const turn = RECOVERY_TURNS.get(ctx);
|
|
48645
|
+
if (turn === void 0 || !turn.pending)
|
|
48646
|
+
return null;
|
|
48647
|
+
turn.pending = false;
|
|
48648
|
+
return turn.safe;
|
|
48649
|
+
}
|
|
48650
|
+
function resumeIdentity(e) {
|
|
48651
|
+
return JSON.stringify(e.resume);
|
|
48652
|
+
}
|
|
48653
|
+
var NON_FORWARD = /^(?:back|go back|previous|return|cancel|close|dismiss|×|x|←|‹|arrow[- ]?left)\b/i;
|
|
48654
|
+
var DIRECTIVE = /\b(?:choose|select|pick|apply|submit|continue|confirm|save|open|click|press|start|create|send|next|review|accept|approve|add|complete|proceed|generate|upload|finish|sign|log|book|buy|upgrade|run|try|edit|set)\b/i;
|
|
48655
|
+
var normalized = (text2) => text2.replace(/\s+/g, " ").trim().toLowerCase();
|
|
48656
|
+
var escaped = (text2) => text2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
48657
|
+
function instructionText(controls, visibleText) {
|
|
48658
|
+
const counts2 = /* @__PURE__ */ new Map();
|
|
48659
|
+
for (const c of controls) {
|
|
48660
|
+
const label2 = normalized(c.name);
|
|
48661
|
+
if (label2 !== "")
|
|
48662
|
+
counts2.set(label2, (counts2.get(label2) ?? 0) + 1);
|
|
48663
|
+
}
|
|
48664
|
+
let text2 = normalized(visibleText);
|
|
48665
|
+
for (const [label2, count] of counts2)
|
|
48666
|
+
for (let i = 0; i < count; i++) {
|
|
48667
|
+
const matches2 = [...text2.matchAll(new RegExp(`\\b${escaped(label2)}\\b`, "g"))];
|
|
48668
|
+
const last = matches2.at(-1);
|
|
48669
|
+
if (last?.index === void 0)
|
|
48670
|
+
break;
|
|
48671
|
+
text2 = `${text2.slice(0, last.index)} ${text2.slice(last.index + last[0].length)}`.replace(/\s+/g, " ").trim();
|
|
48672
|
+
}
|
|
48673
|
+
return text2;
|
|
48674
|
+
}
|
|
48675
|
+
function primaryForwardControl(ctx, step) {
|
|
48676
|
+
const goalTerms = terms(ctx.goalText);
|
|
48677
|
+
if (goalTerms.size === 0)
|
|
48678
|
+
return null;
|
|
48679
|
+
const instruction = instructionText(step.modelControls, step.visibleText);
|
|
48680
|
+
const instructionTerms = terms(instruction);
|
|
48681
|
+
if (instructionTerms.size === 0 || !DIRECTIVE.test(instruction))
|
|
48682
|
+
return null;
|
|
48683
|
+
for (const control of step.modelControls) {
|
|
48684
|
+
if (!control.enabled)
|
|
48685
|
+
continue;
|
|
48686
|
+
const name = control.name.trim();
|
|
48687
|
+
if (name === "" || NON_FORWARD.test(name))
|
|
48688
|
+
continue;
|
|
48689
|
+
if (ctx.depth.tried(step.snap.url, name))
|
|
48690
|
+
continue;
|
|
48691
|
+
const controlTerms = terms(name);
|
|
48692
|
+
if (controlTerms.size === 0)
|
|
48693
|
+
continue;
|
|
48694
|
+
if ([...controlTerms].some((t) => goalTerms.has(t)) && [...controlTerms].every((t) => instructionTerms.has(t)))
|
|
48695
|
+
return control;
|
|
48696
|
+
}
|
|
48697
|
+
return null;
|
|
48698
|
+
}
|
|
48611
48699
|
async function handleBlocked(ctx, step) {
|
|
48612
48700
|
const { modelControls, record: record2 } = step;
|
|
48613
48701
|
const job = await readInProgressStatus(ctx.page);
|
|
@@ -48657,16 +48745,221 @@ async function handleBlocked(ctx, step) {
|
|
|
48657
48745
|
ctx.stop = "inconclusive";
|
|
48658
48746
|
return "stop";
|
|
48659
48747
|
}
|
|
48748
|
+
const forward = ctx.actionAttempts > 0 ? primaryForwardControl(ctx, step) : null;
|
|
48749
|
+
if (forward !== null) {
|
|
48750
|
+
ctx.earlyBlocked += 1;
|
|
48751
|
+
if (ctx.earlyBlocked <= MAX_EARLY_BLOCKED_REFUSALS) {
|
|
48752
|
+
const reason = `blocked refused: ${quote(forward.name || forward.summary, 60)} is an obvious forward control, still untried on this page, and it matches the goal and the page's instruction \u2014 use it before giving up`;
|
|
48753
|
+
ctx.history.push(reason);
|
|
48754
|
+
record2(false, reason, { origin: "engine" });
|
|
48755
|
+
return "continue";
|
|
48756
|
+
}
|
|
48757
|
+
record2(false, `model blocked while ${quote(forward.name || forward.summary, 60)} remained untried`, { origin: "engine" });
|
|
48758
|
+
ctx.failure = {
|
|
48759
|
+
kind: "insufficient-coverage",
|
|
48760
|
+
message: `the model gave up while ${quote(forward.name || forward.summary, 60)} \u2014 the obvious forward control the goal and the page's instruction name \u2014 was still untried after earlier actions \u2014 too little exploration to conclude the goal cannot be done`
|
|
48761
|
+
};
|
|
48762
|
+
ctx.stop = "inconclusive";
|
|
48763
|
+
return "stop";
|
|
48764
|
+
}
|
|
48660
48765
|
const deferred = deferEnding(ctx, step, "blocked");
|
|
48661
48766
|
if (deferred !== null) {
|
|
48662
48767
|
record2(false, deferred, { origin: "engine" });
|
|
48663
48768
|
return "continue";
|
|
48664
48769
|
}
|
|
48770
|
+
const evidence = await collectActionableControlEvidence(ctx.page, { screenshots: false }).catch(() => []);
|
|
48771
|
+
const safe = evidence.filter((e) => e.kind === "safe" && e.name !== "");
|
|
48772
|
+
const turn = RECOVERY_TURNS.get(ctx);
|
|
48773
|
+
if ((turn === void 0 || !turn.given) && safe.length > 0) {
|
|
48774
|
+
RECOVERY_TURNS.set(ctx, { safe, given: true, pending: true });
|
|
48775
|
+
const note = `blocked: one recovery turn is available through the safe control(s) ${safe.map((e) => `${quote(e.name, 40)} (${e.locator})`).join(", ")} \u2014 act through one of them or the run ends blocked`;
|
|
48776
|
+
ctx.history.push(note);
|
|
48777
|
+
record2(false, note, { origin: "engine" });
|
|
48778
|
+
return "continue";
|
|
48779
|
+
}
|
|
48665
48780
|
record2(true, "model blocked");
|
|
48666
|
-
|
|
48781
|
+
const turnSafe = turn !== void 0 && turn.given && turn.safe.length > 0 ? turn.safe : null;
|
|
48782
|
+
const excluded = evidence.filter((e) => e.kind !== "safe");
|
|
48783
|
+
const classes = [...new Set(excluded.map((e) => e.kind))].join(", ");
|
|
48784
|
+
const best = turnSafe !== null ? turnSafe[0] : excluded.find((e) => e.kind !== "hidden" && e.kind !== "disabled") ?? evidence[0];
|
|
48785
|
+
if (best !== void 0) {
|
|
48786
|
+
const captured = (await collectActionableControlEvidence(ctx.page).catch(() => evidence)).find((e) => e.locator === best.locator);
|
|
48787
|
+
ctx.actionableControl = actionableControlOutcome(captured ?? best);
|
|
48788
|
+
}
|
|
48789
|
+
const refusal = ctx.lastRefusal === null ? "" : ` (${ctx.lastRefusal})`;
|
|
48790
|
+
const base = `the model reported the goal cannot be advanced from this page${refusal}`;
|
|
48791
|
+
const recovery = turnSafe !== null ? `the one recovery turn through the safe control ${turnSafe.map(resumeIdentity).join(", ")} did not advance the goal` : `no visible enabled safe control is available to recover through${classes === "" ? "" : ` (excluded control classes: ${classes})`}${best === void 0 ? "" : `; resume identity ${resumeIdentity(best)}`}`;
|
|
48792
|
+
const cause = ctx.blockingCause();
|
|
48793
|
+
ctx.incomplete = cause === null || base.includes(cause) ? `${base}; ${recovery}` : `${base} \u2014 last blocker: ${cause}; ${recovery}`;
|
|
48667
48794
|
ctx.stop = "blocked";
|
|
48668
48795
|
return "stop";
|
|
48669
48796
|
}
|
|
48797
|
+
var RECOVERY_SELECTOR = [
|
|
48798
|
+
"a[href]",
|
|
48799
|
+
"button",
|
|
48800
|
+
"input:not([type=hidden])",
|
|
48801
|
+
"select",
|
|
48802
|
+
"textarea",
|
|
48803
|
+
"[role=button]",
|
|
48804
|
+
"[role=link]",
|
|
48805
|
+
"[role=menuitem]",
|
|
48806
|
+
"[role=tab]",
|
|
48807
|
+
"[role=checkbox]",
|
|
48808
|
+
"[role=radio]",
|
|
48809
|
+
"[role=switch]",
|
|
48810
|
+
"[contenteditable=true]"
|
|
48811
|
+
].join(", ");
|
|
48812
|
+
var APPROVAL = /\b(?:approve|approval|authorize|authorise|accept|grant|sign[ -]?off|confirm)\b/i;
|
|
48813
|
+
var SAFE_PREDICATE = "visible && enabled && !ambiguous && !destructive && !financial && !approval";
|
|
48814
|
+
function readRecoveryCandidates(els) {
|
|
48815
|
+
const nameOf = (el) => {
|
|
48816
|
+
const aria = el.getAttribute("aria-label");
|
|
48817
|
+
if (aria !== null && aria.trim() !== "")
|
|
48818
|
+
return aria.replace(/\s+/g, " ").trim();
|
|
48819
|
+
const labelled = el.getAttribute("aria-labelledby");
|
|
48820
|
+
if (labelled !== null) {
|
|
48821
|
+
const text2 = labelled.split(/\s+/).map((id) => document.getElementById(id)?.textContent ?? "").join(" ");
|
|
48822
|
+
if (text2.trim() !== "")
|
|
48823
|
+
return text2.replace(/\s+/g, " ").trim();
|
|
48824
|
+
}
|
|
48825
|
+
const title = el.getAttribute("title");
|
|
48826
|
+
if (title !== null && title.trim() !== "")
|
|
48827
|
+
return title.replace(/\s+/g, " ").trim();
|
|
48828
|
+
if (el instanceof HTMLInputElement && el.type !== "password" && el.value !== "")
|
|
48829
|
+
return el.value.replace(/\s+/g, " ").trim();
|
|
48830
|
+
return (el.textContent ?? "").replace(/\s+/g, " ").trim();
|
|
48831
|
+
};
|
|
48832
|
+
return els.map((el) => {
|
|
48833
|
+
const style = getComputedStyle(el);
|
|
48834
|
+
const rect = el.getBoundingClientRect();
|
|
48835
|
+
const visible3 = style.display !== "none" && style.visibility !== "hidden" && el.getClientRects().length > 0 && rect.width > 0 && rect.height > 0;
|
|
48836
|
+
const disabled = el.disabled === true || el.getAttribute("aria-disabled") === "true";
|
|
48837
|
+
return {
|
|
48838
|
+
name: nameOf(el),
|
|
48839
|
+
role: el.getAttribute("role") ?? el.tagName.toLowerCase(),
|
|
48840
|
+
testId: el.getAttribute("data-testid") ?? el.getAttribute("data-test"),
|
|
48841
|
+
id: el.id === "" ? null : el.id,
|
|
48842
|
+
visible: visible3,
|
|
48843
|
+
enabled: !disabled,
|
|
48844
|
+
box: visible3 ? { x: rect.x + window.scrollX, y: rect.y + window.scrollY, width: rect.width, height: rect.height } : null
|
|
48845
|
+
};
|
|
48846
|
+
});
|
|
48847
|
+
}
|
|
48848
|
+
function classifyActionableControl(input2) {
|
|
48849
|
+
if (!input2.visible)
|
|
48850
|
+
return "hidden";
|
|
48851
|
+
if (!input2.enabled)
|
|
48852
|
+
return "disabled";
|
|
48853
|
+
if (input2.duplicateName)
|
|
48854
|
+
return "ambiguous";
|
|
48855
|
+
const risk = controlRisk(input2.name, input2.role);
|
|
48856
|
+
if (risk !== null && (risk.risk === "destructive" || risk.risk === "session-end"))
|
|
48857
|
+
return "destructive";
|
|
48858
|
+
if (risk !== null && risk.risk === "paid")
|
|
48859
|
+
return "financial";
|
|
48860
|
+
if (APPROVAL.test(input2.name))
|
|
48861
|
+
return "approval";
|
|
48862
|
+
return "safe";
|
|
48863
|
+
}
|
|
48864
|
+
function recoveryPredicate(kind) {
|
|
48865
|
+
switch (kind) {
|
|
48866
|
+
case "safe":
|
|
48867
|
+
return SAFE_PREDICATE;
|
|
48868
|
+
case "hidden":
|
|
48869
|
+
return "visible";
|
|
48870
|
+
case "disabled":
|
|
48871
|
+
return "enabled";
|
|
48872
|
+
case "ambiguous":
|
|
48873
|
+
return "uniquely named";
|
|
48874
|
+
case "destructive":
|
|
48875
|
+
return "!destructive";
|
|
48876
|
+
case "financial":
|
|
48877
|
+
return "!financial";
|
|
48878
|
+
case "approval":
|
|
48879
|
+
return "!approval";
|
|
48880
|
+
}
|
|
48881
|
+
}
|
|
48882
|
+
function recoveryLocator(c) {
|
|
48883
|
+
if (c.testId !== null && c.testId !== "")
|
|
48884
|
+
return `[data-testid="${c.testId}"]`;
|
|
48885
|
+
if (c.id !== null && c.id !== "")
|
|
48886
|
+
return `#${c.id}`;
|
|
48887
|
+
const role = c.role === "" ? "*" : c.role;
|
|
48888
|
+
return `${role}[name="${c.name.replace(/"/g, '\\"')}"]`;
|
|
48889
|
+
}
|
|
48890
|
+
var SAFE_CLAUSES = ["visible", "enabled", "!ambiguous", "!destructive", "!financial", "!approval"];
|
|
48891
|
+
function failingClause(kind) {
|
|
48892
|
+
switch (kind) {
|
|
48893
|
+
case "safe":
|
|
48894
|
+
return null;
|
|
48895
|
+
case "hidden":
|
|
48896
|
+
return "visible";
|
|
48897
|
+
case "disabled":
|
|
48898
|
+
return "enabled";
|
|
48899
|
+
case "ambiguous":
|
|
48900
|
+
return "!ambiguous";
|
|
48901
|
+
case "destructive":
|
|
48902
|
+
return "!destructive";
|
|
48903
|
+
case "financial":
|
|
48904
|
+
return "!financial";
|
|
48905
|
+
case "approval":
|
|
48906
|
+
return "!approval";
|
|
48907
|
+
}
|
|
48908
|
+
}
|
|
48909
|
+
function actionableControlOutcome(e) {
|
|
48910
|
+
const missing = failingClause(e.kind);
|
|
48911
|
+
return {
|
|
48912
|
+
kind: "author_stopped_with_actionable_control",
|
|
48913
|
+
candidate: {
|
|
48914
|
+
locator: e.locator,
|
|
48915
|
+
label: e.name,
|
|
48916
|
+
visible: e.kind !== "hidden",
|
|
48917
|
+
enabled: e.kind !== "disabled",
|
|
48918
|
+
screenshot: e.screenshot,
|
|
48919
|
+
resume: e.resume
|
|
48920
|
+
},
|
|
48921
|
+
satisfiedPredicates: SAFE_CLAUSES.filter((c) => c !== missing),
|
|
48922
|
+
unsatisfiedPredicates: missing === null ? [] : [missing]
|
|
48923
|
+
};
|
|
48924
|
+
}
|
|
48925
|
+
async function recoveryScreenshot(page, c) {
|
|
48926
|
+
try {
|
|
48927
|
+
const buf = c.box === null ? await page.screenshot({ animations: "disabled" }) : await page.screenshot({ clip: c.box, animations: "disabled" });
|
|
48928
|
+
return `data:image/png;base64,${buf.toString("base64")}`;
|
|
48929
|
+
} catch {
|
|
48930
|
+
return null;
|
|
48931
|
+
}
|
|
48932
|
+
}
|
|
48933
|
+
async function collectActionableControlEvidence(page, opts = {}) {
|
|
48934
|
+
const candidates = await page.locator(RECOVERY_SELECTOR).evaluateAll(readRecoveryCandidates).catch(() => []);
|
|
48935
|
+
const url2 = page.url();
|
|
48936
|
+
const state = await snapshot(page).then((s3) => s3.signature, () => url2);
|
|
48937
|
+
const visibleNames = /* @__PURE__ */ new Map();
|
|
48938
|
+
for (const c of candidates) {
|
|
48939
|
+
if (c.visible && c.enabled && c.name !== "")
|
|
48940
|
+
visibleNames.set(c.name, (visibleNames.get(c.name) ?? 0) + 1);
|
|
48941
|
+
}
|
|
48942
|
+
const out = [];
|
|
48943
|
+
for (const c of candidates) {
|
|
48944
|
+
const kind = classifyActionableControl({
|
|
48945
|
+
name: c.name,
|
|
48946
|
+
role: c.role,
|
|
48947
|
+
visible: c.visible,
|
|
48948
|
+
enabled: c.enabled,
|
|
48949
|
+
duplicateName: c.name !== "" && (visibleNames.get(c.name) ?? 0) > 1
|
|
48950
|
+
});
|
|
48951
|
+
const locator = recoveryLocator(c);
|
|
48952
|
+
out.push({
|
|
48953
|
+
kind,
|
|
48954
|
+
name: c.name,
|
|
48955
|
+
locator,
|
|
48956
|
+
screenshot: opts.screenshots === false ? null : await recoveryScreenshot(page, c),
|
|
48957
|
+
predicate: recoveryPredicate(kind),
|
|
48958
|
+
resume: { url: url2, state, locator }
|
|
48959
|
+
});
|
|
48960
|
+
}
|
|
48961
|
+
return out;
|
|
48962
|
+
}
|
|
48670
48963
|
|
|
48671
48964
|
// ../explore/dist/goal-loop/handle-idle.js
|
|
48672
48965
|
async function handleWaitOrScroll(ctx, step) {
|
|
@@ -49664,6 +49957,21 @@ async function statusWhilePending(page) {
|
|
|
49664
49957
|
const status = await readInProgressStatus(page);
|
|
49665
49958
|
return status === null ? null : `${status} while its request is in flight (within --job-wait-ms)`;
|
|
49666
49959
|
}
|
|
49960
|
+
async function declaredBusy(page) {
|
|
49961
|
+
const status = await readInProgressStatus(page);
|
|
49962
|
+
return status?.startsWith("aria-busy region") === true ? `${status}, declared by the app (within --job-wait-ms)` : null;
|
|
49963
|
+
}
|
|
49964
|
+
function stoodAfterWork(ctx, workBudgetMs) {
|
|
49965
|
+
const s3 = (ms) => `${Math.round(ms / 1e3)}s`;
|
|
49966
|
+
const now2 = clock.now();
|
|
49967
|
+
const since = ctx.hangWorkSince === null ? "" : `, ${s3(now2 - ctx.hangWorkSince)} since the first deferral`;
|
|
49968
|
+
const last = monitorFor(ctx.page).completedSince(ctx.hangWorkSince ?? 0).filter((r) => r.resourceType !== "document" && r.ignored !== true).at(-1);
|
|
49969
|
+
const activity = last === void 0 ? "no app request completed since" : `the app's last request completed ${s3(now2 - last.endedAt)} ago`;
|
|
49970
|
+
if (ctx.hangWorkWaitedMs >= workBudgetMs) {
|
|
49971
|
+
return `still so after ${s3(ctx.hangWorkWaitedMs)} of the page showing work${since} \u2014 past the ${s3(workBudgetMs)} job-wait budget; raise --job-wait-ms for longer jobs; ${activity}`;
|
|
49972
|
+
}
|
|
49973
|
+
return `the page stopped showing work code recognizes (an acknowledged or declared in-progress status, a documented wait, a pending request under a status, or requests completing) after ${s3(ctx.hangWorkWaitedMs)} of the ${s3(workBudgetMs)} job-wait budget${since}; ${activity}`;
|
|
49974
|
+
}
|
|
49667
49975
|
async function checkHang(ctx, step) {
|
|
49668
49976
|
const { perceiveStartedAt, perception, snap } = step;
|
|
49669
49977
|
const documented = perception.hang !== null && perception.hang.kind !== "main-thread-unresponsive" ? await readDocumentedWait(ctx.page) : null;
|
|
@@ -49674,8 +49982,9 @@ async function checkHang(ctx, step) {
|
|
|
49674
49982
|
const working = await readWorkingStatus(ctx.page) ?? (documented === null ? null : `a documented wait ("${documented.text}")`) ?? (perception.hang.kind === "ui-no-progress" ? await liveBusyWork(ctx.page, perception.busyWait) : null) ?? // #330: a request still in flight while the page SAYS it is working ("Designing variations…")
|
|
49675
49983
|
// is the long job the operator sized with --job-wait-ms: waited out within that budget. Without
|
|
49676
49984
|
// the flag a bare status is not enough (a stuck page looks exactly like that, #153).
|
|
49677
|
-
(perception.hang.kind === "request-pending" && ctx.jobWaitExplicit ? await statusWhilePending(ctx.page) : null);
|
|
49985
|
+
(perception.hang.kind === "request-pending" && ctx.jobWaitExplicit ? await statusWhilePending(ctx.page) : null) ?? (perception.hang.kind === "ui-no-progress" && ctx.jobWaitExplicit ? await declaredBusy(ctx.page) : null);
|
|
49678
49986
|
if (working !== null) {
|
|
49987
|
+
ctx.hangWorkSince ??= perceiveStartedAt;
|
|
49679
49988
|
const w = await waitOutJob(ctx.page, Math.min(workBudgetMs - ctx.hangWorkWaitedMs, JOB_WAIT_SLICE_MS), stillShowsWork);
|
|
49680
49989
|
ctx.hangWorkWaitedMs += clock.now() - perceiveStartedAt;
|
|
49681
49990
|
const note = `not a hang yet (${perception.hang.kind}): the page shows ${working} \u2014 the app is still working; waited ${(w.waitedMs / 1e3).toFixed(1)}s (${w.cleared ? "the status cleared" : `still in progress; ${Math.round(ctx.hangWorkWaitedMs / 1e3)}s of the ${Math.round(workBudgetMs / 1e3)}s job-wait budget used`})`;
|
|
@@ -49715,7 +50024,7 @@ async function checkHang(ctx, step) {
|
|
|
49715
50024
|
const heapNow = await sampleHeap(ctx.page, 1e3);
|
|
49716
50025
|
const stood = ctx.hangWorkWaitedMs > 0 ? {
|
|
49717
50026
|
...perception.hang,
|
|
49718
|
-
detail: `${perception.hang.detail} (
|
|
50027
|
+
detail: `${perception.hang.detail} (${stoodAfterWork(ctx, workBudgetMs)})`
|
|
49719
50028
|
} : perception.hang;
|
|
49720
50029
|
const withHost = { ...stood, host: judged.host };
|
|
49721
50030
|
ctx.hang = {
|
|
@@ -49992,7 +50301,7 @@ async function checkProgress(ctx, step) {
|
|
|
49992
50301
|
if (!ctx.lastChanceGiven) {
|
|
49993
50302
|
ctx.lastChanceGiven = true;
|
|
49994
50303
|
ctx.lastChanceTurn = true;
|
|
49995
|
-
ctx.history.push(LAST_CHANCE_NOTE);
|
|
50304
|
+
ctx.history.push(ctx.writeGoal && !ctx.wroteOk ? LAST_CHANCE_NO_WRITE_NOTE : LAST_CHANCE_NOTE);
|
|
49996
50305
|
} else {
|
|
49997
50306
|
ctx.stop = "no-progress";
|
|
49998
50307
|
return "stop";
|
|
@@ -50196,7 +50505,12 @@ async function viewPage(ctx, step) {
|
|
|
50196
50505
|
async function decideStep(ctx, step, view) {
|
|
50197
50506
|
const { cfg } = ctx;
|
|
50198
50507
|
const { perception, snap } = step;
|
|
50199
|
-
const {
|
|
50508
|
+
const { offered, unsubmitted, visibleText } = view;
|
|
50509
|
+
const recovery = takeRecoveryTurn(ctx);
|
|
50510
|
+
const safeNames = new Set((recovery ?? []).map((e) => e.name).filter((n3) => n3 !== ""));
|
|
50511
|
+
const modelControls = recovery === null ? view.modelControls : view.modelControls.filter((c) => safeNames.has(c.name));
|
|
50512
|
+
if (recovery !== null)
|
|
50513
|
+
ctx.history.push("recovery turn: act through one of the safe controls offered, or the run ends blocked");
|
|
50200
50514
|
let decision;
|
|
50201
50515
|
ctx.goalFocus.noteUrl(snap.url);
|
|
50202
50516
|
const offGoal = ctx.goalFocus.offGoal(modelControls, ctx.chrome);
|
|
@@ -50447,6 +50761,7 @@ async function finishRun(ctx) {
|
|
|
50447
50761
|
...ctx.answer !== void 0 && finalOutcome.status === "completed" ? { answer: ctx.answer } : {},
|
|
50448
50762
|
...cause === null ? {} : { blockingCause: cause },
|
|
50449
50763
|
...missCause === void 0 ? {} : { missCause },
|
|
50764
|
+
...ctx.actionableControl === null ? {} : { actionableControl: ctx.actionableControl },
|
|
50450
50765
|
...ctx.endedOnRejectedDone && ctx.stop === "done" ? { doneRejected: true } : {},
|
|
50451
50766
|
...ctx.stop === "crashed" && ctx.failure !== void 0 ? { crash: buildCrashReport(ctx.failure, ctx.crashWatch.signals(), ctx.heap.samples(), { host: await ctx.probeHost() }) } : {}
|
|
50452
50767
|
};
|
|
@@ -52502,7 +52817,7 @@ async function reproduceSeedHang(cfg, run, h) {
|
|
|
52502
52817
|
});
|
|
52503
52818
|
}
|
|
52504
52819
|
function hangResult(run, h, reproduction) {
|
|
52505
|
-
const
|
|
52820
|
+
const finding4 = hangFinding(h.signal, run.transcript, h.recordingStepIndex, reproduction);
|
|
52506
52821
|
return {
|
|
52507
52822
|
// A hang whose replays could not run at all is `inconclusive`, never a non-reproduction.
|
|
52508
52823
|
outcome: reproduction.status === "reproduced" ? "hang" : reproduction.status,
|
|
@@ -52512,8 +52827,8 @@ function hangResult(run, h, reproduction) {
|
|
|
52512
52827
|
recording: run.recording,
|
|
52513
52828
|
transcript: run.transcript,
|
|
52514
52829
|
finalUrl: run.finalUrl,
|
|
52515
|
-
hang:
|
|
52516
|
-
reason: reproduction.withheld !== void 0 ? `${
|
|
52830
|
+
hang: finding4,
|
|
52831
|
+
reason: reproduction.withheld !== void 0 ? `${finding4.title} (${withheldReason(reproduction.withheld)})` : reproduction.attempts === 0 ? `${finding4.title} (unconfirmed: not replayed)` : `${finding4.title} (reproduced ${reproduction.reproduced}/${reproduction.attempts})`
|
|
52517
52832
|
};
|
|
52518
52833
|
}
|
|
52519
52834
|
|
|
@@ -58132,23 +58447,23 @@ function createFrontierContext(params, declared, safety, budget, overlay = null,
|
|
|
58132
58447
|
const vp = ctx.sessions.page.viewportSize();
|
|
58133
58448
|
if (!shouldCheckOverflow(vp?.width, params.overflow?.checkOverflow ?? false))
|
|
58134
58449
|
return;
|
|
58135
|
-
const
|
|
58450
|
+
const finding4 = await detectOverflow(ctx.sessions.page, {
|
|
58136
58451
|
viewport: vp ?? { width: 1280, height: 720 },
|
|
58137
58452
|
...params.overflow?.device === void 0 ? {} : { device: params.overflow.device },
|
|
58138
58453
|
...params.overflow?.toleranceCss === void 0 ? {} : { toleranceCss: params.overflow.toleranceCss },
|
|
58139
58454
|
...params.overflow?.ignoreSelectors === void 0 ? {} : { ignoreSelectors: params.overflow.ignoreSelectors },
|
|
58140
58455
|
...params.overflow?.secrets === void 0 ? {} : { secrets: params.overflow.secrets }
|
|
58141
58456
|
});
|
|
58142
|
-
if (
|
|
58143
|
-
ctx.seenOverflow.add(
|
|
58457
|
+
if (finding4 !== null && !ctx.seenOverflow.has(finding4.fingerprint)) {
|
|
58458
|
+
ctx.seenOverflow.add(finding4.fingerprint);
|
|
58144
58459
|
ctx.defects.push({
|
|
58145
|
-
fingerprint:
|
|
58460
|
+
fingerprint: finding4.fingerprint,
|
|
58146
58461
|
kind: "horizontal-overflow",
|
|
58147
58462
|
stateFingerprint: stateFp,
|
|
58148
58463
|
url: url2,
|
|
58149
|
-
reason: `horizontal-overflow: ${
|
|
58464
|
+
reason: `horizontal-overflow: ${finding4.element.descriptor} overflows the ${finding4.viewport.width}px viewport by ${finding4.overflowPx}px at ${finding4.route}`,
|
|
58150
58465
|
recording,
|
|
58151
|
-
overflow:
|
|
58466
|
+
overflow: finding4
|
|
58152
58467
|
});
|
|
58153
58468
|
}
|
|
58154
58469
|
const clipped = await detectClipping(ctx.sessions.page, {
|
|
@@ -60719,22 +61034,22 @@ function installOracle(ctx, params) {
|
|
|
60719
61034
|
url: c.url,
|
|
60720
61035
|
descriptor: c.element.descriptor
|
|
60721
61036
|
}));
|
|
60722
|
-
const
|
|
61037
|
+
const finding4 = await detectOverflow(ctx.sessions.page, {
|
|
60723
61038
|
viewport: vp ?? { width: 1280, height: 720 },
|
|
60724
61039
|
...params.overflow?.device === void 0 ? {} : { device: params.overflow.device },
|
|
60725
61040
|
...params.overflow?.toleranceCss === void 0 ? {} : { toleranceCss: params.overflow.toleranceCss },
|
|
60726
61041
|
...params.overflow?.ignoreSelectors === void 0 ? {} : { ignoreSelectors: params.overflow.ignoreSelectors },
|
|
60727
61042
|
...params.overflow?.secrets === void 0 ? {} : { secrets: params.overflow.secrets }
|
|
60728
61043
|
});
|
|
60729
|
-
if (
|
|
61044
|
+
if (finding4 === null)
|
|
60730
61045
|
return clipped;
|
|
60731
61046
|
const horizontal = {
|
|
60732
61047
|
kind: "horizontal-overflow",
|
|
60733
|
-
detail: `horizontal-overflow: ${
|
|
60734
|
-
overflowPx:
|
|
60735
|
-
route:
|
|
60736
|
-
url:
|
|
60737
|
-
descriptor:
|
|
61048
|
+
detail: `horizontal-overflow: ${finding4.element.descriptor} overflows the ${finding4.viewport.width}px viewport by ${finding4.overflowPx}px at ${finding4.route}`,
|
|
61049
|
+
overflowPx: finding4.overflowPx,
|
|
61050
|
+
route: finding4.route,
|
|
61051
|
+
url: finding4.url,
|
|
61052
|
+
descriptor: finding4.element.descriptor
|
|
60738
61053
|
};
|
|
60739
61054
|
return [horizontal, ...clipped];
|
|
60740
61055
|
};
|
|
@@ -60903,9 +61218,9 @@ function installOracle(ctx, params) {
|
|
|
60903
61218
|
}
|
|
60904
61219
|
const summary2 = f2.kind === "invariant" ? f2.invariantReason ?? f2.title : f2.kind === "markup-injection" ? f2.title : f2.signals.map((s3) => s3.detail).join("; ");
|
|
60905
61220
|
const triage = await tryTriage(params.generation, { failureSummary: summary2, url: f2.url });
|
|
60906
|
-
const { origin: _origin, ...
|
|
61221
|
+
const { origin: _origin, ...finding4 } = f2;
|
|
60907
61222
|
ctx.defects.set(f2.fingerprint, {
|
|
60908
|
-
...
|
|
61223
|
+
...finding4,
|
|
60909
61224
|
related: new Set(f2.related),
|
|
60910
61225
|
epoch: ctx.segments.indexOf(ctx.recorder),
|
|
60911
61226
|
firstSeenStep: step,
|
|
@@ -60983,9 +61298,9 @@ function installSessions(ctx, params) {
|
|
|
60983
61298
|
perceive: ctx.perceiveOpts,
|
|
60984
61299
|
...params.safety === void 0 ? {} : { safety: params.safety }
|
|
60985
61300
|
});
|
|
60986
|
-
const
|
|
61301
|
+
const finding4 = hangFinding(h, ctx.transcript.entries(), recordingStepIndex, reproduction);
|
|
60987
61302
|
const segment = ctx.segments.indexOf(ctx.recorder);
|
|
60988
|
-
ctx.hangs.set(
|
|
61303
|
+
ctx.hangs.set(finding4.fingerprint, segment > 0 && partial2.ok ? { ...finding4, repro: { ...finding4.repro, recording: partial2.recording } } : finding4);
|
|
60989
61304
|
};
|
|
60990
61305
|
ctx.restartSpend = 0;
|
|
60991
61306
|
ctx.restartAtSeed = async () => {
|
|
@@ -61125,21 +61440,21 @@ function createHuntContext(params) {
|
|
|
61125
61440
|
}
|
|
61126
61441
|
return warning;
|
|
61127
61442
|
};
|
|
61128
|
-
ctx.blockedConsequence = (
|
|
61443
|
+
ctx.blockedConsequence = (finding4, candidate) => {
|
|
61129
61444
|
const causedBy = blockedCauseOf(ctx.blockedRoots, candidate);
|
|
61130
61445
|
if (causedBy === void 0)
|
|
61131
61446
|
return false;
|
|
61132
|
-
const known = ctx.blockedConsequences.get(
|
|
61447
|
+
const known = ctx.blockedConsequences.get(finding4.fingerprint);
|
|
61133
61448
|
if (known !== void 0) {
|
|
61134
61449
|
if (!known.occurrenceSteps.includes(candidate.step))
|
|
61135
61450
|
known.occurrenceSteps.push(candidate.step);
|
|
61136
61451
|
return true;
|
|
61137
61452
|
}
|
|
61138
|
-
ctx.blockedConsequences.set(
|
|
61139
|
-
fingerprint:
|
|
61140
|
-
kind:
|
|
61141
|
-
title:
|
|
61142
|
-
route:
|
|
61453
|
+
ctx.blockedConsequences.set(finding4.fingerprint, {
|
|
61454
|
+
fingerprint: finding4.fingerprint,
|
|
61455
|
+
kind: finding4.kind,
|
|
61456
|
+
title: finding4.title,
|
|
61457
|
+
route: finding4.route,
|
|
61143
61458
|
firstSeenStep: candidate.step,
|
|
61144
61459
|
occurrenceSteps: [candidate.step],
|
|
61145
61460
|
causedBy
|
|
@@ -63118,9 +63433,9 @@ function hostStarvedFailure(failure3, health) {
|
|
|
63118
63433
|
}
|
|
63119
63434
|
|
|
63120
63435
|
// src/build-info.generated.ts
|
|
63121
|
-
var GENERATED_COMMIT = "
|
|
63122
|
-
var GENERATED_TREE = "
|
|
63123
|
-
var GENERATED_BUILT_AT = "2026-10-
|
|
63436
|
+
var GENERATED_COMMIT = "ffe0da9";
|
|
63437
|
+
var GENERATED_TREE = "bc64c13";
|
|
63438
|
+
var GENERATED_BUILT_AT = "2026-10-26T13:27:18.971Z";
|
|
63124
63439
|
|
|
63125
63440
|
// src/version.ts
|
|
63126
63441
|
import { readFileSync as readFileSync5 } from "node:fs";
|
|
@@ -63142,13 +63457,13 @@ function readCliVersion() {
|
|
|
63142
63457
|
|
|
63143
63458
|
// src/engine.ts
|
|
63144
63459
|
function bundledCommit() {
|
|
63145
|
-
return true ? "
|
|
63460
|
+
return true ? "ffe0da9" : void 0;
|
|
63146
63461
|
}
|
|
63147
63462
|
function bundledBuiltAt() {
|
|
63148
|
-
return true ? "2026-10-
|
|
63463
|
+
return true ? "2026-10-26T13:27:18.971Z" : void 0;
|
|
63149
63464
|
}
|
|
63150
63465
|
function bundledTree() {
|
|
63151
|
-
return true ? "
|
|
63466
|
+
return true ? "bc64c13" : void 0;
|
|
63152
63467
|
}
|
|
63153
63468
|
function currentBuildTree() {
|
|
63154
63469
|
return bundledTree() ?? GENERATED_TREE;
|
|
@@ -66594,10 +66909,10 @@ var QueryNode = freeze2({
|
|
|
66594
66909
|
where: node2.where ? WhereNode.cloneWithOperation(node2.where, "And", operation) : WhereNode.create(operation)
|
|
66595
66910
|
});
|
|
66596
66911
|
},
|
|
66597
|
-
cloneWithJoin(node2,
|
|
66912
|
+
cloneWithJoin(node2, join84) {
|
|
66598
66913
|
return freeze2({
|
|
66599
66914
|
...node2,
|
|
66600
|
-
joins: node2.joins ? freeze2([...node2.joins,
|
|
66915
|
+
joins: node2.joins ? freeze2([...node2.joins, join84]) : freeze2([join84])
|
|
66601
66916
|
});
|
|
66602
66917
|
},
|
|
66603
66918
|
cloneWithReturning(node2, selections) {
|
|
@@ -70253,8 +70568,8 @@ var WithSchemaTransformer = class extends OperationNodeTransformer {
|
|
|
70253
70568
|
this.#collectSchemableIdsFromTableExpr(node2.table, schemableIds);
|
|
70254
70569
|
}
|
|
70255
70570
|
if ("joins" in node2 && node2.joins) {
|
|
70256
|
-
for (const
|
|
70257
|
-
this.#collectSchemableIdsFromTableExpr(
|
|
70571
|
+
for (const join84 of node2.joins) {
|
|
70572
|
+
this.#collectSchemableIdsFromTableExpr(join84.table, schemableIds);
|
|
70258
70573
|
}
|
|
70259
70574
|
}
|
|
70260
70575
|
if ("using" in node2 && node2.using) {
|
|
@@ -83967,15 +84282,15 @@ function originOnly(url2) {
|
|
|
83967
84282
|
var LOCKFILES = /* @__PURE__ */ new Set(["pnpm-lock.yaml", "package-lock.json", "yarn.lock", "Cargo.lock", "poetry.lock", "Gemfile.lock", "composer.lock"]);
|
|
83968
84283
|
var SECRET_EXTENSION = /\.(pem|key|p12|pfx)$/i;
|
|
83969
84284
|
function isSkippedChangePath(path2) {
|
|
83970
|
-
const
|
|
83971
|
-
const base =
|
|
84285
|
+
const normalized2 = path2.replace(/\\/g, "/");
|
|
84286
|
+
const base = normalized2.split("/").pop() ?? normalized2;
|
|
83972
84287
|
if (base === ".env" || base.startsWith(".env."))
|
|
83973
84288
|
return true;
|
|
83974
84289
|
if (SECRET_EXTENSION.test(base))
|
|
83975
84290
|
return true;
|
|
83976
84291
|
if (LOCKFILES.has(base))
|
|
83977
84292
|
return true;
|
|
83978
|
-
return
|
|
84293
|
+
return normalized2.toLowerCase().split("/").some((segment) => segment.includes("secret"));
|
|
83979
84294
|
}
|
|
83980
84295
|
function extractChangeEvidence(diff2, notes) {
|
|
83981
84296
|
const evidence = [];
|
|
@@ -91016,9 +91331,9 @@ async function attachDefectEvidence(result, opts) {
|
|
|
91016
91331
|
continue;
|
|
91017
91332
|
}
|
|
91018
91333
|
const signal = defectSignalText(d, secrets);
|
|
91019
|
-
const
|
|
91020
|
-
const recording =
|
|
91021
|
-
if (
|
|
91334
|
+
const finding4 = findFinding(mission, fp);
|
|
91335
|
+
const recording = finding4?.recording ?? mission.recording;
|
|
91336
|
+
if (finding4 === void 0 || recording === null) {
|
|
91022
91337
|
byFp.set(fp, { screenshots: [], signal, skipped: "the defect has no repro Recording to replay" });
|
|
91023
91338
|
continue;
|
|
91024
91339
|
}
|
|
@@ -91036,9 +91351,9 @@ async function attachDefectEvidence(result, opts) {
|
|
|
91036
91351
|
fp,
|
|
91037
91352
|
await replayWithEvidence({
|
|
91038
91353
|
recording,
|
|
91039
|
-
stepIndex:
|
|
91040
|
-
fingerprints: [fp, ...
|
|
91041
|
-
signalCheckable: SIGNAL_KINDS.has(
|
|
91354
|
+
stepIndex: finding4.repro.recordingStepIndex,
|
|
91355
|
+
fingerprints: [fp, ...finding4.related ?? []],
|
|
91356
|
+
signalCheckable: SIGNAL_KINDS.has(finding4.kind),
|
|
91042
91357
|
signal,
|
|
91043
91358
|
seedUrl: mission.target.seedUrl,
|
|
91044
91359
|
allowlist: mission.target.allowlist,
|
|
@@ -92625,6 +92940,7 @@ async function runExploration(opts) {
|
|
|
92625
92940
|
timing: mission.run.timing,
|
|
92626
92941
|
outcome: goalOutcome,
|
|
92627
92942
|
runOutcome: mission.run.outcome,
|
|
92943
|
+
...mission.run.actionableControl === void 0 ? {} : { actionableControl: mission.run.actionableControl },
|
|
92628
92944
|
...mission.run.answer === void 0 ? {} : { answer: mission.run.answer },
|
|
92629
92945
|
...mission.run.partialReport === void 0 ? {} : { partialReport: mission.run.partialReport },
|
|
92630
92946
|
depth: mission.run.depth,
|
|
@@ -96336,8 +96652,8 @@ async function runVerifyFix(opts) {
|
|
|
96336
96652
|
const persisted = parsePersistedMission(raw);
|
|
96337
96653
|
const replay = replayAllowlist(persisted.target.allowlist, opts);
|
|
96338
96654
|
const mission = replay.override === void 0 ? persisted : { ...persisted, target: { ...persisted.target, allowlist: [...replay.allowlist] } };
|
|
96339
|
-
const
|
|
96340
|
-
if (
|
|
96655
|
+
const finding4 = findFinding(mission, opts.fingerprint);
|
|
96656
|
+
if (finding4 === void 0) {
|
|
96341
96657
|
throw new VerifyFixInputError(`no finding with fingerprint ${opts.fingerprint} in ${opts.resultPath}`);
|
|
96342
96658
|
}
|
|
96343
96659
|
const origin = assertAuthorizedExploreTarget(mission.target.seedUrl, mission.target.allowlist);
|
|
@@ -96355,8 +96671,8 @@ async function runVerifyFix(opts) {
|
|
|
96355
96671
|
...target.settle === void 0 ? {} : { settleConfig: target.settle },
|
|
96356
96672
|
...target.hangs === void 0 ? {} : { hangConfig: target.hangs }
|
|
96357
96673
|
};
|
|
96358
|
-
const found =
|
|
96359
|
-
if (found === null) throw new VerifyFixInputError(`finding ${
|
|
96674
|
+
const found = finding4.recording ?? mission.recording;
|
|
96675
|
+
if (found === null) throw new VerifyFixInputError(`finding ${finding4.fingerprint} has no Recording to replay`);
|
|
96360
96676
|
const branch = recordedBranchOf(raw?.result);
|
|
96361
96677
|
let prefix;
|
|
96362
96678
|
if (branch !== void 0) {
|
|
@@ -96369,7 +96685,7 @@ async function runVerifyFix(opts) {
|
|
|
96369
96685
|
}
|
|
96370
96686
|
const recording = prefix === void 0 ? found : anchoredRecording(found);
|
|
96371
96687
|
try {
|
|
96372
|
-
assertSameExtensionBuild(recording.extensions, opts.browser, `finding ${
|
|
96688
|
+
assertSameExtensionBuild(recording.extensions, opts.browser, `finding ${finding4.fingerprint}`);
|
|
96373
96689
|
} catch (e) {
|
|
96374
96690
|
throw new VerifyFixInputError(e instanceof Error ? e.message : String(e));
|
|
96375
96691
|
}
|
|
@@ -96402,9 +96718,9 @@ async function runVerifyFix(opts) {
|
|
|
96402
96718
|
const authTokenValues = [...invariantAuthTokens?.values() ?? []];
|
|
96403
96719
|
const primaryActor = mission.target.actors?.find((a) => a.role === "primary")?.name;
|
|
96404
96720
|
const fx = missionFixtures(mission, opts.fixtureFlags ?? {}, storageState, [...opts.secrets ?? [], ...authTokenValues], target.personas);
|
|
96405
|
-
const declared =
|
|
96721
|
+
const declared = finding4.kind === "invariant" && finding4.invariantId !== void 0 && invariantSpec !== void 0 ? {
|
|
96406
96722
|
spec: invariantSpec,
|
|
96407
|
-
id:
|
|
96723
|
+
id: finding4.invariantId,
|
|
96408
96724
|
allowlist: mission.target.allowlist,
|
|
96409
96725
|
baseUrl: mission.target.seedUrl,
|
|
96410
96726
|
...invariantAuthTokens === void 0 || invariantAuthTokens.size === 0 ? {} : { authTokens: invariantAuthTokens },
|
|
@@ -96433,10 +96749,10 @@ async function runVerifyFix(opts) {
|
|
|
96433
96749
|
const replaySession = fx === void 0 ? branchOpen : fixtureReplayOpener(branchOpen, fx, recording.fixture?.outputs ?? mission.fixtures?.outputs ?? {});
|
|
96434
96750
|
let result;
|
|
96435
96751
|
try {
|
|
96436
|
-
if (
|
|
96437
|
-
if (
|
|
96752
|
+
if (finding4.kind === "server-log") {
|
|
96753
|
+
if (finding4.serverLog === void 0) {
|
|
96438
96754
|
result = {
|
|
96439
|
-
fingerprint:
|
|
96755
|
+
fingerprint: finding4.fingerprint,
|
|
96440
96756
|
verdict: "inconclusive",
|
|
96441
96757
|
observedFingerprints: [],
|
|
96442
96758
|
replay: { outcome: "failed", at: -1, error: "not replayed" },
|
|
@@ -96445,13 +96761,13 @@ async function runVerifyFix(opts) {
|
|
|
96445
96761
|
} else {
|
|
96446
96762
|
result = await verifyServerLogDefect({
|
|
96447
96763
|
recording,
|
|
96448
|
-
recordingStepIndex:
|
|
96449
|
-
fingerprint:
|
|
96450
|
-
sources:
|
|
96451
|
-
matcher:
|
|
96452
|
-
normalizedMessage:
|
|
96453
|
-
drainMs:
|
|
96454
|
-
...
|
|
96764
|
+
recordingStepIndex: finding4.repro.recordingStepIndex,
|
|
96765
|
+
fingerprint: finding4.fingerprint,
|
|
96766
|
+
sources: finding4.serverLog.sources,
|
|
96767
|
+
matcher: finding4.serverLog.matcher,
|
|
96768
|
+
normalizedMessage: finding4.serverLog.normalizedMessage,
|
|
96769
|
+
drainMs: finding4.serverLog.drainMs,
|
|
96770
|
+
...finding4.serverLog.scope === void 0 ? {} : { scope: finding4.serverLog.scope },
|
|
96455
96771
|
// #142 follow-up: an explicit --allow-log-cmd wins; otherwise the operator's own
|
|
96456
96772
|
// ~/.jevitate/targets.json entry for this origin may opt in (never an MCP argument).
|
|
96457
96773
|
allowLogCmd: opts.allowLogCmd === true || target.allowLogCmd === true,
|
|
@@ -96465,15 +96781,15 @@ async function runVerifyFix(opts) {
|
|
|
96465
96781
|
fixtureBaseDir: journeyFixtureBaseDir(),
|
|
96466
96782
|
perceive: perceiveOpts,
|
|
96467
96783
|
recording,
|
|
96468
|
-
recordingStepIndex:
|
|
96469
|
-
fingerprint:
|
|
96470
|
-
defectKind:
|
|
96784
|
+
recordingStepIndex: finding4.repro.recordingStepIndex,
|
|
96785
|
+
fingerprint: finding4.fingerprint,
|
|
96786
|
+
defectKind: finding4.kind,
|
|
96471
96787
|
...declared === void 0 ? {} : { invariant: declared },
|
|
96472
|
-
...
|
|
96788
|
+
...finding4.hang === void 0 ? {} : { hang: finding4.hang },
|
|
96473
96789
|
// #153: never re-send a paid/destructive write unless the operator opted in.
|
|
96474
96790
|
safety: { ...target.safety ?? {}, ...opts.hangReplayWrites === true ? { hangReplayWrites: true } : {} },
|
|
96475
96791
|
writtenBy: recordedWriters(raw),
|
|
96476
|
-
...
|
|
96792
|
+
...finding4.occurrences === void 0 ? {} : { occurrences: finding4.occurrences },
|
|
96477
96793
|
...opts.settleCeilingMs === void 0 ? {} : { settleCeilingMs: opts.settleCeilingMs },
|
|
96478
96794
|
...opts.replays === void 0 ? {} : { replays: opts.replays },
|
|
96479
96795
|
...opts.actionDeltas === true ? { actionDeltas: true, secrets: [...opts.secrets ?? [], ...authTokenValues] } : {},
|
|
@@ -96491,18 +96807,18 @@ async function runVerifyFix(opts) {
|
|
|
96491
96807
|
let shotFields = {};
|
|
96492
96808
|
if (opts.browser?.recordVideo !== void 0 || opts.screenshots !== void 0) {
|
|
96493
96809
|
const secrets = [...opts.secrets ?? [], ...authTokenValues];
|
|
96494
|
-
const stored = persistedDefect(raw,
|
|
96810
|
+
const stored = persistedDefect(raw, finding4.fingerprint);
|
|
96495
96811
|
const storedEvidence = stored !== void 0 && isRecord(stored.evidence) ? stored.evidence : void 0;
|
|
96496
96812
|
const beforeVideo = typeof storedEvidence?.videoPath === "string" ? storedEvidence.videoPath : void 0;
|
|
96497
96813
|
const beforeShots = Array.isArray(storedEvidence?.screenshots) ? storedEvidence.screenshots.filter((x) => typeof x === "string") : [];
|
|
96498
|
-
const shots = opts.screenshots === void 0 ? void 0 : new RunScreenshots({ spec: opts.screenshots, dir: screenshotsDirFor(opts.screenshots, vfArtifact), secrets, title: `verify-fix ${
|
|
96814
|
+
const shots = opts.screenshots === void 0 ? void 0 : new RunScreenshots({ spec: opts.screenshots, dir: screenshotsDirFor(opts.screenshots, vfArtifact), secrets, title: `verify-fix ${finding4.fingerprint}` });
|
|
96499
96815
|
const verdictCard = () => result.verdict === "fixed" ? { text: "After the fix: the defect no longer reproduces", ok: true } : { text: `After: ${result.verdict === "still-reproduces" ? "still reproduces" : result.verdict}`, ok: false };
|
|
96500
96816
|
const after = prefix !== void 0 ? { screenshots: [], skipped: "a finding from a Journey branch point: the after-clip replay does not replay the prefix" } : fx !== void 0 || observers.length > 0 ? { screenshots: [], skipped: fx !== void 0 ? "the run used mission fixtures: the after-clip replay does not restore them" : "a cross-actor defect: no after-clip replay" } : await replayWithEvidence({
|
|
96501
96817
|
recording,
|
|
96502
|
-
stepIndex:
|
|
96503
|
-
fingerprints: [
|
|
96504
|
-
signalCheckable: signalCheckable(
|
|
96505
|
-
signal: defectSignalText(stored ?? { kind:
|
|
96818
|
+
stepIndex: finding4.repro.recordingStepIndex,
|
|
96819
|
+
fingerprints: [finding4.fingerprint, ...finding4.related ?? []],
|
|
96820
|
+
signalCheckable: signalCheckable(finding4.kind),
|
|
96821
|
+
signal: defectSignalText(stored ?? { kind: finding4.kind, ...finding4.title === void 0 ? {} : { title: finding4.title } }, secrets),
|
|
96506
96822
|
seedUrl: mission.target.seedUrl,
|
|
96507
96823
|
allowlist: mission.target.allowlist,
|
|
96508
96824
|
...storageState === void 0 ? {} : { storageState },
|
|
@@ -96531,7 +96847,7 @@ async function runVerifyFix(opts) {
|
|
|
96531
96847
|
...stale === void 0 ? {} : { failure: { kind: "journey-stale", message: stale.message } },
|
|
96532
96848
|
...evidence === void 0 ? {} : { evidence },
|
|
96533
96849
|
...shotFields,
|
|
96534
|
-
...
|
|
96850
|
+
...finding4.title === void 0 ? {} : { title: finding4.title },
|
|
96535
96851
|
...replay.override === void 0 ? {} : { allowlistOverride: replay.override },
|
|
96536
96852
|
...fx === void 0 ? {} : { fixtures: { ...fx.record(), missionIdentity: mission.fixtures?.identity ?? "none" } },
|
|
96537
96853
|
// Every replay closed its own session (finalizing its video) before verifyFix returned.
|
|
@@ -96631,17 +96947,17 @@ function ledgerAdd(opts) {
|
|
|
96631
96947
|
} catch (e) {
|
|
96632
96948
|
throw new LedgerError("E_LEDGER_INPUT", `${opts.resultPath}: ${e instanceof Error ? e.message : String(e)}`);
|
|
96633
96949
|
}
|
|
96634
|
-
const
|
|
96950
|
+
const finding4 = findFinding(mission, opts.fingerprint);
|
|
96635
96951
|
const result = isRecord(raw) && isRecord(raw.result) ? raw.result : {};
|
|
96636
|
-
const item =
|
|
96637
|
-
if (
|
|
96952
|
+
const item = finding4 === void 0 ? void 0 : rawFinding(result, finding4.fingerprint);
|
|
96953
|
+
if (finding4 === void 0 || item === void 0) {
|
|
96638
96954
|
throw new LedgerError("E_LEDGER_INPUT", `no finding with fingerprint ${opts.fingerprint} in ${opts.resultPath}`);
|
|
96639
96955
|
}
|
|
96640
96956
|
const ownRecording = isRecord(item.repro) && item.repro.recording !== void 0;
|
|
96641
96957
|
if (!ownRecording && mission.recording === null) {
|
|
96642
|
-
throw new LedgerError("E_LEDGER_INPUT", `finding ${
|
|
96958
|
+
throw new LedgerError("E_LEDGER_INPUT", `finding ${finding4.fingerprint} has no Recording to replay \u2014 nothing verify-fix could re-check`);
|
|
96643
96959
|
}
|
|
96644
|
-
const list2 =
|
|
96960
|
+
const list2 = finding4.kind === "hang" ? "hangs" : "defects";
|
|
96645
96961
|
const persistedFixtures = isRecord(result.fixtures) ? result.fixtures : void 0;
|
|
96646
96962
|
const material = {
|
|
96647
96963
|
...typeof result.schemaVersion === "number" ? { schemaVersion: result.schemaVersion } : {},
|
|
@@ -96651,7 +96967,7 @@ function ledgerAdd(opts) {
|
|
|
96651
96967
|
recording: ownRecording ? null : mission.recording,
|
|
96652
96968
|
defects: list2 === "defects" ? [item] : [],
|
|
96653
96969
|
hangs: list2 === "hangs" ? [item] : [],
|
|
96654
|
-
...
|
|
96970
|
+
...finding4.kind === "invariant" && mission.invariantSpec !== void 0 ? { invariantSpec: mission.invariantSpec } : {},
|
|
96655
96971
|
// The fixture's spec, hook HASHES and non-secret outputs — what a replay restores; hooks are never stored.
|
|
96656
96972
|
...persistedFixtures === void 0 ? {} : {
|
|
96657
96973
|
fixtures: {
|
|
@@ -96668,14 +96984,14 @@ function ledgerAdd(opts) {
|
|
|
96668
96984
|
};
|
|
96669
96985
|
assertNoSecrets(material, opts.secrets ?? []);
|
|
96670
96986
|
const dir = ledgerDir(opts.regressionsDir);
|
|
96671
|
-
const path2 = entryPath(dir,
|
|
96987
|
+
const path2 = entryPath(dir, finding4.fingerprint);
|
|
96672
96988
|
const previous = existsSync20(path2) ? readEntry(path2) : void 0;
|
|
96673
96989
|
const ticket = opts.ticket ?? previous?.ledger.ticket;
|
|
96674
96990
|
const meta3 = {
|
|
96675
96991
|
version: LEDGER_ENTRY_VERSION,
|
|
96676
|
-
fingerprint:
|
|
96677
|
-
kind:
|
|
96678
|
-
...
|
|
96992
|
+
fingerprint: finding4.fingerprint,
|
|
96993
|
+
kind: finding4.kind,
|
|
96994
|
+
...finding4.title === void 0 ? {} : { title: finding4.title },
|
|
96679
96995
|
...ticket === void 0 ? {} : { ticket },
|
|
96680
96996
|
addedAt: previous?.ledger.addedAt ?? (opts.nowIso ?? (() => clock.nowIso()))(),
|
|
96681
96997
|
source: {
|
|
@@ -96687,16 +97003,16 @@ function ledgerAdd(opts) {
|
|
|
96687
97003
|
addedBy: currentEngineInfo()
|
|
96688
97004
|
};
|
|
96689
97005
|
const entry = { ledger: meta3, result: material };
|
|
96690
|
-
const check3 = findFinding(parsePersistedMission(entry),
|
|
96691
|
-
if (check3 === void 0) throw new LedgerError("E_LEDGER_INPUT", `finding ${
|
|
97006
|
+
const check3 = findFinding(parsePersistedMission(entry), finding4.fingerprint);
|
|
97007
|
+
if (check3 === void 0) throw new LedgerError("E_LEDGER_INPUT", `finding ${finding4.fingerprint} did not survive into its ledger entry`);
|
|
96692
97008
|
mkdirSync9(dir, { recursive: true });
|
|
96693
97009
|
writeFileSync12(path2, `${JSON.stringify(entry, null, 2)}
|
|
96694
97010
|
`, "utf8");
|
|
96695
97011
|
return {
|
|
96696
97012
|
entryPath: path2,
|
|
96697
|
-
fingerprint:
|
|
96698
|
-
kind:
|
|
96699
|
-
...
|
|
97013
|
+
fingerprint: finding4.fingerprint,
|
|
97014
|
+
kind: finding4.kind,
|
|
97015
|
+
...finding4.title === void 0 ? {} : { title: finding4.title },
|
|
96700
97016
|
...ticket === void 0 ? {} : { ticket },
|
|
96701
97017
|
updated: previous !== void 0
|
|
96702
97018
|
};
|
|
@@ -96914,14 +97230,14 @@ function realSecureIO() {
|
|
|
96914
97230
|
return readMaskedLine(process.stdin, process.stdout, message2);
|
|
96915
97231
|
},
|
|
96916
97232
|
async persist(key2, value) {
|
|
96917
|
-
const { mkdir:
|
|
97233
|
+
const { mkdir: mkdir40, writeFile: writeFile47, readFile: readFile46 } = await import("node:fs/promises");
|
|
96918
97234
|
const { dirname: dirname34 } = await import("node:path");
|
|
96919
97235
|
const path2 = resolveDataDir(["credentials.json"]);
|
|
96920
|
-
await
|
|
97236
|
+
await mkdir40(dirname34(path2), { recursive: true });
|
|
96921
97237
|
let existing = {};
|
|
96922
97238
|
let raw;
|
|
96923
97239
|
try {
|
|
96924
|
-
raw = await
|
|
97240
|
+
raw = await readFile46(path2, "utf8");
|
|
96925
97241
|
} catch (err) {
|
|
96926
97242
|
const missing = typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
|
|
96927
97243
|
if (!missing) throw err;
|
|
@@ -96934,7 +97250,7 @@ function realSecureIO() {
|
|
|
96934
97250
|
}
|
|
96935
97251
|
}
|
|
96936
97252
|
existing[key2] = value;
|
|
96937
|
-
await
|
|
97253
|
+
await writeFile47(path2, JSON.stringify(existing, null, 2), { mode: 384 });
|
|
96938
97254
|
}
|
|
96939
97255
|
};
|
|
96940
97256
|
}
|
|
@@ -97318,7 +97634,7 @@ function makeFinding(input2, rubric, evidence) {
|
|
|
97318
97634
|
if (!Number.isInteger(occurrences3) || occurrences3 < 1) {
|
|
97319
97635
|
throw new UxFindingError(`occurrences for '${entry.id}' must be a positive integer, got ${occurrences3}`);
|
|
97320
97636
|
}
|
|
97321
|
-
const
|
|
97637
|
+
const finding4 = {
|
|
97322
97638
|
rubricItemId: entry.id,
|
|
97323
97639
|
citation: { source: entry.citation.source, ref: entry.citation.ref },
|
|
97324
97640
|
severity: input2.severity,
|
|
@@ -97338,7 +97654,7 @@ function makeFinding(input2, rubric, evidence) {
|
|
|
97338
97654
|
...input2.quality ? { quality: { ...input2.quality } } : {},
|
|
97339
97655
|
...input2.predictedAttention ? { predictedAttention: input2.predictedAttention } : {}
|
|
97340
97656
|
};
|
|
97341
|
-
return Object.freeze(
|
|
97657
|
+
return Object.freeze(finding4);
|
|
97342
97658
|
}
|
|
97343
97659
|
|
|
97344
97660
|
// ../ux/dist/redact.js
|
|
@@ -97799,9 +98115,9 @@ function dedupeObjectiveFindings(items, rubric) {
|
|
|
97799
98115
|
}
|
|
97800
98116
|
|
|
97801
98117
|
// ../ux/dist/recommend.js
|
|
97802
|
-
function evidenceSummaryFor(
|
|
98118
|
+
function evidenceSummaryFor(finding4, evidence) {
|
|
97803
98119
|
const parts2 = [];
|
|
97804
|
-
for (const ref of
|
|
98120
|
+
for (const ref of finding4.evidenceRefs) {
|
|
97805
98121
|
if (ref.id.startsWith("control:")) {
|
|
97806
98122
|
const idx = Number(ref.id.slice("control:".length));
|
|
97807
98123
|
const c = evidence.controls.find((ctrl) => ctrl.index === idx);
|
|
@@ -97813,19 +98129,19 @@ function evidenceSummaryFor(finding3, evidence) {
|
|
|
97813
98129
|
}
|
|
97814
98130
|
return parts2.join(" | ").slice(0, 4e3);
|
|
97815
98131
|
}
|
|
97816
|
-
async function recommend(gen,
|
|
98132
|
+
async function recommend(gen, finding4, evidence, secrets = []) {
|
|
97817
98133
|
const input2 = {
|
|
97818
|
-
principle:
|
|
97819
|
-
citationSource:
|
|
97820
|
-
citationRef:
|
|
97821
|
-
judgmentSummary:
|
|
97822
|
-
evidenceSummary: evidenceSummaryFor(
|
|
98134
|
+
principle: finding4.rubricItemId,
|
|
98135
|
+
citationSource: finding4.citation.source,
|
|
98136
|
+
citationRef: finding4.citation.ref,
|
|
98137
|
+
judgmentSummary: finding4.recommendation,
|
|
98138
|
+
evidenceSummary: evidenceSummaryFor(finding4, evidence),
|
|
97823
98139
|
appClass: evidence.appContext.appClass
|
|
97824
98140
|
};
|
|
97825
98141
|
assertNoSecretInPayload(input2, secrets);
|
|
97826
98142
|
const result = await gen.generate("ux.recommendation", input2);
|
|
97827
98143
|
const body = result.output.recommendation.trim();
|
|
97828
|
-
return `${body} (${
|
|
98144
|
+
return `${body} (${finding4.citation.source}: ${finding4.citation.ref})`;
|
|
97829
98145
|
}
|
|
97830
98146
|
|
|
97831
98147
|
// ../ux/dist/a11y.js
|
|
@@ -98172,7 +98488,7 @@ function makeSignalFinding(input2) {
|
|
|
98172
98488
|
...input2.evidence.requests.map((r) => ({ id: `request:${r.id}` })),
|
|
98173
98489
|
...input2.evidence.screenshot === void 0 ? [] : [{ id: `screenshot:${input2.evidence.screenshot}` }]
|
|
98174
98490
|
];
|
|
98175
|
-
const
|
|
98491
|
+
const finding4 = {
|
|
98176
98492
|
rubricItemId: rule.id,
|
|
98177
98493
|
citation: { ...rule.citation },
|
|
98178
98494
|
severity: rule.severity,
|
|
@@ -98190,7 +98506,7 @@ function makeSignalFinding(input2) {
|
|
|
98190
98506
|
screenIds: [...input2.screenIds ?? [input2.screenId]],
|
|
98191
98507
|
signal: input2.evidence
|
|
98192
98508
|
};
|
|
98193
|
-
return Object.freeze(
|
|
98509
|
+
return Object.freeze(finding4);
|
|
98194
98510
|
}
|
|
98195
98511
|
|
|
98196
98512
|
// ../ux/dist/signals/shared.js
|
|
@@ -99948,9 +100264,9 @@ async function analyzeClaims(request, deps) {
|
|
|
99948
100264
|
const dupOptions = /* @__PURE__ */ new Map();
|
|
99949
100265
|
for (const d of group) {
|
|
99950
100266
|
const i = indexOf.get(d);
|
|
99951
|
-
const
|
|
99952
|
-
questions[`grade::${i}::need`] = { kind: "noul", instructions: prompts.grade.need.replace("{finding}",
|
|
99953
|
-
questions[`grade::${i}::ship`] = { kind: "noul", instructions: prompts.grade.ship.replace("{finding}",
|
|
100267
|
+
const finding4 = describeDraft(d, prose[i]);
|
|
100268
|
+
questions[`grade::${i}::need`] = { kind: "noul", instructions: prompts.grade.need.replace("{finding}", finding4).replace("{persona}", persona2) };
|
|
100269
|
+
questions[`grade::${i}::ship`] = { kind: "noul", instructions: prompts.grade.ship.replace("{finding}", finding4).replace("{feature}", feature(d)) };
|
|
99954
100270
|
const earlier = drafts.slice(0, i).map((e, j) => ({ e, j })).filter(({ e }) => d.target !== void 0 && e.target !== void 0 && controlKey2(d.target) === controlKey2(e.target) && e.screen.route === d.screen.route || e.type === d.type && e.screen.route === d.screen.route).map(({ j }) => j).slice(0, MAX_CHOICE_OPTIONS - 1);
|
|
99955
100271
|
if (earlier.length > 0) {
|
|
99956
100272
|
dupOptions.set(i, earlier);
|
|
@@ -99958,7 +100274,7 @@ async function analyzeClaims(request, deps) {
|
|
|
99958
100274
|
kind: "choice",
|
|
99959
100275
|
options: ["new", ...earlier.map((j) => `same:${j}`)],
|
|
99960
100276
|
descriptions: Object.fromEntries([["new", "a different problem"], ...earlier.map((j) => [`same:${j}`, describeDraft(drafts[j], prose[j])])]),
|
|
99961
|
-
instructions: prompts.duplicate.replace("{finding}",
|
|
100277
|
+
instructions: prompts.duplicate.replace("{finding}", finding4)
|
|
99962
100278
|
};
|
|
99963
100279
|
}
|
|
99964
100280
|
}
|
|
@@ -100007,9 +100323,9 @@ async function analyzeClaims(request, deps) {
|
|
|
100007
100323
|
const impact = d.friction?.impact ?? v.impact ?? "confused";
|
|
100008
100324
|
const severity2 = v.severity ?? (d.friction === void 0 ? "minor" : IMPACT_SEVERITY2[d.friction.impact]);
|
|
100009
100325
|
const evidenceRefs = d.target !== void 0 ? [{ id: `control:${d.target.index}` }] : (v.quotes ?? []).length > 0 ? [{ id: "visibleText" }] : [{ id: "url" }];
|
|
100010
|
-
let
|
|
100326
|
+
let finding4;
|
|
100011
100327
|
try {
|
|
100012
|
-
|
|
100328
|
+
finding4 = makeFinding({
|
|
100013
100329
|
rubricItemId,
|
|
100014
100330
|
evidenceRefs,
|
|
100015
100331
|
severity: severity2,
|
|
@@ -100028,8 +100344,8 @@ async function analyzeClaims(request, deps) {
|
|
|
100028
100344
|
if (deps.polish === true) {
|
|
100029
100345
|
if (deps.gen === void 0)
|
|
100030
100346
|
throw new Error("polish requested without a generation gateway");
|
|
100031
|
-
const polished = await recommend(deps.gen,
|
|
100032
|
-
|
|
100347
|
+
const polished = await recommend(deps.gen, finding4, d.screen.red, secrets);
|
|
100348
|
+
finding4 = Object.freeze({ ...finding4, recommendation: polished });
|
|
100033
100349
|
}
|
|
100034
100350
|
} catch (cause) {
|
|
100035
100351
|
return { kind: "failed", reason: `claim finding failed: ${message(cause)}`, screenId: d.screen.raw.screenId, rubricItemId };
|
|
@@ -100042,7 +100358,7 @@ async function analyzeClaims(request, deps) {
|
|
|
100042
100358
|
...d.target === void 0 ? {} : { target: label(d.target) }
|
|
100043
100359
|
};
|
|
100044
100360
|
built.set(i, Object.freeze({
|
|
100045
|
-
...
|
|
100361
|
+
...finding4,
|
|
100046
100362
|
claim: claim2,
|
|
100047
100363
|
impact,
|
|
100048
100364
|
...grade === void 0 ? {} : { grade },
|
|
@@ -100051,7 +100367,7 @@ async function analyzeClaims(request, deps) {
|
|
|
100051
100367
|
const r = root2(i);
|
|
100052
100368
|
if (r !== i) {
|
|
100053
100369
|
const list2 = contributions.get(r) ?? [];
|
|
100054
|
-
list2.push({ rubricItemId, observation:
|
|
100370
|
+
list2.push({ rubricItemId, observation: finding4.observation, confidence: finding4.confidence, occurrences: finding4.occurrences, citation: finding4.citation, ...finding4.quality ? { quality: finding4.quality } : {} });
|
|
100055
100371
|
contributions.set(r, list2);
|
|
100056
100372
|
}
|
|
100057
100373
|
}
|
|
@@ -100082,8 +100398,8 @@ async function analyzeClaims(request, deps) {
|
|
|
100082
100398
|
if (ok2.length === 0)
|
|
100083
100399
|
continue;
|
|
100084
100400
|
a11yEvaluated += ok2.length;
|
|
100085
|
-
for (const
|
|
100086
|
-
a11yFound.push({ finding:
|
|
100401
|
+
for (const finding4 of deps.a11yChecker(s3.red, request.rubric).findings)
|
|
100402
|
+
a11yFound.push({ finding: finding4, refs: s3.red.refs });
|
|
100087
100403
|
}
|
|
100088
100404
|
}
|
|
100089
100405
|
const ledger = summarizeLedger(records);
|
|
@@ -105616,7 +105932,7 @@ ${route ?? ""}
|
|
|
105616
105932
|
${key2}`, "utf8").digest("hex").slice(0, 16);
|
|
105617
105933
|
const step = first.index + 1;
|
|
105618
105934
|
const fix = s3.route !== void 0 && route !== void 0 && s3.route !== route ? s3.fix.split(s3.route).join(route) : s3.fix;
|
|
105619
|
-
const
|
|
105935
|
+
const finding4 = {
|
|
105620
105936
|
fingerprint: fingerprint2,
|
|
105621
105937
|
kind: "ux",
|
|
105622
105938
|
claim: "other",
|
|
@@ -105632,12 +105948,12 @@ ${key2}`, "utf8").digest("hex").slice(0, 16);
|
|
|
105632
105948
|
recommendation: fix,
|
|
105633
105949
|
locator: { element: s3.element, attribute: s3.attribute, testId: s3.testId, fix, steps: s3.occurrences.length }
|
|
105634
105950
|
};
|
|
105635
|
-
const issues = findingIssues(
|
|
105951
|
+
const issues = findingIssues(finding4);
|
|
105636
105952
|
if (issues.length > 0) {
|
|
105637
|
-
warnings.push(findingDropWarning(fingerprint2,
|
|
105953
|
+
warnings.push(findingDropWarning(fingerprint2, finding4.kind, issues));
|
|
105638
105954
|
continue;
|
|
105639
105955
|
}
|
|
105640
|
-
findings.push(
|
|
105956
|
+
findings.push(finding4);
|
|
105641
105957
|
}
|
|
105642
105958
|
return { findings, warnings };
|
|
105643
105959
|
}
|
|
@@ -107619,8 +107935,51 @@ function registerLocatorHealthCommand(program2, deps) {
|
|
|
107619
107935
|
}
|
|
107620
107936
|
|
|
107621
107937
|
// src/report-cli.ts
|
|
107622
|
-
import { mkdir as
|
|
107938
|
+
import { mkdir as mkdir28, readFile as readFile28, writeFile as writeFile33 } from "node:fs/promises";
|
|
107623
107939
|
import { join as join58 } from "node:path";
|
|
107940
|
+
|
|
107941
|
+
// src/journeeze-hint.ts
|
|
107942
|
+
import { createHash as createHash18 } from "node:crypto";
|
|
107943
|
+
import { mkdir as mkdir27, readFile as readFile27, rename as rename6, writeFile as writeFile32 } from "node:fs/promises";
|
|
107944
|
+
async function loadJourneezeHintPreference(path2 = resolveDataDir(["config.json"])) {
|
|
107945
|
+
try {
|
|
107946
|
+
const parsed = JSON.parse(await readFile27(path2, "utf8"));
|
|
107947
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return {};
|
|
107948
|
+
const hints = parsed.hints;
|
|
107949
|
+
if (hints === null || typeof hints !== "object" || Array.isArray(hints)) return {};
|
|
107950
|
+
const value = hints.journeeze;
|
|
107951
|
+
if (value === false) return { enabled: false };
|
|
107952
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return {};
|
|
107953
|
+
const record2 = value;
|
|
107954
|
+
return { ...record2.enabled === false ? { enabled: false } : {}, ...typeof record2.dismissedSignal === "string" ? { dismissedSignal: record2.dismissedSignal } : {} };
|
|
107955
|
+
} catch (err) {
|
|
107956
|
+
if (err.code === "ENOENT") return {};
|
|
107957
|
+
throw err;
|
|
107958
|
+
}
|
|
107959
|
+
}
|
|
107960
|
+
function decideJourneezeHint(signal, preference = {}) {
|
|
107961
|
+
const authors = [...new Set(signal.journeyAuthors.map((author) => author.trim()).filter(Boolean))].sort();
|
|
107962
|
+
const reasons = [
|
|
107963
|
+
...signal.sharedSource ? ["shared Journey source configured"] : [],
|
|
107964
|
+
...authors.length >= 2 ? [`${authors.length} Journey/approval authors`] : [],
|
|
107965
|
+
...signal.unresolvedFindings >= 3 ? [`${signal.unresolvedFindings} persistent unresolved findings`] : [],
|
|
107966
|
+
...signal.strictReviewApproved ? ["human-approved strict Journey review"] : []
|
|
107967
|
+
];
|
|
107968
|
+
const eligible = signal.sharedSource || reasons.length >= 2;
|
|
107969
|
+
const hash3 = createHash18("sha256").update(JSON.stringify({ sharedSource: signal.sharedSource, authors, unresolvedFindings: signal.unresolvedFindings, strictReviewApproved: signal.strictReviewApproved })).digest("hex");
|
|
107970
|
+
return { show: preference.enabled !== false && eligible && preference.dismissedSignal !== hash3, signal: hash3, reasons };
|
|
107971
|
+
}
|
|
107972
|
+
function renderJourneezeHint(signal, preference = {}, context = { isTTY: process.stdout.isTTY === true }) {
|
|
107973
|
+
const env = context.env ?? process.env;
|
|
107974
|
+
if (context.isTTY !== true || context.json === true || (context.mcp ?? inMcpInvocation()) || CI_ENV_MARKERS.some((key2) => env[key2] !== void 0)) return null;
|
|
107975
|
+
const decision = decideJourneezeHint(signal, preference);
|
|
107976
|
+
if (!decision.show) return null;
|
|
107977
|
+
return `
|
|
107978
|
+
Tip: Journeeze can coordinate this shared testing work (${decision.reasons.join(", ")}). Run \`jevitate connect journeeze\` to connect it.
|
|
107979
|
+
`;
|
|
107980
|
+
}
|
|
107981
|
+
|
|
107982
|
+
// src/report-cli.ts
|
|
107624
107983
|
function emit3(program2, envelope, exitCode) {
|
|
107625
107984
|
emitJsonOrRefusal(program2, envelope, exitCode);
|
|
107626
107985
|
}
|
|
@@ -107629,6 +107988,25 @@ function failure2(program2, err, code) {
|
|
|
107629
107988
|
else emit3(program2, fail(code, err instanceof Error ? err.message : String(err)));
|
|
107630
107989
|
}
|
|
107631
107990
|
var collect3 = (v, prev) => [...prev, v];
|
|
107991
|
+
async function hasSharedSource() {
|
|
107992
|
+
try {
|
|
107993
|
+
const raw = JSON.parse(await readFile28(join58(process.cwd(), "jevitate.lock"), "utf8"));
|
|
107994
|
+
return raw !== null && typeof raw === "object" && !Array.isArray(raw) && Array.isArray(raw.sources) && raw.sources.length > 0;
|
|
107995
|
+
} catch {
|
|
107996
|
+
return false;
|
|
107997
|
+
}
|
|
107998
|
+
}
|
|
107999
|
+
async function reportJourneezeHint(json2) {
|
|
108000
|
+
try {
|
|
108001
|
+
return renderJourneezeHint(
|
|
108002
|
+
{ sharedSource: await hasSharedSource(), journeyAuthors: [], unresolvedFindings: 0, strictReviewApproved: false },
|
|
108003
|
+
await loadJourneezeHintPreference(),
|
|
108004
|
+
{ isTTY: process.stdout.isTTY === true, json: json2 }
|
|
108005
|
+
) ?? "";
|
|
108006
|
+
} catch {
|
|
108007
|
+
return "";
|
|
108008
|
+
}
|
|
108009
|
+
}
|
|
107632
108010
|
function registerReportCommands(program2, deps) {
|
|
107633
108011
|
program2.command("report").description("one deduped defect list for a target across every mode and run (markdown + JSON envelope)").option("--target <origin|name>", "the target: an origin (or URL on it), a suite target name, or a registered mission target").option("--since <run|date>", "only runs that started at/after this ISO date or this run").option("--dir <dir>", "results dir to read (repeatable). Default: this project's runs \u2014 its .jevitate/logs plus every run recorded for it in ~/.jevitate/run-index.jsonl (including --out dirs); with --target, every .jevitate/logs dir (project and ~/.jevitate) and the 0.1.0 recordings/ux-reports dirs too", collect3, []).option("--baseline <run|tag|last>", "add a diff section against a baseline: a run, a `baseline tag`, or `last` (the previous run per target+mode)").option("--tag <key=value>", "only runs carrying this tag (repeatable; every tag must match)", collect3, []).option("--out <dir>", "also write report.md and report.json here").option("--json", "emit the JSON envelope instead of markdown").action(async function() {
|
|
107634
108012
|
const o = this.opts();
|
|
@@ -107644,14 +108022,14 @@ function registerReportCommands(program2, deps) {
|
|
|
107644
108022
|
...deps.baselinesDir === void 0 ? {} : { baselinesDir: deps.baselinesDir }
|
|
107645
108023
|
});
|
|
107646
108024
|
if (o.out !== void 0) {
|
|
107647
|
-
await
|
|
107648
|
-
await
|
|
107649
|
-
await
|
|
108025
|
+
await mkdir28(o.out, { recursive: true });
|
|
108026
|
+
await writeFile33(join58(o.out, "report.md"), report2.markdown, "utf8");
|
|
108027
|
+
await writeFile33(join58(o.out, "report.json"), `${JSON.stringify(ok(report2), null, 2)}
|
|
107650
108028
|
`, "utf8");
|
|
107651
108029
|
}
|
|
107652
108030
|
if (o.json) emit3(program2, ok(report2), 0);
|
|
107653
108031
|
else {
|
|
107654
|
-
program2.configureOutput().writeOut?.(report2.markdown);
|
|
108032
|
+
program2.configureOutput().writeOut?.(`${report2.markdown}${await reportJourneezeHint(o.json)}`);
|
|
107655
108033
|
process.exitCode = 0;
|
|
107656
108034
|
}
|
|
107657
108035
|
} catch (err) {
|
|
@@ -107832,7 +108210,7 @@ async function collectAllMissingKeys(store, io, opts = {}) {
|
|
|
107832
108210
|
}
|
|
107833
108211
|
|
|
107834
108212
|
// src/init-mcp.ts
|
|
107835
|
-
import { readFile as
|
|
108213
|
+
import { readFile as readFile29, writeFile as writeFile34, mkdir as mkdir29 } from "node:fs/promises";
|
|
107836
108214
|
import { homedir as realHomedir2 } from "node:os";
|
|
107837
108215
|
import { join as join59, dirname as dirname26 } from "node:path";
|
|
107838
108216
|
var MCP_SERVER_NAME = "jevitate";
|
|
@@ -107965,7 +108343,7 @@ async function registerMcp(runtimes, paths, opts = {}) {
|
|
|
107965
108343
|
for (const unit of units) {
|
|
107966
108344
|
let existing;
|
|
107967
108345
|
try {
|
|
107968
|
-
existing = await
|
|
108346
|
+
existing = await readFile29(unit.path, "utf8");
|
|
107969
108347
|
} catch {
|
|
107970
108348
|
existing = void 0;
|
|
107971
108349
|
}
|
|
@@ -107975,8 +108353,8 @@ async function registerMcp(runtimes, paths, opts = {}) {
|
|
|
107975
108353
|
entry.instruction = renderPrintConfig(unit.harness);
|
|
107976
108354
|
}
|
|
107977
108355
|
if (!opts.dryRun && result.content !== void 0) {
|
|
107978
|
-
await
|
|
107979
|
-
await
|
|
108356
|
+
await mkdir29(dirname26(unit.path), { recursive: true });
|
|
108357
|
+
await writeFile34(unit.path, result.content, "utf8");
|
|
107980
108358
|
}
|
|
107981
108359
|
report2.push(entry);
|
|
107982
108360
|
}
|
|
@@ -108296,7 +108674,7 @@ function registerProfileCommands(program2, deps) {
|
|
|
108296
108674
|
}
|
|
108297
108675
|
|
|
108298
108676
|
// src/site-cli.ts
|
|
108299
|
-
import { readFile as
|
|
108677
|
+
import { readFile as readFile30 } from "node:fs/promises";
|
|
108300
108678
|
function registerSiteCommands(program2, deps) {
|
|
108301
108679
|
const site = program2.command("site").description("per-site policies for Journey runs: human-like pacing, throttles, run budgets and quiet hours");
|
|
108302
108680
|
const sitePolicy = site.command("policy").description("read or set a site's policy (the site is the Journey's origin, e.g. https://app.example.com)");
|
|
@@ -108354,7 +108732,7 @@ function registerSiteCommands(program2, deps) {
|
|
|
108354
108732
|
const { account, db, file: file2, json: json2 } = this.opts();
|
|
108355
108733
|
let policy;
|
|
108356
108734
|
try {
|
|
108357
|
-
const raw = await
|
|
108735
|
+
const raw = await readFile30(file2, "utf8");
|
|
108358
108736
|
policy = SitePolicySchema.parse(JSON.parse(raw));
|
|
108359
108737
|
} catch (err) {
|
|
108360
108738
|
emitJson(program2, fail("E_INVALID_POLICY", String(err)));
|
|
@@ -108382,7 +108760,7 @@ function registerSiteCommands(program2, deps) {
|
|
|
108382
108760
|
const { account, db, script, seed, json: json2 } = this.opts();
|
|
108383
108761
|
let plannedScript;
|
|
108384
108762
|
try {
|
|
108385
|
-
const raw = await
|
|
108763
|
+
const raw = await readFile30(script, "utf8");
|
|
108386
108764
|
plannedScript = parsePlannedScript(raw);
|
|
108387
108765
|
} catch (err) {
|
|
108388
108766
|
emitJson(program2, fail("E_INVALID_SCRIPT", String(err)));
|
|
@@ -108418,12 +108796,12 @@ function registerSiteCommands(program2, deps) {
|
|
|
108418
108796
|
}
|
|
108419
108797
|
|
|
108420
108798
|
// src/recording-cli.ts
|
|
108421
|
-
import { readFile as
|
|
108799
|
+
import { readFile as readFile31, writeFile as writeFile35 } from "node:fs/promises";
|
|
108422
108800
|
import { resolve as resolvePath14 } from "node:path";
|
|
108423
108801
|
import * as clack from "@clack/prompts";
|
|
108424
108802
|
function registerRecordingCommands(program2, deps) {
|
|
108425
108803
|
async function readTakeJson(file2) {
|
|
108426
|
-
const raw = await
|
|
108804
|
+
const raw = await readFile31(file2, "utf8");
|
|
108427
108805
|
let json2;
|
|
108428
108806
|
try {
|
|
108429
108807
|
json2 = JSON.parse(raw);
|
|
@@ -108442,7 +108820,7 @@ function registerRecordingCommands(program2, deps) {
|
|
|
108442
108820
|
return { recording: parsed.recording, values: new Map(Object.entries(parsed.values)) };
|
|
108443
108821
|
}
|
|
108444
108822
|
async function readRecordingOrTake(file2) {
|
|
108445
|
-
const raw = await
|
|
108823
|
+
const raw = await readFile31(file2, "utf8");
|
|
108446
108824
|
const json2 = JSON.parse(raw);
|
|
108447
108825
|
if (json2 !== null && typeof json2 === "object" && !Array.isArray(json2) && "recording" in json2) {
|
|
108448
108826
|
return AuthoringTakeSchema.parse(json2).recording;
|
|
@@ -108517,7 +108895,7 @@ function registerRecordingCommands(program2, deps) {
|
|
|
108517
108895
|
}
|
|
108518
108896
|
const result = applyPostdoc(takes[0], diff2, decisions);
|
|
108519
108897
|
if (out !== void 0) {
|
|
108520
|
-
await
|
|
108898
|
+
await writeFile35(out, `${JSON.stringify(result, null, 2)}
|
|
108521
108899
|
`, "utf8");
|
|
108522
108900
|
}
|
|
108523
108901
|
if (json2) {
|
|
@@ -108595,7 +108973,7 @@ var DraftSaveError = class extends Error {
|
|
|
108595
108973
|
async function saveDraft(draft, out, journeysDir) {
|
|
108596
108974
|
const steps = draft.recording.pages.reduce((n3, p) => n3 + p.steps.length, 0);
|
|
108597
108975
|
if (out !== void 0) {
|
|
108598
|
-
await
|
|
108976
|
+
await writeFile35(out, `${JSON.stringify(draft, null, 2)}
|
|
108599
108977
|
`, "utf8");
|
|
108600
108978
|
return { id: draft.metadata.id, path: resolvePath14(out), savedTo: "file", promoted: false, steps };
|
|
108601
108979
|
}
|
|
@@ -108694,7 +109072,7 @@ async function loadDecisions(file2) {
|
|
|
108694
109072
|
let raw;
|
|
108695
109073
|
let parsed;
|
|
108696
109074
|
try {
|
|
108697
|
-
raw = await
|
|
109075
|
+
raw = await readFile31(file2, "utf8");
|
|
108698
109076
|
parsed = JSON.parse(raw);
|
|
108699
109077
|
} catch (err) {
|
|
108700
109078
|
throw new DecisionsParseError(err);
|
|
@@ -108771,9 +109149,9 @@ function targetsOpts(deps) {
|
|
|
108771
109149
|
const file2 = deps.explore?.targetsConfigPath;
|
|
108772
109150
|
return file2 === void 0 ? {} : { targetsFile: file2 };
|
|
108773
109151
|
}
|
|
108774
|
-
function lintFindingLine(
|
|
108775
|
-
const message2 =
|
|
108776
|
-
return `${
|
|
109152
|
+
function lintFindingLine(finding4) {
|
|
109153
|
+
const message2 = finding4.message.replace(/^step \d+: /, "");
|
|
109154
|
+
return `${finding4.level} ${finding4.step === void 0 ? "" : `step ${finding4.step} `}${finding4.rule} ${message2}${finding4.fix === void 0 ? "" : ` \u2014 fix: ${finding4.fix}`}`;
|
|
108777
109155
|
}
|
|
108778
109156
|
var FULL_FLAG_HELP = "#496: show every detail section: catalog analysis, the full lint list, locator health, provenance, the full change diff";
|
|
108779
109157
|
var DEPRECATED_DIR_HELP = "deprecated: use --journeys-dir <path> (same meaning); default: the repo's .jevitate/journeys, outside a repo ~/.jevitate/journeys";
|
|
@@ -108865,7 +109243,7 @@ function registerJourneyFind(journey, program2, deps) {
|
|
|
108865
109243
|
import { existsSync as existsSync27 } from "node:fs";
|
|
108866
109244
|
|
|
108867
109245
|
// src/journey-annotate-api.ts
|
|
108868
|
-
import { mkdir as
|
|
109246
|
+
import { mkdir as mkdir30, readFile as readFile32, rm as rm12, writeFile as writeFile36 } from "node:fs/promises";
|
|
108869
109247
|
import { dirname as dirname28, join as join62 } from "node:path";
|
|
108870
109248
|
var EVIDENCE_TEXT_CHARS = 1500;
|
|
108871
109249
|
var AnnotationDraftNotFoundError = class extends Error {
|
|
@@ -109013,8 +109391,8 @@ async function annotateJourney(opts) {
|
|
|
109013
109391
|
});
|
|
109014
109392
|
assertNoSecretInPayload(draft, secrets, "journey annotation draft");
|
|
109015
109393
|
const draftPath = annotationDraftPath(opts.dir, opts.id);
|
|
109016
|
-
await
|
|
109017
|
-
await
|
|
109394
|
+
await mkdir30(dirname28(draftPath), { recursive: true, mode: 448 });
|
|
109395
|
+
await writeFile36(draftPath, `${JSON.stringify(draft, null, 2)}
|
|
109018
109396
|
`, { mode: 384 });
|
|
109019
109397
|
return {
|
|
109020
109398
|
id: opts.id,
|
|
@@ -109037,7 +109415,7 @@ async function readAnnotationDraft(dir, id) {
|
|
|
109037
109415
|
const draftPath = annotationDraftPath(dir, id);
|
|
109038
109416
|
let raw;
|
|
109039
109417
|
try {
|
|
109040
|
-
raw = await
|
|
109418
|
+
raw = await readFile32(draftPath, "utf8");
|
|
109041
109419
|
} catch (err) {
|
|
109042
109420
|
if (err.code === "ENOENT") {
|
|
109043
109421
|
throw new AnnotationDraftNotFoundError(`no annotation draft for journey '${id}' (${draftPath}) \u2014 run \`jevitate journey annotate ${id}\` first`);
|
|
@@ -109976,6 +110354,35 @@ async function reviewJourneyById(journeysDir, id, opts = {}) {
|
|
|
109976
110354
|
}
|
|
109977
110355
|
|
|
109978
110356
|
// src/journey-verify.ts
|
|
110357
|
+
async function planJourneyVerification(opts) {
|
|
110358
|
+
const journey = await new JourneyRegistry(new FsJourneyStore(opts.dir)).get(opts.id);
|
|
110359
|
+
if (!journey) throw new UnknownJourneyError(`unknown journey '${opts.id}'`);
|
|
110360
|
+
const sites = journeyAssertions(journey);
|
|
110361
|
+
const checkOf = new Map(sites.map((site) => [assertionSiteKey(site), describeCheck(site.check)]));
|
|
110362
|
+
const plan2 = planJourneyMutations(journey, {
|
|
110363
|
+
...opts.readRequests === void 0 ? {} : { readRequests: opts.readRequests },
|
|
110364
|
+
params: opts.params
|
|
110365
|
+
});
|
|
110366
|
+
const pairs = plan2.pairs.map((pair) => ({ ...pair, check: checkOf.get(pair.site) ?? "unknown assertion" }));
|
|
110367
|
+
const assertions = sites.map((site) => {
|
|
110368
|
+
const key2 = assertionSiteKey(site);
|
|
110369
|
+
return { site: key2, check: describeCheck(site.check), mutations: pairs.filter((pair) => pair.site === key2).map((pair) => pair.mutation) };
|
|
110370
|
+
});
|
|
110371
|
+
return {
|
|
110372
|
+
journeyId: journey.metadata.id,
|
|
110373
|
+
journeyHash: journeyContentHash(journey),
|
|
110374
|
+
mutations: plan2.mutations,
|
|
110375
|
+
pairs,
|
|
110376
|
+
assertions,
|
|
110377
|
+
summary: {
|
|
110378
|
+
mutations: plan2.mutations.length,
|
|
110379
|
+
pairs: pairs.length,
|
|
110380
|
+
assertions: assertions.length,
|
|
110381
|
+
unpaired: assertions.filter((assertion) => assertion.mutations.length === 0).length,
|
|
110382
|
+
minimumReplays: plan2.mutations.length + 1
|
|
110383
|
+
}
|
|
110384
|
+
};
|
|
110385
|
+
}
|
|
109979
110386
|
function verifyExitCode(verdict) {
|
|
109980
110387
|
return verdict === "proven" ? 0 : verdict === "insensitive" ? 1 : 2;
|
|
109981
110388
|
}
|
|
@@ -110678,7 +111085,7 @@ function registerJourneyReadiness(journey, program2, deps) {
|
|
|
110678
111085
|
|
|
110679
111086
|
// src/journey-bundle.ts
|
|
110680
111087
|
import { existsSync as existsSync30 } from "node:fs";
|
|
110681
|
-
import { mkdir as
|
|
111088
|
+
import { mkdir as mkdir31, readFile as readFile33, writeFile as writeFile37 } from "node:fs/promises";
|
|
110682
111089
|
import { join as join63, resolve as resolve19 } from "node:path";
|
|
110683
111090
|
var BundleStaleError = class extends Error {
|
|
110684
111091
|
constructor(changed) {
|
|
@@ -110869,9 +111276,9 @@ async function buildJourneyBundle(opts, deps = {}) {
|
|
|
110869
111276
|
verdict: receipt.verdict,
|
|
110870
111277
|
generatedAt: clock.nowIso()
|
|
110871
111278
|
};
|
|
110872
|
-
await
|
|
110873
|
-
await
|
|
110874
|
-
await
|
|
111279
|
+
await mkdir31(out, { recursive: true });
|
|
111280
|
+
await writeFile37(bundlePath, renderJourneyBundle(receipt, entries, catalog, drafts), "utf8");
|
|
111281
|
+
await writeFile37(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
110875
111282
|
`, "utf8");
|
|
110876
111283
|
return { out, files: [bundlePath, manifestPath], verdict: receipt.verdict };
|
|
110877
111284
|
}
|
|
@@ -110879,7 +111286,7 @@ async function checkJourneyBundle(dir, opts, deps = {}) {
|
|
|
110879
111286
|
const review = deps.review ?? reviewJourneyById;
|
|
110880
111287
|
const catalogOf = deps.loadCatalog ?? loadCatalog;
|
|
110881
111288
|
const manifestPath = join63(resolve19(dir), "manifest.json");
|
|
110882
|
-
const manifest = JSON.parse(await
|
|
111289
|
+
const manifest = JSON.parse(await readFile33(manifestPath, "utf8"));
|
|
110883
111290
|
const reviewOpts = { strict: true, ...opts.catalogDir === void 0 ? {} : { catalogDir: opts.catalogDir }, ...opts.targetsFile === void 0 ? {} : { targetsFile: opts.targetsFile } };
|
|
110884
111291
|
const changed = [];
|
|
110885
111292
|
for (const recorded of manifest.journeys) {
|
|
@@ -110946,7 +111353,7 @@ function registerJourneyBundle(journey, program2, deps) {
|
|
|
110946
111353
|
}
|
|
110947
111354
|
|
|
110948
111355
|
// src/journey-cli/promote.ts
|
|
110949
|
-
import { readFile as
|
|
111356
|
+
import { readFile as readFile34 } from "node:fs/promises";
|
|
110950
111357
|
|
|
110951
111358
|
// src/review-render-options.ts
|
|
110952
111359
|
function processTerminal2() {
|
|
@@ -111018,7 +111425,7 @@ function registerJourneyPromote(journey, program2, deps) {
|
|
|
111018
111425
|
if (reviewSheet !== void 0) {
|
|
111019
111426
|
let text2;
|
|
111020
111427
|
try {
|
|
111021
|
-
text2 = await
|
|
111428
|
+
text2 = await readFile34(reviewSheet, "utf8");
|
|
111022
111429
|
} catch (e) {
|
|
111023
111430
|
emitJson(program2, fail("E_JOURNEY_REVIEW_ARGS", `cannot read --review-sheet ${reviewSheet}: ${e instanceof Error ? e.message : String(e)}`));
|
|
111024
111431
|
return;
|
|
@@ -111075,7 +111482,7 @@ function registerJourneyPromote(journey, program2, deps) {
|
|
|
111075
111482
|
} else if (err instanceof WeakJourneyError) {
|
|
111076
111483
|
if (!json2) {
|
|
111077
111484
|
const out = program2.configureOutput().writeOut;
|
|
111078
|
-
for (const
|
|
111485
|
+
for (const finding4 of err.findings) out?.(`${lintFindingLine(finding4)}
|
|
111079
111486
|
`);
|
|
111080
111487
|
}
|
|
111081
111488
|
emitJson(program2, fail("E_JOURNEY_WEAK", String(err.message)));
|
|
@@ -111095,7 +111502,22 @@ function registerJourneyPromote(journey, program2, deps) {
|
|
|
111095
111502
|
}
|
|
111096
111503
|
|
|
111097
111504
|
// src/journey-cli/review.ts
|
|
111098
|
-
import { writeFile as
|
|
111505
|
+
import { readFile as readFile35, writeFile as writeFile38 } from "node:fs/promises";
|
|
111506
|
+
import { join as join64 } from "node:path";
|
|
111507
|
+
async function reviewJourneezeHint(json2, outFile) {
|
|
111508
|
+
if (json2 || outFile !== void 0) return "";
|
|
111509
|
+
try {
|
|
111510
|
+
const raw = JSON.parse(await readFile35(join64(process.cwd(), "jevitate.lock"), "utf8"));
|
|
111511
|
+
const sharedSource = raw !== null && typeof raw === "object" && !Array.isArray(raw) && Array.isArray(raw.sources) && raw.sources.length > 0;
|
|
111512
|
+
return renderJourneezeHint(
|
|
111513
|
+
{ sharedSource, journeyAuthors: [], unresolvedFindings: 0, strictReviewApproved: false },
|
|
111514
|
+
await loadJourneezeHintPreference(),
|
|
111515
|
+
{ isTTY: process.stdout.isTTY === true, json: json2 }
|
|
111516
|
+
) ?? "";
|
|
111517
|
+
} catch {
|
|
111518
|
+
return "";
|
|
111519
|
+
}
|
|
111520
|
+
}
|
|
111099
111521
|
function registerJourneyReview(journey, program2, deps) {
|
|
111100
111522
|
withProjectFlags(journey.command("review [id]")).description(
|
|
111101
111523
|
"a human-readable review sheet for promotion sign-off: summary, steps, side effects, inputs (names only), proof, change since last approval, content hash. #467: --stale (no id) lists every promoted Journey whose approval is stale, labelling step-id-only changes"
|
|
@@ -111136,14 +111558,14 @@ function registerJourneyReview(journey, program2, deps) {
|
|
|
111136
111558
|
const renderOpts = reviewRenderOptions({ full: full === true, toFile: outFile !== void 0 });
|
|
111137
111559
|
const rendered = json2 ? `${JSON.stringify(review, null, 2)}
|
|
111138
111560
|
` : markdown ? renderReviewMarkdown(review, renderOpts) : renderReviewText(review, renderOpts);
|
|
111139
|
-
if (outFile !== void 0) await
|
|
111561
|
+
if (outFile !== void 0) await writeFile38(outFile, rendered, { mode: 384 });
|
|
111140
111562
|
if (json2) {
|
|
111141
111563
|
emitJson(program2, strict === true ? Object.assign(ok(review), { strict: strictVerdict }) : ok(review));
|
|
111142
111564
|
} else if (outFile !== void 0) {
|
|
111143
111565
|
program2.configureOutput().writeOut?.(`review sheet for journey '${review.id}' written to ${outFile} (content hash ${review.contentHash})
|
|
111144
111566
|
`);
|
|
111145
111567
|
} else {
|
|
111146
|
-
program2.configureOutput().writeOut?.(rendered);
|
|
111568
|
+
program2.configureOutput().writeOut?.(`${rendered}${await reviewJourneezeHint(false, outFile)}`);
|
|
111147
111569
|
}
|
|
111148
111570
|
emitUsageLine(program2, review.jev);
|
|
111149
111571
|
process.exitCode = strict === true && !strictVerdict.passed ? EXIT_CODES.defects : 0;
|
|
@@ -111169,8 +111591,8 @@ function registerJourneyReview(journey, program2, deps) {
|
|
|
111169
111591
|
import { existsSync as existsSync31 } from "node:fs";
|
|
111170
111592
|
|
|
111171
111593
|
// src/journey-migrate-api.ts
|
|
111172
|
-
import { readdir as readdir11, readFile as
|
|
111173
|
-
import { join as
|
|
111594
|
+
import { readdir as readdir11, readFile as readFile36, writeFile as writeFile39 } from "node:fs/promises";
|
|
111595
|
+
import { join as join65 } from "node:path";
|
|
111174
111596
|
async function migrateStepIds(req) {
|
|
111175
111597
|
const store = new FsJourneyStore(req.journeysDir);
|
|
111176
111598
|
const journeys = [];
|
|
@@ -111185,7 +111607,7 @@ async function migrateStepIds(req) {
|
|
|
111185
111607
|
if (!req.dryRun) await store.put(after);
|
|
111186
111608
|
journeys.push({ id: meta3.id, stepsMinted, anchorsLinked, needsReapproval: before.metadata.promoted });
|
|
111187
111609
|
}
|
|
111188
|
-
const recordings = req.projectDir === null ? [] : await migrateRegressions(
|
|
111610
|
+
const recordings = req.projectDir === null ? [] : await migrateRegressions(join65(req.projectDir, "regressions"), req.dryRun);
|
|
111189
111611
|
return {
|
|
111190
111612
|
dryRun: req.dryRun,
|
|
111191
111613
|
journeys,
|
|
@@ -111206,11 +111628,11 @@ async function migrateRegressions(dir, dryRun) {
|
|
|
111206
111628
|
}
|
|
111207
111629
|
const out = [];
|
|
111208
111630
|
for (const name of names) {
|
|
111209
|
-
const path2 =
|
|
111210
|
-
const rec = RecordingSchema.parse(JSON.parse(await
|
|
111631
|
+
const path2 = join65(dir, name);
|
|
111632
|
+
const rec = RecordingSchema.parse(JSON.parse(await readFile36(path2, "utf8")));
|
|
111211
111633
|
const next = ensureStepIds(rec);
|
|
111212
111634
|
if (next === rec) continue;
|
|
111213
|
-
if (!dryRun) await
|
|
111635
|
+
if (!dryRun) await writeFile39(path2, `${JSON.stringify(RecordingSchema.parse(next), null, 2)}
|
|
111214
111636
|
`);
|
|
111215
111637
|
out.push({ path: path2, stepsMinted: countRecIds(next) - countRecIds(rec) });
|
|
111216
111638
|
}
|
|
@@ -111464,7 +111886,7 @@ function registerJourneyMigrate(journey, program2, deps) {
|
|
|
111464
111886
|
}
|
|
111465
111887
|
|
|
111466
111888
|
// src/journey-cli/lint.ts
|
|
111467
|
-
import { writeFile as
|
|
111889
|
+
import { writeFile as writeFile40 } from "node:fs/promises";
|
|
111468
111890
|
|
|
111469
111891
|
// src/journey-anchor-api.ts
|
|
111470
111892
|
async function load(dir, id) {
|
|
@@ -111631,16 +112053,16 @@ function renderJourneyLintSarif(input2) {
|
|
|
111631
112053
|
shortDescription: { text: rule.id },
|
|
111632
112054
|
defaultConfiguration: { level: rule.level }
|
|
111633
112055
|
}));
|
|
111634
|
-
const results = input2.findings.map((
|
|
111635
|
-
ruleId:
|
|
111636
|
-
level:
|
|
111637
|
-
message: { text:
|
|
111638
|
-
...
|
|
112056
|
+
const results = input2.findings.map((finding4) => ({
|
|
112057
|
+
ruleId: finding4.rule,
|
|
112058
|
+
level: finding4.level,
|
|
112059
|
+
message: { text: finding4.fix === void 0 ? finding4.message : `${finding4.message} \u2014 fix: ${finding4.fix}` },
|
|
112060
|
+
...finding4.fix === void 0 ? {} : { properties: { fix: finding4.fix } },
|
|
111639
112061
|
locations: [
|
|
111640
112062
|
{
|
|
111641
112063
|
logicalLocations: [
|
|
111642
112064
|
{ name: input2.id, kind: "module" },
|
|
111643
|
-
...
|
|
112065
|
+
...finding4.step === void 0 ? [] : [{ name: `step ${finding4.step}`, kind: "function" }]
|
|
111644
112066
|
]
|
|
111645
112067
|
}
|
|
111646
112068
|
]
|
|
@@ -111658,7 +112080,7 @@ function renderJourneyLintSarif(input2) {
|
|
|
111658
112080
|
}
|
|
111659
112081
|
|
|
111660
112082
|
// src/journey-link-hints.ts
|
|
111661
|
-
import { join as
|
|
112083
|
+
import { join as join66 } from "node:path";
|
|
111662
112084
|
var MAX_LISTED = 10;
|
|
111663
112085
|
function listed(ids) {
|
|
111664
112086
|
return ids.length <= MAX_LISTED ? ids.join(", ") : `${ids.slice(0, MAX_LISTED).join(", ")}, \u2026 (${ids.length - MAX_LISTED} more: jevitate catalog status)`;
|
|
@@ -111667,7 +112089,7 @@ async function loadLinkCatalog(catalogDir) {
|
|
|
111667
112089
|
const empty = { dir: catalogDir, personasFile: null, jobsFile: null, personas: [], jobs: [], journeys: [] };
|
|
111668
112090
|
if (catalogDir === null) return empty;
|
|
111669
112091
|
try {
|
|
111670
|
-
const loader = new CatalogLoader({ catalogDir, journeysDir:
|
|
112092
|
+
const loader = new CatalogLoader({ catalogDir, journeysDir: join66(catalogDir, "journeys") });
|
|
111671
112093
|
const [{ file: personasFile, personas }, { file: jobsFile, jobs }] = await Promise.all([loader.loadPersonas(), loader.loadJobs()]);
|
|
111672
112094
|
return { ...empty, personasFile, jobsFile, personas, jobs };
|
|
111673
112095
|
} catch {
|
|
@@ -111734,7 +112156,7 @@ function registerJourneyLint(journey, program2, deps) {
|
|
|
111734
112156
|
const result = { ...linted, findings: [...linted.findings, ...linkFindings], warnings: linted.warnings + linkFindings.length };
|
|
111735
112157
|
if (sarif !== void 0) {
|
|
111736
112158
|
const log = renderJourneyLintSarif({ id: result.id, findings: result.findings, version: currentEngineInfo().version });
|
|
111737
|
-
await
|
|
112159
|
+
await writeFile40(sarif, `${JSON.stringify(log, null, 2)}
|
|
111738
112160
|
`);
|
|
111739
112161
|
}
|
|
111740
112162
|
if (json2) {
|
|
@@ -111752,7 +112174,7 @@ function registerJourneyLint(journey, program2, deps) {
|
|
|
111752
112174
|
`);
|
|
111753
112175
|
}
|
|
111754
112176
|
}
|
|
111755
|
-
for (const
|
|
112177
|
+
for (const finding4 of result.findings) out?.(`${lintFindingLine(finding4)}
|
|
111756
112178
|
`);
|
|
111757
112179
|
out?.(`${result.errors} error(s), ${result.warnings} warning(s) \u2014 journey '${result.id}'
|
|
111758
112180
|
`);
|
|
@@ -111771,14 +112193,38 @@ function registerJourneyLint(journey, program2, deps) {
|
|
|
111771
112193
|
// src/journey-cli/verify.ts
|
|
111772
112194
|
import { existsSync as existsSync32 } from "node:fs";
|
|
111773
112195
|
function registerJourneyVerify(journey, program2, deps) {
|
|
111774
|
-
withReplayAllowFlag(withEnvironmentFlags(withBrowserLaunchFlags(withFixtureFlags(withProjectFlags(journey.command("verify <id>")))))).description("prove each assertion
|
|
112196
|
+
withReplayAllowFlag(withEnvironmentFlags(withBrowserLaunchFlags(withFixtureFlags(withProjectFlags(journey.command("verify <id>")))))).description("preview or prove that each Journey assertion fails when its outcome is absent").option("--mutate", "run the mutation proof").option("--plan", "print the static mutation/assertion plan without opening a browser").option("--dir <path>", DEPRECATED_DIR_HELP).option("--param <kv>", "param as key=value (repeatable)", collectParam, {}).option("--storage-state <file>", "Playwright storageState JSON to start each replay authenticated (as journey run)").option("--json", "emit a JSON envelope").action(async function(id) {
|
|
111775
112197
|
const locationFlags = this.opts();
|
|
111776
112198
|
const layout = journeyLayout(program2, deps, { dir: locationFlags.dir, project: locationFlags.project, journeysDir: locationFlags.journeysDir });
|
|
111777
112199
|
if (layout === null) return;
|
|
111778
112200
|
const verifyJourneysDir = layout.journeysDir;
|
|
111779
|
-
const { mutate, param, storageState: storageStateFlag, json: json2, env: envName, baseUrl } = this.opts();
|
|
111780
|
-
if (mutate
|
|
111781
|
-
emitJson(program2, fail("E_JOURNEY_VERIFY_ARGS", "journey verify needs
|
|
112201
|
+
const { mutate, plan: plan2, param, storageState: storageStateFlag, json: json2, env: envName, baseUrl } = this.opts();
|
|
112202
|
+
if (mutate === true === (plan2 === true)) {
|
|
112203
|
+
emitJson(program2, fail("E_JOURNEY_VERIFY_ARGS", "journey verify needs exactly one of --plan or --mutate"));
|
|
112204
|
+
return;
|
|
112205
|
+
}
|
|
112206
|
+
if (plan2 === true) {
|
|
112207
|
+
try {
|
|
112208
|
+
const report2 = await planJourneyVerification({ dir: verifyJourneysDir, id, params: param });
|
|
112209
|
+
if (json2) emitJson(program2, ok(withEngine(report2)));
|
|
112210
|
+
else {
|
|
112211
|
+
const out = program2.configureOutput().writeOut;
|
|
112212
|
+
for (const mutation2 of report2.mutations) {
|
|
112213
|
+
const sites = report2.pairs.filter((pair) => pair.mutation === mutation2.id).map((pair) => pair.site);
|
|
112214
|
+
out?.(`${mutation2.id} (step ${mutation2.step}) -> ${sites.length === 0 ? "unpaired" : sites.join(", ")}
|
|
112215
|
+
`);
|
|
112216
|
+
}
|
|
112217
|
+
for (const assertion of report2.assertions) if (assertion.mutations.length === 0) out?.(`unpaired ${assertion.site} ${assertion.check}
|
|
112218
|
+
`);
|
|
112219
|
+
out?.(
|
|
112220
|
+
`planned \u2014 journey '${report2.journeyId}': ${report2.summary.mutations} mutations, ${report2.summary.pairs} pairs, ${report2.summary.assertions} assertions, ${report2.summary.unpaired} unpaired, at least ${report2.summary.minimumReplays} replays
|
|
112221
|
+
`
|
|
112222
|
+
);
|
|
112223
|
+
}
|
|
112224
|
+
} catch (err) {
|
|
112225
|
+
if (err instanceof UnknownJourneyError) emitJson(program2, fail("E_UNKNOWN_JOURNEY", String(err.message)));
|
|
112226
|
+
else throw err;
|
|
112227
|
+
}
|
|
111782
112228
|
return;
|
|
111783
112229
|
}
|
|
111784
112230
|
let environment;
|
|
@@ -111920,7 +112366,7 @@ function registerJourneyAnchors(journey, program2, deps) {
|
|
|
111920
112366
|
|
|
111921
112367
|
// src/source-api.ts
|
|
111922
112368
|
import { execFile as execFile6 } from "node:child_process";
|
|
111923
|
-
import { mkdir as
|
|
112369
|
+
import { mkdir as mkdir32 } from "node:fs/promises";
|
|
111924
112370
|
import { promisify as promisify5 } from "node:util";
|
|
111925
112371
|
var execFileAsync5 = promisify5(execFile6);
|
|
111926
112372
|
var NotPromotedError = class extends Error {
|
|
@@ -111958,7 +112404,7 @@ async function requireEntry(lockPath, name) {
|
|
|
111958
112404
|
}
|
|
111959
112405
|
async function addSource(deps, req) {
|
|
111960
112406
|
const mgr = mgrOf(deps);
|
|
111961
|
-
await
|
|
112407
|
+
await mkdir32(deps.sourcesDir, { recursive: true });
|
|
111962
112408
|
const pinnedCommit = await mgr.add(req.name, req.gitUrl);
|
|
111963
112409
|
const manifest = await loadManifest(mgr.resolveDir(req.name));
|
|
111964
112410
|
const touSurface = surfaceForAck(manifest, req.gitUrl);
|
|
@@ -112375,7 +112821,7 @@ review or edit it, then apply it: jevitate journey annotate ${id} --approve
|
|
|
112375
112821
|
|
|
112376
112822
|
// src/journey-demo-cli.ts
|
|
112377
112823
|
import { existsSync as existsSync34 } from "node:fs";
|
|
112378
|
-
import { join as
|
|
112824
|
+
import { join as join67, resolve as resolvePath15 } from "node:path";
|
|
112379
112825
|
function registerJourneyDemoCommand(journey, program2, deps) {
|
|
112380
112826
|
withScreenshotsFlag(withEnvironmentFlags(withDemoFlags(withBrowserLaunchFlags(withEmulationFlags(withFixtureFlags(withProjectFlags(journey.command("demo <id>")))))))).description(
|
|
112381
112827
|
"replay a Journey as a narrated demo (goal, step objectives as captions, target highlights) \u2192 a WebM video with .vtt subtitles and/or a Markdown step-by-step guide with screenshots; a Journey that no longer replays fails (exit 1)"
|
|
@@ -112418,9 +112864,9 @@ function registerJourneyDemoCommand(journey, program2, deps) {
|
|
|
112418
112864
|
let video = videoFlag === void 0 ? void 0 : resolvePath15(videoFlag);
|
|
112419
112865
|
let guide = guideFlag === void 0 ? void 0 : resolvePath15(guideFlag);
|
|
112420
112866
|
if (video === void 0 && guide === void 0) {
|
|
112421
|
-
const folder =
|
|
112422
|
-
video =
|
|
112423
|
-
guide =
|
|
112867
|
+
const folder = join67(logsDirFor(), `journey-demo-${id.replace(/[^A-Za-z0-9._-]/g, "_")}-${artifactStamp(clock.nowIso())}`);
|
|
112868
|
+
video = join67(folder, "demo.webm");
|
|
112869
|
+
guide = join67(folder, "guide.md");
|
|
112424
112870
|
}
|
|
112425
112871
|
try {
|
|
112426
112872
|
const result = await withSiteGate(
|
|
@@ -112513,7 +112959,7 @@ function registerJourneyCommands(program2, deps) {
|
|
|
112513
112959
|
}
|
|
112514
112960
|
|
|
112515
112961
|
// src/project-where.ts
|
|
112516
|
-
import { join as
|
|
112962
|
+
import { join as join68 } from "node:path";
|
|
112517
112963
|
function render(data) {
|
|
112518
112964
|
const dir = (p) => p ?? "(none)";
|
|
112519
112965
|
return [
|
|
@@ -112530,7 +112976,7 @@ function registerWhere(program2, deps) {
|
|
|
112530
112976
|
const { project, journeysDir, json: json2 } = this.opts();
|
|
112531
112977
|
const layout = journeyLayout(program2, deps, { project, journeysDir });
|
|
112532
112978
|
if (layout === null) return;
|
|
112533
|
-
const logsDir = layout.projectDir === null ? logsRoot() :
|
|
112979
|
+
const logsDir = layout.projectDir === null ? logsRoot() : join68(layout.projectDir, "logs");
|
|
112534
112980
|
const data = { ...layout, logsDir };
|
|
112535
112981
|
if (json2 === true) {
|
|
112536
112982
|
emitJson(program2, ok(data));
|
|
@@ -113348,7 +113794,7 @@ function resolveSecretArgs(values2, env, flag) {
|
|
|
113348
113794
|
}
|
|
113349
113795
|
|
|
113350
113796
|
// src/multi-run-cli.ts
|
|
113351
|
-
import { join as
|
|
113797
|
+
import { join as join69 } from "node:path";
|
|
113352
113798
|
var MULTI_RUN_ATTRS = /* @__PURE__ */ new Set(["repeat", "minAgreement", "persona", "personas", "out", "json"]);
|
|
113353
113799
|
function forwardedArgv(cmd, omit3 = /* @__PURE__ */ new Set()) {
|
|
113354
113800
|
const opts = cmd.opts();
|
|
@@ -113396,7 +113842,7 @@ function lastEnvelope(lines) {
|
|
|
113396
113842
|
}
|
|
113397
113843
|
async function runExploreMultiRun(args) {
|
|
113398
113844
|
const { cmd, plan: plan2 } = args;
|
|
113399
|
-
const outDir = args.out ??
|
|
113845
|
+
const outDir = args.out ?? join69(logsDirFor((args.nowIso ?? (() => clock.nowIso()))()), `multi-${artifactStamp((args.nowIso ?? (() => clock.nowIso()))())}`);
|
|
113400
113846
|
const omit3 = /* @__PURE__ */ new Set([...plan2.personas === null ? [] : ["storageState"], ...args.authPreflight === void 0 ? [] : ["authCheck"]]);
|
|
113401
113847
|
const base = [...forwardedArgv(cmd, omit3), ...args.authPreflight === void 0 ? [] : ["--auth-check", "off"]];
|
|
113402
113848
|
return runMultiRun({
|
|
@@ -113435,9 +113881,9 @@ import { resolve as resolvePath16 } from "node:path";
|
|
|
113435
113881
|
|
|
113436
113882
|
// src/campaign-api.ts
|
|
113437
113883
|
import { existsSync as existsSync37, readFileSync as readFileSync36 } from "node:fs";
|
|
113438
|
-
import { mkdir as
|
|
113884
|
+
import { mkdir as mkdir33, writeFile as writeFile41 } from "node:fs/promises";
|
|
113439
113885
|
import { homedir as homedir5 } from "node:os";
|
|
113440
|
-
import { dirname as dirname29, isAbsolute as isAbsolute11, join as
|
|
113886
|
+
import { dirname as dirname29, isAbsolute as isAbsolute11, join as join70, resolve as resolve21 } from "node:path";
|
|
113441
113887
|
|
|
113442
113888
|
// src/campaign-preflight.ts
|
|
113443
113889
|
import { basename as basename17 } from "node:path";
|
|
@@ -113562,7 +114008,7 @@ async function validateCampaignSpec(raw, specPath, opts) {
|
|
|
113562
114008
|
if (!parsed.success) throw new CampaignSpecError(parsed.error.issues.map((i) => `${at2(i.path)}: ${i.message}`));
|
|
113563
114009
|
const spec = parsed.data;
|
|
113564
114010
|
const base = dirname29(resolve21(specPath));
|
|
113565
|
-
const abs = (p) => p.startsWith("~/") ?
|
|
114011
|
+
const abs = (p) => p.startsWith("~/") ? join70(homedir5(), p.slice(2)) : isAbsolute11(p) ? p : resolve21(base, p);
|
|
113566
114012
|
const problems = [];
|
|
113567
114013
|
let environment;
|
|
113568
114014
|
try {
|
|
@@ -113748,8 +114194,8 @@ function commonArgs(plan2, job) {
|
|
|
113748
114194
|
async function runCampaign(plan2, opts) {
|
|
113749
114195
|
const now2 = opts.nowIso ?? (() => clock.nowIso());
|
|
113750
114196
|
const startedAt = now2();
|
|
113751
|
-
const outDir = opts.outDir ??
|
|
113752
|
-
await
|
|
114197
|
+
const outDir = opts.outDir ?? join70(logsDirFor(startedAt), `campaign-${artifactStamp(startedAt)}`);
|
|
114198
|
+
await mkdir33(outDir, { recursive: true });
|
|
113753
114199
|
const ai = opts.real === true ? ["--real"] : opts.fakeAi === true ? ["--fake-ai"] : [];
|
|
113754
114200
|
const discovery = [];
|
|
113755
114201
|
const replaySlowness = [];
|
|
@@ -113807,8 +114253,8 @@ async function runCampaign(plan2, opts) {
|
|
|
113807
114253
|
}
|
|
113808
114254
|
}
|
|
113809
114255
|
const preflight = opts.preflight === void 0 ? void 0 : opts.preflight === "skip" ? { ok: true, problems: [], skipped: true } : preflightOf(preflightProblems);
|
|
113810
|
-
const resultPath =
|
|
113811
|
-
const reportPath =
|
|
114256
|
+
const resultPath = join70(outDir, "campaign.json");
|
|
114257
|
+
const reportPath = join70(outDir, "campaign.md");
|
|
113812
114258
|
if (preflight !== void 0 && !preflight.ok) {
|
|
113813
114259
|
const refused = {
|
|
113814
114260
|
...plan2.name === void 0 ? {} : { name: plan2.name },
|
|
@@ -113821,7 +114267,7 @@ async function runCampaign(plan2, opts) {
|
|
|
113821
114267
|
missions: [],
|
|
113822
114268
|
resultPath
|
|
113823
114269
|
};
|
|
113824
|
-
await
|
|
114270
|
+
await writeFile41(resultPath, `${JSON.stringify(refused, null, 2)}
|
|
113825
114271
|
`, "utf8");
|
|
113826
114272
|
throw new CampaignPreflightError(preflight.problems, resultPath);
|
|
113827
114273
|
}
|
|
@@ -113842,7 +114288,7 @@ async function runCampaign(plan2, opts) {
|
|
|
113842
114288
|
});
|
|
113843
114289
|
continue;
|
|
113844
114290
|
}
|
|
113845
|
-
const runDir =
|
|
114291
|
+
const runDir = join70(outDir, job.id, `${anchor2.atStep}-${strategy}`);
|
|
113846
114292
|
const argv = [
|
|
113847
114293
|
"explore",
|
|
113848
114294
|
"--from-journey",
|
|
@@ -113934,9 +114380,9 @@ async function runCampaign(plan2, opts) {
|
|
|
113934
114380
|
missionOutcome,
|
|
113935
114381
|
exitCode: exitCodeForOutcome(missionOutcome)
|
|
113936
114382
|
};
|
|
113937
|
-
await
|
|
114383
|
+
await writeFile41(resultPath, `${JSON.stringify(result, null, 2)}
|
|
113938
114384
|
`, "utf8");
|
|
113939
|
-
await
|
|
114385
|
+
await writeFile41(reportPath, campaignMarkdown(result, report2.markdown), "utf8");
|
|
113940
114386
|
return result;
|
|
113941
114387
|
}
|
|
113942
114388
|
function campaignMarkdown(r, reportMarkdown) {
|
|
@@ -114101,7 +114547,7 @@ var GOAL_RUN_OPTIONS = {
|
|
|
114101
114547
|
),
|
|
114102
114548
|
jobWaitMs: () => new Option(
|
|
114103
114549
|
"--job-wait-ms <ms>",
|
|
114104
|
-
`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`
|
|
114550
|
+
`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) or declares (aria-busy, when this flag is set) before it is a hang, and a wait the page documents ("usually takes a minute") can raise it`
|
|
114105
114551
|
).argParser(positiveIntArg),
|
|
114106
114552
|
deny: () => new Option(
|
|
114107
114553
|
"--deny <pattern>",
|
|
@@ -114137,7 +114583,7 @@ var GOAL_RUN_OPTIONS = {
|
|
|
114137
114583
|
settleIgnore: () => new Option("--settle-ignore <pattern>", "a request URL pattern the target marks as background (never pending work; repeatable, * wildcard)").default([]).argParser(collect7),
|
|
114138
114584
|
longPollMs: () => new Option("--long-poll-ms <n>", "a request pending this long on an interactive page is a long-poll (default 5000)").argParser(nonNegativeIntArg),
|
|
114139
114585
|
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(collect7),
|
|
114140
|
-
ignoreNoProgress: () => new Option("--ignore-no-progress <pattern>", "a route / action label / busy indicator where ui-no-progress is expected (repeatable, * wildcard)").default([]).argParser(collect7),
|
|
114586
|
+
ignoreNoProgress: () => new Option("--ignore-no-progress <pattern>", "a route / action label / busy indicator (its description or visible words) where ui-no-progress is expected (repeatable, * wildcard)").default([]).argParser(collect7),
|
|
114141
114587
|
allowSecretCmd: () => new Option(
|
|
114142
114588
|
"--allow-secret-cmd",
|
|
114143
114589
|
"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)"
|
|
@@ -115662,11 +116108,11 @@ import { resolve as resolve24 } from "node:path";
|
|
|
115662
116108
|
|
|
115663
116109
|
// src/sweep-api.ts
|
|
115664
116110
|
import { existsSync as existsSync40, mkdirSync as mkdirSync11, readFileSync as readFileSync37, realpathSync as realpathSync5, statSync as statSync12, writeFileSync as writeFileSync13 } from "node:fs";
|
|
115665
|
-
import { basename as basename19, dirname as dirname31, extname as extname2, join as
|
|
116111
|
+
import { basename as basename19, dirname as dirname31, extname as extname2, join as join72, resolve as resolve23 } from "node:path";
|
|
115666
116112
|
|
|
115667
116113
|
// src/mcp-paths.ts
|
|
115668
116114
|
import { existsSync as existsSync39, realpathSync as realpathSync4 } from "node:fs";
|
|
115669
|
-
import { basename as basename18, dirname as dirname30, isAbsolute as isAbsolute12, join as
|
|
116115
|
+
import { basename as basename18, dirname as dirname30, isAbsolute as isAbsolute12, join as join71, relative as relative10, resolve as resolve22, sep as sep10 } from "node:path";
|
|
115670
116116
|
var McpPathError = class extends Error {
|
|
115671
116117
|
error = "invalid_args";
|
|
115672
116118
|
constructor(message2) {
|
|
@@ -115685,7 +116131,7 @@ function realish(p) {
|
|
|
115685
116131
|
for (; ; ) {
|
|
115686
116132
|
if (existsSync39(head2)) {
|
|
115687
116133
|
try {
|
|
115688
|
-
return
|
|
116134
|
+
return join71(realpathSync4(head2), ...tail);
|
|
115689
116135
|
} catch {
|
|
115690
116136
|
return p;
|
|
115691
116137
|
}
|
|
@@ -116045,13 +116491,14 @@ var CLI_TOOL_SPECS = [
|
|
|
116045
116491
|
},
|
|
116046
116492
|
{
|
|
116047
116493
|
name: "verify_journey",
|
|
116048
|
-
description: "`jevitate journey verify <id> --mutate` (#402):
|
|
116494
|
+
description: "`jevitate journey verify <id> --plan|--mutate` (#402): plan previews the exact mutations, assertion pairings, unpaired assertions and minimum replay count without opening a browser; mutate proves each assertion can fail by replaying the Journey once, then once per mutation. Mutations only skip steps, abort the app's own writes or alter checked non-secret inputs; nothing is fabricated. Set exactly one of plan or mutate.",
|
|
116049
116495
|
command: {
|
|
116050
116496
|
path: "journey verify",
|
|
116051
116497
|
params: {
|
|
116052
116498
|
id: pos(),
|
|
116053
116499
|
...EXTENSION,
|
|
116054
116500
|
mutate: b("--mutate"),
|
|
116501
|
+
plan: b("--plan"),
|
|
116055
116502
|
params: { kind: "params", flag: "--param" },
|
|
116056
116503
|
storageState: session("--storage-state"),
|
|
116057
116504
|
fixtures: path("--fixtures"),
|
|
@@ -117510,7 +117957,7 @@ function aggregateSweep(args) {
|
|
|
117510
117957
|
kind: "sweep",
|
|
117511
117958
|
targetsPath: plan2.targetsPath,
|
|
117512
117959
|
outDir: plan2.outDir,
|
|
117513
|
-
resultPath:
|
|
117960
|
+
resultPath: join72(plan2.outDir, "sweep.result.json"),
|
|
117514
117961
|
startedAt: args.startedAt,
|
|
117515
117962
|
...args.finishedAt === void 0 ? {} : { finishedAt: args.finishedAt },
|
|
117516
117963
|
complete: args.complete && args.aborted === void 0,
|
|
@@ -117551,19 +117998,19 @@ async function awaitLowHostLoad(probe = hostProbe(), waitMs = HOST_STARVED_WAIT_
|
|
|
117551
117998
|
}
|
|
117552
117999
|
}
|
|
117553
118000
|
function defaultSweepOutDir(iso = clock.nowIso()) {
|
|
117554
|
-
return
|
|
118001
|
+
return join72(logsDirFor(iso), `sweep-${artifactStamp(iso)}`);
|
|
117555
118002
|
}
|
|
117556
118003
|
async function runSweep(opts) {
|
|
117557
118004
|
const { plan: plan2 } = opts;
|
|
117558
118005
|
const now2 = opts.nowIso ?? (() => clock.nowIso());
|
|
117559
118006
|
const startedAt = now2();
|
|
117560
118007
|
mkdirSync11(plan2.outDir, { recursive: true });
|
|
117561
|
-
const resultPath =
|
|
118008
|
+
const resultPath = join72(plan2.outDir, "sweep.result.json");
|
|
117562
118009
|
const rows = plan2.targets.map(() => void 0);
|
|
117563
118010
|
const finished = [];
|
|
117564
118011
|
const queue = [];
|
|
117565
118012
|
plan2.targets.forEach((t, i) => {
|
|
117566
|
-
const envelopePath =
|
|
118013
|
+
const envelopePath = join72(plan2.outDir, t.id, "run.envelope.json");
|
|
117567
118014
|
const prior = plan2.resume ? finishedEnvelope(envelopePath) : void 0;
|
|
117568
118015
|
if (prior === void 0) {
|
|
117569
118016
|
queue.push(i);
|
|
@@ -117585,9 +118032,9 @@ async function runSweep(opts) {
|
|
|
117585
118032
|
});
|
|
117586
118033
|
const runOne = async (i) => {
|
|
117587
118034
|
const target = plan2.targets[i];
|
|
117588
|
-
const runDir =
|
|
118035
|
+
const runDir = join72(plan2.outDir, target.id);
|
|
117589
118036
|
mkdirSync11(runDir, { recursive: true });
|
|
117590
|
-
const envelopePath =
|
|
118037
|
+
const envelopePath = join72(runDir, "run.envelope.json");
|
|
117591
118038
|
const attempt2 = async () => {
|
|
117592
118039
|
try {
|
|
117593
118040
|
return await opts.runOnce({ target, argv: targetArgv(plan2, target, runDir), runDir });
|
|
@@ -118063,13 +118510,13 @@ function registerAuthorJourneyCommands(program2, deps) {
|
|
|
118063
118510
|
|
|
118064
118511
|
// src/demo-aspect-cli.ts
|
|
118065
118512
|
import { existsSync as existsSync43 } from "node:fs";
|
|
118066
|
-
import { join as
|
|
118513
|
+
import { join as join75, resolve as resolvePath18 } from "node:path";
|
|
118067
118514
|
|
|
118068
118515
|
// src/demo-aspect-api.ts
|
|
118069
118516
|
import { existsSync as existsSync42 } from "node:fs";
|
|
118070
|
-
import { mkdir as
|
|
118517
|
+
import { mkdir as mkdir35, mkdtemp as mkdtemp5, readFile as readFile37, rm as rm13, writeFile as writeFile43 } from "node:fs/promises";
|
|
118071
118518
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
118072
|
-
import { dirname as dirname32, join as
|
|
118519
|
+
import { dirname as dirname32, join as join74 } from "node:path";
|
|
118073
118520
|
|
|
118074
118521
|
// ../regression/dist/fingerprint.js
|
|
118075
118522
|
function flattenWithUrls(rec) {
|
|
@@ -118214,8 +118661,8 @@ async function minimizeRecording(recording, reproduces) {
|
|
|
118214
118661
|
}
|
|
118215
118662
|
|
|
118216
118663
|
// ../regression/dist/commit.js
|
|
118217
|
-
import { join as
|
|
118218
|
-
import { mkdir as
|
|
118664
|
+
import { join as join73 } from "node:path";
|
|
118665
|
+
import { mkdir as mkdir34, writeFile as writeFile42 } from "node:fs/promises";
|
|
118219
118666
|
var FlakyNotCommittableError = class extends Error {
|
|
118220
118667
|
};
|
|
118221
118668
|
async function commitRegression(dir, id, minimized, report2, bugSummary, oracle) {
|
|
@@ -118223,9 +118670,9 @@ async function commitRegression(dir, id, minimized, report2, bugSummary, oracle)
|
|
|
118223
118670
|
throw new FlakyNotCommittableError(`refusing to commit '${id}': reproduction rate ${report2.rate} (${report2.reproducedCount}/${report2.attempts}) is flaky, not reproducible`);
|
|
118224
118671
|
}
|
|
118225
118672
|
const validated = RecordingSchema.parse(minimized);
|
|
118226
|
-
await
|
|
118227
|
-
const recordingPath =
|
|
118228
|
-
const metaPath =
|
|
118673
|
+
await mkdir34(dir, { recursive: true });
|
|
118674
|
+
const recordingPath = join73(dir, `${id}.recording.json`);
|
|
118675
|
+
const metaPath = join73(dir, `${id}.meta.json`);
|
|
118229
118676
|
const meta3 = {
|
|
118230
118677
|
id,
|
|
118231
118678
|
capturedAtIso: clock.nowIso(),
|
|
@@ -118234,8 +118681,8 @@ async function commitRegression(dir, id, minimized, report2, bugSummary, oracle)
|
|
|
118234
118681
|
bugSummary,
|
|
118235
118682
|
...oracle === void 0 ? {} : { oracle }
|
|
118236
118683
|
};
|
|
118237
|
-
await
|
|
118238
|
-
await
|
|
118684
|
+
await writeFile42(recordingPath, JSON.stringify(validated, null, 2) + "\n");
|
|
118685
|
+
await writeFile42(metaPath, JSON.stringify(meta3, null, 2) + "\n");
|
|
118239
118686
|
return { recordingPath, metaPath };
|
|
118240
118687
|
}
|
|
118241
118688
|
|
|
@@ -118308,7 +118755,7 @@ function demoJourneyId(aspect) {
|
|
|
118308
118755
|
}
|
|
118309
118756
|
var DEMO_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,99}$/;
|
|
118310
118757
|
function demoDraftPath(journeysDir, id) {
|
|
118311
|
-
return
|
|
118758
|
+
return join74(journeysDir, ".drafts", `${id}.demo.json`);
|
|
118312
118759
|
}
|
|
118313
118760
|
function demoDraftProblem(v) {
|
|
118314
118761
|
if (v === null || typeof v !== "object" || Array.isArray(v)) return "not an object";
|
|
@@ -118329,7 +118776,7 @@ async function readDemoDraft(journeysDir, id) {
|
|
|
118329
118776
|
const path2 = demoDraftPath(journeysDir, id);
|
|
118330
118777
|
let raw;
|
|
118331
118778
|
try {
|
|
118332
|
-
raw = await
|
|
118779
|
+
raw = await readFile37(path2, "utf8");
|
|
118333
118780
|
} catch (err) {
|
|
118334
118781
|
if (err.code === "ENOENT") throw new DemoDraftNotFoundError(`no demo draft '${id}' (${path2}) \u2014 make one with \`jevitate demo "<aspect>" --env <name>\``);
|
|
118335
118782
|
throw err;
|
|
@@ -118409,16 +118856,16 @@ async function demoAspect(opts) {
|
|
|
118409
118856
|
const maxReplays = opts.maxReplays ?? DEMO_MINIMIZE_MAX_REPLAYS;
|
|
118410
118857
|
const now2 = opts.now ?? (() => clock.nowIso());
|
|
118411
118858
|
const base = { id, aspect, environment: env.name ?? env.baseUrl };
|
|
118412
|
-
const work = await mkdtemp5(
|
|
118859
|
+
const work = await mkdtemp5(join74(tmpdir5(), "jevitate-demo-aspect-"));
|
|
118413
118860
|
try {
|
|
118414
118861
|
const authored = await runAuthorJourney({
|
|
118415
118862
|
url: new URL(start, env.baseUrl).toString(),
|
|
118416
118863
|
goal: aspect,
|
|
118417
118864
|
successAssertion: opts.successAssertion,
|
|
118418
118865
|
allowlist: env.allowedOrigins,
|
|
118419
|
-
journeysDir:
|
|
118866
|
+
journeysDir: join74(work, "explored"),
|
|
118420
118867
|
// #369: the discovery take's own artifacts stay in the scratch dir with the rest.
|
|
118421
|
-
outDir:
|
|
118868
|
+
outDir: join74(work, "explored-runs"),
|
|
118422
118869
|
journeyId: id,
|
|
118423
118870
|
journeyName: aspect,
|
|
118424
118871
|
takes: 1,
|
|
@@ -118439,10 +118886,10 @@ async function demoAspect(opts) {
|
|
|
118439
118886
|
...opts.persona === void 0 ? {} : { persona: opts.persona },
|
|
118440
118887
|
successCriteria: [{ description: `The success check holds: ${opts.successSpec}`, check: opts.successAssertion }]
|
|
118441
118888
|
};
|
|
118442
|
-
const scratch = new FsJourneyStore(
|
|
118889
|
+
const scratch = new FsJourneyStore(join74(work, "candidates"));
|
|
118443
118890
|
const replayOk = async (recording2) => {
|
|
118444
118891
|
await scratch.put({ metadata, recording: withSuccessAssert(recording2, opts.successAssertion) });
|
|
118445
|
-
const run = await runJourneyProgrammatically({ dir:
|
|
118892
|
+
const run = await runJourneyProgrammatically({ dir: join74(work, "candidates"), id, params: {}, ...replayOpts(opts) });
|
|
118446
118893
|
return run.outcome === "ok";
|
|
118447
118894
|
};
|
|
118448
118895
|
const explored = withoutSuccessAssert(authored.journey.recording, opts.successAssertion);
|
|
@@ -118473,7 +118920,7 @@ async function demoAspect(opts) {
|
|
|
118473
118920
|
};
|
|
118474
118921
|
const recording = withSuccessAssert(minimized, opts.successAssertion);
|
|
118475
118922
|
const journey = { metadata: { ...metadata, params: deriveParamSchema(recording).required, createdAtIso: now2() }, recording };
|
|
118476
|
-
const stageDir =
|
|
118923
|
+
const stageDir = join74(work, "stage");
|
|
118477
118924
|
await new FsJourneyStore(stageDir).put(journey);
|
|
118478
118925
|
const annotated = await annotateJourney({ dir: stageDir, id, params: {}, gen: opts.gen, now: now2, ...replayOpts(opts) });
|
|
118479
118926
|
if (annotated.replay.outcome !== "completed") {
|
|
@@ -118486,8 +118933,8 @@ async function demoAspect(opts) {
|
|
|
118486
118933
|
}
|
|
118487
118934
|
await store.put(journey);
|
|
118488
118935
|
const draftPath = annotationDraftPath(opts.journeysDir, id);
|
|
118489
|
-
await
|
|
118490
|
-
await
|
|
118936
|
+
await mkdir35(dirname32(draftPath), { recursive: true, mode: 448 });
|
|
118937
|
+
await writeFile43(draftPath, `${JSON.stringify(annotations, null, 2)}
|
|
118491
118938
|
`, { mode: 384 });
|
|
118492
118939
|
const record2 = {
|
|
118493
118940
|
kind: "jevitate.demo.draft",
|
|
@@ -118501,7 +118948,7 @@ async function demoAspect(opts) {
|
|
|
118501
118948
|
draft: outputsOf(demo)
|
|
118502
118949
|
};
|
|
118503
118950
|
assertNoSecretInPayload(record2, [], "demo draft record");
|
|
118504
|
-
await
|
|
118951
|
+
await writeFile43(recordPath, `${JSON.stringify(record2, null, 2)}
|
|
118505
118952
|
`, { mode: 384 });
|
|
118506
118953
|
return {
|
|
118507
118954
|
...base,
|
|
@@ -118530,8 +118977,8 @@ function renderDemo(dir, id, opts, annotations, draft, outDir, jzMask = false) {
|
|
|
118530
118977
|
dir,
|
|
118531
118978
|
id,
|
|
118532
118979
|
params: {},
|
|
118533
|
-
video:
|
|
118534
|
-
guide:
|
|
118980
|
+
video: join74(outDir, "demo.webm"),
|
|
118981
|
+
guide: join74(outDir, "guide.md"),
|
|
118535
118982
|
annotations,
|
|
118536
118983
|
draft,
|
|
118537
118984
|
environment: opts.environment,
|
|
@@ -118609,7 +119056,7 @@ async function approveDemo(opts) {
|
|
|
118609
119056
|
...title === "" ? {} : { title },
|
|
118610
119057
|
demo
|
|
118611
119058
|
});
|
|
118612
|
-
journeeze = { media: true, record:
|
|
119059
|
+
journeeze = { media: true, record: join74(opts.journeysDir, ".demos", opts.id), ...record2.leftOut.length === 0 ? {} : { leftOut: record2.leftOut } };
|
|
118613
119060
|
} else {
|
|
118614
119061
|
await clearApprovedDemo(opts.journeysDir, opts.id);
|
|
118615
119062
|
journeeze = { media: false, reason: `environment '${environment}' does not declare synthetic: true \u2014 no demo media is exported to Journeeze` };
|
|
@@ -118679,7 +119126,7 @@ function withReplayFlags(cmd) {
|
|
|
118679
119126
|
}
|
|
118680
119127
|
function outDirFor(flag, id, kind) {
|
|
118681
119128
|
if (flag !== void 0) return resolvePath18(flag);
|
|
118682
|
-
return
|
|
119129
|
+
return join75(logsDirFor(), `demo-${id}-${kind}-${artifactStamp(clock.nowIso())}`);
|
|
118683
119130
|
}
|
|
118684
119131
|
function stepLines2(steps) {
|
|
118685
119132
|
return steps.map((s3) => ` ${s3.number}. ${s3.objective ?? s3.step}${s3.expectedResult === void 0 ? "" : `
|
|
@@ -118864,8 +119311,8 @@ ${(j.leftOut ?? []).map((l2) => ` left out: ${l2}
|
|
|
118864
119311
|
}
|
|
118865
119312
|
|
|
118866
119313
|
// src/record-api.ts
|
|
118867
|
-
import { mkdir as
|
|
118868
|
-
import { join as
|
|
119314
|
+
import { mkdir as mkdir36, writeFile as writeFile44 } from "node:fs/promises";
|
|
119315
|
+
import { join as join76 } from "node:path";
|
|
118869
119316
|
async function runRecording(opts) {
|
|
118870
119317
|
const origin = assertAuthorizedExploreTarget(opts.url, opts.allowlist);
|
|
118871
119318
|
const portFactory = opts.browserPortFactory ?? (() => new PlaywrightBrowserPort());
|
|
@@ -118888,10 +119335,10 @@ async function runRecording(opts) {
|
|
|
118888
119335
|
const values2 = Object.fromEntries(authoring.values);
|
|
118889
119336
|
const take = AuthoringTakeSchema.parse({ recording, values: values2 });
|
|
118890
119337
|
const outDir = opts.outDir ?? logsDirFor();
|
|
118891
|
-
await
|
|
119338
|
+
await mkdir36(outDir, { recursive: true });
|
|
118892
119339
|
const iso = (opts.nowIso ?? (() => clock.nowIso()))();
|
|
118893
|
-
const recordingPath =
|
|
118894
|
-
await
|
|
119340
|
+
const recordingPath = join76(outDir, `record-${iso.replace(/[:.]/g, "-")}.json`);
|
|
119341
|
+
await writeFile44(recordingPath, `${JSON.stringify(take, null, 2)}
|
|
118895
119342
|
`, "utf8");
|
|
118896
119343
|
const steps = recording.pages.reduce((n3, p) => n3 + p.steps.length, 0);
|
|
118897
119344
|
return { recording, recordingPath, steps, pages: recording.pages.length, finalUrl };
|
|
@@ -119004,12 +119451,12 @@ function registerRecordCommands(program2, deps) {
|
|
|
119004
119451
|
|
|
119005
119452
|
// src/regression-cli.ts
|
|
119006
119453
|
import { existsSync as existsSync45 } from "node:fs";
|
|
119007
|
-
import { readFile as
|
|
119008
|
-
import { join as
|
|
119454
|
+
import { readFile as readFile39, writeFile as writeFile45 } from "node:fs/promises";
|
|
119455
|
+
import { join as join77 } from "node:path";
|
|
119009
119456
|
|
|
119010
119457
|
// src/regression-api.ts
|
|
119011
119458
|
import { existsSync as existsSync44 } from "node:fs";
|
|
119012
|
-
import { readFile as
|
|
119459
|
+
import { readFile as readFile38 } from "node:fs/promises";
|
|
119013
119460
|
var RegressionExistsError = class extends Error {
|
|
119014
119461
|
code = "E_REGRESSION_EXISTS";
|
|
119015
119462
|
constructor(id, dir) {
|
|
@@ -119035,9 +119482,9 @@ var NoOracleInResultError = class extends Error {
|
|
|
119035
119482
|
};
|
|
119036
119483
|
var RegressionHardSignalOracleError = class extends Error {
|
|
119037
119484
|
code = "E_REGRESSION_HARD_SIGNAL";
|
|
119038
|
-
constructor(resultPath,
|
|
119485
|
+
constructor(resultPath, finding4) {
|
|
119039
119486
|
super(
|
|
119040
|
-
`--fingerprint ${
|
|
119487
|
+
`--fingerprint ${finding4.fingerprint} is a ${finding4.kind} defect in ${resultPath} \u2014 a hard signal with its own oracle, which 'regression capture' cannot turn into a page/success-check regression. Keep its repro material with 'jevitate ledger add ${resultPath} ${finding4.fingerprint}', then re-check it with 'jevitate ledger verify' or 'jevitate verify-fix ${finding4.fingerprint}'.`
|
|
119041
119488
|
);
|
|
119042
119489
|
this.name = "RegressionHardSignalOracleError";
|
|
119043
119490
|
}
|
|
@@ -119099,14 +119546,14 @@ async function runRegressionCapture(opts) {
|
|
|
119099
119546
|
if (opts.force !== true && existsSync44(recordingPath)) {
|
|
119100
119547
|
throw new RegressionExistsError(opts.id, opts.regressionsDir);
|
|
119101
119548
|
}
|
|
119102
|
-
const raw = JSON.parse(await
|
|
119549
|
+
const raw = JSON.parse(await readFile38(opts.failingRecordingPath, "utf8"));
|
|
119103
119550
|
const parsed = RecordingSchema.parse(raw);
|
|
119104
119551
|
const recording = opts.anchored === true ? anchoredRecording(parsed) : parsed;
|
|
119105
119552
|
const attempts = opts.attempts ?? 3;
|
|
119106
119553
|
let missionResult;
|
|
119107
119554
|
let persistedMission;
|
|
119108
119555
|
if (opts.resultPath) {
|
|
119109
|
-
const rawResult = JSON.parse(await
|
|
119556
|
+
const rawResult = JSON.parse(await readFile38(opts.resultPath, "utf8"));
|
|
119110
119557
|
missionResult = rawResult;
|
|
119111
119558
|
try {
|
|
119112
119559
|
persistedMission = parsePersistedMission(rawResult);
|
|
@@ -119115,12 +119562,12 @@ async function runRegressionCapture(opts) {
|
|
|
119115
119562
|
}
|
|
119116
119563
|
}
|
|
119117
119564
|
if (opts.fingerprint !== void 0 && persistedMission !== void 0) {
|
|
119118
|
-
const
|
|
119119
|
-
if (
|
|
119120
|
-
return captureInvariantRegression(opts, recording, persistedMission,
|
|
119565
|
+
const finding4 = findFinding(persistedMission, opts.fingerprint);
|
|
119566
|
+
if (finding4 !== void 0 && finding4.kind === "invariant" && finding4.invariantId !== void 0 && persistedMission.invariantSpec !== void 0) {
|
|
119567
|
+
return captureInvariantRegression(opts, recording, persistedMission, finding4, attempts);
|
|
119121
119568
|
}
|
|
119122
|
-
if (
|
|
119123
|
-
throw new RegressionHardSignalOracleError(opts.resultPath,
|
|
119569
|
+
if (finding4 !== void 0 && finding4.kind !== "invariant") {
|
|
119570
|
+
throw new RegressionHardSignalOracleError(opts.resultPath, finding4);
|
|
119124
119571
|
}
|
|
119125
119572
|
}
|
|
119126
119573
|
let working = recording;
|
|
@@ -119199,16 +119646,16 @@ async function captureNetworkCheckRegression(opts, recording, check3, attempts)
|
|
|
119199
119646
|
};
|
|
119200
119647
|
return commitRegression(opts.regressionsDir, opts.id, recording, report2, opts.bugSummary, { kind: "network", check: check3 });
|
|
119201
119648
|
}
|
|
119202
|
-
async function captureInvariantRegression(opts, fallbackRecording, mission,
|
|
119649
|
+
async function captureInvariantRegression(opts, fallbackRecording, mission, finding4, attempts) {
|
|
119203
119650
|
const invariantSpec = mission.invariantSpec;
|
|
119204
|
-
const invariantId =
|
|
119205
|
-
const recording =
|
|
119651
|
+
const invariantId = finding4.invariantId;
|
|
119652
|
+
const recording = finding4.recording ?? mission.recording ?? fallbackRecording;
|
|
119206
119653
|
assertAuthorizedExploreTarget(mission.target.seedUrl, mission.target.allowlist);
|
|
119207
119654
|
const verifyResult = await verifyFix({
|
|
119208
119655
|
fixtureBaseDir: journeyFixtureBaseDir(),
|
|
119209
119656
|
recording,
|
|
119210
|
-
recordingStepIndex:
|
|
119211
|
-
fingerprint:
|
|
119657
|
+
recordingStepIndex: finding4.repro.recordingStepIndex,
|
|
119658
|
+
fingerprint: finding4.fingerprint,
|
|
119212
119659
|
defectKind: "invariant",
|
|
119213
119660
|
invariant: { spec: invariantSpec, id: invariantId, allowlist: mission.target.allowlist, baseUrl: mission.target.seedUrl },
|
|
119214
119661
|
replays: attempts,
|
|
@@ -119226,16 +119673,16 @@ async function captureInvariantRegression(opts, fallbackRecording, mission, find
|
|
|
119226
119673
|
rate: 1,
|
|
119227
119674
|
label: "reproducible",
|
|
119228
119675
|
fingerprint: { stepSignature: `invariant:${invariantId}` },
|
|
119229
|
-
firstFailureAt:
|
|
119676
|
+
firstFailureAt: finding4.repro.recordingStepIndex
|
|
119230
119677
|
};
|
|
119231
119678
|
return commitRegression(opts.regressionsDir, opts.id, recording, report2, opts.bugSummary, {
|
|
119232
119679
|
kind: "invariant",
|
|
119233
119680
|
invariantId,
|
|
119234
119681
|
invariantSpec,
|
|
119235
|
-
recordingStepIndex:
|
|
119682
|
+
recordingStepIndex: finding4.repro.recordingStepIndex,
|
|
119236
119683
|
allowlist: mission.target.allowlist,
|
|
119237
119684
|
baseUrl: mission.target.seedUrl,
|
|
119238
|
-
defectFingerprint:
|
|
119685
|
+
defectFingerprint: finding4.fingerprint
|
|
119239
119686
|
});
|
|
119240
119687
|
}
|
|
119241
119688
|
if (verifyResult.verdict === "intermittent") {
|
|
@@ -119258,8 +119705,8 @@ async function runRegressionRun(opts) {
|
|
|
119258
119705
|
let recording;
|
|
119259
119706
|
let meta3;
|
|
119260
119707
|
try {
|
|
119261
|
-
recording = RecordingSchema.parse(JSON.parse(await
|
|
119262
|
-
meta3 = JSON.parse(await
|
|
119708
|
+
recording = RecordingSchema.parse(JSON.parse(await readFile38(recordingPath, "utf8")));
|
|
119709
|
+
meta3 = JSON.parse(await readFile38(metaPath, "utf8"));
|
|
119263
119710
|
} catch {
|
|
119264
119711
|
throw new RegressionNotFoundError(opts.id, opts.regressionsDir);
|
|
119265
119712
|
}
|
|
@@ -119357,17 +119804,17 @@ function registerRegressionCommands(program2, deps) {
|
|
|
119357
119804
|
const opened = [];
|
|
119358
119805
|
let fx;
|
|
119359
119806
|
try {
|
|
119360
|
-
const raw = JSON.parse(await
|
|
119807
|
+
const raw = JSON.parse(await readFile39(from, "utf8"));
|
|
119361
119808
|
const recording = RecordingSchema.parse(raw);
|
|
119362
119809
|
const captureEmulation = captureEmulationFlag ?? (recording.emulation === void 0 ? void 0 : recording.emulation.device !== void 0 ? { device: recording.emulation.device } : { viewport: recording.emulation.viewport });
|
|
119363
119810
|
fx = regressionFixtures(
|
|
119364
119811
|
flags,
|
|
119365
119812
|
recording,
|
|
119366
|
-
resultPath === void 0 ? void 0 : JSON.parse(await
|
|
119813
|
+
resultPath === void 0 ? void 0 : JSON.parse(await readFile39(resultPath, "utf8")),
|
|
119367
119814
|
storageState
|
|
119368
119815
|
);
|
|
119369
119816
|
const replayFixture = fx;
|
|
119370
|
-
const branch = resultPath === void 0 ? void 0 : recordedBranchOf(JSON.parse(await
|
|
119817
|
+
const branch = resultPath === void 0 ? void 0 : recordedBranchOf(JSON.parse(await readFile39(resultPath, "utf8")).result);
|
|
119371
119818
|
const prefix = branch === void 0 ? void 0 : await branchPrefixOrRefuse(branch, param, storageState);
|
|
119372
119819
|
if (prefix === null) return;
|
|
119373
119820
|
const result = await runRegressionCapture({
|
|
@@ -119393,8 +119840,8 @@ function registerRegressionCommands(program2, deps) {
|
|
|
119393
119840
|
}).then((r) => withEngine(r));
|
|
119394
119841
|
await fx?.restore();
|
|
119395
119842
|
if (branch !== void 0 && "metaPath" in result) {
|
|
119396
|
-
const meta3 = JSON.parse(await
|
|
119397
|
-
await
|
|
119843
|
+
const meta3 = JSON.parse(await readFile39(result.metaPath, "utf8"));
|
|
119844
|
+
await writeFile45(result.metaPath, `${JSON.stringify({ ...meta3, branch }, null, 2)}
|
|
119398
119845
|
`);
|
|
119399
119846
|
}
|
|
119400
119847
|
const envelope = ok(fx === void 0 ? result : { ...result, fixtures: fx.record() });
|
|
@@ -119453,17 +119900,17 @@ function registerRegressionCommands(program2, deps) {
|
|
|
119453
119900
|
}
|
|
119454
119901
|
if (refuseUnsafeName(program2, id, "regression id")) return;
|
|
119455
119902
|
const regressionsDir = resolveRegressionsDir(dir);
|
|
119456
|
-
if (!existsSync45(
|
|
119903
|
+
if (!existsSync45(join77(regressionsDir, `${id}.recording.json`))) {
|
|
119457
119904
|
const notFound = new RegressionNotFoundError(id, regressionsDir);
|
|
119458
119905
|
emitJson(program2, fail(notFound.code, notFound.message));
|
|
119459
119906
|
return;
|
|
119460
119907
|
}
|
|
119461
119908
|
const opened = [];
|
|
119462
119909
|
try {
|
|
119463
|
-
const recording = RecordingSchema.parse(JSON.parse(await
|
|
119910
|
+
const recording = RecordingSchema.parse(JSON.parse(await readFile39(join77(regressionsDir, `${id}.recording.json`), "utf8")));
|
|
119464
119911
|
let branch;
|
|
119465
119912
|
try {
|
|
119466
|
-
branch = recordedBranchOf(JSON.parse(await
|
|
119913
|
+
branch = recordedBranchOf(JSON.parse(await readFile39(join77(regressionsDir, `${id}.meta.json`), "utf8")));
|
|
119467
119914
|
} catch {
|
|
119468
119915
|
branch = void 0;
|
|
119469
119916
|
}
|
|
@@ -119737,9 +120184,9 @@ async function queueExploration(targets, queue, args) {
|
|
|
119737
120184
|
}
|
|
119738
120185
|
|
|
119739
120186
|
// ../inbox/dist/atomic.js
|
|
119740
|
-
import { open as open4, rename as
|
|
120187
|
+
import { open as open4, rename as rename7, rm as rm14 } from "node:fs/promises";
|
|
119741
120188
|
import { randomBytes as randomBytes6 } from "node:crypto";
|
|
119742
|
-
import { dirname as dirname33, join as
|
|
120189
|
+
import { dirname as dirname33, join as join78 } from "node:path";
|
|
119743
120190
|
|
|
119744
120191
|
// ../inbox/dist/durability.js
|
|
119745
120192
|
function durableWritesEnabled(env = process.env) {
|
|
@@ -119752,7 +120199,7 @@ async function flushIfDurable(handle) {
|
|
|
119752
120199
|
|
|
119753
120200
|
// ../inbox/dist/atomic.js
|
|
119754
120201
|
async function writeFileAtomic(path2, data, mode = 384) {
|
|
119755
|
-
const tmp =
|
|
120202
|
+
const tmp = join78(dirname33(path2), `.${randomBytes6(8).toString("hex")}.tmp`);
|
|
119756
120203
|
try {
|
|
119757
120204
|
const handle = await open4(tmp, "wx", mode);
|
|
119758
120205
|
try {
|
|
@@ -119761,7 +120208,7 @@ async function writeFileAtomic(path2, data, mode = 384) {
|
|
|
119761
120208
|
} finally {
|
|
119762
120209
|
await handle.close();
|
|
119763
120210
|
}
|
|
119764
|
-
await
|
|
120211
|
+
await rename7(tmp, path2);
|
|
119765
120212
|
} catch (err) {
|
|
119766
120213
|
await rm14(tmp, { force: true });
|
|
119767
120214
|
throw err;
|
|
@@ -119769,9 +120216,9 @@ async function writeFileAtomic(path2, data, mode = 384) {
|
|
|
119769
120216
|
}
|
|
119770
120217
|
|
|
119771
120218
|
// ../inbox/dist/lock.js
|
|
119772
|
-
import { mkdir as
|
|
120219
|
+
import { mkdir as mkdir37, open as open5, rename as rename8, rm as rm15, stat as stat6 } from "node:fs/promises";
|
|
119773
120220
|
import { randomBytes as randomBytes7 } from "node:crypto";
|
|
119774
|
-
import { join as
|
|
120221
|
+
import { join as join79 } from "node:path";
|
|
119775
120222
|
var LockTimeoutError = class extends Error {
|
|
119776
120223
|
constructor(id) {
|
|
119777
120224
|
super(`could not acquire inbox lock for '${id}'`);
|
|
@@ -119782,8 +120229,8 @@ async function withIdLock(dir, id, fn, opts = {}) {
|
|
|
119782
120229
|
const staleMs = opts.staleMs ?? 3e4;
|
|
119783
120230
|
const retryMs = opts.retryMs ?? 20;
|
|
119784
120231
|
const maxWaitMs = opts.maxWaitMs ?? 1e4;
|
|
119785
|
-
const lockPath =
|
|
119786
|
-
await
|
|
120232
|
+
const lockPath = join79(dir, `${id}.lock`);
|
|
120233
|
+
await mkdir37(dir, { recursive: true, mode: 448 });
|
|
119787
120234
|
const deadline = clock.now() + maxWaitMs;
|
|
119788
120235
|
for (; ; ) {
|
|
119789
120236
|
try {
|
|
@@ -119797,7 +120244,7 @@ async function withIdLock(dir, id, fn, opts = {}) {
|
|
|
119797
120244
|
const st = await stat6(lockPath);
|
|
119798
120245
|
if (clock.now() - st.mtimeMs > staleMs) {
|
|
119799
120246
|
const claimed = `${lockPath}.reclaim.${randomBytes7(6).toString("hex")}`;
|
|
119800
|
-
await
|
|
120247
|
+
await rename8(lockPath, claimed);
|
|
119801
120248
|
await rm15(claimed, { force: true });
|
|
119802
120249
|
continue;
|
|
119803
120250
|
}
|
|
@@ -119890,8 +120337,8 @@ function resolveTransition(kind, action) {
|
|
|
119890
120337
|
}
|
|
119891
120338
|
|
|
119892
120339
|
// ../inbox/dist/store.js
|
|
119893
|
-
import { mkdir as
|
|
119894
|
-
import { join as
|
|
120340
|
+
import { mkdir as mkdir38, open as open6, readFile as readFile40, readdir as readdir12, stat as stat7, unlink } from "node:fs/promises";
|
|
120341
|
+
import { join as join80 } from "node:path";
|
|
119895
120342
|
var InboxItemNotFoundError = class extends Error {
|
|
119896
120343
|
constructor(id) {
|
|
119897
120344
|
super(`inbox item not found: ${id}`);
|
|
@@ -119932,19 +120379,19 @@ var FsInboxStore = class {
|
|
|
119932
120379
|
this.identity = typeof build === "string" ? { version: build } : { version: build.version, commit: build.commit, builtAt: build.builtAt };
|
|
119933
120380
|
}
|
|
119934
120381
|
hotPath(id) {
|
|
119935
|
-
return
|
|
120382
|
+
return join80(this.dir, `${id}.json`);
|
|
119936
120383
|
}
|
|
119937
120384
|
archivePath(id) {
|
|
119938
|
-
return
|
|
120385
|
+
return join80(this.dir, "archive", `${id}.json`);
|
|
119939
120386
|
}
|
|
119940
120387
|
archiveDir() {
|
|
119941
|
-
return
|
|
120388
|
+
return join80(this.dir, "archive");
|
|
119942
120389
|
}
|
|
119943
120390
|
/** Reads and validates the hot file, or null on ENOENT. Throws on malformed content (fail-closed). */
|
|
119944
120391
|
async readHot(id) {
|
|
119945
120392
|
let raw;
|
|
119946
120393
|
try {
|
|
119947
|
-
raw = await
|
|
120394
|
+
raw = await readFile40(this.hotPath(id), "utf8");
|
|
119948
120395
|
} catch (err) {
|
|
119949
120396
|
if (isNodeError(err, "ENOENT"))
|
|
119950
120397
|
return null;
|
|
@@ -119956,7 +120403,7 @@ var FsInboxStore = class {
|
|
|
119956
120403
|
async readArchive(id) {
|
|
119957
120404
|
let raw;
|
|
119958
120405
|
try {
|
|
119959
|
-
raw = await
|
|
120406
|
+
raw = await readFile40(this.archivePath(id), "utf8");
|
|
119960
120407
|
} catch (err) {
|
|
119961
120408
|
if (isNodeError(err, "ENOENT"))
|
|
119962
120409
|
return null;
|
|
@@ -119968,7 +120415,7 @@ var FsInboxStore = class {
|
|
|
119968
120415
|
const validated = InboxItemSchema.parse(item);
|
|
119969
120416
|
assertSafeInboxId(validated.id);
|
|
119970
120417
|
await withIdLock(this.dir, validated.id, async () => {
|
|
119971
|
-
await
|
|
120418
|
+
await mkdir38(this.dir, { recursive: true, mode: 448 });
|
|
119972
120419
|
const serialized = JSON.stringify(validated);
|
|
119973
120420
|
let fh;
|
|
119974
120421
|
try {
|
|
@@ -120002,7 +120449,7 @@ var FsInboxStore = class {
|
|
|
120002
120449
|
if (entry.startsWith("."))
|
|
120003
120450
|
continue;
|
|
120004
120451
|
try {
|
|
120005
|
-
const raw = await
|
|
120452
|
+
const raw = await readFile40(join80(this.dir, entry), "utf8");
|
|
120006
120453
|
const parsed = InboxItemSchema.parse(JSON.parse(raw));
|
|
120007
120454
|
if (parsed.status === "pending")
|
|
120008
120455
|
items.push(parsed);
|
|
@@ -120071,7 +120518,7 @@ var FsInboxStore = class {
|
|
|
120071
120518
|
const decision = transition === "approved" || transition === "rejected" || transition === "resolved" ? transition : "resolved";
|
|
120072
120519
|
updated.resolution = { by: "human", decision, at: now2 };
|
|
120073
120520
|
const validated = InboxItemSchema.parse(updated);
|
|
120074
|
-
await
|
|
120521
|
+
await mkdir38(this.archiveDir(), { recursive: true, mode: 448 });
|
|
120075
120522
|
await writeFileAtomic(this.archivePath(id), JSON.stringify(validated));
|
|
120076
120523
|
await unlink(this.hotPath(id));
|
|
120077
120524
|
return validated;
|
|
@@ -120123,7 +120570,7 @@ var FsInboxStore = class {
|
|
|
120123
120570
|
const updated = { ...item, status: "expired", resolvedAt: at3 };
|
|
120124
120571
|
delete updated.humanInput;
|
|
120125
120572
|
const validated = InboxItemSchema.parse(updated);
|
|
120126
|
-
await
|
|
120573
|
+
await mkdir38(this.archiveDir(), { recursive: true, mode: 448 });
|
|
120127
120574
|
await writeFileAtomic(this.archivePath(id), JSON.stringify(validated));
|
|
120128
120575
|
await unlink(this.hotPath(id));
|
|
120129
120576
|
return true;
|
|
@@ -120151,7 +120598,7 @@ var FsInboxStore = class {
|
|
|
120151
120598
|
for (const entry of entries) {
|
|
120152
120599
|
if (!entry.endsWith(".json") || entry.startsWith("."))
|
|
120153
120600
|
continue;
|
|
120154
|
-
const full =
|
|
120601
|
+
const full = join80(this.dir, entry);
|
|
120155
120602
|
try {
|
|
120156
120603
|
const st = await stat7(full);
|
|
120157
120604
|
if (!st.isFile())
|
|
@@ -120430,8 +120877,8 @@ var tagsArg = (args) => {
|
|
|
120430
120877
|
|
|
120431
120878
|
// src/mcp-tools/findings.ts
|
|
120432
120879
|
import { existsSync as existsSync47 } from "node:fs";
|
|
120433
|
-
import { access, readFile as
|
|
120434
|
-
import { join as
|
|
120880
|
+
import { access, readFile as readFile41 } from "node:fs/promises";
|
|
120881
|
+
import { join as join81 } from "node:path";
|
|
120435
120882
|
function findingTools(ctx) {
|
|
120436
120883
|
const { deps, pathRoots, verifyFixImpl } = ctx;
|
|
120437
120884
|
const resolveResultId = async (id, tool) => {
|
|
@@ -120474,7 +120921,7 @@ function findingTools(ctx) {
|
|
|
120474
120921
|
const resultPathsFor = (resultId) => {
|
|
120475
120922
|
const file2 = missionResultFileName(resultId);
|
|
120476
120923
|
const dirs = deps.resultDirsFor?.(resultId) ?? [deps.recordingsDir, ...resultId.startsWith("usability-") ? [deps.uxReportsDir] : []];
|
|
120477
|
-
return dirs.filter((d) => d !== void 0).map((d) =>
|
|
120924
|
+
return dirs.filter((d) => d !== void 0).map((d) => join81(d, file2));
|
|
120478
120925
|
};
|
|
120479
120926
|
const getMissionResult = async (args) => {
|
|
120480
120927
|
if (!deps.recordingsDir) {
|
|
@@ -120486,7 +120933,7 @@ function findingTools(ctx) {
|
|
|
120486
120933
|
let raw;
|
|
120487
120934
|
for (const path2 of resultPathsFor(ref.resultId)) {
|
|
120488
120935
|
try {
|
|
120489
|
-
raw = await
|
|
120936
|
+
raw = await readFile41(path2, "utf8");
|
|
120490
120937
|
break;
|
|
120491
120938
|
} catch {
|
|
120492
120939
|
}
|
|
@@ -121552,9 +121999,9 @@ function registerMissionCommands(program2, deps) {
|
|
|
121552
121999
|
// src/ui-api.ts
|
|
121553
122000
|
import { createServer } from "node:http";
|
|
121554
122001
|
import { randomBytes as randomBytes8 } from "node:crypto";
|
|
121555
|
-
import { readFile as
|
|
122002
|
+
import { readFile as readFile42, realpath as realpath2 } from "node:fs/promises";
|
|
121556
122003
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
121557
|
-
import { join as
|
|
122004
|
+
import { join as join82, sep as sep11 } from "node:path";
|
|
121558
122005
|
import { spawn as spawn6 } from "node:child_process";
|
|
121559
122006
|
var DEFAULT_PORT = 4180;
|
|
121560
122007
|
var MAX_PORT_ATTEMPTS = 20;
|
|
@@ -121656,7 +122103,7 @@ async function startUiServer(deps) {
|
|
|
121656
122103
|
const shouldOpen = deps.open ?? true;
|
|
121657
122104
|
const indexHtmlPath = fileURLToPath4(new URL("../ui/index.html", import.meta.url));
|
|
121658
122105
|
const appJsPath = fileURLToPath4(new URL("../ui/app.js", import.meta.url));
|
|
121659
|
-
const screenshotsDir =
|
|
122106
|
+
const screenshotsDir = join82(deps.inboxDir, "screenshots");
|
|
121660
122107
|
const server = createServer();
|
|
121661
122108
|
let boundPort = 0;
|
|
121662
122109
|
function send(res, status, body, headers = {}) {
|
|
@@ -121668,7 +122115,7 @@ async function startUiServer(deps) {
|
|
|
121668
122115
|
async function serveIndex(req, res) {
|
|
121669
122116
|
const reqUrl = new URL(req.url ?? "/", `http://127.0.0.1:${boundPort}`);
|
|
121670
122117
|
const t = reqUrl.searchParams.get("t");
|
|
121671
|
-
const html = await
|
|
122118
|
+
const html = await readFile42(indexHtmlPath, "utf8");
|
|
121672
122119
|
const headers = {
|
|
121673
122120
|
"Content-Type": "text/html; charset=utf-8",
|
|
121674
122121
|
"Content-Security-Policy": "default-src 'self'; img-src 'self'; style-src 'self' 'unsafe-inline'"
|
|
@@ -121681,7 +122128,7 @@ async function startUiServer(deps) {
|
|
|
121681
122128
|
return 200;
|
|
121682
122129
|
}
|
|
121683
122130
|
async function serveAppJs(res) {
|
|
121684
|
-
const js = await
|
|
122131
|
+
const js = await readFile42(appJsPath, "utf8");
|
|
121685
122132
|
res.writeHead(200, { "Content-Type": "text/javascript" });
|
|
121686
122133
|
res.end(js);
|
|
121687
122134
|
return 200;
|
|
@@ -121694,7 +122141,7 @@ async function startUiServer(deps) {
|
|
|
121694
122141
|
} catch {
|
|
121695
122142
|
return send(res, 404, { error: "not_found" });
|
|
121696
122143
|
}
|
|
121697
|
-
const candidate =
|
|
122144
|
+
const candidate = join82(screenshotsDir, `${id}.png`);
|
|
121698
122145
|
let resolved;
|
|
121699
122146
|
try {
|
|
121700
122147
|
resolved = await realpath2(candidate);
|
|
@@ -121705,7 +122152,7 @@ async function startUiServer(deps) {
|
|
|
121705
122152
|
return send(res, 404, { error: "not_found" });
|
|
121706
122153
|
}
|
|
121707
122154
|
try {
|
|
121708
|
-
const data = await
|
|
122155
|
+
const data = await readFile42(resolved);
|
|
121709
122156
|
res.writeHead(200, { "Content-Type": "image/png" });
|
|
121710
122157
|
res.end(data);
|
|
121711
122158
|
return 200;
|
|
@@ -121945,7 +122392,7 @@ function registerServeCommands(program2, deps, buildProgram2) {
|
|
|
121945
122392
|
}
|
|
121946
122393
|
|
|
121947
122394
|
// src/ux-cli.ts
|
|
121948
|
-
import { readFile as
|
|
122395
|
+
import { readFile as readFile43 } from "node:fs/promises";
|
|
121949
122396
|
function registerUxCommands(program2, deps) {
|
|
121950
122397
|
program2.command("ux <recording>").description("offline UX review of a saved Recording \u2014 ranked, cited usability findings").option("--app-class <class>", "app class for calibration (required), e.g. consumer|admin|internal").option(
|
|
121951
122398
|
"--show <labels>",
|
|
@@ -121973,7 +122420,7 @@ function registerUxCommands(program2, deps) {
|
|
|
121973
122420
|
}
|
|
121974
122421
|
let recording;
|
|
121975
122422
|
try {
|
|
121976
|
-
recording = RecordingSchema.parse(JSON.parse(await
|
|
122423
|
+
recording = RecordingSchema.parse(JSON.parse(await readFile43(recordingPath, "utf8")));
|
|
121977
122424
|
} catch (err) {
|
|
121978
122425
|
emitJson(program2, fail("E_UX_INPUT", String(err instanceof Error ? err.message : err)));
|
|
121979
122426
|
return;
|
|
@@ -122033,7 +122480,7 @@ function registerUxCommands(program2, deps) {
|
|
|
122033
122480
|
|
|
122034
122481
|
// src/logs-retention.ts
|
|
122035
122482
|
import { readFileSync as readFileSync39, readdirSync as readdirSync11, rmSync as rmSync2, rmdirSync as rmdirSync2, statSync as statSync13 } from "node:fs";
|
|
122036
|
-
import { join as
|
|
122483
|
+
import { join as join83 } from "node:path";
|
|
122037
122484
|
var DEFAULT_LOGS_RETENTION = { ttlDays: 14, keepLatest: 50 };
|
|
122038
122485
|
var LogsConfigError = class extends Error {
|
|
122039
122486
|
code = "E_LOGS_CONFIG";
|
|
@@ -122075,8 +122522,8 @@ function runsIn(root2) {
|
|
|
122075
122522
|
}
|
|
122076
122523
|
const byKey = /* @__PURE__ */ new Map();
|
|
122077
122524
|
for (const date5 of dates) {
|
|
122078
|
-
for (const name of readdirSync11(
|
|
122079
|
-
const path2 =
|
|
122525
|
+
for (const name of readdirSync11(join83(root2, date5))) {
|
|
122526
|
+
const path2 = join83(root2, date5, name);
|
|
122080
122527
|
const key2 = `${date5}/${RUN_KEY.exec(name)?.[1] ?? name}`;
|
|
122081
122528
|
const run = byKey.get(key2) ?? { paths: [], mtimeMs: 0 };
|
|
122082
122529
|
run.paths.push(path2);
|
|
@@ -122084,7 +122531,7 @@ function runsIn(root2) {
|
|
|
122084
122531
|
byKey.set(key2, run);
|
|
122085
122532
|
}
|
|
122086
122533
|
}
|
|
122087
|
-
return { runs: [...byKey].map(([key2, r]) => ({ key: key2, ...r })), dateDirs: dates.map((d) =>
|
|
122534
|
+
return { runs: [...byKey].map(([key2, r]) => ({ key: key2, ...r })), dateDirs: dates.map((d) => join83(root2, d)) };
|
|
122088
122535
|
}
|
|
122089
122536
|
function pruneLogs(root2, retention, opts = {}) {
|
|
122090
122537
|
const nowMs = opts.nowMs ?? clock.now();
|
|
@@ -122332,6 +122779,119 @@ function registerInboxCommands(program2, deps) {
|
|
|
122332
122779
|
}
|
|
122333
122780
|
}
|
|
122334
122781
|
|
|
122782
|
+
// src/acceptance-audit-cli.ts
|
|
122783
|
+
import { readFile as readFile45, mkdir as mkdir39, writeFile as writeFile46 } from "node:fs/promises";
|
|
122784
|
+
|
|
122785
|
+
// src/acceptance-audit.ts
|
|
122786
|
+
import { access as access2, readFile as readFile44 } from "node:fs/promises";
|
|
122787
|
+
var CORE = [
|
|
122788
|
+
["requirements", "requirements"],
|
|
122789
|
+
["persona", "persona"],
|
|
122790
|
+
["source-journey", "sourceJourney"],
|
|
122791
|
+
["approved-hash", "approvedHash"],
|
|
122792
|
+
["verify-fix", "verifyFix"],
|
|
122793
|
+
["strict-review", "strictReview"]
|
|
122794
|
+
];
|
|
122795
|
+
async function finding3(asset, path2) {
|
|
122796
|
+
if (path2 === void 0 || path2 === "") return { asset, state: "missing", reason: "no explicit local path supplied" };
|
|
122797
|
+
try {
|
|
122798
|
+
await access2(path2);
|
|
122799
|
+
} catch {
|
|
122800
|
+
return { asset, state: "missing", path: path2, reason: "local file does not exist" };
|
|
122801
|
+
}
|
|
122802
|
+
try {
|
|
122803
|
+
if ((await readFile44(path2, "utf8")).trim() === "") return { asset, state: "inconclusive", path: path2, reason: "local file is empty" };
|
|
122804
|
+
} catch {
|
|
122805
|
+
return { asset, state: "inconclusive", path: path2, reason: "local file cannot be read as text" };
|
|
122806
|
+
}
|
|
122807
|
+
return { asset, state: "complete", path: path2 };
|
|
122808
|
+
}
|
|
122809
|
+
async function auditCoreAcceptanceAssets(assets) {
|
|
122810
|
+
return Promise.all(CORE.map(([asset, key2]) => finding3(asset, assets[key2])));
|
|
122811
|
+
}
|
|
122812
|
+
|
|
122813
|
+
// src/acceptance-audit-schema.ts
|
|
122814
|
+
var ACCEPTANCE_AUDIT_SCHEMA_VERSION = 1;
|
|
122815
|
+
function acceptanceAuditResult(request, findings) {
|
|
122816
|
+
const passed = findings.every((finding4) => finding4.state === "complete");
|
|
122817
|
+
return { schemaVersion: ACCEPTANCE_AUDIT_SCHEMA_VERSION, request, findings, passed, exitCode: passed ? 0 : 1 };
|
|
122818
|
+
}
|
|
122819
|
+
|
|
122820
|
+
// src/acceptance-audit-suite.ts
|
|
122821
|
+
function auditSuiteIntegrity(entries) {
|
|
122822
|
+
const duplicates = entries.filter((entry, index) => entries.findIndex((other) => other.id === entry.id) !== index).map((entry) => entry.id);
|
|
122823
|
+
const missingSidecar = entries.filter((entry) => !entry.sidecar).map((entry) => entry.id);
|
|
122824
|
+
const missingPartition = entries.filter((entry) => entry.partition.trim() === "").map((entry) => entry.id);
|
|
122825
|
+
const missingRevision = entries.filter((entry) => entry.revision.trim() === "").map((entry) => entry.id);
|
|
122826
|
+
const hasNegativeControl = entries.some((entry) => entry.negativeControl);
|
|
122827
|
+
return [
|
|
122828
|
+
{
|
|
122829
|
+
asset: "suite-sidecar",
|
|
122830
|
+
state: missingSidecar.length === 0 ? "complete" : "missing",
|
|
122831
|
+
...missingSidecar.length === 0 ? {} : { reason: `missing sidecar: ${missingSidecar.join(", ")}` }
|
|
122832
|
+
},
|
|
122833
|
+
{
|
|
122834
|
+
asset: "negative-control",
|
|
122835
|
+
state: hasNegativeControl ? "complete" : "missing",
|
|
122836
|
+
...hasNegativeControl ? {} : { reason: "suite has no declared negative control" }
|
|
122837
|
+
},
|
|
122838
|
+
{
|
|
122839
|
+
asset: "partition",
|
|
122840
|
+
state: duplicates.length === 0 && missingPartition.length === 0 ? "complete" : "inconclusive",
|
|
122841
|
+
...duplicates.length === 0 && missingPartition.length === 0 ? {} : { reason: [...duplicates.length === 0 ? [] : [`duplicate ids: ${duplicates.join(", ")}`], ...missingPartition.length === 0 ? [] : [`unpartitioned: ${missingPartition.join(", ")}`]].join("; ") }
|
|
122842
|
+
},
|
|
122843
|
+
{
|
|
122844
|
+
asset: "exact-revision",
|
|
122845
|
+
state: missingRevision.length === 0 ? "complete" : "stale",
|
|
122846
|
+
...missingRevision.length === 0 ? {} : { reason: `missing exact revision: ${missingRevision.join(", ")}` }
|
|
122847
|
+
}
|
|
122848
|
+
];
|
|
122849
|
+
}
|
|
122850
|
+
|
|
122851
|
+
// src/acceptance-audit-render.ts
|
|
122852
|
+
var xml = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/"/g, """);
|
|
122853
|
+
function renderAcceptanceAuditMarkdown(result) {
|
|
122854
|
+
return `# Acceptance audit
|
|
122855
|
+
|
|
122856
|
+
${result.passed ? "Passed" : "Failed"} (exit ${result.exitCode}).
|
|
122857
|
+
|
|
122858
|
+
| asset | state | reason |
|
|
122859
|
+
| --- | --- | --- |
|
|
122860
|
+
${result.findings.map((f2) => `| ${f2.asset} | ${f2.state} | ${f2.reason ?? ""} |`).join("\n")}
|
|
122861
|
+
`;
|
|
122862
|
+
}
|
|
122863
|
+
function renderAcceptanceAuditJunit(result) {
|
|
122864
|
+
const failures = result.findings.filter((f2) => f2.state !== "complete");
|
|
122865
|
+
return `<?xml version="1.0" encoding="UTF-8"?><testsuite name="jevitate.acceptance-audit" tests="${result.findings.length}" failures="${failures.length}">${result.findings.map((f2) => `<testcase name="${xml(f2.asset)}">${f2.state === "complete" ? "" : `<failure message="${xml(f2.state)}">${xml(f2.reason ?? "")}</failure>`}</testcase>`).join("")}</testsuite>
|
|
122866
|
+
`;
|
|
122867
|
+
}
|
|
122868
|
+
function renderAcceptanceAuditSarif(result) {
|
|
122869
|
+
return `${JSON.stringify({ version: "2.1.0", runs: [{ tool: { driver: { name: "jevitate acceptance audit", rules: result.findings.map((f2) => ({ id: f2.asset })) } }, results: result.findings.filter((f2) => f2.state !== "complete").map((f2) => ({ ruleId: f2.asset, level: "error", message: { text: f2.reason ?? f2.state } })) }] }, null, 2)}
|
|
122870
|
+
`;
|
|
122871
|
+
}
|
|
122872
|
+
|
|
122873
|
+
// src/acceptance-audit-cli.ts
|
|
122874
|
+
function registerAcceptanceAuditCommands(program2) {
|
|
122875
|
+
const acceptance = program2.command("acceptance").description("read-only acceptance evidence tools");
|
|
122876
|
+
acceptance.command("audit").description("audit release acceptance evidence and emit CI artifacts").requiredOption("--requirements <file>").requiredOption("--suite <file>").option("--persona <file>").option("--journey <file>").option("--approval <file>").option("--verify <file>").option("--review <file>").option("--out <dir>").option("--json").action(async function() {
|
|
122877
|
+
const o = this.opts();
|
|
122878
|
+
try {
|
|
122879
|
+
const core = await auditCoreAcceptanceAssets({ requirements: o.requirements, persona: o.persona, sourceJourney: o.journey, approvedHash: o.approval, verifyFix: o.verify, strictReview: o.review });
|
|
122880
|
+
const entries = JSON.parse(await readFile45(o.suite, "utf8"));
|
|
122881
|
+
if (!Array.isArray(entries)) throw new Error("suite must be a JSON array of acceptance entries");
|
|
122882
|
+
const result = acceptanceAuditResult({ requirementsFile: o.requirements, suiteFile: o.suite, strict: true }, [...core, ...auditSuiteIntegrity(entries)]);
|
|
122883
|
+
if (o.out !== void 0) {
|
|
122884
|
+
await mkdir39(o.out, { recursive: true });
|
|
122885
|
+
await Promise.all([writeFile46(`${o.out}/acceptance-audit.md`, renderAcceptanceAuditMarkdown(result)), writeFile46(`${o.out}/acceptance-audit.junit.xml`, renderAcceptanceAuditJunit(result)), writeFile46(`${o.out}/acceptance-audit.sarif`, renderAcceptanceAuditSarif(result)), writeFile46(`${o.out}/acceptance-audit.json`, `${JSON.stringify(result, null, 2)}
|
|
122886
|
+
`)]);
|
|
122887
|
+
}
|
|
122888
|
+
emitEnvelope(program2, ok(result), { json: o.json === true, exitCode: result.exitCode, command: "acceptance audit", human: (r) => renderAcceptanceAuditMarkdown(r) });
|
|
122889
|
+
} catch (err) {
|
|
122890
|
+
emitEnvelope(program2, fail("E_ACCEPTANCE_AUDIT", err instanceof Error ? err.message : String(err)), { json: o.json === true, command: "acceptance audit" });
|
|
122891
|
+
}
|
|
122892
|
+
});
|
|
122893
|
+
}
|
|
122894
|
+
|
|
122335
122895
|
// src/program.ts
|
|
122336
122896
|
function versionString() {
|
|
122337
122897
|
return formatVersion(currentEngineInfo());
|
|
@@ -122371,6 +122931,7 @@ function buildProgram(deps) {
|
|
|
122371
122931
|
registerMissionCommands(program2, deps);
|
|
122372
122932
|
registerServeCommands(program2, deps, buildProgram);
|
|
122373
122933
|
registerInboxCommands(program2, deps);
|
|
122934
|
+
registerAcceptanceAuditCommands(program2);
|
|
122374
122935
|
registerUxCommands(program2, deps);
|
|
122375
122936
|
registerAiCommands(program2, deps);
|
|
122376
122937
|
registerCheckCommand(
|