liangzimixin 0.3.33 → 0.3.36

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
@@ -18067,6 +18067,23 @@ function inferFileNameFromUrl(url2) {
18067
18067
 
18068
18068
  // src/channel/outbound.ts
18069
18069
  var log4 = createLogger("channel/outbound");
18070
+ var messageEncryptionStatus = /* @__PURE__ */ new Map();
18071
+ var activeChatMessage = /* @__PURE__ */ new Map();
18072
+ function setInboundEncryptionStatus(chatId, messageId, isEncrypted) {
18073
+ messageEncryptionStatus.set(messageId, isEncrypted);
18074
+ activeChatMessage.set(chatId, messageId);
18075
+ }
18076
+ function clearInboundEncryptionStatus(chatId, messageId) {
18077
+ messageEncryptionStatus.delete(messageId);
18078
+ if (activeChatMessage.get(chatId) === messageId) {
18079
+ activeChatMessage.delete(chatId);
18080
+ }
18081
+ }
18082
+ function getOutboundEncryptionStatus(chatId) {
18083
+ const messageId = activeChatMessage.get(chatId);
18084
+ if (!messageId) return void 0;
18085
+ return messageEncryptionStatus.get(messageId);
18086
+ }
18070
18087
  var messagePipeGetter = null;
18071
18088
  function setMessagePipeGetter(getter) {
18072
18089
  messagePipeGetter = getter;
@@ -18099,11 +18116,13 @@ var quantumImOutbound = {
18099
18116
  }
18100
18117
  const messagePipe = messagePipeGetter();
18101
18118
  const chatId = parseChannelAddress(to);
18119
+ const isEncrypted = getOutboundEncryptionStatus(chatId);
18102
18120
  await messagePipe.sendMessage({
18103
18121
  chatId,
18104
18122
  senderId: chatId,
18105
18123
  msgType: "markdown",
18106
- content: JSON.stringify({ content: text })
18124
+ content: JSON.stringify({ content: text }),
18125
+ skipEncrypt: isEncrypted === false
18107
18126
  });
18108
18127
  return { channel: CHANNEL_ID, messageId: "", chatId };
18109
18128
  },
@@ -18118,6 +18137,7 @@ var quantumImOutbound = {
18118
18137
  const sdkRuntime = sdkRuntimeGetter();
18119
18138
  const chatId = parseChannelAddress(to);
18120
18139
  const fileCfg = pluginConfigGetter().file;
18140
+ const isEncrypted = getOutboundEncryptionStatus(chatId);
18121
18141
  const result = await resolveAndUploadMedia({
18122
18142
  mediaUrl: mediaUrl ?? void 0,
18123
18143
  tokenManager: tokenManagerGetter(),
@@ -18129,7 +18149,8 @@ var quantumImOutbound = {
18129
18149
  chatId,
18130
18150
  maxFileSizeMb: fileCfg.maxFileSizeMb,
18131
18151
  chunkSizeMb: fileCfg.chunkSizeMb,
18132
- timeoutMs: fileCfg.fetchTimeoutMs
18152
+ timeoutMs: fileCfg.fetchTimeoutMs,
18153
+ skipEncrypt: isEncrypted === false
18133
18154
  });
18134
18155
  if (result.warning) {
18135
18156
  log4.warn("sendMedia:degraded", { chatId, warning: result.warning });
@@ -18533,6 +18554,7 @@ var InboundPipeline = class {
18533
18554
  });
18534
18555
  return;
18535
18556
  }
18557
+ setInboundEncryptionStatus(msg.chatId, msg.messageId, msg.isEncrypted ?? false);
18536
18558
  if (detectAbort(context.text)) {
18537
18559
  log12.info("\u26D4 \u68C0\u6D4B\u5230\u4E2D\u6B62\u6307\u4EE4", {
18538
18560
  messageId: msg.messageId,
@@ -18600,6 +18622,7 @@ var InboundPipeline = class {
18600
18622
  \u56DE\u590D\u6570: counts?.final ?? 0,
18601
18623
  \u8017\u65F6ms: durationMs
18602
18624
  });
18625
+ clearInboundEncryptionStatus(msg.chatId, msg.messageId);
18603
18626
  } catch (err) {
18604
18627
  const durationMs = Date.now() - startMs;
18605
18628
  log12.error("\u274C \u6D88\u606F\u5904\u7406\u5931\u8D25", {
@@ -18610,6 +18633,7 @@ var InboundPipeline = class {
18610
18633
  stack: err.stack,
18611
18634
  durationMs
18612
18635
  });
18636
+ clearInboundEncryptionStatus(msg.chatId, msg.messageId);
18613
18637
  }
18614
18638
  }
18615
18639
  };
@@ -18905,7 +18929,11 @@ var quantumImPlugin = {
18905
18929
  })
18906
18930
  },
