@wwlocal/aibot-plugin-node 20260409.20.0

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 (93) hide show
  1. package/README.md +489 -0
  2. package/config.example.json +169 -0
  3. package/dist/cjs/index.js +76 -0
  4. package/dist/cjs/src/adapters/anthropic-adapter.js +534 -0
  5. package/dist/cjs/src/adapters/base-adapter.js +176 -0
  6. package/dist/cjs/src/adapters/deepseek-adapter.js +328 -0
  7. package/dist/cjs/src/adapters/dify-adapter.js +636 -0
  8. package/dist/cjs/src/adapters/index.js +131 -0
  9. package/dist/cjs/src/adapters/openai-adapter.js +361 -0
  10. package/dist/cjs/src/adapters/webhook-adapter.js +260 -0
  11. package/dist/cjs/src/agent-forwarder.js +87 -0
  12. package/dist/cjs/src/ca-cert.js +162 -0
  13. package/dist/cjs/src/config.js +169 -0
  14. package/dist/cjs/src/const.js +124 -0
  15. package/dist/cjs/src/conversation-manager.js +147 -0
  16. package/dist/cjs/src/dm-policy.js +46 -0
  17. package/dist/cjs/src/group-policy.js +95 -0
  18. package/dist/cjs/src/media-handler.js +136 -0
  19. package/dist/cjs/src/media-loader.js +271 -0
  20. package/dist/cjs/src/media-storage.js +165 -0
  21. package/dist/cjs/src/media-uploader.js +203 -0
  22. package/dist/cjs/src/message-parser.js +133 -0
  23. package/dist/cjs/src/message-sender.js +87 -0
  24. package/dist/cjs/src/monitor.js +849 -0
  25. package/dist/cjs/src/reqid-store.js +87 -0
  26. package/dist/cjs/src/server.js +72 -0
  27. package/dist/cjs/src/service-manager.js +135 -0
  28. package/dist/cjs/src/state-manager.js +143 -0
  29. package/dist/cjs/src/template-card-parser.js +498 -0
  30. package/dist/cjs/src/timeout.js +41 -0
  31. package/dist/cjs/src/version.js +25 -0
  32. package/dist/esm/index.js +74 -0
  33. package/dist/esm/src/adapters/anthropic-adapter.js +512 -0
  34. package/dist/esm/src/adapters/base-adapter.js +174 -0
  35. package/dist/esm/src/adapters/deepseek-adapter.js +326 -0
  36. package/dist/esm/src/adapters/dify-adapter.js +634 -0
  37. package/dist/esm/src/adapters/index.js +123 -0
  38. package/dist/esm/src/adapters/openai-adapter.js +339 -0
  39. package/dist/esm/src/adapters/webhook-adapter.js +258 -0
  40. package/dist/esm/src/agent-forwarder.js +84 -0
  41. package/dist/esm/src/ca-cert.js +136 -0
  42. package/dist/esm/src/config.js +145 -0
  43. package/dist/esm/src/const.js +100 -0
  44. package/dist/esm/src/conversation-manager.js +144 -0
  45. package/dist/esm/src/dm-policy.js +44 -0
  46. package/dist/esm/src/group-policy.js +92 -0
  47. package/dist/esm/src/media-handler.js +133 -0
  48. package/dist/esm/src/media-loader.js +246 -0
  49. package/dist/esm/src/media-storage.js +143 -0
  50. package/dist/esm/src/media-uploader.js +198 -0
  51. package/dist/esm/src/message-parser.js +131 -0
  52. package/dist/esm/src/message-sender.js +83 -0
  53. package/dist/esm/src/monitor.js +841 -0
  54. package/dist/esm/src/reqid-store.js +85 -0
  55. package/dist/esm/src/server.js +69 -0
  56. package/dist/esm/src/service-manager.js +133 -0
  57. package/dist/esm/src/state-manager.js +134 -0
  58. package/dist/esm/src/template-card-parser.js +495 -0
  59. package/dist/esm/src/timeout.js +38 -0
  60. package/dist/esm/src/version.js +22 -0
  61. package/dist/esm/types/index.d.ts +14 -0
  62. package/dist/esm/types/src/adapters/anthropic-adapter.d.ts +93 -0
  63. package/dist/esm/types/src/adapters/base-adapter.d.ts +76 -0
  64. package/dist/esm/types/src/adapters/deepseek-adapter.d.ts +87 -0
  65. package/dist/esm/types/src/adapters/dify-adapter.d.ts +100 -0
  66. package/dist/esm/types/src/adapters/index.d.ts +60 -0
  67. package/dist/esm/types/src/adapters/openai-adapter.d.ts +82 -0
  68. package/dist/esm/types/src/adapters/types.d.ts +373 -0
  69. package/dist/esm/types/src/adapters/webhook-adapter.d.ts +54 -0
  70. package/dist/esm/types/src/agent-forwarder.d.ts +32 -0
  71. package/dist/esm/types/src/ca-cert.d.ts +53 -0
  72. package/dist/esm/types/src/config.d.ts +29 -0
  73. package/dist/esm/types/src/const.d.ts +74 -0
  74. package/dist/esm/types/src/conversation-manager.d.ts +81 -0
  75. package/dist/esm/types/src/dm-policy.d.ts +27 -0
  76. package/dist/esm/types/src/group-policy.d.ts +28 -0
  77. package/dist/esm/types/src/interface.d.ts +332 -0
  78. package/dist/esm/types/src/media-handler.d.ts +36 -0
  79. package/dist/esm/types/src/media-loader.d.ts +47 -0
  80. package/dist/esm/types/src/media-storage.d.ts +35 -0
  81. package/dist/esm/types/src/media-uploader.d.ts +65 -0
  82. package/dist/esm/types/src/message-parser.d.ts +89 -0
  83. package/dist/esm/types/src/message-sender.d.ts +34 -0
  84. package/dist/esm/types/src/monitor.d.ts +30 -0
  85. package/dist/esm/types/src/reqid-store.d.ts +23 -0
  86. package/dist/esm/types/src/server.d.ts +23 -0
  87. package/dist/esm/types/src/service-manager.d.ts +52 -0
  88. package/dist/esm/types/src/state-manager.d.ts +76 -0
  89. package/dist/esm/types/src/template-card-parser.d.ts +18 -0
  90. package/dist/esm/types/src/timeout.d.ts +20 -0
  91. package/dist/esm/types/src/version.d.ts +2 -0
  92. package/dist/index.d.ts +2 -0
  93. package/package.json +51 -0
