wechat-ai 0.1.4 → 0.1.5
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/{chunk-CP3Y2VRX.js → chunk-HSZRSG6L.js} +124 -22
- package/dist/chunk-HSZRSG6L.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +33 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-CP3Y2VRX.js.map +0 -1
|
@@ -596,6 +596,7 @@ var OpenAICompatibleProvider = class {
|
|
|
596
596
|
};
|
|
597
597
|
|
|
598
598
|
// src/gateway.ts
|
|
599
|
+
import { createServer } from "http";
|
|
599
600
|
var log4 = createLogger("\u7F51\u5173");
|
|
600
601
|
var DEBOUNCE_MS = 1500;
|
|
601
602
|
var Gateway = class {
|
|
@@ -608,9 +609,18 @@ var Gateway = class {
|
|
|
608
609
|
processing = /* @__PURE__ */ new Set();
|
|
609
610
|
// Queue for messages that arrive while AI is processing
|
|
610
611
|
queues = /* @__PURE__ */ new Map();
|
|
612
|
+
// Middleware stack
|
|
613
|
+
middlewares = [];
|
|
614
|
+
// Webhook HTTP server
|
|
615
|
+
webhookServer = null;
|
|
611
616
|
constructor(config) {
|
|
612
617
|
this.config = config;
|
|
613
618
|
}
|
|
619
|
+
/** Register a middleware function */
|
|
620
|
+
use(middleware) {
|
|
621
|
+
this.middlewares.push(middleware);
|
|
622
|
+
return this;
|
|
623
|
+
}
|
|
614
624
|
init() {
|
|
615
625
|
for (const [name, chConfig] of Object.entries(this.config.channels)) {
|
|
616
626
|
if (chConfig.enabled === false) continue;
|
|
@@ -647,6 +657,7 @@ var Gateway = class {
|
|
|
647
657
|
if (this.providers.size === 0) {
|
|
648
658
|
throw new Error("\u672A\u914D\u7F6E\u4EFB\u4F55\u6A21\u578B");
|
|
649
659
|
}
|
|
660
|
+
this.startWebhook();
|
|
650
661
|
const startPromises = [...this.channels.entries()].map(([name, channel]) => {
|
|
651
662
|
log4.info(`\u542F\u52A8\u6E20\u9053: ${name}`);
|
|
652
663
|
return channel.start((msg) => this.handleMessage(msg)).catch((err) => {
|
|
@@ -657,6 +668,10 @@ var Gateway = class {
|
|
|
657
668
|
}
|
|
658
669
|
async stop() {
|
|
659
670
|
log4.info("\u6B63\u5728\u5173\u95ED...");
|
|
671
|
+
if (this.webhookServer) {
|
|
672
|
+
this.webhookServer.close();
|
|
673
|
+
this.webhookServer = null;
|
|
674
|
+
}
|
|
660
675
|
const stops = [...this.channels.values()].map((ch) => ch.stop());
|
|
661
676
|
await Promise.allSettled(stops);
|
|
662
677
|
log4.info("\u5DF2\u5173\u95ED");
|
|
@@ -712,29 +727,40 @@ var Gateway = class {
|
|
|
712
727
|
this.processing.add(key);
|
|
713
728
|
try {
|
|
714
729
|
const providerName = this.config.userRoutes?.[msg.senderId] || this.config.defaultProvider;
|
|
715
|
-
const provider = this.providers.get(providerName);
|
|
716
|
-
if (!provider) {
|
|
717
|
-
log4.error(`\u6A21\u578B "${providerName}" \u672A\u627E\u5230`);
|
|
718
|
-
return;
|
|
719
|
-
}
|
|
720
|
-
log4.info(`\u8C03\u7528 ${providerName} \u5904\u7406\u4E2D...`);
|
|
721
730
|
const channel = this.channels.get(msg.channel);
|
|
722
|
-
if (channel && "sendTyping" in channel) {
|
|
723
|
-
channel.sendTyping(msg.senderId, msg.replyToken);
|
|
724
|
-
}
|
|
725
|
-
const options = {};
|
|
726
|
-
if (this.config.systemPrompt) {
|
|
727
|
-
options.systemPrompt = this.config.systemPrompt;
|
|
728
|
-
}
|
|
729
|
-
const sessionKey = `${msg.channel}:${msg.senderId}`;
|
|
730
|
-
const response = await provider.query(msg.text, sessionKey, options);
|
|
731
731
|
if (!channel) return;
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
732
|
+
const ctx = {
|
|
733
|
+
message: msg,
|
|
734
|
+
provider: providerName,
|
|
735
|
+
channel,
|
|
736
|
+
sessionKey: key,
|
|
737
|
+
state: {}
|
|
738
|
+
};
|
|
739
|
+
const coreHandler = async (c) => {
|
|
740
|
+
const provider = this.providers.get(c.provider);
|
|
741
|
+
if (!provider) {
|
|
742
|
+
log4.error(`\u6A21\u578B "${c.provider}" \u672A\u627E\u5230`);
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
log4.info(`\u8C03\u7528 ${c.provider} \u5904\u7406\u4E2D...`);
|
|
746
|
+
if ("sendTyping" in c.channel) {
|
|
747
|
+
c.channel.sendTyping(c.message.senderId, c.message.replyToken);
|
|
748
|
+
}
|
|
749
|
+
const options = {};
|
|
750
|
+
if (this.config.systemPrompt) {
|
|
751
|
+
options.systemPrompt = this.config.systemPrompt;
|
|
752
|
+
}
|
|
753
|
+
c.response = await provider.query(c.message.text, c.sessionKey, options);
|
|
754
|
+
};
|
|
755
|
+
await this.compose(ctx, [...this.middlewares, coreHandler]);
|
|
756
|
+
if (ctx.response) {
|
|
757
|
+
await channel.send({
|
|
758
|
+
targetId: msg.senderId,
|
|
759
|
+
text: ctx.response,
|
|
760
|
+
replyToken: msg.replyToken
|
|
761
|
+
});
|
|
762
|
+
log4.info(`\u5DF2\u56DE\u590D (${ctx.response.length} \u5B57\u7B26)`);
|
|
763
|
+
}
|
|
738
764
|
} catch (err) {
|
|
739
765
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
740
766
|
log4.error(`\u5904\u7406\u6D88\u606F\u5931\u8D25: ${errMsg}`);
|
|
@@ -753,6 +779,82 @@ var Gateway = class {
|
|
|
753
779
|
this.processing.delete(key);
|
|
754
780
|
}
|
|
755
781
|
}
|
|
782
|
+
async compose(ctx, stack) {
|
|
783
|
+
let index = -1;
|
|
784
|
+
const dispatch = async (i) => {
|
|
785
|
+
if (i <= index) throw new Error("next() called multiple times");
|
|
786
|
+
index = i;
|
|
787
|
+
const fn = stack[i];
|
|
788
|
+
if (!fn) return;
|
|
789
|
+
await fn(ctx, () => dispatch(i + 1));
|
|
790
|
+
};
|
|
791
|
+
await dispatch(0);
|
|
792
|
+
}
|
|
793
|
+
startWebhook() {
|
|
794
|
+
const webhookConfig = this.config.webhook;
|
|
795
|
+
if (!webhookConfig?.enabled) return;
|
|
796
|
+
const port = webhookConfig.port || 4800;
|
|
797
|
+
const secret = webhookConfig.secret;
|
|
798
|
+
this.webhookServer = createServer(async (req, res) => {
|
|
799
|
+
if (req.method !== "POST") {
|
|
800
|
+
res.writeHead(405, { "Content-Type": "application/json" });
|
|
801
|
+
res.end(JSON.stringify({ error: "Method not allowed" }));
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
if (secret && req.headers["authorization"] !== `Bearer ${secret}`) {
|
|
805
|
+
res.writeHead(401, { "Content-Type": "application/json" });
|
|
806
|
+
res.end(JSON.stringify({ error: "Unauthorized" }));
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
let body;
|
|
810
|
+
try {
|
|
811
|
+
body = await new Promise((resolve, reject) => {
|
|
812
|
+
const chunks = [];
|
|
813
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
814
|
+
req.on("end", () => resolve(Buffer.concat(chunks).toString()));
|
|
815
|
+
req.on("error", reject);
|
|
816
|
+
});
|
|
817
|
+
} catch {
|
|
818
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
819
|
+
res.end(JSON.stringify({ error: "Failed to read body" }));
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
let payload;
|
|
823
|
+
try {
|
|
824
|
+
payload = JSON.parse(body);
|
|
825
|
+
} catch {
|
|
826
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
827
|
+
res.end(JSON.stringify({ error: "Invalid JSON" }));
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
const { channel: channelName, targetId, text } = payload;
|
|
831
|
+
if (!channelName || !targetId || !text) {
|
|
832
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
833
|
+
res.end(JSON.stringify({ error: "Missing required fields: channel, targetId, text" }));
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
const channel = this.channels.get(channelName);
|
|
837
|
+
if (!channel) {
|
|
838
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
839
|
+
res.end(JSON.stringify({ error: `Channel "${channelName}" not found` }));
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
try {
|
|
843
|
+
await channel.send({ targetId, text });
|
|
844
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
845
|
+
res.end(JSON.stringify({ ok: true }));
|
|
846
|
+
log4.info(`Webhook: \u5DF2\u53D1\u9001\u6D88\u606F\u5230 ${channelName}:${targetId}`);
|
|
847
|
+
} catch (err) {
|
|
848
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
849
|
+
log4.error(`Webhook \u53D1\u9001\u5931\u8D25: ${errMsg}`);
|
|
850
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
851
|
+
res.end(JSON.stringify({ error: "Failed to send message" }));
|
|
852
|
+
}
|
|
853
|
+
});
|
|
854
|
+
this.webhookServer.listen(port, () => {
|
|
855
|
+
log4.info(`Webhook \u670D\u52A1\u5DF2\u542F\u52A8: http://localhost:${port}`);
|
|
856
|
+
});
|
|
857
|
+
}
|
|
756
858
|
async handleCommand(msg) {
|
|
757
859
|
const channel = this.channels.get(msg.channel);
|
|
758
860
|
if (!channel) return;
|
|
@@ -832,4 +934,4 @@ export {
|
|
|
832
934
|
OpenAICompatibleProvider,
|
|
833
935
|
Gateway
|
|
834
936
|
};
|
|
835
|
-
//# sourceMappingURL=chunk-
|
|
937
|
+
//# sourceMappingURL=chunk-HSZRSG6L.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config.ts","../src/logger.ts","../src/channels/weixin.ts","../src/providers/claude-agent.ts","../src/providers/openai-compatible.ts","../src/gateway.ts"],"sourcesContent":["import { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { WaiConfig } from \"./types.js\";\n\nconst WAI_DIR = join(homedir(), \".wai\");\nconst CONFIG_PATH = join(WAI_DIR, \"config.json\");\n\nconst DEFAULT_CONFIG: WaiConfig = {\n defaultProvider: \"qwen\",\n providers: {\n claude: {\n type: \"claude-agent\",\n allowedTools: [\"Read\", \"Glob\", \"Grep\", \"Bash\", \"WebSearch\", \"WebFetch\"],\n },\n qwen: {\n type: \"openai-compatible\",\n baseUrl: \"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n model: \"qwen-plus\",\n apiKeyEnv: \"DASHSCOPE_API_KEY\",\n },\n deepseek: {\n type: \"openai-compatible\",\n baseUrl: \"https://api.deepseek.com/v1\",\n model: \"deepseek-chat\",\n apiKeyEnv: \"DEEPSEEK_API_KEY\",\n },\n gpt: {\n type: \"openai-compatible\",\n baseUrl: \"https://api.openai.com/v1\",\n model: \"gpt-4o\",\n apiKeyEnv: \"OPENAI_API_KEY\",\n },\n gemini: {\n type: \"openai-compatible\",\n baseUrl: \"https://generativelanguage.googleapis.com/v1beta/openai\",\n model: \"gemini-2.0-flash\",\n apiKeyEnv: \"GEMINI_API_KEY\",\n },\n minimax: {\n type: \"openai-compatible\",\n baseUrl: \"https://api.minimax.chat/v1\",\n model: \"MiniMax-Text-01\",\n apiKeyEnv: \"MINIMAX_API_KEY\",\n },\n glm: {\n type: \"openai-compatible\",\n baseUrl: \"https://open.bigmodel.cn/api/paas/v4\",\n model: \"glm-4-plus\",\n apiKeyEnv: \"GLM_API_KEY\",\n },\n },\n channels: {\n weixin: {\n type: \"weixin\",\n enabled: true,\n },\n },\n systemPrompt: \"You are a helpful AI assistant. Respond concisely.\",\n chunkSize: 4000,\n};\n\nexport async function ensureDir(dir: string) {\n if (!existsSync(dir)) {\n await mkdir(dir, { recursive: true });\n }\n}\n\nexport async function loadConfig(): Promise<WaiConfig> {\n await ensureDir(WAI_DIR);\n\n if (!existsSync(CONFIG_PATH)) {\n await writeFile(CONFIG_PATH, JSON.stringify(DEFAULT_CONFIG, null, 2));\n return { ...DEFAULT_CONFIG };\n }\n\n const raw = await readFile(CONFIG_PATH, \"utf-8\");\n const user = JSON.parse(raw) as Partial<WaiConfig>;\n\n // Deep merge: default providers + user providers (user overrides per provider)\n const providers = { ...DEFAULT_CONFIG.providers };\n if (user.providers) {\n for (const [key, val] of Object.entries(user.providers)) {\n providers[key] = val;\n }\n }\n\n const config = { ...DEFAULT_CONFIG, ...user, providers } as WaiConfig;\n\n // Migrate: zhipu → glm\n if (config.providers.zhipu) {\n if (!config.providers.glm) {\n config.providers.glm = { ...config.providers.zhipu, apiKeyEnv: \"GLM_API_KEY\" };\n }\n delete config.providers.zhipu;\n if (config.defaultProvider === \"zhipu\") config.defaultProvider = \"glm\";\n await saveConfig(config);\n }\n\n return config;\n}\n\nexport async function saveConfig(config: WaiConfig): Promise<void> {\n await ensureDir(WAI_DIR);\n await writeFile(CONFIG_PATH, JSON.stringify(config, null, 2));\n}\n\nexport function getDataDir(): string {\n return WAI_DIR;\n}\n\nexport function getAccountsDir(): string {\n return join(WAI_DIR, \"accounts\");\n}\n","const LEVELS = { debug: 0, info: 1, warn: 2, error: 3 } as const;\ntype Level = keyof typeof LEVELS;\n\nlet currentLevel: Level = \"info\";\n\nexport function setLogLevel(level: Level) {\n currentLevel = level;\n}\n\nfunction fmt(level: Level, scope: string, msg: string): string {\n const ts = new Date().toISOString().slice(11, 23);\n const tag = level.toUpperCase().padEnd(5);\n return `\\x1b[90m${ts}\\x1b[0m ${colorize(level, tag)} \\x1b[36m[${scope}]\\x1b[0m ${msg}`;\n}\n\nfunction colorize(level: Level, text: string): string {\n switch (level) {\n case \"debug\": return `\\x1b[90m${text}\\x1b[0m`;\n case \"info\": return `\\x1b[32m${text}\\x1b[0m`;\n case \"warn\": return `\\x1b[33m${text}\\x1b[0m`;\n case \"error\": return `\\x1b[31m${text}\\x1b[0m`;\n }\n}\n\nexport function createLogger(scope: string) {\n return {\n debug: (msg: string) => { if (LEVELS[currentLevel] <= 0) console.log(fmt(\"debug\", scope, msg)); },\n info: (msg: string) => { if (LEVELS[currentLevel] <= 1) console.log(fmt(\"info\", scope, msg)); },\n warn: (msg: string) => { if (LEVELS[currentLevel] <= 2) console.warn(fmt(\"warn\", scope, msg)); },\n error: (msg: string) => { if (LEVELS[currentLevel] <= 3) console.error(fmt(\"error\", scope, msg)); },\n };\n}\n","import { createLogger } from \"../logger.js\";\nimport { getAccountsDir, ensureDir } from \"../config.js\";\nimport { join } from \"node:path\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { randomBytes, randomUUID } from \"node:crypto\";\nimport type { Channel, InboundMessage, OutboundMessage, ChannelConfig } from \"../types.js\";\n\nconst log = createLogger(\"weixin\");\n\nconst DEFAULT_BASE_URL = \"https://ilinkai.weixin.qq.com\";\nconst CHANNEL_VERSION = \"1.0.0\";\nconst API_TIMEOUT_MS = 15_000;\n\n// ── Message constants (from openclaw-weixin protocol) ──\nconst MessageType = { USER: 1, BOT: 2 } as const;\nconst MessageState = { NEW: 0, GENERATING: 1, FINISH: 2 } as const;\nconst MessageItemType = { TEXT: 1, IMAGE: 2, VOICE: 3, FILE: 4, VIDEO: 5 } as const;\n\n// ── Types ──\n\ninterface WeixinAccount {\n accountId: string;\n token: string;\n baseUrl: string;\n userId?: string;\n}\n\ninterface WeixinMessageItem {\n type: number;\n text_item?: { text: string };\n image_item?: unknown;\n voice_item?: unknown;\n file_item?: unknown;\n video_item?: unknown;\n}\n\ninterface WeixinMessage {\n seq?: number;\n message_id?: number;\n from_user_id?: string;\n to_user_id?: string;\n context_token?: string;\n item_list?: WeixinMessageItem[];\n create_time_ms?: number;\n}\n\ninterface GetUpdatesResponse {\n ret?: number;\n errmsg?: string;\n msgs?: WeixinMessage[];\n get_updates_buf?: string;\n}\n\n// ── Weixin Channel ──\n\nexport class WeixinChannel implements Channel {\n readonly name = \"weixin\";\n\n private account: WeixinAccount | null = null;\n private syncBuf = \"\";\n private running = false;\n private abortController: AbortController | null = null;\n private config: ChannelConfig;\n // Cache typing_ticket per user\n private typingTickets = new Map<string, string>();\n\n constructor(config: ChannelConfig) {\n this.config = config;\n }\n\n // ── Auth ──\n\n async login(): Promise<void> {\n const baseUrl = (this.config.baseUrl as string) || DEFAULT_BASE_URL;\n log.info(\"获取二维码中...\");\n\n const qrRes = await this.api(baseUrl, \"ilink/bot/get_bot_qrcode?bot_type=3\", null, {\n method: \"GET\",\n timeout: 10_000,\n });\n\n if (qrRes.ret !== 0) {\n throw new Error(`获取二维码失败: ${qrRes.errmsg || qrRes.ret}`);\n }\n\n const qrUrl: string = qrRes.qrcode_img_content || qrRes.data?.qrcode_img_content;\n const qrCode: string = qrRes.qrcode || qrRes.data?.qrcode;\n\n if (!qrUrl || !qrCode) {\n throw new Error(`二维码响应缺少字段: ${JSON.stringify(qrRes)}`);\n }\n\n log.info(\"请用微信扫描二维码:\");\n console.log();\n try {\n const qrTerminal = await import(\"qrcode-terminal\");\n (qrTerminal.default || qrTerminal).generate(qrUrl, { small: true });\n } catch {\n console.log(` ${qrUrl}`);\n }\n console.log();\n\n log.info(\"等待扫码...\");\n\n let attempts = 0;\n while (attempts < 60) {\n const statusRes = await this.api(\n baseUrl,\n `ilink/bot/get_qrcode_status?qrcode=${encodeURIComponent(qrCode)}`,\n null,\n { method: \"GET\", timeout: 40_000 },\n );\n\n const status = statusRes.data?.status || statusRes.status;\n\n if (status === \"confirmed\") {\n const data = statusRes.data || statusRes;\n const accountId: string = data.ilink_bot_id || data.bot_id;\n const token: string = data.bot_token || data.token;\n\n if (!accountId || !token) {\n throw new Error(\"登录成功但缺少凭证\");\n }\n\n this.account = {\n accountId,\n token,\n baseUrl: data.baseurl || baseUrl,\n userId: data.ilink_user_id,\n };\n\n await this.saveAccount();\n log.info(`登录成功!账号: ${accountId.slice(0, 8)}...`);\n return;\n }\n\n if (status === \"scaned\") {\n log.info(\"已扫码,等待确认...\");\n }\n\n if (status === \"expired\") {\n log.warn(\"二维码已过期\");\n throw new Error(\"二维码已过期\");\n }\n\n attempts++;\n await sleep(500);\n }\n\n throw new Error(\"登录超时\");\n }\n\n // ── Message loop ──\n\n async start(onMessage: (msg: InboundMessage) => void): Promise<void> {\n if (!this.account) {\n await this.loadAccount();\n }\n if (!this.account) {\n log.info(\"未找到账号,开始登录...\");\n await this.login();\n }\n\n await this.loadSyncBuf();\n this.running = true;\n log.info(`消息监听已启动 (${this.account!.accountId.slice(0, 8)}...)`);\n\n while (this.running) {\n try {\n this.abortController = new AbortController();\n const res = await this.getUpdates();\n\n if (res.ret === -14) {\n log.warn(\"会话过期,重新登录...\");\n this.account = null;\n await this.login();\n continue;\n }\n\n if (res.ret && res.ret !== 0) {\n log.warn(`拉取消息失败: ${res.errmsg || JSON.stringify(res)}`);\n await sleep(5000);\n continue;\n }\n\n if (res.get_updates_buf) {\n this.syncBuf = res.get_updates_buf;\n await this.saveSyncBuf();\n }\n\n if (res.msgs && res.msgs.length > 0) {\n for (const msg of res.msgs) {\n const text = this.extractText(msg);\n if (!text || !msg.from_user_id) continue;\n\n log.info(`收到消息 [${msg.from_user_id.slice(0, 8)}...]: ${text.slice(0, 50)}`);\n onMessage({\n id: String(msg.message_id || msg.seq || Date.now()),\n channel: \"weixin\",\n senderId: msg.from_user_id,\n text,\n replyToken: msg.context_token,\n timestamp: msg.create_time_ms || Date.now(),\n });\n }\n }\n } catch (err) {\n if (!this.running) break;\n const message = err instanceof Error ? err.message : String(err);\n if (message.includes(\"aborted\") || message.includes(\"AbortError\")) continue;\n log.error(`轮询出错: ${message}`);\n await sleep(3000);\n }\n }\n }\n\n // ── Send typing indicator ──\n\n async sendTyping(userId: string, contextToken?: string): Promise<void> {\n if (!this.account) return;\n\n try {\n // Get typing_ticket if not cached\n let ticket = this.typingTickets.get(userId);\n if (!ticket) {\n const configRes = await this.api(this.account.baseUrl, \"ilink/bot/getconfig\", {\n ilink_user_id: userId,\n context_token: contextToken,\n base_info: { channel_version: CHANNEL_VERSION },\n }, { timeout: 10_000 });\n\n ticket = configRes.typing_ticket;\n if (ticket) {\n this.typingTickets.set(userId, ticket);\n }\n }\n\n if (!ticket) return;\n\n await this.api(this.account.baseUrl, \"ilink/bot/sendtyping\", {\n ilink_user_id: userId,\n typing_ticket: ticket,\n status: 1,\n base_info: { channel_version: CHANNEL_VERSION },\n }, { timeout: 10_000 });\n\n log.debug(`已发送输入状态给 ${userId.slice(0, 8)}...`);\n } catch {\n // typing 失败不影响主流程\n }\n }\n\n // ── Send message ──\n\n async send(msg: OutboundMessage): Promise<void> {\n if (!this.account) throw new Error(\"未登录\");\n\n const chunks = this.chunkText(msg.text, 4000);\n\n for (const chunk of chunks) {\n const body = {\n msg: {\n from_user_id: \"\",\n to_user_id: msg.targetId,\n client_id: generateClientId(),\n message_type: MessageType.BOT,\n message_state: MessageState.FINISH,\n context_token: msg.replyToken || undefined,\n item_list: [{ type: MessageItemType.TEXT, text_item: { text: chunk } }],\n },\n base_info: { channel_version: CHANNEL_VERSION },\n };\n\n const res = await this.api(\n this.account.baseUrl,\n \"ilink/bot/sendmessage\",\n body,\n { timeout: API_TIMEOUT_MS },\n );\n\n // sendMessage 成功返回 {} (空对象),有错误时返回 ret + errmsg\n if (res.ret && res.ret !== 0) {\n log.error(`发送失败: ${res.errmsg || JSON.stringify(res)}`);\n }\n }\n }\n\n async stop(): Promise<void> {\n this.running = false;\n this.abortController?.abort();\n log.info(\"已停止\");\n }\n\n // ── Internal ──\n\n private async getUpdates(): Promise<GetUpdatesResponse> {\n if (!this.account) throw new Error(\"未登录\");\n\n return this.api(this.account.baseUrl, \"ilink/bot/getupdates\", {\n get_updates_buf: this.syncBuf,\n base_info: { channel_version: CHANNEL_VERSION },\n }, { timeout: 50_000 });\n }\n\n private extractText(msg: WeixinMessage): string | null {\n if (!msg.item_list?.length) return null;\n for (const item of msg.item_list) {\n if (item.type === 1 && item.text_item?.text) {\n return item.text_item.text;\n }\n }\n return null;\n }\n\n private chunkText(text: string, maxLen: number): string[] {\n if (text.length <= maxLen) return [text];\n const chunks: string[] = [];\n let remaining = text;\n while (remaining.length > 0) {\n let breakAt = remaining.lastIndexOf(\"\\n\", maxLen);\n if (breakAt <= 0) breakAt = maxLen;\n chunks.push(remaining.slice(0, breakAt));\n remaining = remaining.slice(breakAt);\n }\n return chunks;\n }\n\n private async api(\n baseUrl: string,\n path: string,\n body: unknown,\n opts: { method?: string; timeout?: number } = {},\n ): Promise<any> {\n const url = `${baseUrl.replace(/\\/$/, \"\")}/${path}`;\n const method = opts.method || \"POST\";\n const bodyStr = body ? JSON.stringify(body) : undefined;\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n\n if (this.account?.token) {\n headers[\"AuthorizationType\"] = \"ilink_bot_token\";\n headers[\"Authorization\"] = `Bearer ${this.account.token}`;\n headers[\"X-WECHAT-UIN\"] = randomUin();\n if (bodyStr) {\n headers[\"Content-Length\"] = String(Buffer.byteLength(bodyStr, \"utf-8\"));\n }\n }\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), opts.timeout || API_TIMEOUT_MS);\n\n try {\n const res = await fetch(url, {\n method,\n headers,\n body: bodyStr,\n signal: controller.signal,\n });\n return await res.json();\n } finally {\n clearTimeout(timer);\n }\n }\n\n // ── Persistence ──\n\n private accountFile(): string {\n return join(getAccountsDir(), \"weixin.json\");\n }\n\n private syncFile(): string {\n return join(getAccountsDir(), \"weixin-sync.json\");\n }\n\n private async saveAccount(): Promise<void> {\n await ensureDir(getAccountsDir());\n await writeFile(this.accountFile(), JSON.stringify(this.account, null, 2));\n }\n\n private async loadAccount(): Promise<void> {\n const path = this.accountFile();\n if (!existsSync(path)) return;\n try {\n const raw = await readFile(path, \"utf-8\");\n this.account = JSON.parse(raw);\n log.info(`已加载账号: ${this.account!.accountId.slice(0, 8)}...`);\n } catch {\n log.warn(\"加载账号失败\");\n }\n }\n\n private async saveSyncBuf(): Promise<void> {\n await ensureDir(getAccountsDir());\n await writeFile(this.syncFile(), JSON.stringify({ get_updates_buf: this.syncBuf }));\n }\n\n private async loadSyncBuf(): Promise<void> {\n const path = this.syncFile();\n if (!existsSync(path)) return;\n try {\n const raw = await readFile(path, \"utf-8\");\n const data = JSON.parse(raw);\n this.syncBuf = data.get_updates_buf || \"\";\n } catch {\n // fresh start\n }\n }\n}\n\n// ── Helpers ──\n\nfunction randomUin(): string {\n const uint32 = randomBytes(4).readUInt32BE(0);\n return Buffer.from(String(uint32), \"utf-8\").toString(\"base64\");\n}\n\nfunction generateClientId(): string {\n return `wai-${randomUUID().replace(/-/g, \"\").slice(0, 16)}`;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n","import { createLogger } from \"../logger.js\";\nimport type { Provider, ProviderOptions, ProviderConfig } from \"../types.js\";\n\nconst log = createLogger(\"claude\");\n\nconst DEFAULT_TOOLS = [\"Read\", \"Glob\", \"Grep\", \"Bash\", \"WebSearch\", \"WebFetch\"];\n\nexport class ClaudeAgentProvider implements Provider {\n readonly name = \"claude-agent\";\n private config: ProviderConfig;\n private sessions = new Map<string, string>(); // userId -> sessionId\n\n constructor(config: ProviderConfig) {\n this.config = config;\n }\n\n async query(\n prompt: string,\n sessionId: string,\n options?: ProviderOptions,\n ): Promise<string> {\n const { query } = await import(\"@anthropic-ai/claude-agent-sdk\");\n\n const allowedTools = options?.allowedTools\n || (this.config.allowedTools as string[])\n || DEFAULT_TOOLS;\n\n const existingSession = this.sessions.get(sessionId);\n const sdkOptions: Record<string, unknown> = {\n allowedTools,\n permissionMode: \"acceptEdits\" as const,\n };\n\n if (options?.maxTokens) {\n sdkOptions.maxTokens = options.maxTokens;\n }\n\n if (options?.cwd) {\n sdkOptions.cwd = options.cwd;\n }\n\n // Resume existing session for conversation continuity\n if (existingSession) {\n sdkOptions.resume = existingSession;\n }\n\n if (options?.systemPrompt) {\n sdkOptions.systemPrompt = options.systemPrompt;\n }\n\n log.info(`Querying Claude (session: ${sessionId.slice(0, 8)}...)`);\n\n let result = \"\";\n let newSessionId: string | undefined;\n\n try {\n for await (const message of query({\n prompt,\n options: sdkOptions as any,\n })) {\n // Capture session ID from init message\n if (isInitMessage(message)) {\n newSessionId = message.session_id;\n }\n\n // Capture result text\n if (isResultMessage(message)) {\n result = message.result;\n }\n\n // Capture assistant text messages for streaming\n if (isAssistantMessage(message)) {\n // accumulate text from assistant messages\n const textContent = extractText(message);\n if (textContent) {\n result = textContent;\n }\n }\n }\n } catch (err) {\n const errMsg = err instanceof Error ? err.message : String(err);\n log.error(`Claude query failed: ${errMsg}`);\n throw err;\n }\n\n // Store session for continuity\n if (newSessionId) {\n this.sessions.set(sessionId, newSessionId);\n }\n\n if (!result) {\n result = \"(No response from Claude)\";\n }\n\n log.info(`Response: ${result.length} chars`);\n return result;\n }\n}\n\n// ── Message type guards ──\n\nfunction isInitMessage(msg: any): msg is { type: \"system\"; subtype: \"init\"; session_id: string } {\n return msg?.type === \"system\" && msg?.subtype === \"init\" && typeof msg?.session_id === \"string\";\n}\n\nfunction isResultMessage(msg: any): msg is { result: string } {\n return typeof msg?.result === \"string\";\n}\n\nfunction isAssistantMessage(msg: any): msg is { type: \"assistant\"; message: { content: unknown[] } } {\n return msg?.type === \"assistant\" && msg?.message?.content;\n}\n\nfunction extractText(msg: any): string | null {\n if (!msg?.message?.content) return null;\n const parts: string[] = [];\n for (const block of msg.message.content) {\n if (block.type === \"text\" && typeof block.text === \"string\") {\n parts.push(block.text);\n }\n }\n return parts.length > 0 ? parts.join(\"\") : null;\n}\n","import { createLogger } from \"../logger.js\";\nimport type { Provider, ProviderOptions, ProviderConfig } from \"../types.js\";\n\nconst log = createLogger(\"openai-compat\");\n\ninterface ChatMessage {\n role: \"system\" | \"user\" | \"assistant\";\n content: string;\n}\n\ninterface ChatCompletionResponse {\n choices: Array<{\n message: { content: string };\n finish_reason: string;\n }>;\n usage?: { prompt_tokens: number; completion_tokens: number };\n}\n\nexport class OpenAICompatibleProvider implements Provider {\n readonly name: string;\n private config: ProviderConfig;\n private histories = new Map<string, ChatMessage[]>();\n\n constructor(name: string, config: ProviderConfig) {\n this.name = name;\n this.config = config;\n }\n\n async query(\n prompt: string,\n sessionId: string,\n options?: ProviderOptions,\n ): Promise<string> {\n const baseUrl = this.config.baseUrl;\n const apiKey = this.config.apiKey || process.env[this.config.apiKeyEnv as string || \"\"];\n const model = options?.model || (this.config.model as string);\n\n if (!baseUrl) throw new Error(`${this.name}: baseUrl is required`);\n if (!apiKey) throw new Error(`${this.name}: apiKey is required`);\n if (!model) throw new Error(`${this.name}: model is required`);\n\n // Build conversation history\n let history = this.histories.get(sessionId);\n if (!history) {\n history = [];\n this.histories.set(sessionId, history);\n }\n\n const messages: ChatMessage[] = [];\n\n // System prompt\n const systemPrompt = options?.systemPrompt || (this.config.systemPrompt as string);\n if (systemPrompt) {\n messages.push({ role: \"system\", content: systemPrompt });\n }\n\n // Conversation history (keep last N turns to stay within context)\n const maxHistory = (this.config.maxHistory as number) || 20;\n const recentHistory = history.slice(-maxHistory);\n messages.push(...recentHistory);\n\n // Current user message\n messages.push({ role: \"user\", content: prompt });\n\n log.info(`Querying ${this.name} (model: ${model}, session: ${sessionId.slice(0, 8)}...)`);\n\n const url = `${baseUrl.replace(/\\/$/, \"\")}/chat/completions`;\n\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n model,\n messages,\n max_tokens: options?.maxTokens || (this.config.maxTokens as number) || 4096,\n temperature: (this.config.temperature as number) ?? 0.7,\n }),\n });\n\n if (!res.ok) {\n const errBody = await res.text();\n log.error(`${this.name} API error ${res.status}: ${errBody.slice(0, 200)}`);\n throw new Error(`${this.name} API error: ${res.status}`);\n }\n\n const data = (await res.json()) as ChatCompletionResponse;\n const reply = data.choices[0]?.message.content || \"(No response)\";\n\n if (data.usage) {\n log.info(`Tokens: ${data.usage.prompt_tokens} in / ${data.usage.completion_tokens} out`);\n }\n\n // Update history\n history.push({ role: \"user\", content: prompt });\n history.push({ role: \"assistant\", content: reply });\n\n log.info(`Response: ${reply.length} chars`);\n return reply;\n }\n}\n","import { createServer, type Server, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport { createLogger } from \"./logger.js\";\nimport type {\n Channel,\n Provider,\n InboundMessage,\n WaiConfig,\n ProviderOptions,\n Middleware,\n Context,\n} from \"./types.js\";\nimport { WeixinChannel } from \"./channels/weixin.js\";\nimport { ClaudeAgentProvider } from \"./providers/claude-agent.js\";\nimport { OpenAICompatibleProvider } from \"./providers/openai-compatible.js\";\n\nconst log = createLogger(\"网关\");\n\nconst DEBOUNCE_MS = 1500;\n\ninterface MessageBuffer {\n messages: InboundMessage[];\n timer: ReturnType<typeof setTimeout>;\n}\n\nexport class Gateway {\n private channels = new Map<string, Channel>();\n private providers = new Map<string, Provider>();\n private config: WaiConfig;\n // Debounce buffer: accumulates messages within DEBOUNCE_MS window\n private buffers = new Map<string, MessageBuffer>();\n // Whether AI is currently processing for a given user\n private processing = new Set<string>();\n // Queue for messages that arrive while AI is processing\n private queues = new Map<string, InboundMessage[]>();\n // Middleware stack\n private middlewares: Middleware[] = [];\n // Webhook HTTP server\n private webhookServer: Server | null = null;\n\n constructor(config: WaiConfig) {\n this.config = config;\n }\n\n /** Register a middleware function */\n use(middleware: Middleware): this {\n this.middlewares.push(middleware);\n return this;\n }\n\n init(): void {\n for (const [name, chConfig] of Object.entries(this.config.channels)) {\n if (chConfig.enabled === false) continue;\n switch (chConfig.type) {\n case \"weixin\":\n this.channels.set(name, new WeixinChannel(chConfig));\n break;\n default:\n log.warn(`未知渠道类型: ${chConfig.type}`);\n }\n }\n\n for (const [name, provConfig] of Object.entries(this.config.providers)) {\n switch (provConfig.type) {\n case \"claude-agent\":\n this.providers.set(name, new ClaudeAgentProvider(provConfig));\n break;\n case \"openai-compatible\":\n this.providers.set(name, new OpenAICompatibleProvider(name, provConfig));\n break;\n default:\n log.warn(`未知模型类型: ${provConfig.type}`);\n }\n }\n\n log.info(`已初始化 ${this.channels.size} 个渠道, ${this.providers.size} 个模型`);\n }\n\n async login(channelName: string): Promise<void> {\n const channel = this.channels.get(channelName);\n if (!channel) {\n throw new Error(`渠道 \"${channelName}\" 不存在`);\n }\n await channel.login();\n }\n\n async start(): Promise<void> {\n if (this.providers.size === 0) {\n throw new Error(\"未配置任何模型\");\n }\n\n this.startWebhook();\n\n const startPromises = [...this.channels.entries()].map(([name, channel]) => {\n log.info(`启动渠道: ${name}`);\n return channel.start((msg) => this.handleMessage(msg)).catch((err) => {\n log.error(`渠道 ${name} 异常: ${err instanceof Error ? err.message : err}`);\n });\n });\n\n await Promise.all(startPromises);\n }\n\n async stop(): Promise<void> {\n log.info(\"正在关闭...\");\n if (this.webhookServer) {\n this.webhookServer.close();\n this.webhookServer = null;\n }\n const stops = [...this.channels.values()].map((ch) => ch.stop());\n await Promise.allSettled(stops);\n log.info(\"已关闭\");\n }\n\n private handleMessage(msg: InboundMessage): void {\n // Commands bypass debounce, execute immediately\n if (msg.text.startsWith(\"/\")) {\n this.handleCommand(msg);\n return;\n }\n\n const key = `${msg.channel}:${msg.senderId}`;\n\n // If AI is processing, queue the message\n if (this.processing.has(key)) {\n const queue = this.queues.get(key) || [];\n queue.push(msg);\n this.queues.set(key, queue);\n log.info(`消息已排队 (AI处理中), 队列长度: ${queue.length}`);\n return;\n }\n\n // Debounce: accumulate messages within time window\n const existing = this.buffers.get(key);\n if (existing) {\n clearTimeout(existing.timer);\n existing.messages.push(msg);\n existing.timer = setTimeout(() => this.flushBuffer(key), DEBOUNCE_MS);\n } else {\n this.buffers.set(key, {\n messages: [msg],\n timer: setTimeout(() => this.flushBuffer(key), DEBOUNCE_MS),\n });\n }\n }\n\n private async flushBuffer(key: string): Promise<void> {\n const buf = this.buffers.get(key);\n if (!buf || buf.messages.length === 0) return;\n this.buffers.delete(key);\n\n // Merge all buffered messages into one\n const merged = this.mergeMessages(buf.messages);\n await this.processMessage(merged);\n\n // After processing, check if there are queued messages\n const queue = this.queues.get(key);\n if (queue && queue.length > 0) {\n this.queues.delete(key);\n // Feed queued messages back through debounce\n for (const msg of queue) {\n this.handleMessage(msg);\n }\n }\n }\n\n private mergeMessages(messages: InboundMessage[]): InboundMessage {\n if (messages.length === 1) return messages[0]!;\n\n const last = messages[messages.length - 1]!;\n const mergedText = messages.map((m) => m.text).join(\"\\n\");\n log.info(`合并 ${messages.length} 条消息`);\n\n return { ...last, text: mergedText };\n }\n\n private async processMessage(msg: InboundMessage): Promise<void> {\n const key = `${msg.channel}:${msg.senderId}`;\n this.processing.add(key);\n\n try {\n const providerName = this.config.userRoutes?.[msg.senderId]\n || this.config.defaultProvider;\n const channel = this.channels.get(msg.channel);\n if (!channel) return;\n\n const ctx: Context = {\n message: msg,\n provider: providerName,\n channel,\n sessionKey: key,\n state: {},\n };\n\n // Build the middleware chain with AI call as the innermost handler\n const coreHandler: Middleware = async (c) => {\n const provider = this.providers.get(c.provider);\n if (!provider) {\n log.error(`模型 \"${c.provider}\" 未找到`);\n return;\n }\n\n log.info(`调用 ${c.provider} 处理中...`);\n\n if (\"sendTyping\" in c.channel) {\n (c.channel as any).sendTyping(c.message.senderId, c.message.replyToken);\n }\n\n const options: ProviderOptions = {};\n if (this.config.systemPrompt) {\n options.systemPrompt = this.config.systemPrompt;\n }\n\n c.response = await provider.query(c.message.text, c.sessionKey, options);\n };\n\n // Compose: middlewares + core handler (Koa-style onion model)\n await this.compose(ctx, [...this.middlewares, coreHandler]);\n\n // Send response if available\n if (ctx.response) {\n await channel.send({\n targetId: msg.senderId,\n text: ctx.response,\n replyToken: msg.replyToken,\n });\n log.info(`已回复 (${ctx.response.length} 字符)`);\n }\n } catch (err) {\n const errMsg = err instanceof Error ? err.message : String(err);\n log.error(`处理消息失败: ${errMsg}`);\n\n try {\n const channel = this.channels.get(msg.channel);\n if (channel) {\n await channel.send({\n targetId: msg.senderId,\n text: `[出错了] 处理消息失败,请重试。`,\n replyToken: msg.replyToken,\n });\n }\n } catch {\n // swallow\n }\n } finally {\n this.processing.delete(key);\n }\n }\n\n private async compose(ctx: Context, stack: Middleware[]): Promise<void> {\n let index = -1;\n const dispatch = async (i: number): Promise<void> => {\n if (i <= index) throw new Error(\"next() called multiple times\");\n index = i;\n const fn = stack[i];\n if (!fn) return;\n await fn(ctx, () => dispatch(i + 1));\n };\n await dispatch(0);\n }\n\n private startWebhook(): void {\n const webhookConfig = this.config.webhook;\n if (!webhookConfig?.enabled) return;\n\n const port = webhookConfig.port || 4800;\n const secret = webhookConfig.secret;\n\n this.webhookServer = createServer(async (req: IncomingMessage, res: ServerResponse) => {\n // Only accept POST\n if (req.method !== \"POST\") {\n res.writeHead(405, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Method not allowed\" }));\n return;\n }\n\n // Auth check\n if (secret && req.headers[\"authorization\"] !== `Bearer ${secret}`) {\n res.writeHead(401, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Unauthorized\" }));\n return;\n }\n\n // Parse body\n let body: string;\n try {\n body = await new Promise<string>((resolve, reject) => {\n const chunks: Buffer[] = [];\n req.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n req.on(\"end\", () => resolve(Buffer.concat(chunks).toString()));\n req.on(\"error\", reject);\n });\n } catch {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Failed to read body\" }));\n return;\n }\n\n let payload: { channel?: string; targetId?: string; text?: string };\n try {\n payload = JSON.parse(body);\n } catch {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Invalid JSON\" }));\n return;\n }\n\n const { channel: channelName, targetId, text } = payload;\n if (!channelName || !targetId || !text) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Missing required fields: channel, targetId, text\" }));\n return;\n }\n\n const channel = this.channels.get(channelName);\n if (!channel) {\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: `Channel \"${channelName}\" not found` }));\n return;\n }\n\n try {\n await channel.send({ targetId, text });\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ ok: true }));\n log.info(`Webhook: 已发送消息到 ${channelName}:${targetId}`);\n } catch (err) {\n const errMsg = err instanceof Error ? err.message : String(err);\n log.error(`Webhook 发送失败: ${errMsg}`);\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Failed to send message\" }));\n }\n });\n\n this.webhookServer.listen(port, () => {\n log.info(`Webhook 服务已启动: http://localhost:${port}`);\n });\n }\n\n private async handleCommand(msg: InboundMessage): Promise<void> {\n const channel = this.channels.get(msg.channel);\n if (!channel) return;\n\n const parts = msg.text.trim().split(/\\s+/);\n const cmd = parts[0]!.toLowerCase();\n const arg = parts[1];\n\n switch (cmd) {\n case \"/model\": {\n if (!arg) {\n const current = this.config.userRoutes?.[msg.senderId] || this.config.defaultProvider;\n const available = [...this.providers.keys()].join(\", \");\n await channel.send({\n targetId: msg.senderId,\n text: `当前模型: ${current}\\n可用模型: ${available}\\n用法: /model <名称>`,\n replyToken: msg.replyToken,\n });\n } else if (this.providers.has(arg.toLowerCase())) {\n const provider = arg.toLowerCase();\n if (!this.config.userRoutes) this.config.userRoutes = {};\n this.config.userRoutes[msg.senderId] = provider;\n await channel.send({\n targetId: msg.senderId,\n text: `已切换到: ${provider}`,\n replyToken: msg.replyToken,\n });\n } else {\n await channel.send({\n targetId: msg.senderId,\n text: `未知模型: ${arg}\\n可用: ${[...this.providers.keys()].join(\", \")}`,\n replyToken: msg.replyToken,\n });\n }\n break;\n }\n\n case \"/help\": {\n await channel.send({\n targetId: msg.senderId,\n text: [\n \"wechat-ai 指令:\",\n \"/model [名称] - 切换AI模型\",\n \"/help - 显示帮助\",\n \"/ping - 检查状态\",\n ].join(\"\\n\"),\n replyToken: msg.replyToken,\n });\n break;\n }\n\n case \"/ping\": {\n await channel.send({\n targetId: msg.senderId,\n text: `pong (${Date.now() - msg.timestamp}ms)`,\n replyToken: msg.replyToken,\n });\n break;\n }\n\n default: {\n await channel.send({\n targetId: msg.senderId,\n text: `未知指令: ${cmd},试试 /help`,\n replyToken: msg.replyToken,\n });\n }\n }\n }\n}\n"],"mappings":"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","names":["join","readFile","writeFile","existsSync","log","log","log"]}
|
package/dist/cli.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -74,6 +74,30 @@ interface Provider {
|
|
|
74
74
|
/** Send a prompt and stream responses */
|
|
75
75
|
stream?(prompt: string, sessionId: string, options?: ProviderOptions): AsyncIterable<ProviderResponse>;
|
|
76
76
|
}
|
|
77
|
+
interface Context {
|
|
78
|
+
/** The inbound message */
|
|
79
|
+
message: InboundMessage;
|
|
80
|
+
/** Set this to override the AI response (skips provider call) */
|
|
81
|
+
response?: string;
|
|
82
|
+
/** The resolved provider name */
|
|
83
|
+
provider: string;
|
|
84
|
+
/** The channel instance */
|
|
85
|
+
channel: Channel;
|
|
86
|
+
/** Session key (channel:senderId) */
|
|
87
|
+
sessionKey: string;
|
|
88
|
+
/** Arbitrary data shared between middlewares */
|
|
89
|
+
state: Record<string, unknown>;
|
|
90
|
+
}
|
|
91
|
+
type NextFunction = () => Promise<void>;
|
|
92
|
+
type Middleware = (ctx: Context, next: NextFunction) => Promise<void>;
|
|
93
|
+
interface WebhookConfig {
|
|
94
|
+
/** Enable webhook HTTP server */
|
|
95
|
+
enabled?: boolean;
|
|
96
|
+
/** Port to listen on (default: 4800) */
|
|
97
|
+
port?: number;
|
|
98
|
+
/** Optional secret token for authentication */
|
|
99
|
+
secret?: string;
|
|
100
|
+
}
|
|
77
101
|
interface WaiConfig {
|
|
78
102
|
/** Default AI provider to use */
|
|
79
103
|
defaultProvider: string;
|
|
@@ -87,6 +111,8 @@ interface WaiConfig {
|
|
|
87
111
|
systemPrompt?: string;
|
|
88
112
|
/** Message chunk size limit */
|
|
89
113
|
chunkSize?: number;
|
|
114
|
+
/** Webhook HTTP server config */
|
|
115
|
+
webhook?: WebhookConfig;
|
|
90
116
|
}
|
|
91
117
|
interface ProviderConfig {
|
|
92
118
|
type: string;
|
|
@@ -109,7 +135,11 @@ declare class Gateway {
|
|
|
109
135
|
private buffers;
|
|
110
136
|
private processing;
|
|
111
137
|
private queues;
|
|
138
|
+
private middlewares;
|
|
139
|
+
private webhookServer;
|
|
112
140
|
constructor(config: WaiConfig);
|
|
141
|
+
/** Register a middleware function */
|
|
142
|
+
use(middleware: Middleware): this;
|
|
113
143
|
init(): void;
|
|
114
144
|
login(channelName: string): Promise<void>;
|
|
115
145
|
start(): Promise<void>;
|
|
@@ -118,6 +148,8 @@ declare class Gateway {
|
|
|
118
148
|
private flushBuffer;
|
|
119
149
|
private mergeMessages;
|
|
120
150
|
private processMessage;
|
|
151
|
+
private compose;
|
|
152
|
+
private startWebhook;
|
|
121
153
|
private handleCommand;
|
|
122
154
|
}
|
|
123
155
|
|
|
@@ -166,4 +198,4 @@ declare class OpenAICompatibleProvider implements Provider {
|
|
|
166
198
|
query(prompt: string, sessionId: string, options?: ProviderOptions): Promise<string>;
|
|
167
199
|
}
|
|
168
200
|
|
|
169
|
-
export { type Channel, type ChannelConfig, ClaudeAgentProvider, Gateway, type InboundMessage, type MediaAttachment, OpenAICompatibleProvider, type OutboundMessage, type Provider, type ProviderConfig, type ProviderOptions, type ProviderResponse, type WaiConfig, WeixinChannel, loadConfig, saveConfig };
|
|
201
|
+
export { type Channel, type ChannelConfig, ClaudeAgentProvider, type Context, Gateway, type InboundMessage, type MediaAttachment, type Middleware, type NextFunction, OpenAICompatibleProvider, type OutboundMessage, type Provider, type ProviderConfig, type ProviderOptions, type ProviderResponse, type WaiConfig, WeixinChannel, loadConfig, saveConfig };
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/config.ts","../src/logger.ts","../src/channels/weixin.ts","../src/providers/claude-agent.ts","../src/providers/openai-compatible.ts","../src/gateway.ts"],"sourcesContent":["import { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { WaiConfig } from \"./types.js\";\n\nconst WAI_DIR = join(homedir(), \".wai\");\nconst CONFIG_PATH = join(WAI_DIR, \"config.json\");\n\nconst DEFAULT_CONFIG: WaiConfig = {\n defaultProvider: \"qwen\",\n providers: {\n claude: {\n type: \"claude-agent\",\n allowedTools: [\"Read\", \"Glob\", \"Grep\", \"Bash\", \"WebSearch\", \"WebFetch\"],\n },\n qwen: {\n type: \"openai-compatible\",\n baseUrl: \"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n model: \"qwen-plus\",\n apiKeyEnv: \"DASHSCOPE_API_KEY\",\n },\n deepseek: {\n type: \"openai-compatible\",\n baseUrl: \"https://api.deepseek.com/v1\",\n model: \"deepseek-chat\",\n apiKeyEnv: \"DEEPSEEK_API_KEY\",\n },\n gpt: {\n type: \"openai-compatible\",\n baseUrl: \"https://api.openai.com/v1\",\n model: \"gpt-4o\",\n apiKeyEnv: \"OPENAI_API_KEY\",\n },\n gemini: {\n type: \"openai-compatible\",\n baseUrl: \"https://generativelanguage.googleapis.com/v1beta/openai\",\n model: \"gemini-2.0-flash\",\n apiKeyEnv: \"GEMINI_API_KEY\",\n },\n minimax: {\n type: \"openai-compatible\",\n baseUrl: \"https://api.minimax.chat/v1\",\n model: \"MiniMax-Text-01\",\n apiKeyEnv: \"MINIMAX_API_KEY\",\n },\n glm: {\n type: \"openai-compatible\",\n baseUrl: \"https://open.bigmodel.cn/api/paas/v4\",\n model: \"glm-4-plus\",\n apiKeyEnv: \"GLM_API_KEY\",\n },\n },\n channels: {\n weixin: {\n type: \"weixin\",\n enabled: true,\n },\n },\n systemPrompt: \"You are a helpful AI assistant. Respond concisely.\",\n chunkSize: 4000,\n};\n\nexport async function ensureDir(dir: string) {\n if (!existsSync(dir)) {\n await mkdir(dir, { recursive: true });\n }\n}\n\nexport async function loadConfig(): Promise<WaiConfig> {\n await ensureDir(WAI_DIR);\n\n if (!existsSync(CONFIG_PATH)) {\n await writeFile(CONFIG_PATH, JSON.stringify(DEFAULT_CONFIG, null, 2));\n return { ...DEFAULT_CONFIG };\n }\n\n const raw = await readFile(CONFIG_PATH, \"utf-8\");\n const user = JSON.parse(raw) as Partial<WaiConfig>;\n\n // Deep merge: default providers + user providers (user overrides per provider)\n const providers = { ...DEFAULT_CONFIG.providers };\n if (user.providers) {\n for (const [key, val] of Object.entries(user.providers)) {\n providers[key] = val;\n }\n }\n\n const config = { ...DEFAULT_CONFIG, ...user, providers } as WaiConfig;\n\n // Migrate: zhipu → glm\n if (config.providers.zhipu) {\n if (!config.providers.glm) {\n config.providers.glm = { ...config.providers.zhipu, apiKeyEnv: \"GLM_API_KEY\" };\n }\n delete config.providers.zhipu;\n if (config.defaultProvider === \"zhipu\") config.defaultProvider = \"glm\";\n await saveConfig(config);\n }\n\n return config;\n}\n\nexport async function saveConfig(config: WaiConfig): Promise<void> {\n await ensureDir(WAI_DIR);\n await writeFile(CONFIG_PATH, JSON.stringify(config, null, 2));\n}\n\nexport function getDataDir(): string {\n return WAI_DIR;\n}\n\nexport function getAccountsDir(): string {\n return join(WAI_DIR, \"accounts\");\n}\n","const LEVELS = { debug: 0, info: 1, warn: 2, error: 3 } as const;\ntype Level = keyof typeof LEVELS;\n\nlet currentLevel: Level = \"info\";\n\nexport function setLogLevel(level: Level) {\n currentLevel = level;\n}\n\nfunction fmt(level: Level, scope: string, msg: string): string {\n const ts = new Date().toISOString().slice(11, 23);\n const tag = level.toUpperCase().padEnd(5);\n return `\\x1b[90m${ts}\\x1b[0m ${colorize(level, tag)} \\x1b[36m[${scope}]\\x1b[0m ${msg}`;\n}\n\nfunction colorize(level: Level, text: string): string {\n switch (level) {\n case \"debug\": return `\\x1b[90m${text}\\x1b[0m`;\n case \"info\": return `\\x1b[32m${text}\\x1b[0m`;\n case \"warn\": return `\\x1b[33m${text}\\x1b[0m`;\n case \"error\": return `\\x1b[31m${text}\\x1b[0m`;\n }\n}\n\nexport function createLogger(scope: string) {\n return {\n debug: (msg: string) => { if (LEVELS[currentLevel] <= 0) console.log(fmt(\"debug\", scope, msg)); },\n info: (msg: string) => { if (LEVELS[currentLevel] <= 1) console.log(fmt(\"info\", scope, msg)); },\n warn: (msg: string) => { if (LEVELS[currentLevel] <= 2) console.warn(fmt(\"warn\", scope, msg)); },\n error: (msg: string) => { if (LEVELS[currentLevel] <= 3) console.error(fmt(\"error\", scope, msg)); },\n };\n}\n","import { createLogger } from \"../logger.js\";\nimport { getAccountsDir, ensureDir } from \"../config.js\";\nimport { join } from \"node:path\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { randomBytes, randomUUID } from \"node:crypto\";\nimport type { Channel, InboundMessage, OutboundMessage, ChannelConfig } from \"../types.js\";\n\nconst log = createLogger(\"weixin\");\n\nconst DEFAULT_BASE_URL = \"https://ilinkai.weixin.qq.com\";\nconst CHANNEL_VERSION = \"1.0.0\";\nconst API_TIMEOUT_MS = 15_000;\n\n// ── Message constants (from openclaw-weixin protocol) ──\nconst MessageType = { USER: 1, BOT: 2 } as const;\nconst MessageState = { NEW: 0, GENERATING: 1, FINISH: 2 } as const;\nconst MessageItemType = { TEXT: 1, IMAGE: 2, VOICE: 3, FILE: 4, VIDEO: 5 } as const;\n\n// ── Types ──\n\ninterface WeixinAccount {\n accountId: string;\n token: string;\n baseUrl: string;\n userId?: string;\n}\n\ninterface WeixinMessageItem {\n type: number;\n text_item?: { text: string };\n image_item?: unknown;\n voice_item?: unknown;\n file_item?: unknown;\n video_item?: unknown;\n}\n\ninterface WeixinMessage {\n seq?: number;\n message_id?: number;\n from_user_id?: string;\n to_user_id?: string;\n context_token?: string;\n item_list?: WeixinMessageItem[];\n create_time_ms?: number;\n}\n\ninterface GetUpdatesResponse {\n ret?: number;\n errmsg?: string;\n msgs?: WeixinMessage[];\n get_updates_buf?: string;\n}\n\n// ── Weixin Channel ──\n\nexport class WeixinChannel implements Channel {\n readonly name = \"weixin\";\n\n private account: WeixinAccount | null = null;\n private syncBuf = \"\";\n private running = false;\n private abortController: AbortController | null = null;\n private config: ChannelConfig;\n // Cache typing_ticket per user\n private typingTickets = new Map<string, string>();\n\n constructor(config: ChannelConfig) {\n this.config = config;\n }\n\n // ── Auth ──\n\n async login(): Promise<void> {\n const baseUrl = (this.config.baseUrl as string) || DEFAULT_BASE_URL;\n log.info(\"获取二维码中...\");\n\n const qrRes = await this.api(baseUrl, \"ilink/bot/get_bot_qrcode?bot_type=3\", null, {\n method: \"GET\",\n timeout: 10_000,\n });\n\n if (qrRes.ret !== 0) {\n throw new Error(`获取二维码失败: ${qrRes.errmsg || qrRes.ret}`);\n }\n\n const qrUrl: string = qrRes.qrcode_img_content || qrRes.data?.qrcode_img_content;\n const qrCode: string = qrRes.qrcode || qrRes.data?.qrcode;\n\n if (!qrUrl || !qrCode) {\n throw new Error(`二维码响应缺少字段: ${JSON.stringify(qrRes)}`);\n }\n\n log.info(\"请用微信扫描二维码:\");\n console.log();\n try {\n const qrTerminal = await import(\"qrcode-terminal\");\n (qrTerminal.default || qrTerminal).generate(qrUrl, { small: true });\n } catch {\n console.log(` ${qrUrl}`);\n }\n console.log();\n\n log.info(\"等待扫码...\");\n\n let attempts = 0;\n while (attempts < 60) {\n const statusRes = await this.api(\n baseUrl,\n `ilink/bot/get_qrcode_status?qrcode=${encodeURIComponent(qrCode)}`,\n null,\n { method: \"GET\", timeout: 40_000 },\n );\n\n const status = statusRes.data?.status || statusRes.status;\n\n if (status === \"confirmed\") {\n const data = statusRes.data || statusRes;\n const accountId: string = data.ilink_bot_id || data.bot_id;\n const token: string = data.bot_token || data.token;\n\n if (!accountId || !token) {\n throw new Error(\"登录成功但缺少凭证\");\n }\n\n this.account = {\n accountId,\n token,\n baseUrl: data.baseurl || baseUrl,\n userId: data.ilink_user_id,\n };\n\n await this.saveAccount();\n log.info(`登录成功!账号: ${accountId.slice(0, 8)}...`);\n return;\n }\n\n if (status === \"scaned\") {\n log.info(\"已扫码,等待确认...\");\n }\n\n if (status === \"expired\") {\n log.warn(\"二维码已过期\");\n throw new Error(\"二维码已过期\");\n }\n\n attempts++;\n await sleep(500);\n }\n\n throw new Error(\"登录超时\");\n }\n\n // ── Message loop ──\n\n async start(onMessage: (msg: InboundMessage) => void): Promise<void> {\n if (!this.account) {\n await this.loadAccount();\n }\n if (!this.account) {\n log.info(\"未找到账号,开始登录...\");\n await this.login();\n }\n\n await this.loadSyncBuf();\n this.running = true;\n log.info(`消息监听已启动 (${this.account!.accountId.slice(0, 8)}...)`);\n\n while (this.running) {\n try {\n this.abortController = new AbortController();\n const res = await this.getUpdates();\n\n if (res.ret === -14) {\n log.warn(\"会话过期,重新登录...\");\n this.account = null;\n await this.login();\n continue;\n }\n\n if (res.ret && res.ret !== 0) {\n log.warn(`拉取消息失败: ${res.errmsg || JSON.stringify(res)}`);\n await sleep(5000);\n continue;\n }\n\n if (res.get_updates_buf) {\n this.syncBuf = res.get_updates_buf;\n await this.saveSyncBuf();\n }\n\n if (res.msgs && res.msgs.length > 0) {\n for (const msg of res.msgs) {\n const text = this.extractText(msg);\n if (!text || !msg.from_user_id) continue;\n\n log.info(`收到消息 [${msg.from_user_id.slice(0, 8)}...]: ${text.slice(0, 50)}`);\n onMessage({\n id: String(msg.message_id || msg.seq || Date.now()),\n channel: \"weixin\",\n senderId: msg.from_user_id,\n text,\n replyToken: msg.context_token,\n timestamp: msg.create_time_ms || Date.now(),\n });\n }\n }\n } catch (err) {\n if (!this.running) break;\n const message = err instanceof Error ? err.message : String(err);\n if (message.includes(\"aborted\") || message.includes(\"AbortError\")) continue;\n log.error(`轮询出错: ${message}`);\n await sleep(3000);\n }\n }\n }\n\n // ── Send typing indicator ──\n\n async sendTyping(userId: string, contextToken?: string): Promise<void> {\n if (!this.account) return;\n\n try {\n // Get typing_ticket if not cached\n let ticket = this.typingTickets.get(userId);\n if (!ticket) {\n const configRes = await this.api(this.account.baseUrl, \"ilink/bot/getconfig\", {\n ilink_user_id: userId,\n context_token: contextToken,\n base_info: { channel_version: CHANNEL_VERSION },\n }, { timeout: 10_000 });\n\n ticket = configRes.typing_ticket;\n if (ticket) {\n this.typingTickets.set(userId, ticket);\n }\n }\n\n if (!ticket) return;\n\n await this.api(this.account.baseUrl, \"ilink/bot/sendtyping\", {\n ilink_user_id: userId,\n typing_ticket: ticket,\n status: 1,\n base_info: { channel_version: CHANNEL_VERSION },\n }, { timeout: 10_000 });\n\n log.debug(`已发送输入状态给 ${userId.slice(0, 8)}...`);\n } catch {\n // typing 失败不影响主流程\n }\n }\n\n // ── Send message ──\n\n async send(msg: OutboundMessage): Promise<void> {\n if (!this.account) throw new Error(\"未登录\");\n\n const chunks = this.chunkText(msg.text, 4000);\n\n for (const chunk of chunks) {\n const body = {\n msg: {\n from_user_id: \"\",\n to_user_id: msg.targetId,\n client_id: generateClientId(),\n message_type: MessageType.BOT,\n message_state: MessageState.FINISH,\n context_token: msg.replyToken || undefined,\n item_list: [{ type: MessageItemType.TEXT, text_item: { text: chunk } }],\n },\n base_info: { channel_version: CHANNEL_VERSION },\n };\n\n const res = await this.api(\n this.account.baseUrl,\n \"ilink/bot/sendmessage\",\n body,\n { timeout: API_TIMEOUT_MS },\n );\n\n // sendMessage 成功返回 {} (空对象),有错误时返回 ret + errmsg\n if (res.ret && res.ret !== 0) {\n log.error(`发送失败: ${res.errmsg || JSON.stringify(res)}`);\n }\n }\n }\n\n async stop(): Promise<void> {\n this.running = false;\n this.abortController?.abort();\n log.info(\"已停止\");\n }\n\n // ── Internal ──\n\n private async getUpdates(): Promise<GetUpdatesResponse> {\n if (!this.account) throw new Error(\"未登录\");\n\n return this.api(this.account.baseUrl, \"ilink/bot/getupdates\", {\n get_updates_buf: this.syncBuf,\n base_info: { channel_version: CHANNEL_VERSION },\n }, { timeout: 50_000 });\n }\n\n private extractText(msg: WeixinMessage): string | null {\n if (!msg.item_list?.length) return null;\n for (const item of msg.item_list) {\n if (item.type === 1 && item.text_item?.text) {\n return item.text_item.text;\n }\n }\n return null;\n }\n\n private chunkText(text: string, maxLen: number): string[] {\n if (text.length <= maxLen) return [text];\n const chunks: string[] = [];\n let remaining = text;\n while (remaining.length > 0) {\n let breakAt = remaining.lastIndexOf(\"\\n\", maxLen);\n if (breakAt <= 0) breakAt = maxLen;\n chunks.push(remaining.slice(0, breakAt));\n remaining = remaining.slice(breakAt);\n }\n return chunks;\n }\n\n private async api(\n baseUrl: string,\n path: string,\n body: unknown,\n opts: { method?: string; timeout?: number } = {},\n ): Promise<any> {\n const url = `${baseUrl.replace(/\\/$/, \"\")}/${path}`;\n const method = opts.method || \"POST\";\n const bodyStr = body ? JSON.stringify(body) : undefined;\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n\n if (this.account?.token) {\n headers[\"AuthorizationType\"] = \"ilink_bot_token\";\n headers[\"Authorization\"] = `Bearer ${this.account.token}`;\n headers[\"X-WECHAT-UIN\"] = randomUin();\n if (bodyStr) {\n headers[\"Content-Length\"] = String(Buffer.byteLength(bodyStr, \"utf-8\"));\n }\n }\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), opts.timeout || API_TIMEOUT_MS);\n\n try {\n const res = await fetch(url, {\n method,\n headers,\n body: bodyStr,\n signal: controller.signal,\n });\n return await res.json();\n } finally {\n clearTimeout(timer);\n }\n }\n\n // ── Persistence ──\n\n private accountFile(): string {\n return join(getAccountsDir(), \"weixin.json\");\n }\n\n private syncFile(): string {\n return join(getAccountsDir(), \"weixin-sync.json\");\n }\n\n private async saveAccount(): Promise<void> {\n await ensureDir(getAccountsDir());\n await writeFile(this.accountFile(), JSON.stringify(this.account, null, 2));\n }\n\n private async loadAccount(): Promise<void> {\n const path = this.accountFile();\n if (!existsSync(path)) return;\n try {\n const raw = await readFile(path, \"utf-8\");\n this.account = JSON.parse(raw);\n log.info(`已加载账号: ${this.account!.accountId.slice(0, 8)}...`);\n } catch {\n log.warn(\"加载账号失败\");\n }\n }\n\n private async saveSyncBuf(): Promise<void> {\n await ensureDir(getAccountsDir());\n await writeFile(this.syncFile(), JSON.stringify({ get_updates_buf: this.syncBuf }));\n }\n\n private async loadSyncBuf(): Promise<void> {\n const path = this.syncFile();\n if (!existsSync(path)) return;\n try {\n const raw = await readFile(path, \"utf-8\");\n const data = JSON.parse(raw);\n this.syncBuf = data.get_updates_buf || \"\";\n } catch {\n // fresh start\n }\n }\n}\n\n// ── Helpers ──\n\nfunction randomUin(): string {\n const uint32 = randomBytes(4).readUInt32BE(0);\n return Buffer.from(String(uint32), \"utf-8\").toString(\"base64\");\n}\n\nfunction generateClientId(): string {\n return `wai-${randomUUID().replace(/-/g, \"\").slice(0, 16)}`;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n","import { createLogger } from \"../logger.js\";\nimport type { Provider, ProviderOptions, ProviderConfig } from \"../types.js\";\n\nconst log = createLogger(\"claude\");\n\nconst DEFAULT_TOOLS = [\"Read\", \"Glob\", \"Grep\", \"Bash\", \"WebSearch\", \"WebFetch\"];\n\nexport class ClaudeAgentProvider implements Provider {\n readonly name = \"claude-agent\";\n private config: ProviderConfig;\n private sessions = new Map<string, string>(); // userId -> sessionId\n\n constructor(config: ProviderConfig) {\n this.config = config;\n }\n\n async query(\n prompt: string,\n sessionId: string,\n options?: ProviderOptions,\n ): Promise<string> {\n const { query } = await import(\"@anthropic-ai/claude-agent-sdk\");\n\n const allowedTools = options?.allowedTools\n || (this.config.allowedTools as string[])\n || DEFAULT_TOOLS;\n\n const existingSession = this.sessions.get(sessionId);\n const sdkOptions: Record<string, unknown> = {\n allowedTools,\n permissionMode: \"acceptEdits\" as const,\n };\n\n if (options?.maxTokens) {\n sdkOptions.maxTokens = options.maxTokens;\n }\n\n if (options?.cwd) {\n sdkOptions.cwd = options.cwd;\n }\n\n // Resume existing session for conversation continuity\n if (existingSession) {\n sdkOptions.resume = existingSession;\n }\n\n if (options?.systemPrompt) {\n sdkOptions.systemPrompt = options.systemPrompt;\n }\n\n log.info(`Querying Claude (session: ${sessionId.slice(0, 8)}...)`);\n\n let result = \"\";\n let newSessionId: string | undefined;\n\n try {\n for await (const message of query({\n prompt,\n options: sdkOptions as any,\n })) {\n // Capture session ID from init message\n if (isInitMessage(message)) {\n newSessionId = message.session_id;\n }\n\n // Capture result text\n if (isResultMessage(message)) {\n result = message.result;\n }\n\n // Capture assistant text messages for streaming\n if (isAssistantMessage(message)) {\n // accumulate text from assistant messages\n const textContent = extractText(message);\n if (textContent) {\n result = textContent;\n }\n }\n }\n } catch (err) {\n const errMsg = err instanceof Error ? err.message : String(err);\n log.error(`Claude query failed: ${errMsg}`);\n throw err;\n }\n\n // Store session for continuity\n if (newSessionId) {\n this.sessions.set(sessionId, newSessionId);\n }\n\n if (!result) {\n result = \"(No response from Claude)\";\n }\n\n log.info(`Response: ${result.length} chars`);\n return result;\n }\n}\n\n// ── Message type guards ──\n\nfunction isInitMessage(msg: any): msg is { type: \"system\"; subtype: \"init\"; session_id: string } {\n return msg?.type === \"system\" && msg?.subtype === \"init\" && typeof msg?.session_id === \"string\";\n}\n\nfunction isResultMessage(msg: any): msg is { result: string } {\n return typeof msg?.result === \"string\";\n}\n\nfunction isAssistantMessage(msg: any): msg is { type: \"assistant\"; message: { content: unknown[] } } {\n return msg?.type === \"assistant\" && msg?.message?.content;\n}\n\nfunction extractText(msg: any): string | null {\n if (!msg?.message?.content) return null;\n const parts: string[] = [];\n for (const block of msg.message.content) {\n if (block.type === \"text\" && typeof block.text === \"string\") {\n parts.push(block.text);\n }\n }\n return parts.length > 0 ? parts.join(\"\") : null;\n}\n","import { createLogger } from \"../logger.js\";\nimport type { Provider, ProviderOptions, ProviderConfig } from \"../types.js\";\n\nconst log = createLogger(\"openai-compat\");\n\ninterface ChatMessage {\n role: \"system\" | \"user\" | \"assistant\";\n content: string;\n}\n\ninterface ChatCompletionResponse {\n choices: Array<{\n message: { content: string };\n finish_reason: string;\n }>;\n usage?: { prompt_tokens: number; completion_tokens: number };\n}\n\nexport class OpenAICompatibleProvider implements Provider {\n readonly name: string;\n private config: ProviderConfig;\n private histories = new Map<string, ChatMessage[]>();\n\n constructor(name: string, config: ProviderConfig) {\n this.name = name;\n this.config = config;\n }\n\n async query(\n prompt: string,\n sessionId: string,\n options?: ProviderOptions,\n ): Promise<string> {\n const baseUrl = this.config.baseUrl;\n const apiKey = this.config.apiKey || process.env[this.config.apiKeyEnv as string || \"\"];\n const model = options?.model || (this.config.model as string);\n\n if (!baseUrl) throw new Error(`${this.name}: baseUrl is required`);\n if (!apiKey) throw new Error(`${this.name}: apiKey is required`);\n if (!model) throw new Error(`${this.name}: model is required`);\n\n // Build conversation history\n let history = this.histories.get(sessionId);\n if (!history) {\n history = [];\n this.histories.set(sessionId, history);\n }\n\n const messages: ChatMessage[] = [];\n\n // System prompt\n const systemPrompt = options?.systemPrompt || (this.config.systemPrompt as string);\n if (systemPrompt) {\n messages.push({ role: \"system\", content: systemPrompt });\n }\n\n // Conversation history (keep last N turns to stay within context)\n const maxHistory = (this.config.maxHistory as number) || 20;\n const recentHistory = history.slice(-maxHistory);\n messages.push(...recentHistory);\n\n // Current user message\n messages.push({ role: \"user\", content: prompt });\n\n log.info(`Querying ${this.name} (model: ${model}, session: ${sessionId.slice(0, 8)}...)`);\n\n const url = `${baseUrl.replace(/\\/$/, \"\")}/chat/completions`;\n\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n model,\n messages,\n max_tokens: options?.maxTokens || (this.config.maxTokens as number) || 4096,\n temperature: (this.config.temperature as number) ?? 0.7,\n }),\n });\n\n if (!res.ok) {\n const errBody = await res.text();\n log.error(`${this.name} API error ${res.status}: ${errBody.slice(0, 200)}`);\n throw new Error(`${this.name} API error: ${res.status}`);\n }\n\n const data = (await res.json()) as ChatCompletionResponse;\n const reply = data.choices[0]?.message.content || \"(No response)\";\n\n if (data.usage) {\n log.info(`Tokens: ${data.usage.prompt_tokens} in / ${data.usage.completion_tokens} out`);\n }\n\n // Update history\n history.push({ role: \"user\", content: prompt });\n history.push({ role: \"assistant\", content: reply });\n\n log.info(`Response: ${reply.length} chars`);\n return reply;\n }\n}\n","import { createLogger } from \"./logger.js\";\nimport type {\n Channel,\n Provider,\n InboundMessage,\n WaiConfig,\n ProviderOptions,\n} from \"./types.js\";\nimport { WeixinChannel } from \"./channels/weixin.js\";\nimport { ClaudeAgentProvider } from \"./providers/claude-agent.js\";\nimport { OpenAICompatibleProvider } from \"./providers/openai-compatible.js\";\n\nconst log = createLogger(\"网关\");\n\nconst DEBOUNCE_MS = 1500;\n\ninterface MessageBuffer {\n messages: InboundMessage[];\n timer: ReturnType<typeof setTimeout>;\n}\n\nexport class Gateway {\n private channels = new Map<string, Channel>();\n private providers = new Map<string, Provider>();\n private config: WaiConfig;\n // Debounce buffer: accumulates messages within DEBOUNCE_MS window\n private buffers = new Map<string, MessageBuffer>();\n // Whether AI is currently processing for a given user\n private processing = new Set<string>();\n // Queue for messages that arrive while AI is processing\n private queues = new Map<string, InboundMessage[]>();\n\n constructor(config: WaiConfig) {\n this.config = config;\n }\n\n init(): void {\n for (const [name, chConfig] of Object.entries(this.config.channels)) {\n if (chConfig.enabled === false) continue;\n switch (chConfig.type) {\n case \"weixin\":\n this.channels.set(name, new WeixinChannel(chConfig));\n break;\n default:\n log.warn(`未知渠道类型: ${chConfig.type}`);\n }\n }\n\n for (const [name, provConfig] of Object.entries(this.config.providers)) {\n switch (provConfig.type) {\n case \"claude-agent\":\n this.providers.set(name, new ClaudeAgentProvider(provConfig));\n break;\n case \"openai-compatible\":\n this.providers.set(name, new OpenAICompatibleProvider(name, provConfig));\n break;\n default:\n log.warn(`未知模型类型: ${provConfig.type}`);\n }\n }\n\n log.info(`已初始化 ${this.channels.size} 个渠道, ${this.providers.size} 个模型`);\n }\n\n async login(channelName: string): Promise<void> {\n const channel = this.channels.get(channelName);\n if (!channel) {\n throw new Error(`渠道 \"${channelName}\" 不存在`);\n }\n await channel.login();\n }\n\n async start(): Promise<void> {\n if (this.providers.size === 0) {\n throw new Error(\"未配置任何模型\");\n }\n\n const startPromises = [...this.channels.entries()].map(([name, channel]) => {\n log.info(`启动渠道: ${name}`);\n return channel.start((msg) => this.handleMessage(msg)).catch((err) => {\n log.error(`渠道 ${name} 异常: ${err instanceof Error ? err.message : err}`);\n });\n });\n\n await Promise.all(startPromises);\n }\n\n async stop(): Promise<void> {\n log.info(\"正在关闭...\");\n const stops = [...this.channels.values()].map((ch) => ch.stop());\n await Promise.allSettled(stops);\n log.info(\"已关闭\");\n }\n\n private handleMessage(msg: InboundMessage): void {\n // Commands bypass debounce, execute immediately\n if (msg.text.startsWith(\"/\")) {\n this.handleCommand(msg);\n return;\n }\n\n const key = `${msg.channel}:${msg.senderId}`;\n\n // If AI is processing, queue the message\n if (this.processing.has(key)) {\n const queue = this.queues.get(key) || [];\n queue.push(msg);\n this.queues.set(key, queue);\n log.info(`消息已排队 (AI处理中), 队列长度: ${queue.length}`);\n return;\n }\n\n // Debounce: accumulate messages within time window\n const existing = this.buffers.get(key);\n if (existing) {\n clearTimeout(existing.timer);\n existing.messages.push(msg);\n existing.timer = setTimeout(() => this.flushBuffer(key), DEBOUNCE_MS);\n } else {\n this.buffers.set(key, {\n messages: [msg],\n timer: setTimeout(() => this.flushBuffer(key), DEBOUNCE_MS),\n });\n }\n }\n\n private async flushBuffer(key: string): Promise<void> {\n const buf = this.buffers.get(key);\n if (!buf || buf.messages.length === 0) return;\n this.buffers.delete(key);\n\n // Merge all buffered messages into one\n const merged = this.mergeMessages(buf.messages);\n await this.processMessage(merged);\n\n // After processing, check if there are queued messages\n const queue = this.queues.get(key);\n if (queue && queue.length > 0) {\n this.queues.delete(key);\n // Feed queued messages back through debounce\n for (const msg of queue) {\n this.handleMessage(msg);\n }\n }\n }\n\n private mergeMessages(messages: InboundMessage[]): InboundMessage {\n if (messages.length === 1) return messages[0]!;\n\n const last = messages[messages.length - 1]!;\n const mergedText = messages.map((m) => m.text).join(\"\\n\");\n log.info(`合并 ${messages.length} 条消息`);\n\n return { ...last, text: mergedText };\n }\n\n private async processMessage(msg: InboundMessage): Promise<void> {\n const key = `${msg.channel}:${msg.senderId}`;\n this.processing.add(key);\n\n try {\n const providerName = this.config.userRoutes?.[msg.senderId]\n || this.config.defaultProvider;\n const provider = this.providers.get(providerName);\n\n if (!provider) {\n log.error(`模型 \"${providerName}\" 未找到`);\n return;\n }\n\n log.info(`调用 ${providerName} 处理中...`);\n\n const channel = this.channels.get(msg.channel);\n if (channel && \"sendTyping\" in channel) {\n (channel as any).sendTyping(msg.senderId, msg.replyToken);\n }\n\n const options: ProviderOptions = {};\n if (this.config.systemPrompt) {\n options.systemPrompt = this.config.systemPrompt;\n }\n\n const sessionKey = `${msg.channel}:${msg.senderId}`;\n const response = await provider.query(msg.text, sessionKey, options);\n\n if (!channel) return;\n\n await channel.send({\n targetId: msg.senderId,\n text: response,\n replyToken: msg.replyToken,\n });\n\n log.info(`已回复 (${response.length} 字符)`);\n } catch (err) {\n const errMsg = err instanceof Error ? err.message : String(err);\n log.error(`处理消息失败: ${errMsg}`);\n\n try {\n const channel = this.channels.get(msg.channel);\n if (channel) {\n await channel.send({\n targetId: msg.senderId,\n text: `[出错了] 处理消息失败,请重试。`,\n replyToken: msg.replyToken,\n });\n }\n } catch {\n // swallow\n }\n } finally {\n this.processing.delete(key);\n }\n }\n\n private async handleCommand(msg: InboundMessage): Promise<void> {\n const channel = this.channels.get(msg.channel);\n if (!channel) return;\n\n const parts = msg.text.trim().split(/\\s+/);\n const cmd = parts[0]!.toLowerCase();\n const arg = parts[1];\n\n switch (cmd) {\n case \"/model\": {\n if (!arg) {\n const current = this.config.userRoutes?.[msg.senderId] || this.config.defaultProvider;\n const available = [...this.providers.keys()].join(\", \");\n await channel.send({\n targetId: msg.senderId,\n text: `当前模型: ${current}\\n可用模型: ${available}\\n用法: /model <名称>`,\n replyToken: msg.replyToken,\n });\n } else if (this.providers.has(arg.toLowerCase())) {\n const provider = arg.toLowerCase();\n if (!this.config.userRoutes) this.config.userRoutes = {};\n this.config.userRoutes[msg.senderId] = provider;\n await channel.send({\n targetId: msg.senderId,\n text: `已切换到: ${provider}`,\n replyToken: msg.replyToken,\n });\n } else {\n await channel.send({\n targetId: msg.senderId,\n text: `未知模型: ${arg}\\n可用: ${[...this.providers.keys()].join(\", \")}`,\n replyToken: msg.replyToken,\n });\n }\n break;\n }\n\n case \"/help\": {\n await channel.send({\n targetId: msg.senderId,\n text: [\n \"wechat-ai 指令:\",\n \"/model [名称] - 切换AI模型\",\n \"/help - 显示帮助\",\n \"/ping - 检查状态\",\n ].join(\"\\n\"),\n replyToken: msg.replyToken,\n });\n break;\n }\n\n case \"/ping\": {\n await channel.send({\n targetId: msg.senderId,\n text: `pong (${Date.now() - msg.timestamp}ms)`,\n replyToken: msg.replyToken,\n });\n break;\n }\n\n default: {\n await channel.send({\n targetId: msg.senderId,\n text: `未知指令: ${cmd},试试 /help`,\n replyToken: msg.replyToken,\n });\n }\n }\n }\n}\n"],"mappings":"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","names":["join","readFile","writeFile","existsSync","log","log","log"]}
|