@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.
Files changed (3) hide show
  1. package/README.md +36 -31
  2. package/index.ts +127 -5
  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,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
- // 延迟启动,等 Gateway 完全就绪
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.1",
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"],