@53ai/53ai-openclaw 1.0.6 → 1.0.8
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/CHANGELOG.md +6 -0
- package/README.md +10 -10
- package/dist/index.cjs.js +91 -12
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +91 -12
- package/dist/index.esm.js.map +1 -1
- package/dist/src/compaction-hooks.d.ts +25 -0
- package/dist/src/message-sender.d.ts +1 -0
- package/dist/src/utils.d.ts +2 -2
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
6
6
|
|
|
7
|
+
## [1.0.7] - 2026-03-19
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
- 规范配置项命名:将 `websocketUrl` 统一更名为 `WSUrl`。
|
|
11
|
+
- 文档更新:优化了 README.md 中的参数说明。
|
|
12
|
+
|
|
7
13
|
## [1.0.6] - 2026-03-19
|
|
8
14
|
|
|
9
15
|
### Changed
|
package/README.md
CHANGED
|
@@ -83,9 +83,9 @@ openclaw gateway restart
|
|
|
83
83
|
sudo systemctl restart openclaw-gateway
|
|
84
84
|
|
|
85
85
|
# 配置必要参数
|
|
86
|
-
openclaw config set channels.53aihub.botId "
|
|
87
|
-
openclaw config set channels.53aihub.secret "
|
|
88
|
-
openclaw config set channels.53aihub.
|
|
86
|
+
openclaw config set channels.53aihub.botId "智能体的botId"
|
|
87
|
+
openclaw config set channels.53aihub.secret "智能体对应的secret"
|
|
88
|
+
openclaw config set channels.53aihub.WSUrl "ws:/你的域名/api/v1/openclaw/ws/connect"
|
|
89
89
|
|
|
90
90
|
# 启用通道
|
|
91
91
|
openclaw config set channels.53aihub.enabled true
|
|
@@ -95,9 +95,9 @@ openclaw config set channels.53aihub.enabled true
|
|
|
95
95
|
|
|
96
96
|
| 参数 | 必填 | 默认值 | 说明 |
|
|
97
97
|
|------|------|--------|------|
|
|
98
|
-
| `botId` | 是 | - |
|
|
99
|
-
| `secret` | 是 | - |
|
|
100
|
-
| `
|
|
98
|
+
| `botId` | 是 | - | 智能体的ID |
|
|
99
|
+
| `secret` | 是 | - | 智能体的Secret |
|
|
100
|
+
| `WSUrl` | 是 | - | 53AIHub平台的WS地址 |
|
|
101
101
|
| `token` | 否 | - | secret 的别名,与 secret 二选一 |
|
|
102
102
|
| `enabled` | 否 | false | 是否启用通道 |
|
|
103
103
|
| `accessPolicy` | 否 | `open` | 访问策略: `open`=开放所有用户, `allowlist`=仅白名单用户, `pairing`=首次使用需审批, `disabled`=禁用 |
|
|
@@ -142,8 +142,8 @@ openclaw plugins list | grep 53aihub
|
|
|
142
142
|
# 检查通道配置
|
|
143
143
|
openclaw config get channels.53aihub
|
|
144
144
|
|
|
145
|
-
# 检查 Gateway 日志
|
|
146
|
-
openclaw
|
|
145
|
+
# 检查 Gateway 日志 (用户级服务)
|
|
146
|
+
journalctl --user -u openclaw-gateway.service -f | grep -i 53aihub
|
|
147
147
|
```
|
|
148
148
|
|
|
149
149
|
## 多模态消息支持
|
|
@@ -397,7 +397,7 @@ npm test
|
|
|
397
397
|
|
|
398
398
|
1. 确认 `openclaw.plugin.json` 存在于插件根目录
|
|
399
399
|
2. 确认 `dist/` 目录包含编译后的文件
|
|
400
|
-
3. 检查 Gateway 日志: `openclaw
|
|
400
|
+
3. 检查 Gateway 日志: `journalctl --user -u openclaw-gateway.service -f`
|
|
401
401
|
|
|
402
402
|
### 通道未生效
|
|
403
403
|
|
|
@@ -407,7 +407,7 @@ npm test
|
|
|
407
407
|
|
|
408
408
|
### WebSocket 连接失败
|
|
409
409
|
|
|
410
|
-
1. 检查 `
|
|
410
|
+
1. 检查 `WSUrl` 格式是否正确
|
|
411
411
|
2. 确认网络可达性
|
|
412
412
|
3. 检查 `botId` 和 `secret` 是否正确
|
|
413
413
|
|
package/dist/index.cjs.js
CHANGED
|
@@ -334,7 +334,7 @@ function parseMessageContent(msg) {
|
|
|
334
334
|
}
|
|
335
335
|
|
|
336
336
|
async function sendReply(params) {
|
|
337
|
-
const { wsClient, text, toChatId, replyToMsgId, runtime, finish, streamId, isError, errorCode, errorDetails } = params;
|
|
337
|
+
const { wsClient, text, toChatId, replyToMsgId, runtime, finish, streamId, isError, errorCode, errorDetails, isThinking } = params;
|
|
338
338
|
const reqId = replyToMsgId || streamId;
|
|
339
339
|
runtime.log?.(`[53aihub] sendReply START: reqId=${reqId}, finish=${finish}, isError=${isError}, textLen=${text?.length || 0}, wsReadyState=${wsClient.readyState}`);
|
|
340
340
|
if (wsClient.readyState !== 1) {
|
|
@@ -395,7 +395,7 @@ async function sendReply(params) {
|
|
|
395
395
|
const payload = {
|
|
396
396
|
req_id: reqId,
|
|
397
397
|
action: "chat",
|
|
398
|
-
status: finish ? "done" : "streaming",
|
|
398
|
+
status: finish ? "done" : isThinking ? "thinking" : "streaming",
|
|
399
399
|
data: chunk,
|
|
400
400
|
};
|
|
401
401
|
const jsonStr = JSON.stringify(payload);
|
|
@@ -973,7 +973,6 @@ async function processMessage(params) {
|
|
|
973
973
|
cfg: config,
|
|
974
974
|
dispatcherOptions: {
|
|
975
975
|
deliver: async (payload, info) => {
|
|
976
|
-
state.accumulatedText += payload.text;
|
|
977
976
|
runtime.log?.(`[53aihub] deliver: kind=${info.kind}, textLen=${payload.text?.length || 0}, accumulatedLen=${state.accumulatedText.length}, isError=${payload.isError}`);
|
|
978
977
|
if (payload.isError) {
|
|
979
978
|
const errorMsg = payload.text || "Unknown error";
|
|
@@ -993,6 +992,23 @@ async function processMessage(params) {
|
|
|
993
992
|
});
|
|
994
993
|
return;
|
|
995
994
|
}
|
|
995
|
+
const isCompaction = payload.text?.startsWith("🧹 Compacting context") ||
|
|
996
|
+
state.accumulatedText.startsWith("🧹 Compacting context");
|
|
997
|
+
if (isCompaction && info.kind !== "final") {
|
|
998
|
+
runtime.log?.(`[53aihub] deliver COMPACTION: text preview=${payload.text?.substring(0, 50)}...`);
|
|
999
|
+
await sendReply({
|
|
1000
|
+
wsClient,
|
|
1001
|
+
text: payload.text || "",
|
|
1002
|
+
toChatId: chatId,
|
|
1003
|
+
replyToMsgId: body.msgId,
|
|
1004
|
+
runtime,
|
|
1005
|
+
finish: false,
|
|
1006
|
+
streamId: state.streamId,
|
|
1007
|
+
isThinking: true,
|
|
1008
|
+
});
|
|
1009
|
+
return;
|
|
1010
|
+
}
|
|
1011
|
+
state.accumulatedText += payload.text;
|
|
996
1012
|
if (info.kind !== "final") {
|
|
997
1013
|
runtime.log?.(`[53aihub] deliver STREAMING: accumulatedText preview=${state.accumulatedText.substring(0, 50)}...`);
|
|
998
1014
|
await sendReply({
|
|
@@ -1108,7 +1124,7 @@ async function monitorProvider(options) {
|
|
|
1108
1124
|
if (isAborted)
|
|
1109
1125
|
return;
|
|
1110
1126
|
// 安全: 不在 URL 中传递敏感信息,仅通过 headers 传递认证
|
|
1111
|
-
const wsUrl = account.
|
|
1127
|
+
const wsUrl = account.WSUrl;
|
|
1112
1128
|
const botId = account.botId || account.config.botId;
|
|
1113
1129
|
const secret = account.secret || account.token || account.config.secret || account.config.token;
|
|
1114
1130
|
// 日志输出时隐藏敏感信息
|
|
@@ -1207,7 +1223,7 @@ function resolveAccount(cfg, accountId = pluginSdk.DEFAULT_ACCOUNT_ID) {
|
|
|
1207
1223
|
accountId,
|
|
1208
1224
|
name: config.name ?? "53AIHub",
|
|
1209
1225
|
enabled: config.enabled !== false,
|
|
1210
|
-
|
|
1226
|
+
WSUrl: config.WSUrl || DEFAULT_WS_URL,
|
|
1211
1227
|
botId: config.botId ?? config.userId ?? "",
|
|
1212
1228
|
secret: config.secret ?? config.token ?? "",
|
|
1213
1229
|
token: config.token ?? config.secret ?? "",
|
|
@@ -1228,8 +1244,8 @@ function setAccount(cfg, account) {
|
|
|
1228
1244
|
allowFrom: account.allowFrom ?? existing.allowFrom,
|
|
1229
1245
|
accessPolicy: account.accessPolicy ?? existing.accessPolicy,
|
|
1230
1246
|
sendThinkingMessage: account.sendThinkingMessage ?? existing.sendThinkingMessage,
|
|
1231
|
-
...(account.
|
|
1232
|
-
? {
|
|
1247
|
+
...(account.WSUrl || existing.WSUrl
|
|
1248
|
+
? { WSUrl: account.WSUrl ?? existing.WSUrl }
|
|
1233
1249
|
: {}),
|
|
1234
1250
|
...(account.name || existing.name
|
|
1235
1251
|
? { name: account.name ?? existing.name }
|
|
@@ -1267,10 +1283,10 @@ async function promptSecret(prompter, account) {
|
|
|
1267
1283
|
validate: (value) => (value?.trim() ? undefined : "必填"),
|
|
1268
1284
|
})).trim();
|
|
1269
1285
|
}
|
|
1270
|
-
async function
|
|
1286
|
+
async function promptWSUrl(prompter, account) {
|
|
1271
1287
|
return String(await prompter.text({
|
|
1272
1288
|
message: "WebSocket URL (例如: ws://localhost:8080/ws)",
|
|
1273
|
-
initialValue: account?.
|
|
1289
|
+
initialValue: account?.WSUrl ?? "",
|
|
1274
1290
|
validate: (value) => {
|
|
1275
1291
|
const trimmed = value?.trim();
|
|
1276
1292
|
if (!trimmed)
|
|
@@ -1340,11 +1356,11 @@ const aiHubOnboardingAdapter = {
|
|
|
1340
1356
|
}
|
|
1341
1357
|
const botId = await promptBotId(prompter, account);
|
|
1342
1358
|
const secret = await promptSecret(prompter, account);
|
|
1343
|
-
const
|
|
1359
|
+
const WSUrl = await promptWSUrl(prompter, account);
|
|
1344
1360
|
const cfgWithAccount = setAccount(cfg, {
|
|
1345
1361
|
botId,
|
|
1346
1362
|
secret,
|
|
1347
|
-
|
|
1363
|
+
WSUrl: WSUrl || undefined,
|
|
1348
1364
|
enabled: true,
|
|
1349
1365
|
accessPolicy: account.config.accessPolicy ?? "open",
|
|
1350
1366
|
allowFrom: account.config.allowFrom ?? [],
|
|
@@ -1423,7 +1439,7 @@ const aiHubPlugin = {
|
|
|
1423
1439
|
enabled: account.enabled,
|
|
1424
1440
|
configured: Boolean(account.botId?.trim() || account.token?.trim()),
|
|
1425
1441
|
botId: account.botId,
|
|
1426
|
-
|
|
1442
|
+
WSUrl: account.WSUrl,
|
|
1427
1443
|
accessPolicy: account.config.accessPolicy ?? "open",
|
|
1428
1444
|
}),
|
|
1429
1445
|
resolveAllowFrom: ({ cfg }) => {
|
|
@@ -1592,6 +1608,67 @@ const aiHubPlugin = {
|
|
|
1592
1608
|
},
|
|
1593
1609
|
};
|
|
1594
1610
|
|
|
1611
|
+
const compactionSessions = new Map();
|
|
1612
|
+
/**
|
|
1613
|
+
* 从 sessionKey 中解析 chatId
|
|
1614
|
+
* sessionKey 格式: agent:{agentId}:{channel}:direct:{chatId}
|
|
1615
|
+
* 例如: agent:main:53aihub:direct:user123 -> user123
|
|
1616
|
+
*/
|
|
1617
|
+
function parseChatIdFromSessionKey(sessionKey) {
|
|
1618
|
+
if (!sessionKey)
|
|
1619
|
+
return null;
|
|
1620
|
+
const parts = sessionKey.split(":");
|
|
1621
|
+
// 格式: agent:{agentId}:{channel}:direct:{chatId}
|
|
1622
|
+
// parts: [0]agent, [1]agentId, [2]channel, [3]direct, [4]chatId
|
|
1623
|
+
if (parts.length >= 5 && parts[2] === CHANNEL_ID && parts[3] === "direct") {
|
|
1624
|
+
return parts[4];
|
|
1625
|
+
}
|
|
1626
|
+
return null;
|
|
1627
|
+
}
|
|
1628
|
+
async function handleBeforeCompaction(event, ctx) {
|
|
1629
|
+
const runtime = getRuntime();
|
|
1630
|
+
const logger = runtime.logging.getChildLogger({ component: "53aihub-compaction" });
|
|
1631
|
+
const log = (msg) => logger.info(msg);
|
|
1632
|
+
log(`before_compaction: sessionKey=${ctx.sessionKey}, channelId=${ctx.channelId}, messageCount=${event.messageCount}`);
|
|
1633
|
+
const sessionKey = ctx.sessionKey || "";
|
|
1634
|
+
if (!sessionKey.startsWith(`${CHANNEL_ID}:`) && !sessionKey.includes(`:${CHANNEL_ID}:`)) {
|
|
1635
|
+
log(`Skipping compaction hook for non-53aihub session: ${sessionKey}`);
|
|
1636
|
+
return;
|
|
1637
|
+
}
|
|
1638
|
+
const chatId = parseChatIdFromSessionKey(sessionKey);
|
|
1639
|
+
if (!chatId) {
|
|
1640
|
+
logger.error(`Cannot parse chatId from sessionKey: ${sessionKey}`);
|
|
1641
|
+
return;
|
|
1642
|
+
}
|
|
1643
|
+
compactionSessions.set(sessionKey, {
|
|
1644
|
+
startTime: Date.now(),
|
|
1645
|
+
messageCount: event.messageCount,
|
|
1646
|
+
});
|
|
1647
|
+
log(`Compaction started for chatId=${chatId}`);
|
|
1648
|
+
}
|
|
1649
|
+
async function handleAfterCompaction(event, ctx) {
|
|
1650
|
+
const runtime = getRuntime();
|
|
1651
|
+
const logger = runtime.logging.getChildLogger({ component: "53aihub-compaction" });
|
|
1652
|
+
const log = (msg) => logger.info(msg);
|
|
1653
|
+
log(`after_compaction: sessionKey=${ctx.sessionKey}, channelId=${ctx.channelId}, messageCount=${event.messageCount}, compactedCount=${event.compactedCount}`);
|
|
1654
|
+
const sessionKey = ctx.sessionKey || "";
|
|
1655
|
+
if (!sessionKey.startsWith(`${CHANNEL_ID}:`) && !sessionKey.includes(`:${CHANNEL_ID}:`)) {
|
|
1656
|
+
log(`Skipping compaction hook for non-53aihub session: ${sessionKey}`);
|
|
1657
|
+
return;
|
|
1658
|
+
}
|
|
1659
|
+
const chatId = parseChatIdFromSessionKey(sessionKey);
|
|
1660
|
+
if (!chatId) {
|
|
1661
|
+
logger.error(`Cannot parse chatId from sessionKey: ${sessionKey}`);
|
|
1662
|
+
return;
|
|
1663
|
+
}
|
|
1664
|
+
const sessionInfo = compactionSessions.get(sessionKey);
|
|
1665
|
+
compactionSessions.delete(sessionKey);
|
|
1666
|
+
if (sessionInfo) {
|
|
1667
|
+
const duration = Date.now() - sessionInfo.startTime;
|
|
1668
|
+
log(`Compaction completed: chatId=${chatId}, duration=${duration}ms, messagesBefore=${sessionInfo.messageCount}, messagesAfter=${event.messageCount}, compacted=${event.compactedCount}`);
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1595
1672
|
const plugin = {
|
|
1596
1673
|
id: "53ai-openclaw",
|
|
1597
1674
|
name: "53AI OpenClaw",
|
|
@@ -1600,6 +1677,8 @@ const plugin = {
|
|
|
1600
1677
|
register(api) {
|
|
1601
1678
|
setRuntime(api.runtime);
|
|
1602
1679
|
api.registerChannel({ plugin: aiHubPlugin });
|
|
1680
|
+
api.on("before_compaction", handleBeforeCompaction);
|
|
1681
|
+
api.on("after_compaction", handleAfterCompaction);
|
|
1603
1682
|
},
|
|
1604
1683
|
};
|
|
1605
1684
|
|