sloth-d2c-mcp 1.0.4-beta100

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.
Files changed (34) hide show
  1. package/README.md +83 -0
  2. package/cli/run.js +328 -0
  3. package/cli/sloth-server.log +1622 -0
  4. package/dist/build/config-manager/index.js +240 -0
  5. package/dist/build/core/prompt-builder.js +366 -0
  6. package/dist/build/core/sampling.js +375 -0
  7. package/dist/build/core/types.js +1 -0
  8. package/dist/build/index.js +852 -0
  9. package/dist/build/interceptor/client.js +142 -0
  10. package/dist/build/interceptor/vscode.js +143 -0
  11. package/dist/build/interceptor/web.js +28 -0
  12. package/dist/build/plugin/index.js +4 -0
  13. package/dist/build/plugin/loader.js +349 -0
  14. package/dist/build/plugin/manager.js +129 -0
  15. package/dist/build/plugin/types.js +6 -0
  16. package/dist/build/server.js +2116 -0
  17. package/dist/build/socket-client.js +166 -0
  18. package/dist/build/socket-server.js +260 -0
  19. package/dist/build/utils/client-capabilities.js +143 -0
  20. package/dist/build/utils/extract.js +168 -0
  21. package/dist/build/utils/file-manager.js +868 -0
  22. package/dist/build/utils/image-matcher.js +154 -0
  23. package/dist/build/utils/logger.js +90 -0
  24. package/dist/build/utils/opencv-loader.js +70 -0
  25. package/dist/build/utils/prompt-parser.js +46 -0
  26. package/dist/build/utils/tj.js +139 -0
  27. package/dist/build/utils/update.js +100 -0
  28. package/dist/build/utils/utils.js +184 -0
  29. package/dist/build/utils/vscode-logger.js +133 -0
  30. package/dist/build/utils/webpack-substitutions.js +196 -0
  31. package/dist/interceptor-web/dist/build-report.json +18 -0
  32. package/dist/interceptor-web/dist/detail.html +1 -0
  33. package/dist/interceptor-web/dist/index.html +1 -0
  34. package/package.json +96 -0
