liangzimixin 0.3.61 → 0.3.63

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
@@ -17556,7 +17556,7 @@ var DEFAULT_INTERNAL_CONFIG = {
17556
17556
  },
17557
17557
  metrics: {
17558
17558
  enabled: true,
17559
- logIntervalMs: 3e5
17559
+ logIntervalMs: 6e4
17560
17560
  }
17561
17561
  };
17562
17562
  function buildPluginConfig(accountConfig, internalOverrides) {
@@ -17992,17 +17992,40 @@ var Metrics = class {
17992
17992
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
17993
17993
  };
17994
17994
  }
17995
+ /** 上次输出时的计数器快照 — 用于增量比对 */
17996
+ lastCounterSnapshot = /* @__PURE__ */ new Map();
17997
+ /** 上次输出时的直方图采样计数 — 用于增量比对 */
17998
+ lastHistogramCount = /* @__PURE__ */ new Map();
17995
17999
  /**
17996
- * 启动定期日志输出。
17997
- * @param intervalMs - 输出间隔 (ms),默认 60000 (1 分钟)
18000
+ * 启动定期日志输出 (增量模式)。
18001
+ * 仅在有新数据变化时才输出,空闲期间静默。
18002
+ * @param intervalMs - 检查间隔 (ms),默认 60000 (1 分钟)
17998
18003
  */