@@ -0,0 +1,87 @@
1
+ 'use strict';
2
+
3
+ // ============================================================================
4
+ // 类型定义
5
+ // ============================================================================
6
+ // ============================================================================
7
+ // 常量
8
+ // ============================================================================
9
+ const DEFAULT_TTL_MS = 7 * 24 * 60 * 60 * 1000; // 7 天
10
+ const DEFAULT_MEMORY_MAX_SIZE = 200;
11
+ // ============================================================================
12
+ // 核心实现
13
+ // ============================================================================
14
+ function createPersistentReqIdStore(accountId, options) {
15
+ const ttlMs = DEFAULT_TTL_MS;
16
+ const memoryMaxSize = DEFAULT_MEMORY_MAX_SIZE;
17
+ // 内存层:chatId → ReqIdEntry
18
+ const memory = new Map();
19
+ // ========== 内部辅助函数 ==========
20
+ /** 检查条目是否过期 */
21
+ function isExpired(entry, now) {
22
+ return now - entry.ts >= ttlMs;
23
+ }
24
+ /**
25
+ * 内存容量控制:淘汰最旧的条目。
26
+ * 利用 Map 的插入顺序 + touch(先 delete 再 set) 实现类 LRU 效果。
27
+ */
28
+ function pruneMemory() {
29
+ if (memory.size <= memoryMaxSize)
30
+ return;
31
+ const sorted = [...memory.entries()].sort((a, b) => a[1].ts - b[1].ts);
32
+ const toRemove = sorted.slice(0, memory.size - memoryMaxSize);
33
+ for (const [key] of toRemove) {
34
+ memory.delete(key);
35
+ }
36
+ }
37
+ // ========== 公开 API ==========
38
+ function set(chatId, reqId) {
39
+ const entry = { reqId, ts: Date.now() };
40
+ // touch:先删再设,保持 Map 插入顺序(类 LRU)
41
+ memory.delete(chatId);
42
+ memory.set(chatId, entry);
43
+ pruneMemory();
44
+ }
45
+ async function get(chatId) {
46
+ const now = Date.now();
47
+ // 仅查内存
48
+ const memEntry = memory.get(chatId);
49
+ if (memEntry && !isExpired(memEntry, now)) {
50
+ return memEntry.reqId;
51
+ }
52
+ if (memEntry) {
53
+ memory.delete(chatId); // 过期则删除
54
+ }
55
+ return undefined;
56
+ }
57
+ function getSync(chatId) {
58
+ const now = Date.now();
59
+ const entry = memory.get(chatId);
60
+ if (entry && !isExpired(entry, now)) {
61
+ return entry.reqId;
62
+ }
63
+ if (entry) {
64
+ memory.delete(chatId);
65
+ }
66
+ return undefined;
67
+ }
68
+ function del(chatId) {
69
+ memory.delete(chatId);
70
+ }
71
+ function clearMemory() {
72
+ memory.clear();
73
+ }
74
+ function memorySize() {
75
+ return memory.size;
76
+ }
77
+ return {
78
+ set,
79
+ get,
80
+ getSync,
81
+ delete: del,
82
+ clearMemory,
83
+ memorySize,
84
+ };
85
+ }
86
+
87
+ exports.createPersistentReqIdStore = createPersistentReqIdStore;
@@ -0,0 +1,72 @@
1
+ 'use strict';
2
+
3
+ var express = require('express');
4
+ var version = require('./version.js');
5
+
6
+ /**
7
+ * Express HTTP 服务器模块
8
+ *
9
+ * 提供:
10
+ * - GET /health — 健康检查
11
+ * - GET /api/status — 服务状态查询(各账号连接状态/消息统计)
12
+ * - POST /callback — 模板卡片事件回调 webhook(预留)
13
+ */
14
+ /**
15
+ * 创建 Express 应用并注册路由
16
+ */
17
+ function createServer(options) {
18
+ const { serviceManager, runtime } = options;
19
+ const app = express();
20
+ // 解析 JSON body
21
+ app.use(express.json());
22
+ // ──────────────────────────────────────────────────────────────────────
23
+ // 健康检查
24
+ // ──────────────────────────────────────────────────────────────────────
25
+ app.get("/health", (_req, res) => {
26
+ res.json({
27
+ status: "ok",
28
+ version: version.PLUGIN_VERSION,
29
+ uptime: process.uptime(),
30
+ timestamp: Date.now(),
31
+ });
32
+ });
33
+ // ──────────────────────────────────────────────────────────────────────
34
+ // 服务状态查询
35
+ // ──────────────────────────────────────────────────────────────────────
36
+ app.get("/api/status", (_req, res) => {
37
+ const statuses = serviceManager.getStatuses();
38
+ res.json({
39
+ version: version.PLUGIN_VERSION,
40
+ accounts: statuses,
41
+ timestamp: Date.now(),
42
+ });
43
+ });
44
+ // ──────────────────────────────────────────────────────────────────────
45
+ // 模板卡片事件回调 webhook(预留)
46
+ // ──────────────────────────────────────────────────────────────────────
47
+ app.post("/callback", (req, res) => {
48
+ runtime.log?.(`[server] Received callback: ${JSON.stringify(req.body).slice(0, 500)}`);
49
+ // 回调事件已通过 WebSocket 事件监听处理,HTTP 回调为预留扩展接口
50
+ res.json({ errcode: 0, errmsg: "ok" });
51
+ });
52
+ // ──────────────────────────────────────────────────────────────────────
53
+ // 404 处理
54
+ // ──────────────────────────────────────────────────────────────────────
55
+ app.use((_req, res) => {
56
+ res.status(404).json({ error: "Not Found" });
57
+ });
58
+ return app;
59
+ }
60
+ /**
61
+ * 启动 HTTP 服务器
62
+ */
63
+ function startServer(app, port, runtime) {
64
+ app.listen(port, () => {
65
+ runtime.log?.(`[server] HTTP server listening on port ${port}`);
66
+ runtime.log?.(`[server] Health check: http://localhost:${port}/health`);
67
+ runtime.log?.(`[server] Status API: http://localhost:${port}/api/status`);
68
+ });
69
+ }
70
+
71
+ exports.createServer = createServer;
72
+ exports.startServer = startServer;
@@ -0,0 +1,135 @@
1
+ 'use strict';
2
+
3
+ var config = require('./config.js');
4
+ var monitor = require('./monitor.js');
5
+
6
+ /**
7
+ * 服务生命周期管理器
8
+ *
9
+ * 管理多账号 WSClient 的启动/停止/重连、状态监控、统一 cleanup。
10
+ */
11
+ // ============================================================================
12
+ // ServiceManager
13
+ // ============================================================================
14
+ class ServiceManager {
15
+ constructor(options) {
16
+ this.accounts = [];
17
+ this.accountStatuses = new Map();
18
+ this.abortControllers = new Map();
19
+ this.monitorPromises = new Map();
20
+ this.config = options.config;
21
+ this.runtime = options.runtime;
22
+ }
23
+ /**
24
+ * 启动所有已启用的账号
25
+ */
26
+ async startAll() {
27
+ this.accounts = config.resolveAllAccounts(this.config);
28
+ if (this.accounts.length === 0) {
29
+ this.runtime.error?.("[service-manager] No enabled accounts found in configuration");
30
+ return;
31
+ }
32
+ this.runtime.log?.(`[service-manager] Starting ${this.accounts.length} account(s)...`);
33
+ for (const account of this.accounts) {
34
+ this.startAccount(account);
35
+ }
36
+ }
37
+ /**
38
+ * 启动单个账号的 WebSocket 监控
39
+ */
40
+ startAccount(account) {
41
+ const { accountId, name } = account;
42
+ // 初始化状态
43
+ this.accountStatuses.set(accountId, {
44
+ accountId,
45
+ name,
46
+ running: false,
47
+ });
48
+ // 创建中止控制器
49
+ const abortController = new AbortController();
50
+ this.abortControllers.set(accountId, abortController);
51
+ // 构建 MonitorOptions
52
+ const monitorOptions = {
53
+ account,
54
+ config: this.config,
55
+ runtime: this.runtime,
56
+ abortSignal: abortController.signal,
57
+ setStatus: (next) => {
58
+ const current = this.accountStatuses.get(accountId);
59
+ if (current) {
60
+ Object.assign(current, next);
61
+ }
62
+ },
63
+ };
64
+ this.runtime.log?.(`[service-manager] Starting account: ${accountId} (${name})`);
65
+ // 启动监控
66
+ const promise = monitor.monitorWeComProvider(monitorOptions)
67
+ .then(() => {
68
+ this.runtime.log?.(`[service-manager] Account ${accountId} monitor resolved`);
69
+ })
70
+ .catch((err) => {
71
+ this.runtime.error?.(`[service-manager] Account ${accountId} monitor failed: ${String(err)}`);
72
+ const status = this.accountStatuses.get(accountId);
73
+ if (status) {
74
+ status.running = false;
75
+ status.lastError = String(err);
76
+ status.lastStopAt = Date.now();
77
+ }
78
+ // 网络断线重连耗尽后自动重试
79
+ this.runtime.log?.(`[service-manager] Will restart account ${accountId} in 30s...`);
80
+ setTimeout(() => {
81
+ if (!abortController.signal.aborted) {
82
+ this.restartAccount(accountId);
83
+ }
84
+ }, 30000);
85
+ });
86
+ this.monitorPromises.set(accountId, promise);
87
+ }
88
+ /**
89
+ * 重启指定账号
90
+ */
91
+ restartAccount(accountId) {
92
+ const account = this.accounts.find((a) => a.accountId === accountId);
93
+ if (!account) {
94
+ this.runtime.error?.(`[service-manager] Cannot restart: account ${accountId} not found`);
95
+ return;
96
+ }
97
+ // 清理旧的中止控制器
98
+ const oldController = this.abortControllers.get(accountId);
99
+ if (oldController && !oldController.signal.aborted) {
100
+ oldController.abort();
101
+ }
102
+ this.runtime.log?.(`[service-manager] Restarting account: ${accountId}`);
103
+ this.startAccount(account);
104
+ }
105
+ /**
106
+ * 停止所有账号
107
+ */
108
+ async stopAll() {
109
+ this.runtime.log?.("[service-manager] Stopping all accounts...");
110
+ for (const [accountId, controller] of this.abortControllers) {
111
+ this.runtime.log?.(`[service-manager] Aborting account: ${accountId}`);
112
+ controller.abort();
113
+ }
114
+ // 等待所有 monitor promises 结束(有超时保护)
115
+ const promises = [...this.monitorPromises.values()];
116
+ await Promise.allSettled(promises);
117
+ this.abortControllers.clear();
118
+ this.monitorPromises.clear();
119
+ this.runtime.log?.("[service-manager] All accounts stopped");
120
+ }
121
+ /**
122
+ * 获取所有账号状态
123
+ */
124
+ getStatuses() {
125
+ return [...this.accountStatuses.values()];
126
+ }
127
+ /**
128
+ * 获取指定账号状态
129
+ */
130
+ getStatus(accountId) {
131
+ return this.accountStatuses.get(accountId);
132
+ }
133
+ }
134
+
135
+ exports.ServiceManager = ServiceManager;
@@ -0,0 +1,143 @@
1
+ 'use strict';
2
+
3
+ var reqidStore = require('./reqid-store.js');
4
+ var _const = require('./const.js');
5
+
6
+ /**
7
+ * 全局状态管理模块
8
+ *
9
+ * 负责管理 WSClient 实例、消息状态(带 TTL 清理)、ReqId 存储
10
+ * 解决全局 Map 的内存泄漏问题
11
+ */
12
+ // ============================================================================
13
+ // WSClient 实例管理
14
+ // ============================================================================
15
+ /** WSClient 实例管理 */
16
+ const wsClientInstances = new Map();
17
+ /**
18
+ * 设置指定账户的 WSClient 实例
19
+ */
20
+ function setWeComWebSocket(accountId, client) {
21
+ wsClientInstances.set(accountId, client);
22
+ }
23
+ /** 消息状态管理 */
24
+ const messageStates = new Map();
25
+ /** 定期清理定时器 */
26
+ let cleanupTimer = null;
27
+ /**
28
+ * 启动消息状态定期清理(自动 TTL 清理 + 容量限制)
29
+ */
30
+ function startMessageStateCleanup() {
31
+ if (cleanupTimer)
32
+ return;
33
+ cleanupTimer = setInterval(() => {
34
+ pruneMessageStates();
35
+ }, _const.MESSAGE_STATE_CLEANUP_INTERVAL_MS);
36
+ // 允许进程退出时不阻塞
37
+ if (cleanupTimer && typeof cleanupTimer === "object" && "unref" in cleanupTimer) {
38
+ cleanupTimer.unref();
39
+ }
40
+ }
41
+ /**
42
+ * 停止消息状态定期清理
43
+ */
44
+ function stopMessageStateCleanup() {
45
+ if (cleanupTimer) {
46
+ clearInterval(cleanupTimer);
47
+ cleanupTimer = null;
48
+ }
49
+ }
50
+ /**
51
+ * 清理过期和超量的消息状态条目
52
+ */
53
+ function pruneMessageStates() {
54
+ const now = Date.now();
55
+ // 1. 清理过期条目
56
+ for (const [key, entry] of messageStates) {
57
+ if (now - entry.createdAt >= _const.MESSAGE_STATE_TTL_MS) {
58
+ messageStates.delete(key);
59
+ }
60
+ }
61
+ // 2. 容量限制:如果仍超过最大条目数,按时间淘汰最旧的
62
+ if (messageStates.size > _const.MESSAGE_STATE_MAX_SIZE) {
63
+ const sorted = [...messageStates.entries()].sort((a, b) => a[1].createdAt - b[1].createdAt);
64
+ const toRemove = sorted.slice(0, messageStates.size - _const.MESSAGE_STATE_MAX_SIZE);
65
+ for (const [key] of toRemove) {
66
+ messageStates.delete(key);
67
+ }
68
+ }
69
+ }
70
+ /**
71
+ * 设置消息状态
72
+ */
73
+ function setMessageState(messageId, state) {
74
+ messageStates.set(messageId, {
75
+ state,
76
+ createdAt: Date.now(),
77
+ });
78
+ }
79
+ /**
80
+ * 删除消息状态
81
+ */
82
+ function deleteMessageState(messageId) {
83
+ messageStates.delete(messageId);
84
+ }
85
+ // ============================================================================
86
+ // ReqId 持久化存储管理(按 accountId 隔离)
87
+ // ============================================================================
88
+ /**
89
+ * ReqId 存储管理(纯内存模式,LRU + TTL)
90
+ */
91
+ const reqIdStores = new Map();
92
+ function getOrCreateReqIdStore(accountId) {
93
+ let store = reqIdStores.get(accountId);
94
+ if (!store) {
95
+ store = reqidStore.createPersistentReqIdStore();
96
+ reqIdStores.set(accountId, store);
97
+ }
98
+ return store;
99
+ }
100
+ // ============================================================================
101
+ // ReqId 操作函数
102
+ // ============================================================================
103
+ /**
104
+ * 设置 chatId 对应的 reqId
105
+ */
106
+ function setReqIdForChat(chatId, reqId, accountId = "default") {
107
+ getOrCreateReqIdStore(accountId).set(chatId, reqId);
108
+ }
109
+ /**
110
+ * 启动时预热 reqId 缓存
111
+ */
112
+ async function warmupReqIdStore(accountId = "default", log) {
113
+ log?.("[wecom] reqid-store warmup: no-op (memory-only mode)");
114
+ return 0;
115
+ }
116
+ // ============================================================================
117
+ // 全局 cleanup(断开连接时释放所有资源)
118
+ // ============================================================================
119
+ /**
120
+ * 清理指定账户的所有资源
121
+ */
122
+ async function cleanupAccount(accountId) {
123
+ // 1. 断开 WSClient
124
+ const wsClient = wsClientInstances.get(accountId);
125
+ if (wsClient) {
126
+ try {
127
+ wsClient.disconnect();
128
+ }
129
+ catch {
130
+ // 忽略断开连接时的错误
131
+ }
132
+ wsClientInstances.delete(accountId);
133
+ }
134
+ }
135
+
136
+ exports.cleanupAccount = cleanupAccount;
137
+ exports.deleteMessageState = deleteMessageState;
138
+ exports.setMessageState = setMessageState;
139
+ exports.setReqIdForChat = setReqIdForChat;
140
+ exports.setWeComWebSocket = setWeComWebSocket;
141
+ exports.startMessageStateCleanup = startMessageStateCleanup;
142
+ exports.stopMessageStateCleanup = stopMessageStateCleanup;
143
+ exports.warmupReqIdStore = warmupReqIdStore;