@claw-camp/openclaw-plugin 2.0.1 → 2.0.2
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 +127 -5
- 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,16 +632,16 @@ export default function (api: OpenClawPluginApi) {
|
|
|
547
632
|
|
|
548
633
|
const channels = new Map<string, ClawCampChannel>();
|
|
549
634
|
|
|
635
|
+
// 直接启动渠道连接(兼容旧路径,gateway.startAccount 有时不触发)
|
|
550
636
|
const startChannels = () => {
|
|
551
637
|
const accounts = config.channels?.['claw-camp']?.accounts || {};
|
|
552
638
|
const accountKeys = Object.keys(accounts);
|
|
553
|
-
|
|
554
639
|
if (accountKeys.length === 0) {
|
|
555
640
|
logger.warn('[Claw Camp] 未配置 accounts,跳过连接');
|
|
556
641
|
return;
|
|
557
642
|
}
|
|
558
|
-
|
|
559
643
|
for (const accountKey of accountKeys) {
|
|
644
|
+
if (channels.has(accountKey)) continue; // 已由 gateway.startAccount 启动,跳过
|
|
560
645
|
const accountConfig = accounts[accountKey];
|
|
561
646
|
const { botId, botToken } = accountConfig;
|
|
562
647
|
if (botId && botToken) {
|
|
@@ -566,7 +651,10 @@ export default function (api: OpenClawPluginApi) {
|
|
|
566
651
|
hubUrl: 'wss://camp.aigc.sx.cn/ws',
|
|
567
652
|
token: botToken,
|
|
568
653
|
agentId: botId,
|
|
569
|
-
agentName
|
|
654
|
+
agentName,
|
|
655
|
+
accountId: accountKey,
|
|
656
|
+
botId,
|
|
657
|
+
botToken,
|
|
570
658
|
});
|
|
571
659
|
channels.set(accountKey, channel);
|
|
572
660
|
channel.connect();
|
|
@@ -585,12 +673,12 @@ export default function (api: OpenClawPluginApi) {
|
|
|
585
673
|
channels.clear();
|
|
586
674
|
};
|
|
587
675
|
|
|
588
|
-
// 注册 shutdown hook
|
|
676
|
+
// 注册 shutdown hook
|
|
589
677
|
process.once('SIGTERM', stopAllChannels);
|
|
590
678
|
process.once('SIGINT', stopAllChannels);
|
|
591
679
|
api.on?.('shutdown', stopAllChannels);
|
|
592
680
|
|
|
593
|
-
//
|
|
681
|
+
// 延迟 2s 启动(等 Gateway 就绪)
|
|
594
682
|
setTimeout(startChannels, 2000);
|
|
595
683
|
|
|
596
684
|
// 注册渠道
|
|
@@ -642,6 +730,40 @@ export default function (api: OpenClawPluginApi) {
|
|
|
642
730
|
listAccountIds: (cfg: any) => Object.keys(cfg?.accounts || {}),
|
|
643
731
|
resolveAccount: (cfg: any, accountId: string) => cfg?.accounts?.[accountId] || null
|
|
644
732
|
},
|
|
733
|
+
gateway: {
|
|
734
|
+
startAccount: async (ctx: any) => {
|
|
735
|
+
const { accountId, account, channelRuntime } = ctx;
|
|
736
|
+
const { botId, botToken, agentName } = account as any;
|
|
737
|
+
if (!botId || !botToken) {
|
|
738
|
+
logger.warn(`[Claw Camp] account ${accountId} 缺少 botId/botToken,跳过`);
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
logger.info(`[Claw Camp] 启动连接: botId=${botId}, name=${agentName || accountId}`);
|
|
742
|
+
const channel = new ClawCampChannel(api, {
|
|
743
|
+
hubUrl: 'wss://camp.aigc.sx.cn/ws',
|
|
744
|
+
token: botToken,
|
|
745
|
+
agentId: botId,
|
|
746
|
+
agentName: agentName || accountId,
|
|
747
|
+
accountId,
|
|
748
|
+
botId,
|
|
749
|
+
botToken,
|
|
750
|
+
});
|
|
751
|
+
if (channelRuntime) {
|
|
752
|
+
channel.setChannelRuntime(channelRuntime);
|
|
753
|
+
} else {
|
|
754
|
+
logger.warn(`[Claw Camp] account ${accountId}: channelRuntime 不可用,聊天消息将降级处理`);
|
|
755
|
+
}
|
|
756
|
+
channels.set(accountId, channel);
|
|
757
|
+
channel.connect();
|
|
758
|
+
},
|
|
759
|
+
stopAccount: async (ctx: any) => {
|
|
760
|
+
const channel = channels.get(ctx.accountId);
|
|
761
|
+
if (channel) {
|
|
762
|
+
channel.disconnect();
|
|
763
|
+
channels.delete(ctx.accountId);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
},
|
|
645
767
|
onboarding: clawCampOnboardingAdapter,
|
|
646
768
|
reload: { configPrefixes: ['channels.claw-camp'] }
|
|
647
769
|
}
|
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.2",
|
|
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"],
|