18907
18931
  messaging: {
18908
- normalizeTarget: (raw) => raw ?? void 0
18932
+ normalizeTarget: (raw) => {
18933
+ if (!raw) return void 0;
18934
+ const normalized = parseChannelAddress(raw).trim();
18935
+ return normalized || void 0;
18936
+ }
18909
18937
  },
18910
18938
  outbound: quantumImOutbound,
18911
18939
  setup: {
@@ -19841,6 +19869,9 @@ var WSClient = class extends import_node_events.EventEmitter {
19841
19869
  ws.on("message", (data) => {
19842
19870
  this.emit("message", data);
19843
19871
  });
19872
+ ws.on("pong", () => {
19873
+ this.emit("pong");
19874
+ });
19844
19875
  ws.on("close", (code, reason) => {
19845
19876
  log19.info("ws:closed", { code, reason: reason.toString() });
19846
19877
  this.emit("close", code, reason.toString());
@@ -19863,6 +19894,12 @@ var WSClient = class extends import_node_events.EventEmitter {
19863
19894
  }
19864
19895
  this.ws.send(data);
19865
19896
  }
19897
+ /** 发送 WebSocket 协议级 Ping 帧 */
19898
+ ping() {
19899
+ if (this.ws && this.ws.readyState === wrapper_default.OPEN) {
19900
+ this.ws.ping();
19901
+ }
19902
+ }
19866
19903
  /** 关闭连接 */
19867
19904
  close(code, reason) {
19868
19905
  if (this.ws) {
@@ -20219,6 +20256,8 @@ var ConnectionManager = class {
20219
20256
  appId = "";
20220
20257
  /** 是否正在重连中 (防止并发重连) */
20221
20258
  reconnecting = false;
20259
+ /** 是否收到了最近一次 pong 响应 */
20260
+ pongReceived = true;
20222
20261
  constructor(client, options) {
20223
20262
  this.client = client;
20224
20263
  this.options = {
@@ -20252,6 +20291,15 @@ var ConnectionManager = class {
20252
20291
  ...this.appId ? { "X-App-ID": this.appId } : {}
20253
20292
  }
20254
20293
  });
20294
+ this.registerEvents();
20295
+ this.startHeartbeat();
20296
+ log22.info("ConnectionManager started \u2713", { url: url2 });
20297
+ }
20298
+ /** 注册 close / error / pong 事件 */
20299
+ registerEvents() {
20300
+ this.client.removeAllListeners("close");
20301
+ this.client.removeAllListeners("error");
20302
+ this.client.removeAllListeners("pong");
20255
20303
  this.client.on("close", () => {
20256
20304
  if (this.running) {
20257
20305
  log22.warn("ws:disconnected, scheduling reconnect");
@@ -20261,17 +20309,29 @@ var ConnectionManager = class {
20261
20309
  this.client.on("error", (err) => {
20262
20310
  log22.error("ws:connection-error", { error: err.message });
20263
20311
  });
20264
- this.startHeartbeat();
20265
- log22.info("ConnectionManager started \u2713", { url: url2 });
20312
+ this.client.on("pong", () => {
20313
+ this.pongReceived = true;
20314
+ });
20266
20315
  }
20267
- /** 启动心跳保活 — 定时检查连接状态 */
20316
+ /** 启动心跳保活 — 定时发送 WebSocket Ping 帧 */
20268
20317
  startHeartbeat() {
20269
20318
  this.stopHeartbeat();
20319
+ this.pongReceived = true;
20270
20320
  this.heartbeatTimer = setInterval(() => {
20271
20321
  if (this.client.readyState !== wrapper_default.OPEN) {
20272
20322
  log22.warn("heartbeat: connection not open, scheduling reconnect");
20273
20323
  this.scheduleReconnect();
20324
+ return;
20325
+ }
20326
+ if (!this.pongReceived) {
20327
+ log22.warn("heartbeat: pong timeout, connection dead");
20328
+ this.client.close(4e3, "pong timeout");
20329
+ this.scheduleReconnect();
20330
+ return;
20274
20331
  }
20332
+ this.pongReceived = false;
20333
+ this.client.ping();
20334
+ log22.debug?.("heartbeat: ping sent");
20275
20335
  }, this.options.heartbeatIntervalMs);
20276
20336
  }
20277
20337
  /** 停止心跳定时器 */
@@ -20313,6 +20373,7 @@ var ConnectionManager = class {
20313
20373
  }
20314
20374
  });
20315
20375
  this.reconnectAttempts = 0;
20376
+ this.registerEvents();
20316
20377
  this.startHeartbeat();
20317
20378
  log22.info("ws:reconnected", { attempt: this.reconnectAttempts, url: this.url });
20318
20379
  return;
package/dist/index.d.cts CHANGED
@@ -684,6 +684,8 @@ declare class WSClient extends EventEmitter {
684
684
  connect(options: WSClientOptions): Promise<void>;
685
685
  /** 发送数据到服务器 — 前置检查连接状态 */
686
686
  send(data: string | Buffer): void;
687
+ /** 发送 WebSocket 协议级 Ping 帧 */
688
+ ping(): void;
687
689
  /** 关闭连接 */
688
690
  close(code?: number, reason?: string): void;
689
691
  /** 当前连接状态 (0=CONNECTING, 1=OPEN, 2=CLOSING, 3=CLOSED) */
@@ -865,6 +867,8 @@ declare class ConnectionManager {
865
867
  private appId;
866
868
  /** 是否正在重连中 (防止并发重连) */
867
869
  private reconnecting;
870
+ /** 是否收到了最近一次 pong 响应 */
871
+ private pongReceived;
868
872
  constructor(client: WSClient, options?: ConnectionManagerOptions);
869
873
  /**
870
874
  * 启动连接 — 连接 WS + 开始心跳 + 注册重连逻辑
@@ -873,7 +877,9 @@ declare class ConnectionManager {
873
877
  * @param appId - 应用 ID (用于 X-App-ID 头)
874
878
  */
875
879
  start(url: string, tokenFn: () => Promise<string>, appId?: string): Promise<void>;
876
- /** 启动心跳保活 定时检查连接状态 */
880
+ /** 注册 close / error / pong 事件 */
881
+ private registerEvents;
882
+ /** 启动心跳保活 — 定时发送 WebSocket Ping 帧 */
877
883
  private startHeartbeat;
878
884
  /** 停止心跳定时器 */
879
885
  private stopHeartbeat;
@@ -4162,6 +4162,23 @@ function inferFileNameFromUrl(url2) {
4162
4162
 
4163
4163
  // src/channel/outbound.ts
4164
4164
  var log4 = createLogger("channel/outbound");
4165
+ var messageEncryptionStatus = /* @__PURE__ */ new Map();
4166
+ var activeChatMessage = /* @__PURE__ */ new Map();
4167
+ function setInboundEncryptionStatus(chatId, messageId, isEncrypted) {
4168
+ messageEncryptionStatus.set(messageId, isEncrypted);
4169
+ activeChatMessage.set(chatId, messageId);
4170
+ }
4171
+ function clearInboundEncryptionStatus(chatId, messageId) {
4172
+ messageEncryptionStatus.delete(messageId);
4173
+ if (activeChatMessage.get(chatId) === messageId) {
4174
+ activeChatMessage.delete(chatId);
4175
+ }
4176
+ }
4177
+ function getOutboundEncryptionStatus(chatId) {
4178
+ const messageId = activeChatMessage.get(chatId);
4179
+ if (!messageId) return void 0;
4180
+ return messageEncryptionStatus.get(messageId);
4181
+ }
4165
4182
  var messagePipeGetter = null;
4166
4183
  function setMessagePipeGetter(getter) {
4167
4184
  messagePipeGetter = getter;
@@ -4194,11 +4211,13 @@ var quantumImOutbound = {
4194
4211
  }
4195
4212
  const messagePipe = messagePipeGetter();
4196
4213
  const chatId = parseChannelAddress(to);
4214
+ const isEncrypted = getOutboundEncryptionStatus(chatId);
4197
4215
  await messagePipe.sendMessage({
4198
4216
  chatId,
4199
4217
  senderId: chatId,
4200
4218
  msgType: "markdown",
4201
- content: JSON.stringify({ content: text })
4219
+ content: JSON.stringify({ content: text }),
4220
+ skipEncrypt: isEncrypted === false
4202
4221
  });
4203
4222
  return { channel: CHANNEL_ID, messageId: "", chatId };
4204
4223
  },
@@ -4213,6 +4232,7 @@ var quantumImOutbound = {
4213
4232
  const sdkRuntime = sdkRuntimeGetter();
4214
4233
  const chatId = parseChannelAddress(to);
4215
4234
  const fileCfg = pluginConfigGetter().file;
4235
+ const isEncrypted = getOutboundEncryptionStatus(chatId);
4216
4236
  const result = await resolveAndUploadMedia({
4217
4237
  mediaUrl: mediaUrl ?? void 0,
4218
4238
  tokenManager: tokenManagerGetter(),
@@ -4224,7 +4244,8 @@ var quantumImOutbound = {
4224
4244
  chatId,
4225
4245
  maxFileSizeMb: fileCfg.maxFileSizeMb,
4226
4246
  chunkSizeMb: fileCfg.chunkSizeMb,
4227
- timeoutMs: fileCfg.fetchTimeoutMs
4247
+ timeoutMs: fileCfg.fetchTimeoutMs,
4248
+ skipEncrypt: isEncrypted === false
4228
4249
  });
4229
4250
  if (result.warning) {
4230
4251
  log4.warn("sendMedia:degraded", { chatId, warning: result.warning });
@@ -18907,6 +18928,9 @@ var WSClient = class extends import_node_events.EventEmitter {
18907
18928
  ws.on("message", (data) => {
18908
18929
  this.emit("message", data);
18909
18930
  });
18931
+ ws.on("pong", () => {
18932
+ this.emit("pong");
18933
+ });
18910
18934
  ws.on("close", (code, reason) => {
18911
18935
  log10.info("ws:closed", { code, reason: reason.toString() });
18912
18936
  this.emit("close", code, reason.toString());
@@ -18929,6 +18953,12 @@ var WSClient = class extends import_node_events.EventEmitter {
18929
18953
  }
18930
18954
  this.ws.send(data);
18931
18955
  }
18956
+ /** 发送 WebSocket 协议级 Ping 帧 */
18957
+ ping() {
18958
+ if (this.ws && this.ws.readyState === wrapper_default.OPEN) {
18959
+ this.ws.ping();
18960
+ }
18961
+ }
18932
18962
  /** 关闭连接 */
18933
18963
  close(code, reason) {
18934
18964
  if (this.ws) {
@@ -19285,6 +19315,8 @@ var ConnectionManager = class {
19285
19315
  appId = "";
19286
19316
  /** 是否正在重连中 (防止并发重连) */
19287
19317
  reconnecting = false;
19318
+ /** 是否收到了最近一次 pong 响应 */
19319
+ pongReceived = true;
19288
19320
  constructor(client, options) {
19289
19321
  this.client = client;
19290
19322
  this.options = {
@@ -19318,6 +19350,15 @@ var ConnectionManager = class {
19318
19350
  ...this.appId ? { "X-App-ID": this.appId } : {}
19319
19351
  }
19320
19352
  });
19353
+ this.registerEvents();
19354
+ this.startHeartbeat();
19355
+ log13.info("ConnectionManager started \u2713", { url: url2 });
19356
+ }
19357
+ /** 注册 close / error / pong 事件 */
19358
+ registerEvents() {
19359
+ this.client.removeAllListeners("close");
19360
+ this.client.removeAllListeners("error");
19361
+ this.client.removeAllListeners("pong");
19321
19362
  this.client.on("close", () => {
19322
19363
  if (this.running) {
19323
19364
  log13.warn("ws:disconnected, scheduling reconnect");
@@ -19327,17 +19368,29 @@ var ConnectionManager = class {
19327
19368
  this.client.on("error", (err) => {
19328
19369
  log13.error("ws:connection-error", { error: err.message });
19329
19370
  });
19330
- this.startHeartbeat();
19331
- log13.info("ConnectionManager started \u2713", { url: url2 });
19371
+ this.client.on("pong", () => {
19372
+ this.pongReceived = true;
19373
+ });
19332
19374
  }
19333
- /** 启动心跳保活 — 定时检查连接状态 */
19375
+ /** 启动心跳保活 — 定时发送 WebSocket Ping 帧 */
19334
19376
  startHeartbeat() {
19335
19377
  this.stopHeartbeat();
19378
+ this.pongReceived = true;
19336
19379
  this.heartbeatTimer = setInterval(() => {
19337
19380
  if (this.client.readyState !== wrapper_default.OPEN) {
19338
19381
  log13.warn("heartbeat: connection not open, scheduling reconnect");
19339
19382
  this.scheduleReconnect();
19383
+ return;
19340
19384
  }
19385
+ if (!this.pongReceived) {
19386
+ log13.warn("heartbeat: pong timeout, connection dead");
19387
+ this.client.close(4e3, "pong timeout");
19388
+ this.scheduleReconnect();
19389
+ return;
19390
+ }
19391
+ this.pongReceived = false;
19392
+ this.client.ping();
19393
+ log13.debug?.("heartbeat: ping sent");
19341
19394
  }, this.options.heartbeatIntervalMs);
19342
19395
  }
19343
19396
  /** 停止心跳定时器 */
@@ -19379,6 +19432,7 @@ var ConnectionManager = class {
19379
19432
  }
19380
19433
  });
19381
19434
  this.reconnectAttempts = 0;
19435
+ this.registerEvents();
19382
19436
  this.startHeartbeat();
19383
19437
  log13.info("ws:reconnected", { attempt: this.reconnectAttempts, url: this.url });
19384
19438
  return;
@@ -20326,6 +20380,7 @@ var InboundPipeline = class {
20326
20380
  });
20327
20381
  return;
20328
20382
  }
20383
+ setInboundEncryptionStatus(msg.chatId, msg.messageId, msg.isEncrypted ?? false);
20329
20384
  if (detectAbort(context.text)) {
20330
20385
  log24.info("\u26D4 \u68C0\u6D4B\u5230\u4E2D\u6B62\u6307\u4EE4", {
20331
20386
  messageId: msg.messageId,
@@ -20393,6 +20448,7 @@ var InboundPipeline = class {
20393
20448
  \u56DE\u590D\u6570: counts?.final ?? 0,
20394
20449
  \u8017\u65F6ms: durationMs
20395
20450
  });
20451
+ clearInboundEncryptionStatus(msg.chatId, msg.messageId);
20396
20452
  } catch (err) {
20397
20453
  const durationMs = Date.now() - startMs;
20398
20454
  log24.error("\u274C \u6D88\u606F\u5904\u7406\u5931\u8D25", {
@@ -20403,6 +20459,7 @@ var InboundPipeline = class {
20403
20459
  stack: err.stack,
20404
20460
  durationMs
20405
20461
  });
20462
+ clearInboundEncryptionStatus(msg.chatId, msg.messageId);
20406
20463
  }
20407
20464
  }
20408
20465
  };
@@ -20637,7 +20694,11 @@ var quantumImPlugin = {
20637
20694
  })
20638
20695
  },
20639
20696
  messaging: {
20640
- normalizeTarget: (raw) => raw ?? void 0
20697
+ normalizeTarget: (raw) => {
20698
+ if (!raw) return void 0;
20699
+ const normalized = parseChannelAddress(raw).trim();
20700
+ return normalized || void 0;
20701
+ }
20641
20702
  },
20642
20703
  outbound: quantumImOutbound,
20643
20704
  setup: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "liangzimixin",
3
- "version": "0.3.33",
3
+ "version": "0.3.36",
4
4
  "description": "Quantum-encrypted IM channel plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",