@riddledc/riddle-proof 0.7.145 → 0.7.147
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 +8 -5
- package/dist/{chunk-7GHBRZHQ.js → chunk-M3ZTY6PQ.js} +14 -3
- package/dist/cli.cjs +229 -11
- package/dist/cli.js +216 -9
- package/dist/index.cjs +14 -3
- package/dist/index.js +1 -1
- package/dist/profile.d.cts +15 -0
- package/dist/profile.d.ts +15 -0
- package/dist/riddle-client.cjs +14 -3
- package/dist/riddle-client.d.cts +1 -0
- package/dist/riddle-client.d.ts +1 -0
- package/dist/riddle-client.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -941,11 +941,14 @@ shape, and errors without rerunning blind.
|
|
|
941
941
|
|
|
942
942
|
`riddle-proof-loop riddle-poll <job-id> --wait` keeps stdout as JSON and writes
|
|
943
943
|
human progress lines to stderr while waiting. The JSON result includes
|
|
944
|
-
`poll.timed_out`, `poll.elapsed_ms`, `poll.queue_elapsed_ms`,
|
|
945
|
-
`poll.running_without_submission` so
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
944
|
+
`poll.timed_out`, `poll.elapsed_ms`, `poll.queue_elapsed_ms`,
|
|
945
|
+
`poll.pre_submission_elapsed_ms`, and `poll.running_without_submission` so
|
|
946
|
+
delayed dispatch is distinguishable from a terminal proof failure.
|
|
947
|
+
`queue_elapsed_ms` reflects Riddle's `created_at` to `submitted_at` timestamps;
|
|
948
|
+
`pre_submission_elapsed_ms` preserves how long the CLI actually observed the
|
|
949
|
+
job before `submitted_at` appeared. If `--wait` exhausts its attempts before a
|
|
950
|
+
terminal job status, the command exits non-zero and the result explains the
|
|
951
|
+
last observed status and `submitted_at` state.
|
|
949
952
|
|
|
950
953
|
## Base vs OpenClaw Wrapper Boundary
|
|
951
954
|
|
|
@@ -279,6 +279,7 @@ function buildPollSnapshot(jobId, job, input) {
|
|
|
279
279
|
submitted_at: submittedAt,
|
|
280
280
|
completed_at: completedAt,
|
|
281
281
|
queue_elapsed_ms: queueElapsedMs,
|
|
282
|
+
pre_submission_elapsed_ms: Math.max(0, Math.floor(input.preSubmissionElapsedMs ?? 0)),
|
|
282
283
|
running_without_submission: Boolean(status && !terminal && !submittedAt)
|
|
283
284
|
};
|
|
284
285
|
}
|
|
@@ -286,7 +287,8 @@ function pollMessage(snapshot, timedOut) {
|
|
|
286
287
|
if (!timedOut) return void 0;
|
|
287
288
|
const submitted = snapshot.submitted_at || "not submitted";
|
|
288
289
|
const queue = snapshot.queue_elapsed_ms !== null ? ` queue_elapsed_ms=${snapshot.queue_elapsed_ms}` : "";
|
|
289
|
-
|
|
290
|
+
const preSubmit = snapshot.pre_submission_elapsed_ms > 0 ? ` pre_submission_elapsed_ms=${snapshot.pre_submission_elapsed_ms}` : "";
|
|
291
|
+
return `Riddle job ${snapshot.job_id} did not reach a terminal status after ${snapshot.attempt} poll attempts; status=${snapshot.status || "unknown"} submitted_at=${submitted}.${queue}${preSubmit}`;
|
|
290
292
|
}
|
|
291
293
|
async function pollRiddleJob(config, jobId, options = {}) {
|
|
292
294
|
if (!jobId?.trim()) throw new Error("jobId is required");
|
|
@@ -298,15 +300,24 @@ async function pollRiddleJob(config, jobId, options = {}) {
|
|
|
298
300
|
let lastSnapshot = null;
|
|
299
301
|
let lastProgressAt = 0;
|
|
300
302
|
let lastProgressKey = "";
|
|
303
|
+
let preSubmissionElapsedMs = 0;
|
|
301
304
|
for (let index = 0; index < attempts; index += 1) {
|
|
302
305
|
job = await riddleRequestJson(config, `/v1/jobs/${jobId}`);
|
|
303
306
|
const observedAt = Date.now();
|
|
304
|
-
|
|
307
|
+
const nextSnapshot = buildPollSnapshot(jobId, job, {
|
|
305
308
|
attempt: index + 1,
|
|
306
309
|
attempts,
|
|
307
310
|
startedAt,
|
|
308
|
-
observedAt
|
|
311
|
+
observedAt,
|
|
312
|
+
preSubmissionElapsedMs
|
|
309
313
|
});
|
|
314
|
+
if (nextSnapshot.running_without_submission) {
|
|
315
|
+
preSubmissionElapsedMs = Math.max(preSubmissionElapsedMs, nextSnapshot.elapsed_ms);
|
|
316
|
+
}
|
|
317
|
+
lastSnapshot = {
|
|
318
|
+
...nextSnapshot,
|
|
319
|
+
pre_submission_elapsed_ms: preSubmissionElapsedMs
|
|
320
|
+
};
|
|
310
321
|
const progressKey = [
|
|
311
322
|
lastSnapshot.status || "unknown",
|
|
312
323
|
lastSnapshot.terminal ? "terminal" : "nonterminal",
|
package/dist/cli.cjs
CHANGED
|
@@ -6840,6 +6840,7 @@ function buildPollSnapshot(jobId, job, input) {
|
|
|
6840
6840
|
submitted_at: submittedAt,
|
|
6841
6841
|
completed_at: completedAt,
|
|
6842
6842
|
queue_elapsed_ms: queueElapsedMs,
|
|
6843
|
+
pre_submission_elapsed_ms: Math.max(0, Math.floor(input.preSubmissionElapsedMs ?? 0)),
|
|
6843
6844
|
running_without_submission: Boolean(status && !terminal && !submittedAt)
|
|
6844
6845
|
};
|
|
6845
6846
|
}
|
|
@@ -6847,7 +6848,8 @@ function pollMessage(snapshot, timedOut) {
|
|
|
6847
6848
|
if (!timedOut) return void 0;
|
|
6848
6849
|
const submitted = snapshot.submitted_at || "not submitted";
|
|
6849
6850
|
const queue = snapshot.queue_elapsed_ms !== null ? ` queue_elapsed_ms=${snapshot.queue_elapsed_ms}` : "";
|
|
6850
|
-
|
|
6851
|
+
const preSubmit = snapshot.pre_submission_elapsed_ms > 0 ? ` pre_submission_elapsed_ms=${snapshot.pre_submission_elapsed_ms}` : "";
|
|
6852
|
+
return `Riddle job ${snapshot.job_id} did not reach a terminal status after ${snapshot.attempt} poll attempts; status=${snapshot.status || "unknown"} submitted_at=${submitted}.${queue}${preSubmit}`;
|
|
6851
6853
|
}
|
|
6852
6854
|
async function pollRiddleJob(config, jobId, options = {}) {
|
|
6853
6855
|
if (!jobId?.trim()) throw new Error("jobId is required");
|
|
@@ -6859,15 +6861,24 @@ async function pollRiddleJob(config, jobId, options = {}) {
|
|
|
6859
6861
|
let lastSnapshot = null;
|
|
6860
6862
|
let lastProgressAt = 0;
|
|
6861
6863
|
let lastProgressKey = "";
|
|
6864
|
+
let preSubmissionElapsedMs = 0;
|
|
6862
6865
|
for (let index = 0; index < attempts; index += 1) {
|
|
6863
6866
|
job = await riddleRequestJson(config, `/v1/jobs/${jobId}`);
|
|
6864
6867
|
const observedAt = Date.now();
|
|
6865
|
-
|
|
6868
|
+
const nextSnapshot = buildPollSnapshot(jobId, job, {
|
|
6866
6869
|
attempt: index + 1,
|
|
6867
6870
|
attempts,
|
|
6868
6871
|
startedAt,
|
|
6869
|
-
observedAt
|
|
6872
|
+
observedAt,
|
|
6873
|
+
preSubmissionElapsedMs
|
|
6870
6874
|
});
|
|
6875
|
+
if (nextSnapshot.running_without_submission) {
|
|
6876
|
+
preSubmissionElapsedMs = Math.max(preSubmissionElapsedMs, nextSnapshot.elapsed_ms);
|
|
6877
|
+
}
|
|
6878
|
+
lastSnapshot = {
|
|
6879
|
+
...nextSnapshot,
|
|
6880
|
+
pre_submission_elapsed_ms: preSubmissionElapsedMs
|
|
6881
|
+
};
|
|
6871
6882
|
const progressKey = [
|
|
6872
6883
|
lastSnapshot.status || "unknown",
|
|
6873
6884
|
lastSnapshot.terminal ? "terminal" : "nonterminal",
|
|
@@ -6938,6 +6949,7 @@ function createRiddleApiClient(config = {}) {
|
|
|
6938
6949
|
|
|
6939
6950
|
// src/profile.ts
|
|
6940
6951
|
var RIDDLE_PROOF_PROFILE_VERSION = "riddle-proof.profile.v1";
|
|
6952
|
+
var RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION = "riddle-proof.profile-evidence.v1";
|
|
6941
6953
|
var RIDDLE_PROOF_PROFILE_RESULT_VERSION = "riddle-proof.profile-result.v1";
|
|
6942
6954
|
var RIDDLE_PROOF_PROFILE_STATUSES = [
|
|
6943
6955
|
"passed",
|
|
@@ -14380,7 +14392,7 @@ function usage() {
|
|
|
14380
14392
|
" riddle-proof-loop respond --state-path <path> --response-json <file|json|->",
|
|
14381
14393
|
" riddle-proof-loop respond --state-path <path> --decision <decision> --summary <text> [--payload-json <file|json|->]",
|
|
14382
14394
|
" riddle-proof-loop status --state-path <path>",
|
|
14383
|
-
" riddle-proof-loop run-profile --profile <file|json|-> --url <base-url> [--runner riddle] [--strict true|false; default false] [--poll-attempts n] [--output <dir>|--output-dir <dir>] [--quiet]",
|
|
14395
|
+
" riddle-proof-loop run-profile --profile <file|json|-> --url <base-url> [--runner riddle] [--strict true|false; default false] [--split-viewports true|false; default false] [--poll-attempts n] [--output <dir>|--output-dir <dir>] [--quiet]",
|
|
14384
14396
|
" riddle-proof-loop profile-body-assertions --artifact <file|url|-> --candidates-json <file|json|-> [--required-json <file|json|->] [--format json|body-contains]",
|
|
14385
14397
|
" riddle-proof-loop profile-http-status-preflight --profile <file|json|-> --url <base-url> [--format json|summary]",
|
|
14386
14398
|
" riddle-proof-loop riddle-preview-deploy <build-dir> <label> [--framework spa|static]",
|
|
@@ -14437,6 +14449,9 @@ function optionBoolean(options, key) {
|
|
|
14437
14449
|
function runProfileStrictOption(options) {
|
|
14438
14450
|
return optionBoolean(options, "strict") ?? false;
|
|
14439
14451
|
}
|
|
14452
|
+
function runProfileSplitViewportsOption(options) {
|
|
14453
|
+
return optionBoolean(options, "splitViewports") ?? false;
|
|
14454
|
+
}
|
|
14440
14455
|
function optionNumber(options, ...keys) {
|
|
14441
14456
|
for (const key of keys) {
|
|
14442
14457
|
const value = optionString(options, key);
|
|
@@ -14476,7 +14491,7 @@ function formatPollDuration(ms) {
|
|
|
14476
14491
|
}
|
|
14477
14492
|
function riddlePollProgressLine(snapshot) {
|
|
14478
14493
|
const submittedAt = snapshot.submitted_at || "not-submitted";
|
|
14479
|
-
const queuePart = snapshot.running_without_submission ? ` queued_for=${formatPollDuration(snapshot.queue_elapsed_ms)}` : snapshot.queue_elapsed_ms !== null ? ` queue=${formatPollDuration(snapshot.queue_elapsed_ms)}` : "";
|
|
14494
|
+
const queuePart = snapshot.running_without_submission ? ` waiting_for_submit=${formatPollDuration(snapshot.pre_submission_elapsed_ms)}${snapshot.queue_elapsed_ms !== null ? ` queued_for=${formatPollDuration(snapshot.queue_elapsed_ms)}` : ""}` : snapshot.queue_elapsed_ms !== null ? ` queue=${formatPollDuration(snapshot.queue_elapsed_ms)}` : "";
|
|
14480
14495
|
const terminalPart = snapshot.terminal ? " terminal=true" : "";
|
|
14481
14496
|
return [
|
|
14482
14497
|
"[riddle-poll]",
|
|
@@ -14749,15 +14764,20 @@ function profileRiddleJobMarkdown(result) {
|
|
|
14749
14764
|
const riddle = cliRecord(result.riddle);
|
|
14750
14765
|
if (!riddle) return [];
|
|
14751
14766
|
const jobId = cliString(riddle.job_id);
|
|
14767
|
+
const mode = cliString(riddle.mode);
|
|
14768
|
+
const jobCount = cliFiniteNumber(riddle.job_count);
|
|
14752
14769
|
const status = cliString(riddle.status);
|
|
14753
14770
|
const terminal = typeof riddle.terminal === "boolean" ? riddle.terminal : void 0;
|
|
14754
14771
|
const queueElapsedMs = cliFiniteNumber(riddle.queue_elapsed_ms);
|
|
14755
14772
|
const elapsedMs3 = cliFiniteNumber(riddle.elapsed_ms);
|
|
14773
|
+
const preSubmissionElapsedMs = cliFiniteNumber(riddle.pre_submission_elapsed_ms);
|
|
14756
14774
|
const attempt = cliFiniteNumber(riddle.attempt);
|
|
14757
14775
|
const attempts = cliFiniteNumber(riddle.attempts);
|
|
14758
14776
|
const submittedAt = cliString(riddle.submitted_at);
|
|
14759
14777
|
const completedAt = cliString(riddle.completed_at);
|
|
14760
14778
|
const parts = [
|
|
14779
|
+
mode ? `mode ${markdownInlineCode(mode)}` : "",
|
|
14780
|
+
jobCount === void 0 ? "" : `jobs ${jobCount}`,
|
|
14761
14781
|
jobId ? `job ${markdownInlineCode(jobId)}` : "",
|
|
14762
14782
|
status ? `status ${markdownInlineCode(status)}` : "",
|
|
14763
14783
|
terminal === void 0 ? "" : `terminal ${terminal ? "true" : "false"}`
|
|
@@ -14765,12 +14785,31 @@ function profileRiddleJobMarkdown(result) {
|
|
|
14765
14785
|
const lines = parts.length ? [`- ${parts.join(", ")}`] : [];
|
|
14766
14786
|
if (queueElapsedMs !== void 0 || elapsedMs3 !== void 0 || attempt !== void 0 || attempts !== void 0) {
|
|
14767
14787
|
lines.push(
|
|
14768
|
-
`- poll: queue ${formatPollDuration(queueElapsedMs)}, elapsed ${formatPollDuration(elapsedMs3)}${attempt === void 0 ? "" : `, attempt ${attempt}${attempts === void 0 ? "" : `/${attempts}`}`}`
|
|
14788
|
+
`- poll: queue ${formatPollDuration(queueElapsedMs)}, elapsed ${formatPollDuration(elapsedMs3)}${preSubmissionElapsedMs === void 0 || preSubmissionElapsedMs < 1e3 ? "" : `, pre-submit ${formatPollDuration(preSubmissionElapsedMs)}`}${attempt === void 0 ? "" : `, attempt ${attempt}${attempts === void 0 ? "" : `/${attempts}`}`}`
|
|
14769
14789
|
);
|
|
14770
14790
|
}
|
|
14771
14791
|
if (submittedAt || completedAt) {
|
|
14772
14792
|
lines.push(`- timing:${submittedAt ? ` submitted ${markdownInlineCode(submittedAt)}` : ""}${completedAt ? ` completed ${markdownInlineCode(completedAt)}` : ""}`);
|
|
14773
14793
|
}
|
|
14794
|
+
const splitJobs = Array.isArray(riddle.split_jobs) ? riddle.split_jobs.map(cliRecord).filter((job) => Boolean(job)) : [];
|
|
14795
|
+
for (const job of splitJobs.slice(0, 12)) {
|
|
14796
|
+
const viewport = cliString(job.viewport) || "viewport";
|
|
14797
|
+
const splitJobId = cliString(job.job_id);
|
|
14798
|
+
const splitStatus = cliString(job.status);
|
|
14799
|
+
const splitTerminal = typeof job.terminal === "boolean" ? job.terminal : void 0;
|
|
14800
|
+
const splitElapsedMs = cliFiniteNumber(job.elapsed_ms);
|
|
14801
|
+
const splitPreSubmissionElapsedMs = cliFiniteNumber(job.pre_submission_elapsed_ms);
|
|
14802
|
+
lines.push(
|
|
14803
|
+
`- ${viewport}: ${[
|
|
14804
|
+
splitJobId ? `job ${markdownInlineCode(splitJobId)}` : "",
|
|
14805
|
+
splitStatus ? `status ${markdownInlineCode(splitStatus)}` : "",
|
|
14806
|
+
splitTerminal === void 0 ? "" : `terminal ${splitTerminal ? "true" : "false"}`,
|
|
14807
|
+
splitElapsedMs === void 0 ? "" : `elapsed ${formatPollDuration(splitElapsedMs)}`,
|
|
14808
|
+
splitPreSubmissionElapsedMs === void 0 || splitPreSubmissionElapsedMs < 1e3 ? "" : `pre-submit ${formatPollDuration(splitPreSubmissionElapsedMs)}`
|
|
14809
|
+
].filter(Boolean).join(", ") || "job metadata unavailable"}`
|
|
14810
|
+
);
|
|
14811
|
+
}
|
|
14812
|
+
if (splitJobs.length > 12) lines.push(`- ${splitJobs.length - 12} additional split job(s) omitted.`);
|
|
14774
14813
|
return lines;
|
|
14775
14814
|
}
|
|
14776
14815
|
function markdownInlineCode(value, maxLength = 80) {
|
|
@@ -15370,6 +15409,7 @@ function withRiddleMetadata(result, input) {
|
|
|
15370
15409
|
submitted_at: poll?.submitted_at ?? result.riddle?.submitted_at,
|
|
15371
15410
|
completed_at: poll?.completed_at ?? result.riddle?.completed_at,
|
|
15372
15411
|
queue_elapsed_ms: poll?.queue_elapsed_ms ?? result.riddle?.queue_elapsed_ms,
|
|
15412
|
+
pre_submission_elapsed_ms: poll?.pre_submission_elapsed_ms ?? result.riddle?.pre_submission_elapsed_ms,
|
|
15373
15413
|
elapsed_ms: poll?.elapsed_ms ?? result.riddle?.elapsed_ms,
|
|
15374
15414
|
attempt: poll?.attempt ?? result.riddle?.attempt,
|
|
15375
15415
|
attempts: poll?.attempts ?? result.riddle?.attempts,
|
|
@@ -15390,19 +15430,155 @@ function riddleMetadataFromPoll(jobId, poll) {
|
|
|
15390
15430
|
submitted_at: poll.poll?.submitted_at,
|
|
15391
15431
|
completed_at: poll.poll?.completed_at,
|
|
15392
15432
|
queue_elapsed_ms: poll.poll?.queue_elapsed_ms,
|
|
15433
|
+
pre_submission_elapsed_ms: poll.poll?.pre_submission_elapsed_ms,
|
|
15393
15434
|
elapsed_ms: poll.poll?.elapsed_ms,
|
|
15394
15435
|
attempt: poll.poll?.attempt,
|
|
15395
15436
|
attempts: poll.poll?.attempts,
|
|
15396
15437
|
timed_out: poll.poll?.timed_out
|
|
15397
15438
|
};
|
|
15398
15439
|
}
|
|
15399
|
-
|
|
15400
|
-
|
|
15401
|
-
|
|
15402
|
-
|
|
15440
|
+
function profileForSplitViewport(profile, viewport) {
|
|
15441
|
+
return {
|
|
15442
|
+
...profile,
|
|
15443
|
+
name: `${profile.name}-${viewport.name || `${viewport.width}x${viewport.height}`}`,
|
|
15444
|
+
target: {
|
|
15445
|
+
...profile.target,
|
|
15446
|
+
viewports: [viewport]
|
|
15447
|
+
},
|
|
15448
|
+
metadata: {
|
|
15449
|
+
...profile.metadata || {},
|
|
15450
|
+
split_parent_profile: profile.name,
|
|
15451
|
+
split_viewport: viewport.name
|
|
15452
|
+
}
|
|
15453
|
+
};
|
|
15454
|
+
}
|
|
15455
|
+
function safeProfileOutputSegment(value) {
|
|
15456
|
+
const safe = value.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
15457
|
+
return safe || "viewport";
|
|
15458
|
+
}
|
|
15459
|
+
function splitViewportOutputDir(outputDir, viewportName, seen) {
|
|
15460
|
+
const base = safeProfileOutputSegment(viewportName);
|
|
15461
|
+
const count = seen.get(base) || 0;
|
|
15462
|
+
seen.set(base, count + 1);
|
|
15463
|
+
return import_node_path6.default.join(outputDir, count ? `${base}-${count + 1}` : base);
|
|
15464
|
+
}
|
|
15465
|
+
function splitViewportArtifactRefs(input) {
|
|
15466
|
+
return (input.result.artifacts.riddle_artifacts || []).map((artifact) => ({
|
|
15467
|
+
...artifact,
|
|
15468
|
+
name: `${safeProfileOutputSegment(input.viewport.name)}/${artifact.name || artifact.kind || "artifact"}`
|
|
15469
|
+
}));
|
|
15470
|
+
}
|
|
15471
|
+
function sumDefinedNumbers(values) {
|
|
15472
|
+
const numbers = values.filter((value) => typeof value === "number" && Number.isFinite(value));
|
|
15473
|
+
return numbers.length ? numbers.reduce((sum, value) => sum + value, 0) : void 0;
|
|
15474
|
+
}
|
|
15475
|
+
function splitViewportRiddleMetadata(childRuns) {
|
|
15476
|
+
const splitJobs = childRuns.map(({ viewport, result }) => ({
|
|
15477
|
+
viewport: viewport.name,
|
|
15478
|
+
job_id: result.riddle?.job_id,
|
|
15479
|
+
status: result.riddle?.status,
|
|
15480
|
+
terminal: result.riddle?.terminal,
|
|
15481
|
+
queue_elapsed_ms: result.riddle?.queue_elapsed_ms,
|
|
15482
|
+
pre_submission_elapsed_ms: result.riddle?.pre_submission_elapsed_ms,
|
|
15483
|
+
elapsed_ms: result.riddle?.elapsed_ms,
|
|
15484
|
+
attempt: result.riddle?.attempt,
|
|
15485
|
+
attempts: result.riddle?.attempts,
|
|
15486
|
+
timed_out: result.riddle?.timed_out
|
|
15487
|
+
}));
|
|
15488
|
+
return {
|
|
15489
|
+
mode: "split-viewports",
|
|
15490
|
+
job_count: childRuns.length,
|
|
15491
|
+
status: "split-viewports",
|
|
15492
|
+
terminal: childRuns.every(({ result }) => result.riddle?.terminal !== false),
|
|
15493
|
+
queue_elapsed_ms: sumDefinedNumbers(splitJobs.map((job) => job.queue_elapsed_ms)),
|
|
15494
|
+
pre_submission_elapsed_ms: sumDefinedNumbers(splitJobs.map((job) => job.pre_submission_elapsed_ms)),
|
|
15495
|
+
elapsed_ms: sumDefinedNumbers(splitJobs.map((job) => job.elapsed_ms)),
|
|
15496
|
+
split_jobs: splitJobs
|
|
15497
|
+
};
|
|
15498
|
+
}
|
|
15499
|
+
function latestCapturedAt(evidences) {
|
|
15500
|
+
let latest = "";
|
|
15501
|
+
let latestMs = Number.NEGATIVE_INFINITY;
|
|
15502
|
+
for (const evidence of evidences) {
|
|
15503
|
+
const parsed = Date.parse(evidence.captured_at);
|
|
15504
|
+
if (Number.isFinite(parsed) && parsed >= latestMs) {
|
|
15505
|
+
latest = evidence.captured_at;
|
|
15506
|
+
latestMs = parsed;
|
|
15507
|
+
}
|
|
15403
15508
|
}
|
|
15509
|
+
return latest || (/* @__PURE__ */ new Date()).toISOString();
|
|
15510
|
+
}
|
|
15511
|
+
function splitViewportDomSummary(profile, childRuns, evidence) {
|
|
15512
|
+
return {
|
|
15513
|
+
split_viewports: true,
|
|
15514
|
+
expected_viewport_count: profile.target.viewports.length,
|
|
15515
|
+
viewport_count: evidence.viewports.length,
|
|
15516
|
+
child_result_count: childRuns.length,
|
|
15517
|
+
child_statuses: childRuns.map(({ viewport, result }) => ({
|
|
15518
|
+
viewport: viewport.name,
|
|
15519
|
+
profile_name: result.profile_name,
|
|
15520
|
+
status: result.status,
|
|
15521
|
+
job_id: result.riddle?.job_id || null
|
|
15522
|
+
})),
|
|
15523
|
+
routes: evidence.viewports.map((viewport) => ({
|
|
15524
|
+
viewport: viewport.name,
|
|
15525
|
+
requested: viewport.route.requested,
|
|
15526
|
+
observed: viewport.route.observed,
|
|
15527
|
+
matched: viewport.route.matched,
|
|
15528
|
+
http_status: viewport.route.http_status ?? null
|
|
15529
|
+
})),
|
|
15530
|
+
titles: evidence.viewports.map((viewport) => viewport.title).filter((title) => Boolean(title)),
|
|
15531
|
+
overflow_px: evidence.viewports.map((viewport) => viewport.overflow_px ?? null),
|
|
15532
|
+
bounds_overflow_px: evidence.viewports.map((viewport) => viewport.bounds_overflow_px ?? null),
|
|
15533
|
+
overflow_offender_counts: evidence.viewports.map((viewport) => (viewport.overflow_offenders || []).length),
|
|
15534
|
+
console_event_count: evidence.console.events.length,
|
|
15535
|
+
console_fatal_count: evidence.console.fatal_count,
|
|
15536
|
+
page_error_count: evidence.page_errors.length,
|
|
15537
|
+
network_mock_count: (profile.target.network_mocks || []).length,
|
|
15538
|
+
network_mock_hit_count: (evidence.network_mocks || []).filter((event) => event.ok !== false).length
|
|
15539
|
+
};
|
|
15540
|
+
}
|
|
15541
|
+
function aggregateSplitViewportEvidence(profile, childRuns) {
|
|
15542
|
+
const evidences = childRuns.map(({ result }) => result.evidence).filter((evidence2) => Boolean(evidence2));
|
|
15543
|
+
const viewports = evidences.flatMap((evidence2) => evidence2.viewports || []);
|
|
15544
|
+
const consoleEvents = evidences.flatMap((evidence2) => evidence2.console?.events || []);
|
|
15545
|
+
const pageErrors = evidences.flatMap((evidence2) => evidence2.page_errors || []);
|
|
15546
|
+
const networkMocks = evidences.flatMap((evidence2) => evidence2.network_mocks || []);
|
|
15547
|
+
const evidence = {
|
|
15548
|
+
version: RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION,
|
|
15549
|
+
profile_name: profile.name,
|
|
15550
|
+
target_url: resolveRiddleProofProfileTargetUrl(profile),
|
|
15551
|
+
baseline_policy: profile.baseline_policy,
|
|
15552
|
+
captured_at: latestCapturedAt(evidences),
|
|
15553
|
+
viewports,
|
|
15554
|
+
console: {
|
|
15555
|
+
events: consoleEvents,
|
|
15556
|
+
fatal_count: evidences.reduce((sum, item) => sum + (item.console?.fatal_count || 0), 0)
|
|
15557
|
+
},
|
|
15558
|
+
page_errors: pageErrors,
|
|
15559
|
+
network_mocks: networkMocks.length ? networkMocks : void 0
|
|
15560
|
+
};
|
|
15561
|
+
evidence.dom_summary = splitViewportDomSummary(profile, childRuns, evidence);
|
|
15562
|
+
return evidence;
|
|
15563
|
+
}
|
|
15564
|
+
function splitViewportBlockedMessage(childRuns) {
|
|
15565
|
+
const blocked = childRuns.filter(({ result }) => !result.evidence || result.status === "environment_blocked" || result.status === "configuration_error").map(({ viewport, result }) => `${viewport.name}: ${result.status}${result.error ? ` (${result.error})` : ""}`);
|
|
15566
|
+
return `Split viewport run did not produce reliable evidence for ${blocked.join("; ")}.`;
|
|
15567
|
+
}
|
|
15568
|
+
function withSplitViewportWarnings(profile, result) {
|
|
15569
|
+
const warnings = [];
|
|
15570
|
+
if (profile.target.network_mocks?.length) {
|
|
15571
|
+
warnings.push("Split viewport mode runs each viewport in a separate Riddle job; global network mock sequencing is assessed from aggregated events.");
|
|
15572
|
+
}
|
|
15573
|
+
if (!warnings.length) return result;
|
|
15574
|
+
return {
|
|
15575
|
+
...result,
|
|
15576
|
+
warnings: [.../* @__PURE__ */ new Set([...result.warnings || [], ...warnings])]
|
|
15577
|
+
};
|
|
15578
|
+
}
|
|
15579
|
+
async function runSingleRiddleProfileForCli(profile, options, input) {
|
|
15580
|
+
const { client, runner } = input;
|
|
15404
15581
|
const targetUrl = resolveRiddleProofProfileTargetUrl(profile);
|
|
15405
|
-
const client = createRiddleApiClient(riddleClientConfig(options));
|
|
15406
15582
|
let created;
|
|
15407
15583
|
try {
|
|
15408
15584
|
created = await client.runScript({
|
|
@@ -15461,6 +15637,48 @@ async function runProfileForCli(profile, options) {
|
|
|
15461
15637
|
artifacts
|
|
15462
15638
|
});
|
|
15463
15639
|
}
|
|
15640
|
+
async function runSplitViewportProfileForCli(profile, options, input) {
|
|
15641
|
+
const outputDir = profileOutputDirOption(options);
|
|
15642
|
+
const seenOutputNames = /* @__PURE__ */ new Map();
|
|
15643
|
+
const childRuns = [];
|
|
15644
|
+
for (const viewport of profile.target.viewports) {
|
|
15645
|
+
const childProfile = profileForSplitViewport(profile, viewport);
|
|
15646
|
+
const result2 = await runSingleRiddleProfileForCli(childProfile, options, input);
|
|
15647
|
+
if (outputDir) {
|
|
15648
|
+
writeProfileOutput(splitViewportOutputDir(outputDir, viewport.name, seenOutputNames), result2);
|
|
15649
|
+
}
|
|
15650
|
+
childRuns.push({ viewport, profile: childProfile, result: result2 });
|
|
15651
|
+
}
|
|
15652
|
+
const artifacts = childRuns.flatMap(splitViewportArtifactRefs);
|
|
15653
|
+
const blocked = childRuns.filter(({ result: result2 }) => !result2.evidence || result2.status === "environment_blocked" || result2.status === "configuration_error");
|
|
15654
|
+
if (blocked.length) {
|
|
15655
|
+
return createRiddleProofProfileEnvironmentBlockedResult({
|
|
15656
|
+
profile,
|
|
15657
|
+
runner: input.runner,
|
|
15658
|
+
error: splitViewportBlockedMessage(childRuns),
|
|
15659
|
+
riddle: splitViewportRiddleMetadata(childRuns),
|
|
15660
|
+
artifacts
|
|
15661
|
+
});
|
|
15662
|
+
}
|
|
15663
|
+
const evidence = aggregateSplitViewportEvidence(profile, childRuns);
|
|
15664
|
+
const result = assessRiddleProofProfileEvidence(profile, evidence, {
|
|
15665
|
+
runner: input.runner,
|
|
15666
|
+
riddle: splitViewportRiddleMetadata(childRuns),
|
|
15667
|
+
artifacts
|
|
15668
|
+
});
|
|
15669
|
+
return withSplitViewportWarnings(profile, result);
|
|
15670
|
+
}
|
|
15671
|
+
async function runProfileForCli(profile, options) {
|
|
15672
|
+
const runner = optionString(options, "runner") || "riddle";
|
|
15673
|
+
if (runner !== "riddle") {
|
|
15674
|
+
throw new Error(`Unsupported --runner ${runner}. The current CLI supports --runner riddle.`);
|
|
15675
|
+
}
|
|
15676
|
+
const client = createRiddleApiClient(riddleClientConfig(options));
|
|
15677
|
+
if (runProfileSplitViewportsOption(options) && profile.target.viewports.length > 1) {
|
|
15678
|
+
return runSplitViewportProfileForCli(profile, options, { client, runner });
|
|
15679
|
+
}
|
|
15680
|
+
return runSingleRiddleProfileForCli(profile, options, { client, runner });
|
|
15681
|
+
}
|
|
15464
15682
|
function requestForRun(options) {
|
|
15465
15683
|
const statePath = optionString(options, "statePath");
|
|
15466
15684
|
const withEngineModuleUrl = (request) => {
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION,
|
|
3
4
|
assessRiddleProofProfileEvidence,
|
|
4
5
|
buildRiddleProofProfileScript,
|
|
5
6
|
collectRiddleProfileArtifactRefs,
|
|
@@ -16,7 +17,7 @@ import {
|
|
|
16
17
|
import {
|
|
17
18
|
createRiddleApiClient,
|
|
18
19
|
parseRiddleViewport
|
|
19
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-M3ZTY6PQ.js";
|
|
20
21
|
import {
|
|
21
22
|
createDisabledRiddleProofAgentAdapter,
|
|
22
23
|
readRiddleProofRunStatus,
|
|
@@ -46,7 +47,7 @@ function usage() {
|
|
|
46
47
|
" riddle-proof-loop respond --state-path <path> --response-json <file|json|->",
|
|
47
48
|
" riddle-proof-loop respond --state-path <path> --decision <decision> --summary <text> [--payload-json <file|json|->]",
|
|
48
49
|
" riddle-proof-loop status --state-path <path>",
|
|
49
|
-
" riddle-proof-loop run-profile --profile <file|json|-> --url <base-url> [--runner riddle] [--strict true|false; default false] [--poll-attempts n] [--output <dir>|--output-dir <dir>] [--quiet]",
|
|
50
|
+
" riddle-proof-loop run-profile --profile <file|json|-> --url <base-url> [--runner riddle] [--strict true|false; default false] [--split-viewports true|false; default false] [--poll-attempts n] [--output <dir>|--output-dir <dir>] [--quiet]",
|
|
50
51
|
" riddle-proof-loop profile-body-assertions --artifact <file|url|-> --candidates-json <file|json|-> [--required-json <file|json|->] [--format json|body-contains]",
|
|
51
52
|
" riddle-proof-loop profile-http-status-preflight --profile <file|json|-> --url <base-url> [--format json|summary]",
|
|
52
53
|
" riddle-proof-loop riddle-preview-deploy <build-dir> <label> [--framework spa|static]",
|
|
@@ -103,6 +104,9 @@ function optionBoolean(options, key) {
|
|
|
103
104
|
function runProfileStrictOption(options) {
|
|
104
105
|
return optionBoolean(options, "strict") ?? false;
|
|
105
106
|
}
|
|
107
|
+
function runProfileSplitViewportsOption(options) {
|
|
108
|
+
return optionBoolean(options, "splitViewports") ?? false;
|
|
109
|
+
}
|
|
106
110
|
function optionNumber(options, ...keys) {
|
|
107
111
|
for (const key of keys) {
|
|
108
112
|
const value = optionString(options, key);
|
|
@@ -142,7 +146,7 @@ function formatPollDuration(ms) {
|
|
|
142
146
|
}
|
|
143
147
|
function riddlePollProgressLine(snapshot) {
|
|
144
148
|
const submittedAt = snapshot.submitted_at || "not-submitted";
|
|
145
|
-
const queuePart = snapshot.running_without_submission ? ` queued_for=${formatPollDuration(snapshot.queue_elapsed_ms)}` : snapshot.queue_elapsed_ms !== null ? ` queue=${formatPollDuration(snapshot.queue_elapsed_ms)}` : "";
|
|
149
|
+
const queuePart = snapshot.running_without_submission ? ` waiting_for_submit=${formatPollDuration(snapshot.pre_submission_elapsed_ms)}${snapshot.queue_elapsed_ms !== null ? ` queued_for=${formatPollDuration(snapshot.queue_elapsed_ms)}` : ""}` : snapshot.queue_elapsed_ms !== null ? ` queue=${formatPollDuration(snapshot.queue_elapsed_ms)}` : "";
|
|
146
150
|
const terminalPart = snapshot.terminal ? " terminal=true" : "";
|
|
147
151
|
return [
|
|
148
152
|
"[riddle-poll]",
|
|
@@ -415,15 +419,20 @@ function profileRiddleJobMarkdown(result) {
|
|
|
415
419
|
const riddle = cliRecord(result.riddle);
|
|
416
420
|
if (!riddle) return [];
|
|
417
421
|
const jobId = cliString(riddle.job_id);
|
|
422
|
+
const mode = cliString(riddle.mode);
|
|
423
|
+
const jobCount = cliFiniteNumber(riddle.job_count);
|
|
418
424
|
const status = cliString(riddle.status);
|
|
419
425
|
const terminal = typeof riddle.terminal === "boolean" ? riddle.terminal : void 0;
|
|
420
426
|
const queueElapsedMs = cliFiniteNumber(riddle.queue_elapsed_ms);
|
|
421
427
|
const elapsedMs = cliFiniteNumber(riddle.elapsed_ms);
|
|
428
|
+
const preSubmissionElapsedMs = cliFiniteNumber(riddle.pre_submission_elapsed_ms);
|
|
422
429
|
const attempt = cliFiniteNumber(riddle.attempt);
|
|
423
430
|
const attempts = cliFiniteNumber(riddle.attempts);
|
|
424
431
|
const submittedAt = cliString(riddle.submitted_at);
|
|
425
432
|
const completedAt = cliString(riddle.completed_at);
|
|
426
433
|
const parts = [
|
|
434
|
+
mode ? `mode ${markdownInlineCode(mode)}` : "",
|
|
435
|
+
jobCount === void 0 ? "" : `jobs ${jobCount}`,
|
|
427
436
|
jobId ? `job ${markdownInlineCode(jobId)}` : "",
|
|
428
437
|
status ? `status ${markdownInlineCode(status)}` : "",
|
|
429
438
|
terminal === void 0 ? "" : `terminal ${terminal ? "true" : "false"}`
|
|
@@ -431,12 +440,31 @@ function profileRiddleJobMarkdown(result) {
|
|
|
431
440
|
const lines = parts.length ? [`- ${parts.join(", ")}`] : [];
|
|
432
441
|
if (queueElapsedMs !== void 0 || elapsedMs !== void 0 || attempt !== void 0 || attempts !== void 0) {
|
|
433
442
|
lines.push(
|
|
434
|
-
`- poll: queue ${formatPollDuration(queueElapsedMs)}, elapsed ${formatPollDuration(elapsedMs)}${attempt === void 0 ? "" : `, attempt ${attempt}${attempts === void 0 ? "" : `/${attempts}`}`}`
|
|
443
|
+
`- poll: queue ${formatPollDuration(queueElapsedMs)}, elapsed ${formatPollDuration(elapsedMs)}${preSubmissionElapsedMs === void 0 || preSubmissionElapsedMs < 1e3 ? "" : `, pre-submit ${formatPollDuration(preSubmissionElapsedMs)}`}${attempt === void 0 ? "" : `, attempt ${attempt}${attempts === void 0 ? "" : `/${attempts}`}`}`
|
|
435
444
|
);
|
|
436
445
|
}
|
|
437
446
|
if (submittedAt || completedAt) {
|
|
438
447
|
lines.push(`- timing:${submittedAt ? ` submitted ${markdownInlineCode(submittedAt)}` : ""}${completedAt ? ` completed ${markdownInlineCode(completedAt)}` : ""}`);
|
|
439
448
|
}
|
|
449
|
+
const splitJobs = Array.isArray(riddle.split_jobs) ? riddle.split_jobs.map(cliRecord).filter((job) => Boolean(job)) : [];
|
|
450
|
+
for (const job of splitJobs.slice(0, 12)) {
|
|
451
|
+
const viewport = cliString(job.viewport) || "viewport";
|
|
452
|
+
const splitJobId = cliString(job.job_id);
|
|
453
|
+
const splitStatus = cliString(job.status);
|
|
454
|
+
const splitTerminal = typeof job.terminal === "boolean" ? job.terminal : void 0;
|
|
455
|
+
const splitElapsedMs = cliFiniteNumber(job.elapsed_ms);
|
|
456
|
+
const splitPreSubmissionElapsedMs = cliFiniteNumber(job.pre_submission_elapsed_ms);
|
|
457
|
+
lines.push(
|
|
458
|
+
`- ${viewport}: ${[
|
|
459
|
+
splitJobId ? `job ${markdownInlineCode(splitJobId)}` : "",
|
|
460
|
+
splitStatus ? `status ${markdownInlineCode(splitStatus)}` : "",
|
|
461
|
+
splitTerminal === void 0 ? "" : `terminal ${splitTerminal ? "true" : "false"}`,
|
|
462
|
+
splitElapsedMs === void 0 ? "" : `elapsed ${formatPollDuration(splitElapsedMs)}`,
|
|
463
|
+
splitPreSubmissionElapsedMs === void 0 || splitPreSubmissionElapsedMs < 1e3 ? "" : `pre-submit ${formatPollDuration(splitPreSubmissionElapsedMs)}`
|
|
464
|
+
].filter(Boolean).join(", ") || "job metadata unavailable"}`
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
if (splitJobs.length > 12) lines.push(`- ${splitJobs.length - 12} additional split job(s) omitted.`);
|
|
440
468
|
return lines;
|
|
441
469
|
}
|
|
442
470
|
function markdownInlineCode(value, maxLength = 80) {
|
|
@@ -1036,6 +1064,7 @@ function withRiddleMetadata(result, input) {
|
|
|
1036
1064
|
submitted_at: poll?.submitted_at ?? result.riddle?.submitted_at,
|
|
1037
1065
|
completed_at: poll?.completed_at ?? result.riddle?.completed_at,
|
|
1038
1066
|
queue_elapsed_ms: poll?.queue_elapsed_ms ?? result.riddle?.queue_elapsed_ms,
|
|
1067
|
+
pre_submission_elapsed_ms: poll?.pre_submission_elapsed_ms ?? result.riddle?.pre_submission_elapsed_ms,
|
|
1039
1068
|
elapsed_ms: poll?.elapsed_ms ?? result.riddle?.elapsed_ms,
|
|
1040
1069
|
attempt: poll?.attempt ?? result.riddle?.attempt,
|
|
1041
1070
|
attempts: poll?.attempts ?? result.riddle?.attempts,
|
|
@@ -1056,19 +1085,155 @@ function riddleMetadataFromPoll(jobId, poll) {
|
|
|
1056
1085
|
submitted_at: poll.poll?.submitted_at,
|
|
1057
1086
|
completed_at: poll.poll?.completed_at,
|
|
1058
1087
|
queue_elapsed_ms: poll.poll?.queue_elapsed_ms,
|
|
1088
|
+
pre_submission_elapsed_ms: poll.poll?.pre_submission_elapsed_ms,
|
|
1059
1089
|
elapsed_ms: poll.poll?.elapsed_ms,
|
|
1060
1090
|
attempt: poll.poll?.attempt,
|
|
1061
1091
|
attempts: poll.poll?.attempts,
|
|
1062
1092
|
timed_out: poll.poll?.timed_out
|
|
1063
1093
|
};
|
|
1064
1094
|
}
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1095
|
+
function profileForSplitViewport(profile, viewport) {
|
|
1096
|
+
return {
|
|
1097
|
+
...profile,
|
|
1098
|
+
name: `${profile.name}-${viewport.name || `${viewport.width}x${viewport.height}`}`,
|
|
1099
|
+
target: {
|
|
1100
|
+
...profile.target,
|
|
1101
|
+
viewports: [viewport]
|
|
1102
|
+
},
|
|
1103
|
+
metadata: {
|
|
1104
|
+
...profile.metadata || {},
|
|
1105
|
+
split_parent_profile: profile.name,
|
|
1106
|
+
split_viewport: viewport.name
|
|
1107
|
+
}
|
|
1108
|
+
};
|
|
1109
|
+
}
|
|
1110
|
+
function safeProfileOutputSegment(value) {
|
|
1111
|
+
const safe = value.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
1112
|
+
return safe || "viewport";
|
|
1113
|
+
}
|
|
1114
|
+
function splitViewportOutputDir(outputDir, viewportName, seen) {
|
|
1115
|
+
const base = safeProfileOutputSegment(viewportName);
|
|
1116
|
+
const count = seen.get(base) || 0;
|
|
1117
|
+
seen.set(base, count + 1);
|
|
1118
|
+
return path.join(outputDir, count ? `${base}-${count + 1}` : base);
|
|
1119
|
+
}
|
|
1120
|
+
function splitViewportArtifactRefs(input) {
|
|
1121
|
+
return (input.result.artifacts.riddle_artifacts || []).map((artifact) => ({
|
|
1122
|
+
...artifact,
|
|
1123
|
+
name: `${safeProfileOutputSegment(input.viewport.name)}/${artifact.name || artifact.kind || "artifact"}`
|
|
1124
|
+
}));
|
|
1125
|
+
}
|
|
1126
|
+
function sumDefinedNumbers(values) {
|
|
1127
|
+
const numbers = values.filter((value) => typeof value === "number" && Number.isFinite(value));
|
|
1128
|
+
return numbers.length ? numbers.reduce((sum, value) => sum + value, 0) : void 0;
|
|
1129
|
+
}
|
|
1130
|
+
function splitViewportRiddleMetadata(childRuns) {
|
|
1131
|
+
const splitJobs = childRuns.map(({ viewport, result }) => ({
|
|
1132
|
+
viewport: viewport.name,
|
|
1133
|
+
job_id: result.riddle?.job_id,
|
|
1134
|
+
status: result.riddle?.status,
|
|
1135
|
+
terminal: result.riddle?.terminal,
|
|
1136
|
+
queue_elapsed_ms: result.riddle?.queue_elapsed_ms,
|
|
1137
|
+
pre_submission_elapsed_ms: result.riddle?.pre_submission_elapsed_ms,
|
|
1138
|
+
elapsed_ms: result.riddle?.elapsed_ms,
|
|
1139
|
+
attempt: result.riddle?.attempt,
|
|
1140
|
+
attempts: result.riddle?.attempts,
|
|
1141
|
+
timed_out: result.riddle?.timed_out
|
|
1142
|
+
}));
|
|
1143
|
+
return {
|
|
1144
|
+
mode: "split-viewports",
|
|
1145
|
+
job_count: childRuns.length,
|
|
1146
|
+
status: "split-viewports",
|
|
1147
|
+
terminal: childRuns.every(({ result }) => result.riddle?.terminal !== false),
|
|
1148
|
+
queue_elapsed_ms: sumDefinedNumbers(splitJobs.map((job) => job.queue_elapsed_ms)),
|
|
1149
|
+
pre_submission_elapsed_ms: sumDefinedNumbers(splitJobs.map((job) => job.pre_submission_elapsed_ms)),
|
|
1150
|
+
elapsed_ms: sumDefinedNumbers(splitJobs.map((job) => job.elapsed_ms)),
|
|
1151
|
+
split_jobs: splitJobs
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1154
|
+
function latestCapturedAt(evidences) {
|
|
1155
|
+
let latest = "";
|
|
1156
|
+
let latestMs = Number.NEGATIVE_INFINITY;
|
|
1157
|
+
for (const evidence of evidences) {
|
|
1158
|
+
const parsed = Date.parse(evidence.captured_at);
|
|
1159
|
+
if (Number.isFinite(parsed) && parsed >= latestMs) {
|
|
1160
|
+
latest = evidence.captured_at;
|
|
1161
|
+
latestMs = parsed;
|
|
1162
|
+
}
|
|
1069
1163
|
}
|
|
1164
|
+
return latest || (/* @__PURE__ */ new Date()).toISOString();
|
|
1165
|
+
}
|
|
1166
|
+
function splitViewportDomSummary(profile, childRuns, evidence) {
|
|
1167
|
+
return {
|
|
1168
|
+
split_viewports: true,
|
|
1169
|
+
expected_viewport_count: profile.target.viewports.length,
|
|
1170
|
+
viewport_count: evidence.viewports.length,
|
|
1171
|
+
child_result_count: childRuns.length,
|
|
1172
|
+
child_statuses: childRuns.map(({ viewport, result }) => ({
|
|
1173
|
+
viewport: viewport.name,
|
|
1174
|
+
profile_name: result.profile_name,
|
|
1175
|
+
status: result.status,
|
|
1176
|
+
job_id: result.riddle?.job_id || null
|
|
1177
|
+
})),
|
|
1178
|
+
routes: evidence.viewports.map((viewport) => ({
|
|
1179
|
+
viewport: viewport.name,
|
|
1180
|
+
requested: viewport.route.requested,
|
|
1181
|
+
observed: viewport.route.observed,
|
|
1182
|
+
matched: viewport.route.matched,
|
|
1183
|
+
http_status: viewport.route.http_status ?? null
|
|
1184
|
+
})),
|
|
1185
|
+
titles: evidence.viewports.map((viewport) => viewport.title).filter((title) => Boolean(title)),
|
|
1186
|
+
overflow_px: evidence.viewports.map((viewport) => viewport.overflow_px ?? null),
|
|
1187
|
+
bounds_overflow_px: evidence.viewports.map((viewport) => viewport.bounds_overflow_px ?? null),
|
|
1188
|
+
overflow_offender_counts: evidence.viewports.map((viewport) => (viewport.overflow_offenders || []).length),
|
|
1189
|
+
console_event_count: evidence.console.events.length,
|
|
1190
|
+
console_fatal_count: evidence.console.fatal_count,
|
|
1191
|
+
page_error_count: evidence.page_errors.length,
|
|
1192
|
+
network_mock_count: (profile.target.network_mocks || []).length,
|
|
1193
|
+
network_mock_hit_count: (evidence.network_mocks || []).filter((event) => event.ok !== false).length
|
|
1194
|
+
};
|
|
1195
|
+
}
|
|
1196
|
+
function aggregateSplitViewportEvidence(profile, childRuns) {
|
|
1197
|
+
const evidences = childRuns.map(({ result }) => result.evidence).filter((evidence2) => Boolean(evidence2));
|
|
1198
|
+
const viewports = evidences.flatMap((evidence2) => evidence2.viewports || []);
|
|
1199
|
+
const consoleEvents = evidences.flatMap((evidence2) => evidence2.console?.events || []);
|
|
1200
|
+
const pageErrors = evidences.flatMap((evidence2) => evidence2.page_errors || []);
|
|
1201
|
+
const networkMocks = evidences.flatMap((evidence2) => evidence2.network_mocks || []);
|
|
1202
|
+
const evidence = {
|
|
1203
|
+
version: RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION,
|
|
1204
|
+
profile_name: profile.name,
|
|
1205
|
+
target_url: resolveRiddleProofProfileTargetUrl(profile),
|
|
1206
|
+
baseline_policy: profile.baseline_policy,
|
|
1207
|
+
captured_at: latestCapturedAt(evidences),
|
|
1208
|
+
viewports,
|
|
1209
|
+
console: {
|
|
1210
|
+
events: consoleEvents,
|
|
1211
|
+
fatal_count: evidences.reduce((sum, item) => sum + (item.console?.fatal_count || 0), 0)
|
|
1212
|
+
},
|
|
1213
|
+
page_errors: pageErrors,
|
|
1214
|
+
network_mocks: networkMocks.length ? networkMocks : void 0
|
|
1215
|
+
};
|
|
1216
|
+
evidence.dom_summary = splitViewportDomSummary(profile, childRuns, evidence);
|
|
1217
|
+
return evidence;
|
|
1218
|
+
}
|
|
1219
|
+
function splitViewportBlockedMessage(childRuns) {
|
|
1220
|
+
const blocked = childRuns.filter(({ result }) => !result.evidence || result.status === "environment_blocked" || result.status === "configuration_error").map(({ viewport, result }) => `${viewport.name}: ${result.status}${result.error ? ` (${result.error})` : ""}`);
|
|
1221
|
+
return `Split viewport run did not produce reliable evidence for ${blocked.join("; ")}.`;
|
|
1222
|
+
}
|
|
1223
|
+
function withSplitViewportWarnings(profile, result) {
|
|
1224
|
+
const warnings = [];
|
|
1225
|
+
if (profile.target.network_mocks?.length) {
|
|
1226
|
+
warnings.push("Split viewport mode runs each viewport in a separate Riddle job; global network mock sequencing is assessed from aggregated events.");
|
|
1227
|
+
}
|
|
1228
|
+
if (!warnings.length) return result;
|
|
1229
|
+
return {
|
|
1230
|
+
...result,
|
|
1231
|
+
warnings: [.../* @__PURE__ */ new Set([...result.warnings || [], ...warnings])]
|
|
1232
|
+
};
|
|
1233
|
+
}
|
|
1234
|
+
async function runSingleRiddleProfileForCli(profile, options, input) {
|
|
1235
|
+
const { client, runner } = input;
|
|
1070
1236
|
const targetUrl = resolveRiddleProofProfileTargetUrl(profile);
|
|
1071
|
-
const client = createRiddleApiClient(riddleClientConfig(options));
|
|
1072
1237
|
let created;
|
|
1073
1238
|
try {
|
|
1074
1239
|
created = await client.runScript({
|
|
@@ -1127,6 +1292,48 @@ async function runProfileForCli(profile, options) {
|
|
|
1127
1292
|
artifacts
|
|
1128
1293
|
});
|
|
1129
1294
|
}
|
|
1295
|
+
async function runSplitViewportProfileForCli(profile, options, input) {
|
|
1296
|
+
const outputDir = profileOutputDirOption(options);
|
|
1297
|
+
const seenOutputNames = /* @__PURE__ */ new Map();
|
|
1298
|
+
const childRuns = [];
|
|
1299
|
+
for (const viewport of profile.target.viewports) {
|
|
1300
|
+
const childProfile = profileForSplitViewport(profile, viewport);
|
|
1301
|
+
const result2 = await runSingleRiddleProfileForCli(childProfile, options, input);
|
|
1302
|
+
if (outputDir) {
|
|
1303
|
+
writeProfileOutput(splitViewportOutputDir(outputDir, viewport.name, seenOutputNames), result2);
|
|
1304
|
+
}
|
|
1305
|
+
childRuns.push({ viewport, profile: childProfile, result: result2 });
|
|
1306
|
+
}
|
|
1307
|
+
const artifacts = childRuns.flatMap(splitViewportArtifactRefs);
|
|
1308
|
+
const blocked = childRuns.filter(({ result: result2 }) => !result2.evidence || result2.status === "environment_blocked" || result2.status === "configuration_error");
|
|
1309
|
+
if (blocked.length) {
|
|
1310
|
+
return createRiddleProofProfileEnvironmentBlockedResult({
|
|
1311
|
+
profile,
|
|
1312
|
+
runner: input.runner,
|
|
1313
|
+
error: splitViewportBlockedMessage(childRuns),
|
|
1314
|
+
riddle: splitViewportRiddleMetadata(childRuns),
|
|
1315
|
+
artifacts
|
|
1316
|
+
});
|
|
1317
|
+
}
|
|
1318
|
+
const evidence = aggregateSplitViewportEvidence(profile, childRuns);
|
|
1319
|
+
const result = assessRiddleProofProfileEvidence(profile, evidence, {
|
|
1320
|
+
runner: input.runner,
|
|
1321
|
+
riddle: splitViewportRiddleMetadata(childRuns),
|
|
1322
|
+
artifacts
|
|
1323
|
+
});
|
|
1324
|
+
return withSplitViewportWarnings(profile, result);
|
|
1325
|
+
}
|
|
1326
|
+
async function runProfileForCli(profile, options) {
|
|
1327
|
+
const runner = optionString(options, "runner") || "riddle";
|
|
1328
|
+
if (runner !== "riddle") {
|
|
1329
|
+
throw new Error(`Unsupported --runner ${runner}. The current CLI supports --runner riddle.`);
|
|
1330
|
+
}
|
|
1331
|
+
const client = createRiddleApiClient(riddleClientConfig(options));
|
|
1332
|
+
if (runProfileSplitViewportsOption(options) && profile.target.viewports.length > 1) {
|
|
1333
|
+
return runSplitViewportProfileForCli(profile, options, { client, runner });
|
|
1334
|
+
}
|
|
1335
|
+
return runSingleRiddleProfileForCli(profile, options, { client, runner });
|
|
1336
|
+
}
|
|
1130
1337
|
function requestForRun(options) {
|
|
1131
1338
|
const statePath = optionString(options, "statePath");
|
|
1132
1339
|
const withEngineModuleUrl = (request) => {
|
package/dist/index.cjs
CHANGED
|
@@ -16464,6 +16464,7 @@ function buildPollSnapshot(jobId, job, input) {
|
|
|
16464
16464
|
submitted_at: submittedAt,
|
|
16465
16465
|
completed_at: completedAt,
|
|
16466
16466
|
queue_elapsed_ms: queueElapsedMs,
|
|
16467
|
+
pre_submission_elapsed_ms: Math.max(0, Math.floor(input.preSubmissionElapsedMs ?? 0)),
|
|
16467
16468
|
running_without_submission: Boolean(status && !terminal && !submittedAt)
|
|
16468
16469
|
};
|
|
16469
16470
|
}
|
|
@@ -16471,7 +16472,8 @@ function pollMessage(snapshot, timedOut) {
|
|
|
16471
16472
|
if (!timedOut) return void 0;
|
|
16472
16473
|
const submitted = snapshot.submitted_at || "not submitted";
|
|
16473
16474
|
const queue = snapshot.queue_elapsed_ms !== null ? ` queue_elapsed_ms=${snapshot.queue_elapsed_ms}` : "";
|
|
16474
|
-
|
|
16475
|
+
const preSubmit = snapshot.pre_submission_elapsed_ms > 0 ? ` pre_submission_elapsed_ms=${snapshot.pre_submission_elapsed_ms}` : "";
|
|
16476
|
+
return `Riddle job ${snapshot.job_id} did not reach a terminal status after ${snapshot.attempt} poll attempts; status=${snapshot.status || "unknown"} submitted_at=${submitted}.${queue}${preSubmit}`;
|
|
16475
16477
|
}
|
|
16476
16478
|
async function pollRiddleJob(config, jobId, options = {}) {
|
|
16477
16479
|
if (!jobId?.trim()) throw new Error("jobId is required");
|
|
@@ -16483,15 +16485,24 @@ async function pollRiddleJob(config, jobId, options = {}) {
|
|
|
16483
16485
|
let lastSnapshot = null;
|
|
16484
16486
|
let lastProgressAt = 0;
|
|
16485
16487
|
let lastProgressKey = "";
|
|
16488
|
+
let preSubmissionElapsedMs = 0;
|
|
16486
16489
|
for (let index = 0; index < attempts; index += 1) {
|
|
16487
16490
|
job = await riddleRequestJson(config, `/v1/jobs/${jobId}`);
|
|
16488
16491
|
const observedAt = Date.now();
|
|
16489
|
-
|
|
16492
|
+
const nextSnapshot = buildPollSnapshot(jobId, job, {
|
|
16490
16493
|
attempt: index + 1,
|
|
16491
16494
|
attempts,
|
|
16492
16495
|
startedAt,
|
|
16493
|
-
observedAt
|
|
16496
|
+
observedAt,
|
|
16497
|
+
preSubmissionElapsedMs
|
|
16494
16498
|
});
|
|
16499
|
+
if (nextSnapshot.running_without_submission) {
|
|
16500
|
+
preSubmissionElapsedMs = Math.max(preSubmissionElapsedMs, nextSnapshot.elapsed_ms);
|
|
16501
|
+
}
|
|
16502
|
+
lastSnapshot = {
|
|
16503
|
+
...nextSnapshot,
|
|
16504
|
+
pre_submission_elapsed_ms: preSubmissionElapsedMs
|
|
16505
|
+
};
|
|
16495
16506
|
const progressKey = [
|
|
16496
16507
|
lastSnapshot.status || "unknown",
|
|
16497
16508
|
lastSnapshot.terminal ? "terminal" : "nonterminal",
|
package/dist/index.js
CHANGED
package/dist/profile.d.cts
CHANGED
|
@@ -376,17 +376,32 @@ interface RiddleProofProfileResult {
|
|
|
376
376
|
warnings?: string[];
|
|
377
377
|
evidence?: RiddleProofProfileEvidence;
|
|
378
378
|
riddle?: {
|
|
379
|
+
mode?: "split-viewports" | (string & {});
|
|
379
380
|
job_id?: string;
|
|
381
|
+
job_count?: number;
|
|
380
382
|
status?: string | null;
|
|
381
383
|
terminal?: boolean;
|
|
382
384
|
created_at?: string | null;
|
|
383
385
|
submitted_at?: string | null;
|
|
384
386
|
completed_at?: string | null;
|
|
385
387
|
queue_elapsed_ms?: number | null;
|
|
388
|
+
pre_submission_elapsed_ms?: number;
|
|
386
389
|
elapsed_ms?: number;
|
|
387
390
|
attempt?: number;
|
|
388
391
|
attempts?: number;
|
|
389
392
|
timed_out?: boolean;
|
|
393
|
+
split_jobs?: Array<{
|
|
394
|
+
viewport: string;
|
|
395
|
+
job_id?: string;
|
|
396
|
+
status?: string | null;
|
|
397
|
+
terminal?: boolean;
|
|
398
|
+
queue_elapsed_ms?: number | null;
|
|
399
|
+
pre_submission_elapsed_ms?: number;
|
|
400
|
+
elapsed_ms?: number;
|
|
401
|
+
attempt?: number;
|
|
402
|
+
attempts?: number;
|
|
403
|
+
timed_out?: boolean;
|
|
404
|
+
}>;
|
|
390
405
|
};
|
|
391
406
|
environment_blocker?: Record<string, JsonValue>;
|
|
392
407
|
error?: string;
|
package/dist/profile.d.ts
CHANGED
|
@@ -376,17 +376,32 @@ interface RiddleProofProfileResult {
|
|
|
376
376
|
warnings?: string[];
|
|
377
377
|
evidence?: RiddleProofProfileEvidence;
|
|
378
378
|
riddle?: {
|
|
379
|
+
mode?: "split-viewports" | (string & {});
|
|
379
380
|
job_id?: string;
|
|
381
|
+
job_count?: number;
|
|
380
382
|
status?: string | null;
|
|
381
383
|
terminal?: boolean;
|
|
382
384
|
created_at?: string | null;
|
|
383
385
|
submitted_at?: string | null;
|
|
384
386
|
completed_at?: string | null;
|
|
385
387
|
queue_elapsed_ms?: number | null;
|
|
388
|
+
pre_submission_elapsed_ms?: number;
|
|
386
389
|
elapsed_ms?: number;
|
|
387
390
|
attempt?: number;
|
|
388
391
|
attempts?: number;
|
|
389
392
|
timed_out?: boolean;
|
|
393
|
+
split_jobs?: Array<{
|
|
394
|
+
viewport: string;
|
|
395
|
+
job_id?: string;
|
|
396
|
+
status?: string | null;
|
|
397
|
+
terminal?: boolean;
|
|
398
|
+
queue_elapsed_ms?: number | null;
|
|
399
|
+
pre_submission_elapsed_ms?: number;
|
|
400
|
+
elapsed_ms?: number;
|
|
401
|
+
attempt?: number;
|
|
402
|
+
attempts?: number;
|
|
403
|
+
timed_out?: boolean;
|
|
404
|
+
}>;
|
|
390
405
|
};
|
|
391
406
|
environment_blocker?: Record<string, JsonValue>;
|
|
392
407
|
error?: string;
|
package/dist/riddle-client.cjs
CHANGED
|
@@ -325,6 +325,7 @@ function buildPollSnapshot(jobId, job, input) {
|
|
|
325
325
|
submitted_at: submittedAt,
|
|
326
326
|
completed_at: completedAt,
|
|
327
327
|
queue_elapsed_ms: queueElapsedMs,
|
|
328
|
+
pre_submission_elapsed_ms: Math.max(0, Math.floor(input.preSubmissionElapsedMs ?? 0)),
|
|
328
329
|
running_without_submission: Boolean(status && !terminal && !submittedAt)
|
|
329
330
|
};
|
|
330
331
|
}
|
|
@@ -332,7 +333,8 @@ function pollMessage(snapshot, timedOut) {
|
|
|
332
333
|
if (!timedOut) return void 0;
|
|
333
334
|
const submitted = snapshot.submitted_at || "not submitted";
|
|
334
335
|
const queue = snapshot.queue_elapsed_ms !== null ? ` queue_elapsed_ms=${snapshot.queue_elapsed_ms}` : "";
|
|
335
|
-
|
|
336
|
+
const preSubmit = snapshot.pre_submission_elapsed_ms > 0 ? ` pre_submission_elapsed_ms=${snapshot.pre_submission_elapsed_ms}` : "";
|
|
337
|
+
return `Riddle job ${snapshot.job_id} did not reach a terminal status after ${snapshot.attempt} poll attempts; status=${snapshot.status || "unknown"} submitted_at=${submitted}.${queue}${preSubmit}`;
|
|
336
338
|
}
|
|
337
339
|
async function pollRiddleJob(config, jobId, options = {}) {
|
|
338
340
|
if (!jobId?.trim()) throw new Error("jobId is required");
|
|
@@ -344,15 +346,24 @@ async function pollRiddleJob(config, jobId, options = {}) {
|
|
|
344
346
|
let lastSnapshot = null;
|
|
345
347
|
let lastProgressAt = 0;
|
|
346
348
|
let lastProgressKey = "";
|
|
349
|
+
let preSubmissionElapsedMs = 0;
|
|
347
350
|
for (let index = 0; index < attempts; index += 1) {
|
|
348
351
|
job = await riddleRequestJson(config, `/v1/jobs/${jobId}`);
|
|
349
352
|
const observedAt = Date.now();
|
|
350
|
-
|
|
353
|
+
const nextSnapshot = buildPollSnapshot(jobId, job, {
|
|
351
354
|
attempt: index + 1,
|
|
352
355
|
attempts,
|
|
353
356
|
startedAt,
|
|
354
|
-
observedAt
|
|
357
|
+
observedAt,
|
|
358
|
+
preSubmissionElapsedMs
|
|
355
359
|
});
|
|
360
|
+
if (nextSnapshot.running_without_submission) {
|
|
361
|
+
preSubmissionElapsedMs = Math.max(preSubmissionElapsedMs, nextSnapshot.elapsed_ms);
|
|
362
|
+
}
|
|
363
|
+
lastSnapshot = {
|
|
364
|
+
...nextSnapshot,
|
|
365
|
+
pre_submission_elapsed_ms: preSubmissionElapsedMs
|
|
366
|
+
};
|
|
356
367
|
const progressKey = [
|
|
357
368
|
lastSnapshot.status || "unknown",
|
|
358
369
|
lastSnapshot.terminal ? "terminal" : "nonterminal",
|
package/dist/riddle-client.d.cts
CHANGED
|
@@ -18,6 +18,7 @@ interface RiddlePollProgressSnapshot {
|
|
|
18
18
|
submitted_at: string | null;
|
|
19
19
|
completed_at: string | null;
|
|
20
20
|
queue_elapsed_ms: number | null;
|
|
21
|
+
pre_submission_elapsed_ms: number;
|
|
21
22
|
running_without_submission: boolean;
|
|
22
23
|
}
|
|
23
24
|
interface RiddlePollSummary extends RiddlePollProgressSnapshot {
|
package/dist/riddle-client.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ interface RiddlePollProgressSnapshot {
|
|
|
18
18
|
submitted_at: string | null;
|
|
19
19
|
completed_at: string | null;
|
|
20
20
|
queue_elapsed_ms: number | null;
|
|
21
|
+
pre_submission_elapsed_ms: number;
|
|
21
22
|
running_without_submission: boolean;
|
|
22
23
|
}
|
|
23
24
|
interface RiddlePollSummary extends RiddlePollProgressSnapshot {
|
package/dist/riddle-client.js
CHANGED