@riddledc/riddle-proof 0.8.36 → 0.8.38

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.
Files changed (36) hide show
  1. package/README.md +21 -0
  2. package/dist/advanced/index.d.cts +1 -1
  3. package/dist/advanced/index.d.ts +1 -1
  4. package/dist/advanced/proof-run-engine.d.cts +1 -1
  5. package/dist/advanced/proof-run-engine.d.ts +1 -1
  6. package/dist/chunk-6KYXX4OE.js +209 -0
  7. package/dist/{chunk-TWTEUS7R.js → chunk-DI2XNGEZ.js} +126 -1
  8. package/dist/{chunk-E25K5PDM.js → chunk-U44KBAPH.js} +168 -7
  9. package/dist/cli/index.js +3 -2
  10. package/dist/cli.cjs +488 -3
  11. package/dist/cli.js +3 -2
  12. package/dist/index.cjs +336 -1
  13. package/dist/index.d.cts +2 -1
  14. package/dist/index.d.ts +2 -1
  15. package/dist/index.js +26 -18
  16. package/dist/pr-comment.cjs +235 -0
  17. package/dist/pr-comment.d.cts +41 -0
  18. package/dist/pr-comment.d.ts +41 -0
  19. package/dist/pr-comment.js +11 -0
  20. package/dist/{proof-run-engine-DYUu2mqY.d.cts → proof-run-engine-4dM37pEx.d.cts} +3 -3
  21. package/dist/{proof-run-engine-BmNYuOJ7.d.ts → proof-run-engine-BqaeqAze.d.ts} +3 -3
  22. package/dist/proof-run-engine.d.cts +1 -1
  23. package/dist/proof-run-engine.d.ts +1 -1
  24. package/dist/riddle-client.cjs +126 -1
  25. package/dist/riddle-client.d.cts +21 -1
  26. package/dist/riddle-client.d.ts +21 -1
  27. package/dist/riddle-client.js +1 -1
  28. package/dist/runtime/index.cjs +126 -1
  29. package/dist/runtime/index.d.cts +1 -1
  30. package/dist/runtime/index.d.ts +1 -1
  31. package/dist/runtime/index.js +1 -1
  32. package/dist/runtime/riddle-client.cjs +126 -1
  33. package/dist/runtime/riddle-client.d.cts +1 -1
  34. package/dist/runtime/riddle-client.d.ts +1 -1
  35. package/dist/runtime/riddle-client.js +1 -1
  36. package/package.json +7 -2
