@coolclaw/coolclaw 0.3.4 → 0.4.1
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.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
coolclawChannelPlugin,
|
|
3
3
|
setCoolclawRuntime
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-XVB6UKBR.js";
|
|
5
5
|
import {
|
|
6
6
|
runCoolclawSetup
|
|
7
7
|
} from "./chunk-A54AF634.js";
|
|
@@ -62,29 +62,41 @@ function splitCsv(value) {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
// src/compat.ts
|
|
65
|
-
var SUPPORTED_HOST_MIN = "2026.
|
|
65
|
+
var SUPPORTED_HOST_MIN = "2026.3.22";
|
|
66
|
+
var MAX_TESTED_VERSION = "2026.5.7";
|
|
67
|
+
function parseVersion(v) {
|
|
68
|
+
const clean = v.split("-")[0].replace(/\s*\(.*\)/, "").trim();
|
|
69
|
+
const [y, m, d] = clean.split(".").map(Number);
|
|
70
|
+
return [y || 0, m || 0, d || 0];
|
|
71
|
+
}
|
|
72
|
+
function cmp(a, b) {
|
|
73
|
+
const [ay, am, ad] = parseVersion(a);
|
|
74
|
+
const [by, bm, bd] = parseVersion(b);
|
|
75
|
+
if (ay !== by) return ay > by ? 1 : -1;
|
|
76
|
+
if (am !== bm) return am > bm ? 1 : -1;
|
|
77
|
+
if (ad !== bd) return ad > bd ? 1 : -1;
|
|
78
|
+
return 0;
|
|
79
|
+
}
|
|
66
80
|
function assertHostCompatibility(hostVersion) {
|
|
67
81
|
if (!hostVersion || hostVersion === "unknown") return;
|
|
68
|
-
if (
|
|
82
|
+
if (cmp(hostVersion, SUPPORTED_HOST_MIN) >= 0) return;
|
|
69
83
|
throw new Error(
|
|
70
84
|
`This version of @coolclaw/coolclaw requires OpenClaw >=${SUPPORTED_HOST_MIN}, but found ${hostVersion}. Please upgrade OpenClaw:
|
|
71
85
|
npm install -g openclaw@latest
|
|
72
86
|
Then reinstall the plugin:
|
|
73
87
|
openclaw plugins install @coolclaw/coolclaw
|
|
74
88
|
|
|
75
|
-
Or use the one-command installer
|
|
89
|
+
Or use the one-command installer:
|
|
76
90
|
npx @coolclaw/coolclaw-cli install`
|
|
77
91
|
);
|
|
78
92
|
}
|
|
79
|
-
function
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if (host[i] < min[i]) return false;
|
|
93
|
+
function advisoryHostCompatibility(hostVersion) {
|
|
94
|
+
if (!hostVersion || hostVersion === "unknown") return;
|
|
95
|
+
if (cmp(hostVersion, MAX_TESTED_VERSION) > 0) {
|
|
96
|
+
console.warn(
|
|
97
|
+
`[coolclaw] Host version ${hostVersion} is newer than the latest tested version (${MAX_TESTED_VERSION}). Proceeding without version gate; please report incompatibilities.`
|
|
98
|
+
);
|
|
86
99
|
}
|
|
87
|
-
return true;
|
|
88
100
|
}
|
|
89
101
|
|
|
90
102
|
// index.ts
|
|
@@ -112,6 +124,7 @@ var entry = defineChannelPluginEntry({
|
|
|
112
124
|
},
|
|
113
125
|
registerFull(api) {
|
|
114
126
|
assertHostCompatibility(api.runtime?.version);
|
|
127
|
+
advisoryHostCompatibility(api.runtime?.version);
|
|
115
128
|
setCoolclawRuntime(api.runtime);
|
|
116
129
|
}
|
|
117
130
|
});
|
|
@@ -167,19 +167,25 @@ function asRecordArray(v) {
|
|
|
167
167
|
function asStringArray(v) {
|
|
168
168
|
return Array.isArray(v) ? v.filter((x) => typeof x === "string") : [];
|
|
169
169
|
}
|
|
170
|
-
function renderPlayerInfo(list) {
|
|
170
|
+
function renderPlayerInfo(list, selfSeat) {
|
|
171
171
|
if (list.length === 0) return "\uFF08\u65E0\u5EA7\u4F4D\u4FE1\u606F\uFF09";
|
|
172
172
|
return list.map((p) => {
|
|
173
173
|
const seat = asNumberOrNull(p.seat);
|
|
174
|
-
const name = asString(p.name, "\u672A\u77E5");
|
|
175
|
-
const voice = asString(p.voiceDesc, "");
|
|
176
174
|
const alive = p.alive === true ? "\u5B58\u6D3B" : "\u5DF2\u6B7B\u4EA1";
|
|
177
|
-
|
|
175
|
+
if (seat != null && selfSeat != null && seat === selfSeat) {
|
|
176
|
+
const name = asString(p.name, "\u672A\u77E5");
|
|
177
|
+
const voice = asString(p.voiceDesc, "");
|
|
178
|
+
return `\u5EA7\u4F4D${seat}\uFF08\u4F60\u81EA\u5DF1\uFF0C${name}${voice ? `\uFF0C${voice}` : ""}\uFF0C${alive}\uFF09`;
|
|
179
|
+
}
|
|
180
|
+
return `\u5EA7\u4F4D${seat ?? "?"}\uFF08${alive}\uFF09`;
|
|
178
181
|
}).join("\uFF1B");
|
|
179
182
|
}
|
|
183
|
+
function stripAudioSuffix(line) {
|
|
184
|
+
return line.replace(/\s*:audio=https?:\/\/\S+/g, "").replace(/\s*:cb=[^\s|]+/g, "");
|
|
185
|
+
}
|
|
180
186
|
function renderHistory(history) {
|
|
181
187
|
if (history.length === 0) return "\uFF08\u6682\u65E0\u5386\u53F2\u8BB0\u5F55\uFF09";
|
|
182
|
-
return history.map((h, i) => `${i + 1}. ${h}`).join("\n");
|
|
188
|
+
return history.map((h, i) => `${i + 1}. ${stripAudioSuffix(h)}`).join("\n");
|
|
183
189
|
}
|
|
184
190
|
function renderAliveSeats(seats) {
|
|
185
191
|
return seats.length === 0 ? "\uFF08\u65E0\uFF09" : `[${seats.join(", ")}]`;
|
|
@@ -191,7 +197,7 @@ function renderHeader(eventType, outer, payload) {
|
|
|
191
197
|
const selfName = asString(payload.selfAgentName, "");
|
|
192
198
|
const phase = eventType.startsWith("DAY_") || eventType === "LAST_WORD_TURN" || eventType === "HUNTER_SKILL_TURN" ? "\u767D\u5929" : "\u591C\u665A";
|
|
193
199
|
const aliveSeats = asNumberArray(payload.aliveSeats);
|
|
194
|
-
const playerInfo = renderPlayerInfo(asRecordArray(payload.playerInfoList));
|
|
200
|
+
const playerInfo = renderPlayerInfo(asRecordArray(payload.playerInfoList), selfSeat);
|
|
195
201
|
const history = renderHistory(asStringArray(payload.scopedHistory));
|
|
196
202
|
return [
|
|
197
203
|
`[\u6E38\u620F] \u72FC\u4EBA\u6740 \xB7 \u7B2C ${round} \u8F6E \xB7 ${phase} \xB7 ${describeEventType(eventType)}`,
|
|
@@ -250,15 +256,26 @@ function renderWolfTurn(payload) {
|
|
|
250
256
|
const round = asNumberOrNull(payload.wolfAttemptRound) ?? 1;
|
|
251
257
|
const isFirst = payload.isFirstSpeakerInRound === true;
|
|
252
258
|
const teammateSeat = asNumberOrNull(payload.teammateSeat);
|
|
253
|
-
const teammateName = asString(payload.teammateName, "");
|
|
254
259
|
const tp = asRecord(payload.teammateProposal);
|
|
255
|
-
const
|
|
260
|
+
const tpSeat = asNumberOrNull(tp.seat);
|
|
261
|
+
const tpTarget = asNumberOrNull(tp.targetSeat);
|
|
262
|
+
const tpSpeech = asString(tp.speech, "").trim();
|
|
263
|
+
let teammateBlock;
|
|
264
|
+
if (tpTarget != null) {
|
|
265
|
+
const speechLine = tpSpeech ? `
|
|
266
|
+
===== \u72FC\u961F\u53CB\u53D1\u8A00\u5F00\u59CB =====
|
|
267
|
+
${tpSpeech}
|
|
268
|
+
===== \u72FC\u961F\u53CB\u53D1\u8A00\u7ED3\u675F =====` : "";
|
|
269
|
+
teammateBlock = `\u540C\u4F34\uFF08\u5EA7\u4F4D ${tpSeat ?? "?"}\uFF09\u672C\u8F6E\u5DF2\u63D0\u8BAE\u51FB\u6740\u5EA7\u4F4D ${tpTarget}\u3002${speechLine}`;
|
|
270
|
+
} else {
|
|
271
|
+
teammateBlock = isFirst ? "\u4F60\u662F\u672C\u8F6E\u9996\u4F4D\u53D1\u8A00\u7684\u72FC\u4EBA\u3002" : "\u540C\u4F34\u5C1A\u672A\u53D1\u8A00\u3002";
|
|
272
|
+
}
|
|
256
273
|
const lastRound = asRecordArray(payload.lastRoundChoices);
|
|
257
274
|
const lastRoundStr = lastRound.length > 0 ? `\u4E0A\u4E00\u8F6E\u6295\u7968\u8BB0\u5F55\uFF1A${lastRound.map((c) => `\u5EA7\u4F4D${c.seat}\u2192\u5EA7\u4F4D${c.targetSeat}`).join("\uFF1B")}\u3002` : "";
|
|
258
275
|
return [
|
|
259
276
|
`\u3010\u4EFB\u52A1\u3011\u72FC\u4EBA\u6740\u4EBA\u534F\u5546\uFF08\u7B2C ${round} \u8F6E\uFF09`,
|
|
260
|
-
teammateSeat != null ? `\u4F60\u7684\u72FC\u540C\u4F34\uFF1A\u5EA7\u4F4D ${teammateSeat}
|
|
261
|
-
|
|
277
|
+
teammateSeat != null ? `\u4F60\u7684\u72FC\u540C\u4F34\uFF1A\u5EA7\u4F4D ${teammateSeat}\u3002` : "\u4F60\u662F\u72EC\u72FC\u3002",
|
|
278
|
+
teammateBlock,
|
|
262
279
|
lastRoundStr,
|
|
263
280
|
`\u5408\u6CD5\u76EE\u6807\uFF1A${renderAliveSeats(aliveSeats)} \u4E2D\u7684\u4EFB\u610F\u4E00\u4E2A\u3002`,
|
|
264
281
|
``,
|
|
@@ -717,6 +734,22 @@ async function sendMedia(input) {
|
|
|
717
734
|
}
|
|
718
735
|
return response.messageId;
|
|
719
736
|
}
|
|
737
|
+
async function sendGameAction(input) {
|
|
738
|
+
const frame = createFrame("GAME_ACTION", {
|
|
739
|
+
gameId: input.gameId,
|
|
740
|
+
actionType: input.actionType,
|
|
741
|
+
actionData: input.actionData,
|
|
742
|
+
// AgentActionRequest.timestamp 契约为 String
|
|
743
|
+
timestamp: String(Date.now()),
|
|
744
|
+
turnSeq: input.turnSeq,
|
|
745
|
+
eventId: input.eventId,
|
|
746
|
+
traceId: input.traceId
|
|
747
|
+
});
|
|
748
|
+
const response = await input.client.request(frame);
|
|
749
|
+
if (response.ok === false) {
|
|
750
|
+
throw new Error(response.error?.message ?? "CoolClaw game action failed");
|
|
751
|
+
}
|
|
752
|
+
}
|
|
720
753
|
|
|
721
754
|
// src/game-action-parser.ts
|
|
722
755
|
function extractActionBlock(text) {
|
|
@@ -821,95 +854,13 @@ function fallbackActionFor(eventType, eventData) {
|
|
|
821
854
|
}
|
|
822
855
|
}
|
|
823
856
|
|
|
824
|
-
// src/game-action-client.ts
|
|
825
|
-
function buildUrl(gatewayUrl) {
|
|
826
|
-
const base = gatewayUrl.replace(/\/+$/, "");
|
|
827
|
-
const tail = base.endsWith("/riddle") ? "/api/chat/agent/action" : "/riddle/api/chat/agent/action";
|
|
828
|
-
return `${base}${tail}`;
|
|
829
|
-
}
|
|
830
|
-
function buildBody(input) {
|
|
831
|
-
return JSON.stringify({
|
|
832
|
-
gameId: input.gameId,
|
|
833
|
-
actionType: input.actionType,
|
|
834
|
-
actionData: input.actionData,
|
|
835
|
-
// AgentActionRequest.timestamp 契约为 String
|
|
836
|
-
timestamp: String(Date.now()),
|
|
837
|
-
turnSeq: input.turnSeq,
|
|
838
|
-
eventId: input.eventId,
|
|
839
|
-
traceId: input.traceId
|
|
840
|
-
});
|
|
841
|
-
}
|
|
842
|
-
function sleep(ms) {
|
|
843
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
844
|
-
}
|
|
845
|
-
async function submitGameAction(input) {
|
|
846
|
-
const url = buildUrl(input.gatewayUrl);
|
|
847
|
-
const body = buildBody(input);
|
|
848
|
-
const timeoutMs = input.timeoutMs ?? 5e3;
|
|
849
|
-
const maxRetries = input.maxRetries ?? 1;
|
|
850
|
-
const fetchImpl = input.fetchImpl ?? fetch;
|
|
851
|
-
const start = Date.now();
|
|
852
|
-
let attempts = 0;
|
|
853
|
-
let lastError;
|
|
854
|
-
let lastStatus;
|
|
855
|
-
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
856
|
-
attempts++;
|
|
857
|
-
const ac = new AbortController();
|
|
858
|
-
const timer = setTimeout(() => ac.abort(), timeoutMs);
|
|
859
|
-
try {
|
|
860
|
-
const resp = await fetchImpl(url, {
|
|
861
|
-
method: "POST",
|
|
862
|
-
headers: {
|
|
863
|
-
"Content-Type": "application/json",
|
|
864
|
-
Authorization: `Bearer ${input.token}`,
|
|
865
|
-
"X-User-Id": input.agentId,
|
|
866
|
-
"X-User-Type": "AGENT"
|
|
867
|
-
},
|
|
868
|
-
body,
|
|
869
|
-
signal: ac.signal
|
|
870
|
-
});
|
|
871
|
-
lastStatus = resp.status;
|
|
872
|
-
if (resp.ok) {
|
|
873
|
-
const elapsedMs2 = Date.now() - start;
|
|
874
|
-
input.log?.info?.(
|
|
875
|
-
`[GAME-ACTION] post ok gameId=${input.gameId} eventId=${input.eventId} actionType=${input.actionType} status=${resp.status} elapsedMs=${elapsedMs2} attempts=${attempts}`
|
|
876
|
-
);
|
|
877
|
-
return { success: true, status: resp.status, elapsedMs: elapsedMs2, attempts };
|
|
878
|
-
}
|
|
879
|
-
let text = "";
|
|
880
|
-
try {
|
|
881
|
-
text = (await resp.text()).slice(0, 500);
|
|
882
|
-
} catch {
|
|
883
|
-
}
|
|
884
|
-
lastError = `http_${resp.status}: ${text}`;
|
|
885
|
-
input.log?.warn?.(
|
|
886
|
-
`[GAME-ACTION] post non-2xx gameId=${input.gameId} eventId=${input.eventId} status=${resp.status} attempt=${attempt} body=${text}`
|
|
887
|
-
);
|
|
888
|
-
} catch (err) {
|
|
889
|
-
lastError = err instanceof Error ? err.message : String(err);
|
|
890
|
-
input.log?.warn?.(
|
|
891
|
-
`[GAME-ACTION] post network error gameId=${input.gameId} eventId=${input.eventId} attempt=${attempt} err=${lastError}`
|
|
892
|
-
);
|
|
893
|
-
} finally {
|
|
894
|
-
clearTimeout(timer);
|
|
895
|
-
}
|
|
896
|
-
if (attempt < maxRetries) {
|
|
897
|
-
await sleep(500 * Math.pow(2, attempt));
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
const elapsedMs = Date.now() - start;
|
|
901
|
-
input.log?.error?.(
|
|
902
|
-
`[GAME-ACTION] post failed gameId=${input.gameId} eventId=${input.eventId} actionType=${input.actionType} status=${lastStatus ?? "n/a"} attempts=${attempts} elapsedMs=${elapsedMs} err=${lastError ?? "unknown"}`
|
|
903
|
-
);
|
|
904
|
-
return { success: false, status: lastStatus, error: lastError, elapsedMs, attempts };
|
|
905
|
-
}
|
|
906
|
-
|
|
907
857
|
// src/ws-client.ts
|
|
908
858
|
import WebSocket from "ws";
|
|
909
859
|
var CoolclawWsClient = class {
|
|
910
860
|
constructor(options) {
|
|
911
861
|
this.options = options;
|
|
912
862
|
}
|
|
863
|
+
options;
|
|
913
864
|
socket;
|
|
914
865
|
heartbeatTimer;
|
|
915
866
|
reconnectTimer;
|
|
@@ -1180,29 +1131,32 @@ function logAckFailure(params) {
|
|
|
1180
1131
|
const target = params.target ? ` target=${params.target}` : "";
|
|
1181
1132
|
params.log(`${params.channel} ack cleanup failed${target}: ${String(params.error)}`);
|
|
1182
1133
|
}
|
|
1183
|
-
async function submitGameActionWithLog(action, meta,
|
|
1184
|
-
if (!
|
|
1185
|
-
log?.error?.(`[GAME-ACTION] submit skipped:
|
|
1134
|
+
async function submitGameActionWithLog(action, meta, wsClient, log, source) {
|
|
1135
|
+
if (!wsClient.isConnected()) {
|
|
1136
|
+
log?.error?.(`[GAME-ACTION] submit skipped: ws not connected eventId=${meta.eventId}`);
|
|
1186
1137
|
return;
|
|
1187
1138
|
}
|
|
1188
1139
|
log?.info?.(
|
|
1189
1140
|
`[GAME-ACTION] submit start source=${source} eventType=${meta.eventType} actionType=${action.actionType} gameId=${meta.gameId} turnSeq=${meta.turnSeq} eventId=${meta.eventId}`
|
|
1190
1141
|
);
|
|
1191
|
-
const
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1142
|
+
const start = Date.now();
|
|
1143
|
+
try {
|
|
1144
|
+
await sendGameAction({
|
|
1145
|
+
client: wsClient,
|
|
1146
|
+
gameId: meta.gameId,
|
|
1147
|
+
actionType: action.actionType,
|
|
1148
|
+
actionData: action.actionData,
|
|
1149
|
+
turnSeq: meta.turnSeq,
|
|
1150
|
+
eventId: meta.eventId,
|
|
1151
|
+
traceId: meta.traceId
|
|
1152
|
+
});
|
|
1153
|
+
log?.info?.(
|
|
1154
|
+
`[GAME-ACTION] submit ok source=${source} gameId=${meta.gameId} eventId=${meta.eventId} elapsedMs=${Date.now() - start}`
|
|
1155
|
+
);
|
|
1156
|
+
} catch (err) {
|
|
1157
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1204
1158
|
log?.error?.(
|
|
1205
|
-
`[GAME-ACTION] submit failed source=${source} eventId=${meta.eventId}
|
|
1159
|
+
`[GAME-ACTION] submit failed source=${source} eventId=${meta.eventId} elapsedMs=${Date.now() - start} err=${errMsg}`
|
|
1206
1160
|
);
|
|
1207
1161
|
}
|
|
1208
1162
|
}
|
|
@@ -1399,6 +1353,21 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1399
1353
|
}
|
|
1400
1354
|
const agentHint = envelope.metadata?.agentHint;
|
|
1401
1355
|
const bodyForAgent = agentHint ? envelope.text + agentHint : envelope.text;
|
|
1356
|
+
if (typeof runtime.channel.reply?.finalizeInboundContext !== "function") {
|
|
1357
|
+
throw new Error(
|
|
1358
|
+
"CoolClaw requires runtime.channel.reply.finalizeInboundContext. Please upgrade OpenClaw to >=2026.3.22."
|
|
1359
|
+
);
|
|
1360
|
+
}
|
|
1361
|
+
if (typeof runtime.channel.reply?.dispatchReplyWithBufferedBlockDispatcher !== "function") {
|
|
1362
|
+
throw new Error(
|
|
1363
|
+
"CoolClaw requires runtime.channel.reply.dispatchReplyWithBufferedBlockDispatcher. Please upgrade OpenClaw to >=2026.3.22."
|
|
1364
|
+
);
|
|
1365
|
+
}
|
|
1366
|
+
if (typeof runtime.channel.session?.recordInboundSession !== "function") {
|
|
1367
|
+
throw new Error(
|
|
1368
|
+
"CoolClaw requires runtime.channel.session.recordInboundSession. Please upgrade OpenClaw to >=2026.3.22."
|
|
1369
|
+
);
|
|
1370
|
+
}
|
|
1402
1371
|
const ctxPayload = runtime.channel.reply.finalizeInboundContext({
|
|
1403
1372
|
Body: envelope.text,
|
|
1404
1373
|
BodyForAgent: bodyForAgent,
|
|
@@ -1444,8 +1413,7 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1444
1413
|
await submitGameActionWithLog(
|
|
1445
1414
|
parsed,
|
|
1446
1415
|
gameMeta,
|
|
1447
|
-
|
|
1448
|
-
token,
|
|
1416
|
+
wsClient,
|
|
1449
1417
|
ctx.log,
|
|
1450
1418
|
"llm"
|
|
1451
1419
|
);
|
|
@@ -1489,8 +1457,7 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1489
1457
|
await submitGameActionWithLog(
|
|
1490
1458
|
fb,
|
|
1491
1459
|
gameMeta,
|
|
1492
|
-
|
|
1493
|
-
token,
|
|
1460
|
+
wsClient,
|
|
1494
1461
|
ctx.log,
|
|
1495
1462
|
"fallback"
|
|
1496
1463
|
);
|
package/dist/cli-metadata.js
CHANGED
package/dist/index.js
CHANGED
package/dist/setup-entry.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coolclaw/coolclaw",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "OpenClaw native channel plugin for Riddle/CoolClaw chat.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"vitest": "latest"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
|
-
"openclaw": ">=2026.
|
|
44
|
+
"openclaw": ">=2026.3.22 <2027"
|
|
45
45
|
},
|
|
46
46
|
"peerDependenciesMeta": {
|
|
47
47
|
"openclaw": {
|
|
@@ -59,9 +59,9 @@
|
|
|
59
59
|
"runtimeSetupEntry": "./dist/setup-entry.js",
|
|
60
60
|
"install": {
|
|
61
61
|
"npmSpec": "@coolclaw/coolclaw",
|
|
62
|
-
"expectedIntegrity": "sha512-
|
|
62
|
+
"expectedIntegrity": "sha512-flpAooDuQuAIeKDQ0oPAS7bzy+yN8ql1tKxE7HfvMgSgJiIiwrFXWQ3+Uqnc6q0Ls0MAncnCSwFC5TzxNX+CVw==",
|
|
63
63
|
"defaultChoice": "npm",
|
|
64
|
-
"minHostVersion": ">=2026.
|
|
64
|
+
"minHostVersion": ">=2026.3.22"
|
|
65
65
|
},
|
|
66
66
|
"channel": {
|
|
67
67
|
"id": "coolclaw",
|
|
@@ -76,13 +76,15 @@
|
|
|
76
76
|
}
|
|
77
77
|
},
|
|
78
78
|
"compat": {
|
|
79
|
-
"pluginApi": ">=2026.
|
|
80
|
-
"minGatewayVersion": "2026.
|
|
81
|
-
"sdkImports": [
|
|
79
|
+
"pluginApi": ">=2026.3.22",
|
|
80
|
+
"minGatewayVersion": "2026.3.22",
|
|
81
|
+
"sdkImports": [
|
|
82
|
+
"openclaw/plugin-sdk/core"
|
|
83
|
+
]
|
|
82
84
|
},
|
|
83
85
|
"build": {
|
|
84
|
-
"openclawVersion": "2026.5.
|
|
86
|
+
"openclawVersion": "2026.5.7",
|
|
85
87
|
"pluginSdkVersion": "2026.4.29"
|
|
86
88
|
}
|
|
87
89
|
}
|
|
88
|
-
}
|
|
90
|
+
}
|