@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 +7 -7
- package/dist/{chunk-LJCZ53PU.js → chunk-UTJTPSHW.js} +112 -16
- package/dist/core.cjs +113 -15
- package/dist/core.d.cts +3 -1
- package/dist/core.d.ts +3 -1
- package/dist/core.js +5 -1
- package/dist/index.cjs +175 -12
- package/dist/index.js +58 -4
- package/openclaw.plugin.json +3 -1
- package/package.json +1 -1
package/CHECKSUMS.txt
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
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
|
-
|
|
488
|
-
|
|
489
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
527
|
-
|
|
528
|
-
|
|
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
|
|
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-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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, {},
|
|
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
|
|
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(
|
|
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, {},
|
|
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
|
|
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(
|
|
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
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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 }
|
package/openclaw.plugin.json
CHANGED
|
@@ -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.
|
|
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",
|