@riddledc/riddle-proof 0.8.83 → 0.8.85

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 (75) hide show
  1. package/README.md +247 -29
  2. package/capabilities.json +17 -0
  3. package/dist/advanced/engine-harness.js +2 -2
  4. package/dist/advanced/index.d.cts +1 -1
  5. package/dist/advanced/index.d.ts +1 -1
  6. package/dist/advanced/index.js +4 -4
  7. package/dist/advanced/proof-run-engine.d.cts +1 -1
  8. package/dist/advanced/proof-run-engine.d.ts +1 -1
  9. package/dist/advanced/runner.js +2 -2
  10. package/dist/change-proof.cjs +22 -260
  11. package/dist/change-proof.d.cts +1 -1
  12. package/dist/change-proof.d.ts +1 -1
  13. package/dist/change-proof.js +2 -2
  14. package/dist/checked-meaning.cjs +67 -0
  15. package/dist/checked-meaning.d.cts +2 -0
  16. package/dist/checked-meaning.d.ts +2 -0
  17. package/dist/checked-meaning.js +41 -0
  18. package/dist/{chunk-QYZ3GKCP.js → chunk-AFARUBE4.js} +12 -11
  19. package/dist/chunk-J2SIQGQI.js +71 -0
  20. package/dist/{chunk-7OAETQU3.js → chunk-JZKLCY5G.js} +1 -1
  21. package/dist/chunk-KB7FYRBG.js +46 -0
  22. package/dist/chunk-NWLKJ2WI.js +31 -0
  23. package/dist/{chunk-LR4UYJDY.js → chunk-O2AO726C.js} +1 -1
  24. package/dist/chunk-ODLGSEP2.js +43 -0
  25. package/dist/chunk-OHDNCXRM.js +17 -0
  26. package/dist/cli/index.js +7 -7
  27. package/dist/cli.cjs +265 -1122
  28. package/dist/cli.d.cts +0 -1
  29. package/dist/cli.d.ts +0 -1
  30. package/dist/cli.js +7 -7
  31. package/dist/engine-harness.js +2 -2
  32. package/dist/grounded-evidence.cjs +95 -0
  33. package/dist/grounded-evidence.d.cts +2 -0
  34. package/dist/grounded-evidence.d.ts +2 -0
  35. package/dist/grounded-evidence.js +69 -0
  36. package/dist/index.cjs +318 -1539
  37. package/dist/index.d.cts +9 -3
  38. package/dist/index.d.ts +9 -3
  39. package/dist/index.js +136 -16
  40. package/dist/pr-comment.cjs +17 -107
  41. package/dist/pr-comment.d.cts +1 -1
  42. package/dist/pr-comment.d.ts +1 -1
  43. package/dist/pr-comment.js +3 -3
  44. package/dist/{proof-run-engine-MiKZt9oY.d.ts → proof-run-engine-BqRoA3Do.d.ts} +3 -3
  45. package/dist/{proof-run-engine-Baiv6l3A.d.cts → proof-run-engine-DpChFR5H.d.cts} +3 -3
  46. package/dist/proof-run-engine.d.cts +1 -1
  47. package/dist/proof-run-engine.d.ts +1 -1
  48. package/dist/receipts.cjs +10 -255
  49. package/dist/receipts.d.cts +7 -115
  50. package/dist/receipts.d.ts +7 -115
  51. package/dist/receipts.js +1 -1
  52. package/dist/riddle-client.cjs +23 -695
  53. package/dist/riddle-client.d.cts +1 -188
  54. package/dist/riddle-client.d.ts +1 -188
  55. package/dist/riddle-client.js +5 -2
  56. package/dist/runner.js +2 -2
  57. package/dist/runtime/index.cjs +23 -695
  58. package/dist/runtime/index.d.cts +1 -5
  59. package/dist/runtime/index.d.ts +1 -5
  60. package/dist/runtime/index.js +5 -2
  61. package/dist/runtime/riddle-client.cjs +23 -695
  62. package/dist/runtime/riddle-client.d.cts +1 -5
  63. package/dist/runtime/riddle-client.d.ts +1 -5
  64. package/dist/runtime/riddle-client.js +5 -2
  65. package/dist/semantic-certificate.cjs +23 -582
  66. package/dist/semantic-certificate.d.cts +2 -191
  67. package/dist/semantic-certificate.d.ts +2 -191
  68. package/dist/semantic-certificate.js +15 -3
  69. package/package.json +28 -5
  70. package/runtime/lib/riddle_core_call.mjs +3 -806
  71. package/dist/chunk-5IFZSUPF.js +0 -644
  72. package/dist/chunk-DB5ZHRUP.js +0 -585
  73. package/dist/chunk-MEVVL4TI.js +0 -258
  74. package/dist/{chunk-WLUMLHII.js → chunk-BID7CKMM.js} +9 -9
  75. package/dist/{chunk-YH7ADFY4.js → chunk-IVSP24XI.js} +3 -3
