@riddledc/openclaw-riddledc 0.9.3 → 0.9.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHECKSUMS.txt CHANGED
@@ -1,9 +1,9 @@
1
- eb37fd02bb17201162234ea8fdd351ab85fe9d96a99c0c8247ec83d3001c7e77 dist/chunk-LJCZ53PU.js
2
- 548b6419feb09a22f2134a12ce378687eb60cfb0c5eb8f2a47eda6c52c89c5ee dist/core.cjs
3
- 9e6f2ccdbc993bea4445dbc20e131b442c01b298c19f87fe81846a22d6c92c48 dist/core.d.cts
4
- 9e6f2ccdbc993bea4445dbc20e131b442c01b298c19f87fe81846a22d6c92c48 dist/core.d.ts
5
- ba8c0a86594c1586b130a787a721ebb3fde4fce481ff924e195bed31a4e6f9b2 dist/core.js
6
- ae60b6124c90f7aa4028120b87856221a2531b262702c82d54da9161f6ed6368 dist/index.cjs
1
+ 0756013923a5d79711ea1b22571589cce1dfc49d85b37de79eb2f9194b11c01e dist/chunk-UTJTPSHW.js
2
+ 628d17715e71385c1c21752d1c90dd956a0552b549444100cc62990ee0ca519d dist/core.cjs
3
+ d6e158722c91097e647d712652cc6bd26792aecf2ff6f990b0a5bf4dfa323aa2 dist/core.d.cts
4
+ d6e158722c91097e647d712652cc6bd26792aecf2ff6f990b0a5bf4dfa323aa2 dist/core.d.ts
5
+ 7d719fd0ea00019cb51bd4b2a02bf2e4104cae2350c91abe6d4cafb8702b4eb2 dist/core.js
6
+ 3d1abeb96c6539cf6e801b8632657a110a3f9767b4b6d51e1c8b419ef5fd94c2 dist/index.cjs
7
7
  94ce04f0e2d84bf64dd68f0500dfdd2f951287a3deccec87f197261961927f6f dist/index.d.cts
8
8
  94ce04f0e2d84bf64dd68f0500dfdd2f951287a3deccec87f197261961927f6f dist/index.d.ts
9
- b92db4977e965d1a9f4d996df1347114046bbcf93ed9ac740e4de40d937bc5eb dist/index.js
9
+ a3554f1a5ef8b62cb94c1c2bfdff682bbe64e935af412afccb06b3075c22b52e dist/index.js
@@ -132,6 +132,43 @@ async function fetchWithRetry(url, init, timeoutMs, label, opts = {}) {
132
132
  function isAlreadyStartedResponse(status, body) {
133
133
  return status === 409 && /already in status:\s*(queued|running|complete|completed)/i.test(body);
134
134
  }
135
+ function previewTimeoutResult(jobId, timeoutMs, lastStatusData, resultExtras = () => ({})) {
136
+ const lastStatus = lastStatusData?.status ?? "unknown";
137
+ const lastPhase = lastStatusData?.phase ?? lastStatus;
138
+ const result = {
139
+ ok: false,
140
+ job_id: jobId,
141
+ status: lastStatus,
142
+ phase: lastPhase,
143
+ phase_updated_at: lastStatusData?.phase_updated_at,
144
+ phase_details: lastStatusData?.phase_details,
145
+ outputs: lastStatusData?.outputs || [],
146
+ compute_seconds: lastStatusData?.compute_seconds,
147
+ egress_bytes: lastStatusData?.egress_bytes,
148
+ error: `Job did not complete within ${timeoutMs / 1e3}s; last status was ${lastStatus}, phase was ${lastPhase}`,
149
+ ...resultExtras(lastStatusData ?? {})
150
+ };
151
+ if (lastStatusData?.error) result.server_error = lastStatusData.error;
152
+ return result;
153
+ }
154
+ function isCompletedPreviewStatus(status) {
155
+ const value = String(status ?? "").toLowerCase();
156
+ return value === "complete" || value === "completed";
157
+ }
158
+ function isTerminalPreviewStatus(status) {
159
+ const value = String(status ?? "").toLowerCase();
160
+ return [
161
+ "complete",
162
+ "completed",
163
+ "failed",
164
+ "completed_error",
165
+ "completed_timeout",
166
+ "timeout",
167
+ "timed_out",
168
+ "cancelled",
169
+ "canceled"
170
+ ].includes(value);
171
+ }
135
172
  async function writeArtifact(workspace, subdir, filename, content) {
136
173
  const dir = join(workspace, "riddle", subdir);
137
174
  await mkdir(dir, { recursive: true });
@@ -467,10 +504,67 @@ async function saveImageOutputs(workspace, jobId, outputs, label) {
467
504
  }
468
505
  }
469
506
  }
