@zhin.js/adapter-wecom 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 (52) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/README.md +79 -216
  3. package/adapters/wecom.ts +27 -0
  4. package/agent/tools/get_dept_users.ts +18 -0
  5. package/agent/tools/get_user.ts +17 -0
  6. package/agent/tools/list_departments.ts +18 -0
  7. package/agent/tools/send_text.ts +19 -0
  8. package/lib/endpoint.d.ts +38 -35
  9. package/lib/endpoint.js +148 -545
  10. package/lib/index.d.ts +5 -15
  11. package/lib/index.js +5 -115
  12. package/lib/platform-permit.d.ts +1 -2
  13. package/lib/platform-permit.js +4 -2
  14. package/lib/protocol.d.ts +96 -0
  15. package/lib/protocol.js +252 -0
  16. package/lib/webhook.d.ts +14 -0
  17. package/lib/webhook.js +86 -0
  18. package/lib/wecom-agent-deps.d.ts +17 -0
  19. package/lib/wecom-agent-deps.js +30 -0
  20. package/package.json +51 -16
  21. package/plugin.ts +12 -0
  22. package/schema.json +24 -0
  23. package/src/endpoint.ts +196 -584
  24. package/src/index.ts +49 -130
  25. package/src/platform-permit.ts +1 -2
  26. package/src/protocol.ts +362 -0
  27. package/src/webhook.ts +130 -0
  28. package/src/wecom-agent-deps.ts +46 -0
  29. package/lib/adapter.d.ts +0 -17
  30. package/lib/adapter.d.ts.map +0 -1
  31. package/lib/adapter.js +0 -22
  32. package/lib/adapter.js.map +0 -1
  33. package/lib/endpoint.d.ts.map +0 -1
  34. package/lib/endpoint.js.map +0 -1
  35. package/lib/index.d.ts.map +0 -1
  36. package/lib/index.js.map +0 -1
  37. package/lib/platform-permit.d.ts.map +0 -1
  38. package/lib/platform-permit.js.map +0 -1
  39. package/lib/segment-mapper.d.ts +0 -2
  40. package/lib/segment-mapper.d.ts.map +0 -1
  41. package/lib/segment-mapper.js +0 -2
  42. package/lib/segment-mapper.js.map +0 -1
  43. package/lib/types.d.ts +0 -48
  44. package/lib/types.d.ts.map +0 -1
  45. package/lib/types.js +0 -5
  46. package/lib/types.js.map +0 -1
  47. package/plugin.yml +0 -3
  48. package/src/adapter.ts +0 -28
  49. package/src/segment-mapper.ts +0 -1
  50. package/src/types.ts +0 -51
  51. /package/{skills/wecom → agent}/PERMITS.md +0 -0
  52. /package/{skills/wecom/SKILL.md → agent/skills/wecom.md} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,53 @@
1
1
  # @zhin.js/adapter-wecom
2
2
 
