liangzimixin 0.2.6 → 0.2.7

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
@@ -2233,7 +2233,7 @@ var require_websocket = __commonJS({
2233
2233
  var http = require("http");
2234
2234
  var net = require("net");
2235
2235
  var tls = require("tls");
2236
- var { randomBytes: randomBytes3, createHash } = require("crypto");
2236
+ var { randomBytes: randomBytes2, createHash } = require("crypto");
2237
2237
  var { Duplex, Readable } = require("stream");
2238
2238
  var { URL: URL2 } = require("url");
2239
2239
  var PerMessageDeflate = require_permessage_deflate();
@@ -2763,7 +2763,7 @@ var require_websocket = __commonJS({
2763
2763
  }
2764
2764
  }
2765
2765
  const defaultPort = isSecure ? 443 : 80;
2766
- const key = randomBytes3(16).toString("base64");
2766
+ const key = randomBytes2(16).toString("base64");
2767
2767
  const request = isSecure ? https.request : http.request;
2768
2768
  const protocolSet = /* @__PURE__ */ new Set();
2769
2769
  let perMessageDeflate;
@@ -3658,7 +3658,7 @@ __export(index_exports, {
3658
3658
  startPlugin: () => startPlugin
3659
3659
  });
3660
3660
  module.exports = __toCommonJS(index_exports);
3661
- var import_node_path2 = require("path");
3661
+ var import_node_path3 = require("path");
3662
3662
  var import_node_os = require("os");
3663
3663
 
3664
3664
  // node_modules/zod/v4/classic/external.js
@@ -17454,6 +17454,14 @@ var AccountConfigSchema = external_exports.object({
17454
17454
  "quantum-appId": external_exports.string().min(1, "quantum-appId \u4E0D\u80FD\u4E3A\u7A7A"),
17455
17455
  /** 🔴 量子服务密钥 — 用于量子密钥分发 */
17456
17456
  "quantum-appSecret": external_exports.string().min(1, "quantum-appSecret \u4E0D\u80FD\u4E3A\u7A7A"),
17457
+ /** 🔴 PIN 口令 — 保护本地敏感数据 (切勿泄露, 修改后需删除旧密钥数据) */
17458
+ pin: external_exports.string().min(1, "pin \u4E0D\u80FD\u4E3A\u7A7A"),
17459
+ /**
17460
+ * 🟡 量子密服地址 — 默认生产环境
17461
+ * 测试环境: http://223.244.14.238:8552
17462
+ * 生产环境: https://cmsp.zdxlz.com:8552
17463
+ */
17464
+ "quantum-url": external_exports.string().url().optional().default("https://cmsp.zdxlz.com:8552"),
17457
17465
  /**
17458
17466
  * 🟡 Bot 自己的用户 ID — 用于 anti-loop 检查
17459
17467
  * 如果不填,需要通过其他 API 在运行时获取。
@@ -17465,6 +17473,8 @@ var AccountConfigSchema = external_exports.object({
17465
17473
  quantumAccount: raw["quantum-account"],
17466
17474
  quantumAppId: raw["quantum-appId"],
17467
17475
  quantumAppSecret: raw["quantum-appSecret"],
17476
+ pin: raw.pin,
17477
+ quantumUrl: raw["quantum-url"],
17468
17478
  botUserId: raw.botUserId
17469
17479
  }));
17470
17480
  var DEFAULT_INTERNAL_CONFIG = {
@@ -17483,10 +17493,7 @@ var DEFAULT_INTERNAL_CONFIG = {
17483
17493
  }
17484
17494
  },
17485
17495
  auth: {
17486
- serverUrl: process.env.LZMX_AUTH_URL || "https://seal.example.com",
17487
- clientName: "liangzimixin",
17488
- scopes: ["openid", "im_sdk", "data_operate", "offline_access"],
17489
- autoRegister: true,
17496
+ serverUrl: process.env.LZMX_AUTH_URL || "https://imtwo.zdxlz.com/open-apis/v1",
17490
17497
  refreshAheadMs: 3e5
17491
17498
  },
17492
17499
  crypto: {
@@ -17974,7 +17981,7 @@ async function resolveAndUploadMedia(params) {
17974
17981
  };
17975
17982
  }
17976
17983
  const msgType = fileType;
17977
- const contentPayload = fileType === "file" ? { fileKey: uploadResult.fileKey, filename: fileName, size: uploadResult.fileSize } : { fileKey: uploadResult.fileKey };
17984
+ const contentPayload = fileType === "file" ? { fileId: uploadResult.fileKey, fileName, size: uploadResult.fileSize } : { fileId: uploadResult.fileKey };
17978
17985
  await messagePipe.sendMessage({
17979
17986
  chatId,
17980
17987
  senderId: chatId,
@@ -17984,7 +17991,7 @@ async function resolveAndUploadMedia(params) {
17984
17991
  });
17985
17992
  log3.info("media:sent", {
17986
17993
  chatId,
17987
- fileKey: uploadResult.fileKey,
17994
+ fileId: uploadResult.fileKey,
17988
17995
  msgType
17989
17996
  });
17990
17997
  return {
@@ -18111,20 +18118,20 @@ ${body}` : body || raw.content;
18111
18118
  }
18112
18119
  case "image": {
18113
18120
  const c = parsed;
18114
- fileId = c?.fileKey;
18121
+ fileId = c?.fileId;
18115
18122
  text = c?.altText ?? (fileId ? `![image](${fileId})` : "[image]");
18116
18123
  break;
18117
18124
  }
18118
18125
  case "file": {
18119
18126
  const c = parsed;
18120
- fileId = c?.fileKey;
18121
- fileName = c?.filename;
18127
+ fileId = c?.fileId;
18128
+ fileName = c?.fileName;
18122
18129
  text = fileName ? `[File: ${fileName}]` : "[file]";
18123
18130
  break;
18124
18131
  }
18125
18132
  case "voice": {
18126
18133
  const c = parsed;
18127
- fileId = c?.fileKey;
18134
+ fileId = c?.fileId;
18128
18135
  const durationMs = c?.duration;
18129
18136
  const durationStr = durationMs != null ? ` ${(durationMs / 1e3).toFixed(1)}s` : "";
18130
18137
  text = `[Voice${durationStr}]`;
@@ -18132,7 +18139,7 @@ ${body}` : body || raw.content;
18132
18139
  }
18133
18140
  case "video": {
18134
18141
  const c = parsed;
18135
- fileId = c?.fileKey;
18142
+ fileId = c?.fileId;
18136
18143
  const durationMs = c?.duration;
18137
18144
  const durationStr = durationMs != null ? ` ${(durationMs / 1e3).toFixed(1)}s` : "";
18138
18145
  text = `[Video${durationStr}]`;
@@ -18454,7 +18461,7 @@ var QUANTUM_IM_CONFIG_JSON_SCHEMA = {
18454
18461
  title: "\u91CF\u5B50\u5BC6\u4FE1 IM \u63D2\u4EF6\u914D\u7F6E",
18455
18462
  description: "\u5BC6\u4FE1 IM Channel \u63D2\u4EF6\uFF0C\u652F\u6301\u91CF\u5B50\u52A0\u5BC6\u7684\u5B89\u5168\u5373\u65F6\u901A\u4FE1\u3002",
18456
18463
  type: "object",
18457
- required: ["appId", "appSecret", "quantum-account", "quantum-appId", "quantum-appSecret"],
18464
+ required: ["appId", "appSecret", "quantum-account", "quantum-appId", "quantum-appSecret", "pin"],
18458
18465
  properties: {
18459
18466
  appId: {
18460
18467
  type: "string",
@@ -18488,6 +18495,19 @@ var QUANTUM_IM_CONFIG_JSON_SCHEMA = {
18488
18495
  minLength: 1,
18489
18496
  format: "password"
18490
18497
  },
18498
+ pin: {
18499
+ type: "string",
18500
+ title: "PIN \u53E3\u4EE4",
18501
+ description: "\u4FDD\u62A4\u672C\u5730\u654F\u611F\u6570\u636E\u7684\u53E3\u4EE4\uFF0C\u7531\u7528\u6237\u81EA\u5B9A\u4E49\uFF08\u5207\u52FF\u6CC4\u9732\uFF0C\u4FEE\u6539\u540E\u8BF7\u5220\u9664\u65E7\u5BC6\u94A5\u6570\u636E\uFF09",
18502
+ minLength: 1,
18503
+ format: "password"
18504
+ },
18505
+ "quantum-url": {
18506
+ type: "string",
18507
+ title: "\u91CF\u5B50\u5BC6\u670D\u5730\u5740",
18508
+ description: "\u91CF\u5B50\u5BC6\u94A5\u7BA1\u7406\u670D\u52A1\u5730\u5740\u3002\u6D4B\u8BD5\u73AF\u5883: http://223.244.14.238:8552\uFF0C\u751F\u4EA7\u73AF\u5883: https://cmsp.zdxlz.com:8552",
18509
+ default: "https://cmsp.zdxlz.com:8552"
18510
+ },
18491
18511
  botUserId: {
18492
18512
  type: "string",
18493
18513
  title: "Bot \u7528\u6237 ID",
@@ -18581,6 +18601,7 @@ var quantumImOnboarding = {
18581
18601
  "\u8BF7\u51C6\u5907\u4EE5\u4E0B\u4FE1\u606F:",
18582
18602
  " 1. \u5E73\u53F0\u7533\u8BF7\u7684 appId \u548C appSecret",
18583
18603
  " 2. \u91CF\u5B50\u52A0\u5BC6\u670D\u52A1\u7684\u8D26\u6237\u3001appId \u548C appSecret",
18604
+ " 3. \u4FDD\u62A4\u672C\u5730\u5BC6\u94A5\u6570\u636E\u7684 PIN \u53E3\u4EE4",
18584
18605
  "",
18585
18606
  "\u5982\u6709\u7591\u95EE\u8BF7\u8054\u7CFB\u5E73\u53F0\u7BA1\u7406\u5458\u83B7\u53D6\u3002"
18586
18607
  ].join("\n"),
@@ -18612,6 +18633,36 @@ var quantumImOnboarding = {
18612
18633
  initialValue: existing["quantum-appSecret"] ?? void 0,
18613
18634
  validate: required2
18614
18635
  });
18636
+ const pin = await prompter.text({
18637
+ message: "PIN \u53E3\u4EE4 (\u4FDD\u62A4\u672C\u5730\u654F\u611F\u6570\u636E, \u5207\u52FF\u6CC4\u9732)",
18638
+ initialValue: existing.pin ?? void 0,
18639
+ validate: required2
18640
+ });
18641
+ const urlChoice = await prompter.select({
18642
+ message: "\u91CF\u5B50\u5BC6\u670D\u73AF\u5883",
18643
+ options: [
18644
+ { value: "https://cmsp.zdxlz.com:8552", label: "\u751F\u4EA7\u73AF\u5883", hint: "https://cmsp.zdxlz.com:8552" },
18645
+ { value: "http://223.244.14.238:8552", label: "\u6D4B\u8BD5\u73AF\u5883", hint: "http://223.244.14.238:8552" },
18646
+ { value: "custom", label: "\u81EA\u5B9A\u4E49\u5730\u5740" }
18647
+ ],
18648
+ initialValue: existing["quantum-url"] ?? "https://cmsp.zdxlz.com:8552"
18649
+ });
18650
+ let quantumUrl = urlChoice;
18651
+ if (urlChoice === "custom") {
18652
+ quantumUrl = await prompter.text({
18653
+ message: "\u8BF7\u8F93\u5165\u91CF\u5B50\u5BC6\u670D\u5730\u5740",
18654
+ placeholder: "https://your-server:8552",
18655
+ validate: (v) => {
18656
+ if (!v.trim()) return "\u4E0D\u80FD\u4E3A\u7A7A";
18657
+ try {
18658
+ new URL(v);
18659
+ } catch {
18660
+ return "\u8BF7\u8F93\u5165\u5408\u6CD5\u7684 URL";
18661
+ }
18662
+ return void 0;
18663
+ }
18664
+ });
18665
+ }
18615
18666
  const channels = cfg.channels ?? {};
18616
18667
  const channelCfg = channels[CHANNEL_ID] ?? {};
18617
18668
  const accounts = channelCfg.accounts ?? {};
@@ -18629,7 +18680,9 @@ var quantumImOnboarding = {
18629
18680
  appSecret: appSecret.trim(),
18630
18681
  "quantum-account": quantumAccount.trim(),
18631
18682
  "quantum-appId": quantumAppId.trim(),
18632
- "quantum-appSecret": quantumAppSecret.trim()
18683
+ "quantum-appSecret": quantumAppSecret.trim(),
18684
+ pin: pin.trim(),
18685
+ "quantum-url": quantumUrl.trim()
18633
18686
  }
18634
18687
  }
18635
18688
  }
@@ -18812,104 +18865,83 @@ var quantumImPlugin = {
18812
18865
  }
18813
18866
  };
18814
18867
 
18815
- // src/crypto/quantun-plug-mock.ts
18816
- var import_node_crypto = require("crypto");
18817
- var log14 = createLogger("crypto/quantun-plug-mock");
18818
- var MOCK_KEY = Buffer.alloc(32, 0);
18819
- MOCK_KEY.write("quantum-mock-key-2026", 0);
18820
- var MOCK_KEY_ID = "mock-key-v1";
18821
- var initialized = false;
18822
- var quantunPlugMock = {
18823
- /**
18824
- * 初始化 Mock SDK
18825
- * 幂等操作 — 重复调用不会报错
18826
- */
18827
- async init() {
18828
- if (initialized) return;
18829
- initialized = true;
18830
- log14.warn("\u26A0\uFE0F MOCK MODE \u2014 \u4F7F\u7528 AES-256-GCM \u6A21\u62DF\u91CF\u5B50\u52A0\u5BC6");
18831
- },
18832
- /**
18833
- * 加密明文 → 密文 + 密钥标识
18834
- *
18835
- * 内部流程:
18836
- * 1. 生成 12 字节随机 IV (确保每次加密结果不同)
18837
- * 2. AES-256-GCM 加密明文
18838
- * 3. 获取 16 字节 AuthTag (认证标签,防篡改)
18839
- * 4. 拼接 [IV(12) | Tag(16) | Ciphertext] → Base64 编码
18840
- */
18841
- async encrypt(plaintext, mode) {
18842
- if (!initialized) {
18843
- throw new Error("quantunPlug not initialized \u2014 call init() first");
18844
- }
18845
- const iv = (0, import_node_crypto.randomBytes)(12);
18846
- const cipher = (0, import_node_crypto.createCipheriv)("aes-256-gcm", MOCK_KEY, iv);
18847
- const encrypted = Buffer.concat([
18848
- cipher.update(plaintext, "utf-8"),
18849
- cipher.final()
18850
- ]);
18851
- const tag = cipher.getAuthTag();
18852
- const combined = Buffer.concat([iv, tag, encrypted]);
18853
- return {
18854
- ciphertext: combined.toString("base64"),
18855
- keyId: MOCK_KEY_ID
18856
- };
18857
- },
18858
- /**
18859
- * 解密密文 → 明文
18860
- *
18861
- * 内部流程:
18862
- * 1. Base64 解码
18863
- * 2. 拆分 IV(12) + Tag(16) + Ciphertext
18864
- * 3. AES-256-GCM 解密 + AuthTag 验证
18865
- * 4. 返回 UTF-8 明文
18866
- *
18867
- * @throws AuthTag 验证失败时抛出异常 (数据被篡改)
18868
- */
18869
- async decrypt(ciphertext, keyId, mode) {
18870
- if (!initialized) {
18871
- throw new Error("quantunPlug not initialized \u2014 call init() first");
18872
- }
18873
- const combined = Buffer.from(ciphertext, "base64");
18874
- const iv = combined.subarray(0, 12);
18875
- const tag = combined.subarray(12, 28);
18876
- const encrypted = combined.subarray(28);
18877
- const decipher = (0, import_node_crypto.createDecipheriv)("aes-256-gcm", MOCK_KEY, iv);
18878
- decipher.setAuthTag(tag);
18879
- const decrypted = Buffer.concat([
18880
- decipher.update(encrypted),
18881
- decipher.final()
18882
- // 此处 Tag 验证失败会抛出异常
18883
- ]);
18884
- return {
18885
- plaintext: decrypted.toString("utf-8")
18886
- };
18887
- }
18868
+ // src/crypto/quantun-plug-adapter.ts
18869
+ var import_node_module = require("module");
18870
+ var import_meta = {};
18871
+ var log14 = createLogger("crypto/quantun-plug-adapter");
18872
+ var SM4_MODE = {
18873
+ ECB_NOPADDING: 1,
18874
+ ECB_PKCS7PADDING: 2,
18875
+ CBC_NOPADDING: 3,
18876
+ CBC_PKCS7PADDING: 4
18888
18877
  };
18889
- var quantun_plug_mock_default = quantunPlugMock;
18878
+ var DEFAULT_ENCRYPT_MODE = SM4_MODE.CBC_PKCS7PADDING;
18879
+ var require2 = (0, import_node_module.createRequire)(import_meta.url);
18880
+ var quantunPlug = null;
18881
+ try {
18882
+ quantunPlug = require2("./sdk/index.min.cjs");
18883
+ log14.info("Quantum SDK loaded from sdk/index.min.cjs");
18884
+ } catch (err) {
18885
+ const msg = err instanceof Error ? err.message : String(err);
18886
+ log14.warn("Quantum SDK missing, only passthrough mode will work:", msg);
18887
+ }
18888
+ var quantun_plug_adapter_default = quantunPlug;
18889
+
18890
+ // src/crypto/quantum-config-writer.ts
18891
+ var import_node_fs = require("fs");
18892
+ var import_node_path = require("path");
18893
+ var import_node_url = require("url");
18894
+ var import_meta2 = {};
18895
+ var log15 = createLogger("crypto/quantum-config-writer");
18896
+ var QUANTUM_JSON_PATH = (0, import_node_path.join)(
18897
+ (0, import_node_path.dirname)((0, import_node_url.fileURLToPath)(import_meta2.url)),
18898
+ "sdk",
18899
+ "quantum.json"
18900
+ );
18901
+ function writeQuantumConfig(credentials) {
18902
+ const sdkConfig = {
18903
+ account: credentials.quantumAccount,
18904
+ appID: credentials.quantumAppId,
18905
+ pin: credentials.pin,
18906
+ authCode: credentials.quantumAppSecret,
18907
+ url: credentials.quantumUrl
18908
+ };
18909
+ (0, import_node_fs.writeFileSync)(QUANTUM_JSON_PATH, JSON.stringify(sdkConfig, null, 2), "utf-8");
18910
+ log15.info("quantum.json written to", QUANTUM_JSON_PATH);
18911
+ }
18890
18912
 
18891
18913
  // src/crypto/crypto-engine.ts
18892
- var log15 = createLogger("crypto/crypto-engine");
18914
+ var log16 = createLogger("crypto/crypto-engine");
18893
18915
  var PASSTHROUGH_KEY_ID = "passthrough";
18916
+ var FILE_CHUNK_SIZE = 10 * 1024 * 1024;
18917
+ var FILE_DECRYPT_CHUNK_SIZE = FILE_CHUNK_SIZE + 16;
18894
18918
  var CryptoEngine = class _CryptoEngine {
18895
- /** 底层量子加密 SDK 实例 (Mock 或真实), 透传模式下为 null */
18919
+ /** 底层量子加密 SDK 实例, 透传模式下为 null */
18896
18920
  plug;
18897
18921
  /** SDK 是否已完成初始化 */
18898
- initialized = false;
18922
+ initialized;
18899
18923
  /** 是否为透传模式 (不加密, 明文直接透传) */
18900
18924
  passthrough;
18925
+ /** 用户凭据 — 用于生成 quantum.json (透传模式下为 undefined) */
18926
+ credentials;
18901
18927
  /**
18902
- * 构造加密引擎 (加密模式)
18928
+ * 构造加密引擎
18903
18929
  *
18904
- * 当前默认使用 Mock SDK。
18905
- * 待真实 SDK 交付后,此处替换为 require('./quantunPlug.ejs')
18930
+ * @param opts - 构造选项
18931
+ * @param opts.passthrough - true 为透传模式 (不加密); false 为加密模式 (默认)
18932
+ * @param opts.credentials - 用户凭据 (加密模式必需, 用于生成 quantum.json)
18906
18933
  *
18907
- * 如需透传模式,请使用静态方法 CryptoEngine.createPassthrough()。
18934
+ * 请使用静态工厂方法:
18935
+ * - new CryptoEngine({ credentials }) → 加密模式, 使用量子 SDK
18936
+ * - CryptoEngine.createPassthrough() → 透传模式, 无需 init()
18908
18937
  */
18909
- constructor() {
18910
- this.plug = quantun_plug_mock_default;
18911
- this.passthrough = false;
18912
- log15.info("CryptoEngine created (mock mode)");
18938
+ constructor(opts = {}) {
18939
+ const { passthrough = false, credentials } = opts;
18940
+ this.passthrough = passthrough;
18941
+ this.credentials = credentials;
18942
+ this.plug = passthrough ? null : quantun_plug_adapter_default;
18943
+ this.initialized = passthrough;
18944
+ log16.info(`CryptoEngine created (${passthrough ? "passthrough mode \u2014 crypto disabled" : "quantum SDK mode"})`);
18913
18945
  }
18914
18946
  /**
18915
18947
  * 创建透传模式的加密引擎 (静态工厂方法)
@@ -18923,17 +18955,12 @@ var CryptoEngine = class _CryptoEngine {
18923
18955
  * 上层模块 (TokenStore, MessagePipe) 无需感知加密是否开启。
18924
18956
  */
18925
18957
  static createPassthrough() {
18926
- const instance = Object.create(_CryptoEngine.prototype);
18927
- instance.plug = null;
18928
- instance.passthrough = true;
18929
- instance.initialized = true;
18930
- log15.info("CryptoEngine created (passthrough mode \u2014 crypto disabled)");
18931
- return instance;
18958
+ return new _CryptoEngine({ passthrough: true });
18932
18959
  }
18933
18960
  /**
18934
18961
  * 初始化加密引擎
18935
18962
  *
18936
- * 加密模式: 调用 SDK 的 init() 方法完成密钥协商等初始化操作。
18963
+ * 加密模式: 调用 SDK 的 init() 方法完成密服入网、密钥协商等初始化操作。
18937
18964
  * 透传模式: 空操作 (already initialized)。
18938
18965
  * 幂等操作 — 重复调用安全。
18939
18966
  */
@@ -18943,29 +18970,37 @@ var CryptoEngine = class _CryptoEngine {
18943
18970
  this.initialized = true;
18944
18971
  return;
18945
18972
  }
18973
+ if (!this.plug) {
18974
+ throw new Error("Quantum encryption SDK is missing. Cannot initialize in encryption mode (check dist/index.min.cjs).");
18975
+ }
18976
+ if (this.credentials) {
18977
+ writeQuantumConfig(this.credentials);
18978
+ }
18946
18979
  await this.plug.init();
18947
18980
  this.initialized = true;
18948
- log15.info("CryptoEngine initialized \u2713");
18981
+ log16.info("CryptoEngine initialized \u2713");
18949
18982
  }
18950
18983
  /**
18951
18984
  * 加密明文
18952
18985
  *
18953
- * 加密模式: 调用底层 SDK 加密, 返回 Base64 密文 + keyId
18986
+ * 加密模式: 调用底层 SDK 加密 (SM4_CBC_PKCS7PADDING), 返回密文 + keyId
18954
18987
  * 透传模式: 直接返回明文作为 "密文", keyId = 'passthrough'
18955
18988
  *
18989
+ * 注意: SDK 返回驼峰 cipherText, 此方法对外统一为小写 ciphertext
18990
+ *
18956
18991
  * @param plaintext - 要加密的明文字符串
18957
18992
  * @returns { ciphertext: 密文字符串, keyId: 密钥标识 }
18958
18993
  * @throws 加密模式下未初始化时抛出错误
18959
18994
  */
18960
18995
  async encrypt(plaintext) {
18961
- this.ensureInitialized();
18962
18996
  if (this.passthrough) {
18963
- log15.debug("Encrypt (passthrough)", { length: plaintext.length });
18997
+ log16.debug("Encrypt (passthrough)", { length: plaintext.length });
18964
18998
  return { ciphertext: plaintext, keyId: PASSTHROUGH_KEY_ID };
18965
18999
  }
18966
- const result = await this.plug.encrypt(plaintext, 1);
18967
- log15.debug("Encrypted", { length: plaintext.length, keyId: result.keyId });
18968
- return result;
19000
+ const plug = this.requirePlug();
19001
+ const result = await plug.encrypt(plaintext, DEFAULT_ENCRYPT_MODE);
19002
+ log16.debug("Encrypted", { length: plaintext.length, keyId: result.keyId });
19003
+ return { ciphertext: result.cipherText, keyId: result.keyId };
18969
19004
  }
18970
19005
  /**
18971
19006
  * 解密密文
@@ -18979,14 +19014,100 @@ var CryptoEngine = class _CryptoEngine {
18979
19014
  * @throws 加密模式下未初始化、密文损坏或 keyId 不匹配时抛出错误
18980
19015
  */
18981
19016
  async decrypt(ciphertext, keyId) {
18982
- this.ensureInitialized();
18983
19017
  if (this.passthrough) {
18984
- log15.debug("Decrypt (passthrough)", { keyId });
19018
+ log16.debug("Decrypt (passthrough)", { keyId });
18985
19019
  return ciphertext;
18986
19020
  }
18987
- const result = await this.plug.decrypt(ciphertext, keyId, 1);
18988
- log15.debug("Decrypted", { keyId });
18989
- return result.plaintext;
19021
+ const plug = this.requirePlug();
19022
+ const result = await plug.decrypt(ciphertext, keyId, DEFAULT_ENCRYPT_MODE);
19023
+ log16.debug("Decrypted", { keyId });
19024
+ return result.plainText;
19025
+ }
19026
+ /**
19027
+ * 加密文件数据
19028
+ *
19029
+ * 自动处理 10MB 分片编排:
19030
+ * - ≤10MB: 单次调用 SDK encryptFile
19031
+ * - >10MB: 按 10MB 分片, 首片无需传密钥参数, 后续片传入前一片返回的 { keyId, sessionKey, fillKey }
19032
+ *
19033
+ * 透传模式: 直接返回原数据
19034
+ *
19035
+ * @param fileData - 文件数据 Buffer
19036
+ * @returns { fileBuffer: 加密后的文件 Buffer, keyId: 密钥标识 }
19037
+ */
19038
+ async encryptFile(fileData) {
19039
+ if (this.passthrough) {
19040
+ log16.debug("EncryptFile (passthrough)", { size: fileData.length });
19041
+ return { fileBuffer: fileData, keyId: PASSTHROUGH_KEY_ID };
19042
+ }
19043
+ if (fileData.length === 0) {
19044
+ return { fileBuffer: Buffer.alloc(0), keyId: PASSTHROUGH_KEY_ID };
19045
+ }
19046
+ const chunks = [];
19047
+ const total = Math.ceil(fileData.length / FILE_CHUNK_SIZE);
19048
+ let keyId;
19049
+ let sessionKey;
19050
+ let fillKey;
19051
+ const plug = this.requirePlug();
19052
+ log16.debug("EncryptFile", { size: fileData.length, chunks: total });
19053
+ for (let i = 0; i < total; i++) {
19054
+ const start = i * FILE_CHUNK_SIZE;
19055
+ const end = Math.min(start + FILE_CHUNK_SIZE, fileData.length);
19056
+ const chunk = fileData.subarray(start, end);
19057
+ const result = await plug.encryptFile(chunk, DEFAULT_ENCRYPT_MODE, {
19058
+ keyId,
19059
+ sessionKey,
19060
+ fillKey
19061
+ });
19062
+ chunks.push(result.fileBuffer);
19063
+ keyId = result.keyId;
19064
+ sessionKey = result.sessionKey;
19065
+ fillKey = result.fillKey;
19066
+ }
19067
+ log16.debug("EncryptFile done", { keyId, chunks: total });
19068
+ return { fileBuffer: Buffer.concat(chunks), keyId: keyId ?? "" };
19069
+ }
19070
+ /**
19071
+ * 解密文件数据
19072
+ *
19073
+ * 自动处理 (10MB + 16) 分片编排:
19074
+ * - ≤(10MB+16): 单次调用 SDK decryptFile
19075
+ * - >(10MB+16): 按 (10MB+16) 分片, 首片传 keyId, 后续片传入前一片返回的 { sessionKey, fillKey }
19076
+ *
19077
+ * 透传模式: 直接返回原数据
19078
+ *
19079
+ * @param fileData - 加密后的文件 Buffer
19080
+ * @param keyId - 加密时返回的密钥标识
19081
+ * @returns 解密后的文件 Buffer
19082
+ */
19083
+ async decryptFile(fileData, keyId) {
19084
+ if (this.passthrough) {
19085
+ log16.debug("DecryptFile (passthrough)", { keyId });
19086
+ return fileData;
19087
+ }
19088
+ if (fileData.length === 0) {
19089
+ return Buffer.alloc(0);
19090
+ }
19091
+ const plug = this.requirePlug();
19092
+ const chunks = [];
19093
+ const total = Math.ceil(fileData.length / FILE_DECRYPT_CHUNK_SIZE);
19094
+ let sessionKey;
19095
+ let fillKey;
19096
+ log16.debug("DecryptFile", { size: fileData.length, chunks: total, keyId });
19097
+ for (let i = 0; i < total; i++) {
19098
+ const start = i * FILE_DECRYPT_CHUNK_SIZE;
19099
+ const end = Math.min(start + FILE_DECRYPT_CHUNK_SIZE, fileData.length);
19100
+ const chunk = fileData.subarray(start, end);
19101
+ const result = await plug.decryptFile(chunk, keyId, DEFAULT_ENCRYPT_MODE, {
19102
+ sessionKey,
19103
+ fillKey
19104
+ });
19105
+ chunks.push(result.fileBuffer);
19106
+ sessionKey = result.sessionKey;
19107
+ fillKey = result.fillKey;
19108
+ }
19109
+ log16.debug("DecryptFile done", { keyId, chunks: total });
19110
+ return Buffer.concat(chunks);
18990
19111
  }
18991
19112
  /**
18992
19113
  * 查询当前是否为透传模式
@@ -18996,25 +19117,26 @@ var CryptoEngine = class _CryptoEngine {
18996
19117
  return this.passthrough;
18997
19118
  }
18998
19119
  /**
18999
- * 确保引擎已初始化
19000
- * 未初始化时抛出明确错误,避免难以排查的 SDK 内部异常
19120
+ * 确保引擎已初始化,同时返回非空 plug 实例
19121
+ * (通过 init() 守卫保证:加密模式下 init() 成功后 plug 一定非 null)
19001
19122
  */
19002
- ensureInitialized() {
19123
+ requirePlug() {
19003
19124
  if (!this.initialized) {
19004
19125
  throw new Error("CryptoEngine not initialized \u2014 call init() first");
19005
19126
  }
19127
+ return this.plug;
19006
19128
  }
19007
19129
  };
19008
19130
 
19009
19131
  // src/auth/oauth-client.ts
19010
- var log16 = createLogger("auth/oauth-client");
19011
- var SealApiError = class extends Error {
19132
+ var log17 = createLogger("auth/oauth-client");
19133
+ var ApiError = class extends Error {
19012
19134
  constructor(status, body, endpoint) {
19013
- super(`Seal API error: ${status} on ${endpoint}`);
19135
+ super(`API error: ${status} on ${endpoint}`);
19014
19136
  this.status = status;
19015
19137
  this.body = body;
19016
19138
  this.endpoint = endpoint;
19017
- this.name = "SealApiError";
19139
+ this.name = "ApiError";
19018
19140
  }
19019
19141
  };
19020
19142
  var TokenAcquireError = class extends Error {
@@ -19031,28 +19153,21 @@ function sleep(ms) {
19031
19153
  return new Promise((resolve2) => setTimeout(resolve2, ms));
19032
19154
  }
19033
19155
  var DEFAULT_TIMEOUT_MS = 3e4;
19034
- var SealClient = class {
19156
+ var GRANT_TYPE = "client_credentials";
19157
+ var SCOPE = "client_credentials refresh_token";
19158
+ var OAuthClient = class {
19035
19159
  baseUrl;
19036
- clientId;
19037
- clientSecret;
19038
- clientName;
19039
- scopes;
19160
+ appId;
19161
+ appSecret;
19040
19162
  constructor(config2) {
19041
- this.baseUrl = config2.serverUrl.replace(/\/+$/, "");
19042
- this.clientId = config2.clientId;
19043
- this.clientSecret = config2.clientSecret;
19044
- this.clientName = config2.clientName;
19045
- this.scopes = config2.scopes;
19046
- log16.info("SealClient initialized", { serverUrl: this.baseUrl, clientName: config2.clientName });
19163
+ this.baseUrl = config2.baseUrl.replace(/\/+$/, "");
19164
+ this.appId = config2.appId;
19165
+ this.appSecret = config2.appSecret;
19166
+ log17.info("OAuthClient initialized", { baseUrl: this.baseUrl, appId: sanitize(this.appId) });
19047
19167
  }
19048
19168
  // ── 通用 HTTP 请求 ──
19049
19169
  /**
19050
19170
  * 统一 HTTP 请求封装 — 含超时、日志脱敏和错误处理
19051
- * @param method - HTTP 方法
19052
- * @param path - 请求路径 (如 '/seal/client/register')
19053
- * @param options - 请求选项
19054
- * @returns 解析后的 JSON 响应
19055
- * @throws SealApiError — HTTP 非 2xx 响应
19056
19171
  */
19057
19172
  async request(method, path2, options = {}) {
19058
19173
  const url2 = `${this.baseUrl}${path2}`;
@@ -19068,7 +19183,7 @@ var SealClient = class {
19068
19183
  bodyStr = JSON.stringify(options.body);
19069
19184
  }
19070
19185
  }
19071
- log16.debug("HTTP request", { method, url: url2 });
19186
+ log17.debug("HTTP request", { method, url: url2 });
19072
19187
  const response = await fetch(url2, {
19073
19188
  method,
19074
19189
  headers,
@@ -19083,110 +19198,39 @@ var SealClient = class {
19083
19198
  } catch {
19084
19199
  parsedBody = responseBody;
19085
19200
  }
19086
- log16.error("HTTP error", { method, url: url2, status: response.status });
19087
- throw new SealApiError(response.status, parsedBody, path2);
19201
+ log17.error("HTTP error", { method, url: url2, status: response.status });
19202
+ throw new ApiError(response.status, parsedBody, path2);
19088
19203
  }
19089
- log16.debug("HTTP response", { method, url: url2, status: response.status });
19204
+ log17.debug("HTTP response", { method, url: url2, status: response.status });
19090
19205
  try {
19091
19206
  return JSON.parse(responseBody);
19092
19207
  } catch {
19093
- throw new SealApiError(response.status, responseBody, path2);
19208
+ throw new ApiError(response.status, responseBody, path2);
19094
19209
  }
19095
19210
  }
19096
19211
  // ── 公共方法 ──
19097
- /** 判断是否已注册 (有 clientId + clientSecret) */
19098
- isRegistered() {
19099
- return !!(this.clientId && this.clientSecret);
19100
- }
19101
- /** 更新 clientId / clientSecret (注册成功后或从持久化加载后调用) */
19102
- setCredentials(clientId, clientSecret) {
19103
- this.clientId = clientId;
19104
- this.clientSecret = clientSecret;
19105
- log16.info("Credentials updated", { clientId: sanitize(clientId) });
19106
- }
19107
19212
  /**
19108
- * 动态注册客户端 → POST /seal/client/register
19213
+ * 获取访问令牌 → POST /auth/token (client_credentials)
19109
19214
  *
19110
- * 首次运行时自动调用,获取 clientId 和 clientSecret。
19111
- * 注册成功后自动更新内部凭据。
19112
- */
19113
- async register(params) {
19114
- const body = {
19115
- client_name: params.clientName,
19116
- scopes: params.scopes,
19117
- authentication_methods: params.authMethods ?? ["client_secret_post"],
19118
- grant_types: params.grantTypes ?? ["authorization_code", "client_credentials", "refresh_token"],
19119
- redirect_uris: params.redirectUris ?? ["https://placeholder.local"],
19120
- logout_redirect_uris: [],
19121
- client_settings: {
19122
- "settings.client.require-authorization-consent": true
19123
- }
19124
- };
19125
- if (params.tokenSettings) {
19126
- const ts = {};
19127
- if (params.tokenSettings.accessTokenTtl) {
19128
- ts["settings.token.access-token-time-to-live"] = params.tokenSettings.accessTokenTtl;
19129
- }
19130
- if (params.tokenSettings.refreshTokenTtl) {
19131
- ts["settings.token.refresh-token-time-to-live"] = params.tokenSettings.refreshTokenTtl;
19132
- }
19133
- if (params.tokenSettings.accessTokenFormat) {
19134
- ts["settings.token.access-token-format"] = params.tokenSettings.accessTokenFormat;
19135
- }
19136
- if (params.tokenSettings.reuseRefreshTokens !== void 0) {
19137
- ts["settings.token.reuse-refresh-tokens"] = params.tokenSettings.reuseRefreshTokens;
19138
- }
19139
- ts["settings.token.authorization-code-time-to-live"] = "300";
19140
- body.token_settings = ts;
19141
- }
19142
- log16.info("Registering OAuth client", { clientName: params.clientName });
19143
- const response = await this.request(
19144
- "POST",
19145
- "/seal/client/register",
19146
- { body, contentType: "json" }
19147
- );
19148
- const registration = {
19149
- clientId: response.client_id,
19150
- clientSecret: response.client_secret,
19151
- clientSecretExpiresAt: response.client_secret_expires_at ?? "",
19152
- clientIdIssuedAt: response.client_id_issued_at ?? "",
19153
- scopes: response.scopes ?? params.scopes
19154
- };
19155
- this.setCredentials(registration.clientId, registration.clientSecret);
19156
- log16.info("OAuth client registered", {
19157
- clientId: sanitize(registration.clientId),
19158
- clientSecret: sanitize(registration.clientSecret),
19159
- scopes: registration.scopes,
19160
- expiresAt: registration.clientSecretExpiresAt
19161
- });
19162
- return registration;
19163
- }
19164
- /**
19165
- * 获取访问令牌 → POST /seal/oauth2/token (client_credentials)
19215
+ * 请求参数 (x-www-form-urlencoded):
19216
+ * - grant_type: client_credentials (写死)
19217
+ * - client_id: appId
19218
+ * - client_secret: appSecret
19219
+ * - scope: client_credentials refresh_token (写死)
19166
19220
  *
19167
- * 使用客户端凭证模式获取 Access Token。
19168
- * 前置条件: clientId 和 clientSecret 必须存在 (通过 register 或 setCredentials 设置)
19169
- *
19170
- * @param scope - 请求的权限范围 (空格分隔),不传则使用注册时的全部 scope
19171
19221
  * @returns TokenData 包含 access_token、过期时间等
19172
- * @throws ErrorclientId/clientSecret 未设置
19173
- * @throws SealApiError — HTTP 错误
19222
+ * @throws ApiErrorHTTP 错误
19174
19223
  */
19175
- async getToken(scope) {
19176
- if (!this.clientId || !this.clientSecret) {
19177
- throw new Error("Cannot get token: clientId and clientSecret are required. Call register() or setCredentials() first.");
19178
- }
19224
+ async getToken() {
19179
19225
  const params = new URLSearchParams();
19180
- params.set("grant_type", "client_credentials");
19181
- params.set("client_id", this.clientId);
19182
- params.set("client_secret", this.clientSecret);
19183
- if (scope) {
19184
- params.set("scope", scope);
19185
- }
19186
- log16.info("Requesting access token", { clientId: sanitize(this.clientId) });
19226
+ params.set("grant_type", GRANT_TYPE);
19227
+ params.set("client_id", this.appId);
19228
+ params.set("client_secret", this.appSecret);
19229
+ params.set("scope", SCOPE);
19230
+ log17.info("Requesting access token", { appId: sanitize(this.appId) });
19187
19231
  const response = await this.request(
19188
19232
  "POST",
19189
- "/seal/oauth2/token",
19233
+ "/auth/token",
19190
19234
  { body: params, contentType: "form" }
19191
19235
  );
19192
19236
  const now = Date.now();
@@ -19197,32 +19241,28 @@ var SealClient = class {
19197
19241
  expiresIn,
19198
19242
  scope: response.scope ?? "",
19199
19243
  refreshToken: void 0,
19200
- // Seal 不返回 refresh_token
19201
19244
  grantedAt: now,
19202
19245
  expiresAt: now + expiresIn * 1e3
19203
19246
  };
19204
- log16.info("Access token acquired", {
19247
+ log17.info("Access token acquired", {
19205
19248
  accessToken: sanitize(tokenData.accessToken),
19206
19249
  expiresIn: tokenData.expiresIn,
19207
19250
  scope: tokenData.scope
19208
19251
  });
19209
19252
  return tokenData;
19210
19253
  }
19211
- /**
19212
- * 令牌校验 → GET /seal/oauth2/introspect
19213
- * 暂不实现 — 保留接口签名,后续需要时再补充
19214
- */
19215
- async introspect(_token) {
19216
- throw new Error("introspect() not implemented yet \u2014 Seal introspect API \u6682\u4E0D\u4F7F\u7528");
19254
+ /** 获取 baseUrl (供其他模块复用) */
19255
+ getBaseUrl() {
19256
+ return this.baseUrl;
19217
19257
  }
19218
19258
  };
19219
19259
 
19220
19260
  // src/auth/token-store.ts
19221
19261
  var import_promises = require("fs/promises");
19222
- var import_node_fs = require("fs");
19223
- var import_node_path = require("path");
19224
- var import_node_crypto2 = require("crypto");
19225
- var log17 = createLogger("auth/token-store");
19262
+ var import_node_fs2 = require("fs");
19263
+ var import_node_path2 = require("path");
19264
+ var import_node_crypto = require("crypto");
19265
+ var log18 = createLogger("auth/token-store");
19226
19266
  var TokenStore = class {
19227
19267
  /** 存储目录路径 */
19228
19268
  storageDir;
@@ -19237,7 +19277,7 @@ var TokenStore = class {
19237
19277
  constructor(storageDir, crypto) {
19238
19278
  this.storageDir = storageDir;
19239
19279
  this.crypto = crypto;
19240
- log17.info("TokenStore initialized", { storageDir });
19280
+ log18.info("TokenStore initialized", { storageDir });
19241
19281
  }
19242
19282
  /**
19243
19283
  * 确保存储目录存在并设置正确的权限。
@@ -19245,9 +19285,9 @@ var TokenStore = class {
19245
19285
  */
19246
19286
  async ensureDir() {
19247
19287
  if (this.dirEnsured) return;
19248
- if (!(0, import_node_fs.existsSync)(this.storageDir)) {
19288
+ if (!(0, import_node_fs2.existsSync)(this.storageDir)) {
19249
19289
  await (0, import_promises.mkdir)(this.storageDir, { recursive: true, mode: 448 });
19250
- log17.debug("Storage directory created", { dir: this.storageDir });
19290
+ log18.debug("Storage directory created", { dir: this.storageDir });
19251
19291
  }
19252
19292
  this.dirEnsured = true;
19253
19293
  }
@@ -19256,7 +19296,7 @@ var TokenStore = class {
19256
19296
  * @param key - 存储键名 (如 'token', 'credentials')
19257
19297
  */
19258
19298
  filePath(key) {
19259
- return (0, import_node_path.join)(this.storageDir, `${key}.enc`);
19299
+ return (0, import_node_path2.join)(this.storageDir, `${key}.enc`);
19260
19300
  }
19261
19301
  /**
19262
19302
  * 保存 Token — 加密写入文件
@@ -19277,12 +19317,12 @@ var TokenStore = class {
19277
19317
  const storage = { ciphertext, keyId };
19278
19318
  const storageJson = JSON.stringify(storage);
19279
19319
  const targetPath = this.filePath(key);
19280
- const tmpSuffix = (0, import_node_crypto2.randomBytes)(4).toString("hex");
19320
+ const tmpSuffix = (0, import_node_crypto.randomBytes)(4).toString("hex");
19281
19321
  const tmpPath = `${targetPath}.${tmpSuffix}.tmp`;
19282
19322
  try {
19283
19323
  await (0, import_promises.writeFile)(tmpPath, storageJson, { mode: 384 });
19284
19324
  await (0, import_promises.rename)(tmpPath, targetPath);
19285
- log17.debug("Token saved", { key, path: targetPath });
19325
+ log18.debug("Token saved", { key, path: targetPath });
19286
19326
  } catch (err) {
19287
19327
  try {
19288
19328
  await (0, import_promises.unlink)(tmpPath);
@@ -19304,8 +19344,8 @@ var TokenStore = class {
19304
19344
  */
19305
19345
  async load(key) {
19306
19346
  const filePath = this.filePath(key);
19307
- if (!(0, import_node_fs.existsSync)(filePath)) {
19308
- log17.debug("Token file not found", { key, path: filePath });
19347
+ if (!(0, import_node_fs2.existsSync)(filePath)) {
19348
+ log18.debug("Token file not found", { key, path: filePath });
19309
19349
  return null;
19310
19350
  }
19311
19351
  try {
@@ -19313,10 +19353,10 @@ var TokenStore = class {
19313
19353
  const storage = JSON.parse(raw);
19314
19354
  const json2 = await this.crypto.decrypt(storage.ciphertext, storage.keyId);
19315
19355
  const data = JSON.parse(json2);
19316
- log17.debug("Token loaded", { key, path: filePath });
19356
+ log18.debug("Token loaded", { key, path: filePath });
19317
19357
  return data;
19318
19358
  } catch (err) {
19319
- log17.warn("Failed to load token (corrupted or key mismatch), treating as empty", {
19359
+ log18.warn("Failed to load token (corrupted or key mismatch), treating as empty", {
19320
19360
  key,
19321
19361
  error: err instanceof Error ? err.message : String(err)
19322
19362
  });
@@ -19332,7 +19372,7 @@ var TokenStore = class {
19332
19372
  const filePath = this.filePath(key);
19333
19373
  try {
19334
19374
  await (0, import_promises.unlink)(filePath);
19335
- log17.debug("Token file cleared", { key, path: filePath });
19375
+ log18.debug("Token file cleared", { key, path: filePath });
19336
19376
  } catch (err) {
19337
19377
  if (err.code !== "ENOENT") {
19338
19378
  throw err;
@@ -19342,17 +19382,15 @@ var TokenStore = class {
19342
19382
  };
19343
19383
 
19344
19384
  // src/auth/token-manager.ts
19345
- var log18 = createLogger("auth/token-manager");
19385
+ var log19 = createLogger("auth/token-manager");
19346
19386
  var DEFAULT_REFRESH_AHEAD_MS = 5 * 60 * 1e3;
19347
19387
  var MAX_RETRIES = 3;
19348
19388
  var RETRY_BASE_MS = 1e3;
19349
19389
  var TokenManager = class {
19350
- sealClient;
19390
+ oauthClient;
19351
19391
  tokenStore;
19352
19392
  /** Token 过期前提前刷新的时间 (ms) */
19353
19393
  refreshAheadMs;
19354
- /** 自动注册的参数 */
19355
- autoRegisterParams;
19356
19394
  // ── 内存缓存 ──
19357
19395
  /** 内存中缓存的 access_token */
19358
19396
  cachedToken = null;
@@ -19366,11 +19404,10 @@ var TokenManager = class {
19366
19404
  /** 定时刷新器句柄 */
19367
19405
  refreshTimer = null;
19368
19406
  constructor(deps) {
19369
- this.sealClient = deps.sealClient;
19407
+ this.oauthClient = deps.oauthClient;
19370
19408
  this.tokenStore = deps.tokenStore;
19371
19409
  this.refreshAheadMs = deps.refreshAheadMs ?? DEFAULT_REFRESH_AHEAD_MS;
19372
- this.autoRegisterParams = deps.autoRegisterParams;
19373
- log18.info("TokenManager initialized", { refreshAheadMs: this.refreshAheadMs });
19410
+ log19.info("TokenManager initialized", { refreshAheadMs: this.refreshAheadMs });
19374
19411
  }
19375
19412
  // ── 核心方法 ──
19376
19413
  /**
@@ -19388,7 +19425,7 @@ var TokenManager = class {
19388
19425
  return this.cachedToken;
19389
19426
  }
19390
19427
  if (this.refreshLock) {
19391
- log18.debug("Waiting for concurrent token acquisition");
19428
+ log19.debug("Waiting for concurrent token acquisition");
19392
19429
  return this.refreshLock;
19393
19430
  }
19394
19431
  this.refreshLock = this._acquireTokenWithRetry();
@@ -19398,13 +19435,6 @@ var TokenManager = class {
19398
19435
  this.refreshLock = null;
19399
19436
  }
19400
19437
  }
19401
- /**
19402
- * 令牌自省 — 暂不实现,后续补充。
19403
- * 返回的 identity_id 用于 gate.ts 的 anti-loop 检查 (bot 自己的 ID)。
19404
- */
19405
- async introspect() {
19406
- throw new Error("introspect() not implemented yet \u2014 Seal introspect API \u6682\u4E0D\u4F7F\u7528");
19407
- }
19408
19438
  /** 检查当前令牌是否包含指定的 scope */
19409
19439
  hasScope(scope) {
19410
19440
  if (!this.cachedScope) return false;
@@ -19421,13 +19451,13 @@ var TokenManager = class {
19421
19451
  this.cachedToken = null;
19422
19452
  this.cachedScope = null;
19423
19453
  this.currentTokenData = null;
19424
- log18.info("Token revoked and cleared");
19454
+ log19.info("Token revoked and cleared");
19425
19455
  }
19426
19456
  /** 清理定时器和并发锁 — 优雅关闭时调用 */
19427
19457
  shutdown() {
19428
19458
  this.clearRefreshTimer();
19429
19459
  this.refreshLock = null;
19430
- log18.info("TokenManager shutdown");
19460
+ log19.info("TokenManager shutdown");
19431
19461
  }
19432
19462
  // ── 内部方法 ──
19433
19463
  /**
@@ -19435,26 +19465,26 @@ var TokenManager = class {
19435
19465
  *
19436
19466
  * 重试策略:
19437
19467
  * - 网络错误 / 5xx → 重试 (最多 3 次)
19438
- * - 401/403 → 不重试 (client_secret 可能无效)
19468
+ * - 401/403 → 不重试 (凭证可能无效)
19439
19469
  */
19440
19470
  async _acquireTokenWithRetry() {
19441
19471
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
19442
19472
  try {
19443
19473
  return await this._acquireToken();
19444
19474
  } catch (err) {
19445
- if (err instanceof SealApiError && (err.status === 401 || err.status === 403)) {
19446
- log18.error("Token acquire failed with auth error, not retrying", { status: err.status });
19475
+ if (err instanceof ApiError && (err.status === 401 || err.status === 403)) {
19476
+ log19.error("Token acquire failed with auth error, not retrying", { status: err.status });
19447
19477
  throw err;
19448
19478
  }
19449
19479
  if (attempt === MAX_RETRIES) {
19450
- log18.error("Token acquire failed after all retries", { attempts: attempt + 1 });
19480
+ log19.error("Token acquire failed after all retries", { attempts: attempt + 1 });
19451
19481
  throw new TokenAcquireError(
19452
19482
  `Token acquire failed after ${attempt + 1} attempts`,
19453
19483
  { cause: err instanceof Error ? err : void 0 }
19454
19484
  );
19455
19485
  }
19456
19486
  const delay = RETRY_BASE_MS * Math.pow(2, attempt);
19457
- log18.warn("Token acquire failed, retrying", {
19487
+ log19.warn("Token acquire failed, retrying", {
19458
19488
  attempt: attempt + 1,
19459
19489
  maxRetries: MAX_RETRIES,
19460
19490
  retryInMs: delay,
@@ -19470,63 +19500,27 @@ var TokenManager = class {
19470
19500
  *
19471
19501
  * 流程:
19472
19502
  * 1. 尝试从 TokenStore 加载缓存
19473
- * 2. 检查是否需要注册客户端
19474
- * 3. 调用 SealClient.getToken() 获取新 Token
19475
- * 4. 保存到内存缓存 + TokenStore
19476
- * 5. 设置定时刷新器
19503
+ * 2. 调用 OAuthClient.getToken() 获取新 Token
19504
+ * 3. 保存到内存缓存 + TokenStore
19505
+ * 4. 设置定时刷新器
19477
19506
  */
19478
19507
  async _acquireToken() {
19479
19508
  try {
19480
19509
  const stored = await this.tokenStore.load("token");
19481
19510
  if (stored && !this.isTokenHardExpired(stored)) {
19482
- log18.info("Token loaded from store (still valid)");
19511
+ log19.info("Token loaded from store (still valid)");
19483
19512
  this.updateCache(stored);
19484
19513
  this.scheduleRefresh(stored);
19485
19514
  return stored.accessToken;
19486
19515
  }
19487
19516
  if (stored) {
19488
- log18.info("Stored token has expired, acquiring new one");
19517
+ log19.info("Stored token has expired, acquiring new one");
19489
19518
  }
19490
19519
  } catch {
19491
- log18.warn("Failed to load token from store, acquiring new one");
19520
+ log19.warn("Failed to load token from store, acquiring new one");
19492
19521
  }
19493
- if (!this.sealClient.isRegistered()) {
19494
- try {
19495
- const savedCreds = await this.tokenStore.load("credentials");
19496
- if (savedCreds?.clientId && savedCreds?.clientSecret) {
19497
- this.sealClient.setCredentials(
19498
- savedCreds.clientId,
19499
- savedCreds.clientSecret
19500
- );
19501
- log18.info("Credentials loaded from store");
19502
- }
19503
- } catch {
19504
- log18.debug("No saved credentials found");
19505
- }
19506
- }
19507
- if (!this.sealClient.isRegistered()) {
19508
- if (!this.autoRegisterParams) {
19509
- throw new Error("SealClient not registered and autoRegisterParams not provided. Call setCredentials() or enable autoRegister.");
19510
- }
19511
- log18.info("Auto-registering OAuth client");
19512
- const registration = await this.sealClient.register({
19513
- clientName: this.autoRegisterParams.clientName,
19514
- scopes: this.autoRegisterParams.scopes,
19515
- tokenSettings: this.autoRegisterParams.tokenSettings ? {
19516
- accessTokenTtl: this.autoRegisterParams.tokenSettings.accessTokenTtl,
19517
- refreshTokenTtl: this.autoRegisterParams.tokenSettings.refreshTokenTtl,
19518
- accessTokenFormat: this.autoRegisterParams.tokenSettings.accessTokenFormat
19519
- } : void 0
19520
- });
19521
- await this.tokenStore.save("credentials", {
19522
- clientId: registration.clientId,
19523
- clientSecret: registration.clientSecret,
19524
- clientSecretExpiresAt: registration.clientSecretExpiresAt
19525
- });
19526
- log18.info("OAuth client registered and credentials saved");
19527
- }
19528
- log18.info("Acquiring new access token");
19529
- const tokenData = await this.sealClient.getToken();
19522
+ log19.info("Acquiring new access token via POST /auth/token");
19523
+ const tokenData = await this.oauthClient.getToken();
19530
19524
  this.updateCache(tokenData);
19531
19525
  await this.tokenStore.save("token", tokenData);
19532
19526
  this.scheduleRefresh(tokenData);
@@ -19556,22 +19550,22 @@ var TokenManager = class {
19556
19550
  this.clearRefreshTimer();
19557
19551
  const refreshInMs = tokenData.expiresAt - this.refreshAheadMs - Date.now();
19558
19552
  if (refreshInMs <= 0) {
19559
- log18.debug("Token already within refresh window, not scheduling timer");
19553
+ log19.debug("Token already within refresh window, not scheduling timer");
19560
19554
  return;
19561
19555
  }
19562
- log18.debug("Scheduling token refresh", {
19556
+ log19.debug("Scheduling token refresh", {
19563
19557
  refreshInMs,
19564
19558
  refreshAt: new Date(Date.now() + refreshInMs).toISOString()
19565
19559
  });
19566
19560
  this.refreshTimer = setTimeout(async () => {
19567
19561
  try {
19568
- log18.info("Scheduled token refresh triggered");
19562
+ log19.info("Scheduled token refresh triggered");
19569
19563
  this.cachedToken = null;
19570
19564
  this.currentTokenData = null;
19571
19565
  await this.getValidToken();
19572
- log18.info("Scheduled token refresh completed");
19566
+ log19.info("Scheduled token refresh completed");
19573
19567
  } catch (err) {
19574
- log18.error("Scheduled token refresh failed", {
19568
+ log19.error("Scheduled token refresh failed", {
19575
19569
  error: err instanceof Error ? err.message : String(err)
19576
19570
  });
19577
19571
  }
@@ -19599,13 +19593,13 @@ var wrapper_default = import_websocket.default;
19599
19593
 
19600
19594
  // src/transport/ws-client.ts
19601
19595
  var import_node_events = require("events");
19602
- var log19 = createLogger("transport/ws-client");
19596
+ var log20 = createLogger("transport/ws-client");
19603
19597
  var WSClient = class extends import_node_events.EventEmitter {
19604
19598
  /** 底层 WebSocket 实例 */
19605
19599
  ws = null;
19606
19600
  constructor() {
19607
19601
  super();
19608
- log19.info("WSClient initialized");
19602
+ log20.info("WSClient initialized");
19609
19603
  }
19610
19604
  /**
19611
19605
  * 连接到 WebSocket 服务器并绑定事件。
@@ -19621,11 +19615,11 @@ var WSClient = class extends import_node_events.EventEmitter {
19621
19615
  this.ws = null;
19622
19616
  }
19623
19617
  const { url: url2, protocols, headers } = options;
19624
- log19.info("ws:connecting", { url: url2 });
19618
+ log20.info("ws:connecting", { url: url2 });
19625
19619
  return new Promise((resolve2, reject) => {
19626
19620
  const ws = new wrapper_default(url2, protocols, { headers });
19627
19621
  ws.on("open", () => {
19628
- log19.info("ws:connected", { url: url2 });
19622
+ log20.info("ws:connected", { url: url2 });
19629
19623
  this.emit("open");
19630
19624
  resolve2();
19631
19625
  });
@@ -19633,11 +19627,11 @@ var WSClient = class extends import_node_events.EventEmitter {
19633
19627
  this.emit("message", data);
19634
19628
  });
19635
19629
  ws.on("close", (code, reason) => {
19636
- log19.info("ws:closed", { code, reason: reason.toString() });
19630
+ log20.info("ws:closed", { code, reason: reason.toString() });
19637
19631
  this.emit("close", code, reason.toString());
19638
19632
  });
19639
19633
  ws.on("error", (err) => {
19640
- log19.error("ws:error", { error: err.message });
19634
+ log20.error("ws:error", { error: err.message });
19641
19635
  this.emit("error", err);
19642
19636
  if (this.ws !== ws) {
19643
19637
  reject(err);
@@ -19649,7 +19643,7 @@ var WSClient = class extends import_node_events.EventEmitter {
19649
19643
  /** 发送数据到服务器 — 前置检查连接状态 */
19650
19644
  send(data) {
19651
19645
  if (!this.ws || this.ws.readyState !== wrapper_default.OPEN) {
19652
- log19.warn("ws:send skipped, not connected", { readyState: this.ws?.readyState });
19646
+ log20.warn("ws:send skipped, not connected", { readyState: this.ws?.readyState });
19653
19647
  return;
19654
19648
  }
19655
19649
  this.ws.send(data);
@@ -19660,7 +19654,7 @@ var WSClient = class extends import_node_events.EventEmitter {
19660
19654
  this.ws.removeAllListeners();
19661
19655
  this.ws.close(code, reason);
19662
19656
  this.ws = null;
19663
- log19.info("ws:close requested", { code, reason });
19657
+ log20.info("ws:close requested", { code, reason });
19664
19658
  }
19665
19659
  }
19666
19660
  /** 当前连接状态 (0=CONNECTING, 1=OPEN, 2=CLOSING, 3=CLOSED) */
@@ -19670,7 +19664,7 @@ var WSClient = class extends import_node_events.EventEmitter {
19670
19664
  };
19671
19665
 
19672
19666
  // src/transport/dedup.ts
19673
- var log20 = createLogger("transport/dedup");
19667
+ var log21 = createLogger("transport/dedup");
19674
19668
  var MessageDedup = class {
19675
19669
  /** 去重缓存生存时间 (ms) */
19676
19670
  ttlMs;
@@ -19681,7 +19675,7 @@ var MessageDedup = class {
19681
19675
  constructor(config2) {
19682
19676
  this.ttlMs = config2?.ttlMs ?? 3e5;
19683
19677
  this.maxEntries = config2?.maxEntries ?? 5e3;
19684
- log20.info("MessageDedup initialized", { ttlMs: this.ttlMs, maxEntries: this.maxEntries });
19678
+ log21.info("MessageDedup initialized", { ttlMs: this.ttlMs, maxEntries: this.maxEntries });
19685
19679
  }
19686
19680
  /**
19687
19681
  * 检查消息是否重复。
@@ -19716,8 +19710,8 @@ var MessageDedup = class {
19716
19710
  };
19717
19711
 
19718
19712
  // src/transport/message-pipe.ts
19719
- var import_node_crypto3 = require("crypto");
19720
- var log21 = createLogger("transport/message-pipe");
19713
+ var import_node_crypto2 = require("crypto");
19714
+ var log22 = createLogger("transport/message-pipe");
19721
19715
  var MessagePipe = class {
19722
19716
  wsClient;
19723
19717
  dedup;
@@ -19736,15 +19730,15 @@ var MessagePipe = class {
19736
19730
  this.messageServiceBaseUrl = deps.messageServiceBaseUrl;
19737
19731
  this.wsClient.on("message", (rawData) => {
19738
19732
  this.handleInbound(rawData).catch((err) => {
19739
- log21.error("inbound:error", { step: "handleInbound", error: err.message });
19733
+ log22.error("inbound:error", { step: "handleInbound", error: err.message });
19740
19734
  });
19741
19735
  });
19742
- log21.info("MessagePipe initialized");
19736
+ log22.info("MessagePipe initialized");
19743
19737
  }
19744
19738
  /** 注册入站消息回调 — 解密后的消息会通过此回调传给 L4 层 */
19745
19739
  onMessage(callback) {
19746
19740
  this.messageCallback = callback;
19747
- log21.info("Inbound message callback registered");
19741
+ log22.info("Inbound message callback registered");
19748
19742
  }
19749
19743
  /**
19750
19744
  * 出站发送 — 通过 HTTP API 发送消息到 IM 服务器。
@@ -19764,7 +19758,7 @@ var MessagePipe = class {
19764
19758
  }
19765
19759
  const result = await this.callMessageApi("/messages/v1/send", body, "outbound");
19766
19760
  if (!result) return;
19767
- log21.info("outbound:sent", {
19761
+ log22.info("outbound:sent", {
19768
19762
  chatId: msg.chatId,
19769
19763
  senderId: msg.senderId,
19770
19764
  msgType: msg.msgType,
@@ -19788,7 +19782,7 @@ var MessagePipe = class {
19788
19782
  };
19789
19783
  const result = await this.callMessageApi("/messages/v1/recall", body, "recall");
19790
19784
  if (!result) return;
19791
- log21.info("recall:sent", {
19785
+ log22.info("recall:sent", {
19792
19786
  messageId: params.messageId,
19793
19787
  requestId: result.request_id
19794
19788
  });
@@ -19815,7 +19809,7 @@ var MessagePipe = class {
19815
19809
  body: JSON.stringify(body)
19816
19810
  });
19817
19811
  if (!resp.ok) {
19818
- log21.error(`${logTag}:http-error`, {
19812
+ log22.error(`${logTag}:http-error`, {
19819
19813
  status: resp.status,
19820
19814
  statusText: resp.statusText,
19821
19815
  url: url2
@@ -19824,7 +19818,7 @@ var MessagePipe = class {
19824
19818
  }
19825
19819
  const result = await resp.json();
19826
19820
  if (result.code !== 0) {
19827
- log21.error(`${logTag}:api-error`, {
19821
+ log22.error(`${logTag}:api-error`, {
19828
19822
  code: result.code,
19829
19823
  msg: result.msg,
19830
19824
  requestId: result.request_id
@@ -19833,7 +19827,7 @@ var MessagePipe = class {
19833
19827
  }
19834
19828
  return result;
19835
19829
  } catch (err) {
19836
- log21.error(`${logTag}:network-error`, {
19830
+ log22.error(`${logTag}:network-error`, {
19837
19831
  url: url2,
19838
19832
  error: err.message
19839
19833
  });
@@ -19855,7 +19849,7 @@ var MessagePipe = class {
19855
19849
  try {
19856
19850
  frame = JSON.parse(String(rawData));
19857
19851
  } catch (err) {
19858
- log21.warn("inbound:json-parse-error", { error: err.message });
19852
+ log22.warn("inbound:json-parse-error", { error: err.message });
19859
19853
  return;
19860
19854
  }
19861
19855
  const timestamp = frame["X-CTQ-Timestamp"];
@@ -19864,9 +19858,9 @@ var MessagePipe = class {
19864
19858
  if (timestamp && nonce && signature) {
19865
19859
  const token = await this.tokenFn();
19866
19860
  const signatureInput = timestamp + nonce + frame.data;
19867
- const expected = (0, import_node_crypto3.createHmac)("sha256", token).update(signatureInput).digest("hex");
19861
+ const expected = (0, import_node_crypto2.createHmac)("sha256", token).update(signatureInput).digest("hex");
19868
19862
  if (expected !== signature) {
19869
- log21.warn("inbound:signature-fail", { timestamp, nonce });
19863
+ log22.warn("inbound:signature-fail", { timestamp, nonce });
19870
19864
  return;
19871
19865
  }
19872
19866
  }
@@ -19880,30 +19874,37 @@ var MessagePipe = class {
19880
19874
  try {
19881
19875
  callbackData = JSON.parse(dataStr);
19882
19876
  } catch (err) {
19883
- log21.warn("inbound:data-parse-error", { error: err.message });
19877
+ log22.warn("inbound:data-parse-error", { error: err.message });
19884
19878
  return;
19885
19879
  }
19886
- if (callbackData.eventType !== "MessageReceived") {
19887
- log21.debug("inbound:event-ignored", { eventType: callbackData.eventType });
19880
+ if (callbackData.eventType !== "callback:direct") {
19881
+ log22.debug("inbound:event-ignored", { eventType: callbackData.eventType });
19888
19882
  return;
19889
19883
  }
19890
- const msg = callbackData.content;
19891
- log21.debug("inbound:frame", { messageId: msg.messageId, eventType: callbackData.eventType });
19884
+ const msg = {
19885
+ messageId: callbackData.msgUid,
19886
+ chatId: callbackData.groupId || callbackData.userId,
19887
+ senderId: callbackData.userId,
19888
+ msgType: callbackData.type,
19889
+ content: JSON.stringify(callbackData.content),
19890
+ timestamp: Date.now()
19891
+ };
19892
+ log22.debug("inbound:frame", { messageId: msg.messageId, eventType: callbackData.eventType });
19892
19893
  if (this.dedup.isDuplicate(msg.messageId)) {
19893
- log21.debug("inbound:dedup-hit", { messageId: msg.messageId });
19894
+ log22.debug("inbound:dedup-hit", { messageId: msg.messageId });
19894
19895
  return;
19895
19896
  }
19896
- log21.info("inbound:verified", { messageId: msg.messageId, chatId: msg.chatId });
19897
+ log22.info("inbound:verified", { messageId: msg.messageId, chatId: msg.chatId });
19897
19898
  if (this.messageCallback) {
19898
19899
  this.messageCallback(msg);
19899
19900
  } else {
19900
- log21.warn("inbound:no-callback", { messageId: msg.messageId });
19901
+ log22.warn("inbound:no-callback", { messageId: msg.messageId });
19901
19902
  }
19902
19903
  }
19903
19904
  };
19904
19905
 
19905
19906
  // src/transport/connection-manager.ts
19906
- var log22 = createLogger("transport/connection-manager");
19907
+ var log23 = createLogger("transport/connection-manager");
19907
19908
  function sleep2(ms) {
19908
19909
  return new Promise((resolve2) => setTimeout(resolve2, ms));
19909
19910
  }
@@ -19933,7 +19934,7 @@ var ConnectionManager = class {
19933
19934
  reconnectMaxMs: options?.reconnectMaxMs ?? 6e4,
19934
19935
  maxReconnectAttempts: options?.maxReconnectAttempts ?? 10
19935
19936
  };
19936
- log22.info("ConnectionManager initialized", this.options);
19937
+ log23.info("ConnectionManager initialized", this.options);
19937
19938
  }
19938
19939
  /**
19939
19940
  * 启动连接 — 连接 WS + 开始心跳 + 注册重连逻辑
@@ -19948,29 +19949,33 @@ var ConnectionManager = class {
19948
19949
  this.running = true;
19949
19950
  this.reconnectAttempts = 0;
19950
19951
  const token = await tokenFn();
19952
+ const wsUrl = url2.replace(/\/+$/, "") + "/events/stream";
19951
19953
  await this.client.connect({
19952
- url: url2,
19954
+ url: wsUrl,
19953
19955
  token,
19954
- headers: this.appId ? { "X-App-ID": this.appId } : void 0
19956
+ headers: {
19957
+ "Authorization": token,
19958
+ ...this.appId ? { "X-App-ID": this.appId } : {}
19959
+ }
19955
19960
  });
19956
19961
  this.client.on("close", () => {
19957
19962
  if (this.running) {
19958
- log22.warn("ws:disconnected, scheduling reconnect");
19963
+ log23.warn("ws:disconnected, scheduling reconnect");
19959
19964
  this.scheduleReconnect();
19960
19965
  }
19961
19966
  });
19962
19967
  this.client.on("error", (err) => {
19963
- log22.error("ws:connection-error", { error: err.message });
19968
+ log23.error("ws:connection-error", { error: err.message });
19964
19969
  });
19965
19970
  this.startHeartbeat();
19966
- log22.info("ConnectionManager started \u2713", { url: url2 });
19971
+ log23.info("ConnectionManager started \u2713", { url: url2 });
19967
19972
  }
19968
19973
  /** 启动心跳保活 — 定时检查连接状态 */
19969
19974
  startHeartbeat() {
19970
19975
  this.stopHeartbeat();
19971
19976
  this.heartbeatTimer = setInterval(() => {
19972
19977
  if (this.client.readyState !== wrapper_default.OPEN) {
19973
- log22.warn("heartbeat: connection not open, scheduling reconnect");
19978
+ log23.warn("heartbeat: connection not open, scheduling reconnect");
19974
19979
  this.scheduleReconnect();
19975
19980
  }
19976
19981
  }, this.options.heartbeatIntervalMs);
@@ -19998,30 +20003,34 @@ var ConnectionManager = class {
19998
20003
  this.options.reconnectBaseMs * 2 ** this.reconnectAttempts,
19999
20004
  this.options.reconnectMaxMs
20000
20005
  );
20001
- log22.info("ws:reconnecting", { attempt: this.reconnectAttempts + 1, delayMs: delay });
20006
+ log23.info("ws:reconnecting", { attempt: this.reconnectAttempts + 1, delayMs: delay });
20002
20007
  await sleep2(delay);
20003
20008
  if (!this.running) return;
20004
20009
  this.reconnectAttempts++;
20005
20010
  try {
20006
20011
  const token = await this.tokenFn();
20012
+ const wsUrl = this.url.replace(/\/+$/, "") + "/events/stream";
20007
20013
  await this.client.connect({
20008
- url: this.url,
20014
+ url: wsUrl,
20009
20015
  token,
20010
- headers: this.appId ? { "X-App-ID": this.appId } : void 0
20016
+ headers: {
20017
+ "Authorization": token,
20018
+ ...this.appId ? { "X-App-ID": this.appId } : {}
20019
+ }
20011
20020
  });
20012
20021
  this.reconnectAttempts = 0;
20013
20022
  this.startHeartbeat();
20014
- log22.info("ws:reconnected", { attempt: this.reconnectAttempts, url: this.url });
20023
+ log23.info("ws:reconnected", { attempt: this.reconnectAttempts, url: this.url });
20015
20024
  return;
20016
20025
  } catch (err) {
20017
- log22.warn("ws:reconnect-failed", {
20026
+ log23.warn("ws:reconnect-failed", {
20018
20027
  attempt: this.reconnectAttempts,
20019
20028
  error: err.message
20020
20029
  });
20021
20030
  }
20022
20031
  }
20023
20032
  if (this.running) {
20024
- log22.error("ws:max-reconnect-reached", {
20033
+ log23.error("ws:max-reconnect-reached", {
20025
20034
  maxAttempts: this.options.maxReconnectAttempts
20026
20035
  });
20027
20036
  }
@@ -20031,7 +20040,7 @@ var ConnectionManager = class {
20031
20040
  this.running = false;
20032
20041
  this.stopHeartbeat();
20033
20042
  this.client.close(1e3, "shutdown");
20034
- log22.info("ConnectionManager stopped");
20043
+ log23.info("ConnectionManager stopped");
20035
20044
  }
20036
20045
  /** 当前是否已连接 (WebSocket readyState === OPEN) */
20037
20046
  get isConnected() {
@@ -20040,7 +20049,7 @@ var ConnectionManager = class {
20040
20049
  };
20041
20050
 
20042
20051
  // src/push/cockatoo-client.ts
20043
- var log23 = createLogger("push/cockatoo-client");
20052
+ var log24 = createLogger("push/cockatoo-client");
20044
20053
  var DEFAULT_TIMEOUT_MS2 = 3e4;
20045
20054
  var CockatooPushError = class extends Error {
20046
20055
  constructor(status, body, endpoint) {
@@ -20074,7 +20083,7 @@ var CockatooClient = class {
20074
20083
  const base = config2.endpoint.replace(/\/+$/, "");
20075
20084
  this.pushUrl = `${base}/api/v1/push`;
20076
20085
  this.healthUrl = `${base}/health`;
20077
- log23.info("CockatooClient initialized", { endpoint: config2.endpoint });
20086
+ log24.info("CockatooClient initialized", { endpoint: config2.endpoint });
20078
20087
  }
20079
20088
  /**
20080
20089
  * 推送消息到 Cockatoo 服务。
@@ -20105,7 +20114,7 @@ var CockatooClient = class {
20105
20114
  }
20106
20115
  throw new CockatooPushError(resp.status, body, this.pushUrl);
20107
20116
  }
20108
- log23.debug("push:sent", { type: payload.type });
20117
+ log24.debug("push:sent", { type: payload.type });
20109
20118
  }
20110
20119
  /**
20111
20120
  * 执行一次健康检查。
@@ -20123,11 +20132,11 @@ var CockatooClient = class {
20123
20132
  });
20124
20133
  const isHealthy = resp.ok;
20125
20134
  if (!isHealthy) {
20126
- log23.warn("health-check:unhealthy", { status: resp.status });
20135
+ log24.warn("health-check:unhealthy", { status: resp.status });
20127
20136
  }
20128
20137
  return isHealthy;
20129
20138
  } catch (err) {
20130
- log23.warn("health-check:error", { error: err.message });
20139
+ log24.warn("health-check:error", { error: err.message });
20131
20140
  return false;
20132
20141
  }
20133
20142
  }
@@ -20139,7 +20148,7 @@ var CockatooClient = class {
20139
20148
  this.healthy = await this.healthCheck();
20140
20149
  } catch {
20141
20150
  this.healthy = false;
20142
- log23.warn("Cockatoo health check failed");
20151
+ log24.warn("Cockatoo health check failed");
20143
20152
  }
20144
20153
  }, this.config.healthCheckIntervalMs ?? 6e4);
20145
20154
  if (typeof this.healthCheckTimer === "object" && "unref" in this.healthCheckTimer) {
@@ -20160,7 +20169,7 @@ var CockatooClient = class {
20160
20169
  };
20161
20170
 
20162
20171
  // src/push/push-queue.ts
20163
- var log24 = createLogger("push/push-queue");
20172
+ var log25 = createLogger("push/push-queue");
20164
20173
  var RETRY_BASE_MS2 = 1e3;
20165
20174
  var PushQueue = class {
20166
20175
  client;
@@ -20202,7 +20211,7 @@ var PushQueue = class {
20202
20211
  this.unhealthyRetryMs = config2?.unhealthyRetryMs ?? 5e3;
20203
20212
  this.drainOnStop = config2?.drainOnStop ?? false;
20204
20213
  this.drainTimeoutMs = config2?.drainTimeoutMs ?? 5e3;
20205
- log24.info("PushQueue initialized", {
20214
+ log25.info("PushQueue initialized", {
20206
20215
  maxSize: this.maxSize,
20207
20216
  retryAttempts: this.retryAttempts,
20208
20217
  processIntervalMs: this.processIntervalMs
@@ -20213,7 +20222,7 @@ var PushQueue = class {
20213
20222
  start() {
20214
20223
  if (this.running) return;
20215
20224
  this.running = true;
20216
- log24.info("PushQueue started");
20225
+ log25.info("PushQueue started");
20217
20226
  this.scheduleNext(0);
20218
20227
  }
20219
20228
  /**
@@ -20227,10 +20236,10 @@ var PushQueue = class {
20227
20236
  this.running = false;
20228
20237
  this.clearTimer();
20229
20238
  if (this.drainOnStop && this.queue.length > 0) {
20230
- log24.info("PushQueue draining", { remaining: this.queue.length });
20239
+ log25.info("PushQueue draining", { remaining: this.queue.length });
20231
20240
  await this.drain();
20232
20241
  }
20233
- log24.info("PushQueue stopped", {
20242
+ log25.info("PushQueue stopped", {
20234
20243
  remaining: this.queue.length,
20235
20244
  sent: this.sentCount,
20236
20245
  dropped: this.droppedCount,
@@ -20245,13 +20254,13 @@ var PushQueue = class {
20245
20254
  if (this.queue.length >= this.maxSize) {
20246
20255
  const dropped = this.queue.shift();
20247
20256
  this.droppedCount++;
20248
- log24.warn("queue:full, dropping oldest", {
20257
+ log25.warn("queue:full, dropping oldest", {
20249
20258
  droppedType: dropped?.payload.type,
20250
20259
  queueSize: this.queue.length
20251
20260
  });
20252
20261
  }
20253
20262
  this.queue.push({ payload, retries: 0, eligibleAt: 0 });
20254
- log24.debug("queue:enqueued", { type: payload.type, queueSize: this.queue.length });
20263
+ log25.debug("queue:enqueued", { type: payload.type, queueSize: this.queue.length });
20255
20264
  }
20256
20265
  /** 当前队列长度 */
20257
20266
  get size() {
@@ -20276,7 +20285,7 @@ var PushQueue = class {
20276
20285
  this.clearTimer();
20277
20286
  this.processTimer = setTimeout(() => {
20278
20287
  this.tick().catch((err) => {
20279
- log24.error("tick:unexpected-error", { error: err.message });
20288
+ log25.error("tick:unexpected-error", { error: err.message });
20280
20289
  if (this.running) {
20281
20290
  this.scheduleNext(this.idleIntervalMs);
20282
20291
  }
@@ -20298,7 +20307,7 @@ var PushQueue = class {
20298
20307
  async tick() {
20299
20308
  if (!this.running) return;
20300
20309
  if (!this.client.isHealthy) {
20301
- log24.debug("tick:service-unhealthy, pausing");
20310
+ log25.debug("tick:service-unhealthy, pausing");
20302
20311
  this.scheduleNext(this.unhealthyRetryMs);
20303
20312
  return;
20304
20313
  }
@@ -20318,7 +20327,7 @@ var PushQueue = class {
20318
20327
  try {
20319
20328
  await this.processItem(item);
20320
20329
  } catch (err) {
20321
- log24.error("tick:process-unexpected", { error: err.message });
20330
+ log25.error("tick:process-unexpected", { error: err.message });
20322
20331
  } finally {
20323
20332
  this.processing = false;
20324
20333
  }
@@ -20337,14 +20346,14 @@ var PushQueue = class {
20337
20346
  try {
20338
20347
  await this.client.push(item.payload);
20339
20348
  this.sentCount++;
20340
- log24.debug("push:success", {
20349
+ log25.debug("push:success", {
20341
20350
  type: item.payload.type,
20342
20351
  retries: item.retries
20343
20352
  });
20344
20353
  } catch (err) {
20345
20354
  if (err instanceof CockatooPushError && err.isClientError) {
20346
20355
  this.droppedCount++;
20347
- log24.warn("push:4xx-dropped", {
20356
+ log25.warn("push:4xx-dropped", {
20348
20357
  type: item.payload.type,
20349
20358
  status: err.status,
20350
20359
  retries: item.retries
@@ -20353,7 +20362,7 @@ var PushQueue = class {
20353
20362
  }
20354
20363
  if (item.retries >= this.retryAttempts) {
20355
20364
  this.droppedCount++;
20356
- log24.error("push:max-retries-dropped", {
20365
+ log25.error("push:max-retries-dropped", {
20357
20366
  type: item.payload.type,
20358
20367
  retries: item.retries,
20359
20368
  error: err.message
@@ -20366,7 +20375,7 @@ var PushQueue = class {
20366
20375
  item.eligibleAt = Date.now() + backoffMs;
20367
20376
  this.queue.push(item);
20368
20377
  const errorDetail = err instanceof CockatooPushError ? `HTTP ${err.status}` : err.message;
20369
- log24.warn("push:retry-enqueued", {
20378
+ log25.warn("push:retry-enqueued", {
20370
20379
  type: item.payload.type,
20371
20380
  retries: item.retries,
20372
20381
  backoffMs,
@@ -20390,22 +20399,22 @@ var PushQueue = class {
20390
20399
  try {
20391
20400
  await this.client.push(item.payload);
20392
20401
  this.sentCount++;
20393
- log24.debug("drain:sent", { type: item.payload.type });
20402
+ log25.debug("drain:sent", { type: item.payload.type });
20394
20403
  } catch (err) {
20395
20404
  this.droppedCount++;
20396
- log24.warn("drain:dropped", {
20405
+ log25.warn("drain:dropped", {
20397
20406
  type: item.payload.type,
20398
20407
  error: err.message
20399
20408
  });
20400
20409
  }
20401
20410
  }
20402
20411
  if (this.queue.length > 0) {
20403
- log24.warn("drain:timeout", {
20412
+ log25.warn("drain:timeout", {
20404
20413
  remaining: this.queue.length,
20405
20414
  timeoutMs: this.drainTimeoutMs
20406
20415
  });
20407
20416
  } else {
20408
- log24.info("drain:complete");
20417
+ log25.info("drain:complete");
20409
20418
  }
20410
20419
  }
20411
20420
  // ── 内部工具 ──
@@ -20419,43 +20428,32 @@ var PushQueue = class {
20419
20428
  };
20420
20429
 
20421
20430
  // src/index.ts
20422
- var log25 = createLogger("plugin");
20431
+ var log26 = createLogger("plugin");
20423
20432
  async function startPlugin(accountConfig, internalOverrides) {
20424
20433
  const config2 = buildPluginConfig(accountConfig, internalOverrides);
20425
- log25.info("Config built \u2713", { pluginId: config2.pluginId });
20434
+ log26.info("Config built \u2713", { pluginId: config2.pluginId });
20426
20435
  let cryptoEngine;
20427
20436
  if (config2.crypto.enabled) {
20428
- cryptoEngine = new CryptoEngine();
20437
+ cryptoEngine = new CryptoEngine({ credentials: accountConfig });
20429
20438
  await cryptoEngine.init();
20430
- log25.info("Crypto initialized \u2713");
20439
+ log26.info("Crypto initialized \u2713");
20431
20440
  } else {
20432
20441
  cryptoEngine = CryptoEngine.createPassthrough();
20433
- log25.info("Crypto passthrough mode \u2713 (disabled by config)");
20434
- }
20435
- const sealClient = new SealClient({
20436
- serverUrl: config2.auth.serverUrl,
20437
- clientName: config2.auth.clientName,
20438
- clientId: config2.auth.clientId,
20439
- clientSecret: config2.auth.clientSecret,
20440
- scopes: config2.auth.scopes
20442
+ log26.info("Crypto passthrough mode \u2713 (disabled by config)");
20443
+ }
20444
+ const oauthClient = new OAuthClient({
20445
+ baseUrl: config2.auth.serverUrl,
20446
+ appId: accountConfig.appId,
20447
+ appSecret: accountConfig.appSecret
20441
20448
  });
20442
- const tokenStorePath = (0, import_node_path2.join)((0, import_node_os.homedir)(), TOKEN_STORE_DIR, "tokens");
20449
+ const tokenStorePath = (0, import_node_path3.join)((0, import_node_os.homedir)(), TOKEN_STORE_DIR, "tokens");
20443
20450
  const tokenStore = new TokenStore(tokenStorePath, cryptoEngine);
20444
20451
  const tokenManager = new TokenManager({
20445
- sealClient,
20452
+ oauthClient,
20446
20453
  tokenStore,
20447
- refreshAheadMs: config2.auth.refreshAheadMs,
20448
- autoRegisterParams: config2.auth.autoRegister ? {
20449
- clientName: config2.auth.clientName,
20450
- scopes: config2.auth.scopes,
20451
- tokenSettings: config2.auth.tokenSettings ? {
20452
- accessTokenTtl: config2.auth.tokenSettings.accessTokenTtl,
20453
- refreshTokenTtl: config2.auth.tokenSettings.refreshTokenTtl,
20454
- accessTokenFormat: config2.auth.tokenSettings.accessTokenFormat
20455
- } : void 0
20456
- } : void 0
20454
+ refreshAheadMs: config2.auth.refreshAheadMs
20457
20455
  });
20458
- log25.info("Auth initialized \u2713");
20456
+ log26.info("Auth initialized \u2713");
20459
20457
  let pushQueue = null;
20460
20458
  let cockatooClient = null;
20461
20459
  if (config2.push?.enabled) {
@@ -20470,7 +20468,7 @@ async function startPlugin(accountConfig, internalOverrides) {
20470
20468
  });
20471
20469
  cockatooClient.startHealthCheck();
20472
20470
  pushQueue.start();
20473
- log25.info("Push initialized \u2713");
20471
+ log26.info("Push initialized \u2713");
20474
20472
  }
20475
20473
  const wsClient = new WSClient();
20476
20474
  const dedup = new MessageDedup(config2.transport.dedup);
@@ -20488,8 +20486,8 @@ async function startPlugin(accountConfig, internalOverrides) {
20488
20486
  reconnectMaxMs: config2.transport.reconnectMaxMs,
20489
20487
  maxReconnectAttempts: config2.transport.maxReconnectAttempts
20490
20488
  });
20491
- log25.info("Transport initialized \u2713");
20492
- log25.info("Plugin started \u2713");
20489
+ log26.info("Transport initialized \u2713");
20490
+ log26.info("Plugin started \u2713");
20493
20491
  return {
20494
20492
  config: config2,
20495
20493
  messagePipe,
@@ -20497,12 +20495,12 @@ async function startPlugin(accountConfig, internalOverrides) {
20497
20495
  tokenManager,
20498
20496
  pushQueue,
20499
20497
  shutdown: async () => {
20500
- log25.info("Shutting down...");
20498
+ log26.info("Shutting down...");
20501
20499
  await connectionManager.stop();
20502
20500
  if (pushQueue) await pushQueue.stop();
20503
20501
  if (cockatooClient) cockatooClient.stopHealthCheck();
20504
20502
  tokenManager.shutdown();
20505
- log25.info("Shutdown complete \u2713");
20503
+ log26.info("Shutdown complete \u2713");
20506
20504
  }
20507
20505
  };
20508
20506
  }
@@ -20512,7 +20510,7 @@ var plugin = {
20512
20510
  description: "Quantum-encrypted IM channel plugin",
20513
20511
  register(api) {
20514
20512
  api.registerChannel({ plugin: quantumImPlugin });
20515
- log25.info("plugin registered \u2713");
20513
+ log26.info("plugin registered \u2713");
20516
20514
  }
20517
20515
  };
20518
20516
  var index_default = plugin;