@riddledc/riddle-proof 0.7.149 → 0.7.151
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -0
- package/dist/{chunk-QXQCG3WB.js → chunk-QJJ3ISMK.js} +194 -2
- package/dist/cli.cjs +267 -5
- package/dist/cli.js +75 -4
- package/dist/index.cjs +194 -2
- package/dist/index.js +1 -1
- package/dist/profile.cjs +194 -2
- package/dist/profile.d.cts +4 -1
- package/dist/profile.d.ts +4 -1
- package/dist/profile.js +1 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -6975,6 +6975,7 @@ var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
|
|
|
6975
6975
|
"selector_text_visible",
|
|
6976
6976
|
"selector_text_absent",
|
|
6977
6977
|
"selector_text_order",
|
|
6978
|
+
"observe_within",
|
|
6978
6979
|
"frame_text_visible",
|
|
6979
6980
|
"frame_url_equals",
|
|
6980
6981
|
"frame_url_matches",
|
|
@@ -8297,7 +8298,7 @@ function normalizeCheck(input, index) {
|
|
|
8297
8298
|
throw new Error(`checks[${index}].type ${type} is not supported. Supported checks: ${RIDDLE_PROOF_PROFILE_CHECK_TYPES.join(", ")}`);
|
|
8298
8299
|
}
|
|
8299
8300
|
const isDialogCountCheck = isDialogCountCheckType(type);
|
|
8300
|
-
if ((type === "selector_visible" || type === "selector_absent" || type === "selector_count_at_least" || type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq" || type === "selector_text_visible" || type === "selector_text_absent") && !stringValue2(input.selector)) {
|
|
8301
|
+
if ((type === "selector_visible" || type === "selector_absent" || type === "selector_count_at_least" || type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq" || type === "selector_text_visible" || type === "selector_text_absent" || type === "observe_within" && !stringValue2(input.text) && !stringValue2(input.pattern)) && !stringValue2(input.selector)) {
|
|
8301
8302
|
throw new Error(`checks[${index}] ${type} requires selector.`);
|
|
8302
8303
|
}
|
|
8303
8304
|
if ((type === "frame_text_visible" || type === "frame_url_equals" || type === "frame_url_matches" || type === "frame_no_horizontal_overflow") && !stringValue2(input.selector)) {
|
|
@@ -8438,7 +8439,7 @@ function normalizeCheck(input, index) {
|
|
|
8438
8439
|
allowed_content_types: allowedContentTypes,
|
|
8439
8440
|
allow_get_fallback: isLinkStatusCheck ? input.allow_get_fallback === false || input.allowGetFallback === false ? false : true : void 0,
|
|
8440
8441
|
max_overflow_px: numberValue(input.max_overflow_px),
|
|
8441
|
-
timeout_ms: numberValue(input.timeout_ms) ?? numberValue(input.timeoutMs),
|
|
8442
|
+
timeout_ms: numberValue(input.timeout_ms) ?? numberValue(input.timeoutMs) ?? numberValue(input.within_ms) ?? numberValue(input.withinMs),
|
|
8442
8443
|
run_direct_routes: input.run_direct_routes === false || input.runDirectRoutes === false ? false : true,
|
|
8443
8444
|
run_clickthroughs: input.run_clickthroughs === false || input.runClickthroughs === false ? false : true,
|
|
8444
8445
|
run_all_viewports: input.run_all_viewports === true || input.runAllViewports === true,
|
|
@@ -8956,6 +8957,16 @@ function summarizeLinkStatusEvidence(viewport, check) {
|
|
|
8956
8957
|
function textKey(check) {
|
|
8957
8958
|
return check.pattern ? `pattern:${check.pattern}/${check.flags || ""}` : `text:${check.text || ""}`;
|
|
8958
8959
|
}
|
|
8960
|
+
function observeWithinTimeoutMs(check) {
|
|
8961
|
+
const raw = check.timeout_ms;
|
|
8962
|
+
if (typeof raw === "number" && Number.isFinite(raw) && raw > 0) return Math.min(Math.round(raw), 6e4);
|
|
8963
|
+
return 2e3;
|
|
8964
|
+
}
|
|
8965
|
+
function observeWithinKey(check) {
|
|
8966
|
+
const target = check.selector ? `selector:${check.selector}` : "page";
|
|
8967
|
+
const expectation = check.pattern ? `pattern:${check.pattern}/${check.flags || ""}` : check.text ? `text:${check.text}` : "visible";
|
|
8968
|
+
return `${target}|${expectation}|within:${observeWithinTimeoutMs(check)}`;
|
|
8969
|
+
}
|
|
8959
8970
|
function textSequenceForCheck(viewport, check) {
|
|
8960
8971
|
const key = selectorKey(check);
|
|
8961
8972
|
const sequence = viewport.text_sequences?.[key];
|
|
@@ -9462,6 +9473,40 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
9462
9473
|
message: failed ? `Selector ${key} text order failed in ${failed} viewport(s).` : void 0
|
|
9463
9474
|
};
|
|
9464
9475
|
}
|
|
9476
|
+
if (check.type === "observe_within") {
|
|
9477
|
+
const key = observeWithinKey(check);
|
|
9478
|
+
const timeoutMs = observeWithinTimeoutMs(check);
|
|
9479
|
+
const results = viewports.map((viewport) => {
|
|
9480
|
+
const observation = viewport.observations?.[key];
|
|
9481
|
+
const matched = observation?.matched === true;
|
|
9482
|
+
return {
|
|
9483
|
+
viewport: viewport.name,
|
|
9484
|
+
matched,
|
|
9485
|
+
elapsed_ms: numberValue(observation?.elapsed_ms) ?? null,
|
|
9486
|
+
timeout_ms: numberValue(observation?.timeout_ms) ?? timeoutMs,
|
|
9487
|
+
attempts: numberValue(observation?.attempts) ?? null,
|
|
9488
|
+
selector_count: numberValue(observation?.selector_count) ?? null,
|
|
9489
|
+
visible_count: numberValue(observation?.visible_count) ?? null,
|
|
9490
|
+
matched_count: numberValue(observation?.matched_count) ?? null,
|
|
9491
|
+
sample: stringValue2(observation?.sample) ?? null,
|
|
9492
|
+
error: stringValue2(observation?.error) ?? null
|
|
9493
|
+
};
|
|
9494
|
+
});
|
|
9495
|
+
const failed = results.filter((result) => !result.matched).length;
|
|
9496
|
+
return {
|
|
9497
|
+
type: check.type,
|
|
9498
|
+
label: checkLabel(check),
|
|
9499
|
+
status: failed ? "failed" : "passed",
|
|
9500
|
+
evidence: {
|
|
9501
|
+
selector: check.selector || null,
|
|
9502
|
+
text: check.text || null,
|
|
9503
|
+
pattern: check.pattern || null,
|
|
9504
|
+
timeout_ms: timeoutMs,
|
|
9505
|
+
viewports: results.map((result) => toJsonValue(result))
|
|
9506
|
+
},
|
|
9507
|
+
message: failed ? `Observation did not match within ${timeoutMs}ms in ${failed} viewport(s).` : void 0
|
|
9508
|
+
};
|
|
9509
|
+
}
|
|
9465
9510
|
if (check.type === "frame_text_visible") {
|
|
9466
9511
|
const key = selectorKey(check);
|
|
9467
9512
|
const results = viewports.map((viewport) => {
|
|
@@ -11355,6 +11400,36 @@ function assessProfile(profile, evidence) {
|
|
|
11355
11400
|
});
|
|
11356
11401
|
continue;
|
|
11357
11402
|
}
|
|
11403
|
+
if (check.type === "observe_within") {
|
|
11404
|
+
const key = observeWithinKey(check);
|
|
11405
|
+
const timeoutMs = observeWithinTimeoutMs(check);
|
|
11406
|
+
const results = checkViewports.map((viewport) => {
|
|
11407
|
+
const observation = viewport.observations && viewport.observations[key] && typeof viewport.observations[key] === "object"
|
|
11408
|
+
? viewport.observations[key]
|
|
11409
|
+
: {};
|
|
11410
|
+
return {
|
|
11411
|
+
viewport: viewport.name,
|
|
11412
|
+
matched: observation.matched === true,
|
|
11413
|
+
elapsed_ms: typeof observation.elapsed_ms === "number" && Number.isFinite(observation.elapsed_ms) ? observation.elapsed_ms : null,
|
|
11414
|
+
timeout_ms: typeof observation.timeout_ms === "number" && Number.isFinite(observation.timeout_ms) ? observation.timeout_ms : timeoutMs,
|
|
11415
|
+
attempts: typeof observation.attempts === "number" && Number.isFinite(observation.attempts) ? observation.attempts : null,
|
|
11416
|
+
selector_count: typeof observation.selector_count === "number" && Number.isFinite(observation.selector_count) ? observation.selector_count : null,
|
|
11417
|
+
visible_count: typeof observation.visible_count === "number" && Number.isFinite(observation.visible_count) ? observation.visible_count : null,
|
|
11418
|
+
matched_count: typeof observation.matched_count === "number" && Number.isFinite(observation.matched_count) ? observation.matched_count : null,
|
|
11419
|
+
sample: typeof observation.sample === "string" && observation.sample.trim() ? observation.sample.trim() : null,
|
|
11420
|
+
error: typeof observation.error === "string" && observation.error.trim() ? observation.error.trim() : null,
|
|
11421
|
+
};
|
|
11422
|
+
});
|
|
11423
|
+
const failed = results.filter((result) => !result.matched).length;
|
|
11424
|
+
checks.push({
|
|
11425
|
+
type: check.type,
|
|
11426
|
+
label: check.label || check.type,
|
|
11427
|
+
status: failed ? "failed" : "passed",
|
|
11428
|
+
evidence: { selector: check.selector || null, text: check.text || null, pattern: check.pattern || null, timeout_ms: timeoutMs, viewports: results },
|
|
11429
|
+
message: failed ? "Observation did not match within " + timeoutMs + "ms in " + failed + " viewport(s)." : undefined,
|
|
11430
|
+
});
|
|
11431
|
+
continue;
|
|
11432
|
+
}
|
|
11358
11433
|
if (check.type === "frame_text_visible") {
|
|
11359
11434
|
const selector = check.selector || "";
|
|
11360
11435
|
const results = checkViewports.map((viewport) => {
|
|
@@ -11787,6 +11862,19 @@ function ensureDialogHandler() {
|
|
|
11787
11862
|
function textKey(check) {
|
|
11788
11863
|
return check.pattern ? "pattern:" + check.pattern + "/" + (check.flags || "") : "text:" + (check.text || "");
|
|
11789
11864
|
}
|
|
11865
|
+
function observeWithinTimeoutMs(check) {
|
|
11866
|
+
const raw = Number(check && check.timeout_ms);
|
|
11867
|
+
return Number.isFinite(raw) && raw > 0 ? Math.min(Math.round(raw), 60000) : 2000;
|
|
11868
|
+
}
|
|
11869
|
+
function observeWithinKey(check) {
|
|
11870
|
+
const target = check && check.selector ? "selector:" + check.selector : "page";
|
|
11871
|
+
const expectation = check && check.pattern
|
|
11872
|
+
? "pattern:" + check.pattern + "/" + (check.flags || "")
|
|
11873
|
+
: check && check.text
|
|
11874
|
+
? "text:" + check.text
|
|
11875
|
+
: "visible";
|
|
11876
|
+
return target + "|" + expectation + "|within:" + observeWithinTimeoutMs(check);
|
|
11877
|
+
}
|
|
11790
11878
|
function textMatches(sample, check) {
|
|
11791
11879
|
if (check.pattern) {
|
|
11792
11880
|
try { return new RegExp(check.pattern, check.flags || "").test(sample || ""); } catch { return false; }
|
|
@@ -13102,6 +13190,104 @@ async function selectorTextSequence(selector) {
|
|
|
13102
13190
|
};
|
|
13103
13191
|
}).catch((error) => ({ count: 0, visible_count: 0, texts: [], visible_texts: [], match_texts: [], visible_match_texts: [], error: String(error && error.message ? error.message : error).slice(0, 500) }));
|
|
13104
13192
|
}
|
|
13193
|
+
async function observeWithinSnapshot(check) {
|
|
13194
|
+
const payload = {
|
|
13195
|
+
selector: check.selector || "",
|
|
13196
|
+
text: check.text || "",
|
|
13197
|
+
pattern: check.pattern || "",
|
|
13198
|
+
flags: check.flags || "",
|
|
13199
|
+
wants_text: Boolean(check.text || check.pattern),
|
|
13200
|
+
};
|
|
13201
|
+
if (payload.selector) {
|
|
13202
|
+
return page.locator(payload.selector).evaluateAll((elements, input) => {
|
|
13203
|
+
const compact = (value) => String(value || "").replace(/\s+/g, " ").trim();
|
|
13204
|
+
const matchText = (value) => {
|
|
13205
|
+
const source = compact(value);
|
|
13206
|
+
if (input.pattern) {
|
|
13207
|
+
try { return new RegExp(input.pattern, input.flags || "").test(source); } catch { return false; }
|
|
13208
|
+
}
|
|
13209
|
+
return source.includes(input.text || "");
|
|
13210
|
+
};
|
|
13211
|
+
const isVisible = (element) => {
|
|
13212
|
+
const style = window.getComputedStyle(element);
|
|
13213
|
+
const rect = element.getBoundingClientRect();
|
|
13214
|
+
return style && style.visibility !== "hidden" && style.display !== "none" && rect.width > 0 && rect.height > 0;
|
|
13215
|
+
};
|
|
13216
|
+
const rows = elements.map((element, index) => {
|
|
13217
|
+
const text = compact(element.innerText || element.textContent || "");
|
|
13218
|
+
const visible = isVisible(element);
|
|
13219
|
+
return { index, text, visible, matched: input.wants_text ? matchText(text) : visible };
|
|
13220
|
+
});
|
|
13221
|
+
const visibleRows = rows.filter((row) => row.visible);
|
|
13222
|
+
const matches = input.wants_text ? visibleRows.filter((row) => row.matched) : visibleRows;
|
|
13223
|
+
const sampleRow = matches[0] || visibleRows[0] || rows[0] || null;
|
|
13224
|
+
return {
|
|
13225
|
+
selector: input.selector,
|
|
13226
|
+
text: input.text || null,
|
|
13227
|
+
pattern: input.pattern || null,
|
|
13228
|
+
selector_count: rows.length,
|
|
13229
|
+
visible_count: visibleRows.length,
|
|
13230
|
+
matched_count: matches.length,
|
|
13231
|
+
matched: matches.length > 0,
|
|
13232
|
+
sample: sampleRow && sampleRow.text ? sampleRow.text.slice(0, 240) : null,
|
|
13233
|
+
};
|
|
13234
|
+
}, payload).catch((error) => ({
|
|
13235
|
+
selector: payload.selector,
|
|
13236
|
+
text: payload.text || null,
|
|
13237
|
+
pattern: payload.pattern || null,
|
|
13238
|
+
selector_count: 0,
|
|
13239
|
+
visible_count: 0,
|
|
13240
|
+
matched_count: 0,
|
|
13241
|
+
matched: false,
|
|
13242
|
+
sample: null,
|
|
13243
|
+
error: String(error && error.message ? error.message : error).slice(0, 500),
|
|
13244
|
+
}));
|
|
13245
|
+
}
|
|
13246
|
+
return page.evaluate((input) => {
|
|
13247
|
+
const compact = (value) => String(value || "").replace(/\s+/g, " ").trim();
|
|
13248
|
+
const sample = compact(document.body ? document.body.innerText || document.body.textContent || "" : "");
|
|
13249
|
+
let matched = false;
|
|
13250
|
+
if (input.pattern) {
|
|
13251
|
+
try { matched = new RegExp(input.pattern, input.flags || "").test(sample); } catch { matched = false; }
|
|
13252
|
+
} else {
|
|
13253
|
+
matched = sample.includes(input.text || "");
|
|
13254
|
+
}
|
|
13255
|
+
return {
|
|
13256
|
+
selector: null,
|
|
13257
|
+
text: input.text || null,
|
|
13258
|
+
pattern: input.pattern || null,
|
|
13259
|
+
matched,
|
|
13260
|
+
matched_count: matched ? 1 : 0,
|
|
13261
|
+
sample: sample.slice(0, 240),
|
|
13262
|
+
};
|
|
13263
|
+
}, payload).catch((error) => ({
|
|
13264
|
+
selector: null,
|
|
13265
|
+
text: payload.text || null,
|
|
13266
|
+
pattern: payload.pattern || null,
|
|
13267
|
+
matched: false,
|
|
13268
|
+
matched_count: 0,
|
|
13269
|
+
sample: null,
|
|
13270
|
+
error: String(error && error.message ? error.message : error).slice(0, 500),
|
|
13271
|
+
}));
|
|
13272
|
+
}
|
|
13273
|
+
async function observeWithin(check) {
|
|
13274
|
+
const timeoutMs = observeWithinTimeoutMs(check);
|
|
13275
|
+
const startedAt = Date.now();
|
|
13276
|
+
let attempts = 0;
|
|
13277
|
+
let last = null;
|
|
13278
|
+
while (true) {
|
|
13279
|
+
attempts += 1;
|
|
13280
|
+
last = await observeWithinSnapshot(check);
|
|
13281
|
+
const elapsedMs = Date.now() - startedAt;
|
|
13282
|
+
if (last && last.matched === true) {
|
|
13283
|
+
return { ...last, timeout_ms: timeoutMs, elapsed_ms: elapsedMs, attempts };
|
|
13284
|
+
}
|
|
13285
|
+
if (elapsedMs >= timeoutMs) {
|
|
13286
|
+
return { ...(last || {}), matched: false, timeout_ms: timeoutMs, elapsed_ms: elapsedMs, attempts };
|
|
13287
|
+
}
|
|
13288
|
+
await page.waitForTimeout(Math.min(100, Math.max(25, timeoutMs - elapsedMs)));
|
|
13289
|
+
}
|
|
13290
|
+
}
|
|
13105
13291
|
function linkProbeMaxLinks(check) {
|
|
13106
13292
|
const value = Number(check.max_links || check.maxLinks || check.limit || 100);
|
|
13107
13293
|
return Number.isInteger(value) && value > 0 ? Math.min(value, 500) : 100;
|
|
@@ -14154,6 +14340,7 @@ async function captureViewport(viewport) {
|
|
|
14154
14340
|
const text_matches = {};
|
|
14155
14341
|
const text_match_samples = {};
|
|
14156
14342
|
const text_case_insensitive_samples = {};
|
|
14343
|
+
const observations = {};
|
|
14157
14344
|
const http_statuses = {};
|
|
14158
14345
|
const link_statuses = {};
|
|
14159
14346
|
for (const check of profile.checks || []) {
|
|
@@ -14174,6 +14361,10 @@ async function captureViewport(viewport) {
|
|
|
14174
14361
|
selectors[check.selector] = selectors[check.selector] || await selectorStats(check.selector);
|
|
14175
14362
|
text_sequences[check.selector] = await selectorTextSequence(check.selector);
|
|
14176
14363
|
}
|
|
14364
|
+
if (check.type === "observe_within") {
|
|
14365
|
+
const key = observeWithinKey(check);
|
|
14366
|
+
observations[key] = observations[key] || await observeWithin(check);
|
|
14367
|
+
}
|
|
14177
14368
|
if ((check.type === "text_visible" || check.type === "text_absent") && (check.text || check.pattern)) {
|
|
14178
14369
|
const key = textKey(check);
|
|
14179
14370
|
const sample = dom.body_text || dom.body_text_sample || "";
|
|
@@ -14267,6 +14458,7 @@ async function captureViewport(viewport) {
|
|
|
14267
14458
|
text_matches,
|
|
14268
14459
|
text_match_samples,
|
|
14269
14460
|
text_case_insensitive_samples,
|
|
14461
|
+
observations,
|
|
14270
14462
|
http_statuses,
|
|
14271
14463
|
link_statuses,
|
|
14272
14464
|
route_inventory: routeInventory,
|
|
@@ -14872,6 +15064,7 @@ function profileRiddleJobMarkdown(result) {
|
|
|
14872
15064
|
const attempts = cliFiniteNumber(riddle.attempts);
|
|
14873
15065
|
const submittedAt = cliString(riddle.submitted_at);
|
|
14874
15066
|
const completedAt = cliString(riddle.completed_at);
|
|
15067
|
+
const artifactRecovery = riddle.artifact_recovery === true;
|
|
14875
15068
|
const parts = [
|
|
14876
15069
|
mode ? `mode ${markdownInlineCode(mode)}` : "",
|
|
14877
15070
|
jobCount === void 0 ? "" : `jobs ${jobCount}`,
|
|
@@ -14888,6 +15081,9 @@ function profileRiddleJobMarkdown(result) {
|
|
|
14888
15081
|
if (submittedAt || completedAt) {
|
|
14889
15082
|
lines.push(`- timing:${submittedAt ? ` submitted ${markdownInlineCode(submittedAt)}` : ""}${completedAt ? ` completed ${markdownInlineCode(completedAt)}` : ""}`);
|
|
14890
15083
|
}
|
|
15084
|
+
if (artifactRecovery) {
|
|
15085
|
+
lines.push("- artifact recovery: used artifacts endpoint after non-terminal poll");
|
|
15086
|
+
}
|
|
14891
15087
|
const splitJobs = Array.isArray(riddle.split_jobs) ? riddle.split_jobs.map(cliRecord).filter((job) => Boolean(job)) : [];
|
|
14892
15088
|
for (const job of splitJobs.slice(0, 12)) {
|
|
14893
15089
|
const viewport = cliString(job.viewport) || "viewport";
|
|
@@ -14896,13 +15092,15 @@ function profileRiddleJobMarkdown(result) {
|
|
|
14896
15092
|
const splitTerminal = typeof job.terminal === "boolean" ? job.terminal : void 0;
|
|
14897
15093
|
const splitElapsedMs = cliFiniteNumber(job.elapsed_ms);
|
|
14898
15094
|
const splitPreSubmissionElapsedMs = cliFiniteNumber(job.pre_submission_elapsed_ms);
|
|
15095
|
+
const splitArtifactRecovery = job.artifact_recovery === true;
|
|
14899
15096
|
lines.push(
|
|
14900
15097
|
`- ${viewport}: ${[
|
|
14901
15098
|
splitJobId ? `job ${markdownInlineCode(splitJobId)}` : "",
|
|
14902
15099
|
splitStatus ? `status ${markdownInlineCode(splitStatus)}` : "",
|
|
14903
15100
|
splitTerminal === void 0 ? "" : `terminal ${splitTerminal ? "true" : "false"}`,
|
|
14904
15101
|
splitElapsedMs === void 0 ? "" : `elapsed ${formatPollDuration(splitElapsedMs)}`,
|
|
14905
|
-
splitPreSubmissionElapsedMs === void 0 || splitPreSubmissionElapsedMs < 1e3 ? "" : `pre-submit ${formatPollDuration(splitPreSubmissionElapsedMs)}
|
|
15102
|
+
splitPreSubmissionElapsedMs === void 0 || splitPreSubmissionElapsedMs < 1e3 ? "" : `pre-submit ${formatPollDuration(splitPreSubmissionElapsedMs)}`,
|
|
15103
|
+
splitArtifactRecovery ? "artifact recovery" : ""
|
|
14906
15104
|
].filter(Boolean).join(", ") || "job metadata unavailable"}`
|
|
14907
15105
|
);
|
|
14908
15106
|
}
|
|
@@ -14949,6 +15147,14 @@ function profileCheckMarkdownTarget(check) {
|
|
|
14949
15147
|
if (check.type === "selector_text_order") {
|
|
14950
15148
|
return selector ? `${markdownInlineCode(selector)} text order` : void 0;
|
|
14951
15149
|
}
|
|
15150
|
+
if (check.type === "observe_within") {
|
|
15151
|
+
const textTarget = profileCheckTextTarget(evidence);
|
|
15152
|
+
const timeoutMs = cliFiniteNumber(evidence.timeout_ms);
|
|
15153
|
+
const withinLabel = timeoutMs === void 0 ? "within timeout" : `within ${timeoutMs}ms`;
|
|
15154
|
+
if (selector && textTarget) return `${markdownInlineCode(selector)} observes ${textTarget} ${withinLabel}`;
|
|
15155
|
+
if (selector) return `${markdownInlineCode(selector)} visible ${withinLabel}`;
|
|
15156
|
+
return textTarget ? `${textTarget} ${withinLabel}` : withinLabel;
|
|
15157
|
+
}
|
|
14952
15158
|
if (check.type === "text_visible" || check.type === "text_absent") {
|
|
14953
15159
|
return profileCheckTextTarget(evidence);
|
|
14954
15160
|
}
|
|
@@ -15534,7 +15740,8 @@ function withRiddleMetadata(result, input) {
|
|
|
15534
15740
|
elapsed_ms: poll?.elapsed_ms ?? result.riddle?.elapsed_ms,
|
|
15535
15741
|
attempt: poll?.attempt ?? result.riddle?.attempt,
|
|
15536
15742
|
attempts: poll?.attempts ?? result.riddle?.attempts,
|
|
15537
|
-
timed_out: poll?.timed_out ?? result.riddle?.timed_out
|
|
15743
|
+
timed_out: poll?.timed_out ?? result.riddle?.timed_out,
|
|
15744
|
+
artifact_recovery: input.artifactRecovery ?? result.riddle?.artifact_recovery
|
|
15538
15745
|
},
|
|
15539
15746
|
artifacts: {
|
|
15540
15747
|
...result.artifacts,
|
|
@@ -15542,6 +15749,52 @@ function withRiddleMetadata(result, input) {
|
|
|
15542
15749
|
}
|
|
15543
15750
|
};
|
|
15544
15751
|
}
|
|
15752
|
+
function riddleArtifactsPayloadStatus(payload) {
|
|
15753
|
+
const record = cliRecord(payload);
|
|
15754
|
+
return cliString(record?.status) ?? cliString(cliRecord(record?.job)?.status);
|
|
15755
|
+
}
|
|
15756
|
+
async function recoverProfileResultFromRiddleArtifacts(profile, input) {
|
|
15757
|
+
if (input.poll.poll?.timed_out !== true) return void 0;
|
|
15758
|
+
let artifactPayload;
|
|
15759
|
+
try {
|
|
15760
|
+
artifactPayload = await input.client.requestJson(`/v1/jobs/${input.jobId}/artifacts`);
|
|
15761
|
+
} catch {
|
|
15762
|
+
return void 0;
|
|
15763
|
+
}
|
|
15764
|
+
const artifacts = collectRiddleProfileArtifactRefs(artifactPayload);
|
|
15765
|
+
if (!artifacts.length) return void 0;
|
|
15766
|
+
const artifactStatus = riddleArtifactsPayloadStatus(artifactPayload);
|
|
15767
|
+
const terminal = artifactStatus ? isTerminalRiddleJobStatus(artifactStatus) : true;
|
|
15768
|
+
const recoveredPoll = input.poll.poll ? {
|
|
15769
|
+
...input.poll.poll,
|
|
15770
|
+
status: artifactStatus ?? input.poll.poll.status,
|
|
15771
|
+
terminal
|
|
15772
|
+
} : void 0;
|
|
15773
|
+
const artifactResult = await profileResultFromRiddleArtifacts(profile, artifacts, [artifactPayload, input.poll.job]);
|
|
15774
|
+
if (artifactResult) {
|
|
15775
|
+
return withRiddleMetadata(artifactResult, {
|
|
15776
|
+
job_id: input.jobId,
|
|
15777
|
+
status: artifactStatus ?? input.poll.status,
|
|
15778
|
+
terminal,
|
|
15779
|
+
poll: recoveredPoll,
|
|
15780
|
+
artifacts,
|
|
15781
|
+
artifactRecovery: true
|
|
15782
|
+
});
|
|
15783
|
+
}
|
|
15784
|
+
if (!terminal) return void 0;
|
|
15785
|
+
return createRiddleProofProfileInsufficientResult({
|
|
15786
|
+
profile,
|
|
15787
|
+
runner: input.runner,
|
|
15788
|
+
error: `Riddle job ${input.jobId} timed out in status ${input.poll.status || "unknown"}, but artifacts were recovered without a proof result.`,
|
|
15789
|
+
riddle: {
|
|
15790
|
+
...riddleMetadataFromPoll(input.jobId, input.poll),
|
|
15791
|
+
status: artifactStatus ?? input.poll.status,
|
|
15792
|
+
terminal,
|
|
15793
|
+
artifact_recovery: true
|
|
15794
|
+
},
|
|
15795
|
+
artifacts
|
|
15796
|
+
});
|
|
15797
|
+
}
|
|
15545
15798
|
function riddleMetadataFromPoll(jobId, poll) {
|
|
15546
15799
|
return {
|
|
15547
15800
|
job_id: jobId,
|
|
@@ -15604,13 +15857,15 @@ function splitViewportRiddleMetadata(childRuns) {
|
|
|
15604
15857
|
elapsed_ms: result.riddle?.elapsed_ms,
|
|
15605
15858
|
attempt: result.riddle?.attempt,
|
|
15606
15859
|
attempts: result.riddle?.attempts,
|
|
15607
|
-
timed_out: result.riddle?.timed_out
|
|
15860
|
+
timed_out: result.riddle?.timed_out,
|
|
15861
|
+
artifact_recovery: result.riddle?.artifact_recovery
|
|
15608
15862
|
}));
|
|
15609
15863
|
return {
|
|
15610
15864
|
mode: "split-viewports",
|
|
15611
15865
|
job_count: childRuns.length,
|
|
15612
15866
|
status: "split-viewports",
|
|
15613
15867
|
terminal: childRuns.every(({ result }) => result.riddle?.terminal !== false),
|
|
15868
|
+
artifact_recovery: childRuns.some(({ result }) => result.riddle?.artifact_recovery === true),
|
|
15614
15869
|
queue_elapsed_ms: sumDefinedNumbers(splitJobs.map((job) => job.queue_elapsed_ms)),
|
|
15615
15870
|
pre_submission_elapsed_ms: sumDefinedNumbers(splitJobs.map((job) => job.pre_submission_elapsed_ms)),
|
|
15616
15871
|
elapsed_ms: sumDefinedNumbers(splitJobs.map((job) => job.elapsed_ms)),
|
|
@@ -15733,6 +15988,13 @@ async function runSingleRiddleProfileForCli(profile, options, input) {
|
|
|
15733
15988
|
});
|
|
15734
15989
|
const artifacts = collectRiddleProfileArtifactRefs(poll.artifacts);
|
|
15735
15990
|
if (!poll.ok || !poll.terminal) {
|
|
15991
|
+
const recoveredResult = await recoverProfileResultFromRiddleArtifacts(profile, {
|
|
15992
|
+
client,
|
|
15993
|
+
runner,
|
|
15994
|
+
jobId,
|
|
15995
|
+
poll
|
|
15996
|
+
});
|
|
15997
|
+
if (recoveredResult) return recoveredResult;
|
|
15736
15998
|
return createRiddleProofProfileEnvironmentBlockedResult({
|
|
15737
15999
|
profile,
|
|
15738
16000
|
runner,
|
package/dist/cli.js
CHANGED
|
@@ -13,9 +13,10 @@ import {
|
|
|
13
13
|
profileStatusExitCode,
|
|
14
14
|
resolveRiddleProofProfileTargetUrl,
|
|
15
15
|
resolveRiddleProofProfileTimeoutSec
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-QJJ3ISMK.js";
|
|
17
17
|
import {
|
|
18
18
|
createRiddleApiClient,
|
|
19
|
+
isTerminalRiddleJobStatus,
|
|
19
20
|
parseRiddleViewport
|
|
20
21
|
} from "./chunk-M3ZTY6PQ.js";
|
|
21
22
|
import {
|
|
@@ -430,6 +431,7 @@ function profileRiddleJobMarkdown(result) {
|
|
|
430
431
|
const attempts = cliFiniteNumber(riddle.attempts);
|
|
431
432
|
const submittedAt = cliString(riddle.submitted_at);
|
|
432
433
|
const completedAt = cliString(riddle.completed_at);
|
|
434
|
+
const artifactRecovery = riddle.artifact_recovery === true;
|
|
433
435
|
const parts = [
|
|
434
436
|
mode ? `mode ${markdownInlineCode(mode)}` : "",
|
|
435
437
|
jobCount === void 0 ? "" : `jobs ${jobCount}`,
|
|
@@ -446,6 +448,9 @@ function profileRiddleJobMarkdown(result) {
|
|
|
446
448
|
if (submittedAt || completedAt) {
|
|
447
449
|
lines.push(`- timing:${submittedAt ? ` submitted ${markdownInlineCode(submittedAt)}` : ""}${completedAt ? ` completed ${markdownInlineCode(completedAt)}` : ""}`);
|
|
448
450
|
}
|
|
451
|
+
if (artifactRecovery) {
|
|
452
|
+
lines.push("- artifact recovery: used artifacts endpoint after non-terminal poll");
|
|
453
|
+
}
|
|
449
454
|
const splitJobs = Array.isArray(riddle.split_jobs) ? riddle.split_jobs.map(cliRecord).filter((job) => Boolean(job)) : [];
|
|
450
455
|
for (const job of splitJobs.slice(0, 12)) {
|
|
451
456
|
const viewport = cliString(job.viewport) || "viewport";
|
|
@@ -454,13 +459,15 @@ function profileRiddleJobMarkdown(result) {
|
|
|
454
459
|
const splitTerminal = typeof job.terminal === "boolean" ? job.terminal : void 0;
|
|
455
460
|
const splitElapsedMs = cliFiniteNumber(job.elapsed_ms);
|
|
456
461
|
const splitPreSubmissionElapsedMs = cliFiniteNumber(job.pre_submission_elapsed_ms);
|
|
462
|
+
const splitArtifactRecovery = job.artifact_recovery === true;
|
|
457
463
|
lines.push(
|
|
458
464
|
`- ${viewport}: ${[
|
|
459
465
|
splitJobId ? `job ${markdownInlineCode(splitJobId)}` : "",
|
|
460
466
|
splitStatus ? `status ${markdownInlineCode(splitStatus)}` : "",
|
|
461
467
|
splitTerminal === void 0 ? "" : `terminal ${splitTerminal ? "true" : "false"}`,
|
|
462
468
|
splitElapsedMs === void 0 ? "" : `elapsed ${formatPollDuration(splitElapsedMs)}`,
|
|
463
|
-
splitPreSubmissionElapsedMs === void 0 || splitPreSubmissionElapsedMs < 1e3 ? "" : `pre-submit ${formatPollDuration(splitPreSubmissionElapsedMs)}
|
|
469
|
+
splitPreSubmissionElapsedMs === void 0 || splitPreSubmissionElapsedMs < 1e3 ? "" : `pre-submit ${formatPollDuration(splitPreSubmissionElapsedMs)}`,
|
|
470
|
+
splitArtifactRecovery ? "artifact recovery" : ""
|
|
464
471
|
].filter(Boolean).join(", ") || "job metadata unavailable"}`
|
|
465
472
|
);
|
|
466
473
|
}
|
|
@@ -507,6 +514,14 @@ function profileCheckMarkdownTarget(check) {
|
|
|
507
514
|
if (check.type === "selector_text_order") {
|
|
508
515
|
return selector ? `${markdownInlineCode(selector)} text order` : void 0;
|
|
509
516
|
}
|
|
517
|
+
if (check.type === "observe_within") {
|
|
518
|
+
const textTarget = profileCheckTextTarget(evidence);
|
|
519
|
+
const timeoutMs = cliFiniteNumber(evidence.timeout_ms);
|
|
520
|
+
const withinLabel = timeoutMs === void 0 ? "within timeout" : `within ${timeoutMs}ms`;
|
|
521
|
+
if (selector && textTarget) return `${markdownInlineCode(selector)} observes ${textTarget} ${withinLabel}`;
|
|
522
|
+
if (selector) return `${markdownInlineCode(selector)} visible ${withinLabel}`;
|
|
523
|
+
return textTarget ? `${textTarget} ${withinLabel}` : withinLabel;
|
|
524
|
+
}
|
|
510
525
|
if (check.type === "text_visible" || check.type === "text_absent") {
|
|
511
526
|
return profileCheckTextTarget(evidence);
|
|
512
527
|
}
|
|
@@ -1092,7 +1107,8 @@ function withRiddleMetadata(result, input) {
|
|
|
1092
1107
|
elapsed_ms: poll?.elapsed_ms ?? result.riddle?.elapsed_ms,
|
|
1093
1108
|
attempt: poll?.attempt ?? result.riddle?.attempt,
|
|
1094
1109
|
attempts: poll?.attempts ?? result.riddle?.attempts,
|
|
1095
|
-
timed_out: poll?.timed_out ?? result.riddle?.timed_out
|
|
1110
|
+
timed_out: poll?.timed_out ?? result.riddle?.timed_out,
|
|
1111
|
+
artifact_recovery: input.artifactRecovery ?? result.riddle?.artifact_recovery
|
|
1096
1112
|
},
|
|
1097
1113
|
artifacts: {
|
|
1098
1114
|
...result.artifacts,
|
|
@@ -1100,6 +1116,52 @@ function withRiddleMetadata(result, input) {
|
|
|
1100
1116
|
}
|
|
1101
1117
|
};
|
|
1102
1118
|
}
|
|
1119
|
+
function riddleArtifactsPayloadStatus(payload) {
|
|
1120
|
+
const record = cliRecord(payload);
|
|
1121
|
+
return cliString(record?.status) ?? cliString(cliRecord(record?.job)?.status);
|
|
1122
|
+
}
|
|
1123
|
+
async function recoverProfileResultFromRiddleArtifacts(profile, input) {
|
|
1124
|
+
if (input.poll.poll?.timed_out !== true) return void 0;
|
|
1125
|
+
let artifactPayload;
|
|
1126
|
+
try {
|
|
1127
|
+
artifactPayload = await input.client.requestJson(`/v1/jobs/${input.jobId}/artifacts`);
|
|
1128
|
+
} catch {
|
|
1129
|
+
return void 0;
|
|
1130
|
+
}
|
|
1131
|
+
const artifacts = collectRiddleProfileArtifactRefs(artifactPayload);
|
|
1132
|
+
if (!artifacts.length) return void 0;
|
|
1133
|
+
const artifactStatus = riddleArtifactsPayloadStatus(artifactPayload);
|
|
1134
|
+
const terminal = artifactStatus ? isTerminalRiddleJobStatus(artifactStatus) : true;
|
|
1135
|
+
const recoveredPoll = input.poll.poll ? {
|
|
1136
|
+
...input.poll.poll,
|
|
1137
|
+
status: artifactStatus ?? input.poll.poll.status,
|
|
1138
|
+
terminal
|
|
1139
|
+
} : void 0;
|
|
1140
|
+
const artifactResult = await profileResultFromRiddleArtifacts(profile, artifacts, [artifactPayload, input.poll.job]);
|
|
1141
|
+
if (artifactResult) {
|
|
1142
|
+
return withRiddleMetadata(artifactResult, {
|
|
1143
|
+
job_id: input.jobId,
|
|
1144
|
+
status: artifactStatus ?? input.poll.status,
|
|
1145
|
+
terminal,
|
|
1146
|
+
poll: recoveredPoll,
|
|
1147
|
+
artifacts,
|
|
1148
|
+
artifactRecovery: true
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
if (!terminal) return void 0;
|
|
1152
|
+
return createRiddleProofProfileInsufficientResult({
|
|
1153
|
+
profile,
|
|
1154
|
+
runner: input.runner,
|
|
1155
|
+
error: `Riddle job ${input.jobId} timed out in status ${input.poll.status || "unknown"}, but artifacts were recovered without a proof result.`,
|
|
1156
|
+
riddle: {
|
|
1157
|
+
...riddleMetadataFromPoll(input.jobId, input.poll),
|
|
1158
|
+
status: artifactStatus ?? input.poll.status,
|
|
1159
|
+
terminal,
|
|
1160
|
+
artifact_recovery: true
|
|
1161
|
+
},
|
|
1162
|
+
artifacts
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1103
1165
|
function riddleMetadataFromPoll(jobId, poll) {
|
|
1104
1166
|
return {
|
|
1105
1167
|
job_id: jobId,
|
|
@@ -1162,13 +1224,15 @@ function splitViewportRiddleMetadata(childRuns) {
|
|
|
1162
1224
|
elapsed_ms: result.riddle?.elapsed_ms,
|
|
1163
1225
|
attempt: result.riddle?.attempt,
|
|
1164
1226
|
attempts: result.riddle?.attempts,
|
|
1165
|
-
timed_out: result.riddle?.timed_out
|
|
1227
|
+
timed_out: result.riddle?.timed_out,
|
|
1228
|
+
artifact_recovery: result.riddle?.artifact_recovery
|
|
1166
1229
|
}));
|
|
1167
1230
|
return {
|
|
1168
1231
|
mode: "split-viewports",
|
|
1169
1232
|
job_count: childRuns.length,
|
|
1170
1233
|
status: "split-viewports",
|
|
1171
1234
|
terminal: childRuns.every(({ result }) => result.riddle?.terminal !== false),
|
|
1235
|
+
artifact_recovery: childRuns.some(({ result }) => result.riddle?.artifact_recovery === true),
|
|
1172
1236
|
queue_elapsed_ms: sumDefinedNumbers(splitJobs.map((job) => job.queue_elapsed_ms)),
|
|
1173
1237
|
pre_submission_elapsed_ms: sumDefinedNumbers(splitJobs.map((job) => job.pre_submission_elapsed_ms)),
|
|
1174
1238
|
elapsed_ms: sumDefinedNumbers(splitJobs.map((job) => job.elapsed_ms)),
|
|
@@ -1291,6 +1355,13 @@ async function runSingleRiddleProfileForCli(profile, options, input) {
|
|
|
1291
1355
|
});
|
|
1292
1356
|
const artifacts = collectRiddleProfileArtifactRefs(poll.artifacts);
|
|
1293
1357
|
if (!poll.ok || !poll.terminal) {
|
|
1358
|
+
const recoveredResult = await recoverProfileResultFromRiddleArtifacts(profile, {
|
|
1359
|
+
client,
|
|
1360
|
+
runner,
|
|
1361
|
+
jobId,
|
|
1362
|
+
poll
|
|
1363
|
+
});
|
|
1364
|
+
if (recoveredResult) return recoveredResult;
|
|
1294
1365
|
return createRiddleProofProfileEnvironmentBlockedResult({
|
|
1295
1366
|
profile,
|
|
1296
1367
|
runner,
|