@wecom/wecom-openclaw-plugin 2026.3.24-beta.0 → 2026.3.24-beta.1

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/dist/index.cjs.js CHANGED
@@ -11,8 +11,6 @@ var path = require('node:path');
11
11
  var os = require('node:os');
12
12
  var node_url = require('node:url');
13
13
  var fileType = require('file-type');
14
- var jsonStore = require('openclaw/plugin-sdk/json-store');
15
- var infraRuntime = require('openclaw/plugin-sdk/infra-runtime');
16
14
  var node_fs = require('node:fs');
17
15
  var setup = require('openclaw/plugin-sdk/setup');
18
16
 
@@ -1202,80 +1200,27 @@ async function checkDmPolicy(params) {
1202
1200
  return { allowed: false };
1203
1201
  }
1204
1202
 
1203
+ // ============================================================================
1204
+ // 类型定义
1205
+ // ============================================================================
1205
1206
  // ============================================================================
1206
1207
  // 常量
1207
1208
  // ============================================================================
1208
1209
  const DEFAULT_TTL_MS = 7 * 24 * 60 * 60 * 1000; // 7 天
1209
1210
  const DEFAULT_MEMORY_MAX_SIZE = 200;
1210
- const DEFAULT_FILE_MAX_ENTRIES = 500;
1211
- const DEFAULT_FLUSH_DEBOUNCE_MS = 1000;
1212
- const DEFAULT_LOCK_OPTIONS = {
1213
- stale: 60000,
1214
- retries: {
1215
- retries: 6,
1216
- factor: 1.35,
1217
- minTimeout: 8,
1218
- maxTimeout: 180,
1219
- randomize: true,
1220
- },
1221
- };
1222
- // ============================================================================
1223
- // 状态目录解析
1224
- // ============================================================================
1225
- function resolveStateDirFromEnv(env = process.env) {
1226
- const stateOverride = env.OPENCLAW_STATE_DIR?.trim() || env.CLAWDBOT_STATE_DIR?.trim();
1227
- if (stateOverride) {
1228
- return stateOverride;
1229
- }
1230
- if (env.VITEST || env.NODE_ENV === "test") {
1231
- return path.join(os.tmpdir(), ["openclaw-vitest", String(process.pid)].join("-"));
1232
- }
1233
- return path.join(os.homedir(), ".openclaw");
1234
- }
1235
- function resolveReqIdFilePath(accountId) {
1236
- const safe = accountId.replace(/[^a-zA-Z0-9_-]/g, "_");
1237
- return path.join(resolveStateDirFromEnv(), "wecom", `reqid-map-${safe}.json`);
1238
- }
1239
1211
  // ============================================================================
1240
1212
  // 核心实现
1241
1213
  // ============================================================================