package/dist/cli.cjs CHANGED
@@ -7438,10 +7438,68 @@ function previewDeployResultFromRecord(input) {
7438
7438
  function canRecoverPreviewPublish(error) {
7439
7439
  return error instanceof RiddleApiError && PREVIEW_PUBLISH_RECOVERY_STATUSES.has(error.status);
7440
7440
  }
7441
+ function summarizePreviewDirectory(directory) {
7442
+ const stack = [directory];
7443
+ let fileCount = 0;
7444
+ let totalBytes = 0;
7445
+ while (stack.length) {
7446
+ const current = stack.pop();
7447
+ for (const entry of (0, import_node_fs5.readdirSync)(current, { withFileTypes: true })) {
7448
+ const fullPath = import_node_path5.default.join(current, entry.name);
7449
+ if (entry.isDirectory()) {
7450
+ stack.push(fullPath);
7451
+ continue;
7452
+ }
7453
+ if (!entry.isFile()) continue;
7454
+ const stat = (0, import_node_fs5.statSync)(fullPath);
7455
+ fileCount += 1;
7456
+ totalBytes += stat.size;
7457
+ }
7458
+ }
7459
+ return { file_count: fileCount, total_bytes: totalBytes };
7460
+ }
7461
+ function previewProgressEmitter(config, base) {
7462
+ return async (snapshot) => {
7463
+ if (!config.onPreviewProgress) return;
7464
+ await config.onPreviewProgress({
7465
+ label: base.label,
7466
+ framework: base.framework,
7467
+ directory: base.directory,
7468
+ elapsed_ms: Math.max(0, Date.now() - base.startedAt),
7469
+ warnings: base.warnings?.length ? base.warnings : void 0,
7470
+ ...snapshot
7471
+ });
7472
+ };
7473
+ }
7441
7474
  async function waitForPublishedPreview(config, input) {
7475
+ await input.emitProgress?.({
7476
+ stage: "publish_recovering",
7477
+ id: input.id,
7478
+ file_count: input.localSummary?.file_count,
7479
+ total_bytes: input.localSummary?.total_bytes,
7480
+ publish_error: input.publishError.message,
7481
+ message: `publish returned ${input.publishError.status}; polling preview status`
7482
+ });
7442
7483
  for (let attempt = 1; attempt <= PREVIEW_PUBLISH_RECOVERY_ATTEMPTS; attempt += 1) {
7443
7484
  const status = await riddleRequestJson(config, `/v1/preview/${input.id}`);
7485
+ await input.emitProgress?.({
7486
+ stage: "checking_status",
7487
+ id: input.id,
7488
+ attempt,
7489
+ attempts: PREVIEW_PUBLISH_RECOVERY_ATTEMPTS,
7490
+ status: typeof status.status === "string" ? status.status : null,
7491
+ preview_url: typeof status.preview_url === "string" ? status.preview_url : void 0,
7492
+ file_count: typeof status.file_count === "number" ? status.file_count : input.localSummary?.file_count,
7493
+ total_bytes: typeof status.total_bytes === "number" ? status.total_bytes : input.localSummary?.total_bytes
7494
+ });
7444
7495
  if (String(status.status || "") === "ready" && String(status.preview_url || "").trim()) {
7496
+ await input.emitProgress?.({
7497
+ stage: "ready",
7498
+ id: input.id,
7499
+ preview_url: String(status.preview_url),
7500
+ file_count: typeof status.file_count === "number" ? status.file_count : input.localSummary?.file_count,
7501
+ total_bytes: typeof status.total_bytes === "number" ? status.total_bytes : input.localSummary?.total_bytes
7502
+ });
7445
7503
  return previewDeployResultFromRecord({
7446
7504
  record: status,
7447
7505
  id: input.id,
@@ -7463,7 +7521,17 @@ async function deployRiddlePreview(config, directory, label, framework = "static
7463
7521
  if (!directory?.trim()) throw new Error("directory is required");
7464
7522
  if (!label?.trim()) throw new Error("label is required");
7465
7523
  if (framework !== "spa" && framework !== "static") throw new Error("framework must be spa or static");
7524
+ const startedAt = Date.now();
7466
7525
  const warnings = collectRiddlePreviewDeployWarnings(directory, framework);
7526
+ const emitProgress = previewProgressEmitter(config, { label, framework, directory, startedAt, warnings });
7527
+ await emitProgress({ stage: "validating", message: "checking preview input directory" });
7528
+ const localSummary = summarizePreviewDirectory(directory);
7529
+ await emitProgress({
7530
+ stage: "creating",
7531
+ file_count: localSummary.file_count,
7532
+ total_bytes: localSummary.total_bytes,
7533
+ message: "creating preview upload target"
7534
+ });
7467
7535
  const created = await riddleRequestJson(config, "/v1/preview", {
7468
7536
  method: "POST",
7469
7537
  body: JSON.stringify({ framework, label })
@@ -7471,10 +7539,42 @@ async function deployRiddlePreview(config, directory, label, framework = "static
7471
7539
  const id = String(created.id || "");
7472
7540
  const uploadUrl = String(created.upload_url || "");
7473
7541
  if (!id || !uploadUrl) throw new Error("Riddle preview create response was missing id or upload_url.");
7542
+ await emitProgress({
7543
+ stage: "created",
7544
+ id,
7545
+ file_count: localSummary.file_count,
7546
+ total_bytes: localSummary.total_bytes,
7547
+ message: "preview upload target created"
7548
+ });
7474
7549
  const scratch = (0, import_node_fs5.mkdtempSync)(import_node_path5.default.join((0, import_node_os2.tmpdir)(), "riddle-preview-upload-"));
7475
7550
  const tarball = import_node_path5.default.join(scratch, `${id}.tar.gz`);
7551
+ let tarballBytes = 0;
7476
7552
  try {
7553
+ await emitProgress({
7554
+ stage: "archiving",
7555
+ id,
7556
+ file_count: localSummary.file_count,
7557
+ total_bytes: localSummary.total_bytes,
7558
+ message: "creating preview archive"
7559
+ });
7477
7560
  (0, import_node_child_process4.execFileSync)("tar", ["czf", tarball, "-C", directory, "."], { stdio: "pipe" });
7561
+ tarballBytes = (0, import_node_fs5.statSync)(tarball).size;
7562
+ await emitProgress({
7563
+ stage: "archived",
7564
+ id,
7565
+ file_count: localSummary.file_count,
7566
+ total_bytes: localSummary.total_bytes,
7567
+ tarball_bytes: tarballBytes,
7568
+ message: "preview archive created"
7569
+ });
7570
+ await emitProgress({
7571
+ stage: "uploading",
7572
+ id,
7573
+ file_count: localSummary.file_count,
7574
+ total_bytes: localSummary.total_bytes,
7575
+ tarball_bytes: tarballBytes,
7576
+ message: "uploading preview archive"
7577
+ });
7478
7578
  const upload = await fetchFor(config)(uploadUrl, {
7479
7579
  method: "PUT",
7480
7580
  headers: { "Content-Type": "application/gzip" },
@@ -7483,14 +7583,37 @@ async function deployRiddlePreview(config, directory, label, framework = "static
7483
7583
  if (!upload.ok) {
7484
7584
  throw new RiddleApiError(uploadUrl, upload.status, await upload.text());
7485
7585
  }
7586
+ await emitProgress({
7587
+ stage: "uploaded",
7588
+ id,
7589
+ file_count: localSummary.file_count,
7590
+ total_bytes: localSummary.total_bytes,
7591
+ tarball_bytes: tarballBytes,
7592
+ message: "preview archive uploaded"
7593
+ });
7486
7594
  } finally {
7487
7595
  (0, import_node_fs5.rmSync)(scratch, { recursive: true, force: true });
7488
7596
  }
7489
7597
  const expiresAt = typeof created.expires_at === "string" ? created.expires_at : void 0;
7490
7598
  try {
7599
+ await emitProgress({
7600
+ stage: "publishing",
7601
+ id,
7602
+ file_count: localSummary.file_count,
7603
+ total_bytes: localSummary.total_bytes,
7604
+ tarball_bytes: tarballBytes || void 0,
7605
+ message: "publishing preview"
7606
+ });
7491
7607
  const published = await riddleRequestJson(config, `/v1/preview/${id}/publish`, {
7492
7608
  method: "POST"
7493
7609
  });
7610
+ await emitProgress({
7611
+ stage: "ready",
7612
+ id,
7613
+ preview_url: String(published.preview_url || ""),
7614
+ file_count: typeof published.file_count === "number" ? published.file_count : localSummary.file_count,
7615
+ total_bytes: typeof published.total_bytes === "number" ? published.total_bytes : localSummary.total_bytes
7616
+ });
7494
7617
  return previewDeployResultFromRecord({ record: published, id, label, framework, expiresAt, warnings });
7495
7618
  } catch (error) {
7496
7619
  if (!canRecoverPreviewPublish(error)) throw error;
@@ -7500,7 +7623,9 @@ async function deployRiddlePreview(config, directory, label, framework = "static
7500
7623
  framework,
7501
7624
  expiresAt,
7502
7625
  publishError: error,
7503
- warnings
7626
+ warnings,
7627
+ localSummary,
7628
+ emitProgress
7504
7629
  });
7505
7630
  }
7506
7631
  }
@@ -17252,6 +17377,210 @@ function extractRiddleProofProfileResult(input) {
17252
17377
  return void 0;
17253
17378
  }
17254
17379
 
17380
+ // src/pr-comment.ts
17381
+ var RIDDLE_PROOF_PR_COMMENT_MARKER = "<!-- riddle-proof:pr-comment:v1 -->";
17382
+ function asRecord(value) {
17383
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
17384
+ }
17385
+ function asArray(value) {
17386
+ return Array.isArray(value) ? value : [];
17387
+ }
17388
+ function stringValue3(value) {
17389
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
17390
+ }
17391
+ function numberValue2(value) {
17392
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
17393
+ }
17394
+ function booleanValue2(value) {
17395
+ return typeof value === "boolean" ? value : void 0;
17396
+ }
17397
+ function artifactKind(name, url) {
17398
+ const target = `${name} ${url}`.toLowerCase();
17399
+ if (/\.(png|jpe?g|gif|webp|avif|svg)(\?|#|$)/.test(target)) return "image";
17400
+ if (/\.(json|har|txt|md|html|log)(\?|#|$)/.test(target)) return "data";
17401
+ return "artifact";
17402
+ }
17403
+ function artifactDisplayName(value, fallback) {
17404
+ const raw = stringValue3(value);
17405
+ if (raw) return raw;
17406
+ return fallback;
17407
+ }
17408
+ function collectArtifacts(runResponse) {
17409
+ const proofResult = asRecord(runResponse.proofResult);
17410
+ const outputs = asArray(proofResult.outputs);
17411
+ const artifacts = [];
17412
+ const seen = /* @__PURE__ */ new Set();
17413
+ for (const [index, item] of outputs.entries()) {
17414
+ const artifact = asRecord(item);
17415
+ const url = stringValue3(artifact.url);
17416
+ if (!url || seen.has(url)) continue;
17417
+ seen.add(url);
17418
+ const name = artifactDisplayName(artifact.name, `artifact-${index + 1}`);
17419
+ artifacts.push({
17420
+ name,
17421
+ url,
17422
+ kind: artifactKind(name, url),
17423
+ size_bytes: numberValue2(artifact.size)
17424
+ });
17425
+ }
17426
+ return artifacts;
17427
+ }
17428
+ function pageSummaries(result) {
17429
+ const pages = [];
17430
+ for (const page of asArray(result.pages)) {
17431
+ const record = asRecord(page);
17432
+ const route = stringValue3(record.route) || stringValue3(record.url) || "page";
17433
+ const checks = asRecord(record.checks);
17434
+ let passed = 0;
17435
+ let failed = 0;
17436
+ for (const value of Object.values(checks)) {
17437
+ if (value === true) passed += 1;
17438
+ if (value === false) failed += 1;
17439
+ }
17440
+ pages.push({ route, passed, failed });
17441
+ }
17442
+ return pages;
17443
+ }
17444
+ function summarizeExplicitChecks(value) {
17445
+ let passed = 0;
17446
+ let failed = 0;
17447
+ const visit = (current, inChecks = false) => {
17448
+ if (current === true && inChecks) {
17449
+ passed += 1;
17450
+ return;
17451
+ }
17452
+ if (current === false && inChecks) {
17453
+ failed += 1;
17454
+ return;
17455
+ }
17456
+ if (Array.isArray(current)) {
17457
+ for (const item of current) visit(item, inChecks);
17458
+ return;
17459
+ }
17460
+ if (current && typeof current === "object") {
17461
+ for (const [key, item] of Object.entries(current)) {
17462
+ visit(item, inChecks || key === "checks");
17463
+ }
17464
+ }
17465
+ };
17466
+ visit(value);
17467
+ return { passed, failed };
17468
+ }
17469
+ function selectPrimaryImage(artifacts) {
17470
+ const images = artifacts.filter((artifact) => artifact.kind === "image");
17471
+ return images.find((artifact) => /after|proof|screenshot/i.test(artifact.name)) || images[0];
17472
+ }
17473
+ function summarizeRiddleProofPrComment(input) {
17474
+ const runResponse = asRecord(input.runResponse);
17475
+ const result = asRecord(input.result);
17476
+ const proofResult = asRecord(runResponse.proofResult);
17477
+ const preview = asRecord(runResponse.preview);
17478
+ const artifacts = collectArtifacts(runResponse);
17479
+ const pages = pageSummaries(result);
17480
+ const checkSource = { ...result };
17481
+ delete checkSource.ok;
17482
+ const nestedChecks = summarizeExplicitChecks(checkSource);
17483
+ const ok = booleanValue2(result.ok) ?? booleanValue2(runResponse.ok) ?? null;
17484
+ return {
17485
+ ok,
17486
+ status: stringValue3(proofResult.status),
17487
+ job_id: stringValue3(proofResult.job_id),
17488
+ duration_ms: numberValue2(proofResult.duration_ms),
17489
+ proof_url: stringValue3(runResponse.proofUrl),
17490
+ preview_id: stringValue3(preview.id),
17491
+ preview_url: stringValue3(preview.preview_url) || stringValue3(preview.url),
17492
+ preview_publish_recovered: booleanValue2(preview.publish_recovered),
17493
+ preview_publish_error: stringValue3(preview.publish_error),
17494
+ passed_checks: nestedChecks.passed,
17495
+ failed_checks: nestedChecks.failed,
17496
+ pages,
17497
+ artifacts,
17498
+ primary_image: selectPrimaryImage(artifacts)
17499
+ };
17500
+ }
17501
+ function formatDuration(ms) {
17502
+ if (typeof ms !== "number" || !Number.isFinite(ms)) return "";
17503
+ const seconds = Math.max(0, Math.round(ms / 1e3));
17504
+ const minutes = Math.floor(seconds / 60);
17505
+ const remainder = seconds % 60;
17506
+ return minutes > 0 ? `${minutes}m${String(remainder).padStart(2, "0")}s` : `${seconds}s`;
17507
+ }
17508
+ function markdownLink(label, url) {
17509
+ return `[${label.replace(/\]/g, "\\]")}](${url})`;
17510
+ }
17511
+ function resultLabel(summary) {
17512
+ if (summary.ok === true) return "passed";
17513
+ if (summary.ok === false) return "failed";
17514
+ return summary.status || "recorded";
17515
+ }
17516
+ function artifactRank(artifact) {
17517
+ const name = artifact.name.toLowerCase();
17518
+ if (name === "proof.json") return 0;
17519
+ if (name === "result.json") return 1;
17520
+ if (name.includes("proof") && name.endsWith(".json") && !name.includes("layout")) return 2;
17521
+ if (name === "console.json") return 3;
17522
+ if (artifact.kind === "data") return 10;
17523
+ if (artifact.kind === "image") return 20;
17524
+ return 30;
17525
+ }
17526
+ function buildRiddleProofPrCommentMarkdown(input) {
17527
+ const summary = summarizeRiddleProofPrComment(input);
17528
+ const title = input.title?.trim() || "Riddle Proof Evidence";
17529
+ const lines = [
17530
+ RIDDLE_PROOF_PR_COMMENT_MARKER,
17531
+ `## ${title}`,
17532
+ "",
17533
+ `**Result:** ${resultLabel(summary)}`
17534
+ ];
17535
+ if (input.goal?.trim()) lines.push(`**Goal:** ${input.goal.trim()}`);
17536
+ if (input.successCriteria?.trim()) lines.push(`**Success criteria:** ${input.successCriteria.trim()}`);
17537
+ if (summary.status) lines.push(`**Riddle job status:** ${summary.status}`);
17538
+ if (summary.job_id) lines.push(`**Riddle job:** \`${summary.job_id}\``);
17539
+ if (summary.duration_ms) lines.push(`**Duration:** ${formatDuration(summary.duration_ms)}`);
17540
+ if (summary.proof_url) lines.push(`**Proof URL:** ${markdownLink(summary.proof_url, summary.proof_url)}`);
17541
+ if (summary.preview_id || summary.preview_url) {
17542
+ const previewLabel = summary.preview_id ? `\`${summary.preview_id}\`` : "preview";
17543
+ lines.push(`**Preview:** ${summary.preview_url ? markdownLink(previewLabel, summary.preview_url) : previewLabel}`);
17544
+ }
17545
+ if (summary.preview_publish_recovered) {
17546
+ const detail = summary.preview_publish_error ? `: ${summary.preview_publish_error}` : "";
17547
+ lines.push(`**Preview publish recovery:** recovered after publish error${detail}`);
17548
+ }
17549
+ lines.push(`**Checks:** ${summary.passed_checks} passed / ${summary.failed_checks} failed`);
17550
+ lines.push("");
17551
+ if (summary.primary_image) {
17552
+ lines.push("### Screenshot");
17553
+ lines.push(`![${summary.primary_image.name}](${summary.primary_image.url})`);
17554
+ lines.push("");
17555
+ }
17556
+ if (summary.pages.length) {
17557
+ lines.push("### Page Checks");
17558
+ for (const page of summary.pages.slice(0, 12)) {
17559
+ lines.push(`- \`${page.route}\`: ${page.passed} passed / ${page.failed} failed`);
17560
+ }
17561
+ if (summary.pages.length > 12) lines.push(`- ${summary.pages.length - 12} more page(s) omitted`);
17562
+ lines.push("");
17563
+ }
17564
+ const linkedArtifacts = summary.artifacts.filter((artifact) => artifact.url !== summary.primary_image?.url).sort((left, right) => artifactRank(left) - artifactRank(right) || left.name.localeCompare(right.name)).slice(0, 20);
17565
+ if (linkedArtifacts.length) {
17566
+ lines.push("### Artifacts");
17567
+ for (const artifact of linkedArtifacts) {
17568
+ lines.push(`- ${markdownLink(artifact.name, artifact.url)}`);
17569
+ }
17570
+ if (summary.artifacts.length - (summary.primary_image ? 1 : 0) > linkedArtifacts.length) {
17571
+ lines.push(`- ${summary.artifacts.length - (summary.primary_image ? 1 : 0) - linkedArtifacts.length} more artifact(s) omitted`);
17572
+ }
17573
+ lines.push("");
17574
+ }
17575
+ if (input.source?.trim()) {
17576
+ lines.push(`_Source: ${input.source.trim()}_`);
17577
+ } else {
17578
+ lines.push("_Updated by `riddle-proof-loop pr-comment`._");
17579
+ }
17580
+ return `${lines.join("\n").trim()}
17581
+ `;
17582
+ }
17583
+
17255
17584
  // src/cli.ts
17256
17585
  var RIDDLE_PROFILE_BALANCE_PREFLIGHT_MIN_SECONDS_PER_JOB = 30;
17257
17586
  var KNOWN_CLI_OPTIONS = /* @__PURE__ */ new Set([
@@ -17274,11 +17603,14 @@ var KNOWN_CLI_OPTIONS = /* @__PURE__ */ new Set([
17274
17603
  "codexSandbox",
17275
17604
  "codexTimeoutMs",
17276
17605
  "command",
17606
+ "commentMode",
17277
17607
  "continueWithStage",
17278
17608
  "createdAt",
17279
17609
  "decision",
17280
17610
  "defaultReviewer",
17281
17611
  "defaultShipMode",
17612
+ "bodyFile",
17613
+ "dryRun",
17282
17614
  "exclude",
17283
17615
  "format",
17284
17616
  "framework",
@@ -17306,20 +17638,25 @@ var KNOWN_CLI_OPTIONS = /* @__PURE__ */ new Set([
17306
17638
  "pack",
17307
17639
  "packFile",
17308
17640
  "profile",
17641
+ "proofDir",
17642
+ "pr",
17309
17643
  "progressEveryMs",
17310
17644
  "quiet",
17311
17645
  "readinessPath",
17312
17646
  "readinessTimeout",
17313
17647
  "reasonsJson",
17648
+ "repo",
17314
17649
  "requestJson",
17315
17650
  "requiredJson",
17316
17651
  "responseJson",
17317
17652
  "resultFormat",
17653
+ "resultJson",
17318
17654
  "resultsDir",
17319
17655
  "riddleEngineModuleUrl",
17320
17656
  "riddleProofDir",
17321
17657
  "route",
17322
17658
  "runDir",
17659
+ "runResponse",
17323
17660
  "runner",
17324
17661
  "scriptFile",
17325
17662
  "sourceKind",
@@ -17356,9 +17693,10 @@ function usage() {
17356
17693
  " riddle-proof-loop run-profile aggregate --profile <file|json|-> --url <base-url> [--base-url <base-url>] --input-dir <dir>|--inputs <path[,path...]> [--output <dir>|--output-dir <dir>] [--result-format json|compact-json|summary|none; default json]",
17357
17694
  " riddle-proof-loop run-profile recover --profile <file|json|-> --url <base-url> [--base-url <base-url>] --job <job-id> [--viewport-name <name[,name...]>] [--output <dir>|--output-dir <dir>] [--result-format json|compact-json|summary|none; default json]",
17358
17695
  " riddle-proof-loop regression-pack run [--pack oc-flow-regression|--pack-file <file>] [--local-core true|false; default true] [--hosted-riddle true|false; default false] [--format json|markdown|compact-json; default json] [--output <dir>|--output-dir <dir>]",
17696
+ " riddle-proof-loop pr-comment --proof-dir <dir>|--run-response <file> [--result-json <file>] --pr <number|url> [--repo owner/name] [--dry-run] [--body-file <file>] [--comment-mode update|append]",
17359
17697
  " riddle-proof-loop profile-body-assertions --artifact <file|url|-> --candidates-json <file|json|-> [--required-json <file|json|->] [--format json|body-contains]",
17360
17698
  " riddle-proof-loop profile-http-status-preflight --profile <file|json|-> --url <base-url> [--format json|summary]",
17361
- " riddle-proof-loop riddle-preview-deploy <build-dir> <label> [--framework spa|static]",
17699
+ " riddle-proof-loop riddle-preview-deploy <build-dir> <label> [--framework spa|static] [--quiet]",
17362
17700
  " riddle-proof-loop riddle-server-preview <directory> --script-file <file> [--path /route] [--wait-for-selector selector]",
17363
17701
  " riddle-proof-loop riddle-run-script --url <url> --script-file <file> [--viewport 1280x720] [--strict true|false]",
17364
17702
  " riddle-proof-loop riddle-poll <job-id> [--wait] [--attempts n] [--quiet]",
@@ -18127,6 +18465,18 @@ function formatPollDuration(ms) {
18127
18465
  const remainder = seconds % 60;
18128
18466
  return minutes > 0 ? `${minutes}m${String(remainder).padStart(2, "0")}s` : `${seconds}s`;
18129
18467
  }
18468
+ function formatByteCount(bytes) {
18469
+ if (typeof bytes !== "number" || !Number.isFinite(bytes)) return "n/a";
18470
+ if (bytes < 1024) return `${bytes}B`;
18471
+ const units = ["KB", "MB", "GB", "TB"];
18472
+ let value = bytes / 1024;
18473
+ let unitIndex = 0;
18474
+ while (value >= 1024 && unitIndex < units.length - 1) {
18475
+ value /= 1024;
18476
+ unitIndex += 1;
18477
+ }
18478
+ return `${value >= 10 ? value.toFixed(0) : value.toFixed(1)}${units[unitIndex]}`;
18479
+ }
18130
18480
  function riddlePollProgressLine(snapshot) {
18131
18481
  const submittedAt = snapshot.submitted_at || "not-submitted";
18132
18482
  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)}` : "";
@@ -18140,6 +18490,93 @@ function riddlePollProgressLine(snapshot) {
18140
18490
  `submitted_at=${submittedAt}${queuePart}${terminalPart}`
18141
18491
  ].join(" ");
18142
18492
  }
18493
+ function riddlePreviewProgressLine(snapshot) {
18494
+ const idPart = snapshot.id ? ` id=${snapshot.id}` : "";
18495
+ const statusPart = snapshot.status ? ` status=${snapshot.status}` : "";
18496
+ const attemptPart = snapshot.attempt && snapshot.attempts ? ` attempt=${snapshot.attempt}/${snapshot.attempts}` : "";
18497
+ const previewPart = snapshot.preview_url ? ` url=${snapshot.preview_url}` : "";
18498
+ const filePart = typeof snapshot.file_count === "number" ? ` files=${snapshot.file_count}` : "";
18499
+ const totalPart = typeof snapshot.total_bytes === "number" ? ` bytes=${formatByteCount(snapshot.total_bytes)}` : "";
18500
+ const archivePart = typeof snapshot.tarball_bytes === "number" ? ` archive=${formatByteCount(snapshot.tarball_bytes)}` : "";
18501
+ const recoveryPart = snapshot.publish_error ? ` publish_error=${JSON.stringify(snapshot.publish_error)}` : "";
18502
+ const messagePart = snapshot.message ? ` ${snapshot.message}` : "";
18503
+ return `[riddle-preview] ${snapshot.stage}${idPart}${statusPart}${attemptPart} elapsed=${formatPollDuration(snapshot.elapsed_ms)} label=${snapshot.label} framework=${snapshot.framework}${filePart}${totalPart}${archivePart}${previewPart}${recoveryPart}${messagePart}`;
18504
+ }
18505
+ function readJsonFileIfExists(filePath) {
18506
+ if (!filePath || !(0, import_node_fs6.existsSync)(filePath)) return void 0;
18507
+ return readJsonValue(filePath, filePath);
18508
+ }
18509
+ function defaultProofDirJsonPath(proofDir, filename) {
18510
+ return proofDir ? import_node_path6.default.join(proofDir, filename) : void 0;
18511
+ }
18512
+ function prNumberFromValue(value) {
18513
+ const text = value?.trim();
18514
+ if (!text) return "";
18515
+ const match = text.match(/\/pull\/(\d+)(?:\/|$)/) || text.match(/^#?(\d+)$/);
18516
+ return match ? match[1] : text;
18517
+ }
18518
+ function ghJson(args, input) {
18519
+ const result = (0, import_node_child_process5.spawnSync)("gh", args, {
18520
+ input: typeof input === "undefined" ? void 0 : `${JSON.stringify(input)}
18521
+ `,
18522
+ encoding: "utf-8",
18523
+ timeout: 9e4
18524
+ });
18525
+ if (result.error) throw result.error;
18526
+ if (result.status !== 0) {
18527
+ const detail = (result.stderr || result.stdout || "").trim();
18528
+ throw new Error(`gh ${args.join(" ")} failed${detail ? `: ${detail}` : ""}`);
18529
+ }
18530
+ const stdout = (result.stdout || "").trim();
18531
+ return stdout ? JSON.parse(stdout) : {};
18532
+ }
18533
+ function resolveGhRepoName(repoOption) {
18534
+ if (repoOption?.trim()) return repoOption.trim();
18535
+ const result = (0, import_node_child_process5.spawnSync)("gh", ["repo", "view", "--json", "nameWithOwner", "--jq", ".nameWithOwner"], {
18536
+ encoding: "utf-8",
18537
+ timeout: 3e4
18538
+ });
18539
+ if (result.error) throw result.error;
18540
+ if (result.status !== 0 || !result.stdout.trim()) {
18541
+ const detail = (result.stderr || result.stdout || "").trim();
18542
+ throw new Error(`Could not resolve GitHub repository. Pass --repo owner/name.${detail ? ` gh said: ${detail}` : ""}`);
18543
+ }
18544
+ return result.stdout.trim();
18545
+ }
18546
+ function findManagedPrComment(repo, prNumber) {
18547
+ const comments = ghJson(["api", `repos/${repo}/issues/${prNumber}/comments`, "--paginate"]);
18548
+ return comments.find((comment) => typeof comment.body === "string" && comment.body.includes(RIDDLE_PROOF_PR_COMMENT_MARKER));
18549
+ }
18550
+ function upsertPrComment(input) {
18551
+ const payload = { body: input.body };
18552
+ if (input.mode !== "append") {
18553
+ const existing = findManagedPrComment(input.repo, input.prNumber);
18554
+ if (existing?.id) {
18555
+ return {
18556
+ action: "updated",
18557
+ comment: ghJson([
18558
+ "api",
18559
+ `repos/${input.repo}/issues/comments/${existing.id}`,
18560
+ "-X",
18561
+ "PATCH",
18562
+ "--input",
18563
+ "-"
18564
+ ], payload)
18565
+ };
18566
+ }
18567
+ }
18568
+ return {
18569
+ action: "created",
18570
+ comment: ghJson([
18571
+ "api",
18572
+ `repos/${input.repo}/issues/${input.prNumber}/comments`,
18573
+ "-X",
18574
+ "POST",
18575
+ "--input",
18576
+ "-"
18577
+ ], payload)
18578
+ };
18579
+ }
18143
18580
  function readJsonValue(value, label) {
18144
18581
  if (!value) throw new Error(`${label} is required.`);
18145
18582
  const raw = value === "-" ? readStdin() : (0, import_node_fs6.existsSync)(value) ? (0, import_node_fs6.readFileSync)(value, "utf-8") : value;
@@ -21772,6 +22209,47 @@ async function main() {
21772
22209
  process.exitCode = result.ok ? 0 : 1;
21773
22210
  return;
21774
22211
  }
22212
+ if (command === "pr-comment") {
22213
+ const proofDir = optionString(options, "proofDir") || optionString(options, "outputDir") || positional[1];
22214
+ const runResponsePath = optionString(options, "runResponse") || defaultProofDirJsonPath(proofDir, "riddle-run-response.json");
22215
+ const resultJsonPath = optionString(options, "resultJson") || defaultProofDirJsonPath(proofDir, "result.json");
22216
+ const runResponse = readJsonFileIfExists(runResponsePath);
22217
+ const result = readJsonFileIfExists(resultJsonPath);
22218
+ if (!runResponse && !result) {
22219
+ throw new Error("pr-comment requires --proof-dir with riddle-run-response.json/result.json or explicit --run-response/--result-json.");
22220
+ }
22221
+ const body = buildRiddleProofPrCommentMarkdown({
22222
+ title: optionString(options, "title"),
22223
+ goal: optionString(options, "summary"),
22224
+ successCriteria: optionString(options, "successCriteria"),
22225
+ runResponse,
22226
+ result
22227
+ });
22228
+ const bodyFile = optionString(options, "bodyFile");
22229
+ if (bodyFile) (0, import_node_fs6.writeFileSync)(bodyFile, body);
22230
+ if (optionBoolean(options, "dryRun")) {
22231
+ process.stdout.write(body);
22232
+ return;
22233
+ }
22234
+ const prNumber = prNumberFromValue(optionString(options, "pr"));
22235
+ if (!prNumber) throw new Error("pr-comment requires --pr <number|url> unless --dry-run is used.");
22236
+ const commentMode = optionString(options, "commentMode") || "update";
22237
+ if (!["update", "append"].includes(commentMode)) throw new Error("--comment-mode must be update or append.");
22238
+ const repo = resolveGhRepoName(optionString(options, "repo"));
22239
+ const resultPayload = upsertPrComment({ repo, prNumber, body, mode: commentMode });
22240
+ const comment = resultPayload.comment;
22241
+ process.stdout.write(`${JSON.stringify({
22242
+ ok: true,
22243
+ action: resultPayload.action,
22244
+ repo,
22245
+ pr: prNumber,
22246
+ comment_url: cliString(comment.html_url) || null,
22247
+ summary: summarizeRiddleProofPrComment({ runResponse, result }),
22248
+ body_file: bodyFile || null
22249
+ }, null, 2)}
22250
+ `);
22251
+ return;
22252
+ }
21775
22253
  if (command === "profile-http-status-preflight") {
21776
22254
  const profile = normalizeProfileForCli(options);
21777
22255
  const result = await preflightRiddleProofProfileHttpStatusChecks(profile);
@@ -21815,7 +22293,14 @@ async function main() {
21815
22293
  if (command === "riddle-preview-deploy") {
21816
22294
  const buildDir = positional[1];
21817
22295
  const label = positional[2];
21818
- const result = await createRiddleApiClient(riddleClientConfig(options)).deployPreview(buildDir, label, previewFrameworkOption(options));
22296
+ const clientConfig = riddleClientConfig(options);
22297
+ if (options.quiet !== true) {
22298
+ clientConfig.onPreviewProgress = (snapshot) => {
22299
+ process.stderr.write(`${riddlePreviewProgressLine(snapshot)}
22300
+ `);
22301
+ };
22302
+ }
22303
+ const result = await createRiddleApiClient(clientConfig).deployPreview(buildDir, label, previewFrameworkOption(options));
21819
22304
  for (const warning of result.warnings ?? []) {
21820
22305
  process.stderr.write(`Warning: ${warning}
21821
22306
  `);
package/dist/cli.js CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import "./chunk-E25K5PDM.js";
2
+ import "./chunk-U44KBAPH.js";
3
+ import "./chunk-DI2XNGEZ.js";
4
+ import "./chunk-6KYXX4OE.js";
3
5
  import "./chunk-Z2LCVROU.js";
4
- import "./chunk-TWTEUS7R.js";
5
6
  import "./chunk-ZREWMTFA.js";
6
7
  import "./chunk-ZQWVXQKJ.js";
7
8
  import "./chunk-RDPG554T.js";