liangzimixin 0.2.5 → 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",
@@ -18538,14 +18558,20 @@ function resolveAccount(cfg, accountId) {
18538
18558
 
18539
18559
  // src/channel/onboarding.ts
18540
18560
  var import_plugin_sdk2 = require("openclaw/plugin-sdk");
18561
+ function getChannelSection(cfg) {
18562
+ return cfg.channels?.[CHANNEL_ID];
18563
+ }
18564
+ function getDefaultAccount(cfg) {
18565
+ const section = getChannelSection(cfg);
18566
+ if (!section?.accounts) return void 0;
18567
+ return section.accounts.default;
18568
+ }
18541
18569
  function isConfigured(cfg) {
18542
- const section = getChannelConfig(cfg);
18543
- if (!section?.accounts) return false;
18544
- const defaultAccount = section.accounts.default;
18545
- if (!defaultAccount) return false;
18546
- return AccountConfigSchema.safeParse(defaultAccount).success;
18570
+ const account = getDefaultAccount(cfg);
18571
+ if (!account) return false;
18572
+ return AccountConfigSchema.safeParse(account).success;
18547
18573
  }
18548
- var required2 = (v) => String(v ?? "").trim() ? void 0 : "\u4E0D\u80FD\u4E3A\u7A7A";
18574
+ var required2 = (v) => v.trim() ? void 0 : "\u4E0D\u80FD\u4E3A\u7A7A";
18549
18575
  var quantumImOnboarding = {
18550
18576
  channel: CHANNEL_ID,
18551
18577
  async getStatus({ cfg }) {
@@ -18559,43 +18585,84 @@ var quantumImOnboarding = {
18559
18585
  };
18560
18586
  },
18561
18587
  async configure({ cfg, prompter }) {
18588
+ const existing = getDefaultAccount(cfg) ?? {};
18589
+ const alreadyConfigured = isConfigured(cfg);
18590
+ if (alreadyConfigured) {
18591
+ const reconfigure = await prompter.confirm({
18592
+ message: "\u91CF\u5B50\u5BC6\u4FE1\u51ED\u636E\u5DF2\u914D\u7F6E\uFF0C\u662F\u5426\u91CD\u65B0\u914D\u7F6E\uFF1F",
18593
+ initialValue: false
18594
+ });
18595
+ if (!reconfigure) {
18596
+ return { cfg, accountId: import_plugin_sdk2.DEFAULT_ACCOUNT_ID };
18597
+ }
18598
+ }
18562
18599
  await prompter.note(
18563
18600
  [
18564
18601
  "\u8BF7\u51C6\u5907\u4EE5\u4E0B\u4FE1\u606F:",
18565
18602
  " 1. \u5E73\u53F0\u7533\u8BF7\u7684 appId \u548C appSecret",
18566
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",
18567
18605
  "",
18568
18606
  "\u5982\u6709\u7591\u95EE\u8BF7\u8054\u7CFB\u5E73\u53F0\u7BA1\u7406\u5458\u83B7\u53D6\u3002"
18569
18607
  ].join("\n"),
18570
18608
  "\u91CF\u5B50\u5BC6\u4FE1 \u2014 \u51ED\u636E\u914D\u7F6E"
18571
18609
  );
18572
- const existing = getChannelConfig(cfg)?.accounts?.default ?? {};
18573
18610
  const appId = await prompter.text({
18574
18611
  message: "\u5E94\u7528 ID (appId)",
18575
- initialValue: existing.appId,
18612
+ initialValue: existing.appId ?? void 0,
18576
18613
  validate: required2
18577
18614
  });
18578
18615
  const appSecret = await prompter.text({
18579
18616
  message: "\u5E94\u7528\u5BC6\u94A5 (appSecret)",
18580
- initialValue: existing.appSecret,
18617
+ initialValue: existing.appSecret ?? void 0,
18581
18618
  validate: required2
18582
18619
  });
18583
18620
  const quantumAccount = await prompter.text({
18584
18621
  message: "\u91CF\u5B50\u8D26\u6237\u6807\u8BC6 (quantum-account)",
18585
18622
  placeholder: "\u5982: 17100001111",
18586
- initialValue: existing["quantum-account"],
18623
+ initialValue: existing["quantum-account"] ?? void 0,
18587
18624
  validate: required2
18588
18625
  });
18589
18626
  const quantumAppId = await prompter.text({
18590
18627
  message: "\u91CF\u5B50\u670D\u52A1 ID (quantum-appId)",
18591
- initialValue: existing["quantum-appId"],
18628
+ initialValue: existing["quantum-appId"] ?? void 0,
18592
18629
  validate: required2
18593
18630
  });
18594
18631
  const quantumAppSecret = await prompter.text({
18595
18632
  message: "\u91CF\u5B50\u670D\u52A1\u5BC6\u94A5 (quantum-appSecret)",
18596
- initialValue: existing["quantum-appSecret"],
18633
+ initialValue: existing["quantum-appSecret"] ?? void 0,
18597
18634
  validate: required2
18598
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
+ }
18599
18666
  const channels = cfg.channels ?? {};
18600
18667
  const channelCfg = channels[CHANNEL_ID] ?? {};
18601
18668
  const accounts = channelCfg.accounts ?? {};
@@ -18605,6 +18672,7 @@ var quantumImOnboarding = {
18605
18672
  ...channels,
18606
18673
  [CHANNEL_ID]: {
18607
18674
  ...channelCfg,
18675
+ enabled: true,
18608
18676
  accounts: {
18609
18677
  ...accounts,
18610
18678
  default: {
@@ -18612,7 +18680,9 @@ var quantumImOnboarding = {
18612
18680
  appSecret: appSecret.trim(),
18613
18681
  "quantum-account": quantumAccount.trim(),
18614
18682
  "quantum-appId": quantumAppId.trim(),
18615
- "quantum-appSecret": quantumAppSecret.trim()
18683
+ "quantum-appSecret": quantumAppSecret.trim(),
18684
+ pin: pin.trim(),
18685
+ "quantum-url": quantumUrl.trim()
18616
18686
  }
18617
18687
  }
18618
18688
  }
@@ -18621,19 +18691,16 @@ var quantumImOnboarding = {
18621
18691
  await prompter.note("\u51ED\u636E\u5DF2\u4FDD\u5B58\uFF0C\u91CD\u542F gateway \u540E\u751F\u6548\u3002", "\u2713 \u914D\u7F6E\u5B8C\u6210");
18622
18692
  return { cfg: next, accountId: import_plugin_sdk2.DEFAULT_ACCOUNT_ID };
18623
18693
  },
18624
- disable(cfg) {
18625
- const channels = cfg.channels ?? {};
18626
- return {
18627
- ...cfg,
18628
- channels: {
18629
- ...channels,
18630
- [CHANNEL_ID]: {
18631
- ...channels[CHANNEL_ID] ?? {},
18632
- enabled: false
18633
- }
18694
+ disable: (cfg) => ({
18695
+ ...cfg,
18696
+ channels: {
18697
+ ...cfg.channels,
18698
+ [CHANNEL_ID]: {
18699
+ ...cfg.channels?.[CHANNEL_ID] ?? {},
18700
+ enabled: false
18634
18701
  }
18635
- };
18636
- }
18702
+ }
18703
+ })
18637
18704
  };
18638
18705
 
18639
18706
  // src/channel/plugin.ts
@@ -18798,104 +18865,83 @@ var quantumImPlugin = {
18798
18865
  }
18799
18866
  };
18800
18867
 
18801
- // src/crypto/quantun-plug-mock.ts
18802
- var import_node_crypto = require("crypto");
18803
- var log14 = createLogger("crypto/quantun-plug-mock");
18804
- var MOCK_KEY = Buffer.alloc(32, 0);
18805
- MOCK_KEY.write("quantum-mock-key-2026", 0);
18806
- var MOCK_KEY_ID = "mock-key-v1";
18807
- var initialized = false;
18808
- var quantunPlugMock = {
18809
- /**
18810
- * 初始化 Mock SDK
18811
- * 幂等操作 — 重复调用不会报错
18812
- */
18813
- async init() {
18814
- if (initialized) return;
18815
- initialized = true;
18816
- log14.warn("\u26A0\uFE0F MOCK MODE \u2014 \u4F7F\u7528 AES-256-GCM \u6A21\u62DF\u91CF\u5B50\u52A0\u5BC6");
18817
- },
18818
- /**
18819
- * 加密明文 → 密文 + 密钥标识
18820
- *
18821
- * 内部流程:
18822
- * 1. 生成 12 字节随机 IV (确保每次加密结果不同)
18823
- * 2. AES-256-GCM 加密明文
18824
- * 3. 获取 16 字节 AuthTag (认证标签,防篡改)
18825
- * 4. 拼接 [IV(12) | Tag(16) | Ciphertext] → Base64 编码
18826
- */
18827
- async encrypt(plaintext, mode) {
18828
- if (!initialized) {
18829
- throw new Error("quantunPlug not initialized \u2014 call init() first");
18830
- }
18831
- const iv = (0, import_node_crypto.randomBytes)(12);
18832
- const cipher = (0, import_node_crypto.createCipheriv)("aes-256-gcm", MOCK_KEY, iv);
18833
- const encrypted = Buffer.concat([
18834
- cipher.update(plaintext, "utf-8"),
18835
- cipher.final()
18836
- ]);
18837
- const tag = cipher.getAuthTag();
18838
- const combined = Buffer.concat([iv, tag, encrypted]);
18839
- return {
18840
- ciphertext: combined.toString("base64"),
18841
- keyId: MOCK_KEY_ID
18842
- };
18843
- },
18844
- /**
18845
- * 解密密文 → 明文
18846
- *
18847
- * 内部流程:
18848
- * 1. Base64 解码
18849
- * 2. 拆分 IV(12) + Tag(16) + Ciphertext
18850
- * 3. AES-256-GCM 解密 + AuthTag 验证
18851
- * 4. 返回 UTF-8 明文
18852
- *
18853
- * @throws AuthTag 验证失败时抛出异常 (数据被篡改)
18854
- */
18855
- async decrypt(ciphertext, keyId, mode) {
18856
- if (!initialized) {
18857
- throw new Error("quantunPlug not initialized \u2014 call init() first");
18858
- }
18859
- const combined = Buffer.from(ciphertext, "base64");
18860
- const iv = combined.subarray(0, 12);
18861
- const tag = combined.subarray(12, 28);
18862
- const encrypted = combined.subarray(28);
18863
- const decipher = (0, import_node_crypto.createDecipheriv)("aes-256-gcm", MOCK_KEY, iv);
18864
- decipher.setAuthTag(tag);
18865
- const decrypted = Buffer.concat([
18866
- decipher.update(encrypted),
18867
- decipher.final()
18868
- // 此处 Tag 验证失败会抛出异常
18869
- ]);
18870
- return {
18871
- plaintext: decrypted.toString("utf-8")
18872
- };
18873
- }
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
18874
18877
  };
18875
- 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
+ }
18876
18912
 
18877
18913
  // src/crypto/crypto-engine.ts
18878
- var log15 = createLogger("crypto/crypto-engine");
18914
+ var log16 = createLogger("crypto/crypto-engine");
18879
18915
  var PASSTHROUGH_KEY_ID = "passthrough";
18916
+ var FILE_CHUNK_SIZE = 10 * 1024 * 1024;
18917
+ var FILE_DECRYPT_CHUNK_SIZE = FILE_CHUNK_SIZE + 16;
18880
18918
  var CryptoEngine = class _CryptoEngine {
18881
- /** 底层量子加密 SDK 实例 (Mock 或真实), 透传模式下为 null */
18919
+ /** 底层量子加密 SDK 实例, 透传模式下为 null */
18882
18920
  plug;
18883
18921
  /** SDK 是否已完成初始化 */
18884
- initialized = false;
18922
+ initialized;
18885
18923
  /** 是否为透传模式 (不加密, 明文直接透传) */
18886
18924
  passthrough;
18925
+ /** 用户凭据 — 用于生成 quantum.json (透传模式下为 undefined) */
18926
+ credentials;
18887
18927
  /**
18888
- * 构造加密引擎 (加密模式)
18928
+ * 构造加密引擎
18889
18929
  *
18890
- * 当前默认使用 Mock SDK。
18891
- * 待真实 SDK 交付后,此处替换为 require('./quantunPlug.ejs')
18930
+ * @param opts - 构造选项
18931
+ * @param opts.passthrough - true 为透传模式 (不加密); false 为加密模式 (默认)
18932
+ * @param opts.credentials - 用户凭据 (加密模式必需, 用于生成 quantum.json)
18892
18933
  *
18893
- * 如需透传模式,请使用静态方法 CryptoEngine.createPassthrough()。
18934
+ * 请使用静态工厂方法:
18935
+ * - new CryptoEngine({ credentials }) → 加密模式, 使用量子 SDK
18936
+ * - CryptoEngine.createPassthrough() → 透传模式, 无需 init()
18894
18937
  */
18895
- constructor() {
18896
- this.plug = quantun_plug_mock_default;
18897
- this.passthrough = false;
18898
- 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"})`);
18899
18945
  }
18900
18946
  /**
18901
18947
  * 创建透传模式的加密引擎 (静态工厂方法)
@@ -18909,17 +18955,12 @@ var CryptoEngine = class _CryptoEngine {
18909
18955
  * 上层模块 (TokenStore, MessagePipe) 无需感知加密是否开启。
18910
18956
  */
18911
18957
  static createPassthrough() {
18912
- const instance = Object.create(_CryptoEngine.prototype);
18913
- instance.plug = null;
18914
- instance.passthrough = true;
18915
- instance.initialized = true;
18916
- log15.info("CryptoEngine created (passthrough mode \u2014 crypto disabled)");
18917
- return instance;
18958
+ return new _CryptoEngine({ passthrough: true });
18918
18959
  }
18919
18960
  /**
18920
18961
  * 初始化加密引擎
18921
18962
  *
18922
- * 加密模式: 调用 SDK 的 init() 方法完成密钥协商等初始化操作。
18963
+ * 加密模式: 调用 SDK 的 init() 方法完成密服入网、密钥协商等初始化操作。
18923
18964
  * 透传模式: 空操作 (already initialized)。
18924
18965
  * 幂等操作 — 重复调用安全。
18925
18966
  */
@@ -18929,29 +18970,37 @@ var CryptoEngine = class _CryptoEngine {
18929
18970
  this.initialized = true;
18930
18971
  return;
18931
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
+ }
18932
18979
  await this.plug.init();
18933
18980
  this.initialized = true;
18934
- log15.info("CryptoEngine initialized \u2713");
18981
+ log16.info("CryptoEngine initialized \u2713");
18935
18982
  }
18936
18983
  /**
18937
18984
  * 加密明文
18938
18985
  *
18939
- * 加密模式: 调用底层 SDK 加密, 返回 Base64 密文 + keyId
18986
+ * 加密模式: 调用底层 SDK 加密 (SM4_CBC_PKCS7PADDING), 返回密文 + keyId
18940
18987
  * 透传模式: 直接返回明文作为 "密文", keyId = 'passthrough'
18941
18988
  *
18989
+ * 注意: SDK 返回驼峰 cipherText, 此方法对外统一为小写 ciphertext
18990
+ *
18942
18991
  * @param plaintext - 要加密的明文字符串
18943
18992
  * @returns { ciphertext: 密文字符串, keyId: 密钥标识 }
18944
18993
  * @throws 加密模式下未初始化时抛出错误
18945
18994
  */
18946
18995
  async encrypt(plaintext) {
18947
- this.ensureInitialized();
18948
18996
  if (this.passthrough) {
18949
- log15.debug("Encrypt (passthrough)", { length: plaintext.length });
18997
+ log16.debug("Encrypt (passthrough)", { length: plaintext.length });
18950
18998
  return { ciphertext: plaintext, keyId: PASSTHROUGH_KEY_ID };
18951
18999
  }
18952
- const result = await this.plug.encrypt(plaintext, 1);
18953
- log15.debug("Encrypted", { length: plaintext.length, keyId: result.keyId });
18954
- 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 };
18955
19004
  }
18956
19005
  /**
18957
19006
  * 解密密文
@@ -18965,14 +19014,100 @@ var CryptoEngine = class _CryptoEngine {
18965
19014
  * @throws 加密模式下未初始化、密文损坏或 keyId 不匹配时抛出错误
18966
19015
  */
18967
19016
  async decrypt(ciphertext, keyId) {
18968
- this.ensureInitialized();
18969
19017
  if (this.passthrough) {
18970
- log15.debug("Decrypt (passthrough)", { keyId });
19018
+ log16.debug("Decrypt (passthrough)", { keyId });
18971
19019
  return ciphertext;
18972
19020
  }
18973
- const result = await this.plug.decrypt(ciphertext, keyId, 1);
18974
- log15.debug("Decrypted", { keyId });
18975
- 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);
18976
19111
  }
18977
19112
  /**
18978
19113
  * 查询当前是否为透传模式
@@ -18982,25 +19117,26 @@ var CryptoEngine = class _CryptoEngine {
18982
19117
  return this.passthrough;
18983
19118
  }
18984
19119
  /**
18985
- * 确保引擎已初始化
18986
- * 未初始化时抛出明确错误,避免难以排查的 SDK 内部异常
19120
+ * 确保引擎已初始化,同时返回非空 plug 实例
19121
+ * (通过 init() 守卫保证:加密模式下 init() 成功后 plug 一定非 null)
18987
19122
  */
18988
- ensureInitialized() {
19123
+ requirePlug() {
18989
19124
  if (!this.initialized) {
18990
19125
  throw new Error("CryptoEngine not initialized \u2014 call init() first");
18991
19126
  }
19127
+ return this.plug;
18992
19128
  }
18993
19129
  };
18994
19130
 
18995
19131
  // src/auth/oauth-client.ts
18996
- var log16 = createLogger("auth/oauth-client");
18997
- var SealApiError = class extends Error {
19132
+ var log17 = createLogger("auth/oauth-client");
19133
+ var ApiError = class extends Error {
18998
19134
  constructor(status, body, endpoint) {
18999
- super(`Seal API error: ${status} on ${endpoint}`);
19135
+ super(`API error: ${status} on ${endpoint}`);
19000
19136
  this.status = status;
19001
19137
  this.body = body;
19002
19138
  this.endpoint = endpoint;
19003
- this.name = "SealApiError";
19139
+ this.name = "ApiError";
19004
19140
  }
19005
19141
  };
19006
19142
  var TokenAcquireError = class extends Error {
@@ -19017,28 +19153,21 @@ function sleep(ms) {
19017
19153
  return new Promise((resolve2) => setTimeout(resolve2, ms));
19018
19154
  }
19019
19155
  var DEFAULT_TIMEOUT_MS = 3e4;
19020
- var SealClient = class {
19156
+ var GRANT_TYPE = "client_credentials";
19157
+ var SCOPE = "client_credentials refresh_token";
19158
+ var OAuthClient = class {
19021
19159
  baseUrl;
19022
- clientId;
19023
- clientSecret;
19024
- clientName;
19025
- scopes;
19160
+ appId;
19161
+ appSecret;
19026
19162
  constructor(config2) {
19027
- this.baseUrl = config2.serverUrl.replace(/\/+$/, "");
19028
- this.clientId = config2.clientId;
19029
- this.clientSecret = config2.clientSecret;
19030
- this.clientName = config2.clientName;
19031
- this.scopes = config2.scopes;
19032
- 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) });
19033
19167
  }
19034
19168
  // ── 通用 HTTP 请求 ──
19035
19169
  /**
19036
19170
  * 统一 HTTP 请求封装 — 含超时、日志脱敏和错误处理
19037
- * @param method - HTTP 方法
19038
- * @param path - 请求路径 (如 '/seal/client/register')
19039
- * @param options - 请求选项
19040
- * @returns 解析后的 JSON 响应
19041
- * @throws SealApiError — HTTP 非 2xx 响应
19042
19171
  */
19043
19172
  async request(method, path2, options = {}) {
19044
19173
  const url2 = `${this.baseUrl}${path2}`;
@@ -19054,7 +19183,7 @@ var SealClient = class {
19054
19183
  bodyStr = JSON.stringify(options.body);
19055
19184
  }
19056
19185
  }
19057
- log16.debug("HTTP request", { method, url: url2 });
19186
+ log17.debug("HTTP request", { method, url: url2 });
19058
19187
  const response = await fetch(url2, {
19059
19188
  method,
19060
19189
  headers,
@@ -19069,110 +19198,39 @@ var SealClient = class {
19069
19198
  } catch {
19070
19199
  parsedBody = responseBody;
19071
19200
  }
19072
- log16.error("HTTP error", { method, url: url2, status: response.status });
19073
- 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);
19074
19203
  }
19075
- log16.debug("HTTP response", { method, url: url2, status: response.status });
19204
+ log17.debug("HTTP response", { method, url: url2, status: response.status });
19076
19205
  try {
19077
19206
  return JSON.parse(responseBody);
19078
19207
  } catch {
19079
- throw new SealApiError(response.status, responseBody, path2);
19208
+ throw new ApiError(response.status, responseBody, path2);
19080
19209
  }
19081
19210
  }
19082
19211
  // ── 公共方法 ──
19083
- /** 判断是否已注册 (有 clientId + clientSecret) */
19084
- isRegistered() {
19085
- return !!(this.clientId && this.clientSecret);
19086
- }
19087
- /** 更新 clientId / clientSecret (注册成功后或从持久化加载后调用) */
19088
- setCredentials(clientId, clientSecret) {
19089
- this.clientId = clientId;
19090
- this.clientSecret = clientSecret;
19091
- log16.info("Credentials updated", { clientId: sanitize(clientId) });
19092
- }
19093
19212
  /**
19094
- * 动态注册客户端 → POST /seal/client/register
19213
+ * 获取访问令牌 → POST /auth/token (client_credentials)
19095
19214
  *
19096
- * 首次运行时自动调用,获取 clientId 和 clientSecret。
19097
- * 注册成功后自动更新内部凭据。
19098
- */
19099
- async register(params) {
19100
- const body = {
19101
- client_name: params.clientName,
19102
- scopes: params.scopes,
19103
- authentication_methods: params.authMethods ?? ["client_secret_post"],
19104
- grant_types: params.grantTypes ?? ["authorization_code", "client_credentials", "refresh_token"],
19105
- redirect_uris: params.redirectUris ?? ["https://placeholder.local"],
19106
- logout_redirect_uris: [],
19107
- client_settings: {
19108
- "settings.client.require-authorization-consent": true
19109
- }
19110
- };
19111
- if (params.tokenSettings) {
19112
- const ts = {};
19113
- if (params.tokenSettings.accessTokenTtl) {
19114
- ts["settings.token.access-token-time-to-live"] = params.tokenSettings.accessTokenTtl;
19115
- }
19116
- if (params.tokenSettings.refreshTokenTtl) {
19117
- ts["settings.token.refresh-token-time-to-live"] = params.tokenSettings.refreshTokenTtl;
19118
- }
19119
- if (params.tokenSettings.accessTokenFormat) {
19120
- ts["settings.token.access-token-format"] = params.tokenSettings.accessTokenFormat;
19121
- }
19122
- if (params.tokenSettings.reuseRefreshTokens !== void 0) {
19123
- ts["settings.token.reuse-refresh-tokens"] = params.tokenSettings.reuseRefreshTokens;
19124
- }
19125
- ts["settings.token.authorization-code-time-to-live"] = "300";
19126
- body.token_settings = ts;
19127
- }
19128
- log16.info("Registering OAuth client", { clientName: params.clientName });
19129
- const response = await this.request(
19130
- "POST",
19131
- "/seal/client/register",
19132
- { body, contentType: "json" }
19133
- );
19134
- const registration = {
19135
- clientId: response.client_id,
19136
- clientSecret: response.client_secret,
19137
- clientSecretExpiresAt: response.client_secret_expires_at ?? "",
19138
- clientIdIssuedAt: response.client_id_issued_at ?? "",
19139
- scopes: response.scopes ?? params.scopes
19140
- };
19141
- this.setCredentials(registration.clientId, registration.clientSecret);
19142
- log16.info("OAuth client registered", {
19143
- clientId: sanitize(registration.clientId),
19144
- clientSecret: sanitize(registration.clientSecret),
19145
- scopes: registration.scopes,
19146
- expiresAt: registration.clientSecretExpiresAt
19147
- });
19148
- return registration;
19149
- }
19150
- /**
19151
- * 获取访问令牌 → 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 (写死)
19152
19220
  *
19153
- * 使用客户端凭证模式获取 Access Token。
19154
- * 前置条件: clientId 和 clientSecret 必须存在 (通过 register 或 setCredentials 设置)
19155
- *
19156
- * @param scope - 请求的权限范围 (空格分隔),不传则使用注册时的全部 scope
19157
19221
  * @returns TokenData 包含 access_token、过期时间等
19158
- * @throws ErrorclientId/clientSecret 未设置
19159
- * @throws SealApiError — HTTP 错误
19222
+ * @throws ApiErrorHTTP 错误
19160
19223
  */
19161
- async getToken(scope) {
19162
- if (!this.clientId || !this.clientSecret) {
19163
- throw new Error("Cannot get token: clientId and clientSecret are required. Call register() or setCredentials() first.");
19164
- }
19224
+ async getToken() {
19165
19225
  const params = new URLSearchParams();
19166
- params.set("grant_type", "client_credentials");
19167
- params.set("client_id", this.clientId);
19168
- params.set("client_secret", this.clientSecret);
19169
- if (scope) {
19170
- params.set("scope", scope);
19171
- }
19172
- 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) });
19173
19231
  const response = await this.request(
19174
19232
  "POST",
19175
- "/seal/oauth2/token",
19233
+ "/auth/token",
19176
19234
  { body: params, contentType: "form" }
19177
19235
  );
19178
19236
  const now = Date.now();
@@ -19183,32 +19241,28 @@ var SealClient = class {
19183
19241
  expiresIn,
19184
19242
  scope: response.scope ?? "",
19185
19243
  refreshToken: void 0,
19186
- // Seal 不返回 refresh_token
19187
19244
  grantedAt: now,
19188
19245
  expiresAt: now + expiresIn * 1e3
19189
19246
  };
19190
- log16.info("Access token acquired", {
19247
+ log17.info("Access token acquired", {
19191
19248
  accessToken: sanitize(tokenData.accessToken),
19192
19249
  expiresIn: tokenData.expiresIn,
19193
19250
  scope: tokenData.scope
19194
19251
  });
19195
19252
  return tokenData;
19196
19253
  }
19197
- /**
19198
- * 令牌校验 → GET /seal/oauth2/introspect
19199
- * 暂不实现 — 保留接口签名,后续需要时再补充
19200
- */
19201
- async introspect(_token) {
19202
- throw new Error("introspect() not implemented yet \u2014 Seal introspect API \u6682\u4E0D\u4F7F\u7528");
19254
+ /** 获取 baseUrl (供其他模块复用) */
19255
+ getBaseUrl() {
19256
+ return this.baseUrl;
19203
19257
  }
19204
19258
  };
19205
19259
 
19206
19260
  // src/auth/token-store.ts
19207
19261
  var import_promises = require("fs/promises");
19208
- var import_node_fs = require("fs");
19209
- var import_node_path = require("path");
19210
- var import_node_crypto2 = require("crypto");
19211
- 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");
19212
19266
  var TokenStore = class {
19213
19267
  /** 存储目录路径 */
19214
19268
  storageDir;
@@ -19223,7 +19277,7 @@ var TokenStore = class {
19223
19277
  constructor(storageDir, crypto) {
19224
19278
  this.storageDir = storageDir;
19225
19279
  this.crypto = crypto;
19226
- log17.info("TokenStore initialized", { storageDir });
19280
+ log18.info("TokenStore initialized", { storageDir });
19227
19281
  }
19228
19282
  /**
19229
19283
  * 确保存储目录存在并设置正确的权限。
@@ -19231,9 +19285,9 @@ var TokenStore = class {
19231
19285
  */
19232
19286
  async ensureDir() {
19233
19287
  if (this.dirEnsured) return;
19234
- if (!(0, import_node_fs.existsSync)(this.storageDir)) {
19288
+ if (!(0, import_node_fs2.existsSync)(this.storageDir)) {
19235
19289
  await (0, import_promises.mkdir)(this.storageDir, { recursive: true, mode: 448 });
19236
- log17.debug("Storage directory created", { dir: this.storageDir });
19290
+ log18.debug("Storage directory created", { dir: this.storageDir });
19237
19291
  }
19238
19292
  this.dirEnsured = true;
19239
19293
  }
@@ -19242,7 +19296,7 @@ var TokenStore = class {
19242
19296
  * @param key - 存储键名 (如 'token', 'credentials')
19243
19297
  */
19244
19298
  filePath(key) {
19245
- return (0, import_node_path.join)(this.storageDir, `${key}.enc`);
19299
+ return (0, import_node_path2.join)(this.storageDir, `${key}.enc`);
19246
19300
  }
19247
19301
  /**
19248
19302
  * 保存 Token — 加密写入文件
@@ -19263,12 +19317,12 @@ var TokenStore = class {
19263
19317
  const storage = { ciphertext, keyId };
19264
19318
  const storageJson = JSON.stringify(storage);
19265
19319
  const targetPath = this.filePath(key);
19266
- const tmpSuffix = (0, import_node_crypto2.randomBytes)(4).toString("hex");
19320
+ const tmpSuffix = (0, import_node_crypto.randomBytes)(4).toString("hex");
19267
19321
  const tmpPath = `${targetPath}.${tmpSuffix}.tmp`;
19268
19322
  try {
19269
19323
  await (0, import_promises.writeFile)(tmpPath, storageJson, { mode: 384 });
19270
19324
  await (0, import_promises.rename)(tmpPath, targetPath);
19271
- log17.debug("Token saved", { key, path: targetPath });
19325
+ log18.debug("Token saved", { key, path: targetPath });
19272
19326
  } catch (err) {
19273
19327
  try {
19274
19328
  await (0, import_promises.unlink)(tmpPath);
@@ -19290,8 +19344,8 @@ var TokenStore = class {
19290
19344
  */
19291
19345
  async load(key) {
19292
19346
  const filePath = this.filePath(key);
19293
- if (!(0, import_node_fs.existsSync)(filePath)) {
19294
- 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 });
19295
19349
  return null;
19296
19350
  }
19297
19351
  try {
@@ -19299,10 +19353,10 @@ var TokenStore = class {
19299
19353
  const storage = JSON.parse(raw);
19300
19354
  const json2 = await this.crypto.decrypt(storage.ciphertext, storage.keyId);
19301
19355
  const data = JSON.parse(json2);
19302
- log17.debug("Token loaded", { key, path: filePath });
19356
+ log18.debug("Token loaded", { key, path: filePath });
19303
19357
  return data;
19304
19358
  } catch (err) {
19305
- 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", {
19306
19360
  key,
19307
19361
  error: err instanceof Error ? err.message : String(err)
19308
19362
  });
@@ -19318,7 +19372,7 @@ var TokenStore = class {
19318
19372
  const filePath = this.filePath(key);
19319
19373
  try {
19320
19374
  await (0, import_promises.unlink)(filePath);
19321
- log17.debug("Token file cleared", { key, path: filePath });
19375
+ log18.debug("Token file cleared", { key, path: filePath });
19322
19376
  } catch (err) {
19323
19377
  if (err.code !== "ENOENT") {
19324
19378
  throw err;
@@ -19328,17 +19382,15 @@ var TokenStore = class {
19328
19382
  };
19329
19383
 
19330
19384
  // src/auth/token-manager.ts
19331
- var log18 = createLogger("auth/token-manager");
19385
+ var log19 = createLogger("auth/token-manager");
19332
19386
  var DEFAULT_REFRESH_AHEAD_MS = 5 * 60 * 1e3;
19333
19387
  var MAX_RETRIES = 3;
19334
19388
  var RETRY_BASE_MS = 1e3;
19335
19389
  var TokenManager = class {
19336
- sealClient;
19390
+ oauthClient;
19337
19391
  tokenStore;
19338
19392
  /** Token 过期前提前刷新的时间 (ms) */
19339
19393
  refreshAheadMs;
19340
- /** 自动注册的参数 */
19341
- autoRegisterParams;
19342
19394
  // ── 内存缓存 ──
19343
19395
  /** 内存中缓存的 access_token */
19344
19396
  cachedToken = null;
@@ -19352,11 +19404,10 @@ var TokenManager = class {
19352
19404
  /** 定时刷新器句柄 */
19353
19405
  refreshTimer = null;
19354
19406
  constructor(deps) {
19355
- this.sealClient = deps.sealClient;
19407
+ this.oauthClient = deps.oauthClient;
19356
19408
  this.tokenStore = deps.tokenStore;
19357
19409
  this.refreshAheadMs = deps.refreshAheadMs ?? DEFAULT_REFRESH_AHEAD_MS;
19358
- this.autoRegisterParams = deps.autoRegisterParams;
19359
- log18.info("TokenManager initialized", { refreshAheadMs: this.refreshAheadMs });
19410
+ log19.info("TokenManager initialized", { refreshAheadMs: this.refreshAheadMs });
19360
19411
  }
19361
19412
  // ── 核心方法 ──
19362
19413
  /**
@@ -19374,7 +19425,7 @@ var TokenManager = class {
19374
19425
  return this.cachedToken;
19375
19426
  }
19376
19427
  if (this.refreshLock) {
19377
- log18.debug("Waiting for concurrent token acquisition");
19428
+ log19.debug("Waiting for concurrent token acquisition");
19378
19429
  return this.refreshLock;
19379
19430
  }
19380
19431
  this.refreshLock = this._acquireTokenWithRetry();
@@ -19384,13 +19435,6 @@ var TokenManager = class {
19384
19435
  this.refreshLock = null;
19385
19436
  }
19386
19437
  }
19387
- /**
19388
- * 令牌自省 — 暂不实现,后续补充。
19389
- * 返回的 identity_id 用于 gate.ts 的 anti-loop 检查 (bot 自己的 ID)。
19390
- */
19391
- async introspect() {
19392
- throw new Error("introspect() not implemented yet \u2014 Seal introspect API \u6682\u4E0D\u4F7F\u7528");
19393
- }
19394
19438
  /** 检查当前令牌是否包含指定的 scope */
19395
19439
  hasScope(scope) {
19396
19440
  if (!this.cachedScope) return false;
@@ -19407,13 +19451,13 @@ var TokenManager = class {
19407
19451
  this.cachedToken = null;
19408
19452
  this.cachedScope = null;
19409
19453
  this.currentTokenData = null;
19410
- log18.info("Token revoked and cleared");
19454
+ log19.info("Token revoked and cleared");
19411
19455
  }
19412
19456
  /** 清理定时器和并发锁 — 优雅关闭时调用 */
19413
19457
  shutdown() {
19414
19458
  this.clearRefreshTimer();
19415
19459
  this.refreshLock = null;
19416
- log18.info("TokenManager shutdown");
19460
+ log19.info("TokenManager shutdown");
19417
19461
  }
19418
19462
  // ── 内部方法 ──
19419
19463
  /**
@@ -19421,26 +19465,26 @@ var TokenManager = class {
19421
19465
  *
19422
19466
  * 重试策略:
19423
19467
  * - 网络错误 / 5xx → 重试 (最多 3 次)
19424
- * - 401/403 → 不重试 (client_secret 可能无效)
19468
+ * - 401/403 → 不重试 (凭证可能无效)
19425
19469
  */
19426
19470
  async _acquireTokenWithRetry() {
19427
19471
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
19428
19472
  try {
19429
19473
  return await this._acquireToken();
19430
19474
  } catch (err) {
19431
- if (err instanceof SealApiError && (err.status === 401 || err.status === 403)) {
19432
- 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 });
19433
19477
  throw err;
19434
19478
  }
19435
19479
  if (attempt === MAX_RETRIES) {
19436
- log18.error("Token acquire failed after all retries", { attempts: attempt + 1 });
19480
+ log19.error("Token acquire failed after all retries", { attempts: attempt + 1 });
19437
19481
  throw new TokenAcquireError(
19438
19482
  `Token acquire failed after ${attempt + 1} attempts`,
19439
19483
  { cause: err instanceof Error ? err : void 0 }
19440
19484
  );
19441
19485
  }
19442
19486
  const delay = RETRY_BASE_MS * Math.pow(2, attempt);
19443
- log18.warn("Token acquire failed, retrying", {
19487
+ log19.warn("Token acquire failed, retrying", {
19444
19488
  attempt: attempt + 1,
19445
19489
  maxRetries: MAX_RETRIES,
19446
19490
  retryInMs: delay,
@@ -19456,63 +19500,27 @@ var TokenManager = class {
19456
19500
  *
19457
19501
  * 流程:
19458
19502
  * 1. 尝试从 TokenStore 加载缓存
19459
- * 2. 检查是否需要注册客户端
19460
- * 3. 调用 SealClient.getToken() 获取新 Token
19461
- * 4. 保存到内存缓存 + TokenStore
19462
- * 5. 设置定时刷新器
19503
+ * 2. 调用 OAuthClient.getToken() 获取新 Token
19504
+ * 3. 保存到内存缓存 + TokenStore
19505
+ * 4. 设置定时刷新器
19463
19506
  */
19464
19507
  async _acquireToken() {
19465
19508
  try {
19466
19509
  const stored = await this.tokenStore.load("token");
19467
19510
  if (stored && !this.isTokenHardExpired(stored)) {
19468
- log18.info("Token loaded from store (still valid)");
19511
+ log19.info("Token loaded from store (still valid)");
19469
19512
  this.updateCache(stored);
19470
19513
  this.scheduleRefresh(stored);
19471
19514
  return stored.accessToken;
19472
19515
  }
19473
19516
  if (stored) {
19474
- log18.info("Stored token has expired, acquiring new one");
19517
+ log19.info("Stored token has expired, acquiring new one");
19475
19518
  }
19476
19519
  } catch {
19477
- log18.warn("Failed to load token from store, acquiring new one");
19520
+ log19.warn("Failed to load token from store, acquiring new one");
19478
19521
  }
19479
- if (!this.sealClient.isRegistered()) {
19480
- try {
19481
- const savedCreds = await this.tokenStore.load("credentials");
19482
- if (savedCreds?.clientId && savedCreds?.clientSecret) {
19483
- this.sealClient.setCredentials(
19484
- savedCreds.clientId,
19485
- savedCreds.clientSecret
19486
- );
19487
- log18.info("Credentials loaded from store");
19488
- }
19489
- } catch {
19490
- log18.debug("No saved credentials found");
19491
- }
19492
- }
19493
- if (!this.sealClient.isRegistered()) {
19494
- if (!this.autoRegisterParams) {
19495
- throw new Error("SealClient not registered and autoRegisterParams not provided. Call setCredentials() or enable autoRegister.");
19496
- }
19497
- log18.info("Auto-registering OAuth client");
19498
- const registration = await this.sealClient.register({
19499
- clientName: this.autoRegisterParams.clientName,
19500
- scopes: this.autoRegisterParams.scopes,
19501
- tokenSettings: this.autoRegisterParams.tokenSettings ? {
19502
- accessTokenTtl: this.autoRegisterParams.tokenSettings.accessTokenTtl,
19503
- refreshTokenTtl: this.autoRegisterParams.tokenSettings.refreshTokenTtl,
19504
- accessTokenFormat: this.autoRegisterParams.tokenSettings.accessTokenFormat
19505
- } : void 0
19506
- });
19507
- await this.tokenStore.save("credentials", {
19508
- clientId: registration.clientId,
19509
- clientSecret: registration.clientSecret,
19510
- clientSecretExpiresAt: registration.clientSecretExpiresAt
19511
- });
19512
- log18.info("OAuth client registered and credentials saved");
19513
- }
19514
- log18.info("Acquiring new access token");
19515
- const tokenData = await this.sealClient.getToken();
19522
+ log19.info("Acquiring new access token via POST /auth/token");
19523
+ const tokenData = await this.oauthClient.getToken();
19516
19524
  this.updateCache(tokenData);
19517
19525
  await this.tokenStore.save("token", tokenData);
19518
19526
  this.scheduleRefresh(tokenData);
@@ -19542,22 +19550,22 @@ var TokenManager = class {
19542
19550
  this.clearRefreshTimer();
19543
19551
  const refreshInMs = tokenData.expiresAt - this.refreshAheadMs - Date.now();
19544
19552
  if (refreshInMs <= 0) {
19545
- log18.debug("Token already within refresh window, not scheduling timer");
19553
+ log19.debug("Token already within refresh window, not scheduling timer");
19546
19554
  return;
19547
19555
  }
19548
- log18.debug("Scheduling token refresh", {
19556
+ log19.debug("Scheduling token refresh", {
19549
19557
  refreshInMs,
19550
19558
  refreshAt: new Date(Date.now() + refreshInMs).toISOString()
19551
19559
  });
19552
19560
  this.refreshTimer = setTimeout(async () => {
19553
19561
  try {
19554
- log18.info("Scheduled token refresh triggered");
19562
+ log19.info("Scheduled token refresh triggered");
19555
19563
  this.cachedToken = null;
19556
19564
  this.currentTokenData = null;
19557
19565
  await this.getValidToken();
19558
- log18.info("Scheduled token refresh completed");
19566
+ log19.info("Scheduled token refresh completed");
19559
19567
  } catch (err) {
19560
- log18.error("Scheduled token refresh failed", {
19568
+ log19.error("Scheduled token refresh failed", {
19561
19569
  error: err instanceof Error ? err.message : String(err)
19562
19570
  });
19563
19571
  }
@@ -19585,13 +19593,13 @@ var wrapper_default = import_websocket.default;
19585
19593
 
19586
19594
  // src/transport/ws-client.ts
19587
19595
  var import_node_events = require("events");
19588
- var log19 = createLogger("transport/ws-client");
19596
+ var log20 = createLogger("transport/ws-client");
19589
19597
  var WSClient = class extends import_node_events.EventEmitter {
19590
19598
  /** 底层 WebSocket 实例 */
19591
19599
  ws = null;
19592
19600
  constructor() {
19593
19601
  super();
19594
- log19.info("WSClient initialized");
19602
+ log20.info("WSClient initialized");
19595
19603
  }
19596
19604
  /**
19597
19605
  * 连接到 WebSocket 服务器并绑定事件。
@@ -19607,11 +19615,11 @@ var WSClient = class extends import_node_events.EventEmitter {
19607
19615
  this.ws = null;
19608
19616
  }
19609
19617
  const { url: url2, protocols, headers } = options;
19610
- log19.info("ws:connecting", { url: url2 });
19618
+ log20.info("ws:connecting", { url: url2 });
19611
19619
  return new Promise((resolve2, reject) => {
19612
19620
  const ws = new wrapper_default(url2, protocols, { headers });
19613
19621
  ws.on("open", () => {
19614
- log19.info("ws:connected", { url: url2 });
19622
+ log20.info("ws:connected", { url: url2 });
19615
19623
  this.emit("open");
19616
19624
  resolve2();
19617
19625
  });
@@ -19619,11 +19627,11 @@ var WSClient = class extends import_node_events.EventEmitter {
19619
19627
  this.emit("message", data);
19620
19628
  });
19621
19629
  ws.on("close", (code, reason) => {
19622
- log19.info("ws:closed", { code, reason: reason.toString() });
19630
+ log20.info("ws:closed", { code, reason: reason.toString() });
19623
19631
  this.emit("close", code, reason.toString());
19624
19632
  });
19625
19633
  ws.on("error", (err) => {
19626
- log19.error("ws:error", { error: err.message });
19634
+ log20.error("ws:error", { error: err.message });
19627
19635
  this.emit("error", err);
19628
19636
  if (this.ws !== ws) {
19629
19637
  reject(err);
@@ -19635,7 +19643,7 @@ var WSClient = class extends import_node_events.EventEmitter {
19635
19643
  /** 发送数据到服务器 — 前置检查连接状态 */
19636
19644
  send(data) {
19637
19645
  if (!this.ws || this.ws.readyState !== wrapper_default.OPEN) {
19638
- log19.warn("ws:send skipped, not connected", { readyState: this.ws?.readyState });
19646
+ log20.warn("ws:send skipped, not connected", { readyState: this.ws?.readyState });
19639
19647
  return;
19640
19648
  }
19641
19649
  this.ws.send(data);
@@ -19646,7 +19654,7 @@ var WSClient = class extends import_node_events.EventEmitter {
19646
19654
  this.ws.removeAllListeners();
19647
19655
  this.ws.close(code, reason);
19648
19656
  this.ws = null;
19649
- log19.info("ws:close requested", { code, reason });
19657
+ log20.info("ws:close requested", { code, reason });
19650
19658
  }
19651
19659
  }
19652
19660
  /** 当前连接状态 (0=CONNECTING, 1=OPEN, 2=CLOSING, 3=CLOSED) */
@@ -19656,7 +19664,7 @@ var WSClient = class extends import_node_events.EventEmitter {
19656
19664
  };
19657
19665
 
19658
19666
  // src/transport/dedup.ts
19659
- var log20 = createLogger("transport/dedup");
19667
+ var log21 = createLogger("transport/dedup");
19660
19668
  var MessageDedup = class {
19661
19669
  /** 去重缓存生存时间 (ms) */
19662
19670
  ttlMs;
@@ -19667,7 +19675,7 @@ var MessageDedup = class {
19667
19675
  constructor(config2) {
19668
19676
  this.ttlMs = config2?.ttlMs ?? 3e5;
19669
19677
  this.maxEntries = config2?.maxEntries ?? 5e3;
19670
- log20.info("MessageDedup initialized", { ttlMs: this.ttlMs, maxEntries: this.maxEntries });
19678
+ log21.info("MessageDedup initialized", { ttlMs: this.ttlMs, maxEntries: this.maxEntries });
19671
19679
  }
19672
19680
  /**
19673
19681
  * 检查消息是否重复。
@@ -19702,8 +19710,8 @@ var MessageDedup = class {
19702
19710
  };
19703
19711
 
19704
19712
  // src/transport/message-pipe.ts
19705
- var import_node_crypto3 = require("crypto");
19706
- var log21 = createLogger("transport/message-pipe");
19713
+ var import_node_crypto2 = require("crypto");
19714
+ var log22 = createLogger("transport/message-pipe");
19707
19715
  var MessagePipe = class {
19708
19716
  wsClient;
19709
19717
  dedup;
@@ -19722,15 +19730,15 @@ var MessagePipe = class {
19722
19730
  this.messageServiceBaseUrl = deps.messageServiceBaseUrl;
19723
19731
  this.wsClient.on("message", (rawData) => {
19724
19732
  this.handleInbound(rawData).catch((err) => {
19725
- log21.error("inbound:error", { step: "handleInbound", error: err.message });
19733
+ log22.error("inbound:error", { step: "handleInbound", error: err.message });
19726
19734
  });
19727
19735
  });
19728
- log21.info("MessagePipe initialized");
19736
+ log22.info("MessagePipe initialized");
19729
19737
  }
19730
19738
  /** 注册入站消息回调 — 解密后的消息会通过此回调传给 L4 层 */
19731
19739
  onMessage(callback) {
19732
19740
  this.messageCallback = callback;
19733
- log21.info("Inbound message callback registered");
19741
+ log22.info("Inbound message callback registered");
19734
19742
  }
19735
19743
  /**
19736
19744
  * 出站发送 — 通过 HTTP API 发送消息到 IM 服务器。
@@ -19750,7 +19758,7 @@ var MessagePipe = class {
19750
19758
  }
19751
19759
  const result = await this.callMessageApi("/messages/v1/send", body, "outbound");
19752
19760
  if (!result) return;
19753
- log21.info("outbound:sent", {
19761
+ log22.info("outbound:sent", {
19754
19762
  chatId: msg.chatId,
19755
19763
  senderId: msg.senderId,
19756
19764
  msgType: msg.msgType,
@@ -19774,7 +19782,7 @@ var MessagePipe = class {
19774
19782
  };
19775
19783
  const result = await this.callMessageApi("/messages/v1/recall", body, "recall");
19776
19784
  if (!result) return;
19777
- log21.info("recall:sent", {
19785
+ log22.info("recall:sent", {
19778
19786
  messageId: params.messageId,
19779
19787
  requestId: result.request_id
19780
19788
  });
@@ -19801,7 +19809,7 @@ var MessagePipe = class {
19801
19809
  body: JSON.stringify(body)
19802
19810
  });
19803
19811
  if (!resp.ok) {
19804
- log21.error(`${logTag}:http-error`, {
19812
+ log22.error(`${logTag}:http-error`, {
19805
19813
  status: resp.status,
19806
19814
  statusText: resp.statusText,
19807
19815
  url: url2
@@ -19810,7 +19818,7 @@ var MessagePipe = class {
19810
19818
  }
19811
19819
  const result = await resp.json();
19812
19820
  if (result.code !== 0) {
19813
- log21.error(`${logTag}:api-error`, {
19821
+ log22.error(`${logTag}:api-error`, {
19814
19822
  code: result.code,
19815
19823
  msg: result.msg,
19816
19824
  requestId: result.request_id
@@ -19819,7 +19827,7 @@ var MessagePipe = class {
19819
19827
  }
19820
19828
  return result;
19821
19829
  } catch (err) {
19822
- log21.error(`${logTag}:network-error`, {
19830
+ log22.error(`${logTag}:network-error`, {
19823
19831
  url: url2,
19824
19832
  error: err.message
19825
19833
  });
@@ -19841,7 +19849,7 @@ var MessagePipe = class {
19841
19849
  try {
19842
19850
  frame = JSON.parse(String(rawData));
19843
19851
  } catch (err) {
19844
- log21.warn("inbound:json-parse-error", { error: err.message });
19852
+ log22.warn("inbound:json-parse-error", { error: err.message });
19845
19853
  return;
19846
19854
  }
19847
19855
  const timestamp = frame["X-CTQ-Timestamp"];
@@ -19850,9 +19858,9 @@ var MessagePipe = class {
19850
19858
  if (timestamp && nonce && signature) {
19851
19859
  const token = await this.tokenFn();
19852
19860
  const signatureInput = timestamp + nonce + frame.data;
19853
- 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");
19854
19862
  if (expected !== signature) {
19855
- log21.warn("inbound:signature-fail", { timestamp, nonce });
19863
+ log22.warn("inbound:signature-fail", { timestamp, nonce });
19856
19864
  return;
19857
19865
  }
19858
19866
  }
@@ -19866,30 +19874,37 @@ var MessagePipe = class {
19866
19874
  try {
19867
19875
  callbackData = JSON.parse(dataStr);
19868
19876
  } catch (err) {
19869
- log21.warn("inbound:data-parse-error", { error: err.message });
19877
+ log22.warn("inbound:data-parse-error", { error: err.message });
19870
19878
  return;
19871
19879
  }
19872
- if (callbackData.eventType !== "MessageReceived") {
19873
- log21.debug("inbound:event-ignored", { eventType: callbackData.eventType });
19880
+ if (callbackData.eventType !== "callback:direct") {
19881
+ log22.debug("inbound:event-ignored", { eventType: callbackData.eventType });
19874
19882
  return;
19875
19883
  }
19876
- const msg = callbackData.content;
19877
- 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 });
19878
19893
  if (this.dedup.isDuplicate(msg.messageId)) {
19879
- log21.debug("inbound:dedup-hit", { messageId: msg.messageId });
19894
+ log22.debug("inbound:dedup-hit", { messageId: msg.messageId });
19880
19895
  return;
19881
19896
  }
19882
- log21.info("inbound:verified", { messageId: msg.messageId, chatId: msg.chatId });
19897
+ log22.info("inbound:verified", { messageId: msg.messageId, chatId: msg.chatId });
19883
19898
  if (this.messageCallback) {
19884
19899
  this.messageCallback(msg);
19885
19900
  } else {
19886
- log21.warn("inbound:no-callback", { messageId: msg.messageId });
19901
+ log22.warn("inbound:no-callback", { messageId: msg.messageId });
19887
19902
  }
19888
19903
  }
19889
19904
  };
19890
19905
 
19891
19906
  // src/transport/connection-manager.ts
19892
- var log22 = createLogger("transport/connection-manager");
19907
+ var log23 = createLogger("transport/connection-manager");
19893
19908
  function sleep2(ms) {
19894
19909
  return new Promise((resolve2) => setTimeout(resolve2, ms));
19895
19910
  }
@@ -19919,7 +19934,7 @@ var ConnectionManager = class {
19919
19934
  reconnectMaxMs: options?.reconnectMaxMs ?? 6e4,
19920
19935
  maxReconnectAttempts: options?.maxReconnectAttempts ?? 10
19921
19936
  };
19922
- log22.info("ConnectionManager initialized", this.options);
19937
+ log23.info("ConnectionManager initialized", this.options);
19923
19938
  }
19924
19939
  /**
19925
19940
  * 启动连接 — 连接 WS + 开始心跳 + 注册重连逻辑
@@ -19934,29 +19949,33 @@ var ConnectionManager = class {
19934
19949
  this.running = true;
19935
19950
  this.reconnectAttempts = 0;
19936
19951
  const token = await tokenFn();
19952
+ const wsUrl = url2.replace(/\/+$/, "") + "/events/stream";
19937
19953
  await this.client.connect({
19938
- url: url2,
19954
+ url: wsUrl,
19939
19955
  token,
19940
- headers: this.appId ? { "X-App-ID": this.appId } : void 0
19956
+ headers: {
19957
+ "Authorization": token,
19958
+ ...this.appId ? { "X-App-ID": this.appId } : {}
19959
+ }
19941
19960
  });
19942
19961
  this.client.on("close", () => {
19943
19962
  if (this.running) {
19944
- log22.warn("ws:disconnected, scheduling reconnect");
19963
+ log23.warn("ws:disconnected, scheduling reconnect");
19945
19964
  this.scheduleReconnect();
19946
19965
  }
19947
19966
  });
19948
19967
  this.client.on("error", (err) => {
19949
- log22.error("ws:connection-error", { error: err.message });
19968
+ log23.error("ws:connection-error", { error: err.message });
19950
19969
  });
19951
19970
  this.startHeartbeat();
19952
- log22.info("ConnectionManager started \u2713", { url: url2 });
19971
+ log23.info("ConnectionManager started \u2713", { url: url2 });
19953
19972
  }
19954
19973
  /** 启动心跳保活 — 定时检查连接状态 */
19955
19974
  startHeartbeat() {
19956
19975
  this.stopHeartbeat();
19957
19976
  this.heartbeatTimer = setInterval(() => {
19958
19977
  if (this.client.readyState !== wrapper_default.OPEN) {
19959
- log22.warn("heartbeat: connection not open, scheduling reconnect");
19978
+ log23.warn("heartbeat: connection not open, scheduling reconnect");
19960
19979
  this.scheduleReconnect();
19961
19980
  }
19962
19981
  }, this.options.heartbeatIntervalMs);
@@ -19984,30 +20003,34 @@ var ConnectionManager = class {
19984
20003
  this.options.reconnectBaseMs * 2 ** this.reconnectAttempts,
19985
20004
  this.options.reconnectMaxMs
19986
20005
  );
19987
- log22.info("ws:reconnecting", { attempt: this.reconnectAttempts + 1, delayMs: delay });
20006
+ log23.info("ws:reconnecting", { attempt: this.reconnectAttempts + 1, delayMs: delay });
19988
20007
  await sleep2(delay);
19989
20008
  if (!this.running) return;
19990
20009
  this.reconnectAttempts++;
19991
20010
  try {
19992
20011
  const token = await this.tokenFn();
20012
+ const wsUrl = this.url.replace(/\/+$/, "") + "/events/stream";
19993
20013
  await this.client.connect({
19994
- url: this.url,
20014
+ url: wsUrl,
19995
20015
  token,
19996
- headers: this.appId ? { "X-App-ID": this.appId } : void 0
20016
+ headers: {
20017
+ "Authorization": token,
20018
+ ...this.appId ? { "X-App-ID": this.appId } : {}
20019
+ }
19997
20020
  });
19998
20021
  this.reconnectAttempts = 0;
19999
20022
  this.startHeartbeat();
20000
- log22.info("ws:reconnected", { attempt: this.reconnectAttempts, url: this.url });
20023
+ log23.info("ws:reconnected", { attempt: this.reconnectAttempts, url: this.url });
20001
20024
  return;
20002
20025
  } catch (err) {
20003
- log22.warn("ws:reconnect-failed", {
20026
+ log23.warn("ws:reconnect-failed", {
20004
20027
  attempt: this.reconnectAttempts,
20005
20028
  error: err.message
20006
20029
  });
20007
20030
  }
20008
20031
  }
20009
20032
  if (this.running) {
20010
- log22.error("ws:max-reconnect-reached", {
20033
+ log23.error("ws:max-reconnect-reached", {
20011
20034
  maxAttempts: this.options.maxReconnectAttempts
20012
20035
  });
20013
20036
  }
@@ -20017,7 +20040,7 @@ var ConnectionManager = class {
20017
20040
  this.running = false;
20018
20041
  this.stopHeartbeat();
20019
20042
  this.client.close(1e3, "shutdown");
20020
- log22.info("ConnectionManager stopped");
20043
+ log23.info("ConnectionManager stopped");
20021
20044
  }
20022
20045
  /** 当前是否已连接 (WebSocket readyState === OPEN) */
20023
20046
  get isConnected() {
@@ -20026,7 +20049,7 @@ var ConnectionManager = class {
20026
20049
  };
20027
20050
 
20028
20051
  // src/push/cockatoo-client.ts
20029
- var log23 = createLogger("push/cockatoo-client");
20052
+ var log24 = createLogger("push/cockatoo-client");
20030
20053
  var DEFAULT_TIMEOUT_MS2 = 3e4;
20031
20054
  var CockatooPushError = class extends Error {
20032
20055
  constructor(status, body, endpoint) {
@@ -20060,7 +20083,7 @@ var CockatooClient = class {
20060
20083
  const base = config2.endpoint.replace(/\/+$/, "");
20061
20084
  this.pushUrl = `${base}/api/v1/push`;
20062
20085
  this.healthUrl = `${base}/health`;
20063
- log23.info("CockatooClient initialized", { endpoint: config2.endpoint });
20086
+ log24.info("CockatooClient initialized", { endpoint: config2.endpoint });
20064
20087
  }
20065
20088
  /**
20066
20089
  * 推送消息到 Cockatoo 服务。
@@ -20091,7 +20114,7 @@ var CockatooClient = class {
20091
20114
  }
20092
20115
  throw new CockatooPushError(resp.status, body, this.pushUrl);
20093
20116
  }
20094
- log23.debug("push:sent", { type: payload.type });
20117
+ log24.debug("push:sent", { type: payload.type });
20095
20118
  }
20096
20119
  /**
20097
20120
  * 执行一次健康检查。
@@ -20109,11 +20132,11 @@ var CockatooClient = class {
20109
20132
  });
20110
20133
  const isHealthy = resp.ok;
20111
20134
  if (!isHealthy) {
20112
- log23.warn("health-check:unhealthy", { status: resp.status });
20135
+ log24.warn("health-check:unhealthy", { status: resp.status });
20113
20136
  }
20114
20137
  return isHealthy;
20115
20138
  } catch (err) {
20116
- log23.warn("health-check:error", { error: err.message });
20139
+ log24.warn("health-check:error", { error: err.message });
20117
20140
  return false;
20118
20141
  }
20119
20142
  }
@@ -20125,7 +20148,7 @@ var CockatooClient = class {
20125
20148
  this.healthy = await this.healthCheck();
20126
20149
  } catch {
20127
20150
  this.healthy = false;
20128
- log23.warn("Cockatoo health check failed");
20151
+ log24.warn("Cockatoo health check failed");
20129
20152
  }
20130
20153
  }, this.config.healthCheckIntervalMs ?? 6e4);
20131
20154
  if (typeof this.healthCheckTimer === "object" && "unref" in this.healthCheckTimer) {
@@ -20146,7 +20169,7 @@ var CockatooClient = class {
20146
20169
  };
20147
20170
 
20148
20171
  // src/push/push-queue.ts
20149
- var log24 = createLogger("push/push-queue");
20172
+ var log25 = createLogger("push/push-queue");
20150
20173
  var RETRY_BASE_MS2 = 1e3;
20151
20174
  var PushQueue = class {
20152
20175
  client;
@@ -20188,7 +20211,7 @@ var PushQueue = class {
20188
20211
  this.unhealthyRetryMs = config2?.unhealthyRetryMs ?? 5e3;
20189
20212
  this.drainOnStop = config2?.drainOnStop ?? false;
20190
20213
  this.drainTimeoutMs = config2?.drainTimeoutMs ?? 5e3;
20191
- log24.info("PushQueue initialized", {
20214
+ log25.info("PushQueue initialized", {
20192
20215
  maxSize: this.maxSize,
20193
20216
  retryAttempts: this.retryAttempts,
20194
20217
  processIntervalMs: this.processIntervalMs
@@ -20199,7 +20222,7 @@ var PushQueue = class {
20199
20222
  start() {
20200
20223
  if (this.running) return;
20201
20224
  this.running = true;
20202
- log24.info("PushQueue started");
20225
+ log25.info("PushQueue started");
20203
20226
  this.scheduleNext(0);
20204
20227
  }
20205
20228
  /**
@@ -20213,10 +20236,10 @@ var PushQueue = class {
20213
20236
  this.running = false;
20214
20237
  this.clearTimer();
20215
20238
  if (this.drainOnStop && this.queue.length > 0) {
20216
- log24.info("PushQueue draining", { remaining: this.queue.length });
20239
+ log25.info("PushQueue draining", { remaining: this.queue.length });
20217
20240
  await this.drain();
20218
20241
  }
20219
- log24.info("PushQueue stopped", {
20242
+ log25.info("PushQueue stopped", {
20220
20243
  remaining: this.queue.length,
20221
20244
  sent: this.sentCount,
20222
20245
  dropped: this.droppedCount,
@@ -20231,13 +20254,13 @@ var PushQueue = class {
20231
20254
  if (this.queue.length >= this.maxSize) {
20232
20255
  const dropped = this.queue.shift();
20233
20256
  this.droppedCount++;
20234
- log24.warn("queue:full, dropping oldest", {
20257
+ log25.warn("queue:full, dropping oldest", {
20235
20258
  droppedType: dropped?.payload.type,
20236
20259
  queueSize: this.queue.length
20237
20260
  });
20238
20261
  }
20239
20262
  this.queue.push({ payload, retries: 0, eligibleAt: 0 });
20240
- log24.debug("queue:enqueued", { type: payload.type, queueSize: this.queue.length });
20263
+ log25.debug("queue:enqueued", { type: payload.type, queueSize: this.queue.length });
20241
20264
  }
20242
20265
  /** 当前队列长度 */
20243
20266
  get size() {
@@ -20262,7 +20285,7 @@ var PushQueue = class {
20262
20285
  this.clearTimer();
20263
20286
  this.processTimer = setTimeout(() => {
20264
20287
  this.tick().catch((err) => {
20265
- log24.error("tick:unexpected-error", { error: err.message });
20288
+ log25.error("tick:unexpected-error", { error: err.message });
20266
20289
  if (this.running) {
20267
20290
  this.scheduleNext(this.idleIntervalMs);
20268
20291
  }
@@ -20284,7 +20307,7 @@ var PushQueue = class {
20284
20307
  async tick() {
20285
20308
  if (!this.running) return;
20286
20309
  if (!this.client.isHealthy) {
20287
- log24.debug("tick:service-unhealthy, pausing");
20310
+ log25.debug("tick:service-unhealthy, pausing");
20288
20311
  this.scheduleNext(this.unhealthyRetryMs);
20289
20312
  return;
20290
20313
  }
@@ -20304,7 +20327,7 @@ var PushQueue = class {
20304
20327
  try {
20305
20328
  await this.processItem(item);
20306
20329
  } catch (err) {
20307
- log24.error("tick:process-unexpected", { error: err.message });
20330
+ log25.error("tick:process-unexpected", { error: err.message });
20308
20331
  } finally {
20309
20332
  this.processing = false;
20310
20333
  }
@@ -20323,14 +20346,14 @@ var PushQueue = class {
20323
20346
  try {
20324
20347
  await this.client.push(item.payload);
20325
20348
  this.sentCount++;
20326
- log24.debug("push:success", {
20349
+ log25.debug("push:success", {
20327
20350
  type: item.payload.type,
20328
20351
  retries: item.retries
20329
20352
  });
20330
20353
  } catch (err) {
20331
20354
  if (err instanceof CockatooPushError && err.isClientError) {
20332
20355
  this.droppedCount++;
20333
- log24.warn("push:4xx-dropped", {
20356
+ log25.warn("push:4xx-dropped", {
20334
20357
  type: item.payload.type,
20335
20358
  status: err.status,
20336
20359
  retries: item.retries
@@ -20339,7 +20362,7 @@ var PushQueue = class {
20339
20362
  }
20340
20363
  if (item.retries >= this.retryAttempts) {
20341
20364
  this.droppedCount++;
20342
- log24.error("push:max-retries-dropped", {
20365
+ log25.error("push:max-retries-dropped", {
20343
20366
  type: item.payload.type,
20344
20367
  retries: item.retries,
20345
20368
  error: err.message
@@ -20352,7 +20375,7 @@ var PushQueue = class {
20352
20375
  item.eligibleAt = Date.now() + backoffMs;
20353
20376
  this.queue.push(item);
20354
20377
  const errorDetail = err instanceof CockatooPushError ? `HTTP ${err.status}` : err.message;
20355
- log24.warn("push:retry-enqueued", {
20378
+ log25.warn("push:retry-enqueued", {
20356
20379
  type: item.payload.type,
20357
20380
  retries: item.retries,
20358
20381
  backoffMs,
@@ -20376,22 +20399,22 @@ var PushQueue = class {
20376
20399
  try {
20377
20400
  await this.client.push(item.payload);
20378
20401
  this.sentCount++;
20379
- log24.debug("drain:sent", { type: item.payload.type });
20402
+ log25.debug("drain:sent", { type: item.payload.type });
20380
20403
  } catch (err) {
20381
20404
  this.droppedCount++;
20382
- log24.warn("drain:dropped", {
20405
+ log25.warn("drain:dropped", {
20383
20406
  type: item.payload.type,
20384
20407
  error: err.message
20385
20408
  });
20386
20409
  }
20387
20410
  }
20388
20411
  if (this.queue.length > 0) {
20389
- log24.warn("drain:timeout", {
20412
+ log25.warn("drain:timeout", {
20390
20413
  remaining: this.queue.length,
20391
20414
  timeoutMs: this.drainTimeoutMs
20392
20415
  });
20393
20416
  } else {
20394
- log24.info("drain:complete");
20417
+ log25.info("drain:complete");
20395
20418
  }
20396
20419
  }
20397
20420
  // ── 内部工具 ──
@@ -20405,43 +20428,32 @@ var PushQueue = class {
20405
20428
  };
20406
20429
 
20407
20430
  // src/index.ts
20408
- var log25 = createLogger("plugin");
20431
+ var log26 = createLogger("plugin");
20409
20432
  async function startPlugin(accountConfig, internalOverrides) {
20410
20433
  const config2 = buildPluginConfig(accountConfig, internalOverrides);
20411
- log25.info("Config built \u2713", { pluginId: config2.pluginId });
20434
+ log26.info("Config built \u2713", { pluginId: config2.pluginId });
20412
20435
  let cryptoEngine;
20413
20436
  if (config2.crypto.enabled) {
20414
- cryptoEngine = new CryptoEngine();
20437
+ cryptoEngine = new CryptoEngine({ credentials: accountConfig });
20415
20438
  await cryptoEngine.init();
20416
- log25.info("Crypto initialized \u2713");
20439
+ log26.info("Crypto initialized \u2713");
20417
20440
  } else {
20418
20441
  cryptoEngine = CryptoEngine.createPassthrough();
20419
- log25.info("Crypto passthrough mode \u2713 (disabled by config)");
20420
- }
20421
- const sealClient = new SealClient({
20422
- serverUrl: config2.auth.serverUrl,
20423
- clientName: config2.auth.clientName,
20424
- clientId: config2.auth.clientId,
20425
- clientSecret: config2.auth.clientSecret,
20426
- 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
20427
20448
  });
20428
- 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");
20429
20450
  const tokenStore = new TokenStore(tokenStorePath, cryptoEngine);
20430
20451
  const tokenManager = new TokenManager({
20431
- sealClient,
20452
+ oauthClient,
20432
20453
  tokenStore,
20433
- refreshAheadMs: config2.auth.refreshAheadMs,
20434
- autoRegisterParams: config2.auth.autoRegister ? {
20435
- clientName: config2.auth.clientName,
20436
- scopes: config2.auth.scopes,
20437
- tokenSettings: config2.auth.tokenSettings ? {
20438
- accessTokenTtl: config2.auth.tokenSettings.accessTokenTtl,
20439
- refreshTokenTtl: config2.auth.tokenSettings.refreshTokenTtl,
20440
- accessTokenFormat: config2.auth.tokenSettings.accessTokenFormat
20441
- } : void 0
20442
- } : void 0
20454
+ refreshAheadMs: config2.auth.refreshAheadMs
20443
20455
  });
20444
- log25.info("Auth initialized \u2713");
20456
+ log26.info("Auth initialized \u2713");
20445
20457
  let pushQueue = null;
20446
20458
  let cockatooClient = null;
20447
20459
  if (config2.push?.enabled) {
@@ -20456,7 +20468,7 @@ async function startPlugin(accountConfig, internalOverrides) {
20456
20468
  });
20457
20469
  cockatooClient.startHealthCheck();
20458
20470
  pushQueue.start();
20459
- log25.info("Push initialized \u2713");
20471
+ log26.info("Push initialized \u2713");
20460
20472
  }
20461
20473
  const wsClient = new WSClient();
20462
20474
  const dedup = new MessageDedup(config2.transport.dedup);
@@ -20474,8 +20486,8 @@ async function startPlugin(accountConfig, internalOverrides) {
20474
20486
  reconnectMaxMs: config2.transport.reconnectMaxMs,
20475
20487
  maxReconnectAttempts: config2.transport.maxReconnectAttempts
20476
20488
  });
20477
- log25.info("Transport initialized \u2713");
20478
- log25.info("Plugin started \u2713");
20489
+ log26.info("Transport initialized \u2713");
20490
+ log26.info("Plugin started \u2713");
20479
20491
  return {
20480
20492
  config: config2,
20481
20493
  messagePipe,
@@ -20483,12 +20495,12 @@ async function startPlugin(accountConfig, internalOverrides) {
20483
20495
  tokenManager,
20484
20496
  pushQueue,
20485
20497
  shutdown: async () => {
20486
- log25.info("Shutting down...");
20498
+ log26.info("Shutting down...");
20487
20499
  await connectionManager.stop();
20488
20500
  if (pushQueue) await pushQueue.stop();
20489
20501
  if (cockatooClient) cockatooClient.stopHealthCheck();
20490
20502
  tokenManager.shutdown();
20491
- log25.info("Shutdown complete \u2713");
20503
+ log26.info("Shutdown complete \u2713");
20492
20504
  }
20493
20505
  };
20494
20506
  }
@@ -20498,7 +20510,7 @@ var plugin = {
20498
20510
  description: "Quantum-encrypted IM channel plugin",
20499
20511
  register(api) {
20500
20512
  api.registerChannel({ plugin: quantumImPlugin });
20501
- log25.info("plugin registered \u2713");
20513
+ log26.info("plugin registered \u2713");
20502
20514
  }
20503
20515
  };
20504
20516
  var index_default = plugin;