3
+ ## 2.0.3
4
+
5
+ ### Patch Changes
6
+
7
+ - cc5c94d: 约定式插件运行时迁移(breaking):插件与适配器由 `usePlugin()` / `extends Adapter` 迁移为 `definePlugin` / `defineAdapter` + `plugin.ts` + 约定目录(`adapters/`、`commands/`、`components/`、`tools/` 等)。
8
+
9
+ - 新增约定式运行时包:`@zhin.js/plugin-runtime`、`@zhin.js/adapter`、`@zhin.js/runtime`、`@zhin.js/host-http`(首版 1.0.0 走 init-publish,不在本 changeset 内 bump)。
10
+ - 全部 20 个平台适配器改为约定式 `defineAdapter`,旧 `usePlugin` / `extends Adapter` / `segment-mapper` 生产入口已删除;onebot11 反向 WSS、onebot12 webhook/wss、milky sse/webhook/wss、satori webhook、kook webhook、qq webhook/middleware 等 slice 1 推迟的连接模式已补齐。
11
+ - 游戏 / 工具 / 服务插件同步迁移到约定目录结构。
12
+ - CLI 增加 plugin-runtime host installer(http/database/outbound/schedule/console 等)。
13
+
14
+ 后续加固(同批):
15
+
16
+ - CLI:`zhin runtime start --daemon`(pidfile/崩溃拉起/风暴保护),orphan watchdog 防僵尸进程;legacy `zhin dev` / `zhin start` 已移除(含 `zhin restart`),`zhin stop` 兼容新 daemon。
17
+ - 安全:builtin 工具统一走 `security/policy-facade.ts` 的 `runToolPolicies`(声明式策略表,deny 优先);审计日志 close flush + 背压队列;`splitCompoundCommand` 引号感知、`extractCommandName` 去引号堵绕过。
18
+ - 日志:Logger 双堆栈修复、本地时区、`getLogger` 挂树(`setLevel` 递归生效)、第三方库(log4js/discord)桥接、启动人读总结。
19
+ - 结构:`plugins/games/shared` 迁为 `packages/game-kit`(`@zhin.js/game-kit`);死目录 `plugins/adapters/common` 删除。
20
+ - 脚手架:`create-zhin-app` / `zhin new` / scaffold-wizard 生成物改为 Plugin Runtime 形态(minimal-bot 同构,新配置格式)。
21
+ - Console:endpoint.list 真实名称与 phase、schema:get-all 按 instanceKey 映射、db:\* 接 DatabaseHost。
22
+
23
+ 注:按仓库发布惯例(见 1bb345dd2),本次 breaking 迁移统一使用 patch,避免 zhin.js 5.0 级联。
24
+
25
+ - Updated dependencies [16ec4e8]
26
+ - Updated dependencies [cc5c94d]
27
+ - Updated dependencies [447f3e2]
28
+ - @zhin.js/core@1.3.5
29
+ - @zhin.js/agent@1.0.4
30
+ - @zhin.js/host-http@1.0.1
31
+ - zhin.js@4.1.3
32
+ - @zhin.js/logger@1.0.75
33
+ - @zhin.js/plugin-runtime@1.0.1
34
+ - @zhin.js/adapter@1.0.1
35
+
36
+ ## 2.0.2
37
+
38
+ ### Patch Changes
39
+
40
+ - 872c583: Slack 适配器 Phase 1/2:mrkdwn 出站、长消息切分、斜杠/按钮 ephemeral 反馈、入站 mrkdwn→Markdown、editMessage 对齐 core。
41
+
42
+ Logger 表格日志与 string-width 列宽;Agent AI Handler 框线表格与 introspection/MCP 导出;Core side-event 归一化;Schedule 时区规划;多适配器 side-event 与 API surface 更新。
43
+
44
+ - 872c583: fix: 代码格式优化
45
+ - Updated dependencies [872c583]
46
+ - Updated dependencies [872c583]
47
+ - @zhin.js/agent@1.0.3
48
+ - @zhin.js/host-router@2.0.3
49
+ - zhin.js@4.1.2
50
+
3
51
  ## 2.0.1
4
52
 
5
53
  ### Patch Changes
package/README.md CHANGED
@@ -1,6 +1,13 @@
1
1
  # @zhin.js/adapter-wecom
2
2
 
3
- Zhin.js 企业微信(WeCom)适配器,支持企业内部应用机器人开发。
3
+ Zhin.js 企业微信(WeCom)适配器(Plugin Runtime),通过 Runtime Host HTTP Webhook 收发消息。
4
+
5
+ ## 功能
6
+
7
+ - Webhook 事件接收(`httpHostToken` GET 验签解密 + POST 消息)
8
+ - AES-256-CBC 消息加解密与 SHA1 签名验证
9
+ - Access Token 自动刷新
10
+ - 约定式 `defineAdapter` / `definePlugin`(无需 `usePlugin`)
4
11
 
5
12
  ## 安装
6
13
 
@@ -8,255 +15,111 @@ Zhin.js 企业微信(WeCom)适配器,支持企业内部应用机器人开
8
15
  pnpm add @zhin.js/adapter-wecom
