@xgjktech/xg_cwork_im 1.11.1 → 1.11.4

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 (53) hide show
  1. package/README.md +25 -18
  2. package/dist/index.d.ts +10 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/{index.ts → dist/index.js} +33 -37
  5. package/dist/index.js.map +1 -0
  6. package/dist/src/auth.d.ts +25 -0
  7. package/dist/src/auth.d.ts.map +1 -0
  8. package/{src/auth.ts → dist/src/auth.js} +55 -79
  9. package/dist/src/auth.js.map +1 -0
  10. package/dist/src/channel.d.ts +13 -0
  11. package/dist/src/channel.d.ts.map +1 -0
  12. package/{src/channel.ts → dist/src/channel.js} +224 -368
  13. package/dist/src/channel.js.map +1 -0
  14. package/dist/src/connection.d.ts +51 -0
  15. package/dist/src/connection.d.ts.map +1 -0
  16. package/{src/connection.ts → dist/src/connection.js} +45 -122
  17. package/dist/src/connection.js.map +1 -0
  18. package/dist/src/group-history-tool.d.ts +23 -0
  19. package/dist/src/group-history-tool.d.ts.map +1 -0
  20. package/{src/group-history-tool.ts → dist/src/group-history-tool.js} +168 -204
  21. package/dist/src/group-history-tool.js.map +1 -0
  22. package/dist/src/inbound-media-local.d.ts +40 -0
  23. package/dist/src/inbound-media-local.d.ts.map +1 -0
  24. package/{src/inbound-media-local.ts → dist/src/inbound-media-local.js} +52 -90
  25. package/dist/src/inbound-media-local.js.map +1 -0
  26. package/dist/src/recommended-system-prompt.d.ts +3 -0
  27. package/dist/src/recommended-system-prompt.d.ts.map +1 -0
  28. package/dist/src/recommended-system-prompt.js +3 -0
  29. package/dist/src/recommended-system-prompt.js.map +1 -0
  30. package/dist/src/resource-file.d.ts +65 -0
  31. package/dist/src/resource-file.d.ts.map +1 -0
  32. package/{src/resource-file.ts → dist/src/resource-file.js} +317 -339
  33. package/dist/src/resource-file.js.map +1 -0
  34. package/dist/src/send-group-message-tool.d.ts +16 -0
  35. package/dist/src/send-group-message-tool.d.ts.map +1 -0
  36. package/{src/send-group-message-tool.ts → dist/src/send-group-message-tool.js} +17 -34
  37. package/dist/src/send-group-message-tool.js.map +1 -0
  38. package/dist/src/send-service.d.ts +34 -0
  39. package/dist/src/send-service.d.ts.map +1 -0
  40. package/dist/src/send-service.js +197 -0
  41. package/dist/src/send-service.js.map +1 -0
  42. package/{src/tool-json-result.ts → dist/src/tool-json-result.d.ts} +14 -24
  43. package/dist/src/tool-json-result.d.ts.map +1 -0
  44. package/dist/src/tool-json-result.js +20 -0
  45. package/dist/src/tool-json-result.js.map +1 -0
  46. package/{src/types.ts → dist/src/types.d.ts} +32 -96
  47. package/dist/src/types.d.ts.map +1 -0
  48. package/dist/src/types.js +32 -0
  49. package/dist/src/types.js.map +1 -0
  50. package/openclaw.plugin.json +9 -0
  51. package/package.json +7 -6
  52. package/src/recommended-system-prompt.ts +0 -3
  53. package/src/send-service.ts +0 -228
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
  IM 系统 → WebSocket → 插件(本项目) → OpenClaw AI → IM 发送接口 → IM 系统