@@ -1,644 +0,0 @@
1
- import {
2
- parseRiddlePreviewReceipt
3
- } from "./chunk-MEVVL4TI.js";
4
-
5
- // src/riddle-client.ts
6
- import { execFileSync } from "child_process";
7
- import { existsSync, mkdtempSync, readdirSync, readFileSync, rmSync, statSync } from "fs";
8
- import { tmpdir } from "os";
9
- import path from "path";
10
- var DEFAULT_RIDDLE_API_BASE_URL = "https://api.riddledc.com";
11
- var DEFAULT_RIDDLE_API_KEY_FILE = "/tmp/riddle-api-key";
12
- var RIDDLE_UNSUBMITTED_WAKE_HINT = "hosted worker may still be waking or waiting for a lease";
13
- var RiddleApiError = class extends Error {
14
- constructor(pathname, status, body) {
15
- super(`Riddle API ${pathname} failed HTTP ${status}: ${body}`);
16
- this.name = "RiddleApiError";
17
- this.status = status;
18
- this.body = body;
19
- this.path = pathname;
20
- }
21
- };
22
- var PREVIEW_PUBLISH_RECOVERY_STATUSES = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
23
- var PREVIEW_PUBLISH_RECOVERY_ATTEMPTS = 30;
24
- var PREVIEW_PUBLISH_RECOVERY_INTERVAL_MS = 2e3;
25
- function normalizeBaseUrl(value) {
26
- return (value || DEFAULT_RIDDLE_API_BASE_URL).replace(/\/$/, "");
27
- }
28
- function fetchFor(config = {}) {
29
- return config.fetchImpl || fetch;
30
- }
31
- function resolveRiddleApiKeyWithSource(config = {}) {
32
- if (config.apiKey?.trim()) return { apiKey: config.apiKey.trim(), source: "option" };
33
- if (process.env.RIDDLE_API_KEY?.trim()) return { apiKey: process.env.RIDDLE_API_KEY.trim(), source: "env" };
34
- const keyFile = config.apiKeyFile || process.env.RIDDLE_API_KEY_FILE || DEFAULT_RIDDLE_API_KEY_FILE;
35
- if (existsSync(keyFile)) {
36
- const key = readFileSync(keyFile, "utf8").trim();
37
- if (key) return { apiKey: key, source: "file", file: keyFile };
38
- }
39
- throw new Error(`Riddle API key missing. Set RIDDLE_API_KEY or write ${DEFAULT_RIDDLE_API_KEY_FILE}.`);
40
- }
41
- function resolveRiddleApiKeySource(config = {}) {
42
- const { apiKey: _apiKey, ...source } = resolveRiddleApiKeyWithSource(config);
43
- return source;
44
- }
45
- function resolveRiddleApiKey(config = {}) {
46
- return resolveRiddleApiKeyWithSource(config).apiKey;
47
- }
48
- async function riddleRequestJson(config, pathname, init = {}) {
49
- const response = await fetchFor(config)(`${normalizeBaseUrl(config.apiBaseUrl)}${pathname}`, {
50
- ...init,
51
- headers: {
52
- Authorization: `Bearer ${resolveRiddleApiKey(config)}`,
53
- "Content-Type": "application/json",
54
- ...init.headers || {}
55
- }
56
- });
57
- const text = await response.text();
58
- let json = null;
59
- try {
60
- json = JSON.parse(text);
61
- } catch {
62
- }
63
- if (!response.ok) throw new RiddleApiError(pathname, response.status, text);
64
- return json ?? text;
65
- }
66
- async function getRiddleBalance(config = {}) {
67
- return riddleRequestJson(config, "/v1/balance", { method: "GET" });
68
- }
69
- function previewDeployResultFromRecord(input) {
70
- const { record, id, label, framework, expiresAt, publishRecovered, publishError } = input;
71
- const receipt = record.receipt && typeof record.receipt === "object" ? parseRiddlePreviewReceipt(record.receipt) : void 0;
72
- return {
73
- ok: true,
74
- id: String(record.id || record.preview_id || id),
75
- label,
76
- framework,
77
- preview_url: String(record.preview_url || ""),
78
- file_count: typeof record.file_count === "number" ? record.file_count : void 0,
79
- total_bytes: typeof record.total_bytes === "number" ? record.total_bytes : void 0,
80
- expires_at: receipt?.expires_at || expiresAt,
81
- receipt,
82
- publish_recovered: publishRecovered || void 0,
83
- publish_error: publishError,
84
- warnings: input.warnings?.length ? input.warnings : void 0,
85
- raw: record
86
- };
87
- }
88
- function gitOutput(directory, args) {
89
- try {
90
- return execFileSync("git", ["-C", directory, ...args], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
91
- } catch {
92
- return "";
93
- }
94
- }
95
- function detectRiddlePreviewSource(directory) {
96
- const gitRevision = gitOutput(directory, ["rev-parse", "HEAD"]);
97
- if (!gitRevision) return {};
98
- const repository = gitOutput(directory, ["config", "--get", "remote.origin.url"]);
99
- const status = gitOutput(directory, ["status", "--porcelain", "--untracked-files=normal"]);
100
- return {
101
- git_revision: gitRevision,
102
- repository: repository || void 0,
103
- dirty: Boolean(status)
104
- };
105
- }
106
- function canRecoverPreviewPublish(error) {
107
- return error instanceof RiddleApiError && PREVIEW_PUBLISH_RECOVERY_STATUSES.has(error.status);
108
- }
109
- function summarizePreviewDirectory(directory) {
110
- const stack = [directory];
111
- let fileCount = 0;
112
- let totalBytes = 0;
113
- while (stack.length) {
114
- const current = stack.pop();
115
- for (const entry of readdirSync(current, { withFileTypes: true })) {
116
- const fullPath = path.join(current, entry.name);
117
- if (entry.isDirectory()) {
118
- stack.push(fullPath);
119
- continue;
120
- }
121
- if (!entry.isFile()) continue;
122
- const stat = statSync(fullPath);
123
- fileCount += 1;
124
- totalBytes += stat.size;
125
- }
126
- }
127
- return { file_count: fileCount, total_bytes: totalBytes };
128
- }
129
- function previewProgressEmitter(config, base) {
130
- return async (snapshot) => {
131
- if (!config.onPreviewProgress) return;
132
- await config.onPreviewProgress({
133
- label: base.label,
134
- framework: base.framework,
135
- directory: base.directory,
136
- elapsed_ms: Math.max(0, Date.now() - base.startedAt),
137
- warnings: base.warnings?.length ? base.warnings : void 0,
138
- ...snapshot
139
- });
140
- };
141
- }
142
- async function waitForPublishedPreview(config, input) {
143
- await input.emitProgress?.({
144
- stage: "publish_recovering",
145
- id: input.id,
146
- file_count: input.localSummary?.file_count,
147
- total_bytes: input.localSummary?.total_bytes,
148
- publish_error: input.publishError.message,
149
- message: `publish returned ${input.publishError.status}; polling preview status`
150
- });
151
- for (let attempt = 1; attempt <= PREVIEW_PUBLISH_RECOVERY_ATTEMPTS; attempt += 1) {
152
- const status = await riddleRequestJson(config, `/v1/preview/${input.id}`);
153
- await input.emitProgress?.({
154
- stage: "checking_status",
155
- id: input.id,
156
- attempt,
157
- attempts: PREVIEW_PUBLISH_RECOVERY_ATTEMPTS,
158
- status: typeof status.status === "string" ? status.status : null,
159
- preview_url: typeof status.preview_url === "string" ? status.preview_url : void 0,
160
- file_count: typeof status.file_count === "number" ? status.file_count : input.localSummary?.file_count,
161
- total_bytes: typeof status.total_bytes === "number" ? status.total_bytes : input.localSummary?.total_bytes
162
- });
163
- if (String(status.status || "") === "ready" && String(status.preview_url || "").trim()) {
164
- await input.emitProgress?.({
165
- stage: "ready",
166
- id: input.id,
167
- preview_url: String(status.preview_url),
168
- file_count: typeof status.file_count === "number" ? status.file_count : input.localSummary?.file_count,
169
- total_bytes: typeof status.total_bytes === "number" ? status.total_bytes : input.localSummary?.total_bytes
170
- });
171
- return previewDeployResultFromRecord({
172
- record: status,
173
- id: input.id,
174
- label: input.label,
175
- framework: input.framework,
176
- expiresAt: input.expiresAt,
177
- publishRecovered: true,
178
- publishError: input.publishError.message,
179
- warnings: input.warnings
180
- });
181
- }
182
- if (attempt < PREVIEW_PUBLISH_RECOVERY_ATTEMPTS) {
183
- await new Promise((resolve) => setTimeout(resolve, PREVIEW_PUBLISH_RECOVERY_INTERVAL_MS));
184
- }
185
- }
186
- throw input.publishError;
187
- }
188
- async function deployRiddlePreview(config, directory, label, framework = "static", options = {}) {
189
- if (!directory?.trim()) throw new Error("directory is required");
190
- if (!label?.trim()) throw new Error("label is required");
191
- if (framework !== "spa" && framework !== "static") throw new Error("framework must be spa or static");
192
- const startedAt = Date.now();
193
- const warnings = collectRiddlePreviewDeployWarnings(directory, framework);
194
- const source = options.source || detectRiddlePreviewSource(directory);
195
- const emitProgress = previewProgressEmitter(config, { label, framework, directory, startedAt, warnings });
196
- await emitProgress({ stage: "validating", message: "checking preview input directory" });
197
- const localSummary = summarizePreviewDirectory(directory);
198
- await emitProgress({
199
- stage: "creating",
200
- file_count: localSummary.file_count,
201
- total_bytes: localSummary.total_bytes,
202
- message: "creating preview upload target"
203
- });
204
- const created = await riddleRequestJson(config, "/v1/preview", {
205
- method: "POST",
206
- body: JSON.stringify({ framework, label, source })
207
- });
208
- const id = String(created.id || "");
209
- const uploadUrl = String(created.upload_url || "");
210
- if (!id || !uploadUrl) throw new Error("Riddle preview create response was missing id or upload_url.");
211
- await emitProgress({
212
- stage: "created",
213
- id,
214
- file_count: localSummary.file_count,
215
- total_bytes: localSummary.total_bytes,
216
- message: "preview upload target created"
217
- });
218
- const scratch = mkdtempSync(path.join(tmpdir(), "riddle-preview-upload-"));
219
- const tarball = path.join(scratch, `${id}.tar.gz`);
220
- let tarballBytes = 0;
221
- try {
222
- await emitProgress({
223
- stage: "archiving",
224
- id,
225
- file_count: localSummary.file_count,
226
- total_bytes: localSummary.total_bytes,
227
- message: "creating preview archive"
228
- });
229
- execFileSync("tar", ["czf", tarball, "-C", directory, "."], { stdio: "pipe" });
230
- tarballBytes = statSync(tarball).size;
231
- await emitProgress({
232
- stage: "archived",
233
- id,
234
- file_count: localSummary.file_count,
235
- total_bytes: localSummary.total_bytes,
236
- tarball_bytes: tarballBytes,
237
- message: "preview archive created"
238
- });
239
- await emitProgress({
240
- stage: "uploading",
241
- id,
242
- file_count: localSummary.file_count,
243
- total_bytes: localSummary.total_bytes,
244
- tarball_bytes: tarballBytes,
245
- message: "uploading preview archive"
246
- });
247
- const upload = await fetchFor(config)(uploadUrl, {
248
- method: "PUT",
249
- headers: { "Content-Type": "application/gzip" },
250
- body: readFileSync(tarball)
251
- });
252
- if (!upload.ok) {
253
- throw new RiddleApiError(uploadUrl, upload.status, await upload.text());
254
- }
255
- await emitProgress({
256
- stage: "uploaded",
257
- id,
258
- file_count: localSummary.file_count,
259
- total_bytes: localSummary.total_bytes,
260
- tarball_bytes: tarballBytes,
261
- message: "preview archive uploaded"
262
- });
263
- } finally {
264
- rmSync(scratch, { recursive: true, force: true });
265
- }
266
- const expiresAt = typeof created.expires_at === "string" ? created.expires_at : void 0;
267
- try {
268
- await emitProgress({
269
- stage: "publishing",
270
- id,
271
- file_count: localSummary.file_count,
272
- total_bytes: localSummary.total_bytes,
273
- tarball_bytes: tarballBytes || void 0,
274
- message: "publishing preview"
275
- });
276
- const published = await riddleRequestJson(config, `/v1/preview/${id}/publish`, {
277
- method: "POST"
278
- });
279
- await emitProgress({
280
- stage: "ready",
281
- id,
282
- preview_url: String(published.preview_url || ""),
283
- file_count: typeof published.file_count === "number" ? published.file_count : localSummary.file_count,
284
- total_bytes: typeof published.total_bytes === "number" ? published.total_bytes : localSummary.total_bytes
285
- });
286
- return previewDeployResultFromRecord({ record: published, id, label, framework, expiresAt, warnings });
287
- } catch (error) {
288
- if (!canRecoverPreviewPublish(error)) throw error;
289
- return waitForPublishedPreview(config, {
290
- id,
291
- label,
292
- framework,
293
- expiresAt,
294
- publishError: error,
295
- warnings,
296
- localSummary,
297
- emitProgress
298
- });
299
- }
300
- }
301
- function latestMatchingMtimeMs(directory, predicate) {
302
- let latest = 0;
303
- const stack = [directory];
304
- let visited = 0;
305
- while (stack.length) {
306
- const current = stack.pop();
307
- for (const entry of readdirSync(current, { withFileTypes: true })) {
308
- const fullPath = path.join(current, entry.name);
309
- if (entry.isDirectory()) {
310
- stack.push(fullPath);
311
- continue;
312
- }
313
- if (!entry.isFile() || !predicate(fullPath)) continue;
314
- const stat = statSync(fullPath);
315
- latest = Math.max(latest, stat.mtimeMs);
316
- visited += 1;
317
- if (visited >= 1e4) return latest;
318
- }
319
- }
320
- return latest;
321
- }
322
- function collectRiddlePreviewDeployWarnings(directory, framework = "static") {
323
- try {
324
- if (framework !== "static") return [];
325
- const resolvedDirectory = path.resolve(directory);
326
- if (path.basename(resolvedDirectory) !== "out") return [];
327
- const repoRoot = path.dirname(resolvedDirectory);
328
- const nextAppDir = path.join(repoRoot, ".next", "server", "app");
329
- if (!existsSync(nextAppDir)) return [];
330
- const nextRenderedMtimeMs = latestMatchingMtimeMs(nextAppDir, (filePath) => /\.(?:html|rsc)$/i.test(filePath));
331
- if (!nextRenderedMtimeMs) return [];
332
- const outRenderedMtimeMs = existsSync(resolvedDirectory) ? latestMatchingMtimeMs(resolvedDirectory, (filePath) => /\.(?:html|txt|rsc)$/i.test(filePath)) : 0;
333
- if (!outRenderedMtimeMs) {
334
- return [
335
- "Riddle Preview static deploy target is an out/ directory with newer Next render output in .next/server/app, but no rendered HTML/RSC files were found in out/. Run the project static bundle/export step before deploying."
336
- ];
337
- }
338
- if (nextRenderedMtimeMs > outRenderedMtimeMs + 1e3) {
339
- return [
340
- "Riddle Preview static deploy target out/ appears older than the Next render output in .next/server/app. Run the project static bundle/export step, such as npm run build:static-deploy or next export, before deploying to avoid a stale Preview."
341
- ];
342
- }
343
- return [];
344
- } catch {
345
- return [];
346
- }
347
- }
348
- async function deployRiddleStaticPreview(config, directory, label, options = {}) {
349
- return deployRiddlePreview(config, directory, label, "static", options);
350
- }
351
- function createTarball(directory, label, exclude = []) {
352
- const scratch = mkdtempSync(path.join(tmpdir(), "riddle-upload-"));
353
- const tarball = path.join(scratch, `${label}.tar.gz`);
354
- const excludeArgs = exclude.flatMap((item) => ["--exclude", item]);
355
- try {
356
- execFileSync("tar", ["czf", tarball, ...excludeArgs, "-C", directory, "."], { stdio: "pipe" });
357
- return { scratch, tarball };
358
- } catch (error) {
359
- rmSync(scratch, { recursive: true, force: true });
360
- throw error;
361
- }
362
- }
363
- function parseRiddleViewport(value) {
364
- if (!value) return void 0;
365
- const match = /^(\d+)x(\d+)$/.exec(value.trim());
366
- if (!match) throw new Error("viewport must look like 1280x720");
367
- return { width: Number(match[1]), height: Number(match[2]) };
368
- }
369
- function scriptErrorFrom(job) {
370
- const nested = job?.proof_of_execution && typeof job.proof_of_execution === "object" && !Array.isArray(job.proof_of_execution) ? job.proof_of_execution : null;
371
- return typeof job?.script_error === "string" && job.script_error.trim() ? job.script_error : typeof nested?.script_error === "string" && nested.script_error.trim() ? nested.script_error : null;
372
- }
373
- async function runRiddleServerPreview(config, input) {
374
- if (!input.directory?.trim()) throw new Error("directory is required");
375
- if (!input.script?.trim()) throw new Error("script is required");
376
- const port = input.port || 3e3;
377
- const routePath = input.path || "/";
378
- const timeoutSec = input.timeoutSec || 180;
379
- const created = await riddleRequestJson(config, "/v1/server-preview", {
380
- method: "POST",
381
- body: JSON.stringify({
382
- image: input.image || "node:22-slim",
383
- command: input.command || "node scripts/riddleSpaPreviewServer.mjs build 3000",
384
- port,
385
- path: routePath,
386
- readiness_path: input.readinessPath || routePath,
387
- readiness_timeout: input.readinessTimeoutSec || 90,
388
- wait_until: input.waitUntil || "domcontentloaded",
389
- wait_for_selector: input.waitForSelector || "body",
390
- navigation_timeout: input.navigationTimeoutSec || 60,
391
- viewport: input.viewport,
392
- timeout: timeoutSec,
393
- script: input.script
394
- })
395
- });
396
- const jobId = String(created.job_id || "");
397
- const uploadUrl = String(created.upload_url || "");
398
- if (!jobId || !uploadUrl) {
399
- throw new Error(`Riddle server preview create response was missing job_id or upload_url.`);
400
- }
401
- const { scratch, tarball } = createTarball(input.directory, jobId, [
402
- ".git",
403
- "node_modules",
404
- "test-results",
405
- ...input.exclude || []
406
- ]);
407
- try {
408
- const upload = await fetchFor(config)(uploadUrl, {
409
- method: "PUT",
410
- headers: { "Content-Type": "application/gzip" },
411
- body: readFileSync(tarball)
412
- });
413
- if (!upload.ok) throw new RiddleApiError(uploadUrl, upload.status, await upload.text());
414
- } finally {
415
- rmSync(scratch, { recursive: true, force: true });
416
- }
417
- await riddleRequestJson(config, `/v1/server-preview/${jobId}/start`, {
418
- method: "POST"
419
- });
420
- let job = null;
421
- const attempts = input.pollAttempts || Math.ceil((timeoutSec + 90) / 2);
422
- const intervalMs = input.pollIntervalMs || 2e3;
423
- for (let index = 0; index < attempts; index += 1) {
424
- job = await riddleRequestJson(config, `/v1/server-preview/${jobId}`);
425
- if (isTerminalRiddleJobStatus(job.status)) break;
426
- if (index + 1 < attempts) await new Promise((resolve) => setTimeout(resolve, intervalMs));
427
- }
428
- const status = job?.status ? String(job.status) : null;
429
- const scriptError = scriptErrorFrom(job);
430
- return {
431
- ok: (status === "completed" || status === "complete") && !scriptError,
432
- job_id: jobId,
433
- status,
434
- terminal: isTerminalRiddleJobStatus(status),
435
- script_error: scriptError,
436
- job
437
- };
438
- }
439
- async function runRiddleScript(config, input) {
440
- if (!input.url?.trim()) throw new Error("url is required");
441
- if (!input.script?.trim()) throw new Error("script is required");
442
- const payload = {
443
- url: input.url,
444
- script: input.script,
445
- sync: input.sync ?? false,
446
- timeout_sec: input.timeoutSec || 120
447
- };
448
- if (input.viewport) payload.viewport = input.viewport;
449
- if (input.include) payload.include = input.include;
450
- if (typeof input.strict === "boolean") payload.strict = input.strict;
451
- if (input.options) payload.options = input.options;
452
- return riddleRequestJson(config, "/v1/run", {
453
- method: "POST",
454
- body: JSON.stringify(payload)
455
- });
456
- }
457
- function isTerminalRiddleJobStatus(status) {
458
- return ["completed", "complete", "completed_error", "completed_timeout", "failed"].includes(String(status || ""));
459
- }
460
- function stringField(record, key) {
461
- const value = record?.[key];
462
- return typeof value === "string" && value.trim() ? value.trim() : null;
463
- }
464
- function parseTimestampMs(value) {
465
- if (!value) return null;
466
- const parsed = Date.parse(value);
467
- return Number.isFinite(parsed) ? parsed : null;
468
- }
469
- function buildPollSnapshot(jobId, job, input) {
470
- const status = job?.status ? String(job.status) : null;
471
- const phase = job?.phase ? String(job.phase) : null;
472
- const terminal = isTerminalRiddleJobStatus(status);
473
- const createdAt = stringField(job, "created_at");
474
- const submittedAt = stringField(job, "submitted_at");
475
- const completedAt = stringField(job, "completed_at");
476
- const createdMs = parseTimestampMs(createdAt);
477
- const submittedMs = parseTimestampMs(submittedAt);
478
- const rawExecution = job?.execution && typeof job.execution === "object" && !Array.isArray(job.execution) ? job.execution : null;
479
- const enqueuedAt = rawExecution ? stringField(rawExecution, "enqueued_at") : null;
480
- const enqueuedMs = parseTimestampMs(enqueuedAt);
481
- const queueStartedMs = enqueuedMs ?? createdMs;
482
- const claimedAt = rawExecution ? stringField(rawExecution, "claimed_at") : null;
483
- const claimedMs = parseTimestampMs(claimedAt);
484
- let queueElapsedMs = null;
485
- if (queueStartedMs !== null && claimedMs !== null) {
486
- queueElapsedMs = Math.max(0, claimedMs - queueStartedMs);
487
- } else if (queueStartedMs !== null && submittedMs !== null) {
488
- queueElapsedMs = Math.max(0, submittedMs - queueStartedMs);
489
- } else if (queueStartedMs !== null && !submittedAt && !terminal) {
490
- queueElapsedMs = Math.max(0, input.observedAt - queueStartedMs);
491
- }
492
- return {
493
- job_id: jobId,
494
- status,
495
- phase,
496
- terminal,
497
- attempt: input.attempt,
498
- attempts: input.attempts,
499
- elapsed_ms: Math.max(0, input.observedAt - input.startedAt),
500
- created_at: createdAt,
501
- submitted_at: submittedAt,
502
- completed_at: completedAt,
503
- queue_elapsed_ms: queueElapsedMs,
504
- pre_submission_elapsed_ms: Math.max(0, Math.floor(input.preSubmissionElapsedMs ?? 0)),
505
- running_without_submission: Boolean(status && !terminal && !submittedAt),
506
- active_execution: Boolean(status === "running" && phase && phase !== "queued"),
507
- execution: rawExecution ? rawExecution : void 0
508
- };
509
- }
510
- function pollMessage(snapshot, timedOut) {
511
- if (!timedOut) return void 0;
512
- const submitted = snapshot.submitted_at || "not submitted";
513
- const wakeHint = snapshot.running_without_submission && !snapshot.created_at && !snapshot.submitted_at ? ` ${RIDDLE_UNSUBMITTED_WAKE_HINT}.` : "";
514
- const queue = snapshot.queue_elapsed_ms !== null ? ` queue_elapsed_ms=${snapshot.queue_elapsed_ms}` : "";
515
- const preSubmit = snapshot.pre_submission_elapsed_ms > 0 ? ` pre_submission_elapsed_ms=${snapshot.pre_submission_elapsed_ms}` : "";
516
- return `Riddle job ${snapshot.job_id} did not reach a terminal status after ${snapshot.attempt} poll attempts; status=${snapshot.status || "unknown"} phase=${snapshot.phase || "unknown"} submitted_at=${submitted}.${wakeHint}${queue}${preSubmit}`;
517
- }
518
- async function pollRiddleJob(config, jobId, options = {}) {
519
- if (!jobId?.trim()) throw new Error("jobId is required");
520
- const attempts = Math.max(1, Math.floor(options.attempts ?? (options.wait ? 300 : 1)));
521
- const intervalMs = Math.max(0, Math.floor(options.intervalMs ?? 2e3));
522
- const progressEveryMs = Math.max(0, Math.floor(options.progressEveryMs ?? 1e4));
523
- const unsubmittedTimeoutMs = Math.max(0, Math.floor(options.unsubmittedTimeoutMs ?? 0));
524
- const startedAt = Date.now();
525
- let job = null;
526
- let lastSnapshot = null;
527
- let lastProgressAt = 0;
528
- let lastProgressKey = "";
529
- let preSubmissionElapsedMs = 0;
530
- let unsubmittedTimedOut = false;
531
- for (let index = 0; index < attempts; index += 1) {
532
- job = await riddleRequestJson(config, `/v1/jobs/${jobId}`);
533
- const observedAt = Date.now();
534
- const nextSnapshot = buildPollSnapshot(jobId, job, {
535
- attempt: index + 1,
536
- attempts,
537
- startedAt,
538
- observedAt,
539
- preSubmissionElapsedMs
540
- });
541
- if (nextSnapshot.running_without_submission) {
542
- preSubmissionElapsedMs = Math.max(preSubmissionElapsedMs, nextSnapshot.elapsed_ms);
543
- }
544
- lastSnapshot = {
545
- ...nextSnapshot,
546
- pre_submission_elapsed_ms: preSubmissionElapsedMs
547
- };
548
- const progressKey = [
549
- lastSnapshot.status || "unknown",
550
- lastSnapshot.phase || "unknown",
551
- lastSnapshot.terminal ? "terminal" : "nonterminal",
552
- lastSnapshot.submitted_at ? "submitted" : "unsubmitted"
553
- ].join(":");
554
- if (options.onProgress) {
555
- const shouldReport = index === 0 || lastSnapshot.terminal || progressKey !== lastProgressKey || observedAt - lastProgressAt >= progressEveryMs;
556
- if (shouldReport) {
557
- lastProgressAt = observedAt;
558
- lastProgressKey = progressKey;
559
- await options.onProgress(lastSnapshot);
560
- }
561
- }
562
- unsubmittedTimedOut = Boolean(
563
- options.wait && unsubmittedTimeoutMs > 0 && lastSnapshot.running_without_submission && !lastSnapshot.created_at && !lastSnapshot.submitted_at && preSubmissionElapsedMs >= unsubmittedTimeoutMs
564
- );
565
- if (lastSnapshot.terminal) break;
566
- if (unsubmittedTimedOut) break;
567
- if (index + 1 < attempts) {
568
- await new Promise((resolve) => setTimeout(resolve, intervalMs));
569
- }
570
- }
571
- const status = job?.status ? String(job.status) : null;
572
- const fallbackObservedAt = Date.now();
573
- const snapshot = lastSnapshot || buildPollSnapshot(jobId, job, {
574
- attempt: 0,
575
- attempts,
576
- startedAt,
577
- observedAt: fallbackObservedAt
578
- });
579
- if (!isTerminalRiddleJobStatus(status)) {
580
- const timedOut = Boolean(options.wait);
581
- return {
582
- ok: !timedOut,
583
- job_id: jobId,
584
- status,
585
- terminal: false,
586
- job,
587
- poll: {
588
- ...snapshot,
589
- timed_out: timedOut,
590
- interval_ms: intervalMs,
591
- unsubmitted_timeout: unsubmittedTimedOut || void 0,
592
- unsubmitted_timeout_ms: unsubmittedTimedOut ? unsubmittedTimeoutMs : void 0,
593
- message: pollMessage(snapshot, timedOut)
594
- }
595
- };
596
- }
597
- const artifacts = await riddleRequestJson(config, `/v1/jobs/${jobId}/artifacts`);
598
- return {
599
- ok: status === "completed" || status === "complete",
600
- job_id: jobId,
601
- status,
602
- terminal: true,
603
- job,
604
- artifacts,
605
- poll: {
606
- ...snapshot,
607
- timed_out: false,
608
- interval_ms: intervalMs
609
- }
610
- };
611
- }
612
- function createRiddleApiClient(config = {}) {
613
- return {
614
- apiKeySource: () => resolveRiddleApiKeySource(config),
615
- requestJson: (pathname, init) => riddleRequestJson(config, pathname, init),
616
- getBalance: () => getRiddleBalance(config),
617
- deployPreview: (directory, label, framework = "static", options) => deployRiddlePreview(config, directory, label, framework, options),
618
- deployStaticPreview: (directory, label, options) => deployRiddleStaticPreview(config, directory, label, options),
619
- runScript: (input) => runRiddleScript(config, input),
620
- runServerPreview: (input) => runRiddleServerPreview(config, input),
621
- pollJob: (jobId, options) => pollRiddleJob(config, jobId, options)
622
- };
623
- }
624
-
625
- export {
626
- DEFAULT_RIDDLE_API_BASE_URL,
627
- DEFAULT_RIDDLE_API_KEY_FILE,
628
- RIDDLE_UNSUBMITTED_WAKE_HINT,
629
- RiddleApiError,
630
- resolveRiddleApiKeySource,
631
- resolveRiddleApiKey,
632
- riddleRequestJson,
633
- getRiddleBalance,
634
- detectRiddlePreviewSource,
635
- deployRiddlePreview,
636
- collectRiddlePreviewDeployWarnings,
637
- deployRiddleStaticPreview,
638
- parseRiddleViewport,
639
- runRiddleServerPreview,
640
- runRiddleScript,
641
- isTerminalRiddleJobStatus,
642
- pollRiddleJob,
643
- createRiddleApiClient
644
- };