9
16
  ```
10
17
 
11
- ## 前置条件
18
+ ## Plugin Runtime
12
19
 
13
- ### 企业微信管理后台配置
20
+ - `@zhin.js/adapter` — 约定式 `adapters/wecom.ts`(`defineAdapter`)
21
+ - `@zhin.js/core` — `messageGatewayToken` 入站/出站
22
+ - `@zhin.js/host-http` — `httpHostToken` 注册 Webhook 路由(**非** legacy host-router/Koa)
23
+ - `@zhin.js/plugin-runtime` — `plugin.ts`(`definePlugin`)
24
+ - 配置经插件 `schema.json` 落到 `plugins.<instanceKey>`
14
25
 
15
- 1. **登录企业微信管理后台**
16
- - 访问 [企业微信管理后台](https://work.weixin.qq.com/wework_admin/frame)
17
- - 使用管理员账号登录
26
+ 入站:`gateway.receive({ adapter, target: FromUserName, content: text, sender, metadata })`
27
+ 出站:`send({ target, payload })` → 企业微信 `message/send` API
18
28
 
19
- 2. **创建应用**
20
- - 进入「应用管理」→「自建」
21
- - 点击「创建应用」
22
- - 填写应用名称、上传 Logo、选择可见范围
29
+ 入站 `metadata.mentioned`:**未接线**。企业微信应用消息回调的 XML 事件不含 mentions/@ 字段,回调里的 `ToUserName` 是 CorpID(企业 ID)而非可比较的 bot 用户 id,配置中也没有 bot id/name 可作可靠判据,故无法可靠识别 @ 机器人。
23
30
 
24
- 3. **获取应用凭证**
25
- - 在应用详情页面获取:
26
- - **CorpId**(企业 ID):在「我的企业」→「企业信息」中查看
27
- - **AgentId**(应用 AgentId):在应用详情页面
28
- - **Secret**(应用 Secret):在应用详情页面
31
+ ## 前置条件
29
32
 
30
- 4. **配置接收消息**
31
- - 在应用详情页面,进入「接收消息」设置
32
- - 设置 **URL**:`https://yourdomain.com/wecom/callback`
33
- - 设置 **Token**:自定义字符串(用于签名验证)
34
- - 设置 **EncodingAESKey**:点击「随机获取」(用于消息加解密)
35
- - 点击「保存」,企业微信会发送验证请求
33
+ ### 企业微信管理后台配置
36
34
 
