@claw-camp/openclaw-plugin 2.0.1 → 2.0.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/README.md +36 -31
- package/index.ts +122 -32
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,48 +2,52 @@
|
|
|
2
2
|
|
|
3
3
|
实时监控你的 OpenClaw Agent 状态、Token 消耗、Gateway 健康度。
|
|
4
4
|
|
|
5
|
-
Dashboard:[camp.aigc.sx.cn](https://camp.aigc.sx.cn)
|
|
5
|
+
**Dashboard:[camp.aigc.sx.cn](https://camp.aigc.sx.cn)**
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## 快速开始
|
|
10
|
+
|
|
11
|
+
### 第一步:注册账号,创建 Bot
|
|
12
|
+
|
|
13
|
+
1. 访问 [camp.aigc.sx.cn](https://camp.aigc.sx.cn) 注册账号
|
|
14
|
+
2. 登录后进入 **Bot 管理** → 创建一个 Bot
|
|
15
|
+
3. 记录下 **Bot ID** 和 **Bot Token**
|
|
16
|
+
|
|
17
|
+
### 第二步:安装插件
|
|
10
18
|
|
|
11
19
|
```bash
|
|
12
20
|
openclaw plugins install @claw-camp/openclaw-plugin
|
|
13
21
|
```
|
|
14
22
|
|
|
15
|
-
|
|
23
|
+
### 第三步:配置 Bot 信息
|
|
24
|
+
|
|
25
|
+
**方式一:交互向导(推荐)**
|
|
16
26
|
|
|
17
27
|
```bash
|
|
18
|
-
openclaw
|
|
28
|
+
openclaw onboard
|
|
19
29
|
```
|
|
20
30
|
|
|
21
|
-
|
|
31
|
+
在渠道选择步骤中选择 `claw-camp`,按提示填入 Bot ID 和 Bot Token 即可。
|
|
22
32
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"channels": {
|
|
30
|
-
"claw-camp": {
|
|
31
|
-
"enabled": true,
|
|
32
|
-
"accounts": {
|
|
33
|
-
"default": {
|
|
34
|
-
"botId": "your-bot-id",
|
|
35
|
-
"botToken": "your-bot-token"
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
},
|
|
40
|
-
"plugins": {
|
|
41
|
-
"allow": ["claw-camp"]
|
|
42
|
-
}
|
|
43
|
-
}
|
|
33
|
+
**方式二:命令行手动配置**
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
openclaw config set channels.claw-camp.accounts.default.botId "your-bot-id"
|
|
37
|
+
openclaw config set channels.claw-camp.accounts.default.botToken "your-bot-token"
|
|
38
|
+
openclaw config set plugins.allow '["claw-camp"]'
|
|
44
39
|
```
|
|
45
40
|
|
|
46
|
-
|
|
41
|
+
### 第四步:启动 Gateway
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
openclaw gateway restart
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
首次启动用:
|
|
48
|
+
```bash
|
|
49
|
+
openclaw gateway
|
|
50
|
+
```
|
|
47
51
|
|
|
48
52
|
---
|
|
49
53
|
|
|
@@ -53,14 +57,14 @@ openclaw gateway restart
|
|
|
53
57
|
openclaw plugins list | grep claw-camp
|
|
54
58
|
```
|
|
55
59
|
|
|
56
|
-
看到 `loaded`
|
|
60
|
+
看到 `loaded` 即成功。访问 [camp.aigc.sx.cn](https://camp.aigc.sx.cn),你的 Agent 会出现在列表中并显示 `online`。
|
|
57
61
|
|
|
58
62
|
---
|
|
59
63
|
|
|
60
64
|
## 监控内容
|
|
61
65
|
|
|
62
|
-
- Gateway
|
|
63
|
-
-
|
|
66
|
+
- Gateway 运行状态(running / stopped)
|
|
67
|
+
- 活跃会话数 & Token 消耗
|
|
64
68
|
- 系统资源(CPU / 内存)
|
|
65
69
|
- 已加载插件列表
|
|
66
70
|
|
|
@@ -68,7 +72,8 @@ openclaw plugins list | grep claw-camp
|
|
|
68
72
|
|
|
69
73
|
## 相关项目
|
|
70
74
|
|
|
71
|
-
- [claw-camp/claw-hub](https://github.com/claw-camp/claw-hub) — Hub
|
|
75
|
+
- [claw-camp/claw-hub](https://github.com/claw-camp/claw-hub) — Hub 服务端(想自建的看这里)
|
|
76
|
+
- [npmjs.com/@claw-camp/openclaw-plugin](https://www.npmjs.com/package/@claw-camp/openclaw-plugin) — npm 包
|
|
72
77
|
|
|
73
78
|
## License
|
|
74
79
|
|
package/index.ts
CHANGED
|
@@ -33,6 +33,11 @@ class ClawCampChannel {
|
|
|
33
33
|
private reconnectTimeout: NodeJS.Timeout | null = null;
|
|
34
34
|
private isShuttingDown = false;
|
|
35
35
|
private reconnectAttempts = 0;
|
|
36
|
+
private channelRuntime: any = null;
|
|
37
|
+
|
|
38
|
+
setChannelRuntime(rt: any) {
|
|
39
|
+
this.channelRuntime = rt;
|
|
40
|
+
}
|
|
36
41
|
|
|
37
42
|
constructor(api: OpenClawPluginApi, config: any) {
|
|
38
43
|
this.api = api;
|
|
@@ -398,6 +403,12 @@ class ClawCampChannel {
|
|
|
398
403
|
this.api.logger.error('[Claw Camp Channel] 任务执行失败:', String(e))
|
|
399
404
|
);
|
|
400
405
|
break;
|
|
406
|
+
case 'chat-message':
|
|
407
|
+
this.api.logger.info('[Claw Camp Channel] 收到聊天消息:', payload?.conversationId);
|
|
408
|
+
this.handleChatMessage(payload).catch((e) =>
|
|
409
|
+
this.api.logger.error('[Claw Camp Channel] 处理聊天消息失败:', String(e))
|
|
410
|
+
);
|
|
411
|
+
break;
|
|
401
412
|
case 'registered':
|
|
402
413
|
this.api.logger.info('[Claw Camp Channel] 注册成功:', payload?.id);
|
|
403
414
|
break;
|
|
@@ -407,6 +418,80 @@ class ClawCampChannel {
|
|
|
407
418
|
}
|
|
408
419
|
}
|
|
409
420
|
|
|
421
|
+
async handleChatMessage(payload: any) {
|
|
422
|
+
const { conversationId, userId, username, content, sessionKey: rawSessionKey, msgId } = payload;
|
|
423
|
+
const botId = this.config.agentId;
|
|
424
|
+
|
|
425
|
+
if (!this.channelRuntime) {
|
|
426
|
+
this.api.logger.warn('[Claw Camp Channel] channelRuntime 未初始化,无法处理聊天消息');
|
|
427
|
+
// 降级:直接返回提示
|
|
428
|
+
this._sendRaw({
|
|
429
|
+
type: 'chat-reply',
|
|
430
|
+
payload: {
|
|
431
|
+
msgId,
|
|
432
|
+
conversationId,
|
|
433
|
+
sessionKey: rawSessionKey || `${botId}:direct:${userId}`,
|
|
434
|
+
reply: '抱歉,Agent 尚未就绪,请稍后再试。'
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const sessionKey = rawSessionKey || `claw-camp:direct:${userId}`;
|
|
441
|
+
const accountId = this.config.accountId || 'default';
|
|
442
|
+
|
|
443
|
+
const ctx = {
|
|
444
|
+
Body: content,
|
|
445
|
+
BodyForAgent: content,
|
|
446
|
+
From: userId,
|
|
447
|
+
To: botId,
|
|
448
|
+
SessionKey: sessionKey,
|
|
449
|
+
AccountId: accountId,
|
|
450
|
+
Channel: 'claw-camp',
|
|
451
|
+
// 可选:附加发言者名称作为上下文
|
|
452
|
+
...(username ? { SenderName: username } : {}),
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
const cfg = this.api.config;
|
|
456
|
+
|
|
457
|
+
try {
|
|
458
|
+
await this.channelRuntime.reply.dispatchReplyWithBufferedBlockDispatcher({
|
|
459
|
+
ctx,
|
|
460
|
+
cfg,
|
|
461
|
+
dispatcherOptions: {
|
|
462
|
+
deliver: async (replyPayload: any) => {
|
|
463
|
+
// replyPayload 是文本块或 block 对象,统一取文本
|
|
464
|
+
const replyText = typeof replyPayload === 'string'
|
|
465
|
+
? replyPayload
|
|
466
|
+
: replyPayload?.text ?? replyPayload?.content ?? JSON.stringify(replyPayload);
|
|
467
|
+
|
|
468
|
+
this._sendRaw({
|
|
469
|
+
type: 'chat-reply',
|
|
470
|
+
payload: {
|
|
471
|
+
msgId,
|
|
472
|
+
conversationId,
|
|
473
|
+
sessionKey,
|
|
474
|
+
reply: replyText
|
|
475
|
+
}
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
} catch (e) {
|
|
481
|
+
this.api.logger.error('[Claw Camp Channel] dispatchReply 失败:', String(e));
|
|
482
|
+
// 降级回复
|
|
483
|
+
this._sendRaw({
|
|
484
|
+
type: 'chat-reply',
|
|
485
|
+
payload: {
|
|
486
|
+
msgId,
|
|
487
|
+
conversationId,
|
|
488
|
+
sessionKey,
|
|
489
|
+
reply: `处理失败: ${e instanceof Error ? e.message : String(e)}`
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
410
495
|
async executeTask(task: any) {
|
|
411
496
|
const { action } = task;
|
|
412
497
|
let result: any;
|
|
@@ -547,34 +632,8 @@ export default function (api: OpenClawPluginApi) {
|
|
|
547
632
|
|
|
548
633
|
const channels = new Map<string, ClawCampChannel>();
|
|
549
634
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
const accountKeys = Object.keys(accounts);
|
|
553
|
-
|
|
554
|
-
if (accountKeys.length === 0) {
|
|
555
|
-
logger.warn('[Claw Camp] 未配置 accounts,跳过连接');
|
|
556
|
-
return;
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
for (const accountKey of accountKeys) {
|
|
560
|
-
const accountConfig = accounts[accountKey];
|
|
561
|
-
const { botId, botToken } = accountConfig;
|
|
562
|
-
if (botId && botToken) {
|
|
563
|
-
const agentName = accountConfig.agentName || accountKey;
|
|
564
|
-
logger.info(`[Claw Camp] 启动连接: botId=${botId}, name=${agentName}`);
|
|
565
|
-
const channel = new ClawCampChannel(api, {
|
|
566
|
-
hubUrl: 'wss://camp.aigc.sx.cn/ws',
|
|
567
|
-
token: botToken,
|
|
568
|
-
agentId: botId,
|
|
569
|
-
agentName
|
|
570
|
-
});
|
|
571
|
-
channels.set(accountKey, channel);
|
|
572
|
-
channel.connect();
|
|
573
|
-
} else {
|
|
574
|
-
logger.warn(`[Claw Camp] account ${accountKey} 缺少 botId/botToken`);
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
};
|
|
635
|
+
// 注意:channelRuntime 通过 gateway.startAccount 传入
|
|
636
|
+
// 如果 startAccount 没被调用,channelRuntime 将不可用
|
|
578
637
|
|
|
579
638
|
const stopAllChannels = () => {
|
|
580
639
|
logger.info(`[Claw Camp] 停止 ${channels.size} 个连接...`);
|
|
@@ -585,14 +644,11 @@ export default function (api: OpenClawPluginApi) {
|
|
|
585
644
|
channels.clear();
|
|
586
645
|
};
|
|
587
646
|
|
|
588
|
-
// 注册 shutdown hook
|
|
647
|
+
// 注册 shutdown hook
|
|
589
648
|
process.once('SIGTERM', stopAllChannels);
|
|
590
649
|
process.once('SIGINT', stopAllChannels);
|
|
591
650
|
api.on?.('shutdown', stopAllChannels);
|
|
592
651
|
|
|
593
|
-
// 延迟启动,等 Gateway 完全就绪
|
|
594
|
-
setTimeout(startChannels, 2000);
|
|
595
|
-
|
|
596
652
|
// 注册渠道
|
|
597
653
|
if (api.registerChannel) {
|
|
598
654
|
api.registerChannel({
|
|
@@ -642,6 +698,40 @@ export default function (api: OpenClawPluginApi) {
|
|
|
642
698
|
listAccountIds: (cfg: any) => Object.keys(cfg?.accounts || {}),
|
|
643
699
|
resolveAccount: (cfg: any, accountId: string) => cfg?.accounts?.[accountId] || null
|
|
644
700
|
},
|
|
701
|
+
gateway: {
|
|
702
|
+
startAccount: async (ctx: any) => {
|
|
703
|
+
const { accountId, account, channelRuntime } = ctx;
|
|
704
|
+
const { botId, botToken, agentName } = account as any;
|
|
705
|
+
if (!botId || !botToken) {
|
|
706
|
+
logger.warn(`[Claw Camp] account ${accountId} 缺少 botId/botToken,跳过`);
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
logger.info(`[Claw Camp] 启动连接: botId=${botId}, name=${agentName || accountId}`);
|
|
710
|
+
const channel = new ClawCampChannel(api, {
|
|
711
|
+
hubUrl: 'wss://camp.aigc.sx.cn/ws',
|
|
712
|
+
token: botToken,
|
|
713
|
+
agentId: botId,
|
|
714
|
+
agentName: agentName || accountId,
|
|
715
|
+
accountId,
|
|
716
|
+
botId,
|
|
717
|
+
botToken,
|
|
718
|
+
});
|
|
719
|
+
if (channelRuntime) {
|
|
720
|
+
channel.setChannelRuntime(channelRuntime);
|
|
721
|
+
} else {
|
|
722
|
+
logger.warn(`[Claw Camp] account ${accountId}: channelRuntime 不可用,聊天消息将降级处理`);
|
|
723
|
+
}
|
|
724
|
+
channels.set(accountId, channel);
|
|
725
|
+
channel.connect();
|
|
726
|
+
},
|
|
727
|
+
stopAccount: async (ctx: any) => {
|
|
728
|
+
const channel = channels.get(ctx.accountId);
|
|
729
|
+
if (channel) {
|
|
730
|
+
channel.disconnect();
|
|
731
|
+
channels.delete(ctx.accountId);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
},
|
|
645
735
|
onboarding: clawCampOnboardingAdapter,
|
|
646
736
|
reload: { configPrefixes: ['channels.claw-camp'] }
|
|
647
737
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claw-camp/openclaw-plugin",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.3",
|
|
4
4
|
"description": "Claw Camp plugin for OpenClaw - monitor your AI agents in real-time",
|
|
5
5
|
"main": "index.ts",
|
|
6
6
|
"keywords": ["openclaw", "plugin", "monitoring", "agent", "claw-camp"],
|