17999
18004
  startPeriodicLog(intervalMs = 6e4) {
18000
18005
  if (this.started) return;
18001
18006
  this.started = true;
18002
18007
  this.logTimer = setInterval(() => {
18008
+ let hasChanges = false;
18009
+ for (const [name, count] of this.counters) {
18010
+ if ((this.lastCounterSnapshot.get(name) ?? 0) !== count) {
18011
+ hasChanges = true;
18012
+ break;
18013
+ }
18014
+ }
18015
+ if (!hasChanges) {
18016
+ for (const [name, hist] of this.histograms) {
18017
+ if ((this.lastHistogramCount.get(name) ?? 0) !== hist.count) {
18018
+ hasChanges = true;
18019
+ break;
18020
+ }
18021
+ }
18022
+ }
18023
+ if (!hasChanges) return;
18003
18024
  const snap = this.snapshot();
18004
- if (Object.keys(snap.counters).length > 0 || Object.keys(snap.histograms).length > 0) {
18005
- log3.info("\u{1F4CA} metrics snapshot", snap);
18025
+ log3.info("\u{1F4CA} metrics snapshot", snap);
18026
+ this.lastCounterSnapshot = new Map(this.counters);
18027
+ for (const [name, hist] of this.histograms) {
18028
+ this.lastHistogramCount.set(name, hist.count);
18006
18029
  }
18007
18030
  }, intervalMs);
18008
18031
  if (this.logTimer && typeof this.logTimer === "object" && "unref" in this.logTimer) {
@@ -18409,10 +18432,31 @@ async function resolveAndUploadMedia(params) {
18409
18432
  const fileType = detectFileType(fileName);
18410
18433
  const mimeType = inferMimeType(fileName);
18411
18434
  const ext = path2.extname(fileName).replace(".", "").toLowerCase();
18435
+ const MIN_IMAGE_SIZE = 100;
18436
+ if (fileType === "image" && buffer.length < MIN_IMAGE_SIZE) {
18437
+ log5.error("media:invalid image \u2014 \u6587\u4EF6\u8FC7\u5C0F\uFF0C\u4E0D\u662F\u5408\u6CD5\u56FE\u7247", {
18438
+ fileName,
18439
+ fileSize: buffer.length,
18440
+ minRequired: MIN_IMAGE_SIZE
18441
+ });
18442
+ throw new FileServiceError({
18443
+ message: `\u56FE\u7247\u6587\u4EF6\u65E0\u6548: ${fileName} \u4EC5 ${buffer.length} \u5B57\u8282\uFF0C\u8FDC\u5C0F\u4E8E\u5408\u6CD5\u56FE\u7247\u7684\u6700\u5C0F\u5927\u5C0F (${MIN_IMAGE_SIZE} \u5B57\u8282)`,
18444
+ code: -1,
18445
+ operationContext: `resolveAndUploadMedia:imageValidation(${fileName})`
18446
+ });
18447
+ }
18412
18448
  let imageDimensions;
18413
18449
  if (fileType === "image") {
18414
18450
  imageDimensions = getImageDimensions(buffer);
18415
- log5.info("media:imageDimensions", { fileName, ...imageDimensions });
18451
+ if (!imageDimensions) {
18452
+ log5.warn("media:imageDimensions failed \u2014 \u65E0\u6CD5\u89E3\u6790\u56FE\u7247\u5BBD\u9AD8\uFF0C\u6587\u4EF6\u53EF\u80FD\u635F\u574F", {
18453
+ fileName,
18454
+ fileSize: buffer.length,
18455
+ headerHex: buffer.subarray(0, Math.min(16, buffer.length)).toString("hex")
18456
+ });
18457
+ } else {
18458
+ log5.info("media:imageDimensions", { fileName, ...imageDimensions });
18459
+ }
18416
18460
  }
18417
18461
  log5.info("media:fileType", { fileName, fileType, mimeType, ext });
18418
18462
  const originalFileSize = buffer.length;
@@ -19150,11 +19194,13 @@ var InboundPipeline = class {
19150
19194
  const cmdPayload = buildInboundPayload(msg, { text: context.text }, this.deps.pluginConfig);
19151
19195
  const cmdCtx = core.channel.reply.finalizeInboundContext(cmdPayload);
19152
19196
  const cmdSkipEncrypt = this.deps.pluginConfig.credentials.encryptionMode === "quantum_only" ? false : true;
19197
+ let deliverCalled = false;
19153
19198
  await core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
19154
19199
  ctx: cmdCtx,
19155
19200
  cfg: this.deps.sdkConfig,
19156
19201
  dispatcherOptions: {
19157
19202
  deliver: async (payload2) => {
19203
+ deliverCalled = true;
19158
19204
  log15.info("\u{1F527} \u547D\u4EE4 deliver \u56DE\u8C03\u6536\u5230", {
19159
19205
  messageId: msg.messageId,
19160
19206
  hasText: Boolean(payload2.text),
@@ -19178,11 +19224,33 @@ var InboundPipeline = class {
19178
19224
  },
19179
19225
  replyOptions: {}
19180
19226
  });
19227
+ if (!deliverCalled) {
19228
+ const command = context.text.trim();
19229
+ const fallbackMessages = {
19230
+ "/new": "\u2705 \u4F1A\u8BDD\u5DF2\u91CD\u7F6E\uFF0C\u53EF\u4EE5\u5F00\u59CB\u65B0\u7684\u5BF9\u8BDD\u4E86\u3002",
19231
+ "/reset": "\u2705 \u4F1A\u8BDD\u5DF2\u91CD\u7F6E\uFF0C\u53EF\u4EE5\u5F00\u59CB\u65B0\u7684\u5BF9\u8BDD\u4E86\u3002"
19232
+ };
19233
+ const fallbackText = fallbackMessages[command] ?? `\u2705 \u547D\u4EE4 \`${command}\` \u5DF2\u6267\u884C\u3002`;
19234
+ log15.info("\u{1F4E8} SDK \u672A\u56DE\u8C03 deliver\uFF0C\u53D1\u9001\u56DE\u9000\u786E\u8BA4\u6D88\u606F", {
19235
+ messageId: msg.messageId,
19236
+ command
19237
+ });
19238
+ await this.deps.messagePipe.sendMessage({
19239
+ chatId: msg.chatId,
19240
+ senderId: msg.senderId,
19241
+ msgType: "markdown",
19242
+ content: JSON.stringify({ content: fallbackText }),
19243
+ skipEncrypt: cmdSkipEncrypt
19244
+ }).catch((err) => {
19245
+ log15.warn("\u26A0\uFE0F \u56DE\u9000\u786E\u8BA4\u6D88\u606F\u53D1\u9001\u5931\u8D25", { error: err.message });
19246
+ });
19247
+ }
19181
19248
  clearInboundEncryptionStatus(msg.chatId, msg.messageId);
19182
19249
  const durationMs2 = Date.now() - startMs;
19183
19250
  log15.info("\u2705 \u7CFB\u7EDF\u547D\u4EE4\u5904\u7406\u5B8C\u6210", {
19184
19251
  messageId: msg.messageId,
19185
19252
  command: context.text.trim(),
19253
+ deliverCalled,
19186
19254
  \u8017\u65F6ms: durationMs2
19187
19255
  });
19188
19256
  return;
@@ -4065,17 +4065,40 @@ var Metrics = class {
4065
4065
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
4066
4066
  };
4067
4067
  }
4068
+ /** 上次输出时的计数器快照 — 用于增量比对 */
4069
+ lastCounterSnapshot = /* @__PURE__ */ new Map();
4070
+ /** 上次输出时的直方图采样计数 — 用于增量比对 */
4071
+ lastHistogramCount = /* @__PURE__ */ new Map();
4068
4072
  /**
4069
- * 启动定期日志输出。
4070
- * @param intervalMs - 输出间隔 (ms),默认 60000 (1 分钟)
4073
+ * 启动定期日志输出 (增量模式)。
4074
+ * 仅在有新数据变化时才输出,空闲期间静默。
4075
+ * @param intervalMs - 检查间隔 (ms),默认 60000 (1 分钟)
4071
4076
  */
4072
4077
  startPeriodicLog(intervalMs = 6e4) {
4073
4078
  if (this.started) return;
4074
4079
  this.started = true;
4075
4080
  this.logTimer = setInterval(() => {
4081
+ let hasChanges = false;
4082
+ for (const [name, count] of this.counters) {
4083
+ if ((this.lastCounterSnapshot.get(name) ?? 0) !== count) {
4084
+ hasChanges = true;
4085
+ break;
4086
+ }
4087
+ }
4088
+ if (!hasChanges) {
4089
+ for (const [name, hist] of this.histograms) {
4090
+ if ((this.lastHistogramCount.get(name) ?? 0) !== hist.count) {
4091
+ hasChanges = true;
4092
+ break;
4093
+ }
4094
+ }
4095
+ }
4096
+ if (!hasChanges) return;
4076
4097
  const snap = this.snapshot();
4077
- if (Object.keys(snap.counters).length > 0 || Object.keys(snap.histograms).length > 0) {
4078
- log3.info("\u{1F4CA} metrics snapshot", snap);
4098
+ log3.info("\u{1F4CA} metrics snapshot", snap);
4099
+ this.lastCounterSnapshot = new Map(this.counters);
4100
+ for (const [name, hist] of this.histograms) {
4101
+ this.lastHistogramCount.set(name, hist.count);
4079
4102
  }
4080
4103
  }, intervalMs);
4081
4104
  if (this.logTimer && typeof this.logTimer === "object" && "unref" in this.logTimer) {
@@ -4482,10 +4505,31 @@ async function resolveAndUploadMedia(params) {
4482
4505
  const fileType = detectFileType(fileName);
4483
4506
  const mimeType = inferMimeType(fileName);
4484
4507
  const ext = path2.extname(fileName).replace(".", "").toLowerCase();
4508
+ const MIN_IMAGE_SIZE = 100;
4509
+ if (fileType === "image" && buffer.length < MIN_IMAGE_SIZE) {
4510
+ log5.error("media:invalid image \u2014 \u6587\u4EF6\u8FC7\u5C0F\uFF0C\u4E0D\u662F\u5408\u6CD5\u56FE\u7247", {
4511
+ fileName,
4512
+ fileSize: buffer.length,
4513
+ minRequired: MIN_IMAGE_SIZE
4514
+ });
4515
+ throw new FileServiceError({
4516
+ message: `\u56FE\u7247\u6587\u4EF6\u65E0\u6548: ${fileName} \u4EC5 ${buffer.length} \u5B57\u8282\uFF0C\u8FDC\u5C0F\u4E8E\u5408\u6CD5\u56FE\u7247\u7684\u6700\u5C0F\u5927\u5C0F (${MIN_IMAGE_SIZE} \u5B57\u8282)`,
4517
+ code: -1,
4518
+ operationContext: `resolveAndUploadMedia:imageValidation(${fileName})`
4519
+ });
4520
+ }
4485
4521
  let imageDimensions;
4486
4522
  if (fileType === "image") {
4487
4523
  imageDimensions = getImageDimensions(buffer);
4488
- log5.info("media:imageDimensions", { fileName, ...imageDimensions });
4524
+ if (!imageDimensions) {
4525
+ log5.warn("media:imageDimensions failed \u2014 \u65E0\u6CD5\u89E3\u6790\u56FE\u7247\u5BBD\u9AD8\uFF0C\u6587\u4EF6\u53EF\u80FD\u635F\u574F", {
4526
+ fileName,
4527
+ fileSize: buffer.length,
4528
+ headerHex: buffer.subarray(0, Math.min(16, buffer.length)).toString("hex")
4529
+ });
4530
+ } else {
4531
+ log5.info("media:imageDimensions", { fileName, ...imageDimensions });
4532
+ }
4489
4533
  }
4490
4534
  log5.info("media:fileType", { fileName, fileType, mimeType, ext });
4491
4535
  const originalFileSize = buffer.length;
@@ -18680,7 +18724,7 @@ var DEFAULT_INTERNAL_CONFIG = {
18680
18724
  },
18681
18725
  metrics: {
18682
18726
  enabled: true,
18683
- logIntervalMs: 3e5
18727
+ logIntervalMs: 6e4
18684
18728
  }
18685
18729
  };
18686
18730
  function buildPluginConfig(accountConfig, internalOverrides) {
@@ -21448,11 +21492,13 @@ var InboundPipeline = class {
21448
21492
  const cmdPayload = buildInboundPayload(msg, { text: context.text }, this.deps.pluginConfig);
21449
21493
  const cmdCtx = core.channel.reply.finalizeInboundContext(cmdPayload);
21450
21494
  const cmdSkipEncrypt = this.deps.pluginConfig.credentials.encryptionMode === "quantum_only" ? false : true;
21495
+ let deliverCalled = false;
21451
21496
  await core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
21452
21497
  ctx: cmdCtx,
21453
21498
  cfg: this.deps.sdkConfig,
21454
21499
  dispatcherOptions: {
21455
21500
  deliver: async (payload2) => {
21501
+ deliverCalled = true;
21456
21502
  log28.info("\u{1F527} \u547D\u4EE4 deliver \u56DE\u8C03\u6536\u5230", {
21457
21503
  messageId: msg.messageId,
21458
21504
  hasText: Boolean(payload2.text),
@@ -21476,11 +21522,33 @@ var InboundPipeline = class {
21476
21522
  },
21477
21523
  replyOptions: {}
21478
21524
  });
21525
+ if (!deliverCalled) {
21526
+ const command = context.text.trim();
21527
+ const fallbackMessages = {
21528
+ "/new": "\u2705 \u4F1A\u8BDD\u5DF2\u91CD\u7F6E\uFF0C\u53EF\u4EE5\u5F00\u59CB\u65B0\u7684\u5BF9\u8BDD\u4E86\u3002",
21529
+ "/reset": "\u2705 \u4F1A\u8BDD\u5DF2\u91CD\u7F6E\uFF0C\u53EF\u4EE5\u5F00\u59CB\u65B0\u7684\u5BF9\u8BDD\u4E86\u3002"
21530
+ };
21531
+ const fallbackText = fallbackMessages[command] ?? `\u2705 \u547D\u4EE4 \`${command}\` \u5DF2\u6267\u884C\u3002`;
21532
+ log28.info("\u{1F4E8} SDK \u672A\u56DE\u8C03 deliver\uFF0C\u53D1\u9001\u56DE\u9000\u786E\u8BA4\u6D88\u606F", {
21533
+ messageId: msg.messageId,
21534
+ command
21535
+ });
21536
+ await this.deps.messagePipe.sendMessage({
21537
+ chatId: msg.chatId,
21538
+ senderId: msg.senderId,
21539
+ msgType: "markdown",
21540
+ content: JSON.stringify({ content: fallbackText }),
21541
+ skipEncrypt: cmdSkipEncrypt
21542
+ }).catch((err) => {
21543
+ log28.warn("\u26A0\uFE0F \u56DE\u9000\u786E\u8BA4\u6D88\u606F\u53D1\u9001\u5931\u8D25", { error: err.message });
21544
+ });
21545
+ }
21479
21546
  clearInboundEncryptionStatus(msg.chatId, msg.messageId);
21480
21547
  const durationMs2 = Date.now() - startMs;
21481
21548
  log28.info("\u2705 \u7CFB\u7EDF\u547D\u4EE4\u5904\u7406\u5B8C\u6210", {
21482
21549
  messageId: msg.messageId,
21483
21550
  command: context.text.trim(),
21551
+ deliverCalled,
21484
21552
  \u8017\u65F6ms: durationMs2
21485
21553
  });
21486
21554
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "liangzimixin",
3
- "version": "0.3.61",
3
+ "version": "0.3.63",
4
4
  "description": "Quantum-encrypted IM channel plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",