@@ -0,0 +1,166 @@
1
+ import * as net from 'net';
2
+ import { Logger } from './utils/logger.js';
3
+ /**
4
+ * Socket 客户端类
5
+ * 用于子进程连接到主进程的 Socket 服务器,接收认证结果
6
+ */
7
+ export class SocketClient {
8
+ socket = null;
9
+ host;
10
+ port;
11
+ connected = false;
12
+ messageBuffer = '';
13
+ messageHandlers = new Map();
14
+ constructor(host = 'localhost', port) {
15
+ this.host = host;
16
+ this.port = port;
17
+ }
18
+ /**
19
+ * 连接到 Socket 服务器
20
+ */
21
+ connect() {
22
+ return new Promise((resolve, reject) => {
23
+ this.socket = new net.Socket();
24
+ this.socket.setEncoding('utf8');
25
+ // 连接成功
26
+ this.socket.on('connect', () => {
27
+ Logger.log(`Socket 客户端已连接到 ${this.host}:${this.port}`);
28
+ this.connected = true;
29
+ resolve();
30
+ });
31
+ // 接收数据
32
+ this.socket.on('data', (data) => {
33
+ this.handleData(data);
34
+ });
35
+ // 处理错误
36
+ this.socket.on('error', (error) => {
37
+ Logger.error('Socket 客户端错误:', error);
38
+ this.connected = false;
39
+ if (!this.socket?.connecting) {
40
+ reject(error);
41
+ }
42
+ });
43
+ // 处理断开连接
44
+ this.socket.on('close', () => {
45
+ Logger.log('Socket 客户端已断开连接');
46
+ this.connected = false;
47
+ });
48
+ // 开始连接
49
+ this.socket.connect(this.port, this.host);
50
+ });
51
+ }
52
+ /**
53
+ * 处理接收到的数据(支持粘包处理)
54
+ */
55
+ handleData(data) {
56
+ // 将数据添加到缓冲区
57
+ this.messageBuffer += data;
58
+ // 按换行符分割消息
59
+ const messages = this.messageBuffer.split('\n');
60
+ // 保留最后一个不完整的消息
61
+ this.messageBuffer = messages.pop() || '';
62
+ // 处理每个完整的消息
63
+ for (const messageStr of messages) {
64
+ if (!messageStr.trim())
65
+ continue;
66
+ try {
67
+ const message = JSON.parse(messageStr);
68
+ this.handleMessage(message);
69
+ }
70
+ catch (error) {
71
+ Logger.error('解析 Socket 消息失败:', error, '原始数据:', messageStr);
72
+ }
73
+ }
74
+ }
75
+ /**
76
+ * 处理解析后的消息
77
+ */
78
+ handleMessage(message) {
79
+ Logger.log('收到 Socket 消息:', message.type);
80
+ // 如果是认证响应,调用对应的处理器
81
+ if (message.type === 'submit-response' && message.token) {
82
+ const handler = this.messageHandlers.get(message.token);
83
+ if (handler) {
84
+ handler(message.data);
85
+ this.messageHandlers.delete(message.token);
86
+ }
87
+ }
88
+ // 处理其他消息类型
89
+ switch (message.type) {
90
+ case 'welcome':
91
+ Logger.log('收到欢迎消息:', message.message);
92
+ break;
93
+ case 'pong':
94
+ Logger.log('收到 pong 响应');
95
+ break;
96
+ case 'error':
97
+ Logger.error('服务器错误:', message.message);
98
+ break;
99
+ }
100
+ }
101
+ /**
102
+ * 发送消息到服务器
103
+ */
104
+ send(message) {
105
+ if (!this.socket || !this.connected) {
106
+ throw new Error('Socket 客户端未连接');
107
+ }
108
+ try {
109
+ this.socket.write(JSON.stringify(message) + '\n');
110
+ }
111
+ catch (error) {
112
+ Logger.error('发送 Socket 消息失败:', error);
113
+ throw error;
114
+ }
115
+ }
116
+ /**
117
+ * 注册 token,等待认证响应
118
+ * @param token 认证 token
119
+ * @param extra 额外数据(可选),如 { workspaceRoot: string, ... }
120
+ */
121
+ registerToken(token, extra) {
122
+ return new Promise((resolve, reject) => {
123
+ // 设置超时(可选,根据需求调整)
124
+ const timeout = setTimeout(() => {
125
+ this.messageHandlers.delete(token);
126
+ reject(new Error('等待认证响应超时'));
127
+ }, 30 * 60 * 1000); // 30分钟超时
128
+ // 注册消息处理器
129
+ this.messageHandlers.set(token, (data) => {
130
+ clearTimeout(timeout);
131
+ resolve(JSON.stringify(data));
132
+ });
133
+ // 发送注册消息到服务器
134
+ this.send({
135
+ type: 'register-token',
136
+ token,
137
+ extra,
138
+ timestamp: Date.now(),
139
+ });
140
+ Logger.log(`已注册 token: ${token},extra: ${JSON.stringify(extra)},等待认证响应...`);
141
+ });
142
+ }
143
+ /**
144
+ * 发送 ping 消息
145
+ */
146
+ ping() {
147
+ this.send({ type: 'ping', timestamp: Date.now() });
148
+ }
149
+ /**
150
+ * 断开连接
151
+ */
152
+ disconnect() {
153
+ if (this.socket) {
154
+ this.socket.destroy();
155
+ this.socket = null;
156
+ this.connected = false;
157
+ this.messageHandlers.clear();
158
+ }
159
+ }
160
+ /**
161
+ * 检查是否已连接
162
+ */
163
+ isConnected() {
164
+ return this.connected;
165
+ }
166
+ }
@@ -0,0 +1,260 @@
1
+ import * as net from 'net';
2
+ import { Logger } from './utils/logger.js';
3
+ /**
4
+ * Socket 服务器类
5
+ * 使用原生 net 模块实现 TCP Socket 通信
6
+ */
7
+ export class SocketServer {
8
+ server = null;
9
+ connections = new Set();
10
+ tokenSockets = new Map(); // token -> socket 映射
11
+ tokenExtras = new Map(); // token -> extra 数据映射
12
+ messageBuffers = new Map(); // socket -> 消息缓冲区
13
+ port = 0;
14
+ constructor() {
15
+ this.server = net.createServer(this.handleConnection.bind(this));
16
+ this.setupServerEvents();
17
+ }
18
+ /**
19
+ * 处理新的 Socket 连接
20
+ */
21
+ handleConnection(socket) {
22
+ const clientId = `${socket.remoteAddress}:${socket.remotePort}`;
23
+ Logger.log(`Socket 客户端已连接: ${clientId}`);
24
+ // 添加到活跃连接集合
25
+ this.connections.add(socket);
26
+ this.messageBuffers.set(socket, '');
27
+ // 设置编码
28
+ socket.setEncoding('utf8');
29
+ // 接收数据
30
+ socket.on('data', (data) => {
31
+ this.handleData(socket, clientId, data);
32
+ });
33
+ // 处理错误
34
+ socket.on('error', (error) => {
35
+ Logger.error(`Socket 客户端 ${clientId} 错误:`, error);
36
+ });
37
+ // 处理断开连接
38
+ socket.on('close', () => {
39
+ Logger.log(`Socket 客户端已断开: ${clientId}`);
40
+ this.connections.delete(socket);
41
+ this.messageBuffers.delete(socket);
42
+ // 清理该 socket 关联的所有 token
43
+ for (const [token, sock] of this.tokenSockets.entries()) {
44
+ if (sock === socket) {
45
+ this.tokenSockets.delete(token);
46
+ this.tokenExtras.delete(token);
47
+ Logger.log(`清理 token: ${token}`);
48
+ }
49
+ }
50
+ });
51
+ // 发送欢迎消息
52
+ this.sendMessage(socket, {
53
+ type: 'welcome',
54
+ message: 'Socket 服务器已连接',
55
+ timestamp: Date.now(),
56
+ });
57
+ }
58
+ /**
59
+ * 处理接收到的数据(支持粘包处理)
60
+ */
61
+ handleData(socket, clientId, data) {
62
+ // 将数据添加到缓冲区
63
+ const buffer = this.messageBuffers.get(socket) || '';
64
+ this.messageBuffers.set(socket, buffer + data);
65
+ // 按换行符分割消息
66
+ const messages = this.messageBuffers.get(socket).split('\n');
67
+ // 保留最后一个不完整的消息
68
+ this.messageBuffers.set(socket, messages.pop() || '');
69
+ // 处理每个完整的消息
70
+ for (const messageStr of messages) {
71
+ if (!messageStr.trim())
72
+ continue;
73
+ try {
74
+ const message = JSON.parse(messageStr);
75
+ this.handleMessage(socket, clientId, message);
76
+ }
77
+ catch (error) {
78
+ Logger.error(`解析 Socket 消息失败: ${error}`, '原始数据:', messageStr);
79
+ this.sendMessage(socket, { type: 'error', message: '无效的 JSON 格式' });
80
+ }
81
+ }
82
+ }
83
+ /**
84
+ * 处理解析后的消息
85
+ */
86
+ handleMessage(socket, clientId, message) {
87
+ Logger.log(`收到来自 ${clientId} 的消息类型: ${message.type}`);
88
+ // 处理不同类型的消息
89
+ switch (message.type) {
90
+ case 'ping':
91
+ this.sendMessage(socket, { type: 'pong', timestamp: Date.now() });
92
+ break;
93
+ case 'echo':
94
+ this.sendMessage(socket, { type: 'echo', data: message.data });
95
+ break;
96
+ case 'register-token':
97
+ // 注册 token,建立 token -> socket 映射
98
+ if (message.token) {
99
+ this.tokenSockets.set(message.token, socket);
100
+ // 保存 extra 数据(如果提供)
101
+ if (message.extra && typeof message.extra === 'object') {
102
+ this.tokenExtras.set(message.token, message.extra);
103
+ Logger.log(`已保存 extra 数据: ${message.token} -> ${JSON.stringify(message.extra)}`);
104
+ }
105
+ Logger.log(`已注册 token: ${message.token} -> ${clientId}`);
106
+ this.sendMessage(socket, {
107
+ type: 'token-registered',
108
+ token: message.token,
109
+ timestamp: Date.now()
110
+ });
111
+ }
112
+ else {
113
+ this.sendMessage(socket, { type: 'error', message: '缺少 token' });
114
+ }
115
+ break;
116
+ default:
117
+ this.sendMessage(socket, { type: 'error', message: '未知的消息类型' });
118
+ }
119
+ }
120
+ /**
121
+ * 发送消息到客户端
122
+ */
123
+ sendMessage(socket, message) {
124
+ try {
125
+ socket.write(JSON.stringify(message) + '\n');
126
+ }
127
+ catch (error) {
128
+ Logger.error('发送 Socket 消息失败:', error);
129
+ }
130
+ }
131
+ /**
132
+ * 广播消息到所有连接的客户端
133
+ */
134
+ broadcast(message) {
135
+ const data = JSON.stringify(message) + '\n';
136
+ for (const socket of this.connections) {
137
+ try {
138
+ socket.write(data);
139
+ }
140
+ catch (error) {
141
+ Logger.error('广播消息失败:', error);
142
+ }
143
+ }
144
+ }
145
+ /**
146
+ * 设置服务器事件监听
147
+ */
148
+ setupServerEvents() {
149
+ if (!this.server)
150
+ return;
151
+ this.server.on('error', (error) => {
152
+ Logger.error('Socket 服务器错误:', error);
153
+ });
154
+ this.server.on('close', () => {
155
+ Logger.log('Socket 服务器已关闭');
156
+ });
157
+ }
158
+ /**
159
+ * 启动 Socket 服务器
160
+ */
161
+ start(port) {
162
+ return new Promise((resolve, reject) => {
163
+ if (!this.server) {
164
+ reject(new Error('Socket 服务器未初始化'));
165
+ return;
166
+ }
167
+ this.port = port;
168
+ this.server.listen(port, () => {
169
+ Logger.log(`Socket server listening on port ${port}`);
170
+ resolve();
171
+ });
172
+ this.server.once('error', (error) => {
173
+ reject(error);
174
+ });
175
+ });
176
+ }
177
+ /**
178
+ * 发送认证响应到指定 token 的客户端
179
+ */
180
+ sendSubmitResponse(token, data) {
181
+ const socket = this.tokenSockets.get(token);
182
+ if (!socket) {
183
+ Logger.warn(`未找到 token 对应的 socket: ${token}`);
184
+ return false;
185
+ }
186
+ try {
187
+ this.sendMessage(socket, {
188
+ type: 'submit-response',
189
+ token,
190
+ data,
191
+ timestamp: Date.now(),
192
+ });
193
+ // 发送后清理 token 映射
194
+ this.tokenSockets.delete(token);
195
+ Logger.log(`已发送认证响应到 token: ${token}`);
196
+ return true;
197
+ }
198
+ catch (error) {
199
+ Logger.error(`发送认证响应失败: ${error}`);
200
+ return false;
201
+ }
202
+ }
203
+ /**
204
+ * 停止 Socket 服务器
205
+ */
206
+ stop() {
207
+ return new Promise((resolve) => {
208
+ // 关闭所有活跃连接
209
+ for (const socket of this.connections) {
210
+ socket.destroy();
211
+ }
212
+ this.connections.clear();
213
+ this.tokenSockets.clear();
214
+ this.tokenExtras.clear();
215
+ this.messageBuffers.clear();
216
+ // 关闭服务器
217
+ if (this.server) {
218
+ this.server.close(() => {
219
+ Logger.log('Socket 服务器已停止');
220
+ this.server = null;
221
+ resolve();
222
+ });
223
+ }
224
+ else {
225
+ resolve();
226
+ }
227
+ });
228
+ }
229
+ /**
230
+ * 获取当前连接数
231
+ */
232
+ getConnectionCount() {
233
+ return this.connections.size;
234
+ }
235
+ /**
236
+ * 获取服务器端口
237
+ */
238
+ getPort() {
239
+ return this.port;
240
+ }
241
+ /**
242
+ * 根据 token 获取对应的 extra 数据
243
+ */
244
+ getTokenExtra(token) {
245
+ return this.tokenExtras.get(token);
246
+ }
247
+ /**
248
+ * 根据 token 获取 extra 中的特定字段
249
+ */
250
+ getTokenExtraField(token, field) {
251
+ const extra = this.tokenExtras.get(token);
252
+ return extra?.[field];
253
+ }
254
+ /**
255
+ * 获取所有已注册的 token extra 映射
256
+ */
257
+ getAllTokenExtras() {
258
+ return new Map(this.tokenExtras);
259
+ }
260
+ }
@@ -0,0 +1,143 @@
1
+ import { Logger } from './logger.js';
2
+ // 缓存检测结果,避免重复检测
3
+ let cachedCapabilities = null;
4
+ /**
5
+ * 检测 MCP Client 的 API 支持程度
6
+ * @param mcpServer MCP 服务器实例
7
+ * @param forceRecheck 是否强制重新检测(忽略缓存)
8
+ * @returns ClientApiSupport 对象
9
+ */
10
+ export async function detectClientApiSupport(mcpServer, forceRecheck = false) {
11
+ // 使用缓存
12
+ if (cachedCapabilities && !forceRecheck) {
13
+ return cachedCapabilities;
14
+ }
15
+ const support = {
16
+ sampling: false,
17
+ roots: false,
18
+ };
19
+ try {
20
+ // 方法1: 通过 capabilities 协商机制检测
21
+ // MCP SDK 在连接时会进行能力协商
22
+ const serverInstance = mcpServer.server;
23
+ // 尝试获取客户端能力
24
+ if (typeof serverInstance.getClientCapabilities === 'function') {
25
+ const clientCapabilities = serverInstance.getClientCapabilities();
26
+ Logger.log('客户端能力:', JSON.stringify(clientCapabilities, null, 2));
27
+ if (clientCapabilities) {
28
+ // 检查 sampling 能力
29
+ support.sampling = clientCapabilities.sampling !== undefined;
30
+ // 检查 roots 能力
31
+ support.roots = clientCapabilities.roots !== undefined;
32
+ }
33
+ }
34
+ // 方法2: 如果 capabilities 检测不到,通过 try-catch 探测
35
+ // 检测 roots 支持
36
+ if (!support.roots) {
37
+ support.roots = await probeRootsSupport(mcpServer);
38
+ }
39
+ // 检测 sampling 支持(仅当 capabilities 未声明时才探测)
40
+ if (!support.sampling) {
41
+ support.sampling = await probeSamplingSupport(mcpServer);
42
+ }
43
+ }
44
+ catch (error) {
45
+ Logger.log('检测客户端能力时出错:', error);
46
+ }
47
+ // 缓存结果
48
+ cachedCapabilities = support;
49
+ Logger.log('MCP Client API 支持检测结果:', support);
50
+ return support;
51
+ }
52
+ /**
53
+ * 探测 listRoots API 支持
54
+ */
55
+ async function probeRootsSupport(mcpServer) {
56
+ try {
57
+ const result = await mcpServer.server.listRoots();
58
+ // 如果调用成功,说明支持
59
+ return result !== undefined;
60
+ }
61
+ catch (error) {
62
+ // 检查错误类型
63
+ const errorMessage = error?.message || String(error);
64
+ const errorCode = error?.code;
65
+ // Method not found 或明确不支持的错误
66
+ if (errorCode === -32601 ||
67
+ errorMessage.includes('not supported') ||
68
+ errorMessage.includes('not implemented') ||
69
+ errorMessage.includes('Method not found')) {
70
+ return false;
71
+ }
72
+ // 其他错误(如超时、网络问题)可能是暂时性的,保守认为支持
73
+ Logger.log('探测 listRoots 时出现非致命错误:', errorMessage);
74
+ return false;
75
+ }
76
+ }
77
+ /**
78
+ * 探测 createMessage (sampling) API 支持
79
+ * 注意:这个探测可能会产生实际的 API 调用,应谨慎使用
80
+ */
81
+ async function probeSamplingSupport(mcpServer) {
82
+ try {
83
+ // 使用最小化的测试请求
84
+ // 注意:某些客户端可能会实际执行这个请求,所以使用非常小的 maxTokens
85
+ await mcpServer.server.createMessage({
86
+ messages: [
87
+ {
88
+ role: 'user',
89
+ content: {
90
+ type: 'text',
91
+ text: 'ping', // 最小化测试消息
92
+ },
93
+ },
94
+ ],
95
+ maxTokens: 1, // 最小化 token 消耗
96
+ }, {
97
+ timeout: 10000, // 10秒超时
98
+ });
99
+ // 如果调用成功,说明支持
100
+ return true;
101
+ }
102
+ catch (error) {
103
+ const errorMessage = error?.message || String(error);
104
+ const errorCode = error?.code;
105
+ // Method not found 或明确不支持的错误
106
+ if (errorCode === -32601 ||
107
+ errorMessage.includes('not supported') ||
108
+ errorMessage.includes('not implemented') ||
109
+ errorMessage.includes('Method not found') ||
110
+ errorMessage.includes('Sampling not supported')) {
111
+ Logger.log('客户端不支持 sampling API');
112
+ return false;
113
+ }
114
+ // 超时或其他错误,可能是支持但调用失败
115
+ // 保守处理:认为不支持,让业务逻辑走降级路径
116
+ Logger.log('探测 sampling 时出现错误:', errorMessage);
117
+ return false;
118
+ }
119
+ }
120
+ /**
121
+ * 清除缓存的能力检测结果
122
+ * 在重新连接时应调用此方法
123
+ */
124
+ export function clearCapabilitiesCache() {
125
+ cachedCapabilities = null;
126
+ Logger.log('已清除客户端能力缓存');
127
+ }
128
+ /**
129
+ * 检查是否支持 sampling
130
+ * 便捷方法,直接返回 boolean
131
+ */
132
+ export async function isSamplingSupported(mcpServer) {
133
+ const support = await detectClientApiSupport(mcpServer);
134
+ return support.sampling;
135
+ }
136
+ /**
137
+ * 检查是否支持 roots
138
+ * 便捷方法,直接返回 boolean
139
+ */
140
+ export async function isRootsSupported(mcpServer) {
141
+ const support = await detectClientApiSupport(mcpServer);
142
+ return support.roots;
143
+ }