@riddledc/riddle-proof 0.5.53 → 0.5.54

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.
@@ -499,8 +499,8 @@ function buildProofAssessmentCheckpointPacket(input) {
499
499
  const artifactContract = recordValue(proofAssessmentRequest.artifact_contract) || recordValue(bundle?.artifact_contract);
500
500
  const requiredSignals = recordValue(recordValue(artifactContract)?.required);
501
501
  const visualDelta = visualDeltaFromState(fullState);
502
- const verificationMode = nonEmptyString(input.request.verification_mode) || nonEmptyString(fullState.verification_mode) || nonEmptyString(bundle?.verification_mode) || "proof";
503
- const visualDeltaRequired = requiredSignals?.visual_delta === true || verificationModeRequiresVisualDelta(verificationMode);
502
+ const verificationMode = nonEmptyString(bundle?.verification_mode) || nonEmptyString(fullState.verification_mode) || nonEmptyString(input.request.verification_mode) || "proof";
503
+ const visualDeltaRequired = requiredSignals?.visual_delta !== false && (requiredSignals?.visual_delta === true || verificationModeRequiresVisualDelta(verificationMode));
504
504
  const evidenceIssueCode = visualDeltaIssueCode(visualDelta, visualDeltaRequired);
505
505
  const summary = nonEmptyString(input.engineResult.summary) || nonEmptyString(fullState.verify_summary) || "Verify captured evidence and needs a supervising proof assessment.";
506
506
  const recoveryHint = evidenceIssueCode ? "Required visual_delta evidence is incomplete. Keep this same run in verify/evidence recovery with decision=revise_capture and continue_with_stage=verify unless the evidence proves an implementation or recon problem." : "Assess whether the current artifacts prove the requested change, then choose the next stage.";
@@ -13,7 +13,7 @@ import {
13
13
  normalizeCheckpointResponse,
14
14
  proofContractFromAuthorCheckpointResponse,
15
15
  statePathsForRunState
16
- } from "./chunk-T5RHGGQ2.js";
16
+ } from "./chunk-33XO42CY.js";
17
17
  import "./chunk-DUFDZJOF.js";
