@riddledc/openclaw-riddledc 0.9.1 → 0.9.3
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 -2
- package/README.md +10 -0
- package/dist/chunk-LJCZ53PU.js +833 -0
- package/dist/core.cjs +872 -0
- package/dist/core.d.cts +57 -0
- package/dist/core.d.ts +57 -0
- package/dist/core.js +36 -0
- package/dist/index.cjs +427 -155
- package/dist/index.js +191 -127
- package/openclaw.plugin.json +1 -1
- package/package.json +7 -2
package/dist/index.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import {
|
|
2
|
+
configFromOpenClawApi,
|
|
3
|
+
createStaticPreview,
|
|
4
|
+
deleteStaticPreview
|
|
5
|
+
} from "./chunk-LJCZ53PU.js";
|
|
6
|
+
|
|
1
7
|
// src/index.ts
|
|
2
8
|
import { Type } from "@sinclair/typebox";
|
|
3
9
|
import { writeFile, mkdir, readFile, stat, rm } from "fs/promises";
|
|
@@ -6,6 +12,11 @@ import { execFile as execFileCb } from "child_process";
|
|
|
6
12
|
import { promisify } from "util";
|
|
7
13
|
var execFile = promisify(execFileCb);
|
|
8
14
|
var INLINE_CAP = 50 * 1024;
|
|
15
|
+
var PREVIEW_REQUEST_TIMEOUT_MS = 3e4;
|
|
16
|
+
var PREVIEW_UPLOAD_TIMEOUT_MS = 5 * 6e4;
|
|
17
|
+
var PREVIEW_ARTIFACT_TIMEOUT_MS = 6e4;
|
|
18
|
+
var PREVIEW_RETRY_ATTEMPTS = 3;
|
|
19
|
+
var PREVIEW_RETRY_BASE_DELAY_MS = 750;
|
|
9
20
|
function getCfg(api) {
|
|
10
21
|
const cfg = api?.config ?? {};
|
|
11
22
|
const pluginCfg = cfg?.plugins?.entries?.["openclaw-riddledc"]?.config ?? {};
|
|
@@ -51,6 +62,76 @@ function abToBase64(ab) {
|
|
|
51
62
|
function getWorkspacePath(api) {
|
|
52
63
|
return api?.workspacePath ?? process.cwd();
|
|
53
64
|
}
|
|
65
|
+
function describeError(err) {
|
|
66
|
+
const anyErr = err;
|
|
67
|
+
const parts = [];
|
|
68
|
+
if (err instanceof Error) parts.push(err.message);
|
|
69
|
+
else parts.push(String(err));
|
|
70
|
+
const cause = anyErr?.cause;
|
|
71
|
+
if (cause) {
|
|
72
|
+
const causeParts = [
|
|
73
|
+
cause.code ? `code=${cause.code}` : "",
|
|
74
|
+
cause.name ? `name=${cause.name}` : "",
|
|
75
|
+
cause.message ? `message=${cause.message}` : ""
|
|
76
|
+
].filter(Boolean);
|
|
77
|
+
if (causeParts.length) parts.push(`cause: ${causeParts.join(" ")}`);
|
|
78
|
+
}
|
|
79
|
+
return parts.join("; ");
|
|
80
|
+
}
|
|
81
|
+
async function fetchWithTimeout(url, init, timeoutMs, label) {
|
|
82
|
+
const controller = new AbortController();
|
|
83
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
84
|
+
try {
|
|
85
|
+
return await fetch(url, { ...init, signal: controller.signal });
|
|
86
|
+
} catch (err) {
|
|
87
|
+
if (err?.name === "AbortError") {
|
|
88
|
+
throw new Error(`${label} timed out after ${Math.round(timeoutMs / 1e3)}s`);
|
|
89
|
+
}
|
|
90
|
+
throw err;
|
|
91
|
+
} finally {
|
|
92
|
+
clearTimeout(timer);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function isTransientFetchError(err) {
|
|
96
|
+
const text = describeError(err).toLowerCase();
|
|
97
|
+
return [
|
|
98
|
+
"fetch failed",
|
|
99
|
+
"timed out",
|
|
100
|
+
"timeout",
|
|
101
|
+
"econnreset",
|
|
102
|
+
"econnrefused",
|
|
103
|
+
"etimedout",
|
|
104
|
+
"eai_again",
|
|
105
|
+
"socket",
|
|
106
|
+
"network",
|
|
107
|
+
"und_err",
|
|
108
|
+
"terminated"
|
|
109
|
+
].some((needle) => text.includes(needle));
|
|
110
|
+
}
|
|
111
|
+
function sleep(ms) {
|
|
112
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
113
|
+
}
|
|
114
|
+
async function fetchWithRetry(url, init, timeoutMs, label, opts = {}) {
|
|
115
|
+
const attempts = Math.max(1, opts.attempts ?? PREVIEW_RETRY_ATTEMPTS);
|
|
116
|
+
const baseDelayMs = opts.baseDelayMs ?? PREVIEW_RETRY_BASE_DELAY_MS;
|
|
117
|
+
let lastErr;
|
|
118
|
+
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
119
|
+
try {
|
|
120
|
+
return await fetchWithTimeout(url, init, timeoutMs, label);
|
|
121
|
+
} catch (err) {
|
|
122
|
+
lastErr = err;
|
|
123
|
+
if (attempt >= attempts || !isTransientFetchError(err)) break;
|
|
124
|
+
const jitterMs = Math.floor(Math.random() * 250);
|
|
125
|
+
const delayMs = Math.min(baseDelayMs * Math.pow(2, attempt - 1) + jitterMs, 5e3);
|
|
126
|
+
console.warn(`[openclaw-riddledc] ${label} attempt ${attempt}/${attempts} failed: ${describeError(err)}; retrying in ${delayMs}ms`);
|
|
127
|
+
await sleep(delayMs);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
throw new Error(`${label} failed after ${attempts} attempts: ${describeError(lastErr)}`);
|
|
131
|
+
}
|
|
132
|
+
function isAlreadyStartedResponse(status, body) {
|
|
133
|
+
return status === 409 && /already in status:\s*(queued|running|complete|completed)/i.test(body);
|
|
134
|
+
}
|
|
54
135
|
async function writeArtifact(workspace, subdir, filename, content) {
|
|
55
136
|
const dir = join(workspace, "riddle", subdir);
|
|
56
137
|
await mkdir(dir, { recursive: true });
|
|
@@ -785,70 +866,8 @@ function register(api) {
|
|
|
785
866
|
framework: Type.Optional(Type.String({ description: "Framework hint: 'spa' (default) or 'static'" }))
|
|
786
867
|
}),
|
|
787
868
|
async execute(_id, params) {
|
|
788
|
-
const
|
|
789
|
-
|
|
790
|
-
return { content: [{ type: "text", text: JSON.stringify({ ok: false, error: "Missing Riddle API key." }, null, 2) }] };
|
|
791
|
-
}
|
|
792
|
-
assertAllowedBaseUrl(baseUrl);
|
|
793
|
-
const dir = params.directory;
|
|
794
|
-
if (!dir || typeof dir !== "string") throw new Error("directory must be an absolute path");
|
|
795
|
-
try {
|
|
796
|
-
const st = await stat(dir);
|
|
797
|
-
if (!st.isDirectory()) throw new Error(`Not a directory: ${dir}`);
|
|
798
|
-
} catch (e) {
|
|
799
|
-
return { content: [{ type: "text", text: JSON.stringify({ ok: false, error: `Cannot access directory: ${e.message}` }, null, 2) }] };
|
|
800
|
-
}
|
|
801
|
-
const endpoint = baseUrl.replace(/\/$/, "");
|
|
802
|
-
const createRes = await fetch(`${endpoint}/v1/preview`, {
|
|
803
|
-
method: "POST",
|
|
804
|
-
headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
|
|
805
|
-
body: JSON.stringify({ framework: params.framework || "spa" })
|
|
806
|
-
});
|
|
807
|
-
if (!createRes.ok) {
|
|
808
|
-
const err = await createRes.text();
|
|
809
|
-
return { content: [{ type: "text", text: JSON.stringify({ ok: false, error: `Create failed: HTTP ${createRes.status} ${err}` }, null, 2) }] };
|
|
810
|
-
}
|
|
811
|
-
const created = await createRes.json();
|
|
812
|
-
const tarball = `/tmp/riddle-preview-${created.id}.tar.gz`;
|
|
813
|
-
try {
|
|
814
|
-
await execFile("tar", ["czf", tarball, "-C", dir, "."], { timeout: 6e4 });
|
|
815
|
-
const tarData = await readFile(tarball);
|
|
816
|
-
const uploadRes = await fetch(created.upload_url, {
|
|
817
|
-
method: "PUT",
|
|
818
|
-
headers: { "Content-Type": "application/gzip" },
|
|
819
|
-
body: tarData
|
|
820
|
-
});
|
|
821
|
-
if (!uploadRes.ok) {
|
|
822
|
-
return { content: [{ type: "text", text: JSON.stringify({ ok: false, id: created.id, error: `Upload failed: HTTP ${uploadRes.status}` }, null, 2) }] };
|
|
823
|
-
}
|
|
824
|
-
} finally {
|
|
825
|
-
try {
|
|
826
|
-
await rm(tarball, { force: true });
|
|
827
|
-
} catch {
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
const publishRes = await fetch(`${endpoint}/v1/preview/${created.id}/publish`, {
|
|
831
|
-
method: "POST",
|
|
832
|
-
headers: { Authorization: `Bearer ${apiKey}` }
|
|
833
|
-
});
|
|
834
|
-
if (!publishRes.ok) {
|
|
835
|
-
const err = await publishRes.text();
|
|
836
|
-
return { content: [{ type: "text", text: JSON.stringify({ ok: false, id: created.id, error: `Publish failed: HTTP ${publishRes.status} ${err}` }, null, 2) }] };
|
|
837
|
-
}
|
|
838
|
-
const published = await publishRes.json();
|
|
839
|
-
return {
|
|
840
|
-
content: [{
|
|
841
|
-
type: "text",
|
|
842
|
-
text: JSON.stringify({
|
|
843
|
-
ok: true,
|
|
844
|
-
id: published.id,
|
|
845
|
-
preview_url: published.preview_url,
|
|
846
|
-
file_count: published.file_count,
|
|
847
|
-
total_bytes: published.total_bytes,
|
|
848
|
-
expires_at: created.expires_at
|
|
849
|
-
}, null, 2)
|
|
850
|
-
}]
|
|
851
|
-
};
|
|
869
|
+
const result = await createStaticPreview(configFromOpenClawApi(api), params);
|
|
870
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
852
871
|
}
|
|
853
872
|
},
|
|
854
873
|
{ optional: true }
|
|
@@ -861,21 +880,8 @@ function register(api) {
|
|
|
861
880
|
id: Type.String({ description: "Preview ID (e.g. pv_a1b2c3d4)" })
|
|
862
881
|
}),
|
|
863
882
|
async execute(_id, params) {
|
|
864
|
-
const
|
|
865
|
-
|
|
866
|
-
return { content: [{ type: "text", text: JSON.stringify({ ok: false, error: "Missing Riddle API key." }, null, 2) }] };
|
|
867
|
-
}
|
|
868
|
-
assertAllowedBaseUrl(baseUrl);
|
|
869
|
-
const res = await fetch(`${baseUrl.replace(/\/$/, "")}/v1/preview/${params.id}`, {
|
|
870
|
-
method: "DELETE",
|
|
871
|
-
headers: { Authorization: `Bearer ${apiKey}` }
|
|
872
|
-
});
|
|
873
|
-
if (!res.ok) {
|
|
874
|
-
const err = await res.text();
|
|
875
|
-
return { content: [{ type: "text", text: JSON.stringify({ ok: false, error: `Delete failed: HTTP ${res.status} ${err}` }, null, 2) }] };
|
|
876
|
-
}
|
|
877
|
-
const data = await res.json();
|
|
878
|
-
return { content: [{ type: "text", text: JSON.stringify({ ok: true, deleted: true, files_removed: data.files_removed }, null, 2) }] };
|
|
883
|
+
const result = await deleteStaticPreview(configFromOpenClawApi(api), params.id);
|
|
884
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
879
885
|
}
|
|
880
886
|
},
|
|
881
887
|
{ optional: true }
|
|
@@ -927,11 +933,16 @@ function register(api) {
|
|
|
927
933
|
const envBody = {};
|
|
928
934
|
if (hasSensitiveEnv) envBody.env = params.sensitive_env;
|
|
929
935
|
if (hasLocalStorage) envBody.localStorage = params.localStorage;
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
936
|
+
let envRes;
|
|
937
|
+
try {
|
|
938
|
+
envRes = await fetchWithTimeout(`${endpoint}/v1/server-preview/env`, {
|
|
939
|
+
method: "POST",
|
|
940
|
+
headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
|
|
941
|
+
body: JSON.stringify(envBody)
|
|
942
|
+
}, PREVIEW_REQUEST_TIMEOUT_MS, "server preview env store");
|
|
943
|
+
} catch (e) {
|
|
944
|
+
return { content: [{ type: "text", text: JSON.stringify({ ok: false, error: `Store env failed: ${describeError(e)}` }, null, 2) }] };
|
|
945
|
+
}
|
|
935
946
|
if (!envRes.ok) {
|
|
936
947
|
const err = await envRes.text();
|
|
937
948
|
return { content: [{ type: "text", text: JSON.stringify({ ok: false, error: `Store env failed: HTTP ${envRes.status} ${err}` }, null, 2) }] };
|
|
@@ -957,11 +968,16 @@ function register(api) {
|
|
|
957
968
|
if (params.navigation_timeout) createBody.navigation_timeout = params.navigation_timeout;
|
|
958
969
|
if (params.color_scheme) createBody.color_scheme = params.color_scheme;
|
|
959
970
|
if (params.viewport) createBody.viewport = params.viewport;
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
971
|
+
let createRes;
|
|
972
|
+
try {
|
|
973
|
+
createRes = await fetchWithRetry(`${endpoint}/v1/server-preview`, {
|
|
974
|
+
method: "POST",
|
|
975
|
+
headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
|
|
976
|
+
body: JSON.stringify(createBody)
|
|
977
|
+
}, PREVIEW_REQUEST_TIMEOUT_MS, "server preview create");
|
|
978
|
+
} catch (e) {
|
|
979
|
+
return { content: [{ type: "text", text: JSON.stringify({ ok: false, error: `Create failed: ${describeError(e)}` }, null, 2) }] };
|
|
980
|
+
}
|
|
965
981
|
if (!createRes.ok) {
|
|
966
982
|
const err = await createRes.text();
|
|
967
983
|
return { content: [{ type: "text", text: JSON.stringify({ ok: false, error: `Create failed: HTTP ${createRes.status} ${err}` }, null, 2) }] };
|
|
@@ -973,11 +989,16 @@ function register(api) {
|
|
|
973
989
|
const excludeArgs = excludes.flatMap((p) => ["--exclude", p]);
|
|
974
990
|
await execFile("tar", ["czf", tarball, ...excludeArgs, "-C", dir, "."], { timeout: 12e4 });
|
|
975
991
|
const tarData = await readFile(tarball);
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
992
|
+
let uploadRes;
|
|
993
|
+
try {
|
|
994
|
+
uploadRes = await fetchWithRetry(created.upload_url, {
|
|
995
|
+
method: "PUT",
|
|
996
|
+
headers: { "Content-Type": "application/gzip" },
|
|
997
|
+
body: tarData
|
|
998
|
+
}, PREVIEW_UPLOAD_TIMEOUT_MS, "server preview upload");
|
|
999
|
+
} catch (e) {
|
|
1000
|
+
return { content: [{ type: "text", text: JSON.stringify({ ok: false, job_id: created.job_id, error: `Upload failed: ${describeError(e)}` }, null, 2) }] };
|
|
1001
|
+
}
|
|
981
1002
|
if (!uploadRes.ok) {
|
|
982
1003
|
return { content: [{ type: "text", text: JSON.stringify({ ok: false, job_id: created.job_id, error: `Upload failed: HTTP ${uploadRes.status}` }, null, 2) }] };
|
|
983
1004
|
}
|
|
@@ -987,21 +1008,35 @@ function register(api) {
|
|
|
987
1008
|
} catch {
|
|
988
1009
|
}
|
|
989
1010
|
}
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
1011
|
+
let startRes;
|
|
1012
|
+
try {
|
|
1013
|
+
startRes = await fetchWithRetry(`${endpoint}/v1/server-preview/${created.job_id}/start`, {
|
|
1014
|
+
method: "POST",
|
|
1015
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
1016
|
+
}, PREVIEW_REQUEST_TIMEOUT_MS, "server preview start");
|
|
1017
|
+
} catch (e) {
|
|
1018
|
+
return { content: [{ type: "text", text: JSON.stringify({ ok: false, job_id: created.job_id, error: `Start failed: ${describeError(e)}` }, null, 2) }] };
|
|
1019
|
+
}
|
|
994
1020
|
if (!startRes.ok) {
|
|
995
1021
|
const err = await startRes.text();
|
|
996
|
-
|
|
1022
|
+
if (isAlreadyStartedResponse(startRes.status, err)) {
|
|
1023
|
+
console.warn(`[openclaw-riddledc] server preview start returned ${startRes.status} for ${created.job_id}; continuing to poll`);
|
|
1024
|
+
} else {
|
|
1025
|
+
return { content: [{ type: "text", text: JSON.stringify({ ok: false, job_id: created.job_id, error: `Start failed: HTTP ${startRes.status} ${err}` }, null, 2) }] };
|
|
1026
|
+
}
|
|
997
1027
|
}
|
|
998
1028
|
const timeoutMs = ((params.timeout || 120) + 60) * 1e3;
|
|
999
1029
|
const pollStart = Date.now();
|
|
1000
1030
|
const POLL_INTERVAL = 3e3;
|
|
1001
1031
|
while (Date.now() - pollStart < timeoutMs) {
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1032
|
+
let statusRes;
|
|
1033
|
+
try {
|
|
1034
|
+
statusRes = await fetchWithRetry(`${endpoint}/v1/server-preview/${created.job_id}`, {
|
|
1035
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
1036
|
+
}, PREVIEW_REQUEST_TIMEOUT_MS, "server preview poll", { attempts: 2 });
|
|
1037
|
+
} catch (e) {
|
|
1038
|
+
return { content: [{ type: "text", text: JSON.stringify({ ok: false, job_id: created.job_id, error: `Poll failed: ${describeError(e)}` }, null, 2) }] };
|
|
1039
|
+
}
|
|
1005
1040
|
if (!statusRes.ok) {
|
|
1006
1041
|
return { content: [{ type: "text", text: JSON.stringify({ ok: false, job_id: created.job_id, error: `Poll failed: HTTP ${statusRes.status}` }, null, 2) }] };
|
|
1007
1042
|
}
|
|
@@ -1020,7 +1055,7 @@ function register(api) {
|
|
|
1020
1055
|
for (const output of result.outputs) {
|
|
1021
1056
|
if (output.name && /\.(png|jpg|jpeg)$/i.test(output.name) && output.url) {
|
|
1022
1057
|
try {
|
|
1023
|
-
const imgRes = await
|
|
1058
|
+
const imgRes = await fetchWithTimeout(output.url, {}, PREVIEW_ARTIFACT_TIMEOUT_MS, "server preview artifact download");
|
|
1024
1059
|
if (imgRes.ok) {
|
|
1025
1060
|
const buf = await imgRes.arrayBuffer();
|
|
1026
1061
|
const base64 = Buffer.from(buf).toString("base64");
|
|
@@ -1096,11 +1131,16 @@ function register(api) {
|
|
|
1096
1131
|
const envBody = {};
|
|
1097
1132
|
if (hasSensitiveEnv) envBody.env = params.sensitive_env;
|
|
1098
1133
|
if (hasLocalStorage) envBody.localStorage = params.localStorage;
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1134
|
+
let envRes;
|
|
1135
|
+
try {
|
|
1136
|
+
envRes = await fetchWithTimeout(`${endpoint}/v1/build-preview/env`, {
|
|
1137
|
+
method: "POST",
|
|
1138
|
+
headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
|
|
1139
|
+
body: JSON.stringify(envBody)
|
|
1140
|
+
}, PREVIEW_REQUEST_TIMEOUT_MS, "build preview env store");
|
|
1141
|
+
} catch (e) {
|
|
1142
|
+
return { content: [{ type: "text", text: JSON.stringify({ ok: false, error: `Store env failed: ${describeError(e)}` }, null, 2) }] };
|
|
1143
|
+
}
|
|
1104
1144
|
if (!envRes.ok) {
|
|
1105
1145
|
const err = await envRes.text();
|
|
1106
1146
|
return { content: [{ type: "text", text: JSON.stringify({ ok: false, error: `Store env failed: HTTP ${envRes.status} ${err}` }, null, 2) }] };
|
|
@@ -1128,11 +1168,16 @@ function register(api) {
|
|
|
1128
1168
|
if (params.color_scheme) createBody.color_scheme = params.color_scheme;
|
|
1129
1169
|
if (params.viewport) createBody.viewport = params.viewport;
|
|
1130
1170
|
if (params.audit) createBody.audit = true;
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1171
|
+
let createRes;
|
|
1172
|
+
try {
|
|
1173
|
+
createRes = await fetchWithRetry(`${endpoint}/v1/build-preview`, {
|
|
1174
|
+
method: "POST",
|
|
1175
|
+
headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
|
|
1176
|
+
body: JSON.stringify(createBody)
|
|
1177
|
+
}, PREVIEW_REQUEST_TIMEOUT_MS, "build preview create");
|
|
1178
|
+
} catch (e) {
|
|
1179
|
+
return { content: [{ type: "text", text: JSON.stringify({ ok: false, error: `Create failed: ${describeError(e)}` }, null, 2) }] };
|
|
1180
|
+
}
|
|
1136
1181
|
if (!createRes.ok) {
|
|
1137
1182
|
const err = await createRes.text();
|
|
1138
1183
|
return { content: [{ type: "text", text: JSON.stringify({ ok: false, error: `Create failed: HTTP ${createRes.status} ${err}` }, null, 2) }] };
|
|
@@ -1144,11 +1189,16 @@ function register(api) {
|
|
|
1144
1189
|
const excludeArgs = excludes.flatMap((p) => ["--exclude", p]);
|
|
1145
1190
|
await execFile("tar", ["czf", tarball, ...excludeArgs, "-C", dir, "."], { timeout: 12e4 });
|
|
1146
1191
|
const tarData = await readFile(tarball);
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1192
|
+
let uploadRes;
|
|
1193
|
+
try {
|
|
1194
|
+
uploadRes = await fetchWithRetry(created.upload_url, {
|
|
1195
|
+
method: "PUT",
|
|
1196
|
+
headers: { "Content-Type": "application/gzip" },
|
|
1197
|
+
body: tarData
|
|
1198
|
+
}, PREVIEW_UPLOAD_TIMEOUT_MS, "build preview upload");
|
|
1199
|
+
} catch (e) {
|
|
1200
|
+
return { content: [{ type: "text", text: JSON.stringify({ ok: false, job_id: created.job_id, error: `Upload failed: ${describeError(e)}` }, null, 2) }] };
|
|
1201
|
+
}
|
|
1152
1202
|
if (!uploadRes.ok) {
|
|
1153
1203
|
return { content: [{ type: "text", text: JSON.stringify({ ok: false, job_id: created.job_id, error: `Upload failed: HTTP ${uploadRes.status}` }, null, 2) }] };
|
|
1154
1204
|
}
|
|
@@ -1158,21 +1208,35 @@ function register(api) {
|
|
|
1158
1208
|
} catch {
|
|
1159
1209
|
}
|
|
1160
1210
|
}
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1211
|
+
let startRes;
|
|
1212
|
+
try {
|
|
1213
|
+
startRes = await fetchWithRetry(`${endpoint}/v1/build-preview/${created.job_id}/start`, {
|
|
1214
|
+
method: "POST",
|
|
1215
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
1216
|
+
}, PREVIEW_REQUEST_TIMEOUT_MS, "build preview start");
|
|
1217
|
+
} catch (e) {
|
|
1218
|
+
return { content: [{ type: "text", text: JSON.stringify({ ok: false, job_id: created.job_id, error: `Start failed: ${describeError(e)}` }, null, 2) }] };
|
|
1219
|
+
}
|
|
1165
1220
|
if (!startRes.ok) {
|
|
1166
1221
|
const err = await startRes.text();
|
|
1167
|
-
|
|
1222
|
+
if (isAlreadyStartedResponse(startRes.status, err)) {
|
|
1223
|
+
console.warn(`[openclaw-riddledc] build preview start returned ${startRes.status} for ${created.job_id}; continuing to poll`);
|
|
1224
|
+
} else {
|
|
1225
|
+
return { content: [{ type: "text", text: JSON.stringify({ ok: false, job_id: created.job_id, error: `Start failed: HTTP ${startRes.status} ${err}` }, null, 2) }] };
|
|
1226
|
+
}
|
|
1168
1227
|
}
|
|
1169
1228
|
const timeoutMs = ((params.timeout || 180) + 120) * 1e3;
|
|
1170
1229
|
const pollStart = Date.now();
|
|
1171
1230
|
const POLL_INTERVAL = 3e3;
|
|
1172
1231
|
while (Date.now() - pollStart < timeoutMs) {
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1232
|
+
let statusRes;
|
|
1233
|
+
try {
|
|
1234
|
+
statusRes = await fetchWithRetry(`${endpoint}/v1/build-preview/${created.job_id}`, {
|
|
1235
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
1236
|
+
}, PREVIEW_REQUEST_TIMEOUT_MS, "build preview poll", { attempts: 2 });
|
|
1237
|
+
} catch (e) {
|
|
1238
|
+
return { content: [{ type: "text", text: JSON.stringify({ ok: false, job_id: created.job_id, error: `Poll failed: ${describeError(e)}` }, null, 2) }] };
|
|
1239
|
+
}
|
|
1176
1240
|
if (!statusRes.ok) {
|
|
1177
1241
|
return { content: [{ type: "text", text: JSON.stringify({ ok: false, job_id: created.job_id, error: `Poll failed: HTTP ${statusRes.status}` }, null, 2) }] };
|
|
1178
1242
|
}
|
|
@@ -1195,7 +1259,7 @@ function register(api) {
|
|
|
1195
1259
|
for (const output of result.outputs) {
|
|
1196
1260
|
if (output.name && /\.(png|jpg|jpeg)$/i.test(output.name) && output.url) {
|
|
1197
1261
|
try {
|
|
1198
|
-
const imgRes = await
|
|
1262
|
+
const imgRes = await fetchWithTimeout(output.url, {}, PREVIEW_ARTIFACT_TIMEOUT_MS, "build preview artifact download");
|
|
1199
1263
|
if (imgRes.ok) {
|
|
1200
1264
|
const buf = await imgRes.arrayBuffer();
|
|
1201
1265
|
const base64 = Buffer.from(buf).toString("base64");
|
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.3",
|
|
6
6
|
"notes": "0.8.0: Added riddle_build_preview for Dockerfile-based builds with image caching.",
|
|
7
7
|
"type": "plugin",
|
|
8
8
|
"bundledSkills": [],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@riddledc/openclaw-riddledc",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.3",
|
|
4
4
|
"description": "OpenClaw integration package for RiddleDC (no secrets).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "RiddleDC",
|
|
@@ -18,6 +18,11 @@
|
|
|
18
18
|
"types": "./dist/index.d.ts",
|
|
19
19
|
"import": "./dist/index.js",
|
|
20
20
|
"require": "./dist/index.cjs"
|
|
21
|
+
},
|
|
22
|
+
"./core": {
|
|
23
|
+
"types": "./dist/core.d.ts",
|
|
24
|
+
"import": "./dist/core.js",
|
|
25
|
+
"require": "./dist/core.cjs"
|
|
21
26
|
}
|
|
22
27
|
},
|
|
23
28
|
"files": [
|
|
@@ -45,7 +50,7 @@
|
|
|
45
50
|
"typescript": "^5.4.5"
|
|
46
51
|
},
|
|
47
52
|
"scripts": {
|
|
48
|
-
"build": "npm run sync:openclaw-plugin-version && tsup src/index.ts --format cjs,esm --dts --out-dir dist",
|
|
53
|
+
"build": "npm run sync:openclaw-plugin-version && tsup src/index.ts src/core.ts --format cjs,esm --dts --out-dir dist",
|
|
49
54
|
"clean": "rm -rf dist",
|
|
50
55
|
"lint": "echo 'lint: (not configured)'",
|
|
51
56
|
"test": "echo 'test: (not configured)'",
|