507
+ async function previewResultFromStatusData(config, pathPrefix, jobId, statusData, resultExtras = () => ({})) {
508
+ const status = statusData?.status ?? "unknown";
509
+ const terminal = isTerminalPreviewStatus(status);
510
+ const completed = isCompletedPreviewStatus(status);
511
+ const label = pathPrefix.slice(1);
512
+ const result = {
513
+ ok: terminal ? completed : true,
514
+ terminal,
515
+ job_id: jobId,
516
+ status,
517
+ phase: statusData?.phase ?? status,
518
+ phase_updated_at: statusData?.phase_updated_at,
519
+ phase_details: statusData?.phase_details,
520
+ outputs: statusData?.outputs || [],
521
+ compute_seconds: statusData?.compute_seconds,
522
+ egress_bytes: statusData?.egress_bytes,
523
+ ...resultExtras(statusData ?? {})
524
+ };
525
+ if (!terminal) {
526
+ result.message = `Job is still ${status}. Call the status tool again later to recover artifacts when it completes.`;
527
+ }
528
+ if (statusData?.error) result.error = statusData.error;
529
+ if (statusData?.script_error) result.script_error = statusData.script_error;
530
+ if (terminal && !completed && !result.error && !result.script_error) {
531
+ result.error = `Job ended with status ${status}`;
532
+ }
533
+ await saveImageOutputs(config.workspace, jobId, result.outputs, label);
534
+ result.screenshots = result.outputs.filter((o) => /\.(png|jpg|jpeg)$/i.test(o.name));
535
+ return result;
536
+ }
537
+ async function getPreviewJobStatus(config, pathPrefix, jobId, resultExtras = () => ({})) {
538
+ let cfg;
539
+ try {
540
+ cfg = requireConfig(config);
541
+ } catch (err) {
542
+ return { ok: false, error: err.message };
543
+ }
544
+ if (!jobId || typeof jobId !== "string") {
545
+ return { ok: false, error: "job_id must be a non-empty string" };
546
+ }
547
+ const endpoint = cfg.baseUrl.replace(/\/$/, "");
548
+ let statusRes;
549
+ try {
550
+ statusRes = await fetchWithRetry(`${endpoint}${pathPrefix}/${jobId}`, {
551
+ headers: { Authorization: `Bearer ${cfg.apiKey}` }
552
+ }, PREVIEW_REQUEST_TIMEOUT_MS, `${pathPrefix.slice(1)} status`, { attempts: 2 });
553
+ } catch (e) {
554
+ return { ok: false, job_id: jobId, error: `Status failed: ${describeError(e)}` };
555
+ }
556
+ if (!statusRes.ok) {
557
+ const err = await statusRes.text().catch(() => "");
558
+ return { ok: false, job_id: jobId, error: `Status failed: HTTP ${statusRes.status}${err ? ` ${err}` : ""}` };
559
+ }
560
+ const statusData = await statusRes.json();
561
+ return previewResultFromStatusData(cfg, pathPrefix, jobId, statusData, resultExtras);
562
+ }
470
563
  async function pollPreviewJob(config, pathPrefix, jobId, timeoutMs, resultExtras) {
471
564
  const endpoint = config.baseUrl.replace(/\/$/, "");
472
565
  const pollStart = Date.now();
473
566
  const pollIntervalMs = 3e3;
567
+ let lastStatusData = null;
474
568
  while (Date.now() - pollStart < timeoutMs) {
475
569
  let statusRes;
476
570
  try {
@@ -484,24 +578,13 @@ async function pollPreviewJob(config, pathPrefix, jobId, timeoutMs, resultExtras
484
578
  return { ok: false, job_id: jobId, error: `Poll failed: HTTP ${statusRes.status}` };
485
579
  }
486
580
  const statusData = await statusRes.json();
487
- if (statusData.status === "complete" || statusData.status === "completed" || statusData.status === "failed") {
488
- const result = {
489
- ok: statusData.status === "complete" || statusData.status === "completed",
490
- job_id: jobId,
491
- status: statusData.status,
492
- outputs: statusData.outputs || [],
493
- compute_seconds: statusData.compute_seconds,
494
- egress_bytes: statusData.egress_bytes,
495
- ...resultExtras(statusData)
496
- };
497
- if (statusData.error) result.error = statusData.error;
498
- await saveImageOutputs(config.workspace, jobId, result.outputs, pathPrefix.slice(1));
499
- result.screenshots = result.outputs.filter((o) => /\.(png|jpg|jpeg)$/i.test(o.name));
500
- return result;
581
+ lastStatusData = statusData;
582
+ if (isTerminalPreviewStatus(statusData.status)) {
583
+ return previewResultFromStatusData(config, pathPrefix, jobId, statusData, resultExtras);
501
584
  }
502
585
  await sleep(pollIntervalMs);
503
586
  }
504
- return { ok: false, job_id: jobId, error: `Job did not complete within ${timeoutMs / 1e3}s` };
587
+ return previewTimeoutResult(jobId, timeoutMs, lastStatusData, resultExtras);
505
588
  }
506
589
  async function createStaticPreview(config, params) {
507
590
  let cfg;
@@ -710,6 +793,9 @@ async function createServerPreview(config, params) {
710
793
  }
711
794
  return pollPreviewJob(cfg, "/v1/server-preview", created.job_id, ((params.timeout || 120) + 60) * 1e3, () => ({}));
712
795
  }
796
+ async function getServerPreviewStatus(config, jobId) {
797
+ return getPreviewJobStatus(config, "/v1/server-preview", jobId, () => ({}));
798
+ }
713
799
  async function createBuildPreview(config, params) {
714
800
  let cfg;
715
801
  try {
@@ -812,6 +898,14 @@ async function createBuildPreview(config, params) {
812
898
  ...statusData.audit ? { audit: statusData.audit } : {}
813
899
  }));
814
900
  }
901
+ async function getBuildPreviewStatus(config, jobId) {
902
+ return getPreviewJobStatus(config, "/v1/build-preview", jobId, (statusData) => ({
903
+ build_duration_ms: statusData.build_duration_ms,
904
+ ...statusData.build_log ? { build_log: statusData.build_log } : {},
905
+ ...statusData.container_log ? { container_log: statusData.container_log } : {},
906
+ ...statusData.audit ? { audit: statusData.audit } : {}
907
+ }));
908
+ }
815
909
 
816
910
  export {
817
911
  configFromOpenClawApi,
@@ -829,5 +923,7 @@ export {
829
923
  createStaticPreview,
830
924
  deleteStaticPreview,
831
925
  createServerPreview,
832
- createBuildPreview
926
+ getServerPreviewStatus,
927
+ createBuildPreview,
928
+ getBuildPreviewStatus
833
929
  };
package/dist/core.cjs CHANGED
@@ -31,6 +31,8 @@ __export(core_exports, {
31
31
  fetchArtifactsAndBuild: () => fetchArtifactsAndBuild,
32
32
  fetchWithRetry: () => fetchWithRetry,
33
33
  fetchWithTimeout: () => fetchWithTimeout,
34
+ getBuildPreviewStatus: () => getBuildPreviewStatus,
35
+ getServerPreviewStatus: () => getServerPreviewStatus,
34
36
  isAlreadyStartedResponse: () => isAlreadyStartedResponse,
35
37
  pollJobStatus: () => pollJobStatus,
36
38
  riddleApiFetch: () => riddleApiFetch,
@@ -171,6 +173,43 @@ async function fetchWithRetry(url, init, timeoutMs, label, opts = {}) {
171
173
  function isAlreadyStartedResponse(status, body) {
172
174
  return status === 409 && /already in status:\s*(queued|running|complete|completed)/i.test(body);
173
175
  }
176
+ function previewTimeoutResult(jobId, timeoutMs, lastStatusData, resultExtras = () => ({})) {
177
+ const lastStatus = lastStatusData?.status ?? "unknown";
178
+ const lastPhase = lastStatusData?.phase ?? lastStatus;
179
+ const result = {
180
+ ok: false,
181
+ job_id: jobId,
182
+ status: lastStatus,
183
+ phase: lastPhase,
184
+ phase_updated_at: lastStatusData?.phase_updated_at,
185
+ phase_details: lastStatusData?.phase_details,
186
+ outputs: lastStatusData?.outputs || [],
187
+ compute_seconds: lastStatusData?.compute_seconds,
188
+ egress_bytes: lastStatusData?.egress_bytes,
189
+ error: `Job did not complete within ${timeoutMs / 1e3}s; last status was ${lastStatus}, phase was ${lastPhase}`,
190
+ ...resultExtras(lastStatusData ?? {})
191
+ };
192
+ if (lastStatusData?.error) result.server_error = lastStatusData.error;
193
+ return result;
194
+ }
195
+ function isCompletedPreviewStatus(status) {
196
+ const value = String(status ?? "").toLowerCase();
197
+ return value === "complete" || value === "completed";
198
+ }
199
+ function isTerminalPreviewStatus(status) {
200
+ const value = String(status ?? "").toLowerCase();
201
+ return [
202
+ "complete",
203
+ "completed",
204
+ "failed",
205
+ "completed_error",
206
+ "completed_timeout",
207
+ "timeout",
208
+ "timed_out",
209
+ "cancelled",
210
+ "canceled"
211
+ ].includes(value);
212
+ }
174
213
  async function writeArtifact(workspace, subdir, filename, content) {
175
214
  const dir = (0, import_node_path.join)(workspace, "riddle", subdir);
176
215
  await (0, import_promises.mkdir)(dir, { recursive: true });
@@ -506,10 +545,67 @@ async function saveImageOutputs(workspace, jobId, outputs, label) {
506
545
  }
507
546
  }
508
547
  }
548
+ async function previewResultFromStatusData(config, pathPrefix, jobId, statusData, resultExtras = () => ({})) {
549
+ const status = statusData?.status ?? "unknown";
550
+ const terminal = isTerminalPreviewStatus(status);
551
+ const completed = isCompletedPreviewStatus(status);
552
+ const label = pathPrefix.slice(1);
553
+ const result = {
554
+ ok: terminal ? completed : true,
555
+ terminal,
556
+ job_id: jobId,
557
+ status,
558
+ phase: statusData?.phase ?? status,
559
+ phase_updated_at: statusData?.phase_updated_at,
560
+ phase_details: statusData?.phase_details,
561
+ outputs: statusData?.outputs || [],
562
+ compute_seconds: statusData?.compute_seconds,
563
+ egress_bytes: statusData?.egress_bytes,
564
+ ...resultExtras(statusData ?? {})
565
+ };
566
+ if (!terminal) {
567
+ result.message = `Job is still ${status}. Call the status tool again later to recover artifacts when it completes.`;
568
+ }
569
+ if (statusData?.error) result.error = statusData.error;
570
+ if (statusData?.script_error) result.script_error = statusData.script_error;
571
+ if (terminal && !completed && !result.error && !result.script_error) {
572
+ result.error = `Job ended with status ${status}`;
573
+ }
574
+ await saveImageOutputs(config.workspace, jobId, result.outputs, label);
575
+ result.screenshots = result.outputs.filter((o) => /\.(png|jpg|jpeg)$/i.test(o.name));
576
+ return result;
577
+ }
578
+ async function getPreviewJobStatus(config, pathPrefix, jobId, resultExtras = () => ({})) {
579
+ let cfg;
580
+ try {
581
+ cfg = requireConfig(config);
582
+ } catch (err) {
583
+ return { ok: false, error: err.message };
584
+ }
585
+ if (!jobId || typeof jobId !== "string") {
586
+ return { ok: false, error: "job_id must be a non-empty string" };
587
+ }
588
+ const endpoint = cfg.baseUrl.replace(/\/$/, "");
589
+ let statusRes;
590
+ try {
591
+ statusRes = await fetchWithRetry(`${endpoint}${pathPrefix}/${jobId}`, {
592
+ headers: { Authorization: `Bearer ${cfg.apiKey}` }
593
+ }, PREVIEW_REQUEST_TIMEOUT_MS, `${pathPrefix.slice(1)} status`, { attempts: 2 });
594
+ } catch (e) {
595
+ return { ok: false, job_id: jobId, error: `Status failed: ${describeError(e)}` };
596
+ }
597
+ if (!statusRes.ok) {
598
+ const err = await statusRes.text().catch(() => "");
599
+ return { ok: false, job_id: jobId, error: `Status failed: HTTP ${statusRes.status}${err ? ` ${err}` : ""}` };
600
+ }
601
+ const statusData = await statusRes.json();
602
+ return previewResultFromStatusData(cfg, pathPrefix, jobId, statusData, resultExtras);
603
+ }
509
604
  async function pollPreviewJob(config, pathPrefix, jobId, timeoutMs, resultExtras) {
510
605
  const endpoint = config.baseUrl.replace(/\/$/, "");
511
606
  const pollStart = Date.now();
512
607
  const pollIntervalMs = 3e3;
608
+ let lastStatusData = null;
513
609
  while (Date.now() - pollStart < timeoutMs) {
514
610
  let statusRes;
515
611
  try {
@@ -523,24 +619,13 @@ async function pollPreviewJob(config, pathPrefix, jobId, timeoutMs, resultExtras
523
619
  return { ok: false, job_id: jobId, error: `Poll failed: HTTP ${statusRes.status}` };
524
620
  }
525
621
  const statusData = await statusRes.json();
526
- if (statusData.status === "complete" || statusData.status === "completed" || statusData.status === "failed") {
527
- const result = {
528
- ok: statusData.status === "complete" || statusData.status === "completed",
529
- job_id: jobId,
530
- status: statusData.status,
531
- outputs: statusData.outputs || [],
532
- compute_seconds: statusData.compute_seconds,
533
- egress_bytes: statusData.egress_bytes,
534
- ...resultExtras(statusData)
535
- };
536
- if (statusData.error) result.error = statusData.error;
537
- await saveImageOutputs(config.workspace, jobId, result.outputs, pathPrefix.slice(1));
538
- result.screenshots = result.outputs.filter((o) => /\.(png|jpg|jpeg)$/i.test(o.name));
539
- return result;
622
+ lastStatusData = statusData;
623
+ if (isTerminalPreviewStatus(statusData.status)) {
624
+ return previewResultFromStatusData(config, pathPrefix, jobId, statusData, resultExtras);
540
625
  }
541
626
  await sleep(pollIntervalMs);
542
627
  }
543
- return { ok: false, job_id: jobId, error: `Job did not complete within ${timeoutMs / 1e3}s` };
628
+ return previewTimeoutResult(jobId, timeoutMs, lastStatusData, resultExtras);
544
629
  }
545
630
  async function createStaticPreview(config, params) {
546
631
  let cfg;
@@ -749,6 +834,9 @@ async function createServerPreview(config, params) {
749
834
  }
750
835
  return pollPreviewJob(cfg, "/v1/server-preview", created.job_id, ((params.timeout || 120) + 60) * 1e3, () => ({}));
751
836
  }
837
+ async function getServerPreviewStatus(config, jobId) {
838
+ return getPreviewJobStatus(config, "/v1/server-preview", jobId, () => ({}));
839
+ }
752
840
  async function createBuildPreview(config, params) {
753
841
  let cfg;
754
842
  try {
@@ -851,6 +939,14 @@ async function createBuildPreview(config, params) {
851
939
  ...statusData.audit ? { audit: statusData.audit } : {}
852
940
  }));
853
941
  }
942
+ async function getBuildPreviewStatus(config, jobId) {
943
+ return getPreviewJobStatus(config, "/v1/build-preview", jobId, (statusData) => ({
944
+ build_duration_ms: statusData.build_duration_ms,
945
+ ...statusData.build_log ? { build_log: statusData.build_log } : {},
946
+ ...statusData.container_log ? { container_log: statusData.container_log } : {},
947
+ ...statusData.audit ? { audit: statusData.audit } : {}
948
+ }));
949
+ }
854
950
  // Annotate the CommonJS export names for ESM import in node:
855
951
  0 && (module.exports = {
856
952
  applySafetySpec,
@@ -864,6 +960,8 @@ async function createBuildPreview(config, params) {
864
960
  fetchArtifactsAndBuild,
865
961
  fetchWithRetry,
866
962
  fetchWithTimeout,
963
+ getBuildPreviewStatus,
964
+ getServerPreviewStatus,
867
965
  isAlreadyStartedResponse,
868
966
  pollJobStatus,
869
967
  riddleApiFetch,
package/dist/core.d.cts CHANGED
@@ -52,6 +52,8 @@ declare function createStaticPreview(config: RiddleCoreConfig, params: {
52
52
  }): Promise<PreviewResult>;
53
53
  declare function deleteStaticPreview(config: RiddleCoreConfig, id: string): Promise<PreviewResult>;
54
54
  declare function createServerPreview(config: RiddleCoreConfig, params: Record<string, any>): Promise<PreviewResult>;
55
+ declare function getServerPreviewStatus(config: RiddleCoreConfig, jobId: string): Promise<PreviewResult>;
55
56
  declare function createBuildPreview(config: RiddleCoreConfig, params: Record<string, any>): Promise<PreviewResult>;
57
+ declare function getBuildPreviewStatus(config: RiddleCoreConfig, jobId: string): Promise<PreviewResult>;
56
58
 
57
- export { type PreviewResult, type RiddleCoreConfig, type RiddlePayload, type RunResult, applySafetySpec, assertAllowedBaseUrl, configFromOpenClawApi, createBuildPreview, createServerPreview, createStaticPreview, deleteStaticPreview, describeError, fetchArtifactsAndBuild, fetchWithRetry, fetchWithTimeout, isAlreadyStartedResponse, pollJobStatus, riddleApiFetch, runWithDefaults, writeArtifactBinary };
59
+ export { type PreviewResult, type RiddleCoreConfig, type RiddlePayload, type RunResult, applySafetySpec, assertAllowedBaseUrl, configFromOpenClawApi, createBuildPreview, createServerPreview, createStaticPreview, deleteStaticPreview, describeError, fetchArtifactsAndBuild, fetchWithRetry, fetchWithTimeout, getBuildPreviewStatus, getServerPreviewStatus, isAlreadyStartedResponse, pollJobStatus, riddleApiFetch, runWithDefaults, writeArtifactBinary };
package/dist/core.d.ts CHANGED
@@ -52,6 +52,8 @@ declare function createStaticPreview(config: RiddleCoreConfig, params: {
52
52
  }): Promise<PreviewResult>;
53
53
  declare function deleteStaticPreview(config: RiddleCoreConfig, id: string): Promise<PreviewResult>;
54
54
  declare function createServerPreview(config: RiddleCoreConfig, params: Record<string, any>): Promise<PreviewResult>;
55
+ declare function getServerPreviewStatus(config: RiddleCoreConfig, jobId: string): Promise<PreviewResult>;
55
56
  declare function createBuildPreview(config: RiddleCoreConfig, params: Record<string, any>): Promise<PreviewResult>;
57
+ declare function getBuildPreviewStatus(config: RiddleCoreConfig, jobId: string): Promise<PreviewResult>;
56
58
 
57
- export { type PreviewResult, type RiddleCoreConfig, type RiddlePayload, type RunResult, applySafetySpec, assertAllowedBaseUrl, configFromOpenClawApi, createBuildPreview, createServerPreview, createStaticPreview, deleteStaticPreview, describeError, fetchArtifactsAndBuild, fetchWithRetry, fetchWithTimeout, isAlreadyStartedResponse, pollJobStatus, riddleApiFetch, runWithDefaults, writeArtifactBinary };
59
+ export { type PreviewResult, type RiddleCoreConfig, type RiddlePayload, type RunResult, applySafetySpec, assertAllowedBaseUrl, configFromOpenClawApi, createBuildPreview, createServerPreview, createStaticPreview, deleteStaticPreview, describeError, fetchArtifactsAndBuild, fetchWithRetry, fetchWithTimeout, getBuildPreviewStatus, getServerPreviewStatus, isAlreadyStartedResponse, pollJobStatus, riddleApiFetch, runWithDefaults, writeArtifactBinary };
package/dist/core.js CHANGED
@@ -10,12 +10,14 @@ import {
10
10
  fetchArtifactsAndBuild,
11
11
  fetchWithRetry,
12
12
  fetchWithTimeout,
13
+ getBuildPreviewStatus,
14
+ getServerPreviewStatus,
13
15
  isAlreadyStartedResponse,
14
16
  pollJobStatus,
15
17
  riddleApiFetch,
16
18
  runWithDefaults,
17
19
  writeArtifactBinary
18
- } from "./chunk-LJCZ53PU.js";
20
+ } from "./chunk-UTJTPSHW.js";
19
21
  export {
20
22
  applySafetySpec,
21
23
  assertAllowedBaseUrl,
@@ -28,6 +30,8 @@ export {
28
30
  fetchArtifactsAndBuild,
29
31
  fetchWithRetry,
30
32
  fetchWithTimeout,
33
+ getBuildPreviewStatus,
34
+ getServerPreviewStatus,
31
35
  isAlreadyStartedResponse,
32
36
  pollJobStatus,
33
37
  riddleApiFetch,
package/dist/index.cjs CHANGED
@@ -38,6 +38,7 @@ var execFile = (0, import_node_util.promisify)(import_node_child_process.execFil
38
38
  var INLINE_CAP = 50 * 1024;
39
39
  var PREVIEW_REQUEST_TIMEOUT_MS = 3e4;
40
40
  var PREVIEW_UPLOAD_TIMEOUT_MS = 5 * 6e4;
41
+ var PREVIEW_ARTIFACT_TIMEOUT_MS = 6e4;
41
42
  var PREVIEW_RETRY_ATTEMPTS = 3;
42
43
  var PREVIEW_RETRY_BASE_DELAY_MS = 750;
43
44
  function configFromOpenClawApi(api) {
@@ -132,6 +133,32 @@ async function fetchWithRetry(url, init, timeoutMs, label, opts = {}) {
132
133
  }
133
134
  throw new Error(`${label} failed after ${attempts} attempts: ${describeError(lastErr)}`);
134
135
  }
136
+ function isCompletedPreviewStatus(status) {
137
+ const value = String(status ?? "").toLowerCase();
138
+ return value === "complete" || value === "completed";
139
+ }
140
+ function isTerminalPreviewStatus(status) {
141
+ const value = String(status ?? "").toLowerCase();
142
+ return [
143
+ "complete",
144
+ "completed",
145
+ "failed",
146
+ "completed_error",
147
+ "completed_timeout",
148
+ "timeout",
149
+ "timed_out",
150
+ "cancelled",
151
+ "canceled"
152
+ ].includes(value);
153
+ }
154
+ async function writeArtifactBinary(workspace, subdir, filename, base64Content) {
155
+ const dir = (0, import_node_path.join)(workspace, "riddle", subdir);
156
+ await (0, import_promises.mkdir)(dir, { recursive: true });
157
+ const filePath = (0, import_node_path.join)(dir, filename);
158
+ const buf = Buffer.from(base64Content, "base64");
159
+ await (0, import_promises.writeFile)(filePath, buf);
160
+ return { path: filePath, sizeBytes: buf.byteLength };
161
+ }
135
162
  async function assertDirectory(dir) {
136
163
  if (!dir || typeof dir !== "string") throw new Error("directory must be an absolute path");
137
164
  try {
@@ -147,6 +174,79 @@ async function tarDirectory(dir, tarball, excludes, timeout) {
147
174
  await execFile("tar", ["czf", tarball, ...excludeArgs, "-C", dir, "."], { timeout });
148
175
  return (0, import_promises.readFile)(tarball);
149
176
  }
177
+ async function saveImageOutputs(workspace, jobId, outputs, label) {
178
+ for (const output of outputs) {
179
+ if (output.name && /\.(png|jpg|jpeg)$/i.test(output.name) && output.url) {
180
+ try {
181
+ const imgRes = await fetchWithTimeout(output.url, {}, PREVIEW_ARTIFACT_TIMEOUT_MS, `${label} artifact download`);
182
+ if (imgRes.ok) {
183
+ const buf = await imgRes.arrayBuffer();
184
+ const base64 = Buffer.from(buf).toString("base64");
185
+ const ref = await writeArtifactBinary(workspace, "screenshots", `${jobId}-${output.name}`, base64);
186
+ output.saved = ref.path;
187
+ output.sizeBytes = ref.sizeBytes;
188
+ }
189
+ } catch {
190
+ }
191
+ }
192
+ }
193
+ }
194
+ async function previewResultFromStatusData(config, pathPrefix, jobId, statusData, resultExtras = () => ({})) {
195
+ const status = statusData?.status ?? "unknown";
196
+ const terminal = isTerminalPreviewStatus(status);
197
+ const completed = isCompletedPreviewStatus(status);
198
+ const label = pathPrefix.slice(1);
199
+ const result = {
200
+ ok: terminal ? completed : true,
201
+ terminal,
202
+ job_id: jobId,
203
+ status,
204
+ phase: statusData?.phase ?? status,
205
+ phase_updated_at: statusData?.phase_updated_at,
206
+ phase_details: statusData?.phase_details,
207
+ outputs: statusData?.outputs || [],
208
+ compute_seconds: statusData?.compute_seconds,
209
+ egress_bytes: statusData?.egress_bytes,
210
+ ...resultExtras(statusData ?? {})
211
+ };
212
+ if (!terminal) {
213
+ result.message = `Job is still ${status}. Call the status tool again later to recover artifacts when it completes.`;
214
+ }
215
+ if (statusData?.error) result.error = statusData.error;
216
+ if (statusData?.script_error) result.script_error = statusData.script_error;
217
+ if (terminal && !completed && !result.error && !result.script_error) {
218
+ result.error = `Job ended with status ${status}`;
219
+ }
220
+ await saveImageOutputs(config.workspace, jobId, result.outputs, label);
221
+ result.screenshots = result.outputs.filter((o) => /\.(png|jpg|jpeg)$/i.test(o.name));
222
+ return result;
223
+ }
224
+ async function getPreviewJobStatus(config, pathPrefix, jobId, resultExtras = () => ({})) {
225
+ let cfg;
226
+ try {
227
+ cfg = requireConfig(config);
228
+ } catch (err) {
229
+ return { ok: false, error: err.message };
230
+ }
231
+ if (!jobId || typeof jobId !== "string") {
232
+ return { ok: false, error: "job_id must be a non-empty string" };
233
+ }
234
+ const endpoint = cfg.baseUrl.replace(/\/$/, "");
235
+ let statusRes;
236
+ try {
237
+ statusRes = await fetchWithRetry(`${endpoint}${pathPrefix}/${jobId}`, {
238
+ headers: { Authorization: `Bearer ${cfg.apiKey}` }
239
+ }, PREVIEW_REQUEST_TIMEOUT_MS, `${pathPrefix.slice(1)} status`, { attempts: 2 });
240
+ } catch (e) {
241
+ return { ok: false, job_id: jobId, error: `Status failed: ${describeError(e)}` };
242
+ }
243
+ if (!statusRes.ok) {
244
+ const err = await statusRes.text().catch(() => "");
245
+ return { ok: false, job_id: jobId, error: `Status failed: HTTP ${statusRes.status}${err ? ` ${err}` : ""}` };
246
+ }
247
+ const statusData = await statusRes.json();
248
+ return previewResultFromStatusData(cfg, pathPrefix, jobId, statusData, resultExtras);
249
+ }
150
250
  async function createStaticPreview(config, params) {
151
251
  let cfg;
152
252
  try {
@@ -240,13 +340,24 @@ async function deleteStaticPreview(config, id) {
240
340
  const data = await res.json();
241
341
  return { ok: true, deleted: true, files_removed: data.files_removed };
242
342
  }
343
+ async function getServerPreviewStatus(config, jobId) {
344
+ return getPreviewJobStatus(config, "/v1/server-preview", jobId, () => ({}));
345
+ }
346
+ async function getBuildPreviewStatus(config, jobId) {
347
+ return getPreviewJobStatus(config, "/v1/build-preview", jobId, (statusData) => ({
348
+ build_duration_ms: statusData.build_duration_ms,
349
+ ...statusData.build_log ? { build_log: statusData.build_log } : {},
350
+ ...statusData.container_log ? { container_log: statusData.container_log } : {},
351
+ ...statusData.audit ? { audit: statusData.audit } : {}
352
+ }));
353
+ }
243
354
 
244
355
  // src/index.ts
245
356
  var execFile2 = (0, import_node_util2.promisify)(import_node_child_process2.execFile);
246
357
  var INLINE_CAP2 = 50 * 1024;
247
358
  var PREVIEW_REQUEST_TIMEOUT_MS2 = 3e4;
248
359
  var PREVIEW_UPLOAD_TIMEOUT_MS2 = 5 * 6e4;
249
- var PREVIEW_ARTIFACT_TIMEOUT_MS = 6e4;
360
+ var PREVIEW_ARTIFACT_TIMEOUT_MS2 = 6e4;
250
361
  var PREVIEW_RETRY_ATTEMPTS2 = 3;
251
362
  var PREVIEW_RETRY_BASE_DELAY_MS2 = 750;
252
363
  function getCfg(api) {
@@ -364,6 +475,21 @@ async function fetchWithRetry2(url, init, timeoutMs, label, opts = {}) {
364
475
  function isAlreadyStartedResponse(status, body) {
365
476
  return status === 409 && /already in status:\s*(queued|running|complete|completed)/i.test(body);
366
477
  }
478
+ function previewTimeoutToolResult(jobId, timeoutMs, lastStatusData, extras = {}) {
479
+ const lastStatus = lastStatusData?.status ?? "unknown";
480
+ const result = {
481
+ ok: false,
482
+ job_id: jobId,
483
+ status: lastStatus,
484
+ outputs: lastStatusData?.outputs || [],
485
+ compute_seconds: lastStatusData?.compute_seconds,
486
+ egress_bytes: lastStatusData?.egress_bytes,
487
+ error: `Job did not complete within ${timeoutMs / 1e3}s; last status was ${lastStatus}`,
488
+ ...extras
489
+ };
490
+ if (lastStatusData?.error) result.server_error = lastStatusData.error;
491
+ return result;
492
+ }
367
493
  async function writeArtifact(workspace, subdir, filename, content) {
368
494
  const dir = (0, import_node_path2.join)(workspace, "riddle", subdir);
369
495
  await (0, import_promises2.mkdir)(dir, { recursive: true });
@@ -372,7 +498,7 @@ async function writeArtifact(workspace, subdir, filename, content) {
372
498
  await (0, import_promises2.writeFile)(filePath, buf);
373
499
  return { path: filePath, sizeBytes: buf.byteLength };
374
500
  }
375
- async function writeArtifactBinary(workspace, subdir, filename, base64Content) {
501
+ async function writeArtifactBinary2(workspace, subdir, filename, base64Content) {
376
502
  const dir = (0, import_node_path2.join)(workspace, "riddle", subdir);
377
503
  await (0, import_promises2.mkdir)(dir, { recursive: true });
378
504
  const filePath = (0, import_node_path2.join)(dir, filename);
@@ -390,7 +516,7 @@ async function applySafetySpec(result, opts) {
390
516
  base64Data = result.screenshot.data.replace(/^data:image\/\w+;base64,/, "");
391
517
  }
392
518
  if (base64Data) {
393
- const ref = await writeArtifactBinary(opts.workspace, "screenshots", `${jobId}.png`, base64Data);
519
+ const ref = await writeArtifactBinary2(opts.workspace, "screenshots", `${jobId}.png`, base64Data);
394
520
  const cdnUrl = typeof result.screenshot === "object" ? result.screenshot.url : void 0;
395
521
  result.screenshot = { saved: ref.path, sizeBytes: ref.sizeBytes, ...cdnUrl ? { url: cdnUrl } : {} };
396
522
  }
@@ -407,14 +533,14 @@ async function applySafetySpec(result, opts) {
407
533
  base64Data = ss.data.replace(/^data:image\/\w+;base64,/, "");
408
534
  }
409
535
  if (base64Data) {
410
- const ref = await writeArtifactBinary(opts.workspace, "screenshots", `${jobId}-${i}.png`, base64Data);
536
+ const ref = await writeArtifactBinary2(opts.workspace, "screenshots", `${jobId}-${i}.png`, base64Data);
411
537
  savedRefs.push({ saved: ref.path, sizeBytes: ref.sizeBytes, ...cdnUrl ? { url: cdnUrl } : {} });
412
538
  }
413
539
  }
414
540
  result.screenshots = savedRefs;
415
541
  }
416
542
  if (result.rawPngBase64 != null) {
417
- const ref = await writeArtifactBinary(opts.workspace, "screenshots", `${jobId}.png`, result.rawPngBase64);
543
+ const ref = await writeArtifactBinary2(opts.workspace, "screenshots", `${jobId}.png`, result.rawPngBase64);
418
544
  result.screenshot = { saved: ref.path, sizeBytes: ref.sizeBytes };
419
545
  delete result.rawPngBase64;
420
546
  }
@@ -1260,6 +1386,7 @@ function register(api) {
1260
1386
  const timeoutMs = ((params.timeout || 120) + 60) * 1e3;
1261
1387
  const pollStart = Date.now();
1262
1388
  const POLL_INTERVAL = 3e3;
1389
+ let lastStatusData = null;
1263
1390
  while (Date.now() - pollStart < timeoutMs) {
1264
1391
  let statusRes;
1265
1392
  try {
@@ -1273,6 +1400,7 @@ function register(api) {
1273
1400
  return { content: [{ type: "text", text: JSON.stringify({ ok: false, job_id: created.job_id, error: `Poll failed: HTTP ${statusRes.status}` }, null, 2) }] };
1274
1401
  }
1275
1402
  const statusData = await statusRes.json();
1403
+ lastStatusData = statusData;
1276
1404
  if (statusData.status === "complete" || statusData.status === "completed" || statusData.status === "failed") {
1277
1405
  const result = {
1278
1406
  ok: statusData.status === "complete" || statusData.status === "completed",
@@ -1287,11 +1415,11 @@ function register(api) {
1287
1415
  for (const output of result.outputs) {
1288
1416
  if (output.name && /\.(png|jpg|jpeg)$/i.test(output.name) && output.url) {
1289
1417
  try {
1290
- const imgRes = await fetchWithTimeout2(output.url, {}, PREVIEW_ARTIFACT_TIMEOUT_MS, "server preview artifact download");
1418
+ const imgRes = await fetchWithTimeout2(output.url, {}, PREVIEW_ARTIFACT_TIMEOUT_MS2, "server preview artifact download");
1291
1419
  if (imgRes.ok) {
1292
1420
  const buf = await imgRes.arrayBuffer();
1293
1421
  const base64 = Buffer.from(buf).toString("base64");
1294
- const ref = await writeArtifactBinary(workspace, "screenshots", `${created.job_id}-${output.name}`, base64);
1422
+ const ref = await writeArtifactBinary2(workspace, "screenshots", `${created.job_id}-${output.name}`, base64);
1295
1423
  output.saved = ref.path;
1296
1424
  output.sizeBytes = ref.sizeBytes;
1297
1425
  }
@@ -1304,7 +1432,21 @@ function register(api) {
1304
1432
  }
1305
1433
  await new Promise((r) => setTimeout(r, POLL_INTERVAL));
1306
1434
  }
1307
- return { content: [{ type: "text", text: JSON.stringify({ ok: false, job_id: created.job_id, error: `Job did not complete within ${timeoutMs / 1e3}s` }, null, 2) }] };
1435
+ return { content: [{ type: "text", text: JSON.stringify(previewTimeoutToolResult(created.job_id, timeoutMs, lastStatusData), null, 2) }] };
1436
+ }
1437
+ },
1438
+ { optional: true }
1439
+ );
1440
+ api.registerTool(
1441
+ {
1442
+ name: "riddle_server_preview_status",
1443
+ description: "Check/recover a server-preview job by ID (sp_...). Use when riddle_server_preview printed a job_id but the local watcher was interrupted or timed out before artifacts were returned. Downloads screenshot outputs into the workspace when available.",
1444
+ parameters: import_typebox.Type.Object({
1445
+ job_id: import_typebox.Type.String({ description: "Server-preview job ID, for example sp_abc123" })
1446
+ }),
1447
+ async execute(_id, params) {
1448
+ const result = await getServerPreviewStatus(configFromOpenClawApi(api), params.job_id);
1449
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
1308
1450
  }
1309
1451
  },
1310
1452
  { optional: true }
@@ -1460,6 +1602,7 @@ function register(api) {
1460
1602
  const timeoutMs = ((params.timeout || 180) + 120) * 1e3;
1461
1603
  const pollStart = Date.now();
1462
1604
  const POLL_INTERVAL = 3e3;
1605
+ let lastStatusData = null;
1463
1606
  while (Date.now() - pollStart < timeoutMs) {
1464
1607
  let statusRes;
1465
1608
  try {
@@ -1473,6 +1616,7 @@ function register(api) {
1473
1616
  return { content: [{ type: "text", text: JSON.stringify({ ok: false, job_id: created.job_id, error: `Poll failed: HTTP ${statusRes.status}` }, null, 2) }] };
1474
1617
  }
1475
1618
  const statusData = await statusRes.json();
1619
+ lastStatusData = statusData;
1476
1620
  if (statusData.status === "complete" || statusData.status === "completed" || statusData.status === "failed") {
1477
1621
  const result = {
1478
1622
  ok: statusData.status === "complete" || statusData.status === "completed",
@@ -1491,11 +1635,11 @@ function register(api) {
1491
1635
  for (const output of result.outputs) {
1492
1636
  if (output.name && /\.(png|jpg|jpeg)$/i.test(output.name) && output.url) {
1493
1637
  try {
1494
- const imgRes = await fetchWithTimeout2(output.url, {}, PREVIEW_ARTIFACT_TIMEOUT_MS, "build preview artifact download");
1638
+ const imgRes = await fetchWithTimeout2(output.url, {}, PREVIEW_ARTIFACT_TIMEOUT_MS2, "build preview artifact download");
1495
1639
  if (imgRes.ok) {
1496
1640
  const buf = await imgRes.arrayBuffer();
1497
1641
  const base64 = Buffer.from(buf).toString("base64");
1498
- const ref = await writeArtifactBinary(workspace, "screenshots", `${created.job_id}-${output.name}`, base64);
1642
+ const ref = await writeArtifactBinary2(workspace, "screenshots", `${created.job_id}-${output.name}`, base64);
1499
1643
  output.saved = ref.path;
1500
1644
  output.sizeBytes = ref.sizeBytes;
1501
1645
  }
@@ -1508,7 +1652,26 @@ function register(api) {
1508
1652
  }
1509
1653
  await new Promise((r) => setTimeout(r, POLL_INTERVAL));
1510
1654
  }
1511
- return { content: [{ type: "text", text: JSON.stringify({ ok: false, job_id: created.job_id, error: `Job did not complete within ${timeoutMs / 1e3}s` }, null, 2) }] };
1655
+ return { content: [{ type: "text", text: JSON.stringify(previewTimeoutToolResult(created.job_id, timeoutMs, lastStatusData, {
1656
+ build_duration_ms: lastStatusData?.build_duration_ms,
1657
+ ...lastStatusData?.build_log ? { build_log: lastStatusData.build_log } : {},
1658
+ ...lastStatusData?.container_log ? { container_log: lastStatusData.container_log } : {},
1659
+ ...lastStatusData?.audit ? { audit: lastStatusData.audit } : {}
1660
+ }), null, 2) }] };
1661
+ }
1662
+ },
1663
+ { optional: true }
1664
+ );
1665
+ api.registerTool(
1666
+ {
1667
+ name: "riddle_build_preview_status",
1668
+ description: "Check/recover a build-preview job by ID (bp_...). Use when riddle_build_preview printed a job_id but the local watcher was interrupted or timed out before artifacts were returned. Downloads screenshot outputs and returns build/container logs when available.",
1669
+ parameters: import_typebox.Type.Object({
1670
+ job_id: import_typebox.Type.String({ description: "Build-preview job ID, for example bp_abc123" })
1671
+ }),
1672
+ async execute(_id, params) {
1673
+ const result = await getBuildPreviewStatus(configFromOpenClawApi(api), params.job_id);
1674
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
1512
1675
  }
1513
1676
  },
1514
1677
  { optional: true }
@@ -1633,7 +1796,7 @@ function register(api) {
1633
1796
  if (imgRes.ok) {
1634
1797
  const buf = await imgRes.arrayBuffer();
1635
1798
  const base64 = Buffer.from(buf).toString("base64");
1636
- const ref = await writeArtifactBinary(workspace, "screenshots", `${data.job_id}-${output.name}`, base64);
1799
+ const ref = await writeArtifactBinary2(workspace, "screenshots", `${data.job_id}-${output.name}`, base64);
1637
1800
  output.saved = ref.path;
1638
1801
  output.sizeBytes = ref.sizeBytes;
1639
1802
  }
package/dist/index.js CHANGED
@@ -1,8 +1,10 @@
1
1
  import {
2
2
  configFromOpenClawApi,
3
3
  createStaticPreview,
4
- deleteStaticPreview
5
- } from "./chunk-LJCZ53PU.js";
4
+ deleteStaticPreview,
5
+ getBuildPreviewStatus,
6
+ getServerPreviewStatus
7
+ } from "./chunk-UTJTPSHW.js";
6
8
 
7
9
  // src/index.ts
8
10
  import { Type } from "@sinclair/typebox";
@@ -132,6 +134,21 @@ async function fetchWithRetry(url, init, timeoutMs, label, opts = {}) {
132
134
  function isAlreadyStartedResponse(status, body) {
133
135
  return status === 409 && /already in status:\s*(queued|running|complete|completed)/i.test(body);
134
136
  }
137
+ function previewTimeoutToolResult(jobId, timeoutMs, lastStatusData, extras = {}) {
138
+ const lastStatus = lastStatusData?.status ?? "unknown";
139
+ const result = {
140
+ ok: false,
141
+ job_id: jobId,
142
+ status: lastStatus,
143
+ outputs: lastStatusData?.outputs || [],
144
+ compute_seconds: lastStatusData?.compute_seconds,
145
+ egress_bytes: lastStatusData?.egress_bytes,
146
+ error: `Job did not complete within ${timeoutMs / 1e3}s; last status was ${lastStatus}`,
147
+ ...extras
148
+ };
149
+ if (lastStatusData?.error) result.server_error = lastStatusData.error;
150
+ return result;
151
+ }
135
152
  async function writeArtifact(workspace, subdir, filename, content) {
136
153
  const dir = join(workspace, "riddle", subdir);
137
154
  await mkdir(dir, { recursive: true });
@@ -1028,6 +1045,7 @@ function register(api) {
1028
1045
  const timeoutMs = ((params.timeout || 120) + 60) * 1e3;
1029
1046
  const pollStart = Date.now();
1030
1047
  const POLL_INTERVAL = 3e3;
1048
+ let lastStatusData = null;
1031
1049
  while (Date.now() - pollStart < timeoutMs) {
1032
1050
  let statusRes;
1033
1051
  try {
@@ -1041,6 +1059,7 @@ function register(api) {
1041
1059
  return { content: [{ type: "text", text: JSON.stringify({ ok: false, job_id: created.job_id, error: `Poll failed: HTTP ${statusRes.status}` }, null, 2) }] };
1042
1060
  }
1043
1061
  const statusData = await statusRes.json();
1062
+ lastStatusData = statusData;
1044
1063
  if (statusData.status === "complete" || statusData.status === "completed" || statusData.status === "failed") {
1045
1064
  const result = {
1046
1065
  ok: statusData.status === "complete" || statusData.status === "completed",
@@ -1072,7 +1091,21 @@ function register(api) {
1072
1091
  }
1073
1092
  await new Promise((r) => setTimeout(r, POLL_INTERVAL));
1074
1093
  }
1075
- return { content: [{ type: "text", text: JSON.stringify({ ok: false, job_id: created.job_id, error: `Job did not complete within ${timeoutMs / 1e3}s` }, null, 2) }] };
1094
+ return { content: [{ type: "text", text: JSON.stringify(previewTimeoutToolResult(created.job_id, timeoutMs, lastStatusData), null, 2) }] };
1095
+ }
1096
+ },
1097
+ { optional: true }
1098
+ );
1099
+ api.registerTool(
1100
+ {
1101
+ name: "riddle_server_preview_status",
1102
+ description: "Check/recover a server-preview job by ID (sp_...). Use when riddle_server_preview printed a job_id but the local watcher was interrupted or timed out before artifacts were returned. Downloads screenshot outputs into the workspace when available.",
1103
+ parameters: Type.Object({
1104
+ job_id: Type.String({ description: "Server-preview job ID, for example sp_abc123" })
1105
+ }),
1106
+ async execute(_id, params) {
1107
+ const result = await getServerPreviewStatus(configFromOpenClawApi(api), params.job_id);
1108
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
1076
1109
  }
1077
1110
  },
1078
1111
  { optional: true }
@@ -1228,6 +1261,7 @@ function register(api) {
1228
1261
  const timeoutMs = ((params.timeout || 180) + 120) * 1e3;
1229
1262
  const pollStart = Date.now();
1230
1263
  const POLL_INTERVAL = 3e3;
1264
+ let lastStatusData = null;
1231
1265
  while (Date.now() - pollStart < timeoutMs) {
1232
1266
  let statusRes;
1233
1267
  try {
@@ -1241,6 +1275,7 @@ function register(api) {
1241
1275
  return { content: [{ type: "text", text: JSON.stringify({ ok: false, job_id: created.job_id, error: `Poll failed: HTTP ${statusRes.status}` }, null, 2) }] };
1242
1276
  }
1243
1277
  const statusData = await statusRes.json();
1278
+ lastStatusData = statusData;
1244
1279
  if (statusData.status === "complete" || statusData.status === "completed" || statusData.status === "failed") {
1245
1280
  const result = {
1246
1281
  ok: statusData.status === "complete" || statusData.status === "completed",
@@ -1276,7 +1311,26 @@ function register(api) {
1276
1311
  }
1277
1312
  await new Promise((r) => setTimeout(r, POLL_INTERVAL));
1278
1313
  }
1279
- return { content: [{ type: "text", text: JSON.stringify({ ok: false, job_id: created.job_id, error: `Job did not complete within ${timeoutMs / 1e3}s` }, null, 2) }] };
1314
+ return { content: [{ type: "text", text: JSON.stringify(previewTimeoutToolResult(created.job_id, timeoutMs, lastStatusData, {
1315
+ build_duration_ms: lastStatusData?.build_duration_ms,
1316
+ ...lastStatusData?.build_log ? { build_log: lastStatusData.build_log } : {},
1317
+ ...lastStatusData?.container_log ? { container_log: lastStatusData.container_log } : {},
1318
+ ...lastStatusData?.audit ? { audit: lastStatusData.audit } : {}
1319
+ }), null, 2) }] };
1320
+ }
1321
+ },
1322
+ { optional: true }
1323
+ );
1324
+ api.registerTool(
1325
+ {
1326
+ name: "riddle_build_preview_status",
1327
+ description: "Check/recover a build-preview job by ID (bp_...). Use when riddle_build_preview printed a job_id but the local watcher was interrupted or timed out before artifacts were returned. Downloads screenshot outputs and returns build/container logs when available.",
1328
+ parameters: Type.Object({
1329
+ job_id: Type.String({ description: "Build-preview job ID, for example bp_abc123" })
1330
+ }),
1331
+ async execute(_id, params) {
1332
+ const result = await getBuildPreviewStatus(configFromOpenClawApi(api), params.job_id);
1333
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
1280
1334
  }
1281
1335
  },
1282
1336
  { optional: true }
@@ -2,7 +2,7 @@
2
2
  "id": "openclaw-riddledc",
3
3
  "name": "Riddle",
4
4
  "description": "Riddle (riddledc.com) hosted browser API tools for OpenClaw agents.",
5
- "version": "0.9.3",
5
+ "version": "0.9.5",
6
6
  "notes": "0.8.0: Added riddle_build_preview for Dockerfile-based builds with image caching.",
7
7
  "type": "plugin",
8
8
  "bundledSkills": [],
@@ -42,7 +42,9 @@
42
42
  "riddle_preview",
43
43
  "riddle_preview_delete",
44
44
  "riddle_server_preview",
45
+ "riddle_server_preview_status",
45
46
  "riddle_build_preview",
47
+ "riddle_build_preview_status",
46
48
  "riddle_session_create",
47
49
  "riddle_session_list",
48
50
  "riddle_session_run",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riddledc/openclaw-riddledc",
3
- "version": "0.9.3",
3
+ "version": "0.9.5",
4
4
  "description": "OpenClaw integration package for RiddleDC (no secrets).",
5
5
  "license": "MIT",
6
6
  "author": "RiddleDC",