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.
@@ -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
- await channel.send({
733
- targetId: msg.senderId,
734
- text: response,
735
- replyToken: msg.replyToken
736
- });
737
- log4.info(`\u5DF2\u56DE\u590D (${response.length} \u5B57\u7B26)`);
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-CP3Y2VRX.js.map
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
@@ -5,7 +5,7 @@ import {
5
5
  loadConfig,
6
6
  saveConfig,
7
7
  setLogLevel
8
- } from "./chunk-CP3Y2VRX.js";
8
+ } from "./chunk-HSZRSG6L.js";
9
9
 
10
10
  // src/cli.ts
11
11
  import { readFileSync } from "fs";
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
@@ -5,7 +5,7 @@ import {
5
5
  WeixinChannel,
6
6
  loadConfig,
7
7
  saveConfig
8
- } from "./chunk-CP3Y2VRX.js";
8
+ } from "./chunk-HSZRSG6L.js";
9
9
  export {
10
10
  ClaudeAgentProvider,
11
11
  Gateway,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wechat-ai",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "WeChat AI Bot — Bridge WeChat to Claude, Qwen, DeepSeek and more",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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"]}