9
9
  ```
10
10
 
11
- 1. 插件启动时用 `appKey` 换取 `access_token` 和 `userId`
11
+ 1. 插件启动时用 `robotKey`(兼容旧字段 `appKey`)换取 `access_token` 和 `userId`
12
12
  2. 建立 WebSocket 长连接,接收 `robotMention` 事件
13
13
  3. 将收到的消息转发给 OpenClaw 处理
14
14
  4. OpenClaw AI 回复后,插件:
@@ -22,7 +22,7 @@ IM 系统 → WebSocket → 插件(本项目) → OpenClaw AI → IM
22
22
  ## 前置条件
23
23
 
24
24
  - 已安装 [OpenClaw](https://github.com/openclaw) (`>=2026.2.13`)
25
- - 已在 IM 后台注册机器人,获取到 `appKey`
25
+ - 已在 IM 后台注册机器人,获取到 `robotKey`(旧配置中的 `appKey` 仍兼容)
26
26
 
27
27
  ---
28
28
 
@@ -135,7 +135,7 @@ openclaw plugins enable xg_cwork_im
135
135
  "baseUrl": "https://cwork-web-test.xgjktech.com.cn",
136
136
  "wsBaseUrl": "wss://cwork-web-test.xgjktech.com.cn",
137
137
  "accounts": {
138
- "main": { "appKey": "你的机器人 appKey", "agentId": "main", "name": "个人助手" }
138
+ "main": { "robotKey": "你的机器人 robotKey", "agentId": "main", "name": "个人助手" }
139
139
  }
140
140
  }
141
141
  }
@@ -180,8 +180,8 @@ openclaw gateway restart
180
180
  // 沙箱 Agent 建议加:MediaPath 使用 /workspace/... 而不是 ./...(避免 read 按 cwd 解析失败)
181
181
  "inboundMediaSandboxRootPrefix": "/workspace",
182
182
  "accounts": {
183
- "main": { "appKey": "appKey_机器人A", "agentId": "main", "name": "个人助手" },
184
- "sales": { "appKey": "appKey_机器人B", "agentId": "sales", "name": "销售助手" }
183
+ "main": { "robotKey": "robotKey_机器人A", "agentId": "main", "name": "个人助手" },
184
+ "sales": { "robotKey": "robotKey_机器人B", "agentId": "sales", "name": "销售助手" }
185
185
  }
186
186
  }
187
187
  },
@@ -209,7 +209,7 @@ openclaw gateway restart
209
209
  // 按需启用:入站附件落盘到 workspace,见配置表 inboundMediaWorkspaceSubdir
210
210
  // "inboundMediaWorkspaceSubdir": "xg_im_inbound",
211
211
  "accounts": {
212
- "main": { "appKey": "你的生产 appKey", "agentId": "main", "name": "个人助手" }
212
+ "main": { "robotKey": "你的生产 robotKey", "agentId": "main", "name": "个人助手" }
213
213
  }
214
214
  }
215
215
  }
@@ -242,7 +242,7 @@ openclaw gateway restart
242
242
 
243
243
  | 选项 | 类型 | 默认值 | 说明 |
244
244
  |---|---|---|---|
245
- | `appKey` | string | **必填** | 机器人 appKey(IM 后台注册获取) |
245
+ | `robotKey` | string | **必填** | 机器人标识(IM 后台注册获取);旧字段 `appKey` 仍兼容 |
246
246
  | `agentId` | string | `"main"` | 对应的 OpenClaw Agent ID |
247
247
  | `name` | string | — | 账户显示名称(仅用于日志标识) |
248
248
  | `groupPolicy` | string | 继承顶层 | 可覆盖顶层的 groupPolicy |
@@ -255,7 +255,7 @@ openclaw gateway restart
255
255
  ## 故障排除
256
256
 
257
257
  ### 收不到消息
258
- - 确认 `appKey` 正确,且机器人已在 IM 后台激活
258
+ - 确认 `robotKey` 正确(或旧字段 `appKey` 正确),且机器人已在 IM 后台激活
259
259
  - 查看 OpenClaw 日志,确认 WebSocket 连接成功(`WebSocket connected successfully`)
260
260
  - 群聊中确认已 `@` 机器人
261
261
 
@@ -266,7 +266,7 @@ openclaw gateway restart
266
266
 
267
267
  ### token 相关报错
268
268
  - 确认 `baseUrl` 域名可达
269
- - 确认 `appKey` 有效(可在 IM 后台查看机器人状态)
269
+ - 确认 `robotKey` 有效(或旧字段 `appKey` 有效,可在 IM 后台查看机器人状态)
270
270
 
271
271
  ---
272
272
 
@@ -277,7 +277,7 @@ openclaw-channel-xg-cwork-im/
277
277
  ├── index.ts # 插件入口
278
278
  ├── src/
279
279
  │ ├── types.ts # 类型定义
280
- │ ├── auth.ts # 认证(appKey → token)
280
+ │ ├── auth.ts # 认证(robotKey/appKey → token)
281
281
  │ ├── connection.ts # WebSocket 连接管理
282
282
  │ ├── send-service.ts # IM 消息发送
283
283
  │ ├── group-history-tool.ts # 拉取群消息历史的 Agent Tool
@@ -304,19 +304,26 @@ openclaw-channel-xg-cwork-im/
304
304
 
305
305
  代码在内部 Git 上维护,发布到 npm 后同事即可一条命令安装。按下列步骤操作。
306
306
 
307
- ### 步骤 1:在 npm 注册 / 登录
307
+ ### 步骤 1:在 npm 注册 / 登录(仅本次发布走官方)
308
+
309
+ 你可以保留系统默认镜像不变;只在本项目发布时显式指定官方 registry。
308
310
 
309
311
  1. 无 npm 账号:打开 [https://www.npmjs.com/signup](https://www.npmjs.com/signup) 注册。
310
- 2. 在项目根目录执行:
312
+ 2. 在项目根目录执行(不会改全局配置):
313
+
314
+ ```bash
315
+ npm login --registry=https://registry.npmjs.org/
316
+ ```
317
+
318
+ 3. 登录成功后校验当前身份(同样只看官方 registry):
311
319
 
312
320
  ```bash