37
- 5. **配置权限**
38
- - 在「API 权限」中申请所需权限:
39
- - `通讯录只读权限` - 读取组织架构和用户信息
40
- - `应用消息发送权限` - 向用户发送消息
41
- - 其他业务需要的权限
35
+ 1. 登录 [企业微信管理后台](https://work.weixin.qq.com/wework_admin/frame)
36
+ 2. 「应用管理」→「自建」→ 创建应用,获取 **CorpId**、**AgentId**、**Secret**
37
+ 3. 「接收消息」设置:
38
+ - **URL**:`https://yourdomain.com/wecom/callback`
39
+ - **Token** / **EncodingAESKey** 与配置一致
40
+ 4. Runtime Host(`http`)须已 listen,Webhook 才可达
42
41
 
43
- ## 配置
42
+ 必填字段:`corpId`、`agentSecret`、`token`、`encodingAESKey`。
44
43
 
45
- ### 基础配置(zhin.config.yml)
44
+ ## 最小配置
46
45
 
47
46
  ```yaml
48
- endpoints:
49
- - name: wecom-bot
50
- context: wecom
51
- corpId: "ww1234567890abcdef"
52
- agentSecret: "your-agent-secret"
53
- token: "your-verify-token"
54
- encodingAESKey: "your-43-char-encoding-aes-key"
55
- webhookPath: "/wecom/callback"
56
-
47
+ # zhin.config.yml(Plugin Runtime)
57
48
  plugins:
58
- - "@zhin.js/host-router"
59
- - "@zhin.js/adapter-wecom"
60
- ```
61
-
62
- ### 完整配置
63
-
64
- ```yaml
65
- endpoints:
66
- - name: wecom-bot
67
- context: wecom
68
- corpId: "ww1234567890abcdef"
69
- agentSecret: "your-agent-secret"
70
- token: "your-verify-token"
71
- encodingAESKey: "your-43-char-encoding-aes-key"
72
- webhookPath: "/wecom/callback"
73
- apiBaseUrl: "https://qyapi.weixin.qq.com" # 可选,默认值
49
+ wecom:
50
+ name: wecom-bot
51
+ corpId: ${WECOM_CORP_ID}
52
+ agentSecret: ${WECOM_AGENT_SECRET}
53
+ token: ${WECOM_TOKEN}
54
+ encodingAESKey: ${WECOM_AES_KEY}
55
+ webhookPath: /wecom/callback # 可选,默认 /wecom/callback
56
+ apiBaseUrl: https://qyapi.weixin.qq.com # 可选
74
57
  ```
75
58
 
76
- ### 配置参数说明
77
-
78
- | 参数 | 必需 | 说明 |
79
- |------|------|------|
80
- | `corpId` | 是 | 企业 ID,在管理后台「我的企业」查看 |
81
- | `agentSecret` | 是 | 应用 Secret,在应用详情页获取 |
82
- | `token` | 是 | 用于签名验证,需与管理后台「接收消息」设置中的 Token 一致 |
83
- | `encodingAESKey` | 是 | 用于消息加解密,需与管理后台一致(43 字符) |
84
- | `webhookPath` | 否 | Webhook 回调路径,默认 `/wecom/callback` |
85
- | `apiBaseUrl` | 否 | 企业微信 API 地址,默认 `https://qyapi.weixin.qq.com` |
59
+ 根插件 `zhin.plugins`(或项目图)需引用 `@zhin.js/adapter-wecom`(`instanceKey: wecom`)。
86
60
 
87
- ### 环境变量推荐
88
-
89
- ```yaml
90
- endpoints:
91
- - name: wecom-bot
92
- context: wecom
93
- corpId: "${WECOM_CORP_ID}"
94
- agentSecret: "${WECOM_AGENT_SECRET}"
95
- token: "${WECOM_TOKEN}"
96
- encodingAESKey: "${WECOM_AES_KEY}"
97
- ```
98
-
99
- ## 使用示例
100
-
101
- ### 接收和发送消息
102
-
103
- ```typescript
104
- import { usePlugin, MessageCommand } from 'zhin.js'
105
-
106
- const { addCommand, logger } = usePlugin()
107
-
108
- // 定义命令
109
- addCommand(new MessageCommand('hello <name:text>')
110
- .action(async (message, result) => {
111
- logger.info(`收到问候: ${result.params.name}`)
112
- return `你好,${result.params.name}!欢迎使用企业微信机器人。`
113
- })
114
- )
115
-
116
- // 监听所有消息
117
- import { onMessage } from 'zhin.js'
118
-
119
- onMessage(async (message) => {
120
- logger.info(`收到消息:${JSON.stringify(message.$content)}`)
121
- logger.info(`发送者:${message.$sender.name}`)
122
- logger.info(`会话类型:${message.$channel.type}`)
123
- })
124
- ```
61
+ ## 环境变量
125
62
 
126
- ### 发送富文本消息
127
-
128
- ```typescript
129
- addCommand(new MessageCommand('info')
130
- .action(async (message) => {
131
- return [
132
- {
133
- type: 'markdown',
134
- data: {
135
- content: `# 系统信息\n\n- 服务器: 运行正常\n- 版本: v1.0.0\n- 状态: 在线`
136
- }
137
- }
138
- ]
139
- })
140
- )
141
- ```
63
+ | 变量 | 说明 |
64
+ |------|------|
65
+ | `WECOM_CORP_ID` | 企业 ID |
66
+ | `WECOM_AGENT_SECRET` | 应用 Secret |
67
+ | `WECOM_TOKEN` | 回调签名 Token |
68
+ | `WECOM_AES_KEY` | EncodingAESKey(43 字符) |
142
69
 
143
70
  ## 消息类型支持
144
71
 
145
- ### 接收消息类型
146
-
147
- | 类型 | 状态 | 说明 |
148
- |------|------|------|
149
- | 文本消息 (`text`) | 支持 | 普通文本 |
150
- | 图片消息 (`image`) | 支持 | 图片文件 |
151
- | 语音消息 (`voice`) | 支持 | 含语音识别结果 |
152
- | 视频消息 (`video`) | 支持 | 视频文件 |
153
- | 位置消息 (`location`) | 支持 | 地理位置 |
154
- | 链接消息 (`link`) | 支持 | 链接卡片 |
155
- | 事件消息 (`event`) | 支持 | 关注/取消关注等 |
156
-
157
- ### 发送消息类型
158
-
159
- | 类型 | 状态 | 说明 |
160
- |------|------|------|
161
- | 文本消息 | 支持 | 支持 @ 提醒 |
162
- | 图片消息 | 支持 | 需要 media_id |
163
- | Markdown 消息 | 支持 | 企业微信原生 Markdown |
164
- | 图文消息 | 支持 | news 类型 |
165
- | 撤回消息 | 不支持 | 企业微信机器人不支持撤回 |
72
+ | 入站类型 | content 摘要 |
73
+ |----------|--------------|
74
+ | text | 原文 |
75
+ | image | `[image: url]` |
76
+ | voice | 识别结果或 `[voice]` |
77
+ | video / shortvideo | `[video]` |
78
+ | location | `[位置] …` |
79
+ | link | `[link: …]` |
80
+ | event | `[事件] …` |
81
+
82
+ | 出站 wire | 说明 |
83
+ |-----------|------|
84
+ | text | 支持 `<@userid>` |
85
+ | image | 需 `media_id` |
86
+ | markdown | 企业微信原生 Markdown |
87
+ | news | link 段映射 |
166
88
 
167
89
  ## 安全说明
168
90
 
169
- ### 消息加解密
170
-
171
- 企业微信使用 AES-256-CBC 加密算法对回调消息进行加密:
172
-
173
- - **签名验证**:SHA1 排序拼接 `[token, timestamp, nonce, encrypt]`
174
- - **解密流程**:
175
- 1. Base64 解码加密消息
176
- 2. AES-256-CBC 解密(Key: `encodingAESKey` + `=` 的 Base64 解码,IV: Key 的前 16 字节)
177
- 3. PKCS7 去填充
178
- 4. 提取:16 字节随机数 + 4 字节消息长度 + 消息体 + CorpId
91
+ 企业微信回调始终加密:
179
92
 
180
- 适配器自动处理所有加解密,无需手动处理。
181
-
182
- ### Token 管理
183
-
184
- 适配器会自动管理 access_token:
185
- - 首次连接时获取 token
186
- - Token 过期前 5 分钟自动刷新
187
- - 所有 API 请求自动携带有效 token
188
-
189
- ## API 方法
190
-
191
- ### 获取用户信息
192
-
193
- ```typescript
194
- const endpoint = app.adapters.get('wecom')?.endpoints.get('wecom-bot')
195
- if (endpoint) {
196
- const userInfo = await endpoint.getUserInfo('user-id')
197
- console.log(userInfo)
198
- }
199
- ```
93
+ - **签名**:SHA1 排序拼接 `[token, timestamp, nonce, encrypt]`
94
+ - **解密**:AES-256-CBC(Key = `encodingAESKey` + `=` 的 Base64,IV = Key 前 16 字节)
200
95
 
201
- ### 获取部门用户列表
202
-
203
- ```typescript
204
- const users = await endpoint.getDepartmentUsers(1) // 部门 ID
205
- console.log(users)
206
- ```
207
-
208
- ### 获取部门列表
209
-
210
- ```typescript
211
- const departments = await endpoint.getDepartmentList(1) // 父部门 ID
212
- console.log(departments)
213
- ```
214
-
215
- ### 发送文本消息
216
-
217
- ```typescript
218
- const success = await endpoint.sendTextMessage('user-id', '这是一条测试消息')
219
- ```
96
+ Access Token 在过期前 5 分钟自动刷新。
220
97
 
221
98
  ## 故障排查
222
99
 
223
- ### Q: Webhook 验证失败?
224
-
225
- A: 检查以下几点:
226
- 1. `token` 是否与企业微信管理后台设置的 Token 一致
227
- 2. `encodingAESKey` 是否与管理后台一致(43 字符)
228
- 3. 服务器是否可从公网访问
229
- 4. 服务器是否返回了正确的验证响应(解密后的 echostr)
230
-
231
- ### Q: 收不到消息回调?
232
-
233
- A: 可能的原因:
234
- 1. 应用未启用「接收消息」功能
235
- 2. Webhook URL 配置错误或不可达
236
- 3. 应用可见范围未包含发送消息的用户
237
- 4. 服务器日志中是否有签名验证失败的警告
100
+ | 现象 | 排查 |
101
+ |------|------|
102
+ | URL 验证失败 | `token` / `encodingAESKey` / `corpId` 与管理后台一致;Host 已 listen 且公网可达 |
103
+ | 收不到消息 | 应用已启用接收消息;可见范围包含发送者;endpoint 已 `open()` |
104
+ | 发送失败 | `corpId` + `agentSecret` 可换取 token;接收者在可见范围 |
238
105
 
239
- ### Q: 发送消息失败?
106
+ ## AI 工具
240
107
 
241
- A: 可能的原因:
242
- 1. `agentSecret`(应用 Secret)配置错误
243
- 2. access_token 获取失败(检查 CorpId Secret)
244
- 3. 接收者不在应用可见范围内
245
- 4. 应用未开通消息发送权限
108
+ | 类别 | 路径 |
109
+ |------|------|
110
+ | Permit 词汇 | `agent/PERMITS.md` |
111
+ | 平台工具(4 个) | `agent/tools/` |
112
+ | 技能说明 | `agent/skills/wecom.md` |
246
113
 
247
- ### Q: Token 刷新失败?
114
+ ## 平台权限(platform permit)
248
115
 
249
- A: 检查:
250
- 1. CorpId 是否正确
251
- 2. agentSecret 是否是应用 Secret(不是应用的 AgentId 数字)
252
- 3. 企业微信 API 是否可访问
116
+ `plugin.ts` 在 generation setup 注册 `src/platform-permit.ts` checker,并在 dispose 注销;CapabilityIngress 与 ToolSystem 统一经 Core `canAccessTool()` 消费工具权限。
253
117
 
254
118
  ## 相关链接
255
119
 
256
120
  - [企业微信开发文档](https://developer.work.weixin.qq.com/document/)
257
- - [企业微信接收消息](https://developer.work.weixin.qq.com/document/path/90930)
258
- - [企业微信发送应用消息](https://developer.work.weixin.qq.com/document/path/90236)
259
- - [Zhin.js 官方文档](https://github.com/zhinjs/zhin)
121
+ - [接收消息](https://developer.work.weixin.qq.com/document/path/90930)
122
+ - [发送应用消息](https://developer.work.weixin.qq.com/document/path/90236)
260
123
 
261
124
  ## 许可证
262
125
 
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Convention entry: discover `adapters/wecom.ts` → defineAdapter.
3
+ * Implementation lives under `src/` (endpoint / webhook / protocol).
4
+ */
5
+ import { defineAdapter } from '@zhin.js/adapter';
6
+ import { messageGatewayToken } from '@zhin.js/core/runtime';
7
+ import { httpHostToken } from '@zhin.js/host-http';
8
+ import { WecomEndpoint } from '../src/endpoint.js';
9
+ import {
10
+ resolveWecomConfig,
11
+ type WecomAdapterConfig,
12
+ } from '../src/protocol.js';
13
+
14
+ export { WecomEndpoint } from '../src/endpoint.js';
15
+ export type { WecomEndpointOptions, WecomFetch } from '../src/endpoint.js';
16
+
17
+ export default defineAdapter<WecomAdapterConfig>({
18
+ capabilities: ['inbound', 'outbound'],
19
+ create(context) {
20
+ return new WecomEndpoint({
21
+ id: context.id,
22
+ gateway: context.use(messageGatewayToken),
23
+ http: context.use(httpHostToken),
24
+ config: resolveWecomConfig(context.config),
25
+ });
26
+ },
27
+ });
@@ -0,0 +1,18 @@
1
+ import { defineAgentTool } from '@zhin.js/agent/tools';
2
+ import { z } from 'zod';
3
+ import { getWecomAgentDeps } from '../../src/wecom-agent-deps.js';
4
+ export default defineAgentTool<{ endpoint_id: string; dept_id: string }>({
5
+ description: '获取企业微信部门用户列表',
6
+ inputSchema: z.object({
7
+ endpoint_id: z.string().describe('Endpoint 名称'),
8
+ dept_id: z.string().describe('部门 ID'),
9
+ }),
10
+ platforms: ['wecom'],
11
+ tags: ['wecom'],
12
+ async execute({ endpoint_id, dept_id }: { endpoint_id: string; dept_id: string }) {
13
+ const endpoint = getWecomAgentDeps().getEndpoint(endpoint_id);
14
+ const users = await endpoint.getDepartmentUsers(Number(dept_id));
15
+ return { users, count: users.length };
16
+ },
17
+ });
18
+
@@ -0,0 +1,17 @@
1
+ import { defineAgentTool } from '@zhin.js/agent/tools';
2
+ import { z } from 'zod';
3
+ import { getWecomAgentDeps } from '../../src/wecom-agent-deps.js';
4
+ export default defineAgentTool<{ endpoint_id: string; user_id: string }>({
5
+ description: '获取企业微信用户信息',
6
+ inputSchema: z.object({
7
+ endpoint_id: z.string().describe('Endpoint 名称'),
8
+ user_id: z.string().describe('用户 ID'),
9
+ }),
10
+ platforms: ['wecom'],
11
+ tags: ['wecom'],
12
+ async execute({ endpoint_id, user_id }: { endpoint_id: string; user_id: string }) {
13
+ const endpoint = getWecomAgentDeps().getEndpoint(endpoint_id);
14
+ return await endpoint.getUserInfo(user_id);
15
+ },
16
+ });
17
+
@@ -0,0 +1,18 @@
1
+ import { defineAgentTool } from '@zhin.js/agent/tools';
2
+ import { z } from 'zod';
3
+ import { getWecomAgentDeps } from '../../src/wecom-agent-deps.js';
4
+ export default defineAgentTool<{ endpoint_id: string; dept_id?: string }>({
5
+ description: '获取企业微信部门列表',
6
+ inputSchema: z.object({
7
+ endpoint_id: z.string().describe('Endpoint 名称'),
8
+ dept_id: z.string().optional().describe('父部门 ID,默认 1(根部门)'),
9
+ }),
10
+ platforms: ['wecom'],
11
+ tags: ['wecom'],
12
+ async execute({ endpoint_id, dept_id }: { endpoint_id: string; dept_id?: string }) {
13
+ const endpoint = getWecomAgentDeps().getEndpoint(endpoint_id);
14
+ const departments = await endpoint.getDepartmentList(Number(dept_id) || 1);
15
+ return { departments, count: departments.length };
16
+ },
17
+ });
18
+
@@ -0,0 +1,19 @@
1
+ import { defineAgentTool } from '@zhin.js/agent/tools';
2
+ import { z } from 'zod';
3
+ import { getWecomAgentDeps } from '../../src/wecom-agent-deps.js';
4
+ export default defineAgentTool<{ endpoint_id: string; user_id: string; content: string }>({
5
+ description: '向指定企业微信用户发送文本消息',
6
+ inputSchema: z.object({
7
+ endpoint_id: z.string().describe('Endpoint 名称'),
8
+ user_id: z.string().describe('用户 ID'),
9
+ content: z.string().describe('消息内容'),
10
+ }),
11
+ platforms: ['wecom'],
12
+ tags: ['wecom'],
13
+ async execute({ endpoint_id, user_id, content }: { endpoint_id: string; user_id: string; content: string }) {
14
+ const endpoint = getWecomAgentDeps().getEndpoint(endpoint_id);
15
+ const success = await endpoint.sendTextMessage(user_id, content);
16
+ return { success, message: success ? '消息已发送' : '发送失败' };
17
+ },
18
+ });
19
+
package/lib/endpoint.d.ts CHANGED
@@ -1,43 +1,46 @@
1
1
  /**
2
- * 企业微信 Endpoint 实现
2
+ * WecomEndpoint lifecycle, outbound send, inbound admit, OpenAPI helpers for agent tools.
3
3
  */
4
- import { Endpoint, Message, type SendOptions } from 'zhin.js';
5
- import { type Router } from '@zhin.js/host-router/router';
6
- import type { WecomEndpointConfig, WecomMessage, WecomApiResponse } from './types.js';
7
- import type { WecomAdapter } from './adapter.js';
8
- export declare class WecomEndpoint implements Endpoint<WecomEndpointConfig, WecomMessage> {
4
+ import type { EndpointInstance } from '@zhin.js/adapter';
5
+ import type { MessageGateway } from '@zhin.js/core/runtime';
6
+ import type { HttpHost } from '@zhin.js/host-http';
7
+ import type { CapabilityId } from '@zhin.js/plugin-runtime';
8
+ import { type ResolvedWecomConfig, type WecomApiResponse, type WecomMessage } from './protocol.js';
9
+ export type WecomFetch = (url: string, init?: {
10
+ readonly method?: string;
11
+ readonly headers?: Record<string, string>;
12
+ readonly body?: string;
13
+ }) => Promise<{
14
+ readonly ok: boolean;
15
+ readonly status: number;
16
+ text(): Promise<string>;
17
+ json(): Promise<unknown>;
18
+ }>;
19
+ export interface WecomEndpointOptions {
20
+ readonly id: CapabilityId;
21
+ readonly gateway: MessageGateway;
22
+ readonly http: HttpHost;
23
+ readonly config: ResolvedWecomConfig;
24
+ readonly fetch?: WecomFetch;
25
+ }
26
+ export declare class WecomEndpoint implements EndpointInstance {
9
27
  #private;
10
- adapter: WecomAdapter;
11
- $config: WecomEndpointConfig;
12
- $connected: boolean;
13
- private router;
14
- private accessToken;
15
- private baseURL;
16
- private aesKey;
17
- private corpId;
18
- get $id(): string;
19
- get logger(): import("zhin.js").Logger;
20
- constructor(adapter: WecomAdapter, router: Router, $config: WecomEndpointConfig);
21
- private request;
22
- private setupWebhookRoute;
23
- private handleVerification;
24
- private handleWebhook;
25
- private verifySignature;
26
- private decryptMessage;
27
- private parseXmlMessage;
28
- private handleMessage;
29
- private ensureAccessToken;
30
- private refreshAccessToken;
31
- $formatMessage(msg: WecomMessage): Message<WecomMessage>;
32
- private parseMessageContent;
33
- $sendMessage(options: SendOptions): Promise<string>;
34
- $recallMessage(_id: string): Promise<void>;
35
- private formatSendContent;
36
- $connect(): Promise<void>;
37
- $disconnect(): Promise<void>;
28
+ constructor(options: WecomEndpointOptions);
29
+ /** Used by webhook handler. */
30
+ get isOpen(): boolean;
31
+ get config(): ResolvedWecomConfig;
32
+ start(): Promise<void>;
33
+ open(): void;
34
+ close(): void;
35
+ stop(): Promise<void>;
36
+ send({ target, payload }: {
37
+ readonly target: string;
38
+ readonly payload: unknown;
39
+ }): Promise<string>;
40
+ /** Test / internal: admit a parsed message when open (non-webhook path). */
41
+ admit(msg: WecomMessage): void;
38
42
  getUserInfo(userId: string): Promise<WecomApiResponse | null>;
39
43
  getDepartmentUsers(deptId: number): Promise<unknown[]>;
40
44
  getDepartmentList(deptId?: number): Promise<unknown[]>;
41
45
  sendTextMessage(userId: string, content: string): Promise<boolean>;
42
46
  }
43
- //# sourceMappingURL=endpoint.d.ts.map