@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.
- package/README.md +25 -18
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/{index.ts → dist/index.js} +33 -37
- package/dist/index.js.map +1 -0
- package/dist/src/auth.d.ts +25 -0
- package/dist/src/auth.d.ts.map +1 -0
- package/{src/auth.ts → dist/src/auth.js} +55 -79
- package/dist/src/auth.js.map +1 -0
- package/dist/src/channel.d.ts +13 -0
- package/dist/src/channel.d.ts.map +1 -0
- package/{src/channel.ts → dist/src/channel.js} +224 -368
- package/dist/src/channel.js.map +1 -0
- package/dist/src/connection.d.ts +51 -0
- package/dist/src/connection.d.ts.map +1 -0
- package/{src/connection.ts → dist/src/connection.js} +45 -122
- package/dist/src/connection.js.map +1 -0
- package/dist/src/group-history-tool.d.ts +23 -0
- package/dist/src/group-history-tool.d.ts.map +1 -0
- package/{src/group-history-tool.ts → dist/src/group-history-tool.js} +168 -204
- package/dist/src/group-history-tool.js.map +1 -0
- package/dist/src/inbound-media-local.d.ts +40 -0
- package/dist/src/inbound-media-local.d.ts.map +1 -0
- package/{src/inbound-media-local.ts → dist/src/inbound-media-local.js} +52 -90
- package/dist/src/inbound-media-local.js.map +1 -0
- package/dist/src/recommended-system-prompt.d.ts +3 -0
- package/dist/src/recommended-system-prompt.d.ts.map +1 -0
- package/dist/src/recommended-system-prompt.js +3 -0
- package/dist/src/recommended-system-prompt.js.map +1 -0
- package/dist/src/resource-file.d.ts +65 -0
- package/dist/src/resource-file.d.ts.map +1 -0
- package/{src/resource-file.ts → dist/src/resource-file.js} +317 -339
- package/dist/src/resource-file.js.map +1 -0
- package/dist/src/send-group-message-tool.d.ts +16 -0
- package/dist/src/send-group-message-tool.d.ts.map +1 -0
- package/{src/send-group-message-tool.ts → dist/src/send-group-message-tool.js} +17 -34
- package/dist/src/send-group-message-tool.js.map +1 -0
- package/dist/src/send-service.d.ts +34 -0
- package/dist/src/send-service.d.ts.map +1 -0
- package/dist/src/send-service.js +197 -0
- package/dist/src/send-service.js.map +1 -0
- package/{src/tool-json-result.ts → dist/src/tool-json-result.d.ts} +14 -24
- package/dist/src/tool-json-result.d.ts.map +1 -0
- package/dist/src/tool-json-result.js +20 -0
- package/dist/src/tool-json-result.js.map +1 -0
- package/{src/types.ts → dist/src/types.d.ts} +32 -96
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +32 -0
- package/dist/src/types.js.map +1 -0
- package/openclaw.plugin.json +9 -0
- package/package.json +7 -6
- package/src/recommended-system-prompt.ts +0 -3
- 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
|
|
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": { "
|
|
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": { "
|
|
184
|
-
"sales": { "
|
|
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": { "
|
|
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
|
-
| `
|
|
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`
|
|
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`
|
|
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
|
|
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
|
-
-
|
|
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. 通知同事执行 **更新已安装的插件**(见上文「更新已安装的插件」),例如:
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
config
|
|
31
|
-
log
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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"}
|