xiaozhi-client 0.0.1 → 1.0.2
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 +54 -55
- package/dist/cli.cjs +5 -450
- package/dist/cli.cjs.map +1 -1
- package/dist/configManager.cjs +1 -233
- package/dist/configManager.cjs.map +1 -1
- package/dist/configManager.d.ts +33 -2
- package/dist/mcpCommands.cjs +2 -0
- package/dist/mcpCommands.cjs.map +1 -0
- package/dist/mcpCommands.d.ts +19 -0
- package/dist/mcpPipe.cjs +6 -262
- package/dist/mcpPipe.cjs.map +1 -1
- package/dist/mcpServerProxy.cjs +6 -491
- package/dist/mcpServerProxy.cjs.map +1 -1
- package/docs/CLI.md +189 -0
- package/docs/SettingManager.md +122 -0
- package/docs/images/preview.png +0 -0
- package/package.json +44 -22
- package/templates/hello-world/mcpServers/calculator.js +100 -0
- package/templates/hello-world/mcpServers/datetime.js +384 -0
- package/templates/hello-world/package.json +12 -0
- package/dist/mcpServers/calculator.js +0 -96
- package/dist/mcpServers/datetime.js +0 -354
- /package/dist/{config.default.json → xiaozhi.config.default.json} +0 -0
- /package/{dist/config.json → templates/hello-world/xiaozhi.config.json} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/mcpCommands.ts"],"sourcesContent":["import chalk from \"chalk\";\nimport ora from \"ora\";\nimport { configManager } from \"./configManager.js\";\n\n/**\n * MCP 相关的命令行功能\n */\n\n/**\n * 列出所有 MCP 服务\n */\nexport async function listMcpServers(\n options: { tools?: boolean } = {}\n): Promise<void> {\n const spinner = ora(\"获取 MCP 服务列表...\").start();\n\n try {\n const mcpServers = configManager.getMcpServers();\n const serverNames = Object.keys(mcpServers);\n\n if (serverNames.length === 0) {\n spinner.warn(\"未配置任何 MCP 服务\");\n console.log(\n chalk.yellow(\"💡 提示: 使用 'xiaozhi config' 命令配置 MCP 服务\")\n );\n return;\n }\n\n spinner.succeed(`找到 ${serverNames.length} 个 MCP 服务`);\n\n if (options.tools) {\n // 显示所有服务的工具列表\n console.log();\n console.log(chalk.bold(\"MCP 服务工具列表:\"));\n console.log();\n\n // 表头\n const headers = [\"服务名称\", \"工具名称\", \"工具描述\", \"状态\"];\n const colWidths = [20, 30, 40, 8];\n\n console.log(\n headers\n .map((header, i) => chalk.bold(header.padEnd(colWidths[i])))\n .join(\" | \")\n );\n console.log(headers.map((_, i) => \"-\".repeat(colWidths[i])).join(\"-|-\"));\n\n for (const serverName of serverNames) {\n const toolsConfig = configManager.getServerToolsConfig(serverName);\n const toolNames = Object.keys(toolsConfig);\n\n if (toolNames.length === 0) {\n console.log(\n [\n serverName.padEnd(colWidths[0]),\n chalk.gray(\"(无工具)\").padEnd(colWidths[1]),\n chalk.gray(\"请先启动服务扫描工具\").padEnd(colWidths[2]),\n chalk.gray(\"-\").padEnd(colWidths[3]),\n ].join(\" | \")\n );\n } else {\n let displayServerName = serverName;\n for (const toolName of toolNames) {\n const toolConfig = toolsConfig[toolName];\n const status = toolConfig.enable\n ? chalk.green(\"启用\")\n : chalk.red(\"禁用\");\n const description = (toolConfig.description || \"\").substring(0, 35);\n\n console.log(\n [\n displayServerName.padEnd(colWidths[0]),\n toolName.padEnd(colWidths[1]),\n description.padEnd(colWidths[2]),\n status.padEnd(colWidths[3]),\n ].join(\" | \")\n );\n\n // 只显示第一行服务名称\n displayServerName = \"\";\n }\n }\n }\n } else {\n // 只显示服务列表\n console.log();\n console.log(chalk.bold(\"MCP 服务列表:\"));\n console.log();\n\n for (const serverName of serverNames) {\n const serverConfig = mcpServers[serverName];\n const toolsConfig = configManager.getServerToolsConfig(serverName);\n const toolCount = Object.keys(toolsConfig).length;\n const enabledCount = Object.values(toolsConfig).filter(\n (t) => t.enable !== false\n ).length;\n\n console.log(`${chalk.cyan(\"•\")} ${chalk.bold(serverName)}`);\n console.log(\n ` 命令: ${chalk.gray(serverConfig.command)} ${chalk.gray(serverConfig.args.join(\" \"))}`\n );\n if (toolCount > 0) {\n console.log(\n ` 工具: ${chalk.green(enabledCount)} 启用 / ${chalk.yellow(toolCount)} 总计`\n );\n } else {\n console.log(` 工具: ${chalk.gray(\"未扫描 (请先启动服务)\")}`);\n }\n console.log();\n }\n }\n\n console.log(chalk.gray(\"💡 提示:\"));\n console.log(chalk.gray(\" - 使用 'xiaozhi mcp list --tools' 查看所有工具\"));\n console.log(\n chalk.gray(\" - 使用 'xiaozhi mcp <服务名> list' 查看指定服务的工具\")\n );\n console.log(\n chalk.gray(\n \" - 使用 'xiaozhi mcp <服务名> <工具名> enable/disable' 启用/禁用工具\"\n )\n );\n } catch (error) {\n spinner.fail(\"获取 MCP 服务列表失败\");\n console.error(\n chalk.red(\n `错误: ${error instanceof Error ? error.message : String(error)}`\n )\n );\n process.exit(1);\n }\n}\n\n/**\n * 列出指定服务的工具\n */\nexport async function listServerTools(serverName: string): Promise<void> {\n const spinner = ora(`获取 ${serverName} 服务的工具列表...`).start();\n\n try {\n const mcpServers = configManager.getMcpServers();\n\n if (!mcpServers[serverName]) {\n spinner.fail(`服务 '${serverName}' 不存在`);\n console.log(\n chalk.yellow(\"💡 提示: 使用 'xiaozhi mcp list' 查看所有可用服务\")\n );\n return;\n }\n\n const toolsConfig = configManager.getServerToolsConfig(serverName);\n const toolNames = Object.keys(toolsConfig);\n\n if (toolNames.length === 0) {\n spinner.warn(`服务 '${serverName}' 暂无工具信息`);\n console.log(chalk.yellow(\"💡 提示: 请先启动服务以扫描工具列表\"));\n return;\n }\n\n spinner.succeed(`服务 '${serverName}' 共有 ${toolNames.length} 个工具`);\n\n console.log();\n console.log(chalk.bold(`${serverName} 服务工具列表:`));\n console.log();\n\n // 表头\n const headers = [\"工具名称\", \"工具描述\", \"状态\"];\n const colWidths = [30, 50, 8];\n\n console.log(\n headers\n .map((header, i) => chalk.bold(header.padEnd(colWidths[i])))\n .join(\" | \")\n );\n console.log(headers.map((_, i) => \"-\".repeat(colWidths[i])).join(\"-|-\"));\n\n for (const toolName of toolNames) {\n const toolConfig = toolsConfig[toolName];\n const status = toolConfig.enable\n ? chalk.green(\"启用\")\n : chalk.red(\"禁用\");\n const description = (toolConfig.description || \"\").substring(0, 45);\n\n console.log(\n [\n toolName.padEnd(colWidths[0]),\n description.padEnd(colWidths[1]),\n status.padEnd(colWidths[2]),\n ].join(\" | \")\n );\n }\n\n console.log();\n console.log(chalk.gray(\"💡 提示:\"));\n console.log(\n chalk.gray(\n ` - 使用 'xiaozhi mcp ${serverName} <工具名> enable' 启用工具`\n )\n );\n console.log(\n chalk.gray(\n ` - 使用 'xiaozhi mcp ${serverName} <工具名> disable' 禁用工具`\n )\n );\n } catch (error) {\n spinner.fail(\"获取工具列表失败\");\n console.error(\n chalk.red(\n `错误: ${error instanceof Error ? error.message : String(error)}`\n )\n );\n process.exit(1);\n }\n}\n\n/**\n * 启用或禁用工具\n */\nexport async function setToolEnabled(\n serverName: string,\n toolName: string,\n enabled: boolean\n): Promise<void> {\n const action = enabled ? \"启用\" : \"禁用\";\n const spinner = ora(`${action}工具 ${serverName}/${toolName}...`).start();\n\n try {\n const mcpServers = configManager.getMcpServers();\n\n if (!mcpServers[serverName]) {\n spinner.fail(`服务 '${serverName}' 不存在`);\n console.log(\n chalk.yellow(\"💡 提示: 使用 'xiaozhi mcp list' 查看所有可用服务\")\n );\n return;\n }\n\n const toolsConfig = configManager.getServerToolsConfig(serverName);\n\n if (!toolsConfig[toolName]) {\n spinner.fail(`工具 '${toolName}' 在服务 '${serverName}' 中不存在`);\n console.log(\n chalk.yellow(\n `💡 提示: 使用 'xiaozhi mcp ${serverName} list' 查看该服务的所有工具`\n )\n );\n return;\n }\n\n // 更新工具状态\n configManager.setToolEnabled(\n serverName,\n toolName,\n enabled,\n toolsConfig[toolName].description\n );\n\n spinner.succeed(\n `成功${action}工具 ${chalk.cyan(serverName)}/${chalk.cyan(toolName)}`\n );\n\n console.log();\n console.log(chalk.gray(\"💡 提示: 工具状态更改将在下次启动服务时生效\"));\n } catch (error) {\n spinner.fail(`${action}工具失败`);\n console.error(\n chalk.red(\n `错误: ${error instanceof Error ? error.message : String(error)}`\n )\n );\n process.exit(1);\n }\n}\n"],"mappings":"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","names":["mcpCommands_exports","__export","listMcpServers","listServerTools","setToolEnabled","__toCommonJS","import_chalk","import_ora","import_configManager","listMcpServers","options","spinner","ora","mcpServers","serverNames","chalk","headers","colWidths","header","i","_","serverName","toolsConfig","toolNames","displayServerName","toolName","toolConfig","status","description","serverConfig","toolCount","enabledCount","t","error","__name","listServerTools","setToolEnabled","enabled","action"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP 相关的命令行功能
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* 列出所有 MCP 服务
|
|
6
|
+
*/
|
|
7
|
+
declare function listMcpServers(options?: {
|
|
8
|
+
tools?: boolean;
|
|
9
|
+
}): Promise<void>;
|
|
10
|
+
/**
|
|
11
|
+
* 列出指定服务的工具
|
|
12
|
+
*/
|
|
13
|
+
declare function listServerTools(serverName: string): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* 启用或禁用工具
|
|
16
|
+
*/
|
|
17
|
+
declare function setToolEnabled(serverName: string, toolName: string, enabled: boolean): Promise<void>;
|
|
18
|
+
|
|
19
|
+
export { listMcpServers, listServerTools, setToolEnabled };
|
package/dist/mcpPipe.cjs
CHANGED
|
@@ -1,264 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
-
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
19
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
20
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
21
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
22
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
23
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
24
|
-
mod
|
|
25
|
-
));
|
|
26
|
-
var import_ws = __toESM(require("ws"));
|
|
27
|
-
var import_child_process = require("child_process");
|
|
28
|
-
var import_dotenv = require("dotenv");
|
|
29
|
-
var import_process = __toESM(require("process"));
|
|
30
|
-
var import_configManager = require("./configManager.cjs");
|
|
31
|
-
(0, import_dotenv.config)();
|
|
32
|
-
class Logger {
|
|
33
|
-
static {
|
|
34
|
-
__name(this, "Logger");
|
|
35
|
-
}
|
|
36
|
-
name;
|
|
37
|
-
constructor(name) {
|
|
38
|
-
this.name = name;
|
|
39
|
-
}
|
|
40
|
-
info(message) {
|
|
41
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
42
|
-
console.error(`${timestamp} - ${this.name} - INFO - ${message}`);
|
|
43
|
-
}
|
|
44
|
-
error(message) {
|
|
45
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
46
|
-
console.error(`${timestamp} - ${this.name} - ERROR - ${message}`);
|
|
47
|
-
}
|
|
48
|
-
warning(message) {
|
|
49
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
50
|
-
console.error(`${timestamp} - ${this.name} - WARNING - ${message}`);
|
|
51
|
-
}
|
|
52
|
-
debug(message) {
|
|
53
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
54
|
-
console.error(`${timestamp} - ${this.name} - DEBUG - ${message}`);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
const logger = new Logger("MCP_PIPE");
|
|
58
|
-
const INITIAL_BACKOFF = 1e3;
|
|
59
|
-
const MAX_BACKOFF = 3e4;
|
|
60
|
-
let reconnectAttempt = 0;
|
|
61
|
-
let backoff = INITIAL_BACKOFF;
|
|
62
|
-
class MCPPipe {
|
|
63
|
-
static {
|
|
64
|
-
__name(this, "MCPPipe");
|
|
65
|
-
}
|
|
66
|
-
mcpScript;
|
|
67
|
-
endpointUrl;
|
|
68
|
-
process;
|
|
69
|
-
websocket;
|
|
70
|
-
shouldReconnect;
|
|
71
|
-
isConnected;
|
|
72
|
-
shutdownResolve;
|
|
73
|
-
constructor(mcpScript, endpointUrl) {
|
|
74
|
-
this.mcpScript = mcpScript;
|
|
75
|
-
this.endpointUrl = endpointUrl;
|
|
76
|
-
this.process = null;
|
|
77
|
-
this.websocket = null;
|
|
78
|
-
this.shouldReconnect = true;
|
|
79
|
-
this.isConnected = false;
|
|
80
|
-
}
|
|
81
|
-
async start() {
|
|
82
|
-
this.startMCPProcess();
|
|
83
|
-
await this.connectToServer();
|
|
84
|
-
return new Promise((resolve) => {
|
|
85
|
-
this.shutdownResolve = resolve;
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
async connectToServer() {
|
|
89
|
-
if (this.isConnected) {
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
logger.info("Connecting to WebSocket server...");
|
|
93
|
-
this.websocket = new import_ws.default(this.endpointUrl);
|
|
94
|
-
this.websocket.on("open", () => {
|
|
95
|
-
logger.info("Successfully connected to WebSocket server");
|
|
96
|
-
this.isConnected = true;
|
|
97
|
-
reconnectAttempt = 0;
|
|
98
|
-
backoff = INITIAL_BACKOFF;
|
|
99
|
-
});
|
|
100
|
-
this.websocket.on("message", (data) => {
|
|
101
|
-
const message = data.toString();
|
|
102
|
-
logger.debug(`<< ${message.substring(0, 120)}...`);
|
|
103
|
-
if (this.process && this.process.stdin && !this.process.stdin.destroyed) {
|
|
104
|
-
this.process.stdin.write(message + "\n");
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
this.websocket.on("close", (code, reason) => {
|
|
108
|
-
logger.error(`WebSocket connection closed: ${code} ${reason}`);
|
|
109
|
-
this.isConnected = false;
|
|
110
|
-
this.websocket = null;
|
|
111
|
-
if (this.shouldReconnect && code !== 4004) {
|
|
112
|
-
this.scheduleReconnect();
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
this.websocket.on("error", (error) => {
|
|
116
|
-
logger.error(`WebSocket error: ${error.message}`);
|
|
117
|
-
this.isConnected = false;
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
scheduleReconnect() {
|
|
121
|
-
if (!this.shouldReconnect) return;
|
|
122
|
-
reconnectAttempt++;
|
|
123
|
-
const waitTime = Math.min(backoff * Math.pow(2, reconnectAttempt - 1), MAX_BACKOFF);
|
|
124
|
-
logger.info(`Scheduling reconnection attempt ${reconnectAttempt} in ${(waitTime / 1e3).toFixed(2)} seconds...`);
|
|
125
|
-
setTimeout(() => {
|
|
126
|
-
if (this.shouldReconnect) {
|
|
127
|
-
this.connectToServer();
|
|
128
|
-
}
|
|
129
|
-
}, waitTime);
|
|
130
|
-
}
|
|
131
|
-
startMCPProcess() {
|
|
132
|
-
if (this.process) {
|
|
133
|
-
logger.info(`${this.mcpScript} process already running`);
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
logger.info(`Starting ${this.mcpScript} process`);
|
|
137
|
-
this.process = (0, import_child_process.spawn)("node", [this.mcpScript], {
|
|
138
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
139
|
-
});
|
|
140
|
-
this.process.stdout?.on("data", (data) => {
|
|
141
|
-
const message = data.toString();
|
|
142
|
-
logger.debug(`>> ${message.substring(0, 120)}...`);
|
|
143
|
-
if (this.websocket && this.websocket.readyState === import_ws.default.OPEN) {
|
|
144
|
-
this.websocket.send(message);
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
this.process.stderr?.on("data", (data) => {
|
|
148
|
-
import_process.default.stderr.write(data);
|
|
149
|
-
});
|
|
150
|
-
this.process.on("exit", (code, signal) => {
|
|
151
|
-
logger.info(`${this.mcpScript} process exited with code ${code}, signal ${signal}`);
|
|
152
|
-
this.process = null;
|
|
153
|
-
this.shouldReconnect = false;
|
|
154
|
-
if (this.websocket) {
|
|
155
|
-
this.websocket.close();
|
|
156
|
-
}
|
|
157
|
-
});
|
|
158
|
-
this.process.on("error", (error) => {
|
|
159
|
-
logger.error(`Process error: ${error.message}`);
|
|
160
|
-
this.process = null;
|
|
161
|
-
this.shouldReconnect = false;
|
|
162
|
-
if (this.websocket) {
|
|
163
|
-
this.websocket.close();
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
cleanup() {
|
|
168
|
-
if (this.process) {
|
|
169
|
-
logger.info(`Terminating ${this.mcpScript} process`);
|
|
170
|
-
try {
|
|
171
|
-
this.process.kill("SIGTERM");
|
|
172
|
-
setTimeout(() => {
|
|
173
|
-
if (this.process && !this.process.killed) {
|
|
174
|
-
this.process.kill("SIGKILL");
|
|
175
|
-
}
|
|
176
|
-
}, 5e3);
|
|
177
|
-
} catch (error) {
|
|
178
|
-
logger.error(`Error terminating process: ${error instanceof Error ? error.message : String(error)}`);
|
|
179
|
-
}
|
|
180
|
-
this.process = null;
|
|
181
|
-
}
|
|
182
|
-
if (this.websocket) {
|
|
183
|
-
this.websocket = null;
|
|
184
|
-
}
|
|
185
|
-
this.isConnected = false;
|
|
186
|
-
}
|
|
187
|
-
sleep(ms) {
|
|
188
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
189
|
-
}
|
|
190
|
-
shutdown() {
|
|
191
|
-
logger.info("Shutting down MCP Pipe...");
|
|
192
|
-
this.shouldReconnect = false;
|
|
193
|
-
this.cleanup();
|
|
194
|
-
if (this.websocket) {
|
|
195
|
-
this.websocket.close();
|
|
196
|
-
}
|
|
197
|
-
if (this.shutdownResolve) {
|
|
198
|
-
this.shutdownResolve();
|
|
199
|
-
}
|
|
200
|
-
import_process.default.exit(0);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
function setupSignalHandlers(mcpPipe) {
|
|
204
|
-
import_process.default.on("SIGINT", () => {
|
|
205
|
-
logger.info("Received interrupt signal, shutting down...");
|
|
206
|
-
mcpPipe.shutdown();
|
|
207
|
-
});
|
|
208
|
-
import_process.default.on("SIGTERM", () => {
|
|
209
|
-
logger.info("Received terminate signal, shutting down...");
|
|
210
|
-
mcpPipe.shutdown();
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
__name(setupSignalHandlers, "setupSignalHandlers");
|
|
214
|
-
async function main() {
|
|
215
|
-
if (import_process.default.argv.length < 3) {
|
|
216
|
-
logger.error("Usage: node mcp_pipe.js <mcp_script>");
|
|
217
|
-
import_process.default.exit(1);
|
|
218
|
-
}
|
|
219
|
-
const mcpScript = import_process.default.argv[2];
|
|
220
|
-
let endpointUrl;
|
|
221
|
-
try {
|
|
222
|
-
if (import_configManager.configManager.configExists()) {
|
|
223
|
-
endpointUrl = import_configManager.configManager.getMcpEndpoint();
|
|
224
|
-
logger.info("\u4F7F\u7528\u914D\u7F6E\u6587\u4EF6\u4E2D\u7684 MCP \u7AEF\u70B9");
|
|
225
|
-
} else {
|
|
226
|
-
endpointUrl = import_process.default.env.MCP_ENDPOINT || "";
|
|
227
|
-
if (!endpointUrl) {
|
|
228
|
-
logger.error("\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728\u4E14\u672A\u8BBE\u7F6E MCP_ENDPOINT \u73AF\u5883\u53D8\u91CF");
|
|
229
|
-
logger.error('\u8BF7\u8FD0\u884C "xiaozhi init" \u521D\u59CB\u5316\u914D\u7F6E\uFF0C\u6216\u8BBE\u7F6E MCP_ENDPOINT \u73AF\u5883\u53D8\u91CF');
|
|
230
|
-
import_process.default.exit(1);
|
|
231
|
-
}
|
|
232
|
-
logger.info("\u4F7F\u7528\u73AF\u5883\u53D8\u91CF\u4E2D\u7684 MCP \u7AEF\u70B9\uFF08\u5EFA\u8BAE\u4F7F\u7528\u914D\u7F6E\u6587\u4EF6\uFF09");
|
|
233
|
-
}
|
|
234
|
-
} catch (error) {
|
|
235
|
-
logger.error(`\u8BFB\u53D6\u914D\u7F6E\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`);
|
|
236
|
-
endpointUrl = import_process.default.env.MCP_ENDPOINT || "";
|
|
237
|
-
if (!endpointUrl) {
|
|
238
|
-
logger.error('\u8BF7\u8FD0\u884C "xiaozhi init" \u521D\u59CB\u5316\u914D\u7F6E\uFF0C\u6216\u8BBE\u7F6E MCP_ENDPOINT \u73AF\u5883\u53D8\u91CF');
|
|
239
|
-
import_process.default.exit(1);
|
|
240
|
-
}
|
|
241
|
-
logger.info("\u4F7F\u7528\u73AF\u5883\u53D8\u91CF\u4E2D\u7684 MCP \u7AEF\u70B9\u4F5C\u4E3A\u5907\u7528\u65B9\u6848");
|
|
242
|
-
}
|
|
243
|
-
if (!endpointUrl || endpointUrl.includes("<\u8BF7\u586B\u5199")) {
|
|
244
|
-
logger.error("MCP \u7AEF\u70B9\u672A\u914D\u7F6E\u6216\u914D\u7F6E\u65E0\u6548");
|
|
245
|
-
logger.error('\u8BF7\u8FD0\u884C "xiaozhi config mcpEndpoint <your-endpoint-url>" \u8BBE\u7F6E\u7AEF\u70B9');
|
|
246
|
-
import_process.default.exit(1);
|
|
247
|
-
}
|
|
248
|
-
const mcpPipe = new MCPPipe(mcpScript, endpointUrl);
|
|
249
|
-
setupSignalHandlers(mcpPipe);
|
|
250
|
-
try {
|
|
251
|
-
await mcpPipe.start();
|
|
252
|
-
} catch (error) {
|
|
253
|
-
logger.error(`Program execution error: ${error instanceof Error ? error.message : String(error)}`);
|
|
254
|
-
import_process.default.exit(1);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
__name(main, "main");
|
|
258
|
-
if (require.main === module) {
|
|
259
|
-
main().catch((error) => {
|
|
260
|
-
logger.error(`Unhandled error: ${error instanceof Error ? error.message : String(error)}`);
|
|
261
|
-
import_process.default.exit(1);
|
|
262
|
-
});
|
|
263
|
-
}
|
|
2
|
+
"use strict";var w=Object.create;var p=Object.defineProperty;var S=Object.getOwnPropertyDescriptor;var b=Object.getOwnPropertyNames;var P=Object.getPrototypeOf,$=Object.prototype.hasOwnProperty;var c=(i,e)=>p(i,"name",{value:e,configurable:!0});var v=(i,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of b(e))!$.call(i,n)&&n!==t&&p(i,n,{get:()=>e[n],enumerable:!(r=S(e,n))||r.enumerable});return i};var d=(i,e,t)=>(t=i!=null?w(P(i)):{},v(e||!i||!i.__esModule?p(t,"default",{value:i,enumerable:!0}):t,i));var u=require("node:child_process"),o=d(require("node:process")),m=require("dotenv"),l=d(require("ws")),a=require("./configManager.cjs");(0,m.config)();class C{static{c(this,"Logger")}name;constructor(e){this.name=e}info(e){const t=new Date().toISOString();console.error(`${t} - ${this.name} - INFO - ${e}`)}error(e){const t=new Date().toISOString();console.error(`${t} - ${this.name} - ERROR - ${e}`)}warning(e){const t=new Date().toISOString();console.error(`${t} - ${this.name} - WARNING - ${e}`)}debug(e){const t=new Date().toISOString();console.error(`${t} - ${this.name} - DEBUG - ${e}`)}}const s=new C("MCP_PIPE"),g=1e3,k=3e4;let h=0,f=g;class I{static{c(this,"MCPPipe")}mcpScript;endpointUrl;process;websocket;shouldReconnect;isConnected;shutdownResolve;constructor(e,t){this.mcpScript=e,this.endpointUrl=t,this.process=null,this.websocket=null,this.shouldReconnect=!0,this.isConnected=!1}async start(){return this.startMCPProcess(),await this.connectToServer(),new Promise(e=>{this.shutdownResolve=e})}async connectToServer(){this.isConnected||(s.info("Connecting to WebSocket server..."),this.websocket=new l.default(this.endpointUrl),this.websocket.on("open",()=>{s.info("Successfully connected to WebSocket server"),this.isConnected=!0,h=0,f=g}),this.websocket.on("message",e=>{const t=e.toString();s.debug(`<< ${t.substring(0,120)}...`),this.process?.stdin&&!this.process.stdin.destroyed&&this.process.stdin.write(`${t}
|
|
3
|
+
`)}),this.websocket.on("close",(e,t)=>{s.error(`WebSocket connection closed: ${e} ${t}`),this.isConnected=!1,this.websocket=null,this.shouldReconnect&&e!==4004&&this.scheduleReconnect()}),this.websocket.on("error",e=>{s.error(`WebSocket error: ${e.message}`),this.isConnected=!1}))}scheduleReconnect(){if(!this.shouldReconnect)return;h++;const e=Math.min(f*2**(h-1),k);s.info(`Scheduling reconnection attempt ${h} in ${(e/1e3).toFixed(2)} seconds...`),setTimeout(()=>{this.shouldReconnect&&this.connectToServer()},e)}startMCPProcess(){if(this.process){s.info(`${this.mcpScript} process already running`);return}s.info(`Starting ${this.mcpScript} process`),this.process=(0,u.spawn)("node",[this.mcpScript],{stdio:["pipe","pipe","pipe"]}),this.process.stdout?.on("data",e=>{const t=e.toString();s.debug(`>> ${t.substring(0,120)}...`),this.websocket&&this.websocket.readyState===l.default.OPEN&&this.websocket.send(t)}),this.process.stderr?.on("data",e=>{o.default.stderr.write(e)}),this.process.on("exit",(e,t)=>{s.info(`${this.mcpScript} process exited with code ${e}, signal ${t}`),this.process=null,this.shouldReconnect=!1,this.websocket&&this.websocket.close()}),this.process.on("error",e=>{s.error(`Process error: ${e.message}`),this.process=null,this.shouldReconnect=!1,this.websocket&&this.websocket.close()})}cleanup(){if(this.process){s.info(`Terminating ${this.mcpScript} process`);try{this.process.kill("SIGTERM"),setTimeout(()=>{this.process&&!this.process.killed&&this.process.kill("SIGKILL")},5e3)}catch(e){s.error(`Error terminating process: ${e instanceof Error?e.message:String(e)}`)}this.process=null}this.websocket&&(this.websocket=null),this.isConnected=!1}sleep(e){return new Promise(t=>setTimeout(t,e))}shutdown(){s.info("Shutting down MCP Pipe..."),this.shouldReconnect=!1,this.cleanup(),this.websocket&&this.websocket.close(),this.shutdownResolve&&this.shutdownResolve(),o.default.exit(0)}}function E(i){o.default.on("SIGINT",()=>{s.info("Received interrupt signal, shutting down..."),i.shutdown()}),o.default.on("SIGTERM",()=>{s.info("Received terminate signal, shutting down..."),i.shutdown()})}c(E,"setupSignalHandlers");async function R(){o.default.argv.length<3&&(s.error("Usage: node mcp_pipe.js <mcp_script>"),o.default.exit(1));const i=o.default.argv[2];let e;try{o.default.stderr.write(`[DEBUG] XIAOZHI_CONFIG_DIR: ${o.default.env.XIAOZHI_CONFIG_DIR}
|
|
4
|
+
`),o.default.stderr.write(`[DEBUG] process.cwd(): ${o.default.cwd()}
|
|
5
|
+
`),o.default.stderr.write(`[DEBUG] configManager.getConfigPath(): ${a.configManager.getConfigPath()}
|
|
6
|
+
`),o.default.stderr.write(`[DEBUG] configManager.configExists(): ${a.configManager.configExists()}
|
|
7
|
+
`),a.configManager.configExists()?(e=a.configManager.getMcpEndpoint(),s.info("\u4F7F\u7528\u914D\u7F6E\u6587\u4EF6\u4E2D\u7684 MCP \u7AEF\u70B9")):(e=o.default.env.MCP_ENDPOINT||"",e||(s.error("\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728\u4E14\u672A\u8BBE\u7F6E MCP_ENDPOINT \u73AF\u5883\u53D8\u91CF"),s.error('\u8BF7\u8FD0\u884C "xiaozhi init" \u521D\u59CB\u5316\u914D\u7F6E\uFF0C\u6216\u8BBE\u7F6E MCP_ENDPOINT \u73AF\u5883\u53D8\u91CF'),o.default.exit(1)),s.info("\u4F7F\u7528\u73AF\u5883\u53D8\u91CF\u4E2D\u7684 MCP \u7AEF\u70B9\uFF08\u5EFA\u8BAE\u4F7F\u7528\u914D\u7F6E\u6587\u4EF6\uFF09"))}catch(r){s.error(`\u8BFB\u53D6\u914D\u7F6E\u5931\u8D25: ${r instanceof Error?r.message:String(r)}`),e=o.default.env.MCP_ENDPOINT||"",e||(s.error('\u8BF7\u8FD0\u884C "xiaozhi init" \u521D\u59CB\u5316\u914D\u7F6E\uFF0C\u6216\u8BBE\u7F6E MCP_ENDPOINT \u73AF\u5883\u53D8\u91CF'),o.default.exit(1)),s.info("\u4F7F\u7528\u73AF\u5883\u53D8\u91CF\u4E2D\u7684 MCP \u7AEF\u70B9\u4F5C\u4E3A\u5907\u7528\u65B9\u6848")}(!e||e.includes("<\u8BF7\u586B\u5199"))&&(s.error("MCP \u7AEF\u70B9\u672A\u914D\u7F6E\u6216\u914D\u7F6E\u65E0\u6548"),s.error('\u8BF7\u8FD0\u884C "xiaozhi config mcpEndpoint <your-endpoint-url>" \u8BBE\u7F6E\u7AEF\u70B9'),o.default.exit(1));const t=new I(i,e);E(t);try{await t.start()}catch(r){s.error(`Program execution error: ${r instanceof Error?r.message:String(r)}`),o.default.exit(1)}}c(R,"main"),require.main===module&&R().catch(i=>{s.error(`Unhandled error: ${i instanceof Error?i.message:String(i)}`),o.default.exit(1)});
|
|
264
8
|
//# sourceMappingURL=mcpPipe.cjs.map
|
package/dist/mcpPipe.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/mcpPipe.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * MCP Pipe - JavaScript Implementation\n * Connects to MCP server and pipes input/output to WebSocket endpoint\n * d\n * Version: 0.2.0\n *\n * Usage:\n * export MCP_ENDPOINT=<mcp_endpoint>\n * node mcp_pipe.js <mcp_script>\n */\n\nimport WebSocket from 'ws';\nimport { spawn, ChildProcess } from 'child_process';\nimport { config } from 'dotenv';\nimport process from 'process';\nimport { configManager } from './configManager.js';\n\n// Load environment variables\nconfig();\n\n// Logger utility\nclass Logger {\n private name: string;\n\n constructor(name: string) {\n this.name = name;\n }\n\n info(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`${timestamp} - ${this.name} - INFO - ${message}`);\n }\n\n error(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`${timestamp} - ${this.name} - ERROR - ${message}`);\n }\n\n warning(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`${timestamp} - ${this.name} - WARNING - ${message}`);\n }\n\n debug(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`${timestamp} - ${this.name} - DEBUG - ${message}`);\n }\n}\n\nconst logger = new Logger('MCP_PIPE');\n\n// Reconnection settings\nconst INITIAL_BACKOFF = 1000; // Initial wait time in milliseconds\nconst MAX_BACKOFF = 30000; // Maximum wait time in milliseconds (reduced)\nlet reconnectAttempt = 0;\nlet backoff = INITIAL_BACKOFF;\n\nclass MCPPipe {\n private mcpScript: string;\n private endpointUrl: string;\n private process: ChildProcess | null;\n private websocket: WebSocket | null;\n private shouldReconnect: boolean;\n private isConnected: boolean;\n private shutdownResolve?: () => void;\n\n constructor(mcpScript: string, endpointUrl: string) {\n this.mcpScript = mcpScript;\n this.endpointUrl = endpointUrl;\n this.process = null;\n this.websocket = null;\n this.shouldReconnect = true;\n this.isConnected = false;\n }\n\n async start() {\n // Start MCP script process\n this.startMCPProcess();\n\n // Start WebSocket connection\n await this.connectToServer();\n\n // Keep the process running\n return new Promise<void>((resolve) => {\n // This promise will only resolve when shutdown is called\n this.shutdownResolve = resolve;\n });\n }\n\n async connectToServer() {\n if (this.isConnected) {\n return;\n }\n\n logger.info('Connecting to WebSocket server...');\n\n this.websocket = new WebSocket(this.endpointUrl);\n\n this.websocket.on('open', () => {\n logger.info('Successfully connected to WebSocket server');\n this.isConnected = true;\n\n // Reset reconnection counter\n reconnectAttempt = 0;\n backoff = INITIAL_BACKOFF;\n });\n\n this.websocket.on('message', (data: WebSocket.Data) => {\n const message = data.toString();\n logger.debug(`<< ${message.substring(0, 120)}...`);\n\n // Write to process stdin\n if (this.process && this.process.stdin && !this.process.stdin.destroyed) {\n this.process.stdin.write(message + '\\n');\n }\n });\n\n this.websocket.on('close', (code: number, reason: Buffer) => {\n logger.error(`WebSocket connection closed: ${code} ${reason}`);\n this.isConnected = false;\n this.websocket = null;\n\n // Only reconnect if we should and it's not a permanent error\n if (this.shouldReconnect && code !== 4004) {\n this.scheduleReconnect();\n }\n });\n\n this.websocket.on('error', (error: Error) => {\n logger.error(`WebSocket error: ${error.message}`);\n this.isConnected = false;\n });\n }\n\n scheduleReconnect() {\n if (!this.shouldReconnect) return;\n\n reconnectAttempt++;\n const waitTime = Math.min(backoff * Math.pow(2, reconnectAttempt - 1), MAX_BACKOFF);\n\n logger.info(`Scheduling reconnection attempt ${reconnectAttempt} in ${(waitTime / 1000).toFixed(2)} seconds...`);\n\n setTimeout(() => {\n if (this.shouldReconnect) {\n this.connectToServer();\n }\n }, waitTime);\n }\n\n startMCPProcess() {\n if (this.process) {\n logger.info(`${this.mcpScript} process already running`);\n return;\n }\n\n logger.info(`Starting ${this.mcpScript} process`);\n\n this.process = spawn('node', [this.mcpScript], {\n stdio: ['pipe', 'pipe', 'pipe']\n });\n\n // Handle process stdout - send to WebSocket\n this.process.stdout?.on('data', (data: Buffer) => {\n const message = data.toString();\n logger.debug(`>> ${message.substring(0, 120)}...`);\n\n if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {\n this.websocket.send(message);\n }\n });\n\n // Handle process stderr - print to terminal\n this.process.stderr?.on('data', (data: Buffer) => {\n process.stderr.write(data);\n });\n\n // Handle process exit\n this.process.on('exit', (code: number | null, signal: NodeJS.Signals | null) => {\n logger.info(`${this.mcpScript} process exited with code ${code}, signal ${signal}`);\n this.process = null;\n this.shouldReconnect = false;\n if (this.websocket) {\n this.websocket.close();\n }\n });\n\n // Handle process error\n this.process.on('error', (error: Error) => {\n logger.error(`Process error: ${error.message}`);\n this.process = null;\n this.shouldReconnect = false;\n if (this.websocket) {\n this.websocket.close();\n }\n });\n }\n\n cleanup() {\n if (this.process) {\n logger.info(`Terminating ${this.mcpScript} process`);\n try {\n this.process.kill('SIGTERM');\n\n // Force kill after timeout\n setTimeout(() => {\n if (this.process && !this.process.killed) {\n this.process.kill('SIGKILL');\n }\n }, 5000);\n } catch (error) {\n logger.error(`Error terminating process: ${error instanceof Error ? error.message : String(error)}`);\n }\n this.process = null;\n }\n\n if (this.websocket) {\n this.websocket = null;\n }\n\n this.isConnected = false;\n }\n\n sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n\n shutdown() {\n logger.info('Shutting down MCP Pipe...');\n this.shouldReconnect = false;\n this.cleanup();\n if (this.websocket) {\n this.websocket.close();\n }\n if (this.shutdownResolve) {\n this.shutdownResolve();\n }\n process.exit(0);\n }\n}\n\n// Signal handlers\nfunction setupSignalHandlers(mcpPipe: MCPPipe): void {\n process.on('SIGINT', () => {\n logger.info('Received interrupt signal, shutting down...');\n mcpPipe.shutdown();\n });\n\n process.on('SIGTERM', () => {\n logger.info('Received terminate signal, shutting down...');\n mcpPipe.shutdown();\n });\n}\n\n// Main execution\nasync function main() {\n // Check command line arguments\n if (process.argv.length < 3) {\n logger.error('Usage: node mcp_pipe.js <mcp_script>');\n process.exit(1);\n }\n\n const mcpScript = process.argv[2];\n\n // Get endpoint URL from config file or environment variable (fallback)\n let endpointUrl: string;\n\n try {\n // 首先尝试从配置文件读取\n if (configManager.configExists()) {\n endpointUrl = configManager.getMcpEndpoint();\n logger.info('使用配置文件中的 MCP 端点');\n } else {\n // 如果配置文件不存在,尝试从环境变量读取(向后兼容)\n endpointUrl = process.env.MCP_ENDPOINT || '';\n if (!endpointUrl) {\n logger.error('配置文件不存在且未设置 MCP_ENDPOINT 环境变量');\n logger.error('请运行 \"xiaozhi init\" 初始化配置,或设置 MCP_ENDPOINT 环境变量');\n process.exit(1);\n }\n logger.info('使用环境变量中的 MCP 端点(建议使用配置文件)');\n }\n } catch (error) {\n logger.error(`读取配置失败: ${error instanceof Error ? error.message : String(error)}`);\n\n // 尝试从环境变量读取作为备用方案\n endpointUrl = process.env.MCP_ENDPOINT || '';\n if (!endpointUrl) {\n logger.error('请运行 \"xiaozhi init\" 初始化配置,或设置 MCP_ENDPOINT 环境变量');\n process.exit(1);\n }\n logger.info('使用环境变量中的 MCP 端点作为备用方案');\n }\n\n // 验证端点 URL\n if (!endpointUrl || endpointUrl.includes('<请填写')) {\n logger.error('MCP 端点未配置或配置无效');\n logger.error('请运行 \"xiaozhi config mcpEndpoint <your-endpoint-url>\" 设置端点');\n process.exit(1);\n }\n\n // Create MCP Pipe instance\n const mcpPipe = new MCPPipe(mcpScript, endpointUrl);\n\n // Setup signal handlers\n setupSignalHandlers(mcpPipe);\n\n // Start the MCP pipe\n try {\n await mcpPipe.start();\n } catch (error) {\n logger.error(`Program execution error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(1);\n }\n}\n\n// Run if this file is executed directly\nif (require.main === module) {\n main().catch(error => {\n logger.error(`Unhandled error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(1);\n });\n}\n"],"mappings":"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","names":["WebSocket","process"]}
|
|
1
|
+
{"version":3,"sources":["../src/mcpPipe.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * MCP Pipe - JavaScript Implementation\n * Connects to MCP server and pipes input/output to WebSocket endpoint\n * d\n * Version: 0.2.0\n *\n * Usage:\n * export MCP_ENDPOINT=<mcp_endpoint>\n * node mcp_pipe.js <mcp_script>\n */\n\nimport { type ChildProcess, spawn } from \"node:child_process\";\nimport process from \"node:process\";\nimport { config } from \"dotenv\";\nimport WebSocket from \"ws\";\nimport { configManager } from \"./configManager.js\";\n\n// Load environment variables\nconfig();\n\n// Logger utility\nclass Logger {\n private name: string;\n\n constructor(name: string) {\n this.name = name;\n }\n\n info(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`${timestamp} - ${this.name} - INFO - ${message}`);\n }\n\n error(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`${timestamp} - ${this.name} - ERROR - ${message}`);\n }\n\n warning(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`${timestamp} - ${this.name} - WARNING - ${message}`);\n }\n\n debug(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`${timestamp} - ${this.name} - DEBUG - ${message}`);\n }\n}\n\nconst logger = new Logger(\"MCP_PIPE\");\n\n// Reconnection settings\nconst INITIAL_BACKOFF = 1000; // Initial wait time in milliseconds\nconst MAX_BACKOFF = 30000; // Maximum wait time in milliseconds (reduced)\nlet reconnectAttempt = 0;\nlet backoff = INITIAL_BACKOFF;\n\nclass MCPPipe {\n private mcpScript: string;\n private endpointUrl: string;\n private process: ChildProcess | null;\n private websocket: WebSocket | null;\n private shouldReconnect: boolean;\n private isConnected: boolean;\n private shutdownResolve?: () => void;\n\n constructor(mcpScript: string, endpointUrl: string) {\n this.mcpScript = mcpScript;\n this.endpointUrl = endpointUrl;\n this.process = null;\n this.websocket = null;\n this.shouldReconnect = true;\n this.isConnected = false;\n }\n\n async start() {\n // Start MCP script process\n this.startMCPProcess();\n\n // Start WebSocket connection\n await this.connectToServer();\n\n // Keep the process running\n return new Promise<void>((resolve) => {\n // This promise will only resolve when shutdown is called\n this.shutdownResolve = resolve;\n });\n }\n\n async connectToServer() {\n if (this.isConnected) {\n return;\n }\n\n logger.info(\"Connecting to WebSocket server...\");\n\n this.websocket = new WebSocket(this.endpointUrl);\n\n this.websocket.on(\"open\", () => {\n logger.info(\"Successfully connected to WebSocket server\");\n this.isConnected = true;\n\n // Reset reconnection counter\n reconnectAttempt = 0;\n backoff = INITIAL_BACKOFF;\n });\n\n this.websocket.on(\"message\", (data: WebSocket.Data) => {\n const message = data.toString();\n logger.debug(`<< ${message.substring(0, 120)}...`);\n\n // Write to process stdin\n if (this.process?.stdin && !this.process.stdin.destroyed) {\n this.process.stdin.write(`${message}\\n`);\n }\n });\n\n this.websocket.on(\"close\", (code: number, reason: Buffer) => {\n logger.error(`WebSocket connection closed: ${code} ${reason}`);\n this.isConnected = false;\n this.websocket = null;\n\n // Only reconnect if we should and it's not a permanent error\n if (this.shouldReconnect && code !== 4004) {\n this.scheduleReconnect();\n }\n });\n\n this.websocket.on(\"error\", (error: Error) => {\n logger.error(`WebSocket error: ${error.message}`);\n this.isConnected = false;\n });\n }\n\n scheduleReconnect() {\n if (!this.shouldReconnect) return;\n\n reconnectAttempt++;\n const waitTime = Math.min(\n backoff * 2 ** (reconnectAttempt - 1),\n MAX_BACKOFF\n );\n\n logger.info(\n `Scheduling reconnection attempt ${reconnectAttempt} in ${(waitTime / 1000).toFixed(2)} seconds...`\n );\n\n setTimeout(() => {\n if (this.shouldReconnect) {\n this.connectToServer();\n }\n }, waitTime);\n }\n\n startMCPProcess() {\n if (this.process) {\n logger.info(`${this.mcpScript} process already running`);\n return;\n }\n\n logger.info(`Starting ${this.mcpScript} process`);\n\n this.process = spawn(\"node\", [this.mcpScript], {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n\n // Handle process stdout - send to WebSocket\n this.process.stdout?.on(\"data\", (data: Buffer) => {\n const message = data.toString();\n logger.debug(`>> ${message.substring(0, 120)}...`);\n\n if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {\n this.websocket.send(message);\n }\n });\n\n // Handle process stderr - print to terminal\n this.process.stderr?.on(\"data\", (data: Buffer) => {\n process.stderr.write(data);\n });\n\n // Handle process exit\n this.process.on(\n \"exit\",\n (code: number | null, signal: NodeJS.Signals | null) => {\n logger.info(\n `${this.mcpScript} process exited with code ${code}, signal ${signal}`\n );\n this.process = null;\n this.shouldReconnect = false;\n if (this.websocket) {\n this.websocket.close();\n }\n }\n );\n\n // Handle process error\n this.process.on(\"error\", (error: Error) => {\n logger.error(`Process error: ${error.message}`);\n this.process = null;\n this.shouldReconnect = false;\n if (this.websocket) {\n this.websocket.close();\n }\n });\n }\n\n cleanup() {\n if (this.process) {\n logger.info(`Terminating ${this.mcpScript} process`);\n try {\n this.process.kill(\"SIGTERM\");\n\n // Force kill after timeout\n setTimeout(() => {\n if (this.process && !this.process.killed) {\n this.process.kill(\"SIGKILL\");\n }\n }, 5000);\n } catch (error) {\n logger.error(\n `Error terminating process: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n this.process = null;\n }\n\n if (this.websocket) {\n this.websocket = null;\n }\n\n this.isConnected = false;\n }\n\n sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n shutdown() {\n logger.info(\"Shutting down MCP Pipe...\");\n this.shouldReconnect = false;\n this.cleanup();\n if (this.websocket) {\n this.websocket.close();\n }\n if (this.shutdownResolve) {\n this.shutdownResolve();\n }\n process.exit(0);\n }\n}\n\n// Signal handlers\nfunction setupSignalHandlers(mcpPipe: MCPPipe): void {\n process.on(\"SIGINT\", () => {\n logger.info(\"Received interrupt signal, shutting down...\");\n mcpPipe.shutdown();\n });\n\n process.on(\"SIGTERM\", () => {\n logger.info(\"Received terminate signal, shutting down...\");\n mcpPipe.shutdown();\n });\n}\n\n// Main execution\nasync function main() {\n // Check command line arguments\n if (process.argv.length < 3) {\n logger.error(\"Usage: node mcp_pipe.js <mcp_script>\");\n process.exit(1);\n }\n\n const mcpScript = process.argv[2];\n\n // Get endpoint URL from config file or environment variable (fallback)\n let endpointUrl: string;\n\n try {\n // 调试信息 - 使用 process.stderr.write 确保能看到\n process.stderr.write(\n `[DEBUG] XIAOZHI_CONFIG_DIR: ${process.env.XIAOZHI_CONFIG_DIR}\\n`\n );\n process.stderr.write(`[DEBUG] process.cwd(): ${process.cwd()}\\n`);\n process.stderr.write(\n `[DEBUG] configManager.getConfigPath(): ${configManager.getConfigPath()}\\n`\n );\n process.stderr.write(\n `[DEBUG] configManager.configExists(): ${configManager.configExists()}\\n`\n );\n\n // 首先尝试从配置文件读取\n if (configManager.configExists()) {\n endpointUrl = configManager.getMcpEndpoint();\n logger.info(\"使用配置文件中的 MCP 端点\");\n } else {\n // 如果配置文件不存在,尝试从环境变量读取(向后兼容)\n endpointUrl = process.env.MCP_ENDPOINT || \"\";\n if (!endpointUrl) {\n logger.error(\"配置文件不存在且未设置 MCP_ENDPOINT 环境变量\");\n logger.error(\n '请运行 \"xiaozhi init\" 初始化配置,或设置 MCP_ENDPOINT 环境变量'\n );\n process.exit(1);\n }\n logger.info(\"使用环境变量中的 MCP 端点(建议使用配置文件)\");\n }\n } catch (error) {\n logger.error(\n `读取配置失败: ${error instanceof Error ? error.message : String(error)}`\n );\n\n // 尝试从环境变量读取作为备用方案\n endpointUrl = process.env.MCP_ENDPOINT || \"\";\n if (!endpointUrl) {\n logger.error(\n '请运行 \"xiaozhi init\" 初始化配置,或设置 MCP_ENDPOINT 环境变量'\n );\n process.exit(1);\n }\n logger.info(\"使用环境变量中的 MCP 端点作为备用方案\");\n }\n\n // 验证端点 URL\n if (!endpointUrl || endpointUrl.includes(\"<请填写\")) {\n logger.error(\"MCP 端点未配置或配置无效\");\n logger.error(\n '请运行 \"xiaozhi config mcpEndpoint <your-endpoint-url>\" 设置端点'\n );\n process.exit(1);\n }\n\n // Create MCP Pipe instance\n const mcpPipe = new MCPPipe(mcpScript, endpointUrl);\n\n // Setup signal handlers\n setupSignalHandlers(mcpPipe);\n\n // Start the MCP pipe\n try {\n await mcpPipe.start();\n } catch (error) {\n logger.error(\n `Program execution error: ${error instanceof Error ? error.message : String(error)}`\n );\n process.exit(1);\n }\n}\n\n// Run if this file is executed directly\nif (require.main === module) {\n main().catch((error) => {\n logger.error(\n `Unhandled error: ${error instanceof Error ? error.message : String(error)}`\n );\n process.exit(1);\n });\n}\n"],"mappings":"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","names":["import_node_child_process","import_node_process","import_dotenv","import_ws","import_configManager","Logger","__name","name","message","timestamp","logger","INITIAL_BACKOFF","MAX_BACKOFF","reconnectAttempt","backoff","MCPPipe","mcpScript","endpointUrl","resolve","WebSocket","data","code","reason","error","waitTime","process","signal","ms","setupSignalHandlers","mcpPipe","main"]}
|