313
- npm login
321
+ npm whoami --registry=https://registry.npmjs.org/
314
322
  ```
315
323
 
316
- 3. 按提示输入 **Username**、**Password**、**Email**。
317
324
  4. 若账号开启了**双因素认证(2FA)**,发布时必须使用:
318
325
  - 在 npm 网站 **Access Tokens** 里创建 **Granular Access Token**,勾选 **Publish packages**,并勾选 **Bypass 2FA**;或
319
- - 在命令行登录时使用 **OTP**(一次性密码)。
326
+ - 在发布时输入 **OTP**(一次性密码)。
320
327
  否则会报错:`Two-factor authentication or granular access token with bypass 2fa enabled is required to publish packages`。
321
328
 
322
329
  ### 步骤 2:确认 scope 与权限
@@ -335,12 +342,12 @@ npm pack --dry-run
335
342
 
336
343
  确认列出的文件无敏感内容(如 `.env`、密钥)。发布内容由 `package.json` 的 `"files"` 控制。
337
344
 
338
- ### 步骤 4:执行发布
345
+ ### 步骤 4:执行发布(强制官方 registry)
339
346
 
340
347
  **scoped 包必须加 `--access public`**,否则会按私有包计费并可能报 402:
341
348
 
342
349
  ```bash
343
- npm publish --access public
350
+ npm publish --access public --registry=https://registry.npmjs.org/
344
351
  ```
345
352
 
346
353
  成功后可在此查看:
@@ -355,7 +362,7 @@ npm publish --access public
355
362
  3. 在项目根目录执行:
356
363
 
357
364
  ```bash
