@coolclaw/coolclaw 0.4.0 → 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-IPHJPPD4.js";
4
+ } from "./chunk-XVB6UKBR.js";
5
5
  import {
6
6
  runCoolclawSetup
7
7
  } from "./chunk-A54AF634.js";
@@ -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
- return `\u5EA7\u4F4D${seat ?? "?"} ${name}\uFF08${voice || "\u672A\u6807\u6CE8"}\uFF0C${alive}\uFF09`;
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 teammateProposalStr = tp.targetSeat != null ? `\u540C\u4F34\uFF08\u5EA7\u4F4D ${tp.seat}\uFF09\u672C\u8F6E\u5DF2\u63D0\u8BAE\u51FB\u6740\u5EA7\u4F4D ${tp.targetSeat}${tp.reason ? `\uFF0C\u7406\u7531\uFF1A${tp.reason}` : ""}${tp.speech ? `\uFF0C\u53D1\u8A00\uFF1A"${tp.speech}"` : ""}\u3002` : isFirst ? "\u4F60\u662F\u672C\u8F6E\u9996\u4F4D\u53D1\u8A00\u7684\u72FC\u4EBA\u3002" : "\u540C\u4F34\u5C1A\u672A\u53D1\u8A00\u3002";
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} ${teammateName}\u3002` : "\u4F60\u662F\u72EC\u72FC\u3002",
261
- teammateProposalStr,
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,89 +854,6 @@ 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 {
@@ -1181,29 +1131,32 @@ function logAckFailure(params) {
1181
1131
  const target = params.target ? ` target=${params.target}` : "";
1182
1132
  params.log(`${params.channel} ack cleanup failed${target}: ${String(params.error)}`);
1183
1133
  }
1184
- async function submitGameActionWithLog(action, meta, account, token, log, source) {
1185
- if (!account.gatewayUrl || !account.agentId) {
1186
- log?.error?.(`[GAME-ACTION] submit skipped: missing gatewayUrl/agentId eventId=${meta.eventId}`);
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}`);
1187
1137
  return;
1188
1138
  }
1189
1139
  log?.info?.(
1190
1140
  `[GAME-ACTION] submit start source=${source} eventType=${meta.eventType} actionType=${action.actionType} gameId=${meta.gameId} turnSeq=${meta.turnSeq} eventId=${meta.eventId}`
1191
1141
  );
1192
- const result = await submitGameAction({
1193
- gatewayUrl: account.gatewayUrl,
1194
- token,
1195
- agentId: String(account.agentId),
1196
- gameId: meta.gameId,
1197
- actionType: action.actionType,
1198
- actionData: action.actionData,
1199
- turnSeq: meta.turnSeq,
1200
- eventId: meta.eventId,
1201
- traceId: meta.traceId,
1202
- log
1203
- });
1204
- if (!result.success) {
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);
1205
1158
  log?.error?.(
1206
- `[GAME-ACTION] submit failed source=${source} eventId=${meta.eventId} attempts=${result.attempts} status=${result.status ?? "n/a"} err=${result.error ?? "unknown"}`
1159
+ `[GAME-ACTION] submit failed source=${source} eventId=${meta.eventId} elapsedMs=${Date.now() - start} err=${errMsg}`
1207
1160
  );
1208
1161
  }
1209
1162
  }
@@ -1460,8 +1413,7 @@ var coolclawChannelPlugin = createChatChannelPlugin({
1460
1413
  await submitGameActionWithLog(
1461
1414
  parsed,
1462
1415
  gameMeta,
1463
- account,
1464
- token,
1416
+ wsClient,
1465
1417
  ctx.log,
1466
1418
  "llm"
1467
1419
  );
@@ -1505,8 +1457,7 @@ var coolclawChannelPlugin = createChatChannelPlugin({
1505
1457
  await submitGameActionWithLog(
1506
1458
  fb,
1507
1459
  gameMeta,
1508
- account,
1509
- token,
1460
+ wsClient,
1510
1461
  ctx.log,
1511
1462
  "fallback"
1512
1463
  );
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  index_default
3
- } from "./chunk-NY6UBBGB.js";
4
- import "./chunk-IPHJPPD4.js";
3
+ } from "./chunk-QZL3FKVL.js";
4
+ import "./chunk-XVB6UKBR.js";
5
5
  import "./chunk-A54AF634.js";
6
6
  import "./chunk-Q3NF4NWE.js";
7
7
 
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  index_default
3
- } from "./chunk-NY6UBBGB.js";
4
- import "./chunk-IPHJPPD4.js";
3
+ } from "./chunk-QZL3FKVL.js";
4
+ import "./chunk-XVB6UKBR.js";
5
5
  import "./chunk-A54AF634.js";
6
6
  import "./chunk-Q3NF4NWE.js";
7
7
  export {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  coolclawChannelPlugin
3
- } from "./chunk-IPHJPPD4.js";
3
+ } from "./chunk-XVB6UKBR.js";
4
4
  import "./chunk-Q3NF4NWE.js";
5
5
 
6
6
  // setup-entry.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coolclaw/coolclaw",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "OpenClaw native channel plugin for Riddle/CoolClaw chat.",
5
5
  "type": "module",
6
6
  "files": [
@@ -59,7 +59,7 @@
59
59
  "runtimeSetupEntry": "./dist/setup-entry.js",
60
60
  "install": {
61
61
  "npmSpec": "@coolclaw/coolclaw",
62
- "expectedIntegrity": "sha512-jZn9gMqpzKzbqQBz7GMFkTUdqUBtV1SZRYnYKP9eV/75xbx1RkoCADvhLF5LviH3JWsYd3jzZEV/6fy1H5FNTw==",
62
+ "expectedIntegrity": "sha512-flpAooDuQuAIeKDQ0oPAS7bzy+yN8ql1tKxE7HfvMgSgJiIiwrFXWQ3+Uqnc6q0Ls0MAncnCSwFC5TzxNX+CVw==",
63
63
  "defaultChoice": "npm",
64
64
  "minHostVersion": ">=2026.3.22"
65
65
  },