@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.
Files changed (3) hide show
  1. package/README.md +36 -31
  2. package/index.ts +122 -32
  3. 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
- 安装完成后重启 Gateway:
23
+ ### 第三步:配置 Bot 信息
24
+
25
+ **方式一:交互向导(推荐)**
16
26
 
17
27
  ```bash
18
- openclaw gateway restart
28
+ openclaw onboard
19
29
  ```
20
30
 
21
- ---
31
+ 在渠道选择步骤中选择 `claw-camp`,按提示填入 Bot ID 和 Bot Token 即可。
22
32
 
23
- ## 配置
24
-
25
- 安装后在 OpenClaw 配置文件中添加:
26
-
27
- ```json
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
- > `botId` 和 `botToken` 从 [camp.aigc.sx.cn](https://camp.aigc.sx.cn) 注册获取。
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` 状态即成功。访问 [camp.aigc.sx.cn](https://camp.aigc.sx.cn) 查看你的 Agent 上线。
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
- - 会话列表 & Token 消耗
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
- const startChannels = () => {
551
- const accounts = config.channels?.['claw-camp']?.accounts || {};
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.1",
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"],