@yanhaidao/wecom 2.2.5 → 2.2.28

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 (46) hide show
  1. package/.claude/settings.local.json +11 -0
  2. package/.github/workflows/release.yml +56 -0
  3. package/CLAUDE.md +238 -0
  4. package/GOVERNANCE.md +26 -0
  5. package/LICENSE +7 -0
  6. package/README.md +279 -87
  7. package/assets/01.bot-add.png +0 -0
  8. package/assets/01.bot-setp2.png +0 -0
  9. package/assets/02.agent.add.png +0 -0
  10. package/assets/02.agent.api-set.png +0 -0
  11. package/changelog/v2.2.28.md +70 -0
  12. package/compat-single-account.md +118 -0
  13. package/package.json +11 -3
  14. package/src/accounts.ts +17 -55
  15. package/src/agent/api-client.ts +8 -3
  16. package/src/agent/handler.event-filter.test.ts +50 -0
  17. package/src/agent/handler.ts +162 -139
  18. package/src/channel.config.test.ts +147 -0
  19. package/src/channel.lifecycle.test.ts +234 -0
  20. package/src/channel.ts +90 -140
  21. package/src/config/accounts.resolve.test.ts +38 -0
  22. package/src/config/accounts.ts +257 -22
  23. package/src/config/index.ts +6 -0
  24. package/src/config/routing.test.ts +88 -0
  25. package/src/config/routing.ts +26 -0
  26. package/src/config/schema.ts +52 -4
  27. package/src/config-schema.ts +5 -41
  28. package/src/dynamic-agent.account-scope.test.ts +17 -0
  29. package/src/dynamic-agent.ts +178 -0
  30. package/src/gateway-monitor.ts +200 -0
  31. package/src/monitor/state.queue.test.ts +1 -1
  32. package/src/monitor/state.ts +1 -1
  33. package/src/monitor/types.ts +1 -1
  34. package/src/monitor.active.test.ts +6 -3
  35. package/src/monitor.inbound-filter.test.ts +63 -0
  36. package/src/monitor.ts +482 -53
  37. package/src/monitor.webhook.test.ts +288 -3
  38. package/src/outbound.test.ts +130 -0
  39. package/src/outbound.ts +38 -9
  40. package/src/shared/command-auth.ts +4 -2
  41. package/src/shared/xml-parser.test.ts +21 -1
  42. package/src/shared/xml-parser.ts +18 -0
  43. package/src/types/account.ts +43 -14
  44. package/src/types/config.ts +51 -2
  45. package/src/types/index.ts +3 -0
  46. package/src/types.ts +29 -147
