sloth-d2c-mcp 1.0.4-beta65
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 +100 -0
- package/cli/run.js +102 -0
- package/dist/build/config-manager/index.js +160 -0
- package/dist/build/index.js +839 -0
- package/dist/build/interceptor/client.js +142 -0
- package/dist/build/interceptor/vscode.js +143 -0
- package/dist/build/interceptor/web.js +28 -0
- package/dist/build/server.js +539 -0
- package/dist/build/utils/extract.js +166 -0
- package/dist/build/utils/file-manager.js +241 -0
- package/dist/build/utils/logger.js +90 -0
- package/dist/build/utils/update.js +54 -0
- package/dist/build/utils/utils.js +165 -0
- package/dist/build/utils/vscode-logger.js +133 -0
- package/dist/build/utils/webpack-substitutions.js +196 -0
- package/dist/interceptor-web/dist/build-report.json +18 -0
- package/dist/interceptor-web/dist/detail.html +1 -0
- package/dist/interceptor-web/dist/index.html +1 -0
- package/package.json +90 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import http from 'http';
|
|
3
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
4
|
+
import { spawn } from 'child_process';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
// ES模块中获取__dirname的替代方案
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
// 存储等待中的请求 { token: { resolve } }
|
|
11
|
+
const pendingRequests = new Map();
|
|
12
|
+
let server = null;
|
|
13
|
+
let app = null;
|
|
14
|
+
let serverPort = 3005;
|
|
15
|
+
// 初始化Express应用和服务器
|
|
16
|
+
function initializeServer() {
|
|
17
|
+
if (app && server) {
|
|
18
|
+
return Promise.resolve(); // 已经初始化过了
|
|
19
|
+
}
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
app = express();
|
|
22
|
+
app.use(express.urlencoded({ extended: true }));
|
|
23
|
+
app.use(express.json());
|
|
24
|
+
// 接收Tauri应用发送的数据
|
|
25
|
+
app.post('/callback/:token', (req, res) => {
|
|
26
|
+
const token = req.params.token;
|
|
27
|
+
const { result } = req.body;
|
|
28
|
+
console.log(`收到来自Tauri应用的数据 - Token: ${token}, Result: ${result}`);
|
|
29
|
+
const request = pendingRequests.get(token);
|
|
30
|
+
if (request && request.resolve) {
|
|
31
|
+
request.resolve(result);
|
|
32
|
+
res.json({ success: true, message: 'Data received successfully' });
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
res.status(404).json({ success: false, message: 'Invalid or expired token' });
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
// 启动服务器,如果端口被占用则尝试下一个端口
|
|
39
|
+
server = http.createServer(app);
|
|
40
|
+
const tryListen = (port) => {
|
|
41
|
+
server.listen(port, () => {
|
|
42
|
+
serverPort = port;
|
|
43
|
+
console.log(`Tauri callback server running on http://localhost:${port}`);
|
|
44
|
+
resolve();
|
|
45
|
+
});
|
|
46
|
+
server.on('error', (err) => {
|
|
47
|
+
if (err.code === 'EADDRINUSE') {
|
|
48
|
+
console.log(`Port ${port} is busy, trying ${port + 1}...`);
|
|
49
|
+
tryListen(port + 1);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
reject(err);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
tryListen(serverPort);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
// 打开Tauri应用并获取用户输入
|
|
60
|
+
export async function getUserInputFromTauri(prompt = "Please provide input") {
|
|
61
|
+
return new Promise(async (resolve, reject) => {
|
|
62
|
+
try {
|
|
63
|
+
// 确保服务器已初始化
|
|
64
|
+
await initializeServer();
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
reject(new Error(`Failed to initialize server: ${error}`));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const token = uuidv4();
|
|
71
|
+
// 声明heartbeat变量
|
|
72
|
+
let heartbeat;
|
|
73
|
+
// 设置超时 - 增加到10分钟
|
|
74
|
+
const timeout = setTimeout(() => {
|
|
75
|
+
if (pendingRequests.has(token)) {
|
|
76
|
+
pendingRequests.delete(token);
|
|
77
|
+
clearInterval(heartbeat);
|
|
78
|
+
console.log(`Token ${token} 超时,用户未在10分钟内提供输入`);
|
|
79
|
+
reject(new Error('Timeout: User did not provide input within 10 minutes'));
|
|
80
|
+
}
|
|
81
|
+
}, 600000); // 10分钟超时
|
|
82
|
+
console.log(`Token ${token} 已创建,等待用户输入...`);
|
|
83
|
+
// 添加心跳机制,每30秒输出一次状态
|
|
84
|
+
heartbeat = setInterval(() => {
|
|
85
|
+
if (pendingRequests.has(token)) {
|
|
86
|
+
console.log(`Token ${token} 仍在等待用户输入...`);
|
|
87
|
+
}
|
|
88
|
+
}, 30000);
|
|
89
|
+
// 存储解析函数
|
|
90
|
+
pendingRequests.set(token, {
|
|
91
|
+
resolve: (value) => {
|
|
92
|
+
console.log(`Token ${token} 收到用户输入,正在解析...`);
|
|
93
|
+
clearTimeout(timeout);
|
|
94
|
+
clearInterval(heartbeat);
|
|
95
|
+
pendingRequests.delete(token);
|
|
96
|
+
resolve(value);
|
|
97
|
+
},
|
|
98
|
+
timeout
|
|
99
|
+
});
|
|
100
|
+
// 启动Tauri应用
|
|
101
|
+
const tauriAppPath = path.resolve(__dirname, '../../client');
|
|
102
|
+
console.log(`正在启动Tauri应用: ${tauriAppPath}`);
|
|
103
|
+
console.log(`Token: ${token}`);
|
|
104
|
+
// 设置环境变量传递token和端口
|
|
105
|
+
const env = {
|
|
106
|
+
...process.env,
|
|
107
|
+
MCP_TOKEN: token,
|
|
108
|
+
MCP_PORT: serverPort.toString()
|
|
109
|
+
};
|
|
110
|
+
// 使用pnpm tauri dev启动应用
|
|
111
|
+
const tauriProcess = spawn('pnpm', ['tauri', 'dev'], {
|
|
112
|
+
cwd: tauriAppPath,
|
|
113
|
+
stdio: 'ignore',
|
|
114
|
+
detached: false,
|
|
115
|
+
env: env
|
|
116
|
+
});
|
|
117
|
+
tauriProcess.stdout?.on('data', (data) => {
|
|
118
|
+
console.log(`Tauri stdout: ${data}`);
|
|
119
|
+
});
|
|
120
|
+
tauriProcess.stderr?.on('data', (data) => {
|
|
121
|
+
console.error(`Tauri stderr: ${data}`);
|
|
122
|
+
});
|
|
123
|
+
tauriProcess.on('error', (error) => {
|
|
124
|
+
clearTimeout(timeout);
|
|
125
|
+
clearInterval(heartbeat);
|
|
126
|
+
pendingRequests.delete(token);
|
|
127
|
+
reject(new Error(`Failed to start Tauri app: ${error.message}`));
|
|
128
|
+
});
|
|
129
|
+
tauriProcess.on('close', (code) => {
|
|
130
|
+
console.log(`Tauri process exited with code ${code}`);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
// 清理函数
|
|
135
|
+
export function cleanup() {
|
|
136
|
+
if (server) {
|
|
137
|
+
server.close();
|
|
138
|
+
server = null;
|
|
139
|
+
app = null;
|
|
140
|
+
}
|
|
141
|
+
pendingRequests.clear();
|
|
142
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import * as net from 'net';
|
|
2
|
+
/**
|
|
3
|
+
* 与VSCode扩展通信的工具类
|
|
4
|
+
*/
|
|
5
|
+
export class VSCodeCommunicator {
|
|
6
|
+
host = '127.0.0.1';
|
|
7
|
+
port = 13141;
|
|
8
|
+
// 新增: 生成会话ID
|
|
9
|
+
generateSessionId() {
|
|
10
|
+
return `${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* 长连接模式 - 直接等待用户输入,无超时限制
|
|
14
|
+
* @param sessionId 会话ID
|
|
15
|
+
* @param payload 传递给webview的数据
|
|
16
|
+
* @returns Promise<string> 返回用户输入的数据
|
|
17
|
+
*/
|
|
18
|
+
waitForUserInputLongConnection(sessionId, payload) {
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
const client = net.connect({ port: this.port, host: this.host }, () => {
|
|
21
|
+
console.log(`[MCP] 建立长连接等待用户输入,会话ID: ${sessionId}`);
|
|
22
|
+
// 发送长连接等待命令,包含 payload 数据
|
|
23
|
+
const requestPayload = JSON.stringify({
|
|
24
|
+
cmd: 'waitForInput',
|
|
25
|
+
sessionId,
|
|
26
|
+
payload: payload
|
|
27
|
+
});
|
|
28
|
+
client.write(requestPayload);
|
|
29
|
+
});
|
|
30
|
+
let isResolved = false;
|
|
31
|
+
client.on('data', (data) => {
|
|
32
|
+
const response = data.toString();
|
|
33
|
+
console.log('[MCP] 收到响应:', response);
|
|
34
|
+
if (response === 'WAITING_FOR_INPUT') {
|
|
35
|
+
console.log('[MCP] 服务器确认等待状态,webview已打开,等待用户输入...');
|
|
36
|
+
}
|
|
37
|
+
else if (response === 'SERVER_STOPPING') {
|
|
38
|
+
if (!isResolved) {
|
|
39
|
+
isResolved = true;
|
|
40
|
+
client.end();
|
|
41
|
+
reject(new Error('服务器正在关闭'));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
// 收到用户数据
|
|
46
|
+
if (!isResolved) {
|
|
47
|
+
isResolved = true;
|
|
48
|
+
console.log('[MCP] 成功获取用户输入:', response);
|
|
49
|
+
resolve(response);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
client.on('error', (err) => {
|
|
54
|
+
if (!isResolved) {
|
|
55
|
+
isResolved = true;
|
|
56
|
+
reject(new Error(`VSCode扩展连接失败: ${err.message}`));
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
client.on('close', () => {
|
|
60
|
+
if (!isResolved) {
|
|
61
|
+
isResolved = true;
|
|
62
|
+
reject(new Error('连接意外关闭'));
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* 从VSCode获取用户输入 - 主要方法
|
|
69
|
+
* 使用长连接模式,无超时限制
|
|
70
|
+
* @param payload 传输参数
|
|
71
|
+
* @returns Promise<string> 返回用户输入的数据
|
|
72
|
+
*/
|
|
73
|
+
async getUserInputFromVSCode(payload) {
|
|
74
|
+
try {
|
|
75
|
+
console.log('[MCP] 正在打开VSCode webview表单...');
|
|
76
|
+
// 生成会话ID
|
|
77
|
+
const sessionId = this.generateSessionId();
|
|
78
|
+
console.log(`[MCP] 生成会话ID: ${sessionId}`);
|
|
79
|
+
console.log('[MCP] 请在VSCode webview中输入内容并提交...');
|
|
80
|
+
console.log('[MCP] 使用长连接模式,无超时限制,请耐心等待用户输入');
|
|
81
|
+
// 使用长连接模式等待用户输入
|
|
82
|
+
return await this.waitForUserInputLongConnection(sessionId, payload);
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
throw new Error(`获取用户输入失败: ${error}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* 检查VSCode扩展是否可用
|
|
90
|
+
* @returns Promise<boolean> 返回扩展是否可用
|
|
91
|
+
*/
|
|
92
|
+
async isVSCodeExtensionAvailable() {
|
|
93
|
+
try {
|
|
94
|
+
// 简单的连接测试
|
|
95
|
+
const client = net.connect({ port: this.port, host: this.host });
|
|
96
|
+
return new Promise((resolve) => {
|
|
97
|
+
client.on('connect', () => {
|
|
98
|
+
client.end();
|
|
99
|
+
resolve(true);
|
|
100
|
+
});
|
|
101
|
+
client.on('error', () => {
|
|
102
|
+
resolve(false);
|
|
103
|
+
});
|
|
104
|
+
setTimeout(() => {
|
|
105
|
+
client.destroy();
|
|
106
|
+
resolve(false);
|
|
107
|
+
}, 1000);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// 创建单例实例
|
|
116
|
+
const vscodeComm = new VSCodeCommunicator();
|
|
117
|
+
/**
|
|
118
|
+
* 从VSCode获取用户输入的便捷函数
|
|
119
|
+
* @param payload 传输参数
|
|
120
|
+
* @returns Promise<string> 返回用户输入的数据
|
|
121
|
+
*/
|
|
122
|
+
export async function getUserInputFromVSCode(payload) {
|
|
123
|
+
return vscodeComm.getUserInputFromVSCode(payload);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* 检查VSCode扩展是否可用的便捷函数
|
|
127
|
+
* @returns Promise<boolean> 返回扩展是否可用
|
|
128
|
+
*/
|
|
129
|
+
export async function isVSCodeAvailable() {
|
|
130
|
+
return vscodeComm.isVSCodeExtensionAvailable();
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* 清理资源的函数
|
|
134
|
+
*/
|
|
135
|
+
export function cleanup() {
|
|
136
|
+
console.log('[MCP] VSCode通信模块清理完成');
|
|
137
|
+
// 这里可以添加任何需要清理的资源
|
|
138
|
+
// 比如关闭连接池、清理缓存等
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* 导出通信器实例,供高级用法使用
|
|
142
|
+
*/
|
|
143
|
+
export { vscodeComm };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { getUserInput as getServerUserInput } from '../server.js';
|
|
2
|
+
import ConfigManager from '../config-manager/index.js';
|
|
3
|
+
const configManager = new ConfigManager("d2c-mcp");
|
|
4
|
+
// 获取用户输入的主函数 - 现在使用共享的MCP服务器
|
|
5
|
+
export async function getUserInput(payload) {
|
|
6
|
+
try {
|
|
7
|
+
return await getServerUserInput(payload);
|
|
8
|
+
}
|
|
9
|
+
catch (error) {
|
|
10
|
+
throw new Error(`Failed to get user input: ${error.message}`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
// 获取配置的辅助函数
|
|
14
|
+
export async function getConfig() {
|
|
15
|
+
if (await configManager.exists()) {
|
|
16
|
+
return await configManager.load();
|
|
17
|
+
}
|
|
18
|
+
return {};
|
|
19
|
+
}
|
|
20
|
+
// 保存配置的辅助函数
|
|
21
|
+
export async function saveConfig(config) {
|
|
22
|
+
await configManager.save(config);
|
|
23
|
+
}
|
|
24
|
+
// 清理函数 - 现在只需要清理本地资源
|
|
25
|
+
export function cleanup() {
|
|
26
|
+
// 本地清理逻辑,如果有的话
|
|
27
|
+
console.log('Web interceptor cleanup completed');
|
|
28
|
+
}
|