liangzimixin 0.3.74 → 0.3.76

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 CHANGED
@@ -18741,6 +18741,44 @@ function destroyEncryptionMaps() {
18741
18741
  messageEncryptionStatus.destroy();
18742
18742
  activeChatMessage.destroy();
18743
18743
  }
18744
+ var _readyResolve = null;
18745
+ var _readyPromise = null;
18746
+ function initReadyGate() {
18747
+ _readyPromise = new Promise((resolve3) => {
18748
+ _readyResolve = resolve3;
18749
+ });
18750
+ }
18751
+ initReadyGate();
18752
+ function markOutboundReady() {
18753
+ if (_readyResolve) {
18754
+ _readyResolve();
18755
+ _readyResolve = null;
18756
+ _readyPromise = null;
18757
+ }
18758
+ }
18759
+ function resetOutbound() {
18760
+ messagePipeGetter = null;
18761
+ sdkRuntimeGetter = null;
18762
+ tokenManagerGetter = null;
18763
+ pluginConfigGetter = null;
18764
+ initReadyGate();
18765
+ }
18766
+ var READY_TIMEOUT_MS = 15e3;
18767
+ async function waitForReady() {
18768
+ if (!_readyPromise) return true;
18769
+ log7.info("outbound: waiting for dependencies to initialize...");
18770
+ return new Promise((resolve3) => {
18771
+ const timer = setTimeout(() => {
18772
+ log7.error(`outbound: dependencies not ready after ${READY_TIMEOUT_MS}ms`);
18773
+ resolve3(false);
18774
+ }, READY_TIMEOUT_MS);
18775
+ _readyPromise.then(() => {
18776
+ clearTimeout(timer);
18777
+ log7.info("outbound: dependencies ready \u2713");
18778
+ resolve3(true);
18779
+ });
18780
+ });
18781
+ }
18744
18782
  function getOutboundEncryptionStatus(chatId) {
18745
18783
  const messageId = activeChatMessage.get(chatId);
18746
18784
  if (!messageId) return void 0;
@@ -18777,8 +18815,11 @@ var quantumImOutbound = {
18777
18815
  return { channel: CHANNEL_ID, messageId: "", chatId: to };
18778
18816
  }
18779
18817
  if (!messagePipeGetter) {
18780
- log7.error("outbound:sendText failed, messagePipe not initialized");
18781
- return { channel: CHANNEL_ID, messageId: "", chatId: to };
18818
+ const ready = await waitForReady();
18819
+ if (!ready || !messagePipeGetter) {
18820
+ log7.error("outbound:sendText failed, messagePipe not initialized");
18821
+ return { channel: CHANNEL_ID, messageId: "", chatId: to };
18822
+ }
18782
18823
  }
18783
18824
  const messagePipe = messagePipeGetter();
18784
18825
  const chatId = parseChannelAddress(to);
@@ -18798,8 +18839,11 @@ var quantumImOutbound = {
18798
18839
  sendMedia: async ({ cfg, to, text, mediaUrl, mediaLocalRoots, accountId }) => {
18799
18840
  log7.info("sendMedia called", { to, mediaUrl });
18800
18841
  if (!messagePipeGetter || !sdkRuntimeGetter || !tokenManagerGetter || !pluginConfigGetter) {
18801
- log7.error("outbound:sendMedia failed, dependencies not initialized");
18802
- return { channel: CHANNEL_ID, messageId: "", chatId: to };
18842
+ const ready = await waitForReady();
18843
+ if (!messagePipeGetter || !sdkRuntimeGetter || !tokenManagerGetter || !pluginConfigGetter) {
18844
+ log7.error("outbound:sendMedia failed, dependencies not initialized after wait");
18845
+ return { channel: CHANNEL_ID, messageId: "", chatId: to };
18846
+ }
18803
18847
  }
18804
18848
  const messagePipe = messagePipeGetter();
18805
18849
  const sdkRuntime = sdkRuntimeGetter();
@@ -19875,6 +19919,11 @@ var quantumImPlugin = {
19875
19919
  const instance = await startPlugin(resolved.credentials);
19876
19920
  activeInstance = instance;
19877
19921
  const { config: config2 } = instance;
19922
+ setMessagePipeGetter(() => instance.messagePipe);
19923
+ setSdkRuntimeGetter(() => getPluginRuntime());
19924
+ setTokenManagerGetter(() => instance.tokenManager);
19925
+ setPluginConfigGetter(() => instance.config);
19926
+ markOutboundReady();
19878
19927
  await instance.connectionManager.start(
19879
19928
  config2.transport.wsUrl,
19880
19929
  () => instance.tokenManager.getValidToken(),
@@ -19892,10 +19941,6 @@ var quantumImPlugin = {
19892
19941
  instance.messagePipe.onMessage((msg) => {
19893
19942
  pipeline.handle(msg);
19894
19943
  });
19895
- setMessagePipeGetter(() => instance.messagePipe);
19896
- setSdkRuntimeGetter(() => getPluginRuntime());
19897
- setTokenManagerGetter(() => instance.tokenManager);
19898
- setPluginConfigGetter(() => instance.config);
19899
19944
  log16.info(`liangzimixin[${ctx.accountId}] started \u2713`);
19900
19945
  log16.info("\u{1F389} \u6B22\u8FCE\u4F7F\u7528\u91CF\u5B50\u5BC6\u4FE1\u9F99\u867E\uFF0C\u60A8\u5DF2\u6210\u529F\u8FDE\u63A5\u667A\u80FD\u52A9\u7406");
19901
19946
  await new Promise((resolve3) => {
@@ -20575,6 +20620,16 @@ var TokenManager = class {
20575
20620
  isAuthorized() {
20576
20621
  return this.cachedToken !== null;
20577
20622
  }
20623
+ /**
20624
+ * 失效当前内存缓存的 Token — 下次 getValidToken() 将重新获取。
20625
+ * 用于出站 HTTP 收到 401 时主动清除可能已过期的 Token。
20626
+ * 注意: 不清除文件存储,_acquireToken 会重新获取并覆盖。
20627
+ */
20628
+ invalidate() {
20629
+ this.cachedToken = null;
20630
+ this.currentTokenData = null;
20631
+ log21.info("Token invalidated (will re-acquire on next call)");
20632
+ }
20578
20633
  /** 废置并清除所有令牌 (包括文件存储和内存缓存) */
20579
20634
  async revokeAndClear() {
20580
20635
  this.clearRefreshTimer();
@@ -20978,6 +21033,8 @@ var MessagePipe = class _MessagePipe {
20978
21033
  crypto;
20979
21034
  /** 获取最新 access_token 的回调 (用于 HMAC 验签和出站 Bearer 认证) */
20980
21035
  tokenFn;
21036
+ /** 失效当前 Token 缓存的回调 — 收到 401 时调用,强制下次 tokenFn 重新获取 */
21037
+ invalidateTokenFn;
20981
21038
  /** 消息服务 API 基础地址 (用于出站发送/撤回) */
20982
21039
  messageServiceBaseUrl;
20983
21040
  /** L4 层注册的入站消息回调 */
@@ -20997,6 +21054,7 @@ var MessagePipe = class _MessagePipe {
20997
21054
  this.dedup = deps.dedup;
20998
21055
  this.crypto = deps.crypto;
20999
21056
  this.tokenFn = deps.tokenFn;
21057
+ this.invalidateTokenFn = deps.invalidateTokenFn;
21000
21058
  this.messageServiceBaseUrl = deps.messageServiceBaseUrl;
21001
21059
  this.quantumAccount = deps.quantumAccount;
21002
21060
  this.encryptionMode = deps.encryptionMode;
@@ -21300,6 +21358,11 @@ var MessagePipe = class _MessagePipe {
21300
21358
  url: url2,
21301
21359
  body
21302
21360
  });
21361
+ if (resp.status === 401 && this.invalidateTokenFn) {
21362
+ log25.warn(`${logTag}:token-expired, invalidating cached token for retry`);
21363
+ this.invalidateTokenFn();
21364
+ return { code: -1, msg: `HTTP ${resp.status}`, _retryable: true };
21365
+ }
21303
21366
  return { code: -1, msg: `HTTP ${resp.status}`, _retryable: resp.status >= 500 };
21304
21367
  }
21305
21368
  const result = await resp.json();
@@ -21335,7 +21398,7 @@ var MessagePipe = class _MessagePipe {
21335
21398
  const result = await this._callMessageApiOnce(url2, body, logTag);
21336
21399
  if (result && "_retryable" in result) {
21337
21400
  if (result._retryable) {
21338
- log25.warn(`${logTag}:retrying after 5xx`, { url: url2 });
21401
+ log25.warn(`${logTag}:retrying after ${result.msg}`, { url: url2 });
21339
21402
  await new Promise((r) => setTimeout(r, 1e3));
21340
21403
  try {
21341
21404
  const retryResult = await this._callMessageApiOnce(url2, body, `${logTag}:retry`);
@@ -22261,6 +22324,7 @@ async function startPlugin(accountConfig, internalOverrides) {
22261
22324
  dedup,
22262
22325
  crypto: cryptoEngine,
22263
22326
  tokenFn: () => tokenManager.getValidToken(),
22327
+ invalidateTokenFn: () => tokenManager.invalidate(),
22264
22328
  messageServiceBaseUrl: config2.message.messageServiceBaseUrl,
22265
22329
  quantumAccount: accountConfig.quantumAccount,
22266
22330
  encryptionMode: accountConfig.encryptionMode,
@@ -22299,6 +22363,7 @@ async function startPlugin(accountConfig, internalOverrides) {
22299
22363
  tokenManager.shutdown();
22300
22364
  metrics.stop();
22301
22365
  destroyEncryptionMaps();
22366
+ resetOutbound();
22302
22367
  log29.info("Shutdown complete \u2713");
22303
22368
  }
22304
22369
  };
package/dist/index.d.cts CHANGED
@@ -650,6 +650,12 @@ declare class TokenManager {
650
650
  hasScope(scope: string): boolean;
651
651
  /** 检查是否已授权 (是否持有有效令牌) */
652
652
  isAuthorized(): boolean;
653
+ /**
654
+ * 失效当前内存缓存的 Token — 下次 getValidToken() 将重新获取。
655
+ * 用于出站 HTTP 收到 401 时主动清除可能已过期的 Token。
656
+ * 注意: 不清除文件存储,_acquireToken 会重新获取并覆盖。
657
+ */
658
+ invalidate(): void;
653
659
  /** 废置并清除所有令牌 (包括文件存储和内存缓存) */
654
660
  revokeAndClear(): Promise<void>;
655
661
  /** 清理定时器和并发锁 — 优雅关闭时调用 */
@@ -866,6 +872,8 @@ declare class MessagePipe {
866
872
  private readonly crypto;
867
873
  /** 获取最新 access_token 的回调 (用于 HMAC 验签和出站 Bearer 认证) */
868
874
  private readonly tokenFn;
875
+ /** 失效当前 Token 缓存的回调 — 收到 401 时调用,强制下次 tokenFn 重新获取 */
876
+ private readonly invalidateTokenFn?;
869
877
  /** 消息服务 API 基础地址 (用于出站发送/撤回) */
870
878
  private readonly messageServiceBaseUrl;
871
879
  /** L4 层注册的入站消息回调 */
@@ -886,6 +894,8 @@ declare class MessagePipe {
886
894
  crypto: CryptoEngine;
887
895
  /** TokenManager.getValidToken — 用于验签和出站认证 */
888
896
  tokenFn: () => Promise<string>;
897
+ /** TokenManager.invalidate — 收到 401 时清除 Token 缓存 */
898
+ invalidateTokenFn?: () => void;
889
899
  /** 消息服务 API 基础地址 */
890
900
  messageServiceBaseUrl: string;
891
901
  /** 量子账户标识 — 用于判断是否具备解密能力 */
@@ -4812,6 +4812,44 @@ function destroyEncryptionMaps() {
4812
4812
  messageEncryptionStatus.destroy();
4813
4813
  activeChatMessage.destroy();
4814
4814
  }
4815
+ var _readyResolve = null;
4816
+ var _readyPromise = null;
4817
+ function initReadyGate() {
4818
+ _readyPromise = new Promise((resolve3) => {
4819
+ _readyResolve = resolve3;
4820
+ });
4821
+ }
4822
+ initReadyGate();
4823
+ function markOutboundReady() {
4824
+ if (_readyResolve) {
4825
+ _readyResolve();
4826
+ _readyResolve = null;
4827
+ _readyPromise = null;
4828
+ }
4829
+ }
4830
+ function resetOutbound() {
4831
+ messagePipeGetter = null;
4832
+ sdkRuntimeGetter = null;
4833
+ tokenManagerGetter = null;
4834
+ pluginConfigGetter = null;
4835
+ initReadyGate();
4836
+ }
4837
+ var READY_TIMEOUT_MS = 15e3;
4838
+ async function waitForReady() {
4839
+ if (!_readyPromise) return true;
4840
+ log7.info("outbound: waiting for dependencies to initialize...");
4841
+ return new Promise((resolve3) => {
4842
+ const timer = setTimeout(() => {
4843
+ log7.error(`outbound: dependencies not ready after ${READY_TIMEOUT_MS}ms`);
4844
+ resolve3(false);
4845
+ }, READY_TIMEOUT_MS);
4846
+ _readyPromise.then(() => {
4847
+ clearTimeout(timer);
4848
+ log7.info("outbound: dependencies ready \u2713");
4849
+ resolve3(true);
4850
+ });
4851
+ });
4852
+ }
4815
4853
  function getOutboundEncryptionStatus(chatId) {
4816
4854
  const messageId = activeChatMessage.get(chatId);
4817
4855
  if (!messageId) return void 0;
@@ -4848,8 +4886,11 @@ var quantumImOutbound = {
4848
4886
  return { channel: CHANNEL_ID, messageId: "", chatId: to };
4849
4887
  }
4850
4888
  if (!messagePipeGetter) {
4851
- log7.error("outbound:sendText failed, messagePipe not initialized");
4852
- return { channel: CHANNEL_ID, messageId: "", chatId: to };
4889
+ const ready = await waitForReady();
4890
+ if (!ready || !messagePipeGetter) {
4891
+ log7.error("outbound:sendText failed, messagePipe not initialized");
4892
+ return { channel: CHANNEL_ID, messageId: "", chatId: to };
4893
+ }
4853
4894
  }
4854
4895
  const messagePipe = messagePipeGetter();
4855
4896
  const chatId = parseChannelAddress(to);
@@ -4869,8 +4910,11 @@ var quantumImOutbound = {
4869
4910
  sendMedia: async ({ cfg, to, text, mediaUrl, mediaLocalRoots, accountId }) => {
4870
4911
  log7.info("sendMedia called", { to, mediaUrl });
4871
4912
  if (!messagePipeGetter || !sdkRuntimeGetter || !tokenManagerGetter || !pluginConfigGetter) {
4872
- log7.error("outbound:sendMedia failed, dependencies not initialized");
4873
- return { channel: CHANNEL_ID, messageId: "", chatId: to };
4913
+ const ready = await waitForReady();
4914
+ if (!messagePipeGetter || !sdkRuntimeGetter || !tokenManagerGetter || !pluginConfigGetter) {
4915
+ log7.error("outbound:sendMedia failed, dependencies not initialized after wait");
4916
+ return { channel: CHANNEL_ID, messageId: "", chatId: to };
4917
+ }
4874
4918
  }
4875
4919
  const messagePipe = messagePipeGetter();
4876
4920
  const sdkRuntime = sdkRuntimeGetter();
@@ -19461,6 +19505,16 @@ var TokenManager = class {
19461
19505
  isAuthorized() {
19462
19506
  return this.cachedToken !== null;
19463
19507
  }
19508
+ /**
19509
+ * 失效当前内存缓存的 Token — 下次 getValidToken() 将重新获取。
19510
+ * 用于出站 HTTP 收到 401 时主动清除可能已过期的 Token。
19511
+ * 注意: 不清除文件存储,_acquireToken 会重新获取并覆盖。
19512
+ */
19513
+ invalidate() {
19514
+ this.cachedToken = null;
19515
+ this.currentTokenData = null;
19516
+ log12.info("Token invalidated (will re-acquire on next call)");
19517
+ }
19464
19518
  /** 废置并清除所有令牌 (包括文件存储和内存缓存) */
19465
19519
  async revokeAndClear() {
19466
19520
  this.clearRefreshTimer();
@@ -19864,6 +19918,8 @@ var MessagePipe = class _MessagePipe {
19864
19918
  crypto;
19865
19919
  /** 获取最新 access_token 的回调 (用于 HMAC 验签和出站 Bearer 认证) */
19866
19920
  tokenFn;
19921
+ /** 失效当前 Token 缓存的回调 — 收到 401 时调用,强制下次 tokenFn 重新获取 */
19922
+ invalidateTokenFn;
19867
19923
  /** 消息服务 API 基础地址 (用于出站发送/撤回) */
19868
19924
  messageServiceBaseUrl;
19869
19925
  /** L4 层注册的入站消息回调 */
@@ -19883,6 +19939,7 @@ var MessagePipe = class _MessagePipe {
19883
19939
  this.dedup = deps.dedup;
19884
19940
  this.crypto = deps.crypto;
19885
19941
  this.tokenFn = deps.tokenFn;
19942
+ this.invalidateTokenFn = deps.invalidateTokenFn;
19886
19943
  this.messageServiceBaseUrl = deps.messageServiceBaseUrl;
19887
19944
  this.quantumAccount = deps.quantumAccount;
19888
19945
  this.encryptionMode = deps.encryptionMode;
@@ -20186,6 +20243,11 @@ var MessagePipe = class _MessagePipe {
20186
20243
  url: url2,
20187
20244
  body
20188
20245
  });
20246
+ if (resp.status === 401 && this.invalidateTokenFn) {
20247
+ log16.warn(`${logTag}:token-expired, invalidating cached token for retry`);
20248
+ this.invalidateTokenFn();
20249
+ return { code: -1, msg: `HTTP ${resp.status}`, _retryable: true };
20250
+ }
20189
20251
  return { code: -1, msg: `HTTP ${resp.status}`, _retryable: resp.status >= 500 };
20190
20252
  }
20191
20253
  const result = await resp.json();
@@ -20221,7 +20283,7 @@ var MessagePipe = class _MessagePipe {
20221
20283
  const result = await this._callMessageApiOnce(url2, body, logTag);
20222
20284
  if (result && "_retryable" in result) {
20223
20285
  if (result._retryable) {
20224
- log16.warn(`${logTag}:retrying after 5xx`, { url: url2 });
20286
+ log16.warn(`${logTag}:retrying after ${result.msg}`, { url: url2 });
20225
20287
  await new Promise((r) => setTimeout(r, 1e3));
20226
20288
  try {
20227
20289
  const retryResult = await this._callMessageApiOnce(url2, body, `${logTag}:retry`);
@@ -21224,6 +21286,7 @@ async function startPlugin(accountConfig, internalOverrides) {
21224
21286
  dedup,
21225
21287
  crypto: cryptoEngine,
21226
21288
  tokenFn: () => tokenManager.getValidToken(),
21289
+ invalidateTokenFn: () => tokenManager.invalidate(),
21227
21290
  messageServiceBaseUrl: config2.message.messageServiceBaseUrl,
21228
21291
  quantumAccount: accountConfig.quantumAccount,
21229
21292
  encryptionMode: accountConfig.encryptionMode,
@@ -21262,6 +21325,7 @@ async function startPlugin(accountConfig, internalOverrides) {
21262
21325
  tokenManager.shutdown();
21263
21326
  metrics.stop();
21264
21327
  destroyEncryptionMaps();
21328
+ resetOutbound();
21265
21329
  log20.info("Shutdown complete \u2713");
21266
21330
  }
21267
21331
  };
@@ -22233,6 +22297,11 @@ var quantumImPlugin = {
22233
22297
  const instance = await startPlugin(resolved.credentials);
22234
22298
  activeInstance = instance;
22235
22299
  const { config: config2 } = instance;
22300
+ setMessagePipeGetter(() => instance.messagePipe);
22301
+ setSdkRuntimeGetter(() => getPluginRuntime());
22302
+ setTokenManagerGetter(() => instance.tokenManager);
22303
+ setPluginConfigGetter(() => instance.config);
22304
+ markOutboundReady();
22236
22305
  await instance.connectionManager.start(
22237
22306
  config2.transport.wsUrl,
22238
22307
  () => instance.tokenManager.getValidToken(),
@@ -22250,10 +22319,6 @@ var quantumImPlugin = {
22250
22319
  instance.messagePipe.onMessage((msg) => {
22251
22320
  pipeline.handle(msg);
22252
22321
  });
22253
- setMessagePipeGetter(() => instance.messagePipe);
22254
- setSdkRuntimeGetter(() => getPluginRuntime());
22255
- setTokenManagerGetter(() => instance.tokenManager);
22256
- setPluginConfigGetter(() => instance.config);
22257
22322
  log29.info(`liangzimixin[${ctx.accountId}] started \u2713`);
22258
22323
  log29.info("\u{1F389} \u6B22\u8FCE\u4F7F\u7528\u91CF\u5B50\u5BC6\u4FE1\u9F99\u867E\uFF0C\u60A8\u5DF2\u6210\u529F\u8FDE\u63A5\u667A\u80FD\u52A9\u7406");
22259
22324
  await new Promise((resolve3) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "liangzimixin",
3
- "version": "0.3.74",
3
+ "version": "0.3.76",
4
4
  "description": "Quantum-encrypted IM channel plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -7,7 +7,7 @@ REM liangzimixin install script (Windows)
7
7
  REM Usage: liangzimixin_install.bat <appId> <appSecret> [quantumAccount]
8
8
  REM ============================================================
9
9
 
10
- set "SCRIPT_VERSION=0.3.74"
10
+ set "SCRIPT_VERSION=0.3.76"
11
11
  set "NPM_PACKAGE=liangzimixin"
12
12
 
13
13
  set "SKIP_SELF_UPDATE=0"
@@ -6,7 +6,7 @@ set -euo pipefail
6
6
  # 用法: ./liangzimixin_install.sh <appId> <appSecret> [quantumAccount]
7
7
  # ============================================================
8
8
 
9
- SCRIPT_VERSION="0.3.74"
9
+ SCRIPT_VERSION="0.3.76"
10
10
  NPM_PACKAGE="liangzimixin"
11
11
 
12
12
  # ── 颜色 ──────────────────────────────────────────────────────