358
- npm publish --access public
365
+ npm publish --access public --registry=https://registry.npmjs.org/
359
366
  ```
360
367
 
361
368
  4. 通知同事执行 **更新已安装的插件**(见上文「更新已安装的插件」),例如:
@@ -0,0 +1,10 @@
1
+ /**
2
+ * XG-IM Channel Plugin for OpenClaw
3
+ *
4
+ * 插件入口:注册 XG-IM channel,并注入 PluginRuntime 供消息路由使用
5
+ */
6
+ import type { XgCworkImPluginModule } from "./src/types.js";
7
+ export { XG_IM_RECOMMENDED_CHANNEL_SYSTEM_PROMPT } from "./src/recommended-system-prompt.js";
8
+ declare const plugin: XgCworkImPluginModule;
9
+ export default plugin;
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAE5D,OAAO,EAAE,uCAAuC,EAAE,MAAM,oCAAoC,CAAC;AAE7F,QAAA,MAAM,MAAM,EAAE,qBAmBb,CAAC;AAEF,eAAe,MAAM,CAAC"}
@@ -1,37 +1,33 @@
1
- /**
2
- * XG-IM Channel Plugin for OpenClaw
3
- *
4
- * 插件入口:注册 XG-IM channel,并注入 PluginRuntime 供消息路由使用
5
- */
6
-
7
- import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
8
- import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
9
- import { setXgImRuntime, xgCworkImChannelPlugin } from "./src/channel.js";
10
- import { buildGroupHistoryTool, extractXgCworkImConfig } from "./src/group-history-tool.js";
11
- import { buildSendGroupMessageTool } from "./src/send-group-message-tool.js";
12
- import type { XgCworkImPluginModule } from "./src/types.js";
13
-
14
- export { XG_IM_RECOMMENDED_CHANNEL_SYSTEM_PROMPT } from "./src/recommended-system-prompt.js";
15
-
16
- const plugin: XgCworkImPluginModule = {
17
- id: "xg_cwork_im",
18
- name: "XG CWork IM Channel",
19
- description: "工作说说 IM 机器人 Channel 插件,通过 WebSocket 长连接接入",
20
- configSchema: emptyPluginConfigSchema(),
21
- register(api: OpenClawPluginApi): void {
22
- setXgImRuntime(api.runtime);
23
- api.registerChannel({ plugin: xgCworkImChannelPlugin });
24
- // 注册 AI 工具(静态工具对象,确保 toolNames 正确注册)
25
- const cworkConfig = extractXgCworkImConfig(api.config);
26
- if (cworkConfig) {
27
- // 工具涉及网络请求 / 副作用,按文档要求标记为 optional,由用户在 Agent 的 tools.allow 中显式启用
28
- api.registerTool(buildGroupHistoryTool(cworkConfig), { optional: true });
29
- // 主动推送(Cron/定时等)向 IM 发纯文本;用户经 IM 对话时不用此工具
30
- api.registerTool(buildSendGroupMessageTool(cworkConfig), { optional: true });
31
- } else {
32
- console.warn("[xg_cwork_im] channel config not found, skipping tool registration");
33
- }
34
- },
35
- };
36
-
37
- export default plugin;
1
+ /**
2
+ * XG-IM Channel Plugin for OpenClaw
3
+ *
4
+ * 插件入口:注册 XG-IM channel,并注入 PluginRuntime 供消息路由使用
5
+ */
6
+ import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
7
+ import { setXgImRuntime, xgCworkImChannelPlugin } from "./src/channel.js";
8
+ import { buildGroupHistoryTool, extractXgCworkImConfig } from "./src/group-history-tool.js";
9
+ import { buildSendGroupMessageTool } from "./src/send-group-message-tool.js";
10
+ export { XG_IM_RECOMMENDED_CHANNEL_SYSTEM_PROMPT } from "./src/recommended-system-prompt.js";
11
+ const plugin = {
12
+ id: "xg_cwork_im",
13
+ name: "XG CWork IM Channel",
14
+ description: "工作说说 IM 机器人 Channel 插件,通过 WebSocket 长连接接入",
15
+ configSchema: emptyPluginConfigSchema(),
16
+ register(api) {
17
+ setXgImRuntime(api.runtime);
18
+ api.registerChannel({ plugin: xgCworkImChannelPlugin });
19
+ // 注册 AI 工具(静态工具对象,确保 toolNames 正确注册)
20
+ const cworkConfig = extractXgCworkImConfig(api.config);
21
+ if (cworkConfig) {
22
+ // 工具涉及网络请求 / 副作用,按文档要求标记为 optional,由用户在 Agent 的 tools.allow 中显式启用
23
+ api.registerTool(buildGroupHistoryTool(cworkConfig), { optional: true });
24
+ // 主动推送(Cron/定时等)向 IM 发纯文本;用户经 IM 对话时不用此工具
25
+ api.registerTool(buildSendGroupMessageTool(cworkConfig), { optional: true });
26
+ }
27
+ else {
28
+ console.warn("[xg_cwork_im] channel config not found, skipping tool registration");
29
+ }
30
+ },
31
+ };
32
+ export default plugin;
33
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAC5F,OAAO,EAAE,yBAAyB,EAAE,MAAM,kCAAkC,CAAC;AAG7E,OAAO,EAAE,uCAAuC,EAAE,MAAM,oCAAoC,CAAC;AAE7F,MAAM,MAAM,GAA0B;IAClC,EAAE,EAAE,aAAa;IACjB,IAAI,EAAE,qBAAqB;IAC3B,WAAW,EAAE,2CAA2C;IACxD,YAAY,EAAE,uBAAuB,EAAE;IACvC,QAAQ,CAAC,GAAsB;QAC3B,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC5B,GAAG,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,sBAAsB,EAAE,CAAC,CAAC;QACxD,qCAAqC;QACrC,MAAM,WAAW,GAAG,sBAAsB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACvD,IAAI,WAAW,EAAE,CAAC;YACd,kEAAkE;YAClE,GAAG,CAAC,YAAY,CAAC,qBAAqB,CAAC,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;YACzE,0CAA0C;YAC1C,GAAG,CAAC,YAAY,CAAC,yBAAyB,CAAC,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QACjF,CAAC;aAAM,CAAC;YACJ,OAAO,CAAC,IAAI,CAAC,oEAAoE,CAAC,CAAC;QACvF,CAAC;IACL,CAAC;CACJ,CAAC;AAEF,eAAe,MAAM,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * XG-IM 认证模块
3
+ *
4
+ * 通过 appKey 换取 access_token 和机器人 userId。
5
+ * token 有效期一年,启动时获取一次并缓存,无需定期刷新。
6
+ */
7
+ import type { BotIdentity, XgImConfig } from "./types.js";
8
+ export type Log = {
9
+ info: (msg: string) => void;
10
+ warn?: (msg: string) => void;
11
+ error: (msg: string) => void;
12
+ debug?: (msg: string) => void;
13
+ };
14
+ /**
15
+ * 通过 appKey 获取机器人身份信息(token + userId)。
16
+ * 若缓存中已有有效 token,直接返回缓存;否则请求接口。
17
+ *
18
+ * @param config 插件配置
19
+ * @param log 日志接口
20
+ * @param force 强制重新获取(忽略缓存)
21
+ */
22
+ export declare function getToken(config: XgImConfig, log?: Log, force?: boolean): Promise<BotIdentity>;
23
+ /** 清除指定账户的 token 缓存(用于重连时强制重新鉴权) */
24
+ export declare function clearTokenCache(config: XgImConfig): void;
25
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/auth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAoB,UAAU,EAAE,MAAM,YAAY,CAAC;AAE5E,MAAM,MAAM,GAAG,GAAG;IACd,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC,CAAC;AAKF;;;;;;;GAOG;AACH,wBAAsB,QAAQ,CAC1B,MAAM,EAAE,UAAU,EAClB,GAAG,CAAC,EAAE,GAAG,EACT,KAAK,UAAQ,GACd,OAAO,CAAC,WAAW,CAAC,CAwCtB;AAED,oCAAoC;AACpC,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAGxD"}
@@ -1,79 +1,55 @@
1
- /**
2
- * XG-IM 认证模块
3
- *
4
- * 通过 appKey 换取 access_token 和机器人 userId。
5
- * token 有效期一年,启动时获取一次并缓存,无需定期刷新。
6
- */
7
-
8
- import axios from "axios";
9
- import type { BotIdentity, GetTokenResponse, XgImConfig } from "./types.js";
10
-
11
- export type Log = {
12
- info: (msg: string) => void;
13
- warn?: (msg: string) => void;
14
- error: (msg: string) => void;
15
- debug?: (msg: string) => void;
16
- };
17
-
18
- /** 运行时 token 缓存,按 appKey 索引 */
19
- const tokenCache = new Map<string, BotIdentity>();
20
-
21
- /**
22
- * 通过 appKey 获取机器人身份信息(token + userId)。
23
- * 若缓存中已有有效 token,直接返回缓存;否则请求接口。
24
- *
25
- * @param config 插件配置
26
- * @param log 日志接口
27
- * @param force 强制重新获取(忽略缓存)
28
- */
29
- export async function getToken(
30
- config: XgImConfig,
31
- log?: Log,
32
- force = false,
33
- ): Promise<BotIdentity> {
34
- if (!config.appKey) {
35
- throw new Error("[cwork_im] appKey is required");
36
- }
37
- const cacheKey = `${config.baseUrl}:${config.appKey}`;
38
-
39
- if (!force) {
40
- const cached = tokenCache.get(cacheKey);
41
- if (cached) {
42
- log?.info?.(`[cwork_im] Using cached token for appKey=${config.appKey}`);
43
- return cached;
44
- }
45
- }
46
-
47
- const url = `${config.baseUrl}/user/login/appkey?appKey=${encodeURIComponent(config.appKey)}&appCode=im`;
48
- log?.info(`[cwork_im:auth] GET ${url} appKey=${config.appKey}`);
49
-
50
- const response = await axios.get<GetTokenResponse>(url, {
51
- headers: { "Content-Type": "application/json" },
52
- timeout: 10_000,
53
- });
54
-
55
- const body = response.data;
56
- log?.info(`[cwork_im:auth] Response status=${response.status} resultCode=${body?.resultCode ?? "n/a"}`);
57
- if (!body?.data?.xgToken || !body?.data?.empId) {
58
- throw new Error(
59
- `[cwork_im] getToken failed: unexpected response — ${JSON.stringify(body)}`,
60
- );
61
- }
62
-
63
- const identity: BotIdentity = {
64
- token: body.data.xgToken,
65
- userId: body.data.empId,
66
- name: body.data.userName ?? "bot",
67
- };
68
-
69
- tokenCache.set(cacheKey, identity);
70
- log?.info(`[cwork_im:auth] Token acquired: userId=${identity.userId} name=${identity.name} appKey=${config.appKey}`);
71
-
72
- return identity;
73
- }
74
-
75
- /** 清除指定账户的 token 缓存(用于重连时强制重新鉴权) */
76
- export function clearTokenCache(config: XgImConfig): void {
77
- const cacheKey = `${config.baseUrl}:${config.appKey}`;
78
- tokenCache.delete(cacheKey);
79
- }
1
+ /**
2
+ * XG-IM 认证模块
3
+ *
4
+ * 通过 appKey 换取 access_token 和机器人 userId。
5
+ * token 有效期一年,启动时获取一次并缓存,无需定期刷新。
6
+ */
7
+ import axios from "axios";
8
+ /** 运行时 token 缓存,按 appKey 索引 */
9
+ const tokenCache = new Map();
10
+ /**
11
+ * 通过 appKey 获取机器人身份信息(token + userId)。
12
+ * 若缓存中已有有效 token,直接返回缓存;否则请求接口。
13
+ *
14
+ * @param config 插件配置
15
+ * @param log 日志接口
16
+ * @param force 强制重新获取(忽略缓存)
17
+ */
18
+ export async function getToken(config, log, force = false) {
19
+ if (!config.appKey) {
20
+ throw new Error("[cwork_im] appKey is required");
21
+ }
22
+ const cacheKey = `${config.baseUrl}:${config.appKey}`;
23
+ if (!force) {
24
+ const cached = tokenCache.get(cacheKey);
25
+ if (cached) {
26
+ log?.info?.(`[cwork_im] Using cached token for appKey=${config.appKey}`);
27
+ return cached;
28
+ }
29
+ }
30
+ const url = `${config.baseUrl}/user/login/appkey?appKey=${encodeURIComponent(config.appKey)}&appCode=im`;
31
+ log?.info(`[cwork_im:auth] GET ${url} appKey=${config.appKey}`);
32
+ const response = await axios.get(url, {
33
+ headers: { "Content-Type": "application/json" },
34
+ timeout: 10_000,
35
+ });
36
+ const body = response.data;
37
+ log?.info(`[cwork_im:auth] Response status=${response.status} resultCode=${body?.resultCode ?? "n/a"}`);
38
+ if (!body?.data?.xgToken || !body?.data?.empId) {
39
+ throw new Error(`[cwork_im] getToken failed: unexpected response — ${JSON.stringify(body)}`);
40
+ }
41
+ const identity = {
42
+ token: body.data.xgToken,
43
+ userId: body.data.empId,
44
+ name: body.data.userName ?? "bot",
45
+ };
46
+ tokenCache.set(cacheKey, identity);
47
+ log?.info(`[cwork_im:auth] Token acquired: userId=${identity.userId} name=${identity.name} appKey=${config.appKey}`);
48
+ return identity;
49
+ }
50
+ /** 清除指定账户的 token 缓存(用于重连时强制重新鉴权) */
51
+ export function clearTokenCache(config) {
52
+ const cacheKey = `${config.baseUrl}:${config.appKey}`;
53
+ tokenCache.delete(cacheKey);
54
+ }
55
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/auth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAU1B,+BAA+B;AAC/B,MAAM,UAAU,GAAG,IAAI,GAAG,EAAuB,CAAC;AAElD;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC1B,MAAkB,EAClB,GAAS,EACT,KAAK,GAAG,KAAK;IAEb,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;IACrD,CAAC;IACD,MAAM,QAAQ,GAAG,GAAG,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;IAEtD,IAAI,CAAC,KAAK,EAAE,CAAC;QACT,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACxC,IAAI,MAAM,EAAE,CAAC;YACT,GAAG,EAAE,IAAI,EAAE,CAAC,4CAA4C,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;YACzE,OAAO,MAAM,CAAC;QAClB,CAAC;IACL,CAAC;IAED,MAAM,GAAG,GAAG,GAAG,MAAM,CAAC,OAAO,6BAA6B,kBAAkB,CAAC,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC;IACzG,GAAG,EAAE,IAAI,CAAC,uBAAuB,GAAG,WAAW,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IAEhE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAmB,GAAG,EAAE;QACpD,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,OAAO,EAAE,MAAM;KAClB,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC;IAC3B,GAAG,EAAE,IAAI,CAAC,mCAAmC,QAAQ,CAAC,MAAM,eAAe,IAAI,EAAE,UAAU,IAAI,KAAK,EAAE,CAAC,CAAC;IACxG,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CACX,qDAAqD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAC9E,CAAC;IACN,CAAC;IAED,MAAM,QAAQ,GAAgB;QAC1B,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO;QACxB,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK;QACvB,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,KAAK;KACpC,CAAC;IAEF,UAAU,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACnC,GAAG,EAAE,IAAI,CAAC,0CAA0C,QAAQ,CAAC,MAAM,SAAS,QAAQ,CAAC,IAAI,WAAW,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IAErH,OAAO,QAAQ,CAAC;AACpB,CAAC;AAED,oCAAoC;AACpC,MAAM,UAAU,eAAe,CAAC,MAAkB;IAC9C,MAAM,QAAQ,GAAG,GAAG,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;IACtD,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;AAChC,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * XG-IM Channel Plugin — 核心定义
3
+ *
4
+ * 遵循 OpenClaw Channel Plugin 架构(参考 openclaw-channel-dingtalk):
5
+ * - config : 账户解析
6
+ * - outbound : AI 回复发送
7
+ * - gateway : 启动 WebSocket 长连接,接收 robotMention 消息后通过
8
+ * PluginRuntime 路由给 OpenClaw 处理
9
+ */
10
+ import { type PluginRuntime, type XgImChannelPlugin } from "./types.js";
11
+ export declare function setXgImRuntime(rt: PluginRuntime): void;
12
+ export declare const xgCworkImChannelPlugin: XgImChannelPlugin;
13
+ //# sourceMappingURL=channel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../src/channel.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAUH,OAAO,EAKH,KAAK,aAAa,EAKlB,KAAK,iBAAiB,EAEzB,MAAM,YAAY,CAAC;AAMpB,wBAAgB,cAAc,CAAC,EAAE,EAAE,aAAa,GAAG,IAAI,CAEtD;AAujBD,eAAO,MAAM,sBAAsB,EAAE,iBA+XpC,CAAC"}