18
18
  export {
19
19
  RIDDLE_PROOF_CHECKPOINT_PACKET_VERSION,
@@ -445,8 +445,8 @@ function buildProofAssessmentCheckpointPacket(input) {
445
445
  const artifactContract = recordValue(proofAssessmentRequest.artifact_contract) || recordValue(bundle?.artifact_contract);
446
446
  const requiredSignals = recordValue(recordValue(artifactContract)?.required);
447
447
  const visualDelta = visualDeltaFromState(fullState);
448
- const verificationMode = nonEmptyString(input.request.verification_mode) || nonEmptyString(fullState.verification_mode) || nonEmptyString(bundle?.verification_mode) || "proof";
449
- const visualDeltaRequired = requiredSignals?.visual_delta === true || verificationModeRequiresVisualDelta(verificationMode);
448
+ const verificationMode = nonEmptyString(bundle?.verification_mode) || nonEmptyString(fullState.verification_mode) || nonEmptyString(input.request.verification_mode) || "proof";
449
+ const visualDeltaRequired = requiredSignals?.visual_delta !== false && (requiredSignals?.visual_delta === true || verificationModeRequiresVisualDelta(verificationMode));
450
450
  const evidenceIssueCode = visualDeltaIssueCode(visualDelta, visualDeltaRequired);
451
451
  const summary = nonEmptyString(input.engineResult.summary) || nonEmptyString(fullState.verify_summary) || "Verify captured evidence and needs a supervising proof assessment.";
452
452
  const recoveryHint = evidenceIssueCode ? "Required visual_delta evidence is incomplete. Keep this same run in verify/evidence recovery with decision=revise_capture and continue_with_stage=verify unless the evidence proves an implementation or recon problem." : "Assess whether the current artifacts prove the requested change, then choose the next stage.";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  statePathsForRunState
3
- } from "./chunk-T5RHGGQ2.js";
3
+ } from "./chunk-33XO42CY.js";
4
4
  import {
5
5
  compactRecord,
6
6
  isTerminalStatus,
@@ -2,7 +2,7 @@ import {
2
2
  visualDeltaForState,
3
3
  visualDeltaRequiredForState,
4
4
  visualDeltaShipGateReason
5
- } from "./chunk-4ASMX4R6.js";
5
+ } from "./chunk-RFJ5BQF6.js";
6
6
  import {
7
7
  appendRunEvent,
8
8
  appendStageHeartbeat,
@@ -10,10 +10,10 @@ import {
10
10
  createRunStatusSnapshot,
11
11
  normalizeRunParams,
12
12
  setRunStatus
13
- } from "./chunk-PVUZZ2P6.js";
13
+ } from "./chunk-MO24D3PY.js";
14
14
  import {
15
15
  createRiddleProofRunCard
16
- } from "./chunk-53UPEUVU.js";
16
+ } from "./chunk-3UHWI3FO.js";
17
17
  import {
18
18
  authorPacketPayloadFromCheckpointResponse,
19
19
  buildCheckpointPacketForEngineResult,
@@ -23,7 +23,7 @@ import {
23
23
  normalizeCheckpointResponse,
24
24
  proofContractFromAuthorCheckpointResponse,
25
25
  statePathsForRunState
26
- } from "./chunk-T5RHGGQ2.js";
26
+ } from "./chunk-33XO42CY.js";
27
27
  import {
28
28
  applyTerminalMetadata,
29
29
  compactRecord,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createRiddleProofRunCard
3
- } from "./chunk-53UPEUVU.js";
3
+ } from "./chunk-3UHWI3FO.js";
4
4
  import {
5
5
  compactRecord,
6
6
  isTerminalStatus,
@@ -108,6 +108,7 @@ function normalizeRunParams(input) {
108
108
  leave_draft: input.leave_draft,
109
109
  engine_state_path: input.engine_state_path,
110
110
  harness_state_path: input.harness_state_path,
111
+ riddle_engine_module_url: input.riddle_engine_module_url,
111
112
  max_iterations: input.max_iterations,
112
113
  auto_approve: input.auto_approve,
113
114
  dry_run: input.dry_run,
@@ -355,6 +355,7 @@ function visualDeltaRequiredForState(state = {}) {
355
355
  const bundle = objectValue(state?.evidence_bundle);
356
356
  const contract = objectValue(bundle.artifact_contract);
357
357
  const required = objectValue(contract.required);
358
+ if (required.visual_delta === false) return false;
358
359
  return required.visual_delta === true || VISUAL_FIRST_MODES.has(normalizedVerificationMode(state));
359
360
  }
360
361
  function visualDeltaForState(state = {}) {
@@ -3,7 +3,7 @@ import {
3
3
  appendStageHeartbeat,
4
4
  createRunState,
5
5
  setRunStatus
6
- } from "./chunk-PVUZZ2P6.js";
6
+ } from "./chunk-MO24D3PY.js";
7
7
  import {
8
8
  createRunResult
9
9
  } from "./chunk-DUFDZJOF.js";
@@ -0,0 +1,163 @@
1
+ // src/riddle-client.ts
2
+ import { execFileSync } from "child_process";
3
+ import { existsSync, mkdtempSync, readFileSync, rmSync } from "fs";
4
+ import { tmpdir } from "os";
5
+ import path from "path";
6
+ var DEFAULT_RIDDLE_API_BASE_URL = "https://api.riddledc.com";
7
+ var DEFAULT_RIDDLE_API_KEY_FILE = "/tmp/riddle-api-key";
8
+ var RiddleApiError = class extends Error {
9
+ constructor(pathname, status, body) {
10
+ super(`Riddle API ${pathname} failed HTTP ${status}: ${body}`);
11
+ this.name = "RiddleApiError";
12
+ this.status = status;
13
+ this.body = body;
14
+ this.path = pathname;
15
+ }
16
+ };
17
+ function normalizeBaseUrl(value) {
18
+ return (value || DEFAULT_RIDDLE_API_BASE_URL).replace(/\/$/, "");
19
+ }
20
+ function fetchFor(config = {}) {
21
+ return config.fetchImpl || fetch;
22
+ }
23
+ function resolveRiddleApiKey(config = {}) {
24
+ if (config.apiKey?.trim()) return config.apiKey.trim();
25
+ if (process.env.RIDDLE_API_KEY?.trim()) return process.env.RIDDLE_API_KEY.trim();
26
+ const keyFile = config.apiKeyFile || process.env.RIDDLE_API_KEY_FILE || DEFAULT_RIDDLE_API_KEY_FILE;
27
+ if (existsSync(keyFile)) {
28
+ const key = readFileSync(keyFile, "utf8").trim();
29
+ if (key) return key;
30
+ }
31
+ throw new Error(`Riddle API key missing. Set RIDDLE_API_KEY or write ${DEFAULT_RIDDLE_API_KEY_FILE}.`);
32
+ }
33
+ async function riddleRequestJson(config, pathname, init = {}) {
34
+ const response = await fetchFor(config)(`${normalizeBaseUrl(config.apiBaseUrl)}${pathname}`, {
35
+ ...init,
36
+ headers: {
37
+ Authorization: `Bearer ${resolveRiddleApiKey(config)}`,
38
+ "Content-Type": "application/json",
39
+ ...init.headers || {}
40
+ }
41
+ });
42
+ const text = await response.text();
43
+ let json = null;
44
+ try {
45
+ json = JSON.parse(text);
46
+ } catch {
47
+ }
48
+ if (!response.ok) throw new RiddleApiError(pathname, response.status, text);
49
+ return json ?? text;
50
+ }
51
+ async function deployRiddleStaticPreview(config, directory, label) {
52
+ if (!directory?.trim()) throw new Error("directory is required");
53
+ if (!label?.trim()) throw new Error("label is required");
54
+ const created = await riddleRequestJson(config, "/v1/preview", {
55
+ method: "POST",
56
+ body: JSON.stringify({ framework: "spa", label })
57
+ });
58
+ const id = String(created.id || "");
59
+ const uploadUrl = String(created.upload_url || "");
60
+ if (!id || !uploadUrl) throw new Error("Riddle preview create response was missing id or upload_url.");
61
+ const scratch = mkdtempSync(path.join(tmpdir(), "riddle-preview-upload-"));
62
+ const tarball = path.join(scratch, `${id}.tar.gz`);
63
+ try {
64
+ execFileSync("tar", ["czf", tarball, "-C", directory, "."], { stdio: "pipe" });
65
+ const upload = await fetchFor(config)(uploadUrl, {
66
+ method: "PUT",
67
+ headers: { "Content-Type": "application/gzip" },
68
+ body: readFileSync(tarball)
69
+ });
70
+ if (!upload.ok) {
71
+ throw new RiddleApiError(uploadUrl, upload.status, await upload.text());
72
+ }
73
+ } finally {
74
+ rmSync(scratch, { recursive: true, force: true });
75
+ }
76
+ const published = await riddleRequestJson(config, `/v1/preview/${id}/publish`, {
77
+ method: "POST"
78
+ });
79
+ return {
80
+ ok: true,
81
+ id: String(published.id || id),
82
+ label,
83
+ preview_url: String(published.preview_url || ""),
84
+ file_count: typeof published.file_count === "number" ? published.file_count : void 0,
85
+ total_bytes: typeof published.total_bytes === "number" ? published.total_bytes : void 0,
86
+ expires_at: typeof created.expires_at === "string" ? created.expires_at : void 0,
87
+ raw: published
88
+ };
89
+ }
90
+ function parseRiddleViewport(value) {
91
+ if (!value) return void 0;
92
+ const match = /^(\d+)x(\d+)$/.exec(value.trim());
93
+ if (!match) throw new Error("viewport must look like 1280x720");
94
+ return { width: Number(match[1]), height: Number(match[2]) };
95
+ }
96
+ async function runRiddleScript(config, input) {
97
+ if (!input.url?.trim()) throw new Error("url is required");
98
+ if (!input.script?.trim()) throw new Error("script is required");
99
+ const payload = {
100
+ url: input.url,
101
+ script: input.script,
102
+ sync: input.sync ?? false,
103
+ timeout_sec: input.timeoutSec || 120
104
+ };
105
+ if (input.viewport) payload.viewport = input.viewport;
106
+ if (input.include) payload.include = input.include;
107
+ if (input.options) payload.options = input.options;
108
+ return riddleRequestJson(config, "/v1/run", {
109
+ method: "POST",
110
+ body: JSON.stringify(payload)
111
+ });
112
+ }
113
+ function isTerminalRiddleJobStatus(status) {
114
+ return ["completed", "complete", "completed_error", "completed_timeout", "failed"].includes(String(status || ""));
115
+ }
116
+ async function pollRiddleJob(config, jobId, options = {}) {
117
+ if (!jobId?.trim()) throw new Error("jobId is required");
118
+ const attempts = options.attempts || (options.wait ? 60 : 1);
119
+ const intervalMs = options.intervalMs || 2e3;
120
+ let job = null;
121
+ for (let index = 0; index < attempts; index += 1) {
122
+ job = await riddleRequestJson(config, `/v1/jobs/${jobId}`);
123
+ if (isTerminalRiddleJobStatus(job.status)) break;
124
+ if (index + 1 < attempts) {
125
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
126
+ }
127
+ }
128
+ const status = job?.status ? String(job.status) : null;
129
+ if (!isTerminalRiddleJobStatus(status)) {
130
+ return { ok: true, job_id: jobId, status, terminal: false, job };
131
+ }
132
+ const artifacts = await riddleRequestJson(config, `/v1/jobs/${jobId}/artifacts`);
133
+ return {
134
+ ok: status === "completed" || status === "complete",
135
+ job_id: jobId,
136
+ status,
137
+ terminal: true,
138
+ job,
139
+ artifacts
140
+ };
141
+ }
142
+ function createRiddleApiClient(config = {}) {
143
+ return {
144
+ requestJson: (pathname, init) => riddleRequestJson(config, pathname, init),
145
+ deployStaticPreview: (directory, label) => deployRiddleStaticPreview(config, directory, label),
146
+ runScript: (input) => runRiddleScript(config, input),
147
+ pollJob: (jobId, options) => pollRiddleJob(config, jobId, options)
148
+ };
149
+ }
150
+
151
+ export {
152
+ DEFAULT_RIDDLE_API_BASE_URL,
153
+ DEFAULT_RIDDLE_API_KEY_FILE,
154
+ RiddleApiError,
155
+ resolveRiddleApiKey,
156
+ riddleRequestJson,
157
+ deployRiddleStaticPreview,
158
+ parseRiddleViewport,
159
+ runRiddleScript,
160
+ isTerminalRiddleJobStatus,
161
+ pollRiddleJob,
162
+ createRiddleApiClient
163
+ };
package/dist/cli.cjs CHANGED
@@ -363,6 +363,7 @@ function visualDeltaRequiredForState(state = {}) {
363
363
  const bundle = objectValue(state?.evidence_bundle);
364
364
  const contract = objectValue(bundle.artifact_contract);
365
365
  const required = objectValue(contract.required);
366
+ if (required.visual_delta === false) return false;
366
367
  return required.visual_delta === true || VISUAL_FIRST_MODES.has(normalizedVerificationMode(state));
367
368
  }
368
369
  function visualDeltaForState(state = {}) {
@@ -1407,9 +1408,9 @@ function cleanupMergedProofRun(state, repoDir, params, prState) {
1407
1408
  const baseBranch = stringValue(prState.base_branch) || stringValue(state?.base_branch) || "main";
1408
1409
  let fetchedBase = params.fetch_base === false;
1409
1410
  if (params.fetch_base !== false && baseBranch) {
1410
- const fetch = commandResult("git", ["fetch", "origin", baseBranch], repoDir, 12e4);
1411
- cleanup.fetch = fetch.ok ? { ok: true, base_branch: baseBranch } : { ok: false, base_branch: baseBranch, error: fetch.stderr.slice(0, 300) };
1412
- if (fetch.ok) {
1411
+ const fetch2 = commandResult("git", ["fetch", "origin", baseBranch], repoDir, 12e4);
1412
+ cleanup.fetch = fetch2.ok ? { ok: true, base_branch: baseBranch } : { ok: false, base_branch: baseBranch, error: fetch2.stderr.slice(0, 300) };
1413
+ if (fetch2.ok) {
1413
1414
  fetchedBase = true;
1414
1415
  state.base_synced_at = (/* @__PURE__ */ new Date()).toISOString();
1415
1416
  state.base_branch = baseBranch;
@@ -2770,7 +2771,7 @@ var init_proof_run_engine = __esm({
2770
2771
  });
2771
2772
 
2772
2773
  // src/cli.ts
2773
- var import_node_fs5 = require("fs");
2774
+ var import_node_fs6 = require("fs");
2774
2775
 
2775
2776
  // src/engine-harness.ts
2776
2777
  var import_node_child_process2 = require("child_process");
@@ -3434,8 +3435,8 @@ function buildProofAssessmentCheckpointPacket(input) {
3434
3435
  const artifactContract = recordValue(proofAssessmentRequest.artifact_contract) || recordValue(bundle?.artifact_contract);
3435
3436
  const requiredSignals = recordValue(recordValue(artifactContract)?.required);
3436
3437
  const visualDelta = visualDeltaFromState(fullState);
3437
- const verificationMode = nonEmptyString(input.request.verification_mode) || nonEmptyString(fullState.verification_mode) || nonEmptyString(bundle?.verification_mode) || "proof";
3438
- const visualDeltaRequired = requiredSignals?.visual_delta === true || verificationModeRequiresVisualDelta(verificationMode);
3438
+ const verificationMode = nonEmptyString(bundle?.verification_mode) || nonEmptyString(fullState.verification_mode) || nonEmptyString(input.request.verification_mode) || "proof";
3439
+ const visualDeltaRequired = requiredSignals?.visual_delta !== false && (requiredSignals?.visual_delta === true || verificationModeRequiresVisualDelta(verificationMode));
3439
3440
  const evidenceIssueCode2 = visualDeltaIssueCode(visualDelta, visualDeltaRequired);
3440
3441
  const summary = nonEmptyString(input.engineResult.summary) || nonEmptyString(fullState.verify_summary) || "Verify captured evidence and needs a supervising proof assessment.";
3441
3442
  const recoveryHint = evidenceIssueCode2 ? "Required visual_delta evidence is incomplete. Keep this same run in verify/evidence recovery with decision=revise_capture and continue_with_stage=verify unless the evidence proves an implementation or recon problem." : "Assess whether the current artifacts prove the requested change, then choose the next stage.";
@@ -3982,6 +3983,7 @@ function normalizeRunParams(input) {
3982
3983
  leave_draft: input.leave_draft,
3983
3984
  engine_state_path: input.engine_state_path,
3984
3985
  harness_state_path: input.harness_state_path,
3986
+ riddle_engine_module_url: input.riddle_engine_module_url,
3985
3987
  max_iterations: input.max_iterations,
3986
3988
  auto_approve: input.auto_approve,
3987
3989
  dry_run: input.dry_run,
@@ -6396,6 +6398,156 @@ async function runCodexExecAgentDoctor(config = {}, runner = createCodexExecJson
6396
6398
  };
6397
6399
  }
6398
6400
 
6401
+ // src/riddle-client.ts
6402
+ var import_node_child_process4 = require("child_process");
6403
+ var import_node_fs5 = require("fs");
6404
+ var import_node_os2 = require("os");
6405
+ var import_node_path5 = __toESM(require("path"), 1);
6406
+ var DEFAULT_RIDDLE_API_BASE_URL = "https://api.riddledc.com";
6407
+ var DEFAULT_RIDDLE_API_KEY_FILE = "/tmp/riddle-api-key";
6408
+ var RiddleApiError = class extends Error {
6409
+ constructor(pathname, status, body) {
6410
+ super(`Riddle API ${pathname} failed HTTP ${status}: ${body}`);
6411
+ this.name = "RiddleApiError";
6412
+ this.status = status;
6413
+ this.body = body;
6414
+ this.path = pathname;
6415
+ }
6416
+ };
6417
+ function normalizeBaseUrl(value) {
6418
+ return (value || DEFAULT_RIDDLE_API_BASE_URL).replace(/\/$/, "");
6419
+ }
6420
+ function fetchFor(config = {}) {
6421
+ return config.fetchImpl || fetch;
6422
+ }
6423
+ function resolveRiddleApiKey(config = {}) {
6424
+ if (config.apiKey?.trim()) return config.apiKey.trim();
6425
+ if (process.env.RIDDLE_API_KEY?.trim()) return process.env.RIDDLE_API_KEY.trim();
6426
+ const keyFile = config.apiKeyFile || process.env.RIDDLE_API_KEY_FILE || DEFAULT_RIDDLE_API_KEY_FILE;
6427
+ if ((0, import_node_fs5.existsSync)(keyFile)) {
6428
+ const key = (0, import_node_fs5.readFileSync)(keyFile, "utf8").trim();
6429
+ if (key) return key;
6430
+ }
6431
+ throw new Error(`Riddle API key missing. Set RIDDLE_API_KEY or write ${DEFAULT_RIDDLE_API_KEY_FILE}.`);
6432
+ }
6433
+ async function riddleRequestJson(config, pathname, init = {}) {
6434
+ const response = await fetchFor(config)(`${normalizeBaseUrl(config.apiBaseUrl)}${pathname}`, {
6435
+ ...init,
6436
+ headers: {
6437
+ Authorization: `Bearer ${resolveRiddleApiKey(config)}`,
6438
+ "Content-Type": "application/json",
6439
+ ...init.headers || {}
6440
+ }
6441
+ });
6442
+ const text = await response.text();
6443
+ let json = null;
6444
+ try {
6445
+ json = JSON.parse(text);
6446
+ } catch {
6447
+ }
6448
+ if (!response.ok) throw new RiddleApiError(pathname, response.status, text);
6449
+ return json ?? text;
6450
+ }
6451
+ async function deployRiddleStaticPreview(config, directory, label) {
6452
+ if (!directory?.trim()) throw new Error("directory is required");
6453
+ if (!label?.trim()) throw new Error("label is required");
6454
+ const created = await riddleRequestJson(config, "/v1/preview", {
6455
+ method: "POST",
6456
+ body: JSON.stringify({ framework: "spa", label })
6457
+ });
6458
+ const id = String(created.id || "");
6459
+ const uploadUrl = String(created.upload_url || "");
6460
+ if (!id || !uploadUrl) throw new Error("Riddle preview create response was missing id or upload_url.");
6461
+ const scratch = (0, import_node_fs5.mkdtempSync)(import_node_path5.default.join((0, import_node_os2.tmpdir)(), "riddle-preview-upload-"));
6462
+ const tarball = import_node_path5.default.join(scratch, `${id}.tar.gz`);
6463
+ try {
6464
+ (0, import_node_child_process4.execFileSync)("tar", ["czf", tarball, "-C", directory, "."], { stdio: "pipe" });
6465
+ const upload = await fetchFor(config)(uploadUrl, {
6466
+ method: "PUT",
6467
+ headers: { "Content-Type": "application/gzip" },
6468
+ body: (0, import_node_fs5.readFileSync)(tarball)
6469
+ });
6470
+ if (!upload.ok) {
6471
+ throw new RiddleApiError(uploadUrl, upload.status, await upload.text());
6472
+ }
6473
+ } finally {
6474
+ (0, import_node_fs5.rmSync)(scratch, { recursive: true, force: true });
6475
+ }
6476
+ const published = await riddleRequestJson(config, `/v1/preview/${id}/publish`, {
6477
+ method: "POST"
6478
+ });
6479
+ return {
6480
+ ok: true,
6481
+ id: String(published.id || id),
6482
+ label,
6483
+ preview_url: String(published.preview_url || ""),
6484
+ file_count: typeof published.file_count === "number" ? published.file_count : void 0,
6485
+ total_bytes: typeof published.total_bytes === "number" ? published.total_bytes : void 0,
6486
+ expires_at: typeof created.expires_at === "string" ? created.expires_at : void 0,
6487
+ raw: published
6488
+ };
6489
+ }
6490
+ function parseRiddleViewport(value) {
6491
+ if (!value) return void 0;
6492
+ const match = /^(\d+)x(\d+)$/.exec(value.trim());
6493
+ if (!match) throw new Error("viewport must look like 1280x720");
6494
+ return { width: Number(match[1]), height: Number(match[2]) };
6495
+ }
6496
+ async function runRiddleScript(config, input) {
6497
+ if (!input.url?.trim()) throw new Error("url is required");
6498
+ if (!input.script?.trim()) throw new Error("script is required");
6499
+ const payload = {
6500
+ url: input.url,
6501
+ script: input.script,
6502
+ sync: input.sync ?? false,
6503
+ timeout_sec: input.timeoutSec || 120
6504
+ };
6505
+ if (input.viewport) payload.viewport = input.viewport;
6506
+ if (input.include) payload.include = input.include;
6507
+ if (input.options) payload.options = input.options;
6508
+ return riddleRequestJson(config, "/v1/run", {
6509
+ method: "POST",
6510
+ body: JSON.stringify(payload)
6511
+ });
6512
+ }
6513
+ function isTerminalRiddleJobStatus(status) {
6514
+ return ["completed", "complete", "completed_error", "completed_timeout", "failed"].includes(String(status || ""));
6515
+ }
6516
+ async function pollRiddleJob(config, jobId, options = {}) {
6517
+ if (!jobId?.trim()) throw new Error("jobId is required");
6518
+ const attempts = options.attempts || (options.wait ? 60 : 1);
6519
+ const intervalMs = options.intervalMs || 2e3;
6520
+ let job = null;
6521
+ for (let index = 0; index < attempts; index += 1) {
6522
+ job = await riddleRequestJson(config, `/v1/jobs/${jobId}`);
6523
+ if (isTerminalRiddleJobStatus(job.status)) break;
6524
+ if (index + 1 < attempts) {
6525
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
6526
+ }
6527
+ }
6528
+ const status = job?.status ? String(job.status) : null;
6529
+ if (!isTerminalRiddleJobStatus(status)) {
6530
+ return { ok: true, job_id: jobId, status, terminal: false, job };
6531
+ }
6532
+ const artifacts = await riddleRequestJson(config, `/v1/jobs/${jobId}/artifacts`);
6533
+ return {
6534
+ ok: status === "completed" || status === "complete",
6535
+ job_id: jobId,
6536
+ status,
6537
+ terminal: true,
6538
+ job,
6539
+ artifacts
6540
+ };
6541
+ }
6542
+ function createRiddleApiClient(config = {}) {
6543
+ return {
6544
+ requestJson: (pathname, init) => riddleRequestJson(config, pathname, init),
6545
+ deployStaticPreview: (directory, label) => deployRiddleStaticPreview(config, directory, label),
6546
+ runScript: (input) => runRiddleScript(config, input),
6547
+ pollJob: (jobId, options) => pollRiddleJob(config, jobId, options)
6548
+ };
6549
+ }
6550
+
6399
6551
  // src/cli.ts
6400
6552
  function usage() {
6401
6553
  return [
@@ -6405,6 +6557,9 @@ function usage() {
6405
6557
  " riddle-proof-loop respond --state-path <path> --response-json <file|json|->",
6406
6558
  " riddle-proof-loop respond --state-path <path> --decision <decision> --summary <text> [--payload-json <file|json|->]",
6407
6559
  " riddle-proof-loop status --state-path <path>",
6560
+ " riddle-proof-loop riddle-preview-deploy <build-dir> <label>",
6561
+ " riddle-proof-loop riddle-run-script --url <url> --script-file <file> [--viewport 1280x720]",
6562
+ " riddle-proof-loop riddle-poll <job-id> [--wait]",
6408
6563
  " riddle-proof-loop doctor local [--codex-command <path>]",
6409
6564
  "",
6410
6565
  "The default CLI run mode is checkpoint-mode=yield unless --agent local is used.",
@@ -6436,11 +6591,11 @@ function optionString(options, key) {
6436
6591
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
6437
6592
  }
6438
6593
  function readStdin() {
6439
- return (0, import_node_fs5.readFileSync)(0, "utf-8");
6594
+ return (0, import_node_fs6.readFileSync)(0, "utf-8");
6440
6595
  }
6441
6596
  function readJsonValue(value, label) {
6442
6597
  if (!value) throw new Error(`${label} is required.`);
6443
- const raw = value === "-" ? readStdin() : (0, import_node_fs5.existsSync)(value) ? (0, import_node_fs5.readFileSync)(value, "utf-8") : value;
6598
+ const raw = value === "-" ? readStdin() : (0, import_node_fs6.existsSync)(value) ? (0, import_node_fs6.readFileSync)(value, "utf-8") : value;
6444
6599
  const parsed = JSON.parse(raw);
6445
6600
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
6446
6601
  throw new Error(`${label} must be a JSON object.`);
@@ -6453,7 +6608,7 @@ function readOptionalJsonRecord(value, label) {
6453
6608
  }
6454
6609
  function readOptionalJsonStringArray(value, label) {
6455
6610
  if (!value) return void 0;
6456
- const parsed = value === "-" ? JSON.parse(readStdin()) : (0, import_node_fs5.existsSync)(value) ? JSON.parse((0, import_node_fs5.readFileSync)(value, "utf-8")) : JSON.parse(value);
6611
+ const parsed = value === "-" ? JSON.parse(readStdin()) : (0, import_node_fs6.existsSync)(value) ? JSON.parse((0, import_node_fs6.readFileSync)(value, "utf-8")) : JSON.parse(value);
6457
6612
  if (!Array.isArray(parsed) || parsed.some((item) => typeof item !== "string")) {
6458
6613
  throw new Error(`${label} must be a JSON array of strings.`);
6459
6614
  }
@@ -6585,14 +6740,28 @@ function agentFor(options) {
6585
6740
  if (agentMode === "disabled") return createDisabledRiddleProofAgentAdapter();
6586
6741
  throw new Error(`Unsupported --agent ${agentMode}. Use disabled or local.`);
6587
6742
  }
6743
+ function riddleClientConfig(options) {
6744
+ return {
6745
+ apiKey: optionString(options, "apiKey"),
6746
+ apiKeyFile: optionString(options, "apiKeyFile"),
6747
+ apiBaseUrl: optionString(options, "apiBaseUrl")
6748
+ };
6749
+ }
6588
6750
  function requestForRun(options) {
6589
6751
  const statePath = optionString(options, "statePath");
6752
+ const withEngineModuleUrl = (request) => {
6753
+ const moduleUrl = optionString(options, "riddleEngineModuleUrl");
6754
+ return moduleUrl && !request.riddle_engine_module_url ? { ...request, riddle_engine_module_url: moduleUrl } : request;
6755
+ };
6590
6756
  if (optionString(options, "requestJson")) {
6591
- return readJsonValue(optionString(options, "requestJson"), "--request-json");
6757
+ return withEngineModuleUrl(readJsonValue(optionString(options, "requestJson"), "--request-json"));
6592
6758
  }
6593
- if (statePath) return readRunState(statePath).request;
6759
+ if (statePath) return withEngineModuleUrl(readRunState(statePath).request);
6594
6760
  throw new Error("--request-json is required unless --state-path points to an existing run state.");
6595
6761
  }
6762
+ function riddleEngineModuleUrlFor(options, request) {
6763
+ return optionString(options, "riddleEngineModuleUrl") || (typeof request.riddle_engine_module_url === "string" && request.riddle_engine_module_url.trim() ? request.riddle_engine_module_url.trim() : void 0);
6764
+ }
6596
6765
  function checkpointModeFor(options) {
6597
6766
  const explicit = optionString(options, "checkpointMode");
6598
6767
  if (explicit) return explicit;
@@ -6624,6 +6793,42 @@ async function main() {
6624
6793
  `);
6625
6794
  return;
6626
6795
  }
6796
+ if (command === "riddle-preview-deploy") {
6797
+ const buildDir = positional[1];
6798
+ const label = positional[2];
6799
+ const result = await createRiddleApiClient(riddleClientConfig(options)).deployStaticPreview(buildDir, label);
6800
+ process.stdout.write(`${JSON.stringify(result, null, 2)}
6801
+ `);
6802
+ return;
6803
+ }
6804
+ if (command === "riddle-run-script") {
6805
+ const url = optionString(options, "url");
6806
+ const scriptFile = optionString(options, "scriptFile");
6807
+ if (!url || !scriptFile) throw new Error("riddle-run-script requires --url and --script-file.");
6808
+ const result = await createRiddleApiClient(riddleClientConfig(options)).runScript({
6809
+ url,
6810
+ script: (0, import_node_fs6.readFileSync)(scriptFile, "utf-8"),
6811
+ viewport: parseRiddleViewport(optionString(options, "viewport")),
6812
+ timeoutSec: optionString(options, "timeout") ? Number(optionString(options, "timeout")) : void 0,
6813
+ sync: options.sync === true ? true : void 0
6814
+ });
6815
+ process.stdout.write(`${JSON.stringify(result, null, 2)}
6816
+ `);
6817
+ return;
6818
+ }
6819
+ if (command === "riddle-poll") {
6820
+ const jobId = positional[1];
6821
+ if (!jobId) throw new Error("riddle-poll requires <job-id>.");
6822
+ const result = await createRiddleApiClient(riddleClientConfig(options)).pollJob(jobId, {
6823
+ wait: options.wait === true,
6824
+ attempts: optionString(options, "attempts") ? Number(optionString(options, "attempts")) : void 0,
6825
+ intervalMs: optionString(options, "intervalMs") ? Number(optionString(options, "intervalMs")) : void 0
6826
+ });
6827
+ process.stdout.write(`${JSON.stringify(result, null, 2)}
6828
+ `);
6829
+ process.exitCode = result.ok ? 0 : 1;
6830
+ return;
6831
+ }
6627
6832
  if (command === "checkpoint") {
6628
6833
  const statePath = optionString(options, "statePath");
6629
6834
  if (!statePath) throw new Error("--state-path is required.");
@@ -6674,7 +6879,7 @@ async function main() {
6674
6879
  agent: agentFor(options),
6675
6880
  config: {
6676
6881
  stateDir: optionString(options, "stateDir"),
6677
- riddleEngineModuleUrl: optionString(options, "riddleEngineModuleUrl"),
6882
+ riddleEngineModuleUrl: riddleEngineModuleUrlFor(options, request),
6678
6883
  riddleProofDir: optionString(options, "riddleProofDir"),
6679
6884
  defaultReviewer: optionString(options, "defaultReviewer"),
6680
6885
  defaultShipMode: optionString(options, "defaultShipMode")