@@ -0,0 +1,11 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(npx tsc:*)",
5
+ "Bash(./node_modules/.bin/tsc:*)",
6
+ "Bash(npm install)",
7
+ "Bash(npx vitest:*)",
8
+ "Bash(node --input-type=module -e:*)"
9
+ ]
10
+ }
11
+ }
@@ -0,0 +1,56 @@
1
+ name: Release & Publish
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*'
7
+
8
+ jobs:
9
+ publish:
10
+ runs-on: ubuntu-latest
11
+ # 🌟 必须与 npm 设置的 Environment 一致
12
+ environment: release
13
+ permissions:
14
+ contents: write # 发布 Release
15
+ id-token: write # 获取 OIDC 身份令牌
16
+ steps:
17
+ - name: Checkout Code
18
+ uses: actions/checkout@v4
19
+
20
+ - name: Setup Node.js
21
+ uses: actions/setup-node@v4
22
+ with:
23
+ # npm Trusted Publishing (OIDC) requires npm >=11.5.1.
24
+ # Use Node 24 to get modern npm in hosted runners.
25
+ node-version: '24'
26
+ registry-url: 'https://registry.npmjs.org'
27
+
28
+ - name: Show runtime versions
29
+ run: |
30
+ node -v
31
+ npm -v
32
+
33
+ - name: Ensure npm trusted publishing support
34
+ run: |
35
+ npm i -g npm@^11.5.1
36
+ npm -v
37
+
38
+ - name: Install Dependencies
39
+ run: npm install --legacy-peer-deps || npm install
40
+
41
+ - name: Build Plugin
42
+ run: npm run build --if-present
43
+
44
+ # 🚀 自动发布到 npm (使用 OIDC 自动授权,无需 Token)
45
+ - name: Publish to npm
46
+ run: npm publish --access public
47
+
48
+ # 自动同步发布 GitHub Release
49
+ - name: Create GitHub Release
50
+ uses: softprops/action-gh-release@v2
51
+ with:
52
+ body_path: changelog/${{ github.ref_name }}.md
53
+ draft: false
54
+ prerelease: false
55
+ env:
56
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
package/CLAUDE.md ADDED
@@ -0,0 +1,238 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ This is an **OpenClaw Channel Plugin** for WeCom (企业微信 / WeChat Work). It enables AI bot integration with enterprise WeChat through a dual-mode architecture.
8
+
9
+ - **Package**: `@yanhaidao/wecom`
10
+ - **Type**: ES Module (NodeNext)
11
+ - **Entry**: `index.ts`
12
+
13
+ ## Architecture
14
+
15
+ ### Dual-Mode Design (Bot + Agent)
16
+
17
+ The plugin implements a unique dual-mode architecture:
18
+
19
+ | Mode | Purpose | Webhook Path | Capabilities |
20
+ |------|---------|--------------|--------------|
21
+ | **Bot** (智能体) | Real-time streaming chat | `/wecom`, `/wecom/bot` | Streaming responses, low latency, text/image only |
22
+ | **Agent** (自建应用) | Fallback & broadcast | `/wecom/agent` | File sending, broadcasts, long tasks (>6min) |
23
+
24
+ **Key Design Principle**: Bot is preferred for conversations; Agent is used as fallback when Bot cannot deliver (files, timeouts) or for proactive broadcasts.
25
+
26
+ ### Core Components
27
+
28
+ ```
29
+ index.ts # Plugin entry - registers channel and HTTP handlers
30
+ src/
31
+ channel.ts # ChannelPlugin implementation, lifecycle management
32
+ monitor.ts # Core webhook handler, message flow, stream state
33
+ runtime.ts # Runtime state singleton
34
+ http.ts # HTTP client with undici + proxy support
35
+ crypto.ts # AES-CBC encryption/decryption for webhooks
36
+ media.ts # Media file download/decryption
37
+ outbound.ts # Outbound message adapter
38
+ target.ts # Target resolution (user/party/tag/chat)
39
+ dynamic-agent.ts # Dynamic agent routing (per-user/per-group isolation)
40
+ agent/
41
+ api-client.ts # WeCom API client with AccessToken caching
42
+ handler.ts # XML webhook handler for Agent mode
43
+ config/
44
+ schema.ts # Zod schemas for configuration
45
+ monitor/
46
+ state.ts # StreamStore and ActiveReplyStore with TTL pruning
47
+ types/constants.ts # API endpoints and limits
48
+ ```
49
+
50
+ ### Stream State Management
51
+
52
+ The plugin uses a sophisticated stream state system (`src/monitor/state.ts`):
53
+
54
+ - **StreamStore**: Manages message streams with 6-minute timeout window
55
+ - **ActiveReplyStore**: Tracks `response_url` for proactive pushes
56
+ - **Pending Queue**: Debounces rapid messages (500ms default)
57
+ - **Message Deduplication**: Uses `msgid` to prevent duplicate processing
58
+
59
+ ### Token Management
60
+
61
+ Agent mode uses automatic AccessToken caching (`src/agent/api-client.ts`):
62
+ - Token cached with 60-second refresh buffer
63
+ - Automatic retry on expiration
64
+ - Thread-safe refresh deduplication
65
+
66
+ ## Development Commands
67
+
68
+ ### Testing
69
+
70
+ This project uses **Vitest**. Tests extend from a base config at `../../vitest.config.ts`:
71
+
72
+ ```bash
73
+ # Run all tests
74
+ npx vitest --config vitest.config.ts
75
+
76
+ # Run specific test file
77
+ npx vitest --config vitest.config.ts src/crypto.test.ts
78
+
79
+ # Run tests matching pattern
80
+ npx vitest --config vitest.config.ts --testNamePattern="should encrypt"
81
+
82
+ # Watch mode
83
+ npx vitest --config vitest.config.ts --watch
84
+ ```
85
+
86
+ Test files are located alongside source files with `.test.ts` suffix:
87
+ - `src/crypto.test.ts`
88
+ - `src/monitor.integration.test.ts`
89
+ - `src/monitor/state.queue.test.ts`
90
+ - etc.
91
+
92
+ ### Type Checking
93
+
94
+ ```bash
95
+ npx tsc --noEmit
96
+ ```
97
+
98
+ ### Build
99
+
100
+ The plugin is loaded directly as TypeScript by OpenClaw. No build step is required for development, but type checking is recommended.
101
+
102
+ ## Configuration Schema
103
+
104
+ Configuration is validated via Zod (`src/config/schema.ts`):
105
+
106
+ ```typescript
107
+ {
108
+ enabled: boolean,
109
+ bot: {
110
+ token: string, // Bot webhook token
111
+ encodingAESKey: string, // AES encryption key
112
+ receiveId: string?, // Optional receive ID
113
+ streamPlaceholderContent: string?, // "Thinking..."
114
+ welcomeText: string?,
115
+ dm: { policy, allowFrom }
116
+ },
117
+ agent: {
118
+ corpId: string,
119
+ corpSecret: string,
120
+ agentId: number,
121
+ token: string, // Callback token
122
+ encodingAESKey: string, // Callback AES key
123
+ welcomeText: string?,
124
+ dm: { policy, allowFrom }
125
+ },
126
+ network: {
127
+ egressProxyUrl: string? // For dynamic IP scenarios
128
+ },
129
+ media: {
130
+ maxBytes: number? // Default 25MB
131
+ },
132
+ dynamicAgents: {
133
+ enabled: boolean? // Enable per-user/per-group agents
134
+ dmCreateAgent: boolean? // Create agent per DM user
135
+ groupEnabled: boolean? // Enable for group chats
136
+ adminUsers: string[]? // Admin users (bypass dynamic routing)
137
+ }
138
+ }
139
+ ```
140
+
141
+ ### Dynamic Agent Routing
142
+
143
+ When `dynamicAgents.enabled` is `true`, the plugin automatically creates isolated Agent instances for each user/group:
144
+
145
+ ```bash
146
+ # Enable dynamic agents
147
+ openclaw config set channels.wecom.dynamicAgents.enabled true
148
+
149
+ # Configure admin users (use main agent)
150
+ openclaw config set channels.wecom.dynamicAgents.adminUsers '["admin1","admin2"]'
151
+ ```
152
+
153
+ **Generated Agent ID format**: `wecom-{type}-{peerId}`
154
+ - DM: `wecom-dm-zhangsan`
155
+ - Group: `wecom-group-wr123456`
156
+
157
+ Dynamic agents are automatically added to `agents.list` in the config file.
158
+
159
+ ## Key Technical Details
160
+
161
+ ### Webhook Security
162
+
163
+ - **Signature Verification**: HMAC-SHA256 with token
164
+ - **Encryption**: AES-CBC with PKCS#7 padding (32-byte blocks)
165
+ - **Paths**: `/wecom` (legacy), `/wecom/bot`, `/wecom/agent`
166
+
167
+ ### Timeout Handling
168
+
169
+ Bot mode has a 6-minute window (360s) for streaming responses. The plugin:
170
+ - Tracks deadline: `createdAt + 6 * 60 * 1000`
171
+ - Switches to Agent fallback at `deadline - 30s` margin
172
+ - Sends DM via Agent for remaining content
173
+
174
+ ### Media Handling
175
+
176
+ - **Inbound**: Decrypts WeCom encrypted media URLs
177
+ - **Outbound Images**: Base64 encoded via `msg_item` in stream
178
+ - **Outbound Files**: Requires Agent mode, sent via `media/upload` + `message/send`
179
+ - **Max Size**: 25MB default (configurable via `channels.wecom.media.maxBytes`)
180
+
181
+ ### Proxy Support
182
+
183
+ For servers with dynamic IPs (common error: `60020 not allow to access from your ip`):
184
+
185
+ ```bash
186
+ openclaw config set channels.wecom.network.egressProxyUrl "http://proxy.company.local:3128"
187
+ ```
188
+
189
+ ## Testing Notes
190
+
191
+ - Tests use Vitest with `../../vitest.config.ts` as base
192
+ - Integration tests mock WeCom API responses
193
+ - Crypto tests verify AES encryption round-trips
194
+ - Monitor tests cover stream state transitions and queue behavior
195
+
196
+ ## Common Patterns
197
+
198
+ ### Adding a New Message Type Handler
199
+
200
+ 1. Update `buildInboundBody()` in `src/monitor.ts` to parse the message
201
+ 2. Add type definitions in `src/types/message.ts`
202
+ 3. Update `processInboundMessage()` if media handling is needed
203
+
204
+ ### Agent API Calls
205
+
206
+ Always use `api-client.ts` methods which handle token management:
207
+
208
+ ```typescript
209
+ import { sendText, uploadMedia } from "./agent/api-client.js";
210
+
211
+ // Token is automatically cached and refreshed
212
+ await sendText({ agent, toUser: "userid", text: "Hello" });
213
+ ```
214
+
215
+ ### Stream Content Updates
216
+
217
+ Use `streamStore.updateStream()` for thread-safe updates:
218
+
219
+ ```typescript
220
+ streamStore.updateStream(streamId, (state) => {
221
+ state.content = "new content";
222
+ state.finished = true;
223
+ });
224
+ ```
225
+
226
+ ## Dependencies
227
+
228
+ - `undici`: HTTP client with proxy support
229
+ - `fast-xml-parser`: XML parsing for Agent callbacks
230
+ - `zod`: Configuration validation
231
+ - `openclaw`: Peer dependency (>=2026.2.24)
232
+
233
+ ## WeCom API Endpoints Used
234
+
235
+ - `GET_TOKEN`: `https://qyapi.weixin.qq.com/cgi-bin/gettoken`
236
+ - `SEND_MESSAGE`: `https://qyapi.weixin.qq.com/cgi-bin/message/send`
237
+ - `UPLOAD_MEDIA`: `https://qyapi.weixin.qq.com/cgi-bin/media/upload`
238
+ - `DOWNLOAD_MEDIA`: `https://qyapi.weixin.qq.com/cgi-bin/media/get`
package/GOVERNANCE.md ADDED
@@ -0,0 +1,26 @@
1
+ # Governance
2
+
3
+ This document clarifies project ownership and collaboration expectations so contributors can work together with fewer ambiguities.
4
+
5
+ ## 1. Upstream ownership
6
+ - This repository is the **Upstream / Source of Truth** for the OpenClaw WeCom plugin.
7
+ - **Author & Lead Maintainer:** YanHaidao (GitHub: YanHaidao).
8
+
9
+ ## 2. Co-maintenance model
10
+ - Tencent Cloud contributors are welcome as **co-maintainers** for code, docs, testing, and cloud deployment adaptation.
11
+ - Tencent Cloud may host an official mirror repository for sync and downstream integration needs.
12
+
13
+ ## 3. Decision-making
14
+ - We prefer discussion and consensus on non-trivial changes.
15
+ - If consensus is not reached in time, the Lead Maintainer makes the final upstream decision for roadmap, architecture, and release direction.
16
+
17
+ ## 4. Contribution workflow
18
+ - Non-trivial changes should be proposed via Pull Request.
19
+ - Keep change scope clear, include test notes when relevant, and document behavior changes.
20
+
21
+ ## 5. Mirrors and downstream adaptations
22
+ - Mirrors may carry downstream patches (for example deployment integration or cloud templates).
23
+ - Mirrors/downstream repositories should keep attribution in README or NOTICE:
24
+ - Upstream source: this repository
25
+ - Author: YanHaidao
26
+ - Co-maintained with Tencent Cloud contributors
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ ISC License (ISC)
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.