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 +426 -428
- package/dist/index.d.cts +126 -162
- package/dist/setup-entry.cjs +456 -458
- package/package.json +1 -1
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:
|
|
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 =
|
|
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
|
|
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://
|
|
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" ? {
|
|
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
|
-
|
|
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?.
|
|
18121
|
+
fileId = c?.fileId;
|
|
18115
18122
|
text = c?.altText ?? (fileId ? `` : "[image]");
|
|
18116
18123
|
break;
|
|
18117
18124
|
}
|
|
18118
18125
|
case "file": {
|
|
18119
18126
|
const c = parsed;
|
|
18120
|
-
fileId = c?.
|
|
18121
|
-
fileName = c?.
|
|
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?.
|
|
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?.
|
|
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-
|
|
18816
|
-
var
|
|
18817
|
-
var
|
|
18818
|
-
var
|
|
18819
|
-
|
|
18820
|
-
|
|
18821
|
-
|
|
18822
|
-
|
|
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
|
|
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
|
|
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
|
|
18919
|
+
/** 底层量子加密 SDK 实例, 透传模式下为 null */
|
|
18896
18920
|
plug;
|
|
18897
18921
|
/** SDK 是否已完成初始化 */
|
|
18898
|
-
initialized
|
|
18922
|
+
initialized;
|
|
18899
18923
|
/** 是否为透传模式 (不加密, 明文直接透传) */
|
|
18900
18924
|
passthrough;
|
|
18925
|
+
/** 用户凭据 — 用于生成 quantum.json (透传模式下为 undefined) */
|
|
18926
|
+
credentials;
|
|
18901
18927
|
/**
|
|
18902
|
-
* 构造加密引擎
|
|
18928
|
+
* 构造加密引擎
|
|
18903
18929
|
*
|
|
18904
|
-
*
|
|
18905
|
-
*
|
|
18930
|
+
* @param opts - 构造选项
|
|
18931
|
+
* @param opts.passthrough - true 为透传模式 (不加密); false 为加密模式 (默认)
|
|
18932
|
+
* @param opts.credentials - 用户凭据 (加密模式必需, 用于生成 quantum.json)
|
|
18906
18933
|
*
|
|
18907
|
-
*
|
|
18934
|
+
* 请使用静态工厂方法:
|
|
18935
|
+
* - new CryptoEngine({ credentials }) → 加密模式, 使用量子 SDK
|
|
18936
|
+
* - CryptoEngine.createPassthrough() → 透传模式, 无需 init()
|
|
18908
18937
|
*/
|
|
18909
|
-
constructor() {
|
|
18910
|
-
|
|
18911
|
-
this.passthrough =
|
|
18912
|
-
|
|
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
|
-
|
|
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
|
-
|
|
18981
|
+
log16.info("CryptoEngine initialized \u2713");
|
|
18949
18982
|
}
|
|
18950
18983
|
/**
|
|
18951
18984
|
* 加密明文
|
|
18952
18985
|
*
|
|
18953
|
-
* 加密模式: 调用底层 SDK
|
|
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
|
-
|
|
18997
|
+
log16.debug("Encrypt (passthrough)", { length: plaintext.length });
|
|
18964
18998
|
return { ciphertext: plaintext, keyId: PASSTHROUGH_KEY_ID };
|
|
18965
18999
|
}
|
|
18966
|
-
const
|
|
18967
|
-
|
|
18968
|
-
|
|
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
|
-
|
|
19018
|
+
log16.debug("Decrypt (passthrough)", { keyId });
|
|
18985
19019
|
return ciphertext;
|
|
18986
19020
|
}
|
|
18987
|
-
const
|
|
18988
|
-
|
|
18989
|
-
|
|
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
|
-
*
|
|
19120
|
+
* 确保引擎已初始化,同时返回非空 plug 实例
|
|
19121
|
+
* (通过 init() 守卫保证:加密模式下 init() 成功后 plug 一定非 null)
|
|
19001
19122
|
*/
|
|
19002
|
-
|
|
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
|
|
19011
|
-
var
|
|
19132
|
+
var log17 = createLogger("auth/oauth-client");
|
|
19133
|
+
var ApiError = class extends Error {
|
|
19012
19134
|
constructor(status, body, endpoint) {
|
|
19013
|
-
super(`
|
|
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 = "
|
|
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
|
|
19156
|
+
var GRANT_TYPE = "client_credentials";
|
|
19157
|
+
var SCOPE = "client_credentials refresh_token";
|
|
19158
|
+
var OAuthClient = class {
|
|
19035
19159
|
baseUrl;
|
|
19036
|
-
|
|
19037
|
-
|
|
19038
|
-
clientName;
|
|
19039
|
-
scopes;
|
|
19160
|
+
appId;
|
|
19161
|
+
appSecret;
|
|
19040
19162
|
constructor(config2) {
|
|
19041
|
-
this.baseUrl = config2.
|
|
19042
|
-
this.
|
|
19043
|
-
this.
|
|
19044
|
-
this.
|
|
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
|
-
|
|
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
|
-
|
|
19087
|
-
throw new
|
|
19201
|
+
log17.error("HTTP error", { method, url: url2, status: response.status });
|
|
19202
|
+
throw new ApiError(response.status, parsedBody, path2);
|
|
19088
19203
|
}
|
|
19089
|
-
|
|
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
|
|
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
|
-
*
|
|
19213
|
+
* 获取访问令牌 → POST /auth/token (client_credentials)
|
|
19109
19214
|
*
|
|
19110
|
-
*
|
|
19111
|
-
*
|
|
19112
|
-
|
|
19113
|
-
|
|
19114
|
-
|
|
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
|
|
19173
|
-
* @throws SealApiError — HTTP 错误
|
|
19222
|
+
* @throws ApiError — HTTP 错误
|
|
19174
19223
|
*/
|
|
19175
|
-
async getToken(
|
|
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",
|
|
19181
|
-
params.set("client_id", this.
|
|
19182
|
-
params.set("client_secret", this.
|
|
19183
|
-
|
|
19184
|
-
|
|
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
|
-
"/
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
19223
|
-
var
|
|
19224
|
-
var
|
|
19225
|
-
var
|
|
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
|
-
|
|
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,
|
|
19288
|
+
if (!(0, import_node_fs2.existsSync)(this.storageDir)) {
|
|
19249
19289
|
await (0, import_promises.mkdir)(this.storageDir, { recursive: true, mode: 448 });
|
|
19250
|
-
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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,
|
|
19308
|
-
|
|
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
|
-
|
|
19356
|
+
log18.debug("Token loaded", { key, path: filePath });
|
|
19317
19357
|
return data;
|
|
19318
19358
|
} catch (err) {
|
|
19319
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
19407
|
+
this.oauthClient = deps.oauthClient;
|
|
19370
19408
|
this.tokenStore = deps.tokenStore;
|
|
19371
19409
|
this.refreshAheadMs = deps.refreshAheadMs ?? DEFAULT_REFRESH_AHEAD_MS;
|
|
19372
|
-
|
|
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
|
-
|
|
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
|
-
|
|
19454
|
+
log19.info("Token revoked and cleared");
|
|
19425
19455
|
}
|
|
19426
19456
|
/** 清理定时器和并发锁 — 优雅关闭时调用 */
|
|
19427
19457
|
shutdown() {
|
|
19428
19458
|
this.clearRefreshTimer();
|
|
19429
19459
|
this.refreshLock = null;
|
|
19430
|
-
|
|
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 → 不重试 (
|
|
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
|
|
19446
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
19475
|
-
* 4.
|
|
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
|
-
|
|
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
|
-
|
|
19517
|
+
log19.info("Stored token has expired, acquiring new one");
|
|
19489
19518
|
}
|
|
19490
19519
|
} catch {
|
|
19491
|
-
|
|
19520
|
+
log19.warn("Failed to load token from store, acquiring new one");
|
|
19492
19521
|
}
|
|
19493
|
-
|
|
19494
|
-
|
|
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
|
-
|
|
19553
|
+
log19.debug("Token already within refresh window, not scheduling timer");
|
|
19560
19554
|
return;
|
|
19561
19555
|
}
|
|
19562
|
-
|
|
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
|
-
|
|
19562
|
+
log19.info("Scheduled token refresh triggered");
|
|
19569
19563
|
this.cachedToken = null;
|
|
19570
19564
|
this.currentTokenData = null;
|
|
19571
19565
|
await this.getValidToken();
|
|
19572
|
-
|
|
19566
|
+
log19.info("Scheduled token refresh completed");
|
|
19573
19567
|
} catch (err) {
|
|
19574
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
19720
|
-
var
|
|
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
|
-
|
|
19733
|
+
log22.error("inbound:error", { step: "handleInbound", error: err.message });
|
|
19740
19734
|
});
|
|
19741
19735
|
});
|
|
19742
|
-
|
|
19736
|
+
log22.info("MessagePipe initialized");
|
|
19743
19737
|
}
|
|
19744
19738
|
/** 注册入站消息回调 — 解密后的消息会通过此回调传给 L4 层 */
|
|
19745
19739
|
onMessage(callback) {
|
|
19746
19740
|
this.messageCallback = callback;
|
|
19747
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
19861
|
+
const expected = (0, import_node_crypto2.createHmac)("sha256", token).update(signatureInput).digest("hex");
|
|
19868
19862
|
if (expected !== signature) {
|
|
19869
|
-
|
|
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
|
-
|
|
19877
|
+
log22.warn("inbound:data-parse-error", { error: err.message });
|
|
19884
19878
|
return;
|
|
19885
19879
|
}
|
|
19886
|
-
if (callbackData.eventType !== "
|
|
19887
|
-
|
|
19880
|
+
if (callbackData.eventType !== "callback:direct") {
|
|
19881
|
+
log22.debug("inbound:event-ignored", { eventType: callbackData.eventType });
|
|
19888
19882
|
return;
|
|
19889
19883
|
}
|
|
19890
|
-
const msg =
|
|
19891
|
-
|
|
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
|
-
|
|
19894
|
+
log22.debug("inbound:dedup-hit", { messageId: msg.messageId });
|
|
19894
19895
|
return;
|
|
19895
19896
|
}
|
|
19896
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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:
|
|
19954
|
+
url: wsUrl,
|
|
19953
19955
|
token,
|
|
19954
|
-
headers:
|
|
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
|
-
|
|
19963
|
+
log23.warn("ws:disconnected, scheduling reconnect");
|
|
19959
19964
|
this.scheduleReconnect();
|
|
19960
19965
|
}
|
|
19961
19966
|
});
|
|
19962
19967
|
this.client.on("error", (err) => {
|
|
19963
|
-
|
|
19968
|
+
log23.error("ws:connection-error", { error: err.message });
|
|
19964
19969
|
});
|
|
19965
19970
|
this.startHeartbeat();
|
|
19966
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
20014
|
+
url: wsUrl,
|
|
20009
20015
|
token,
|
|
20010
|
-
headers:
|
|
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
|
-
|
|
20023
|
+
log23.info("ws:reconnected", { attempt: this.reconnectAttempts, url: this.url });
|
|
20015
20024
|
return;
|
|
20016
20025
|
} catch (err) {
|
|
20017
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
20135
|
+
log24.warn("health-check:unhealthy", { status: resp.status });
|
|
20127
20136
|
}
|
|
20128
20137
|
return isHealthy;
|
|
20129
20138
|
} catch (err) {
|
|
20130
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
20239
|
+
log25.info("PushQueue draining", { remaining: this.queue.length });
|
|
20231
20240
|
await this.drain();
|
|
20232
20241
|
}
|
|
20233
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
20402
|
+
log25.debug("drain:sent", { type: item.payload.type });
|
|
20394
20403
|
} catch (err) {
|
|
20395
20404
|
this.droppedCount++;
|
|
20396
|
-
|
|
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
|
-
|
|
20412
|
+
log25.warn("drain:timeout", {
|
|
20404
20413
|
remaining: this.queue.length,
|
|
20405
20414
|
timeoutMs: this.drainTimeoutMs
|
|
20406
20415
|
});
|
|
20407
20416
|
} else {
|
|
20408
|
-
|
|
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
|
|
20431
|
+
var log26 = createLogger("plugin");
|
|
20423
20432
|
async function startPlugin(accountConfig, internalOverrides) {
|
|
20424
20433
|
const config2 = buildPluginConfig(accountConfig, internalOverrides);
|
|
20425
|
-
|
|
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
|
-
|
|
20439
|
+
log26.info("Crypto initialized \u2713");
|
|
20431
20440
|
} else {
|
|
20432
20441
|
cryptoEngine = CryptoEngine.createPassthrough();
|
|
20433
|
-
|
|
20434
|
-
}
|
|
20435
|
-
const
|
|
20436
|
-
|
|
20437
|
-
|
|
20438
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
20492
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
20513
|
+
log26.info("plugin registered \u2713");
|
|
20516
20514
|
}
|
|
20517
20515
|
};
|
|
20518
20516
|
var index_default = plugin;
|