wechat-ai 0.1.4 → 0.1.6
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/README.md +12 -12
- package/dist/{chunk-CP3Y2VRX.js → chunk-57KZRJCI.js} +380 -53
- package/dist/chunk-57KZRJCI.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +98 -1
- package/dist/index.js +3 -1
- package/package.json +2 -1
- package/dist/chunk-CP3Y2VRX.js.map +0 -1
package/README.md
CHANGED
|
@@ -28,6 +28,18 @@ wechat-ai
|
|
|
28
28
|
|
|
29
29
|
Claude 通过 [Agent SDK](https://github.com/anthropics/claude-agent-sdk-typescript) 接入,支持执行代码、读写文件、搜索网页,不只是聊天。
|
|
30
30
|
|
|
31
|
+
### API Key 获取
|
|
32
|
+
|
|
33
|
+
| 模型 | 申请地址 |
|
|
34
|
+
|------|---------|
|
|
35
|
+
| 通义千问 (Qwen) | https://dashscope.console.aliyun.com/apiKey |
|
|
36
|
+
| DeepSeek | https://platform.deepseek.com/api_keys |
|
|
37
|
+
| Claude | https://console.anthropic.com/settings/keys |
|
|
38
|
+
| GPT | https://platform.openai.com/api-keys |
|
|
39
|
+
| Gemini | https://aistudio.google.com/apikey |
|
|
40
|
+
| MiniMax | https://platform.minimaxi.com/user-center/basic-information/interface-key |
|
|
41
|
+
| 智谱 (GLM) | https://open.bigmodel.cn/usercenter/apikeys |
|
|
42
|
+
|
|
31
43
|
## 安装运行
|
|
32
44
|
|
|
33
45
|
```bash
|
|
@@ -110,18 +122,6 @@ src/
|
|
|
110
122
|
- [ ] Telegram / Discord 渠道
|
|
111
123
|
- [ ] MCP 支持
|
|
112
124
|
|
|
113
|
-
## API Key 获取
|
|
114
|
-
|
|
115
|
-
| 模型 | 申请地址 |
|
|
116
|
-
|------|---------|
|
|
117
|
-
| 通义千问 (Qwen) | https://dashscope.console.aliyun.com/apiKey |
|
|
118
|
-
| DeepSeek | https://platform.deepseek.com/api_keys |
|
|
119
|
-
| Claude | https://console.anthropic.com/settings/keys |
|
|
120
|
-
| GPT | https://platform.openai.com/api-keys |
|
|
121
|
-
| Gemini | https://aistudio.google.com/apikey |
|
|
122
|
-
| MiniMax | https://platform.minimaxi.com/user-center/basic-information/interface-key |
|
|
123
|
-
| 智谱 (GLM) | https://open.bigmodel.cn/usercenter/apikeys |
|
|
124
|
-
|
|
125
125
|
## 协议
|
|
126
126
|
|
|
127
127
|
MIT
|
|
@@ -56,7 +56,21 @@ var DEFAULT_CONFIG = {
|
|
|
56
56
|
}
|
|
57
57
|
},
|
|
58
58
|
systemPrompt: "You are a helpful AI assistant. Respond concisely.",
|
|
59
|
-
chunkSize: 4e3
|
|
59
|
+
chunkSize: 4e3,
|
|
60
|
+
skills: {
|
|
61
|
+
translator: {
|
|
62
|
+
description: "\u4E2D\u82F1\u7FFB\u8BD1\u52A9\u624B",
|
|
63
|
+
systemPrompt: "You are a professional translator. Translate Chinese to English and English to Chinese. Only output the translation, no explanations."
|
|
64
|
+
},
|
|
65
|
+
coder: {
|
|
66
|
+
description: "\u7F16\u7A0B\u52A9\u624B",
|
|
67
|
+
systemPrompt: "You are a senior software engineer. Help with coding questions. Be concise and provide code examples."
|
|
68
|
+
},
|
|
69
|
+
writer: {
|
|
70
|
+
description: "\u5199\u4F5C\u52A9\u624B",
|
|
71
|
+
systemPrompt: "You are a skilled writer. Help with writing, editing, and polishing text. Match the user's language."
|
|
72
|
+
}
|
|
73
|
+
}
|
|
60
74
|
};
|
|
61
75
|
async function ensureDir(dir) {
|
|
62
76
|
if (!existsSync(dir)) {
|
|
@@ -534,6 +548,7 @@ function extractText(msg) {
|
|
|
534
548
|
|
|
535
549
|
// src/providers/openai-compatible.ts
|
|
536
550
|
var log3 = createLogger("openai-compat");
|
|
551
|
+
var MAX_TOOL_ROUNDS = 10;
|
|
537
552
|
var OpenAICompatibleProvider = class {
|
|
538
553
|
name;
|
|
539
554
|
config;
|
|
@@ -565,28 +580,72 @@ var OpenAICompatibleProvider = class {
|
|
|
565
580
|
messages.push({ role: "user", content: prompt });
|
|
566
581
|
log3.info(`Querying ${this.name} (model: ${model}, session: ${sessionId.slice(0, 8)}...)`);
|
|
567
582
|
const url = `${baseUrl.replace(/\/$/, "")}/chat/completions`;
|
|
568
|
-
const
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
body: JSON.stringify({
|
|
583
|
+
const tools = options?.mcpTools;
|
|
584
|
+
const callTool = options?.mcpCallTool;
|
|
585
|
+
const hasTools = tools && tools.length > 0 && callTool;
|
|
586
|
+
let reply = "";
|
|
587
|
+
for (let round = 0; round < MAX_TOOL_ROUNDS; round++) {
|
|
588
|
+
const body = {
|
|
575
589
|
model,
|
|
576
590
|
messages,
|
|
577
591
|
max_tokens: options?.maxTokens || this.config.maxTokens || 4096,
|
|
578
592
|
temperature: this.config.temperature ?? 0.7
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
593
|
+
};
|
|
594
|
+
if (hasTools) {
|
|
595
|
+
body.tools = tools;
|
|
596
|
+
}
|
|
597
|
+
const res = await fetch(url, {
|
|
598
|
+
method: "POST",
|
|
599
|
+
headers: {
|
|
600
|
+
"Content-Type": "application/json",
|
|
601
|
+
"Authorization": `Bearer ${apiKey}`
|
|
602
|
+
},
|
|
603
|
+
body: JSON.stringify(body)
|
|
604
|
+
});
|
|
605
|
+
if (!res.ok) {
|
|
606
|
+
const errBody = await res.text();
|
|
607
|
+
log3.error(`${this.name} API error ${res.status}: ${errBody.slice(0, 200)}`);
|
|
608
|
+
throw new Error(`${this.name} API error: ${res.status}`);
|
|
609
|
+
}
|
|
610
|
+
const data = await res.json();
|
|
611
|
+
const choice = data.choices[0];
|
|
612
|
+
if (!choice) throw new Error(`${this.name}: empty response`);
|
|
613
|
+
if (data.usage) {
|
|
614
|
+
log3.info(`Tokens: ${data.usage.prompt_tokens} in / ${data.usage.completion_tokens} out`);
|
|
615
|
+
}
|
|
616
|
+
const assistantMsg = choice.message;
|
|
617
|
+
if (!assistantMsg.tool_calls || assistantMsg.tool_calls.length === 0 || !callTool) {
|
|
618
|
+
reply = assistantMsg.content || "(No response)";
|
|
619
|
+
break;
|
|
620
|
+
}
|
|
621
|
+
messages.push({
|
|
622
|
+
role: "assistant",
|
|
623
|
+
content: assistantMsg.content,
|
|
624
|
+
tool_calls: assistantMsg.tool_calls
|
|
625
|
+
});
|
|
626
|
+
for (const tc of assistantMsg.tool_calls) {
|
|
627
|
+
const fnName = tc.function.name;
|
|
628
|
+
let fnArgs;
|
|
629
|
+
try {
|
|
630
|
+
fnArgs = JSON.parse(tc.function.arguments);
|
|
631
|
+
} catch {
|
|
632
|
+
fnArgs = {};
|
|
633
|
+
}
|
|
634
|
+
log3.info(`\u5DE5\u5177\u8C03\u7528: ${fnName}(${JSON.stringify(fnArgs).slice(0, 100)})`);
|
|
635
|
+
let toolResult;
|
|
636
|
+
try {
|
|
637
|
+
toolResult = await callTool(fnName, fnArgs);
|
|
638
|
+
} catch (err) {
|
|
639
|
+
toolResult = `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
640
|
+
log3.error(`\u5DE5\u5177\u8C03\u7528\u5931\u8D25: ${fnName} \u2014 ${toolResult}`);
|
|
641
|
+
}
|
|
642
|
+
messages.push({
|
|
643
|
+
role: "tool",
|
|
644
|
+
content: toolResult,
|
|
645
|
+
tool_call_id: tc.id
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
log3.info(`\u5DE5\u5177\u8C03\u7528\u5B8C\u6210 (round ${round + 1}), \u7EE7\u7EED\u5904\u7406...`);
|
|
590
649
|
}
|
|
591
650
|
history.push({ role: "user", content: prompt });
|
|
592
651
|
history.push({ role: "assistant", content: reply });
|
|
@@ -595,8 +654,113 @@ var OpenAICompatibleProvider = class {
|
|
|
595
654
|
}
|
|
596
655
|
};
|
|
597
656
|
|
|
657
|
+
// src/mcp.ts
|
|
658
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
659
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
660
|
+
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
661
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
662
|
+
var log4 = createLogger("mcp");
|
|
663
|
+
var McpManager = class {
|
|
664
|
+
connections = /* @__PURE__ */ new Map();
|
|
665
|
+
async connect(servers) {
|
|
666
|
+
const connectPromises = Object.entries(servers).map(async ([name, config]) => {
|
|
667
|
+
try {
|
|
668
|
+
await this.connectServer(name, config);
|
|
669
|
+
log4.info(`MCP \u670D\u52A1\u5668\u5DF2\u8FDE\u63A5: ${name} (${config.transport || "stdio"})`);
|
|
670
|
+
} catch (err) {
|
|
671
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
672
|
+
log4.error(`MCP \u670D\u52A1\u5668\u8FDE\u63A5\u5931\u8D25: ${name} \u2014 ${errMsg}`);
|
|
673
|
+
}
|
|
674
|
+
});
|
|
675
|
+
await Promise.all(connectPromises);
|
|
676
|
+
}
|
|
677
|
+
async connectServer(name, config) {
|
|
678
|
+
let transport;
|
|
679
|
+
const transportType = config.transport || "stdio";
|
|
680
|
+
if (transportType === "stdio") {
|
|
681
|
+
if (!config.command) throw new Error(`MCP server "${name}": command is required for stdio`);
|
|
682
|
+
transport = new StdioClientTransport({
|
|
683
|
+
command: config.command,
|
|
684
|
+
args: config.args || [],
|
|
685
|
+
env: config.env
|
|
686
|
+
});
|
|
687
|
+
} else if (transportType === "sse") {
|
|
688
|
+
if (!config.url) throw new Error(`MCP server "${name}": url is required for sse`);
|
|
689
|
+
transport = new SSEClientTransport(new URL(config.url));
|
|
690
|
+
} else if (transportType === "streamable-http") {
|
|
691
|
+
if (!config.url) throw new Error(`MCP server "${name}": url is required for streamable-http`);
|
|
692
|
+
transport = new StreamableHTTPClientTransport(new URL(config.url));
|
|
693
|
+
} else {
|
|
694
|
+
throw new Error(`MCP server "${name}": unknown transport "${transportType}"`);
|
|
695
|
+
}
|
|
696
|
+
const client = new Client(
|
|
697
|
+
{ name: "wechat-ai", version: "0.1.0" },
|
|
698
|
+
{ capabilities: {} }
|
|
699
|
+
);
|
|
700
|
+
await client.connect(transport);
|
|
701
|
+
const toolsResult = await client.listTools();
|
|
702
|
+
const tools = (toolsResult.tools || []).map((t) => ({
|
|
703
|
+
name: t.name,
|
|
704
|
+
description: t.description || "",
|
|
705
|
+
inputSchema: t.inputSchema,
|
|
706
|
+
serverName: name
|
|
707
|
+
}));
|
|
708
|
+
log4.info(`${name}: \u53D1\u73B0 ${tools.length} \u4E2A\u5DE5\u5177`);
|
|
709
|
+
this.connections.set(name, { client, transport, tools });
|
|
710
|
+
}
|
|
711
|
+
/** Get all available tools across all connected servers */
|
|
712
|
+
getTools() {
|
|
713
|
+
const allTools = [];
|
|
714
|
+
for (const conn of this.connections.values()) {
|
|
715
|
+
allTools.push(...conn.tools);
|
|
716
|
+
}
|
|
717
|
+
return allTools;
|
|
718
|
+
}
|
|
719
|
+
/** Convert MCP tools to OpenAI function calling format */
|
|
720
|
+
getOpenAITools() {
|
|
721
|
+
return this.getTools().map((t) => ({
|
|
722
|
+
type: "function",
|
|
723
|
+
function: {
|
|
724
|
+
name: t.name,
|
|
725
|
+
description: t.description,
|
|
726
|
+
parameters: t.inputSchema
|
|
727
|
+
}
|
|
728
|
+
}));
|
|
729
|
+
}
|
|
730
|
+
/** Call a tool by name */
|
|
731
|
+
async callTool(toolName, args) {
|
|
732
|
+
for (const [, conn] of this.connections) {
|
|
733
|
+
const tool = conn.tools.find((t) => t.name === toolName);
|
|
734
|
+
if (tool) {
|
|
735
|
+
const result = await conn.client.callTool({ name: toolName, arguments: args });
|
|
736
|
+
const texts = [];
|
|
737
|
+
if (Array.isArray(result.content)) {
|
|
738
|
+
for (const item of result.content) {
|
|
739
|
+
if (item.type === "text" && typeof item.text === "string") {
|
|
740
|
+
texts.push(item.text);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
return texts.join("\n") || JSON.stringify(result.content);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
throw new Error(`MCP tool "${toolName}" not found`);
|
|
748
|
+
}
|
|
749
|
+
async disconnect() {
|
|
750
|
+
for (const [name, conn] of this.connections) {
|
|
751
|
+
try {
|
|
752
|
+
await conn.client.close();
|
|
753
|
+
log4.info(`MCP \u670D\u52A1\u5668\u5DF2\u65AD\u5F00: ${name}`);
|
|
754
|
+
} catch {
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
this.connections.clear();
|
|
758
|
+
}
|
|
759
|
+
};
|
|
760
|
+
|
|
598
761
|
// src/gateway.ts
|
|
599
|
-
|
|
762
|
+
import { createServer } from "http";
|
|
763
|
+
var log5 = createLogger("\u7F51\u5173");
|
|
600
764
|
var DEBOUNCE_MS = 1500;
|
|
601
765
|
var Gateway = class {
|
|
602
766
|
channels = /* @__PURE__ */ new Map();
|
|
@@ -608,9 +772,20 @@ var Gateway = class {
|
|
|
608
772
|
processing = /* @__PURE__ */ new Set();
|
|
609
773
|
// Queue for messages that arrive while AI is processing
|
|
610
774
|
queues = /* @__PURE__ */ new Map();
|
|
775
|
+
// Middleware stack
|
|
776
|
+
middlewares = [];
|
|
777
|
+
// Webhook HTTP server
|
|
778
|
+
webhookServer = null;
|
|
779
|
+
// MCP client manager
|
|
780
|
+
mcp = new McpManager();
|
|
611
781
|
constructor(config) {
|
|
612
782
|
this.config = config;
|
|
613
783
|
}
|
|
784
|
+
/** Register a middleware function */
|
|
785
|
+
use(middleware) {
|
|
786
|
+
this.middlewares.push(middleware);
|
|
787
|
+
return this;
|
|
788
|
+
}
|
|
614
789
|
init() {
|
|
615
790
|
for (const [name, chConfig] of Object.entries(this.config.channels)) {
|
|
616
791
|
if (chConfig.enabled === false) continue;
|
|
@@ -619,7 +794,7 @@ var Gateway = class {
|
|
|
619
794
|
this.channels.set(name, new WeixinChannel(chConfig));
|
|
620
795
|
break;
|
|
621
796
|
default:
|
|
622
|
-
|
|
797
|
+
log5.warn(`\u672A\u77E5\u6E20\u9053\u7C7B\u578B: ${chConfig.type}`);
|
|
623
798
|
}
|
|
624
799
|
}
|
|
625
800
|
for (const [name, provConfig] of Object.entries(this.config.providers)) {
|
|
@@ -631,10 +806,10 @@ var Gateway = class {
|
|
|
631
806
|
this.providers.set(name, new OpenAICompatibleProvider(name, provConfig));
|
|
632
807
|
break;
|
|
633
808
|
default:
|
|
634
|
-
|
|
809
|
+
log5.warn(`\u672A\u77E5\u6A21\u578B\u7C7B\u578B: ${provConfig.type}`);
|
|
635
810
|
}
|
|
636
811
|
}
|
|
637
|
-
|
|
812
|
+
log5.info(`\u5DF2\u521D\u59CB\u5316 ${this.channels.size} \u4E2A\u6E20\u9053, ${this.providers.size} \u4E2A\u6A21\u578B`);
|
|
638
813
|
}
|
|
639
814
|
async login(channelName) {
|
|
640
815
|
const channel = this.channels.get(channelName);
|
|
@@ -647,19 +822,32 @@ var Gateway = class {
|
|
|
647
822
|
if (this.providers.size === 0) {
|
|
648
823
|
throw new Error("\u672A\u914D\u7F6E\u4EFB\u4F55\u6A21\u578B");
|
|
649
824
|
}
|
|
825
|
+
if (this.config.mcpServers && Object.keys(this.config.mcpServers).length > 0) {
|
|
826
|
+
await this.mcp.connect(this.config.mcpServers);
|
|
827
|
+
const toolCount = this.mcp.getTools().length;
|
|
828
|
+
if (toolCount > 0) {
|
|
829
|
+
log5.info(`MCP: ${toolCount} \u4E2A\u5DE5\u5177\u5DF2\u5C31\u7EEA`);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
this.startWebhook();
|
|
650
833
|
const startPromises = [...this.channels.entries()].map(([name, channel]) => {
|
|
651
|
-
|
|
834
|
+
log5.info(`\u542F\u52A8\u6E20\u9053: ${name}`);
|
|
652
835
|
return channel.start((msg) => this.handleMessage(msg)).catch((err) => {
|
|
653
|
-
|
|
836
|
+
log5.error(`\u6E20\u9053 ${name} \u5F02\u5E38: ${err instanceof Error ? err.message : err}`);
|
|
654
837
|
});
|
|
655
838
|
});
|
|
656
839
|
await Promise.all(startPromises);
|
|
657
840
|
}
|
|
658
841
|
async stop() {
|
|
659
|
-
|
|
842
|
+
log5.info("\u6B63\u5728\u5173\u95ED...");
|
|
843
|
+
if (this.webhookServer) {
|
|
844
|
+
this.webhookServer.close();
|
|
845
|
+
this.webhookServer = null;
|
|
846
|
+
}
|
|
847
|
+
await this.mcp.disconnect();
|
|
660
848
|
const stops = [...this.channels.values()].map((ch) => ch.stop());
|
|
661
849
|
await Promise.allSettled(stops);
|
|
662
|
-
|
|
850
|
+
log5.info("\u5DF2\u5173\u95ED");
|
|
663
851
|
}
|
|
664
852
|
handleMessage(msg) {
|
|
665
853
|
if (msg.text.startsWith("/")) {
|
|
@@ -671,7 +859,7 @@ var Gateway = class {
|
|
|
671
859
|
const queue = this.queues.get(key) || [];
|
|
672
860
|
queue.push(msg);
|
|
673
861
|
this.queues.set(key, queue);
|
|
674
|
-
|
|
862
|
+
log5.info(`\u6D88\u606F\u5DF2\u6392\u961F (AI\u5904\u7406\u4E2D), \u961F\u5217\u957F\u5EA6: ${queue.length}`);
|
|
675
863
|
return;
|
|
676
864
|
}
|
|
677
865
|
const existing = this.buffers.get(key);
|
|
@@ -704,40 +892,56 @@ var Gateway = class {
|
|
|
704
892
|
if (messages.length === 1) return messages[0];
|
|
705
893
|
const last = messages[messages.length - 1];
|
|
706
894
|
const mergedText = messages.map((m) => m.text).join("\n");
|
|
707
|
-
|
|
895
|
+
log5.info(`\u5408\u5E76 ${messages.length} \u6761\u6D88\u606F`);
|
|
708
896
|
return { ...last, text: mergedText };
|
|
709
897
|
}
|
|
710
898
|
async processMessage(msg) {
|
|
711
899
|
const key = `${msg.channel}:${msg.senderId}`;
|
|
712
900
|
this.processing.add(key);
|
|
713
901
|
try {
|
|
714
|
-
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
902
|
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
903
|
if (!channel) return;
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
904
|
+
const activeSkillName = this.config.userSkills?.[msg.senderId];
|
|
905
|
+
const activeSkill = activeSkillName ? this.config.skills?.[activeSkillName] : void 0;
|
|
906
|
+
const providerName = activeSkill?.provider || this.config.userRoutes?.[msg.senderId] || this.config.defaultProvider;
|
|
907
|
+
const ctx = {
|
|
908
|
+
message: msg,
|
|
909
|
+
provider: providerName,
|
|
910
|
+
channel,
|
|
911
|
+
sessionKey: key,
|
|
912
|
+
state: {}
|
|
913
|
+
};
|
|
914
|
+
const coreHandler = async (c) => {
|
|
915
|
+
const provider = this.providers.get(c.provider);
|
|
916
|
+
if (!provider) {
|
|
917
|
+
log5.error(`\u6A21\u578B "${c.provider}" \u672A\u627E\u5230`);
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
log5.info(`\u8C03\u7528 ${c.provider} \u5904\u7406\u4E2D...`);
|
|
921
|
+
if ("sendTyping" in c.channel) {
|
|
922
|
+
c.channel.sendTyping(c.message.senderId, c.message.replyToken);
|
|
923
|
+
}
|
|
924
|
+
const options = {};
|
|
925
|
+
options.systemPrompt = activeSkill?.systemPrompt || this.config.systemPrompt;
|
|
926
|
+
const mcpTools = this.mcp.getOpenAITools();
|
|
927
|
+
if (mcpTools.length > 0) {
|
|
928
|
+
options.mcpTools = mcpTools;
|
|
929
|
+
options.mcpCallTool = (name, args) => this.mcp.callTool(name, args);
|
|
930
|
+
}
|
|
931
|
+
c.response = await provider.query(c.message.text, c.sessionKey, options);
|
|
932
|
+
};
|
|
933
|
+
await this.compose(ctx, [...this.middlewares, coreHandler]);
|
|
934
|
+
if (ctx.response) {
|
|
935
|
+
await channel.send({
|
|
936
|
+
targetId: msg.senderId,
|
|
937
|
+
text: ctx.response,
|
|
938
|
+
replyToken: msg.replyToken
|
|
939
|
+
});
|
|
940
|
+
log5.info(`\u5DF2\u56DE\u590D (${ctx.response.length} \u5B57\u7B26)`);
|
|
941
|
+
}
|
|
738
942
|
} catch (err) {
|
|
739
943
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
740
|
-
|
|
944
|
+
log5.error(`\u5904\u7406\u6D88\u606F\u5931\u8D25: ${errMsg}`);
|
|
741
945
|
try {
|
|
742
946
|
const channel = this.channels.get(msg.channel);
|
|
743
947
|
if (channel) {
|
|
@@ -753,6 +957,82 @@ var Gateway = class {
|
|
|
753
957
|
this.processing.delete(key);
|
|
754
958
|
}
|
|
755
959
|
}
|
|
960
|
+
async compose(ctx, stack) {
|
|
961
|
+
let index = -1;
|
|
962
|
+
const dispatch = async (i) => {
|
|
963
|
+
if (i <= index) throw new Error("next() called multiple times");
|
|
964
|
+
index = i;
|
|
965
|
+
const fn = stack[i];
|
|
966
|
+
if (!fn) return;
|
|
967
|
+
await fn(ctx, () => dispatch(i + 1));
|
|
968
|
+
};
|
|
969
|
+
await dispatch(0);
|
|
970
|
+
}
|
|
971
|
+
startWebhook() {
|
|
972
|
+
const webhookConfig = this.config.webhook;
|
|
973
|
+
if (!webhookConfig?.enabled) return;
|
|
974
|
+
const port = webhookConfig.port || 4800;
|
|
975
|
+
const secret = webhookConfig.secret;
|
|
976
|
+
this.webhookServer = createServer(async (req, res) => {
|
|
977
|
+
if (req.method !== "POST") {
|
|
978
|
+
res.writeHead(405, { "Content-Type": "application/json" });
|
|
979
|
+
res.end(JSON.stringify({ error: "Method not allowed" }));
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
982
|
+
if (secret && req.headers["authorization"] !== `Bearer ${secret}`) {
|
|
983
|
+
res.writeHead(401, { "Content-Type": "application/json" });
|
|
984
|
+
res.end(JSON.stringify({ error: "Unauthorized" }));
|
|
985
|
+
return;
|
|
986
|
+
}
|
|
987
|
+
let body;
|
|
988
|
+
try {
|
|
989
|
+
body = await new Promise((resolve, reject) => {
|
|
990
|
+
const chunks = [];
|
|
991
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
992
|
+
req.on("end", () => resolve(Buffer.concat(chunks).toString()));
|
|
993
|
+
req.on("error", reject);
|
|
994
|
+
});
|
|
995
|
+
} catch {
|
|
996
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
997
|
+
res.end(JSON.stringify({ error: "Failed to read body" }));
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
let payload;
|
|
1001
|
+
try {
|
|
1002
|
+
payload = JSON.parse(body);
|
|
1003
|
+
} catch {
|
|
1004
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1005
|
+
res.end(JSON.stringify({ error: "Invalid JSON" }));
|
|
1006
|
+
return;
|
|
1007
|
+
}
|
|
1008
|
+
const { channel: channelName, targetId, text } = payload;
|
|
1009
|
+
if (!channelName || !targetId || !text) {
|
|
1010
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1011
|
+
res.end(JSON.stringify({ error: "Missing required fields: channel, targetId, text" }));
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
const channel = this.channels.get(channelName);
|
|
1015
|
+
if (!channel) {
|
|
1016
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1017
|
+
res.end(JSON.stringify({ error: `Channel "${channelName}" not found` }));
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
try {
|
|
1021
|
+
await channel.send({ targetId, text });
|
|
1022
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1023
|
+
res.end(JSON.stringify({ ok: true }));
|
|
1024
|
+
log5.info(`Webhook: \u5DF2\u53D1\u9001\u6D88\u606F\u5230 ${channelName}:${targetId}`);
|
|
1025
|
+
} catch (err) {
|
|
1026
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1027
|
+
log5.error(`Webhook \u53D1\u9001\u5931\u8D25: ${errMsg}`);
|
|
1028
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
1029
|
+
res.end(JSON.stringify({ error: "Failed to send message" }));
|
|
1030
|
+
}
|
|
1031
|
+
});
|
|
1032
|
+
this.webhookServer.listen(port, () => {
|
|
1033
|
+
log5.info(`Webhook \u670D\u52A1\u5DF2\u542F\u52A8: http://localhost:${port}`);
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
756
1036
|
async handleCommand(msg) {
|
|
757
1037
|
const channel = this.channels.get(msg.channel);
|
|
758
1038
|
if (!channel) return;
|
|
@@ -790,12 +1070,58 @@ var Gateway = class {
|
|
|
790
1070
|
}
|
|
791
1071
|
break;
|
|
792
1072
|
}
|
|
1073
|
+
case "/skill": {
|
|
1074
|
+
const skills = this.config.skills || {};
|
|
1075
|
+
const skillNames = Object.keys(skills);
|
|
1076
|
+
if (!arg) {
|
|
1077
|
+
const current = this.config.userSkills?.[msg.senderId] || "\u65E0";
|
|
1078
|
+
const list = skillNames.length > 0 ? skillNames.map((k) => ` ${k} - ${skills[k].description || "\u65E0\u63CF\u8FF0"}`).join("\n") : " (\u672A\u914D\u7F6E\u4EFB\u4F55\u6280\u80FD)";
|
|
1079
|
+
await channel.send({
|
|
1080
|
+
targetId: msg.senderId,
|
|
1081
|
+
text: `\u5F53\u524D\u6280\u80FD: ${current}
|
|
1082
|
+
\u53EF\u7528\u6280\u80FD:
|
|
1083
|
+
${list}
|
|
1084
|
+
\u7528\u6CD5: /skill <\u540D\u79F0> \u6216 /skill off`,
|
|
1085
|
+
replyToken: msg.replyToken
|
|
1086
|
+
});
|
|
1087
|
+
} else if (arg.toLowerCase() === "off") {
|
|
1088
|
+
if (this.config.userSkills) {
|
|
1089
|
+
delete this.config.userSkills[msg.senderId];
|
|
1090
|
+
}
|
|
1091
|
+
await channel.send({
|
|
1092
|
+
targetId: msg.senderId,
|
|
1093
|
+
text: "\u5DF2\u5173\u95ED\u6280\u80FD\uFF0C\u6062\u590D\u9ED8\u8BA4\u6A21\u5F0F",
|
|
1094
|
+
replyToken: msg.replyToken
|
|
1095
|
+
});
|
|
1096
|
+
} else if (skills[arg.toLowerCase()]) {
|
|
1097
|
+
const skillName = arg.toLowerCase();
|
|
1098
|
+
if (!this.config.userSkills) this.config.userSkills = {};
|
|
1099
|
+
this.config.userSkills[msg.senderId] = skillName;
|
|
1100
|
+
const skill = skills[skillName];
|
|
1101
|
+
const info = skill.provider ? `(\u6A21\u578B: ${skill.provider})` : "";
|
|
1102
|
+
await channel.send({
|
|
1103
|
+
targetId: msg.senderId,
|
|
1104
|
+
text: `\u5DF2\u5207\u6362\u5230\u6280\u80FD: ${skillName} ${info}
|
|
1105
|
+
${skill.description || ""}`,
|
|
1106
|
+
replyToken: msg.replyToken
|
|
1107
|
+
});
|
|
1108
|
+
} else {
|
|
1109
|
+
await channel.send({
|
|
1110
|
+
targetId: msg.senderId,
|
|
1111
|
+
text: `\u672A\u77E5\u6280\u80FD: ${arg}
|
|
1112
|
+
\u53EF\u7528: ${skillNames.join(", ") || "\u65E0"}`,
|
|
1113
|
+
replyToken: msg.replyToken
|
|
1114
|
+
});
|
|
1115
|
+
}
|
|
1116
|
+
break;
|
|
1117
|
+
}
|
|
793
1118
|
case "/help": {
|
|
794
1119
|
await channel.send({
|
|
795
1120
|
targetId: msg.senderId,
|
|
796
1121
|
text: [
|
|
797
1122
|
"wechat-ai \u6307\u4EE4:",
|
|
798
1123
|
"/model [\u540D\u79F0] - \u5207\u6362AI\u6A21\u578B",
|
|
1124
|
+
"/skill [\u540D\u79F0] - \u5207\u6362\u6280\u80FD (off \u5173\u95ED)",
|
|
799
1125
|
"/help - \u663E\u793A\u5E2E\u52A9",
|
|
800
1126
|
"/ping - \u68C0\u67E5\u72B6\u6001"
|
|
801
1127
|
].join("\n"),
|
|
@@ -830,6 +1156,7 @@ export {
|
|
|
830
1156
|
WeixinChannel,
|
|
831
1157
|
ClaudeAgentProvider,
|
|
832
1158
|
OpenAICompatibleProvider,
|
|
1159
|
+
McpManager,
|
|
833
1160
|
Gateway
|
|
834
1161
|
};
|
|
835
|
-
//# sourceMappingURL=chunk-
|
|
1162
|
+
//# sourceMappingURL=chunk-57KZRJCI.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/mcp.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 skills: {\n translator: {\n description: \"中英翻译助手\",\n systemPrompt: \"You are a professional translator. Translate Chinese to English and English to Chinese. Only output the translation, no explanations.\",\n },\n coder: {\n description: \"编程助手\",\n systemPrompt: \"You are a senior software engineer. Help with coding questions. Be concise and provide code examples.\",\n },\n writer: {\n description: \"写作助手\",\n systemPrompt: \"You are a skilled writer. Help with writing, editing, and polishing text. Match the user's language.\",\n },\n },\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\" | \"tool\";\n content: string | null;\n tool_calls?: ToolCall[];\n tool_call_id?: string;\n}\n\ninterface ToolCall {\n id: string;\n type: \"function\";\n function: { name: string; arguments: string };\n}\n\ninterface ChatCompletionResponse {\n choices: Array<{\n message: { content: string | null; tool_calls?: ToolCall[] };\n finish_reason: string;\n }>;\n usage?: { prompt_tokens: number; completion_tokens: number };\n}\n\nconst MAX_TOOL_ROUNDS = 10;\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)\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 const tools = options?.mcpTools;\n const callTool = options?.mcpCallTool;\n const hasTools = tools && tools.length > 0 && callTool;\n\n // Tool calling loop\n let reply = \"\";\n for (let round = 0; round < MAX_TOOL_ROUNDS; round++) {\n const body: Record<string, unknown> = {\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 if (hasTools) {\n body.tools = tools;\n }\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(body),\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 choice = data.choices[0];\n if (!choice) throw new Error(`${this.name}: empty response`);\n\n if (data.usage) {\n log.info(`Tokens: ${data.usage.prompt_tokens} in / ${data.usage.completion_tokens} out`);\n }\n\n const assistantMsg = choice.message;\n\n // If no tool calls, we're done\n if (!assistantMsg.tool_calls || assistantMsg.tool_calls.length === 0 || !callTool) {\n reply = assistantMsg.content || \"(No response)\";\n break;\n }\n\n // Add assistant message with tool calls to messages\n messages.push({\n role: \"assistant\",\n content: assistantMsg.content,\n tool_calls: assistantMsg.tool_calls,\n });\n\n // Execute each tool call\n for (const tc of assistantMsg.tool_calls) {\n const fnName = tc.function.name;\n let fnArgs: Record<string, unknown>;\n try {\n fnArgs = JSON.parse(tc.function.arguments);\n } catch {\n fnArgs = {};\n }\n\n log.info(`工具调用: ${fnName}(${JSON.stringify(fnArgs).slice(0, 100)})`);\n\n let toolResult: string;\n try {\n toolResult = await callTool(fnName, fnArgs);\n } catch (err) {\n toolResult = `Error: ${err instanceof Error ? err.message : String(err)}`;\n log.error(`工具调用失败: ${fnName} — ${toolResult}`);\n }\n\n messages.push({\n role: \"tool\",\n content: toolResult,\n tool_call_id: tc.id,\n });\n }\n\n // Continue loop — send tool results back to model\n log.info(`工具调用完成 (round ${round + 1}), 继续处理...`);\n }\n\n // Update history (only user message and final reply, not tool calls)\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 { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { StdioClientTransport } from \"@modelcontextprotocol/sdk/client/stdio.js\";\nimport { SSEClientTransport } from \"@modelcontextprotocol/sdk/client/sse.js\";\nimport { StreamableHTTPClientTransport } from \"@modelcontextprotocol/sdk/client/streamableHttp.js\";\nimport { createLogger } from \"./logger.js\";\nimport type { McpServerConfig } from \"./types.js\";\n\nconst log = createLogger(\"mcp\");\n\nexport interface McpTool {\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n /** Which MCP server provides this tool */\n serverName: string;\n}\n\ninterface McpConnection {\n client: Client;\n transport: StdioClientTransport | SSEClientTransport | StreamableHTTPClientTransport;\n tools: McpTool[];\n}\n\nexport class McpManager {\n private connections = new Map<string, McpConnection>();\n\n async connect(servers: Record<string, McpServerConfig>): Promise<void> {\n const connectPromises = Object.entries(servers).map(async ([name, config]) => {\n try {\n await this.connectServer(name, config);\n log.info(`MCP 服务器已连接: ${name} (${config.transport || \"stdio\"})`);\n } catch (err) {\n const errMsg = err instanceof Error ? err.message : String(err);\n log.error(`MCP 服务器连接失败: ${name} — ${errMsg}`);\n }\n });\n await Promise.all(connectPromises);\n }\n\n private async connectServer(name: string, config: McpServerConfig): Promise<void> {\n let transport: StdioClientTransport | SSEClientTransport | StreamableHTTPClientTransport;\n const transportType = config.transport || \"stdio\";\n\n if (transportType === \"stdio\") {\n if (!config.command) throw new Error(`MCP server \"${name}\": command is required for stdio`);\n transport = new StdioClientTransport({\n command: config.command,\n args: config.args || [],\n env: config.env as Record<string, string> | undefined,\n });\n } else if (transportType === \"sse\") {\n if (!config.url) throw new Error(`MCP server \"${name}\": url is required for sse`);\n transport = new SSEClientTransport(new URL(config.url));\n } else if (transportType === \"streamable-http\") {\n if (!config.url) throw new Error(`MCP server \"${name}\": url is required for streamable-http`);\n transport = new StreamableHTTPClientTransport(new URL(config.url));\n } else {\n throw new Error(`MCP server \"${name}\": unknown transport \"${transportType}\"`);\n }\n\n const client = new Client(\n { name: \"wechat-ai\", version: \"0.1.0\" },\n { capabilities: {} },\n );\n\n await client.connect(transport);\n\n // Discover tools\n const toolsResult = await client.listTools();\n const tools: McpTool[] = (toolsResult.tools || []).map((t) => ({\n name: t.name,\n description: t.description || \"\",\n inputSchema: t.inputSchema as Record<string, unknown>,\n serverName: name,\n }));\n\n log.info(`${name}: 发现 ${tools.length} 个工具`);\n\n this.connections.set(name, { client, transport, tools });\n }\n\n /** Get all available tools across all connected servers */\n getTools(): McpTool[] {\n const allTools: McpTool[] = [];\n for (const conn of this.connections.values()) {\n allTools.push(...conn.tools);\n }\n return allTools;\n }\n\n /** Convert MCP tools to OpenAI function calling format */\n getOpenAITools(): Array<{\n type: \"function\";\n function: { name: string; description: string; parameters: Record<string, unknown> };\n }> {\n return this.getTools().map((t) => ({\n type: \"function\" as const,\n function: {\n name: t.name,\n description: t.description,\n parameters: t.inputSchema,\n },\n }));\n }\n\n /** Call a tool by name */\n async callTool(toolName: string, args: Record<string, unknown>): Promise<string> {\n // Find which server has this tool\n for (const [, conn] of this.connections) {\n const tool = conn.tools.find((t) => t.name === toolName);\n if (tool) {\n const result = await conn.client.callTool({ name: toolName, arguments: args });\n // Extract text from result content\n const texts: string[] = [];\n if (Array.isArray(result.content)) {\n for (const item of result.content) {\n if (item.type === \"text\" && typeof item.text === \"string\") {\n texts.push(item.text);\n }\n }\n }\n return texts.join(\"\\n\") || JSON.stringify(result.content);\n }\n }\n throw new Error(`MCP tool \"${toolName}\" not found`);\n }\n\n async disconnect(): Promise<void> {\n for (const [name, conn] of this.connections) {\n try {\n await conn.client.close();\n log.info(`MCP 服务器已断开: ${name}`);\n } catch {\n // swallow\n }\n }\n this.connections.clear();\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\";\nimport { McpManager } from \"./mcp.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 // MCP client manager\n private mcp = new McpManager();\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 // Connect MCP servers\n if (this.config.mcpServers && Object.keys(this.config.mcpServers).length > 0) {\n await this.mcp.connect(this.config.mcpServers);\n const toolCount = this.mcp.getTools().length;\n if (toolCount > 0) {\n log.info(`MCP: ${toolCount} 个工具已就绪`);\n }\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 await this.mcp.disconnect();\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 channel = this.channels.get(msg.channel);\n if (!channel) return;\n\n // Resolve skill overrides\n const activeSkillName = this.config.userSkills?.[msg.senderId];\n const activeSkill = activeSkillName ? this.config.skills?.[activeSkillName] : undefined;\n\n const providerName = activeSkill?.provider\n || this.config.userRoutes?.[msg.senderId]\n || this.config.defaultProvider;\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 // Skill system prompt takes priority over global\n options.systemPrompt = activeSkill?.systemPrompt || this.config.systemPrompt;\n\n // Pass MCP tools if available\n const mcpTools = this.mcp.getOpenAITools();\n if (mcpTools.length > 0) {\n options.mcpTools = mcpTools;\n options.mcpCallTool = (name, args) => this.mcp.callTool(name, args);\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 \"/skill\": {\n const skills = this.config.skills || {};\n const skillNames = Object.keys(skills);\n\n if (!arg) {\n const current = this.config.userSkills?.[msg.senderId] || \"无\";\n const list = skillNames.length > 0\n ? skillNames.map((k) => ` ${k} - ${skills[k]!.description || \"无描述\"}`).join(\"\\n\")\n : \" (未配置任何技能)\";\n await channel.send({\n targetId: msg.senderId,\n text: `当前技能: ${current}\\n可用技能:\\n${list}\\n用法: /skill <名称> 或 /skill off`,\n replyToken: msg.replyToken,\n });\n } else if (arg.toLowerCase() === \"off\") {\n if (this.config.userSkills) {\n delete this.config.userSkills[msg.senderId];\n }\n await channel.send({\n targetId: msg.senderId,\n text: \"已关闭技能,恢复默认模式\",\n replyToken: msg.replyToken,\n });\n } else if (skills[arg.toLowerCase()]) {\n const skillName = arg.toLowerCase();\n if (!this.config.userSkills) this.config.userSkills = {};\n this.config.userSkills[msg.senderId] = skillName;\n const skill = skills[skillName]!;\n const info = skill.provider ? `(模型: ${skill.provider})` : \"\";\n await channel.send({\n targetId: msg.senderId,\n text: `已切换到技能: ${skillName} ${info}\\n${skill.description || \"\"}`,\n replyToken: msg.replyToken,\n });\n } else {\n await channel.send({\n targetId: msg.senderId,\n text: `未知技能: ${arg}\\n可用: ${skillNames.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 \"/skill [名称] - 切换技能 (off 关闭)\",\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":";AAAA,SAAS,UAAU,WAAW,aAAa;AAC3C,SAAS,kBAAkB;AAC3B,SAAS,eAAe;AACxB,SAAS,YAAY;AAGrB,IAAM,UAAU,KAAK,QAAQ,GAAG,MAAM;AACtC,IAAM,cAAc,KAAK,SAAS,aAAa;AAE/C,IAAM,iBAA4B;AAAA,EAChC,iBAAiB;AAAA,EACjB,WAAW;AAAA,IACT,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,cAAc,CAAC,QAAQ,QAAQ,QAAQ,QAAQ,aAAa,UAAU;AAAA,IACxE;AAAA,IACA,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,SAAS;AAAA,MACT,OAAO;AAAA,MACP,WAAW;AAAA,IACb;AAAA,IACA,UAAU;AAAA,MACR,MAAM;AAAA,MACN,SAAS;AAAA,MACT,OAAO;AAAA,MACP,WAAW;AAAA,IACb;AAAA,IACA,KAAK;AAAA,MACH,MAAM;AAAA,MACN,SAAS;AAAA,MACT,OAAO;AAAA,MACP,WAAW;AAAA,IACb;AAAA,IACA,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,MACT,OAAO;AAAA,MACP,WAAW;AAAA,IACb;AAAA,IACA,SAAS;AAAA,MACP,MAAM;AAAA,MACN,SAAS;AAAA,MACT,OAAO;AAAA,MACP,WAAW;AAAA,IACb;AAAA,IACA,KAAK;AAAA,MACH,MAAM;AAAA,MACN,SAAS;AAAA,MACT,OAAO;AAAA,MACP,WAAW;AAAA,IACb;AAAA,EACF;AAAA,EACA,UAAU;AAAA,IACR,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,cAAc;AAAA,EACd,WAAW;AAAA,EACX,QAAQ;AAAA,IACN,YAAY;AAAA,MACV,aAAa;AAAA,MACb,cAAc;AAAA,IAChB;AAAA,IACA,OAAO;AAAA,MACL,aAAa;AAAA,MACb,cAAc;AAAA,IAChB;AAAA,IACA,QAAQ;AAAA,MACN,aAAa;AAAA,MACb,cAAc;AAAA,IAChB;AAAA,EACF;AACF;AAEA,eAAsB,UAAU,KAAa;AAC3C,MAAI,CAAC,WAAW,GAAG,GAAG;AACpB,UAAM,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,EACtC;AACF;AAEA,eAAsB,aAAiC;AACrD,QAAM,UAAU,OAAO;AAEvB,MAAI,CAAC,WAAW,WAAW,GAAG;AAC5B,UAAM,UAAU,aAAa,KAAK,UAAU,gBAAgB,MAAM,CAAC,CAAC;AACpE,WAAO,EAAE,GAAG,eAAe;AAAA,EAC7B;AAEA,QAAM,MAAM,MAAM,SAAS,aAAa,OAAO;AAC/C,QAAM,OAAO,KAAK,MAAM,GAAG;AAG3B,QAAM,YAAY,EAAE,GAAG,eAAe,UAAU;AAChD,MAAI,KAAK,WAAW;AAClB,eAAW,CAAC,KAAK,GAAG,KAAK,OAAO,QAAQ,KAAK,SAAS,GAAG;AACvD,gBAAU,GAAG,IAAI;AAAA,IACnB;AAAA,EACF;AAEA,QAAM,SAAS,EAAE,GAAG,gBAAgB,GAAG,MAAM,UAAU;AAGvD,MAAI,OAAO,UAAU,OAAO;AAC1B,QAAI,CAAC,OAAO,UAAU,KAAK;AACzB,aAAO,UAAU,MAAM,EAAE,GAAG,OAAO,UAAU,OAAO,WAAW,cAAc;AAAA,IAC/E;AACA,WAAO,OAAO,UAAU;AACxB,QAAI,OAAO,oBAAoB,QAAS,QAAO,kBAAkB;AACjE,UAAM,WAAW,MAAM;AAAA,EACzB;AAEA,SAAO;AACT;AAEA,eAAsB,WAAW,QAAkC;AACjE,QAAM,UAAU,OAAO;AACvB,QAAM,UAAU,aAAa,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAC9D;AAMO,SAAS,iBAAyB;AACvC,SAAO,KAAK,SAAS,UAAU;AACjC;;;AChIA,IAAM,SAAS,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,EAAE;AAGtD,IAAI,eAAsB;AAEnB,SAAS,YAAY,OAAc;AACxC,iBAAe;AACjB;AAEA,SAAS,IAAI,OAAc,OAAe,KAAqB;AAC7D,QAAM,MAAK,oBAAI,KAAK,GAAE,YAAY,EAAE,MAAM,IAAI,EAAE;AAChD,QAAM,MAAM,MAAM,YAAY,EAAE,OAAO,CAAC;AACxC,SAAO,WAAW,EAAE,WAAW,SAAS,OAAO,GAAG,CAAC,aAAa,KAAK,YAAY,GAAG;AACtF;AAEA,SAAS,SAAS,OAAc,MAAsB;AACpD,UAAQ,OAAO;AAAA,IACb,KAAK;AAAS,aAAO,WAAW,IAAI;AAAA,IACpC,KAAK;AAAS,aAAO,WAAW,IAAI;AAAA,IACpC,KAAK;AAAS,aAAO,WAAW,IAAI;AAAA,IACpC,KAAK;AAAS,aAAO,WAAW,IAAI;AAAA,EACtC;AACF;AAEO,SAAS,aAAa,OAAe;AAC1C,SAAO;AAAA,IACL,OAAO,CAAC,QAAgB;AAAE,UAAI,OAAO,YAAY,KAAK,EAAG,SAAQ,IAAI,IAAI,SAAS,OAAO,GAAG,CAAC;AAAA,IAAG;AAAA,IAChG,MAAO,CAAC,QAAgB;AAAE,UAAI,OAAO,YAAY,KAAK,EAAG,SAAQ,IAAI,IAAI,QAAS,OAAO,GAAG,CAAC;AAAA,IAAG;AAAA,IAChG,MAAO,CAAC,QAAgB;AAAE,UAAI,OAAO,YAAY,KAAK,EAAG,SAAQ,KAAK,IAAI,QAAS,OAAO,GAAG,CAAC;AAAA,IAAG;AAAA,IACjG,OAAO,CAAC,QAAgB;AAAE,UAAI,OAAO,YAAY,KAAK,EAAG,SAAQ,MAAM,IAAI,SAAS,OAAO,GAAG,CAAC;AAAA,IAAG;AAAA,EACpG;AACF;;;AC7BA,SAAS,QAAAA,aAAY;AACrB,SAAS,YAAAC,WAAU,aAAAC,kBAAiB;AACpC,SAAS,cAAAC,mBAAkB;AAC3B,SAAS,aAAa,kBAAkB;AAGxC,IAAM,MAAM,aAAa,QAAQ;AAEjC,IAAM,mBAAmB;AACzB,IAAM,kBAAkB;AACxB,IAAM,iBAAiB;AAGvB,IAAM,cAAc,EAAE,MAAM,GAAG,KAAK,EAAE;AACtC,IAAM,eAAe,EAAE,KAAK,GAAG,YAAY,GAAG,QAAQ,EAAE;AACxD,IAAM,kBAAkB,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,EAAE;AAuClE,IAAM,gBAAN,MAAuC;AAAA,EACnC,OAAO;AAAA,EAER,UAAgC;AAAA,EAChC,UAAU;AAAA,EACV,UAAU;AAAA,EACV,kBAA0C;AAAA,EAC1C;AAAA;AAAA,EAEA,gBAAgB,oBAAI,IAAoB;AAAA,EAEhD,YAAY,QAAuB;AACjC,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA,EAIA,MAAM,QAAuB;AAC3B,UAAM,UAAW,KAAK,OAAO,WAAsB;AACnD,QAAI,KAAK,yCAAW;AAEpB,UAAM,QAAQ,MAAM,KAAK,IAAI,SAAS,uCAAuC,MAAM;AAAA,MACjF,QAAQ;AAAA,MACR,SAAS;AAAA,IACX,CAAC;AAED,QAAI,MAAM,QAAQ,GAAG;AACnB,YAAM,IAAI,MAAM,+CAAY,MAAM,UAAU,MAAM,GAAG,EAAE;AAAA,IACzD;AAEA,UAAM,QAAgB,MAAM,sBAAsB,MAAM,MAAM;AAC9D,UAAM,SAAiB,MAAM,UAAU,MAAM,MAAM;AAEnD,QAAI,CAAC,SAAS,CAAC,QAAQ;AACrB,YAAM,IAAI,MAAM,2DAAc,KAAK,UAAU,KAAK,CAAC,EAAE;AAAA,IACvD;AAEA,QAAI,KAAK,yDAAY;AACrB,YAAQ,IAAI;AACZ,QAAI;AACF,YAAM,aAAa,MAAM,OAAO,iBAAiB;AACjD,OAAC,WAAW,WAAW,YAAY,SAAS,OAAO,EAAE,OAAO,KAAK,CAAC;AAAA,IACpE,QAAQ;AACN,cAAQ,IAAI,KAAK,KAAK,EAAE;AAAA,IAC1B;AACA,YAAQ,IAAI;AAEZ,QAAI,KAAK,6BAAS;AAElB,QAAI,WAAW;AACf,WAAO,WAAW,IAAI;AACpB,YAAM,YAAY,MAAM,KAAK;AAAA,QAC3B;AAAA,QACA,sCAAsC,mBAAmB,MAAM,CAAC;AAAA,QAChE;AAAA,QACA,EAAE,QAAQ,OAAO,SAAS,IAAO;AAAA,MACnC;AAEA,YAAM,SAAS,UAAU,MAAM,UAAU,UAAU;AAEnD,UAAI,WAAW,aAAa;AAC1B,cAAM,OAAO,UAAU,QAAQ;AAC/B,cAAM,YAAoB,KAAK,gBAAgB,KAAK;AACpD,cAAM,QAAgB,KAAK,aAAa,KAAK;AAE7C,YAAI,CAAC,aAAa,CAAC,OAAO;AACxB,gBAAM,IAAI,MAAM,wDAAW;AAAA,QAC7B;AAEA,aAAK,UAAU;AAAA,UACb;AAAA,UACA;AAAA,UACA,SAAS,KAAK,WAAW;AAAA,UACzB,QAAQ,KAAK;AAAA,QACf;AAEA,cAAM,KAAK,YAAY;AACvB,YAAI,KAAK,+CAAY,UAAU,MAAM,GAAG,CAAC,CAAC,KAAK;AAC/C;AAAA,MACF;AAEA,UAAI,WAAW,UAAU;AACvB,YAAI,KAAK,qDAAa;AAAA,MACxB;AAEA,UAAI,WAAW,WAAW;AACxB,YAAI,KAAK,sCAAQ;AACjB,cAAM,IAAI,MAAM,sCAAQ;AAAA,MAC1B;AAEA;AACA,YAAM,MAAM,GAAG;AAAA,IACjB;AAEA,UAAM,IAAI,MAAM,0BAAM;AAAA,EACxB;AAAA;AAAA,EAIA,MAAM,MAAM,WAAyD;AACnE,QAAI,CAAC,KAAK,SAAS;AACjB,YAAM,KAAK,YAAY;AAAA,IACzB;AACA,QAAI,CAAC,KAAK,SAAS;AACjB,UAAI,KAAK,iEAAe;AACxB,YAAM,KAAK,MAAM;AAAA,IACnB;AAEA,UAAM,KAAK,YAAY;AACvB,SAAK,UAAU;AACf,QAAI,KAAK,+CAAY,KAAK,QAAS,UAAU,MAAM,GAAG,CAAC,CAAC,MAAM;AAE9D,WAAO,KAAK,SAAS;AACnB,UAAI;AACF,aAAK,kBAAkB,IAAI,gBAAgB;AAC3C,cAAM,MAAM,MAAM,KAAK,WAAW;AAElC,YAAI,IAAI,QAAQ,KAAK;AACnB,cAAI,KAAK,2DAAc;AACvB,eAAK,UAAU;AACf,gBAAM,KAAK,MAAM;AACjB;AAAA,QACF;AAEA,YAAI,IAAI,OAAO,IAAI,QAAQ,GAAG;AAC5B,cAAI,KAAK,yCAAW,IAAI,UAAU,KAAK,UAAU,GAAG,CAAC,EAAE;AACvD,gBAAM,MAAM,GAAI;AAChB;AAAA,QACF;AAEA,YAAI,IAAI,iBAAiB;AACvB,eAAK,UAAU,IAAI;AACnB,gBAAM,KAAK,YAAY;AAAA,QACzB;AAEA,YAAI,IAAI,QAAQ,IAAI,KAAK,SAAS,GAAG;AACnC,qBAAW,OAAO,IAAI,MAAM;AAC1B,kBAAM,OAAO,KAAK,YAAY,GAAG;AACjC,gBAAI,CAAC,QAAQ,CAAC,IAAI,aAAc;AAEhC,gBAAI,KAAK,6BAAS,IAAI,aAAa,MAAM,GAAG,CAAC,CAAC,SAAS,KAAK,MAAM,GAAG,EAAE,CAAC,EAAE;AAC1E,sBAAU;AAAA,cACR,IAAI,OAAO,IAAI,cAAc,IAAI,OAAO,KAAK,IAAI,CAAC;AAAA,cAClD,SAAS;AAAA,cACT,UAAU,IAAI;AAAA,cACd;AAAA,cACA,YAAY,IAAI;AAAA,cAChB,WAAW,IAAI,kBAAkB,KAAK,IAAI;AAAA,YAC5C,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF,SAAS,KAAK;AACZ,YAAI,CAAC,KAAK,QAAS;AACnB,cAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,YAAI,QAAQ,SAAS,SAAS,KAAK,QAAQ,SAAS,YAAY,EAAG;AACnE,YAAI,MAAM,6BAAS,OAAO,EAAE;AAC5B,cAAM,MAAM,GAAI;AAAA,MAClB;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAIA,MAAM,WAAW,QAAgB,cAAsC;AACrE,QAAI,CAAC,KAAK,QAAS;AAEnB,QAAI;AAEF,UAAI,SAAS,KAAK,cAAc,IAAI,MAAM;AAC1C,UAAI,CAAC,QAAQ;AACX,cAAM,YAAY,MAAM,KAAK,IAAI,KAAK,QAAQ,SAAS,uBAAuB;AAAA,UAC5E,eAAe;AAAA,UACf,eAAe;AAAA,UACf,WAAW,EAAE,iBAAiB,gBAAgB;AAAA,QAChD,GAAG,EAAE,SAAS,IAAO,CAAC;AAEtB,iBAAS,UAAU;AACnB,YAAI,QAAQ;AACV,eAAK,cAAc,IAAI,QAAQ,MAAM;AAAA,QACvC;AAAA,MACF;AAEA,UAAI,CAAC,OAAQ;AAEb,YAAM,KAAK,IAAI,KAAK,QAAQ,SAAS,wBAAwB;AAAA,QAC3D,eAAe;AAAA,QACf,eAAe;AAAA,QACf,QAAQ;AAAA,QACR,WAAW,EAAE,iBAAiB,gBAAgB;AAAA,MAChD,GAAG,EAAE,SAAS,IAAO,CAAC;AAEtB,UAAI,MAAM,oDAAY,OAAO,MAAM,GAAG,CAAC,CAAC,KAAK;AAAA,IAC/C,QAAQ;AAAA,IAER;AAAA,EACF;AAAA;AAAA,EAIA,MAAM,KAAK,KAAqC;AAC9C,QAAI,CAAC,KAAK,QAAS,OAAM,IAAI,MAAM,oBAAK;AAExC,UAAM,SAAS,KAAK,UAAU,IAAI,MAAM,GAAI;AAE5C,eAAW,SAAS,QAAQ;AAC1B,YAAM,OAAO;AAAA,QACX,KAAK;AAAA,UACH,cAAc;AAAA,UACd,YAAY,IAAI;AAAA,UAChB,WAAW,iBAAiB;AAAA,UAC5B,cAAc,YAAY;AAAA,UAC1B,eAAe,aAAa;AAAA,UAC5B,eAAe,IAAI,cAAc;AAAA,UACjC,WAAW,CAAC,EAAE,MAAM,gBAAgB,MAAM,WAAW,EAAE,MAAM,MAAM,EAAE,CAAC;AAAA,QACxE;AAAA,QACA,WAAW,EAAE,iBAAiB,gBAAgB;AAAA,MAChD;AAEA,YAAM,MAAM,MAAM,KAAK;AAAA,QACrB,KAAK,QAAQ;AAAA,QACb;AAAA,QACA;AAAA,QACA,EAAE,SAAS,eAAe;AAAA,MAC5B;AAGA,UAAI,IAAI,OAAO,IAAI,QAAQ,GAAG;AAC5B,YAAI,MAAM,6BAAS,IAAI,UAAU,KAAK,UAAU,GAAG,CAAC,EAAE;AAAA,MACxD;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,OAAsB;AAC1B,SAAK,UAAU;AACf,SAAK,iBAAiB,MAAM;AAC5B,QAAI,KAAK,oBAAK;AAAA,EAChB;AAAA;AAAA,EAIA,MAAc,aAA0C;AACtD,QAAI,CAAC,KAAK,QAAS,OAAM,IAAI,MAAM,oBAAK;AAExC,WAAO,KAAK,IAAI,KAAK,QAAQ,SAAS,wBAAwB;AAAA,MAC5D,iBAAiB,KAAK;AAAA,MACtB,WAAW,EAAE,iBAAiB,gBAAgB;AAAA,IAChD,GAAG,EAAE,SAAS,IAAO,CAAC;AAAA,EACxB;AAAA,EAEQ,YAAY,KAAmC;AACrD,QAAI,CAAC,IAAI,WAAW,OAAQ,QAAO;AACnC,eAAW,QAAQ,IAAI,WAAW;AAChC,UAAI,KAAK,SAAS,KAAK,KAAK,WAAW,MAAM;AAC3C,eAAO,KAAK,UAAU;AAAA,MACxB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,UAAU,MAAc,QAA0B;AACxD,QAAI,KAAK,UAAU,OAAQ,QAAO,CAAC,IAAI;AACvC,UAAM,SAAmB,CAAC;AAC1B,QAAI,YAAY;AAChB,WAAO,UAAU,SAAS,GAAG;AAC3B,UAAI,UAAU,UAAU,YAAY,MAAM,MAAM;AAChD,UAAI,WAAW,EAAG,WAAU;AAC5B,aAAO,KAAK,UAAU,MAAM,GAAG,OAAO,CAAC;AACvC,kBAAY,UAAU,MAAM,OAAO;AAAA,IACrC;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,IACZ,SACA,MACA,MACA,OAA8C,CAAC,GACjC;AACd,UAAM,MAAM,GAAG,QAAQ,QAAQ,OAAO,EAAE,CAAC,IAAI,IAAI;AACjD,UAAM,SAAS,KAAK,UAAU;AAC9B,UAAM,UAAU,OAAO,KAAK,UAAU,IAAI,IAAI;AAE9C,UAAM,UAAkC;AAAA,MACtC,gBAAgB;AAAA,IAClB;AAEA,QAAI,KAAK,SAAS,OAAO;AACvB,cAAQ,mBAAmB,IAAI;AAC/B,cAAQ,eAAe,IAAI,UAAU,KAAK,QAAQ,KAAK;AACvD,cAAQ,cAAc,IAAI,UAAU;AACpC,UAAI,SAAS;AACX,gBAAQ,gBAAgB,IAAI,OAAO,OAAO,WAAW,SAAS,OAAO,CAAC;AAAA,MACxE;AAAA,IACF;AAEA,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,WAAW,cAAc;AAEjF,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,KAAK;AAAA,QAC3B;AAAA,QACA;AAAA,QACA,MAAM;AAAA,QACN,QAAQ,WAAW;AAAA,MACrB,CAAC;AACD,aAAO,MAAM,IAAI,KAAK;AAAA,IACxB,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF;AAAA;AAAA,EAIQ,cAAsB;AAC5B,WAAOH,MAAK,eAAe,GAAG,aAAa;AAAA,EAC7C;AAAA,EAEQ,WAAmB;AACzB,WAAOA,MAAK,eAAe,GAAG,kBAAkB;AAAA,EAClD;AAAA,EAEA,MAAc,cAA6B;AACzC,UAAM,UAAU,eAAe,CAAC;AAChC,UAAME,WAAU,KAAK,YAAY,GAAG,KAAK,UAAU,KAAK,SAAS,MAAM,CAAC,CAAC;AAAA,EAC3E;AAAA,EAEA,MAAc,cAA6B;AACzC,UAAM,OAAO,KAAK,YAAY;AAC9B,QAAI,CAACC,YAAW,IAAI,EAAG;AACvB,QAAI;AACF,YAAM,MAAM,MAAMF,UAAS,MAAM,OAAO;AACxC,WAAK,UAAU,KAAK,MAAM,GAAG;AAC7B,UAAI,KAAK,mCAAU,KAAK,QAAS,UAAU,MAAM,GAAG,CAAC,CAAC,KAAK;AAAA,IAC7D,QAAQ;AACN,UAAI,KAAK,sCAAQ;AAAA,IACnB;AAAA,EACF;AAAA,EAEA,MAAc,cAA6B;AACzC,UAAM,UAAU,eAAe,CAAC;AAChC,UAAMC,WAAU,KAAK,SAAS,GAAG,KAAK,UAAU,EAAE,iBAAiB,KAAK,QAAQ,CAAC,CAAC;AAAA,EACpF;AAAA,EAEA,MAAc,cAA6B;AACzC,UAAM,OAAO,KAAK,SAAS;AAC3B,QAAI,CAACC,YAAW,IAAI,EAAG;AACvB,QAAI;AACF,YAAM,MAAM,MAAMF,UAAS,MAAM,OAAO;AACxC,YAAM,OAAO,KAAK,MAAM,GAAG;AAC3B,WAAK,UAAU,KAAK,mBAAmB;AAAA,IACzC,QAAQ;AAAA,IAER;AAAA,EACF;AACF;AAIA,SAAS,YAAoB;AAC3B,QAAM,SAAS,YAAY,CAAC,EAAE,aAAa,CAAC;AAC5C,SAAO,OAAO,KAAK,OAAO,MAAM,GAAG,OAAO,EAAE,SAAS,QAAQ;AAC/D;AAEA,SAAS,mBAA2B;AAClC,SAAO,OAAO,WAAW,EAAE,QAAQ,MAAM,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC;AAC3D;AAEA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAC7C;;;ACtaA,IAAMG,OAAM,aAAa,QAAQ;AAEjC,IAAM,gBAAgB,CAAC,QAAQ,QAAQ,QAAQ,QAAQ,aAAa,UAAU;AAEvE,IAAM,sBAAN,MAA8C;AAAA,EAC1C,OAAO;AAAA,EACR;AAAA,EACA,WAAW,oBAAI,IAAoB;AAAA;AAAA,EAE3C,YAAY,QAAwB;AAClC,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,MAAM,MACJ,QACA,WACA,SACiB;AACjB,UAAM,EAAE,MAAM,IAAI,MAAM,OAAO,gCAAgC;AAE/D,UAAM,eAAe,SAAS,gBACxB,KAAK,OAAO,gBACb;AAEL,UAAM,kBAAkB,KAAK,SAAS,IAAI,SAAS;AACnD,UAAM,aAAsC;AAAA,MAC1C;AAAA,MACA,gBAAgB;AAAA,IAClB;AAEA,QAAI,SAAS,WAAW;AACtB,iBAAW,YAAY,QAAQ;AAAA,IACjC;AAEA,QAAI,SAAS,KAAK;AAChB,iBAAW,MAAM,QAAQ;AAAA,IAC3B;AAGA,QAAI,iBAAiB;AACnB,iBAAW,SAAS;AAAA,IACtB;AAEA,QAAI,SAAS,cAAc;AACzB,iBAAW,eAAe,QAAQ;AAAA,IACpC;AAEA,IAAAA,KAAI,KAAK,6BAA6B,UAAU,MAAM,GAAG,CAAC,CAAC,MAAM;AAEjE,QAAI,SAAS;AACb,QAAI;AAEJ,QAAI;AACF,uBAAiB,WAAW,MAAM;AAAA,QAChC;AAAA,QACA,SAAS;AAAA,MACX,CAAC,GAAG;AAEF,YAAI,cAAc,OAAO,GAAG;AAC1B,yBAAe,QAAQ;AAAA,QACzB;AAGA,YAAI,gBAAgB,OAAO,GAAG;AAC5B,mBAAS,QAAQ;AAAA,QACnB;AAGA,YAAI,mBAAmB,OAAO,GAAG;AAE/B,gBAAM,cAAc,YAAY,OAAO;AACvC,cAAI,aAAa;AACf,qBAAS;AAAA,UACX;AAAA,QACF;AAAA,MACF;AAAA,IACF,SAAS,KAAK;AACZ,YAAM,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC9D,MAAAA,KAAI,MAAM,wBAAwB,MAAM,EAAE;AAC1C,YAAM;AAAA,IACR;AAGA,QAAI,cAAc;AAChB,WAAK,SAAS,IAAI,WAAW,YAAY;AAAA,IAC3C;AAEA,QAAI,CAAC,QAAQ;AACX,eAAS;AAAA,IACX;AAEA,IAAAA,KAAI,KAAK,aAAa,OAAO,MAAM,QAAQ;AAC3C,WAAO;AAAA,EACT;AACF;AAIA,SAAS,cAAc,KAA0E;AAC/F,SAAO,KAAK,SAAS,YAAY,KAAK,YAAY,UAAU,OAAO,KAAK,eAAe;AACzF;AAEA,SAAS,gBAAgB,KAAqC;AAC5D,SAAO,OAAO,KAAK,WAAW;AAChC;AAEA,SAAS,mBAAmB,KAAyE;AACnG,SAAO,KAAK,SAAS,eAAe,KAAK,SAAS;AACpD;AAEA,SAAS,YAAY,KAAyB;AAC5C,MAAI,CAAC,KAAK,SAAS,QAAS,QAAO;AACnC,QAAM,QAAkB,CAAC;AACzB,aAAW,SAAS,IAAI,QAAQ,SAAS;AACvC,QAAI,MAAM,SAAS,UAAU,OAAO,MAAM,SAAS,UAAU;AAC3D,YAAM,KAAK,MAAM,IAAI;AAAA,IACvB;AAAA,EACF;AACA,SAAO,MAAM,SAAS,IAAI,MAAM,KAAK,EAAE,IAAI;AAC7C;;;ACvHA,IAAMC,OAAM,aAAa,eAAe;AAuBxC,IAAM,kBAAkB;AAEjB,IAAM,2BAAN,MAAmD;AAAA,EAC/C;AAAA,EACD;AAAA,EACA,YAAY,oBAAI,IAA2B;AAAA,EAEnD,YAAY,MAAc,QAAwB;AAChD,SAAK,OAAO;AACZ,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,MAAM,MACJ,QACA,WACA,SACiB;AACjB,UAAM,UAAU,KAAK,OAAO;AAC5B,UAAM,SAAS,KAAK,OAAO,UAAU,QAAQ,IAAI,KAAK,OAAO,aAAuB,EAAE;AACtF,UAAM,QAAQ,SAAS,SAAU,KAAK,OAAO;AAE7C,QAAI,CAAC,QAAS,OAAM,IAAI,MAAM,GAAG,KAAK,IAAI,uBAAuB;AACjE,QAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,GAAG,KAAK,IAAI,sBAAsB;AAC/D,QAAI,CAAC,MAAO,OAAM,IAAI,MAAM,GAAG,KAAK,IAAI,qBAAqB;AAG7D,QAAI,UAAU,KAAK,UAAU,IAAI,SAAS;AAC1C,QAAI,CAAC,SAAS;AACZ,gBAAU,CAAC;AACX,WAAK,UAAU,IAAI,WAAW,OAAO;AAAA,IACvC;AAEA,UAAM,WAA0B,CAAC;AAGjC,UAAM,eAAe,SAAS,gBAAiB,KAAK,OAAO;AAC3D,QAAI,cAAc;AAChB,eAAS,KAAK,EAAE,MAAM,UAAU,SAAS,aAAa,CAAC;AAAA,IACzD;AAGA,UAAM,aAAc,KAAK,OAAO,cAAyB;AACzD,UAAM,gBAAgB,QAAQ,MAAM,CAAC,UAAU;AAC/C,aAAS,KAAK,GAAG,aAAa;AAG9B,aAAS,KAAK,EAAE,MAAM,QAAQ,SAAS,OAAO,CAAC;AAE/C,IAAAA,KAAI,KAAK,YAAY,KAAK,IAAI,YAAY,KAAK,cAAc,UAAU,MAAM,GAAG,CAAC,CAAC,MAAM;AAExF,UAAM,MAAM,GAAG,QAAQ,QAAQ,OAAO,EAAE,CAAC;AACzC,UAAM,QAAQ,SAAS;AACvB,UAAM,WAAW,SAAS;AAC1B,UAAM,WAAW,SAAS,MAAM,SAAS,KAAK;AAG9C,QAAI,QAAQ;AACZ,aAAS,QAAQ,GAAG,QAAQ,iBAAiB,SAAS;AACpD,YAAM,OAAgC;AAAA,QACpC;AAAA,QACA;AAAA,QACA,YAAY,SAAS,aAAc,KAAK,OAAO,aAAwB;AAAA,QACvE,aAAc,KAAK,OAAO,eAA0B;AAAA,MACtD;AAEA,UAAI,UAAU;AACZ,aAAK,QAAQ;AAAA,MACf;AAEA,YAAM,MAAM,MAAM,MAAM,KAAK;AAAA,QAC3B,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,iBAAiB,UAAU,MAAM;AAAA,QACnC;AAAA,QACA,MAAM,KAAK,UAAU,IAAI;AAAA,MAC3B,CAAC;AAED,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,UAAU,MAAM,IAAI,KAAK;AAC/B,QAAAA,KAAI,MAAM,GAAG,KAAK,IAAI,cAAc,IAAI,MAAM,KAAK,QAAQ,MAAM,GAAG,GAAG,CAAC,EAAE;AAC1E,cAAM,IAAI,MAAM,GAAG,KAAK,IAAI,eAAe,IAAI,MAAM,EAAE;AAAA,MACzD;AAEA,YAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,YAAM,SAAS,KAAK,QAAQ,CAAC;AAC7B,UAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,GAAG,KAAK,IAAI,kBAAkB;AAE3D,UAAI,KAAK,OAAO;AACd,QAAAA,KAAI,KAAK,WAAW,KAAK,MAAM,aAAa,SAAS,KAAK,MAAM,iBAAiB,MAAM;AAAA,MACzF;AAEA,YAAM,eAAe,OAAO;AAG5B,UAAI,CAAC,aAAa,cAAc,aAAa,WAAW,WAAW,KAAK,CAAC,UAAU;AACjF,gBAAQ,aAAa,WAAW;AAChC;AAAA,MACF;AAGA,eAAS,KAAK;AAAA,QACZ,MAAM;AAAA,QACN,SAAS,aAAa;AAAA,QACtB,YAAY,aAAa;AAAA,MAC3B,CAAC;AAGD,iBAAW,MAAM,aAAa,YAAY;AACxC,cAAM,SAAS,GAAG,SAAS;AAC3B,YAAI;AACJ,YAAI;AACF,mBAAS,KAAK,MAAM,GAAG,SAAS,SAAS;AAAA,QAC3C,QAAQ;AACN,mBAAS,CAAC;AAAA,QACZ;AAEA,QAAAA,KAAI,KAAK,6BAAS,MAAM,IAAI,KAAK,UAAU,MAAM,EAAE,MAAM,GAAG,GAAG,CAAC,GAAG;AAEnE,YAAI;AACJ,YAAI;AACF,uBAAa,MAAM,SAAS,QAAQ,MAAM;AAAA,QAC5C,SAAS,KAAK;AACZ,uBAAa,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AACvE,UAAAA,KAAI,MAAM,yCAAW,MAAM,WAAM,UAAU,EAAE;AAAA,QAC/C;AAEA,iBAAS,KAAK;AAAA,UACZ,MAAM;AAAA,UACN,SAAS;AAAA,UACT,cAAc,GAAG;AAAA,QACnB,CAAC;AAAA,MACH;AAGA,MAAAA,KAAI,KAAK,+CAAiB,QAAQ,CAAC,gCAAY;AAAA,IACjD;AAGA,YAAQ,KAAK,EAAE,MAAM,QAAQ,SAAS,OAAO,CAAC;AAC9C,YAAQ,KAAK,EAAE,MAAM,aAAa,SAAS,MAAM,CAAC;AAElD,IAAAA,KAAI,KAAK,aAAa,MAAM,MAAM,QAAQ;AAC1C,WAAO;AAAA,EACT;AACF;;;AC3KA,SAAS,cAAc;AACvB,SAAS,4BAA4B;AACrC,SAAS,0BAA0B;AACnC,SAAS,qCAAqC;AAI9C,IAAMC,OAAM,aAAa,KAAK;AAgBvB,IAAM,aAAN,MAAiB;AAAA,EACd,cAAc,oBAAI,IAA2B;AAAA,EAErD,MAAM,QAAQ,SAAyD;AACrE,UAAM,kBAAkB,OAAO,QAAQ,OAAO,EAAE,IAAI,OAAO,CAAC,MAAM,MAAM,MAAM;AAC5E,UAAI;AACF,cAAM,KAAK,cAAc,MAAM,MAAM;AACrC,QAAAA,KAAI,KAAK,6CAAe,IAAI,KAAK,OAAO,aAAa,OAAO,GAAG;AAAA,MACjE,SAAS,KAAK;AACZ,cAAM,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC9D,QAAAA,KAAI,MAAM,mDAAgB,IAAI,WAAM,MAAM,EAAE;AAAA,MAC9C;AAAA,IACF,CAAC;AACD,UAAM,QAAQ,IAAI,eAAe;AAAA,EACnC;AAAA,EAEA,MAAc,cAAc,MAAc,QAAwC;AAChF,QAAI;AACJ,UAAM,gBAAgB,OAAO,aAAa;AAE1C,QAAI,kBAAkB,SAAS;AAC7B,UAAI,CAAC,OAAO,QAAS,OAAM,IAAI,MAAM,eAAe,IAAI,kCAAkC;AAC1F,kBAAY,IAAI,qBAAqB;AAAA,QACnC,SAAS,OAAO;AAAA,QAChB,MAAM,OAAO,QAAQ,CAAC;AAAA,QACtB,KAAK,OAAO;AAAA,MACd,CAAC;AAAA,IACH,WAAW,kBAAkB,OAAO;AAClC,UAAI,CAAC,OAAO,IAAK,OAAM,IAAI,MAAM,eAAe,IAAI,4BAA4B;AAChF,kBAAY,IAAI,mBAAmB,IAAI,IAAI,OAAO,GAAG,CAAC;AAAA,IACxD,WAAW,kBAAkB,mBAAmB;AAC9C,UAAI,CAAC,OAAO,IAAK,OAAM,IAAI,MAAM,eAAe,IAAI,wCAAwC;AAC5F,kBAAY,IAAI,8BAA8B,IAAI,IAAI,OAAO,GAAG,CAAC;AAAA,IACnE,OAAO;AACL,YAAM,IAAI,MAAM,eAAe,IAAI,yBAAyB,aAAa,GAAG;AAAA,IAC9E;AAEA,UAAM,SAAS,IAAI;AAAA,MACjB,EAAE,MAAM,aAAa,SAAS,QAAQ;AAAA,MACtC,EAAE,cAAc,CAAC,EAAE;AAAA,IACrB;AAEA,UAAM,OAAO,QAAQ,SAAS;AAG9B,UAAM,cAAc,MAAM,OAAO,UAAU;AAC3C,UAAM,SAAoB,YAAY,SAAS,CAAC,GAAG,IAAI,CAAC,OAAO;AAAA,MAC7D,MAAM,EAAE;AAAA,MACR,aAAa,EAAE,eAAe;AAAA,MAC9B,aAAa,EAAE;AAAA,MACf,YAAY;AAAA,IACd,EAAE;AAEF,IAAAA,KAAI,KAAK,GAAG,IAAI,kBAAQ,MAAM,MAAM,qBAAM;AAE1C,SAAK,YAAY,IAAI,MAAM,EAAE,QAAQ,WAAW,MAAM,CAAC;AAAA,EACzD;AAAA;AAAA,EAGA,WAAsB;AACpB,UAAM,WAAsB,CAAC;AAC7B,eAAW,QAAQ,KAAK,YAAY,OAAO,GAAG;AAC5C,eAAS,KAAK,GAAG,KAAK,KAAK;AAAA,IAC7B;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,iBAGG;AACD,WAAO,KAAK,SAAS,EAAE,IAAI,CAAC,OAAO;AAAA,MACjC,MAAM;AAAA,MACN,UAAU;AAAA,QACR,MAAM,EAAE;AAAA,QACR,aAAa,EAAE;AAAA,QACf,YAAY,EAAE;AAAA,MAChB;AAAA,IACF,EAAE;AAAA,EACJ;AAAA;AAAA,EAGA,MAAM,SAAS,UAAkB,MAAgD;AAE/E,eAAW,CAAC,EAAE,IAAI,KAAK,KAAK,aAAa;AACvC,YAAM,OAAO,KAAK,MAAM,KAAK,CAAC,MAAM,EAAE,SAAS,QAAQ;AACvD,UAAI,MAAM;AACR,cAAM,SAAS,MAAM,KAAK,OAAO,SAAS,EAAE,MAAM,UAAU,WAAW,KAAK,CAAC;AAE7E,cAAM,QAAkB,CAAC;AACzB,YAAI,MAAM,QAAQ,OAAO,OAAO,GAAG;AACjC,qBAAW,QAAQ,OAAO,SAAS;AACjC,gBAAI,KAAK,SAAS,UAAU,OAAO,KAAK,SAAS,UAAU;AACzD,oBAAM,KAAK,KAAK,IAAI;AAAA,YACtB;AAAA,UACF;AAAA,QACF;AACA,eAAO,MAAM,KAAK,IAAI,KAAK,KAAK,UAAU,OAAO,OAAO;AAAA,MAC1D;AAAA,IACF;AACA,UAAM,IAAI,MAAM,aAAa,QAAQ,aAAa;AAAA,EACpD;AAAA,EAEA,MAAM,aAA4B;AAChC,eAAW,CAAC,MAAM,IAAI,KAAK,KAAK,aAAa;AAC3C,UAAI;AACF,cAAM,KAAK,OAAO,MAAM;AACxB,QAAAA,KAAI,KAAK,6CAAe,IAAI,EAAE;AAAA,MAChC,QAAQ;AAAA,MAER;AAAA,IACF;AACA,SAAK,YAAY,MAAM;AAAA,EACzB;AACF;;;AC1IA,SAAS,oBAA4E;AAgBrF,IAAMC,OAAM,aAAa,cAAI;AAE7B,IAAM,cAAc;AAOb,IAAM,UAAN,MAAc;AAAA,EACX,WAAW,oBAAI,IAAqB;AAAA,EACpC,YAAY,oBAAI,IAAsB;AAAA,EACtC;AAAA;AAAA,EAEA,UAAU,oBAAI,IAA2B;AAAA;AAAA,EAEzC,aAAa,oBAAI,IAAY;AAAA;AAAA,EAE7B,SAAS,oBAAI,IAA8B;AAAA;AAAA,EAE3C,cAA4B,CAAC;AAAA;AAAA,EAE7B,gBAA+B;AAAA;AAAA,EAE/B,MAAM,IAAI,WAAW;AAAA,EAE7B,YAAY,QAAmB;AAC7B,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA,EAGA,IAAI,YAA8B;AAChC,SAAK,YAAY,KAAK,UAAU;AAChC,WAAO;AAAA,EACT;AAAA,EAEA,OAAa;AACX,eAAW,CAAC,MAAM,QAAQ,KAAK,OAAO,QAAQ,KAAK,OAAO,QAAQ,GAAG;AACnE,UAAI,SAAS,YAAY,MAAO;AAChC,cAAQ,SAAS,MAAM;AAAA,QACrB,KAAK;AACH,eAAK,SAAS,IAAI,MAAM,IAAI,cAAc,QAAQ,CAAC;AACnD;AAAA,QACF;AACE,UAAAA,KAAI,KAAK,yCAAW,SAAS,IAAI,EAAE;AAAA,MACvC;AAAA,IACF;AAEA,eAAW,CAAC,MAAM,UAAU,KAAK,OAAO,QAAQ,KAAK,OAAO,SAAS,GAAG;AACtE,cAAQ,WAAW,MAAM;AAAA,QACvB,KAAK;AACH,eAAK,UAAU,IAAI,MAAM,IAAI,oBAAoB,UAAU,CAAC;AAC5D;AAAA,QACF,KAAK;AACH,eAAK,UAAU,IAAI,MAAM,IAAI,yBAAyB,MAAM,UAAU,CAAC;AACvE;AAAA,QACF;AACE,UAAAA,KAAI,KAAK,yCAAW,WAAW,IAAI,EAAE;AAAA,MACzC;AAAA,IACF;AAEA,IAAAA,KAAI,KAAK,4BAAQ,KAAK,SAAS,IAAI,wBAAS,KAAK,UAAU,IAAI,qBAAM;AAAA,EACvE;AAAA,EAEA,MAAM,MAAM,aAAoC;AAC9C,UAAM,UAAU,KAAK,SAAS,IAAI,WAAW;AAC7C,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI,MAAM,iBAAO,WAAW,sBAAO;AAAA,IAC3C;AACA,UAAM,QAAQ,MAAM;AAAA,EACtB;AAAA,EAEA,MAAM,QAAuB;AAC3B,QAAI,KAAK,UAAU,SAAS,GAAG;AAC7B,YAAM,IAAI,MAAM,4CAAS;AAAA,IAC3B;AAGA,QAAI,KAAK,OAAO,cAAc,OAAO,KAAK,KAAK,OAAO,UAAU,EAAE,SAAS,GAAG;AAC5E,YAAM,KAAK,IAAI,QAAQ,KAAK,OAAO,UAAU;AAC7C,YAAM,YAAY,KAAK,IAAI,SAAS,EAAE;AACtC,UAAI,YAAY,GAAG;AACjB,QAAAA,KAAI,KAAK,QAAQ,SAAS,uCAAS;AAAA,MACrC;AAAA,IACF;AAEA,SAAK,aAAa;AAElB,UAAM,gBAAgB,CAAC,GAAG,KAAK,SAAS,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,MAAM,OAAO,MAAM;AAC1E,MAAAA,KAAI,KAAK,6BAAS,IAAI,EAAE;AACxB,aAAO,QAAQ,MAAM,CAAC,QAAQ,KAAK,cAAc,GAAG,CAAC,EAAE,MAAM,CAAC,QAAQ;AACpE,QAAAA,KAAI,MAAM,gBAAM,IAAI,kBAAQ,eAAe,QAAQ,IAAI,UAAU,GAAG,EAAE;AAAA,MACxE,CAAC;AAAA,IACH,CAAC;AAED,UAAM,QAAQ,IAAI,aAAa;AAAA,EACjC;AAAA,EAEA,MAAM,OAAsB;AAC1B,IAAAA,KAAI,KAAK,6BAAS;AAClB,QAAI,KAAK,eAAe;AACtB,WAAK,cAAc,MAAM;AACzB,WAAK,gBAAgB;AAAA,IACvB;AACA,UAAM,KAAK,IAAI,WAAW;AAC1B,UAAM,QAAQ,CAAC,GAAG,KAAK,SAAS,OAAO,CAAC,EAAE,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;AAC/D,UAAM,QAAQ,WAAW,KAAK;AAC9B,IAAAA,KAAI,KAAK,oBAAK;AAAA,EAChB;AAAA,EAEQ,cAAc,KAA2B;AAE/C,QAAI,IAAI,KAAK,WAAW,GAAG,GAAG;AAC5B,WAAK,cAAc,GAAG;AACtB;AAAA,IACF;AAEA,UAAM,MAAM,GAAG,IAAI,OAAO,IAAI,IAAI,QAAQ;AAG1C,QAAI,KAAK,WAAW,IAAI,GAAG,GAAG;AAC5B,YAAM,QAAQ,KAAK,OAAO,IAAI,GAAG,KAAK,CAAC;AACvC,YAAM,KAAK,GAAG;AACd,WAAK,OAAO,IAAI,KAAK,KAAK;AAC1B,MAAAA,KAAI,KAAK,oFAAwB,MAAM,MAAM,EAAE;AAC/C;AAAA,IACF;AAGA,UAAM,WAAW,KAAK,QAAQ,IAAI,GAAG;AACrC,QAAI,UAAU;AACZ,mBAAa,SAAS,KAAK;AAC3B,eAAS,SAAS,KAAK,GAAG;AAC1B,eAAS,QAAQ,WAAW,MAAM,KAAK,YAAY,GAAG,GAAG,WAAW;AAAA,IACtE,OAAO;AACL,WAAK,QAAQ,IAAI,KAAK;AAAA,QACpB,UAAU,CAAC,GAAG;AAAA,QACd,OAAO,WAAW,MAAM,KAAK,YAAY,GAAG,GAAG,WAAW;AAAA,MAC5D,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,MAAc,YAAY,KAA4B;AACpD,UAAM,MAAM,KAAK,QAAQ,IAAI,GAAG;AAChC,QAAI,CAAC,OAAO,IAAI,SAAS,WAAW,EAAG;AACvC,SAAK,QAAQ,OAAO,GAAG;AAGvB,UAAM,SAAS,KAAK,cAAc,IAAI,QAAQ;AAC9C,UAAM,KAAK,eAAe,MAAM;AAGhC,UAAM,QAAQ,KAAK,OAAO,IAAI,GAAG;AACjC,QAAI,SAAS,MAAM,SAAS,GAAG;AAC7B,WAAK,OAAO,OAAO,GAAG;AAEtB,iBAAW,OAAO,OAAO;AACvB,aAAK,cAAc,GAAG;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,cAAc,UAA4C;AAChE,QAAI,SAAS,WAAW,EAAG,QAAO,SAAS,CAAC;AAE5C,UAAM,OAAO,SAAS,SAAS,SAAS,CAAC;AACzC,UAAM,aAAa,SAAS,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,IAAI;AACxD,IAAAA,KAAI,KAAK,gBAAM,SAAS,MAAM,qBAAM;AAEpC,WAAO,EAAE,GAAG,MAAM,MAAM,WAAW;AAAA,EACrC;AAAA,EAEA,MAAc,eAAe,KAAoC;AAC/D,UAAM,MAAM,GAAG,IAAI,OAAO,IAAI,IAAI,QAAQ;AAC1C,SAAK,WAAW,IAAI,GAAG;AAEvB,QAAI;AACF,YAAM,UAAU,KAAK,SAAS,IAAI,IAAI,OAAO;AAC7C,UAAI,CAAC,QAAS;AAGd,YAAM,kBAAkB,KAAK,OAAO,aAAa,IAAI,QAAQ;AAC7D,YAAM,cAAc,kBAAkB,KAAK,OAAO,SAAS,eAAe,IAAI;AAE9E,YAAM,eAAe,aAAa,YAC7B,KAAK,OAAO,aAAa,IAAI,QAAQ,KACrC,KAAK,OAAO;AAEjB,YAAM,MAAe;AAAA,QACnB,SAAS;AAAA,QACT,UAAU;AAAA,QACV;AAAA,QACA,YAAY;AAAA,QACZ,OAAO,CAAC;AAAA,MACV;AAGA,YAAM,cAA0B,OAAO,MAAM;AAC3C,cAAM,WAAW,KAAK,UAAU,IAAI,EAAE,QAAQ;AAC9C,YAAI,CAAC,UAAU;AACb,UAAAA,KAAI,MAAM,iBAAO,EAAE,QAAQ,sBAAO;AAClC;AAAA,QACF;AAEA,QAAAA,KAAI,KAAK,gBAAM,EAAE,QAAQ,wBAAS;AAElC,YAAI,gBAAgB,EAAE,SAAS;AAC7B,UAAC,EAAE,QAAgB,WAAW,EAAE,QAAQ,UAAU,EAAE,QAAQ,UAAU;AAAA,QACxE;AAEA,cAAM,UAA2B,CAAC;AAElC,gBAAQ,eAAe,aAAa,gBAAgB,KAAK,OAAO;AAGhE,cAAM,WAAW,KAAK,IAAI,eAAe;AACzC,YAAI,SAAS,SAAS,GAAG;AACvB,kBAAQ,WAAW;AACnB,kBAAQ,cAAc,CAAC,MAAM,SAAS,KAAK,IAAI,SAAS,MAAM,IAAI;AAAA,QACpE;AAEA,UAAE,WAAW,MAAM,SAAS,MAAM,EAAE,QAAQ,MAAM,EAAE,YAAY,OAAO;AAAA,MACzE;AAGA,YAAM,KAAK,QAAQ,KAAK,CAAC,GAAG,KAAK,aAAa,WAAW,CAAC;AAG1D,UAAI,IAAI,UAAU;AAChB,cAAM,QAAQ,KAAK;AAAA,UACjB,UAAU,IAAI;AAAA,UACd,MAAM,IAAI;AAAA,UACV,YAAY,IAAI;AAAA,QAClB,CAAC;AACD,QAAAA,KAAI,KAAK,uBAAQ,IAAI,SAAS,MAAM,gBAAM;AAAA,MAC5C;AAAA,IACF,SAAS,KAAK;AACZ,YAAM,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC9D,MAAAA,KAAI,MAAM,yCAAW,MAAM,EAAE;AAE7B,UAAI;AACF,cAAM,UAAU,KAAK,SAAS,IAAI,IAAI,OAAO;AAC7C,YAAI,SAAS;AACX,gBAAM,QAAQ,KAAK;AAAA,YACjB,UAAU,IAAI;AAAA,YACd,MAAM;AAAA,YACN,YAAY,IAAI;AAAA,UAClB,CAAC;AAAA,QACH;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF,UAAE;AACA,WAAK,WAAW,OAAO,GAAG;AAAA,IAC5B;AAAA,EACF;AAAA,EAEA,MAAc,QAAQ,KAAc,OAAoC;AACtE,QAAI,QAAQ;AACZ,UAAM,WAAW,OAAO,MAA6B;AACnD,UAAI,KAAK,MAAO,OAAM,IAAI,MAAM,8BAA8B;AAC9D,cAAQ;AACR,YAAM,KAAK,MAAM,CAAC;AAClB,UAAI,CAAC,GAAI;AACT,YAAM,GAAG,KAAK,MAAM,SAAS,IAAI,CAAC,CAAC;AAAA,IACrC;AACA,UAAM,SAAS,CAAC;AAAA,EAClB;AAAA,EAEQ,eAAqB;AAC3B,UAAM,gBAAgB,KAAK,OAAO;AAClC,QAAI,CAAC,eAAe,QAAS;AAE7B,UAAM,OAAO,cAAc,QAAQ;AACnC,UAAM,SAAS,cAAc;AAE7B,SAAK,gBAAgB,aAAa,OAAO,KAAsB,QAAwB;AAErF,UAAI,IAAI,WAAW,QAAQ;AACzB,YAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,YAAI,IAAI,KAAK,UAAU,EAAE,OAAO,qBAAqB,CAAC,CAAC;AACvD;AAAA,MACF;AAGA,UAAI,UAAU,IAAI,QAAQ,eAAe,MAAM,UAAU,MAAM,IAAI;AACjE,YAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,YAAI,IAAI,KAAK,UAAU,EAAE,OAAO,eAAe,CAAC,CAAC;AACjD;AAAA,MACF;AAGA,UAAI;AACJ,UAAI;AACF,eAAO,MAAM,IAAI,QAAgB,CAAC,SAAS,WAAW;AACpD,gBAAM,SAAmB,CAAC;AAC1B,cAAI,GAAG,QAAQ,CAAC,UAAkB,OAAO,KAAK,KAAK,CAAC;AACpD,cAAI,GAAG,OAAO,MAAM,QAAQ,OAAO,OAAO,MAAM,EAAE,SAAS,CAAC,CAAC;AAC7D,cAAI,GAAG,SAAS,MAAM;AAAA,QACxB,CAAC;AAAA,MACH,QAAQ;AACN,YAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,YAAI,IAAI,KAAK,UAAU,EAAE,OAAO,sBAAsB,CAAC,CAAC;AACxD;AAAA,MACF;AAEA,UAAI;AACJ,UAAI;AACF,kBAAU,KAAK,MAAM,IAAI;AAAA,MAC3B,QAAQ;AACN,YAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,YAAI,IAAI,KAAK,UAAU,EAAE,OAAO,eAAe,CAAC,CAAC;AACjD;AAAA,MACF;AAEA,YAAM,EAAE,SAAS,aAAa,UAAU,KAAK,IAAI;AACjD,UAAI,CAAC,eAAe,CAAC,YAAY,CAAC,MAAM;AACtC,YAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,YAAI,IAAI,KAAK,UAAU,EAAE,OAAO,mDAAmD,CAAC,CAAC;AACrF;AAAA,MACF;AAEA,YAAM,UAAU,KAAK,SAAS,IAAI,WAAW;AAC7C,UAAI,CAAC,SAAS;AACZ,YAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,YAAI,IAAI,KAAK,UAAU,EAAE,OAAO,YAAY,WAAW,cAAc,CAAC,CAAC;AACvE;AAAA,MACF;AAEA,UAAI;AACF,cAAM,QAAQ,KAAK,EAAE,UAAU,KAAK,CAAC;AACrC,YAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,YAAI,IAAI,KAAK,UAAU,EAAE,IAAI,KAAK,CAAC,CAAC;AACpC,QAAAA,KAAI,KAAK,iDAAmB,WAAW,IAAI,QAAQ,EAAE;AAAA,MACvD,SAAS,KAAK;AACZ,cAAM,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC9D,QAAAA,KAAI,MAAM,qCAAiB,MAAM,EAAE;AACnC,YAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,YAAI,IAAI,KAAK,UAAU,EAAE,OAAO,yBAAyB,CAAC,CAAC;AAAA,MAC7D;AAAA,IACF,CAAC;AAED,SAAK,cAAc,OAAO,MAAM,MAAM;AACpC,MAAAA,KAAI,KAAK,4DAAmC,IAAI,EAAE;AAAA,IACpD,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,cAAc,KAAoC;AAC9D,UAAM,UAAU,KAAK,SAAS,IAAI,IAAI,OAAO;AAC7C,QAAI,CAAC,QAAS;AAEd,UAAM,QAAQ,IAAI,KAAK,KAAK,EAAE,MAAM,KAAK;AACzC,UAAM,MAAM,MAAM,CAAC,EAAG,YAAY;AAClC,UAAM,MAAM,MAAM,CAAC;AAEnB,YAAQ,KAAK;AAAA,MACX,KAAK,UAAU;AACb,YAAI,CAAC,KAAK;AACR,gBAAM,UAAU,KAAK,OAAO,aAAa,IAAI,QAAQ,KAAK,KAAK,OAAO;AACtE,gBAAM,YAAY,CAAC,GAAG,KAAK,UAAU,KAAK,CAAC,EAAE,KAAK,IAAI;AACtD,gBAAM,QAAQ,KAAK;AAAA,YACjB,UAAU,IAAI;AAAA,YACd,MAAM,6BAAS,OAAO;AAAA,4BAAW,SAAS;AAAA;AAAA,YAC1C,YAAY,IAAI;AAAA,UAClB,CAAC;AAAA,QACH,WAAW,KAAK,UAAU,IAAI,IAAI,YAAY,CAAC,GAAG;AAChD,gBAAM,WAAW,IAAI,YAAY;AACjC,cAAI,CAAC,KAAK,OAAO,WAAY,MAAK,OAAO,aAAa,CAAC;AACvD,eAAK,OAAO,WAAW,IAAI,QAAQ,IAAI;AACvC,gBAAM,QAAQ,KAAK;AAAA,YACjB,UAAU,IAAI;AAAA,YACd,MAAM,6BAAS,QAAQ;AAAA,YACvB,YAAY,IAAI;AAAA,UAClB,CAAC;AAAA,QACH,OAAO;AACL,gBAAM,QAAQ,KAAK;AAAA,YACjB,UAAU,IAAI;AAAA,YACd,MAAM,6BAAS,GAAG;AAAA,gBAAS,CAAC,GAAG,KAAK,UAAU,KAAK,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA,YAChE,YAAY,IAAI;AAAA,UAClB,CAAC;AAAA,QACH;AACA;AAAA,MACF;AAAA,MAEA,KAAK,UAAU;AACb,cAAM,SAAS,KAAK,OAAO,UAAU,CAAC;AACtC,cAAM,aAAa,OAAO,KAAK,MAAM;AAErC,YAAI,CAAC,KAAK;AACR,gBAAM,UAAU,KAAK,OAAO,aAAa,IAAI,QAAQ,KAAK;AAC1D,gBAAM,OAAO,WAAW,SAAS,IAC7B,WAAW,IAAI,CAAC,MAAM,KAAK,CAAC,MAAM,OAAO,CAAC,EAAG,eAAe,oBAAK,EAAE,EAAE,KAAK,IAAI,IAC9E;AACJ,gBAAM,QAAQ,KAAK;AAAA,YACjB,UAAU,IAAI;AAAA,YACd,MAAM,6BAAS,OAAO;AAAA;AAAA,EAAY,IAAI;AAAA;AAAA,YACtC,YAAY,IAAI;AAAA,UAClB,CAAC;AAAA,QACH,WAAW,IAAI,YAAY,MAAM,OAAO;AACtC,cAAI,KAAK,OAAO,YAAY;AAC1B,mBAAO,KAAK,OAAO,WAAW,IAAI,QAAQ;AAAA,UAC5C;AACA,gBAAM,QAAQ,KAAK;AAAA,YACjB,UAAU,IAAI;AAAA,YACd,MAAM;AAAA,YACN,YAAY,IAAI;AAAA,UAClB,CAAC;AAAA,QACH,WAAW,OAAO,IAAI,YAAY,CAAC,GAAG;AACpC,gBAAM,YAAY,IAAI,YAAY;AAClC,cAAI,CAAC,KAAK,OAAO,WAAY,MAAK,OAAO,aAAa,CAAC;AACvD,eAAK,OAAO,WAAW,IAAI,QAAQ,IAAI;AACvC,gBAAM,QAAQ,OAAO,SAAS;AAC9B,gBAAM,OAAO,MAAM,WAAW,kBAAQ,MAAM,QAAQ,MAAM;AAC1D,gBAAM,QAAQ,KAAK;AAAA,YACjB,UAAU,IAAI;AAAA,YACd,MAAM,yCAAW,SAAS,IAAI,IAAI;AAAA,EAAK,MAAM,eAAe,EAAE;AAAA,YAC9D,YAAY,IAAI;AAAA,UAClB,CAAC;AAAA,QACH,OAAO;AACL,gBAAM,QAAQ,KAAK;AAAA,YACjB,UAAU,IAAI;AAAA,YACd,MAAM,6BAAS,GAAG;AAAA,gBAAS,WAAW,KAAK,IAAI,KAAK,QAAG;AAAA,YACvD,YAAY,IAAI;AAAA,UAClB,CAAC;AAAA,QACH;AACA;AAAA,MACF;AAAA,MAEA,KAAK,SAAS;AACZ,cAAM,QAAQ,KAAK;AAAA,UACjB,UAAU,IAAI;AAAA,UACd,MAAM;AAAA,YACJ;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF,EAAE,KAAK,IAAI;AAAA,UACX,YAAY,IAAI;AAAA,QAClB,CAAC;AACD;AAAA,MACF;AAAA,MAEA,KAAK,SAAS;AACZ,cAAM,QAAQ,KAAK;AAAA,UACjB,UAAU,IAAI;AAAA,UACd,MAAM,SAAS,KAAK,IAAI,IAAI,IAAI,SAAS;AAAA,UACzC,YAAY,IAAI;AAAA,QAClB,CAAC;AACD;AAAA,MACF;AAAA,MAEA,SAAS;AACP,cAAM,QAAQ,KAAK;AAAA,UACjB,UAAU,IAAI;AAAA,UACd,MAAM,6BAAS,GAAG;AAAA,UAClB,YAAY,IAAI;AAAA,QAClB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;","names":["join","readFile","writeFile","existsSync","log","log","log","log"]}
|
package/dist/cli.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -66,6 +66,17 @@ interface ProviderOptions {
|
|
|
66
66
|
allowedTools?: string[];
|
|
67
67
|
/** Working directory for agent-type providers */
|
|
68
68
|
cwd?: string;
|
|
69
|
+
/** MCP tools in OpenAI function calling format */
|
|
70
|
+
mcpTools?: Array<{
|
|
71
|
+
type: "function";
|
|
72
|
+
function: {
|
|
73
|
+
name: string;
|
|
74
|
+
description: string;
|
|
75
|
+
parameters: Record<string, unknown>;
|
|
76
|
+
};
|
|
77
|
+
}>;
|
|
78
|
+
/** Callback to execute an MCP tool */
|
|
79
|
+
mcpCallTool?: (name: string, args: Record<string, unknown>) => Promise<string>;
|
|
69
80
|
}
|
|
70
81
|
interface Provider {
|
|
71
82
|
readonly name: string;
|
|
@@ -74,6 +85,50 @@ interface Provider {
|
|
|
74
85
|
/** Send a prompt and stream responses */
|
|
75
86
|
stream?(prompt: string, sessionId: string, options?: ProviderOptions): AsyncIterable<ProviderResponse>;
|
|
76
87
|
}
|
|
88
|
+
interface Context {
|
|
89
|
+
/** The inbound message */
|
|
90
|
+
message: InboundMessage;
|
|
91
|
+
/** Set this to override the AI response (skips provider call) */
|
|
92
|
+
response?: string;
|
|
93
|
+
/** The resolved provider name */
|
|
94
|
+
provider: string;
|
|
95
|
+
/** The channel instance */
|
|
96
|
+
channel: Channel;
|
|
97
|
+
/** Session key (channel:senderId) */
|
|
98
|
+
sessionKey: string;
|
|
99
|
+
/** Arbitrary data shared between middlewares */
|
|
100
|
+
state: Record<string, unknown>;
|
|
101
|
+
}
|
|
102
|
+
type NextFunction = () => Promise<void>;
|
|
103
|
+
type Middleware = (ctx: Context, next: NextFunction) => Promise<void>;
|
|
104
|
+
interface McpServerConfig {
|
|
105
|
+
/** Transport type: "stdio" (default), "sse", or "streamable-http" */
|
|
106
|
+
transport?: "stdio" | "sse" | "streamable-http";
|
|
107
|
+
/** Command to run (stdio) */
|
|
108
|
+
command?: string;
|
|
109
|
+
/** Command arguments (stdio) */
|
|
110
|
+
args?: string[];
|
|
111
|
+
/** Environment variables (stdio) */
|
|
112
|
+
env?: Record<string, string>;
|
|
113
|
+
/** Server URL (sse / streamable-http) */
|
|
114
|
+
url?: string;
|
|
115
|
+
}
|
|
116
|
+
interface WebhookConfig {
|
|
117
|
+
/** Enable webhook HTTP server */
|
|
118
|
+
enabled?: boolean;
|
|
119
|
+
/** Port to listen on (default: 4800) */
|
|
120
|
+
port?: number;
|
|
121
|
+
/** Optional secret token for authentication */
|
|
122
|
+
secret?: string;
|
|
123
|
+
}
|
|
124
|
+
interface SkillConfig {
|
|
125
|
+
/** Human-readable description */
|
|
126
|
+
description?: string;
|
|
127
|
+
/** System prompt override */
|
|
128
|
+
systemPrompt: string;
|
|
129
|
+
/** Provider override (optional, falls back to user's current provider) */
|
|
130
|
+
provider?: string;
|
|
131
|
+
}
|
|
77
132
|
interface WaiConfig {
|
|
78
133
|
/** Default AI provider to use */
|
|
79
134
|
defaultProvider: string;
|
|
@@ -83,10 +138,18 @@ interface WaiConfig {
|
|
|
83
138
|
channels: Record<string, ChannelConfig>;
|
|
84
139
|
/** Per-user provider overrides: senderId -> providerName */
|
|
85
140
|
userRoutes?: Record<string, string>;
|
|
141
|
+
/** Per-user active skill: senderId -> skillName */
|
|
142
|
+
userSkills?: Record<string, string>;
|
|
143
|
+
/** Skill presets */
|
|
144
|
+
skills?: Record<string, SkillConfig>;
|
|
86
145
|
/** Global system prompt */
|
|
87
146
|
systemPrompt?: string;
|
|
88
147
|
/** Message chunk size limit */
|
|
89
148
|
chunkSize?: number;
|
|
149
|
+
/** Webhook HTTP server config */
|
|
150
|
+
webhook?: WebhookConfig;
|
|
151
|
+
/** MCP server configurations */
|
|
152
|
+
mcpServers?: Record<string, McpServerConfig>;
|
|
90
153
|
}
|
|
91
154
|
interface ProviderConfig {
|
|
92
155
|
type: string;
|
|
@@ -109,7 +172,12 @@ declare class Gateway {
|
|
|
109
172
|
private buffers;
|
|
110
173
|
private processing;
|
|
111
174
|
private queues;
|
|
175
|
+
private middlewares;
|
|
176
|
+
private webhookServer;
|
|
177
|
+
private mcp;
|
|
112
178
|
constructor(config: WaiConfig);
|
|
179
|
+
/** Register a middleware function */
|
|
180
|
+
use(middleware: Middleware): this;
|
|
113
181
|
init(): void;
|
|
114
182
|
login(channelName: string): Promise<void>;
|
|
115
183
|
start(): Promise<void>;
|
|
@@ -118,6 +186,8 @@ declare class Gateway {
|
|
|
118
186
|
private flushBuffer;
|
|
119
187
|
private mergeMessages;
|
|
120
188
|
private processMessage;
|
|
189
|
+
private compose;
|
|
190
|
+
private startWebhook;
|
|
121
191
|
private handleCommand;
|
|
122
192
|
}
|
|
123
193
|
|
|
@@ -166,4 +236,31 @@ declare class OpenAICompatibleProvider implements Provider {
|
|
|
166
236
|
query(prompt: string, sessionId: string, options?: ProviderOptions): Promise<string>;
|
|
167
237
|
}
|
|
168
238
|
|
|
169
|
-
|
|
239
|
+
interface McpTool {
|
|
240
|
+
name: string;
|
|
241
|
+
description: string;
|
|
242
|
+
inputSchema: Record<string, unknown>;
|
|
243
|
+
/** Which MCP server provides this tool */
|
|
244
|
+
serverName: string;
|
|
245
|
+
}
|
|
246
|
+
declare class McpManager {
|
|
247
|
+
private connections;
|
|
248
|
+
connect(servers: Record<string, McpServerConfig>): Promise<void>;
|
|
249
|
+
private connectServer;
|
|
250
|
+
/** Get all available tools across all connected servers */
|
|
251
|
+
getTools(): McpTool[];
|
|
252
|
+
/** Convert MCP tools to OpenAI function calling format */
|
|
253
|
+
getOpenAITools(): Array<{
|
|
254
|
+
type: "function";
|
|
255
|
+
function: {
|
|
256
|
+
name: string;
|
|
257
|
+
description: string;
|
|
258
|
+
parameters: Record<string, unknown>;
|
|
259
|
+
};
|
|
260
|
+
}>;
|
|
261
|
+
/** Call a tool by name */
|
|
262
|
+
callTool(toolName: string, args: Record<string, unknown>): Promise<string>;
|
|
263
|
+
disconnect(): Promise<void>;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export { type Channel, type ChannelConfig, ClaudeAgentProvider, type Context, Gateway, type InboundMessage, McpManager, type McpServerConfig, type MediaAttachment, type Middleware, type NextFunction, OpenAICompatibleProvider, type OutboundMessage, type Provider, type ProviderConfig, type ProviderOptions, type ProviderResponse, type SkillConfig, type WaiConfig, WeixinChannel, loadConfig, saveConfig };
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ClaudeAgentProvider,
|
|
3
3
|
Gateway,
|
|
4
|
+
McpManager,
|
|
4
5
|
OpenAICompatibleProvider,
|
|
5
6
|
WeixinChannel,
|
|
6
7
|
loadConfig,
|
|
7
8
|
saveConfig
|
|
8
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-57KZRJCI.js";
|
|
9
10
|
export {
|
|
10
11
|
ClaudeAgentProvider,
|
|
11
12
|
Gateway,
|
|
13
|
+
McpManager,
|
|
12
14
|
OpenAICompatibleProvider,
|
|
13
15
|
WeixinChannel,
|
|
14
16
|
loadConfig,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wechat-ai",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "WeChat AI Bot — Bridge WeChat to Claude, Qwen, DeepSeek and more",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
],
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@anthropic-ai/claude-agent-sdk": "^0.2.81",
|
|
48
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
48
49
|
"qrcode-terminal": "^0.12.0"
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|
|
@@ -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"]}
|