1242
1214
  function createPersistentReqIdStore(accountId, options) {
1243
1215
  const ttlMs = DEFAULT_TTL_MS;
1244
1216
  const memoryMaxSize = DEFAULT_MEMORY_MAX_SIZE;
1245
- const fileMaxEntries = DEFAULT_FILE_MAX_ENTRIES;
1246
- const flushDebounceMs = DEFAULT_FLUSH_DEBOUNCE_MS;
1247
- const filePath = resolveReqIdFilePath(accountId);
1248
1217
  // 内存层:chatId → ReqIdEntry
1249
1218
  const memory = new Map();
1250
- // 防抖写入相关
1251
- let dirty = false;
1252
- let flushTimer = null;
1253
1219
  // ========== 内部辅助函数 ==========
1254
1220
  /** 检查条目是否过期 */
1255
1221
  function isExpired(entry, now) {
1256
1222
  return now - entry.ts >= ttlMs;
1257
1223
  }
1258
- /** 验证磁盘条目的合法性 */
1259
- function isValidEntry(entry) {
1260
- return (typeof entry === "object" &&
1261
- entry !== null &&
1262
- typeof entry.reqId === "string" &&
1263
- typeof entry.ts === "number" &&
1264
- Number.isFinite(entry.ts));
1265
- }
1266
- /** 清理磁盘数据中的无效值,返回干净的 Record */
1267
- function sanitizeData(value) {
1268
- if (!value || typeof value !== "object") {
1269
- return {};
1270
- }
1271
- const out = {};
1272
- for (const [key, entry] of Object.entries(value)) {
1273
- if (isValidEntry(entry)) {
1274
- out[key] = entry;
1275
- }
1276
- }
1277
- return out;
1278
- }
1279
1224
  /**
1280
1225
  * 内存容量控制:淘汰最旧的条目。
1281
1226
  * 利用 Map 的插入顺序 + touch(先 delete 再 set) 实现类 LRU 效果。
@@ -1289,61 +1234,6 @@ function createPersistentReqIdStore(accountId, options) {
1289
1234
  memory.delete(key);
1290
1235
  }
1291
1236
  }
1292
- /** 磁盘数据容量控制:先清过期,再按时间淘汰超量 */
1293
- function pruneFileData(data, now) {
1294
- {
1295
- for (const [key, entry] of Object.entries(data)) {
1296
- if (now - entry.ts >= ttlMs) {
1297
- delete data[key];
1298
- }
1299
- }
1300
- }
1301
- const keys = Object.keys(data);
1302
- if (keys.length <= fileMaxEntries)
1303
- return;
1304
- keys
1305
- .sort((a, b) => data[a].ts - data[b].ts)
1306
- .slice(0, keys.length - fileMaxEntries)
1307
- .forEach((key) => delete data[key]);
1308
- }
1309
- /** 防抖写入磁盘 */
1310
- function scheduleDiskFlush() {
1311
- dirty = true;
1312
- if (flushTimer)
1313
- return;
1314
- flushTimer = setTimeout(async () => {
1315
- flushTimer = null;
1316
- if (!dirty)
1317
- return;
1318
- await flushToDisk();
1319
- }, flushDebounceMs);
1320
- }
1321
- /** 立即写入磁盘(带文件锁,参考 createPersistentDedupe 的 checkAndRecordInner) */
1322
- async function flushToDisk() {
1323
- dirty = false;
1324
- const now = Date.now();
1325
- try {
1326
- await infraRuntime.withFileLock(filePath, DEFAULT_LOCK_OPTIONS, async () => {
1327
- // 读取现有磁盘数据并合并
1328
- const { value } = await jsonStore.readJsonFileWithFallback(filePath, {});
1329
- const data = sanitizeData(value);
1330
- // 将内存中未过期的数据合并到磁盘数据(内存优先)
1331
- for (const [chatId, entry] of memory) {
1332
- if (!isExpired(entry, now)) {
1333
- data[chatId] = entry;
1334
- }
1335
- }
1336
- // 清理过期和超量
1337
- pruneFileData(data, now);
1338
- // 原子写入
1339
- await jsonStore.writeJsonFileAtomically(filePath, data);
1340
- });
1341
- }
1342
- catch (error) {
1343
- // 磁盘写入失败不影响内存使用,降级到纯内存模式
1344
- // console.error(`[WeCom] reqid-store: flush to disk failed: ${String(error)}`);
1345
- }
1346
- }
1347
1237
  // ========== 公开 API ==========
1348
1238
  function set(chatId, reqId) {
1349
1239
  const entry = { reqId, ts: Date.now() };
@@ -1351,11 +1241,10 @@ function createPersistentReqIdStore(accountId, options) {
1351
1241
  memory.delete(chatId);
1352
1242
  memory.set(chatId, entry);
1353
1243
  pruneMemory();
1354
- scheduleDiskFlush();
1355
1244
  }
1356
1245
  async function get(chatId) {
1357
1246
  const now = Date.now();
1358
- // 1. 先查内存
1247
+ // 仅查内存
1359
1248
  const memEntry = memory.get(chatId);
1360
1249
  if (memEntry && !isExpired(memEntry, now)) {
1361
1250
  return memEntry.reqId;
@@ -1363,20 +1252,6 @@ function createPersistentReqIdStore(accountId, options) {
1363
1252
  if (memEntry) {
1364
1253
  memory.delete(chatId); // 过期则删除
1365
1254
  }
1366
- // 2. 内存 miss,回查磁盘并回填内存
1367
- try {
1368
- const { value } = await jsonStore.readJsonFileWithFallback(filePath, {});
1369
- const data = sanitizeData(value);
1370
- const diskEntry = data[chatId];
1371
- if (diskEntry && !isExpired(diskEntry, now)) {
1372
- // 回填内存
1373
- memory.set(chatId, diskEntry);
1374
- return diskEntry.reqId;
1375
- }
1376
- }
1377
- catch {
1378
- // 磁盘读取失败,降级返回 undefined
1379
- }
1380
1255
  return undefined;
1381
1256
  }
1382
1257
  function getSync(chatId) {
@@ -1392,34 +1267,6 @@ function createPersistentReqIdStore(accountId, options) {
1392
1267
  }
1393
1268
  function del(chatId) {
1394
1269
  memory.delete(chatId);
1395
- scheduleDiskFlush();
1396
- }
1397
- async function warmup(onError) {
1398
- const now = Date.now();
1399
- try {
1400
- const { value } = await jsonStore.readJsonFileWithFallback(filePath, {});
1401
- const data = sanitizeData(value);
1402
- let loaded = 0;
1403
- for (const [chatId, entry] of Object.entries(data)) {
1404
- if (!isExpired(entry, now)) {
1405
- memory.set(chatId, entry);
1406
- loaded++;
1407
- }
1408
- }
1409
- pruneMemory();
1410
- return loaded;
1411
- }
1412
- catch (error) {
1413
- onError?.(error);
1414
- return 0;
1415
- }
1416
- }
1417
- async function flush() {
1418
- if (flushTimer) {
1419
- clearTimeout(flushTimer);
1420
- flushTimer = null;
1421
- }
1422
- await flushToDisk();
1423
1270
  }
1424
1271
  function clearMemory() {
1425
1272
  memory.clear();
@@ -1432,8 +1279,6 @@ function createPersistentReqIdStore(accountId, options) {
1432
1279
  get,
1433
1280
  getSync,
1434
1281
  delete: del,
1435
- warmup,
1436
- flush,
1437
1282
  clearMemory,
1438
1283
  memorySize,
1439
1284
  };
@@ -1536,7 +1381,7 @@ const reqIdStores = new Map();
1536
1381
  function getOrCreateReqIdStore(accountId) {
1537
1382
  let store = reqIdStores.get(accountId);
1538
1383
  if (!store) {
1539
- store = createPersistentReqIdStore(accountId);
1384
+ store = createPersistentReqIdStore();
1540
1385
  reqIdStores.set(accountId, store);
1541
1386
  }
1542
1387
  return store;
@@ -1552,12 +1397,13 @@ function setReqIdForChat(chatId, reqId, accountId = "default") {
1552
1397
  }
1553
1398
  /**
1554
1399
  * 启动时预热 reqId 缓存(从磁盘加载到内存)
1400
+ *
1401
+ * 注意:由于移除了磁盘存储,此函数现在只返回 0(无预热条目)
1555
1402
  */
1556
1403
  async function warmupReqIdStore(accountId = "default", log) {
1557
- const store = getOrCreateReqIdStore(accountId);
1558
- return store.warmup((error) => {
1559
- log?.(`[WeCom] reqid-store warmup error: ${String(error)}`);
1560
- });
1404
+ // 由于移除了磁盘存储,不再需要预热过程
1405
+ log?.("[WeCom] reqid-store warmup: no-op (disk storage removed)");
1406
+ return 0;
1561
1407
  }
1562
1408
  // ============================================================================
1563
1409
  // 全局 cleanup(断开连接时释放所有资源)
@@ -1577,17 +1423,8 @@ async function cleanupAccount(accountId) {
1577
1423
  }
1578
1424
  wsClientInstances.delete(accountId);
1579
1425
  }
1580
- // 2. flush reqId 存储到磁盘
1581
- const store = reqIdStores.get(accountId);
1582
- if (store) {
1583
- try {
1584
- await store.flush();
1585
- }
1586
- catch {
1587
- // 忽略 flush 错误
1588
- }
1589
- // 注意:不删除 store,因为重连后可能还需要
1590
- }
1426
+ // 2. 由于移除了磁盘存储,不再需要 flush reqId 存储
1427
+ // 注意:不删除 store,因为重连后可能还需要
1591
1428
  }
1592
1429
 
1593
1430
  /** 从 package.json 中读取版本号,兼容打包产物和直接运行 .ts 两种场景 */