sloth-d2c-mcp 1.0.4-beta86 → 1.0.4-beta89
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/dist/build/server.js +32 -23
- package/dist/build/socket-client.js +5 -2
- package/dist/build/socket-server.js +27 -0
- package/dist/build/utils/file-manager.js +28 -1
- package/dist/build/utils/utils.js +6 -0
- package/dist/interceptor-web/dist/build-report.json +1 -1
- package/dist/interceptor-web/dist/detail.html +1 -1
- package/dist/interceptor-web/dist/index.html +1 -1
- package/package.json +3 -3
package/dist/build/server.js
CHANGED
|
@@ -32,10 +32,11 @@ const upload = multer({
|
|
|
32
32
|
});
|
|
33
33
|
// 导入默认提示词
|
|
34
34
|
import { chunkOptimizeCodePrompt, aggregationOptimizeCodePrompt, finalOptimizeCodePrompt, chunkOptimizeCodePromptVue, aggregationOptimizeCodePromptVue, finalOptimizeCodePromptVue, noSamplingAggregationPrompt, noSamplingAggregationPromptVue, } from 'sloth-d2c-node/convert';
|
|
35
|
+
import { getParam } from './utils/utils.js';
|
|
35
36
|
// 保存 HTTP 服务器实例
|
|
36
|
-
let httpServer = null;
|
|
37
|
+
export let httpServer = null;
|
|
37
38
|
// 保存 Socket 服务器实例
|
|
38
|
-
let socketServer = null;
|
|
39
|
+
export let socketServer = null;
|
|
39
40
|
// 管理所有活跃的传输对象,按 sessionId 分类
|
|
40
41
|
const transports = {
|
|
41
42
|
streamable: {}, // 流式 HTTP 传输
|
|
@@ -196,6 +197,13 @@ export async function startHttpServer(port = PORT, mcpServer, configManagerInsta
|
|
|
196
197
|
next();
|
|
197
198
|
});
|
|
198
199
|
// app.use(express.json());
|
|
200
|
+
app.use((req, _res, next) => {
|
|
201
|
+
// 通过referer取url上面的uuid
|
|
202
|
+
const uuid = getParam(req.headers.referer || '', 'token') || '';
|
|
203
|
+
req.uuid = uuid;
|
|
204
|
+
req.fileManager = fileManager.withUUID(uuid);
|
|
205
|
+
next();
|
|
206
|
+
});
|
|
199
207
|
// 处理流式 HTTP 的 POST 请求,支持会话复用和初始化
|
|
200
208
|
app.post('/mcp', async (req, res) => {
|
|
201
209
|
Logger.log('Received StreamableHTTP request');
|
|
@@ -333,11 +341,11 @@ export async function startHttpServer(port = PORT, mcpServer, configManagerInsta
|
|
|
333
341
|
// 如果提供了 fileKey,返回该 fileKey 的特定配置
|
|
334
342
|
const globalConfig = await configManager.load();
|
|
335
343
|
// const fileConfig = globalConfig.fileConfigs?.[fileKey] || {}
|
|
336
|
-
const fileConfig = (await fileManager.loadConfigSetting(fileKey, nodeId)) || {};
|
|
344
|
+
const fileConfig = (await req.fileManager.loadConfigSetting(fileKey, nodeId)) || {};
|
|
337
345
|
// 从 fileManager 按 nodeId 加载 groupsData 和 promptSetting
|
|
338
346
|
// const fileManager = new FileManager('d2c-mcp')
|
|
339
|
-
const groupsData = await fileManager.loadGroupsData(fileKey, nodeId);
|
|
340
|
-
const savedPromptSetting = await fileManager.loadPromptSetting(fileKey, nodeId);
|
|
347
|
+
const groupsData = await req.fileManager.loadGroupsData(fileKey, nodeId);
|
|
348
|
+
const savedPromptSetting = await req.fileManager.loadPromptSetting(fileKey, nodeId);
|
|
341
349
|
let curFramework = fileConfig?.convertSetting?.framework;
|
|
342
350
|
// 获取框架列表
|
|
343
351
|
const frameworks = await configManager.getFrameworks();
|
|
@@ -681,11 +689,11 @@ export async function startHttpServer(port = PORT, mcpServer, configManagerInsta
|
|
|
681
689
|
// 使用 fileManager 按 nodeId 保存 groupsData 和 promptSetting
|
|
682
690
|
// const fileManager = new FileManager('d2c-mcp')
|
|
683
691
|
if (fileKey && value.groupsData && Array.isArray(value.groupsData)) {
|
|
684
|
-
await fileManager.saveGroupsData(fileKey, nodeId, value.groupsData);
|
|
692
|
+
await req.fileManager.saveGroupsData(fileKey, nodeId, value.groupsData);
|
|
685
693
|
Logger.log(`已保存 groupsData 到 fileKey "${fileKey}", nodeId "${nodeId}":`, value.groupsData.length, '个分组');
|
|
686
694
|
}
|
|
687
695
|
if (fileKey && value.promptSetting) {
|
|
688
|
-
await fileManager.savePromptSetting(fileKey, nodeId, value.promptSetting);
|
|
696
|
+
await req.fileManager.savePromptSetting(fileKey, nodeId, value.promptSetting);
|
|
689
697
|
Logger.log(`已保存 promptSetting 到 fileKey "${fileKey}", nodeId "${nodeId}"`);
|
|
690
698
|
}
|
|
691
699
|
// 如果有 MCP 配置,更新全局配置
|
|
@@ -766,9 +774,9 @@ export async function startHttpServer(port = PORT, mcpServer, configManagerInsta
|
|
|
766
774
|
/**
|
|
767
775
|
* 获取项目根目录
|
|
768
776
|
*/
|
|
769
|
-
function getProjectRoot() {
|
|
777
|
+
function getProjectRoot(fs) {
|
|
770
778
|
try {
|
|
771
|
-
const projectPath =
|
|
779
|
+
const projectPath = fs.getWorkspaceRoot();
|
|
772
780
|
return projectPath || './';
|
|
773
781
|
}
|
|
774
782
|
catch (error) {
|
|
@@ -782,7 +790,7 @@ export async function startHttpServer(port = PORT, mcpServer, configManagerInsta
|
|
|
782
790
|
app.get('/getProjectFiles', async (req, res) => {
|
|
783
791
|
try {
|
|
784
792
|
const { directory = '' } = req.query;
|
|
785
|
-
const projectPath = getProjectRoot();
|
|
793
|
+
const projectPath = getProjectRoot(req.fileManager);
|
|
786
794
|
const targetPath = path.join(projectPath, directory);
|
|
787
795
|
Logger.log(`获取项目文件树: ${targetPath}`);
|
|
788
796
|
// 排除的文件扩展名(静态资源和配置文件)
|
|
@@ -912,12 +920,12 @@ export async function startHttpServer(port = PORT, mcpServer, configManagerInsta
|
|
|
912
920
|
return;
|
|
913
921
|
}
|
|
914
922
|
Logger.log(`批量分析 ${filePaths.length} 个项目文件`);
|
|
915
|
-
const projectPath = getProjectRoot();
|
|
923
|
+
const projectPath = getProjectRoot(req.fileManager);
|
|
916
924
|
// 尝试加载保存的提示词设置
|
|
917
925
|
let savedPromptSetting = null;
|
|
918
926
|
if (fileKey && nodeId) {
|
|
919
927
|
try {
|
|
920
|
-
savedPromptSetting = await fileManager.loadPromptSetting(fileKey, nodeId);
|
|
928
|
+
savedPromptSetting = await req.fileManager.loadPromptSetting(fileKey, nodeId);
|
|
921
929
|
Logger.log(`已加载提示词设置: fileKey=${fileKey}, nodeId=${nodeId}`);
|
|
922
930
|
}
|
|
923
931
|
catch (error) {
|
|
@@ -1109,7 +1117,7 @@ export async function startHttpServer(port = PORT, mcpServer, configManagerInsta
|
|
|
1109
1117
|
}
|
|
1110
1118
|
Logger.log(`准备保存 ${components.length} 个组件`);
|
|
1111
1119
|
// 读取现有组件
|
|
1112
|
-
const existingComponents = await fileManager.loadComponentsDatabase();
|
|
1120
|
+
const existingComponents = await req.fileManager.loadComponentsDatabase();
|
|
1113
1121
|
const existingMap = new Map(existingComponents.map((c) => [c.id, c]));
|
|
1114
1122
|
let addedCount = 0;
|
|
1115
1123
|
let updatedCount = 0;
|
|
@@ -1128,7 +1136,7 @@ export async function startHttpServer(port = PORT, mcpServer, configManagerInsta
|
|
|
1128
1136
|
}
|
|
1129
1137
|
const allComponents = Array.from(existingMap.values());
|
|
1130
1138
|
// 保存到文件(带备份)
|
|
1131
|
-
await fileManager.saveComponentsDatabase(allComponents);
|
|
1139
|
+
await req.fileManager.saveComponentsDatabase(allComponents);
|
|
1132
1140
|
Logger.log(`✅ 成功保存:新增 ${addedCount} 个,更新 ${updatedCount} 个,共 ${allComponents.length} 个组件`);
|
|
1133
1141
|
res.json({
|
|
1134
1142
|
success: true,
|
|
@@ -1154,7 +1162,7 @@ export async function startHttpServer(port = PORT, mcpServer, configManagerInsta
|
|
|
1154
1162
|
app.get('/scanComponents', async (req, res) => {
|
|
1155
1163
|
try {
|
|
1156
1164
|
const { includePaths, excludePaths } = req.query;
|
|
1157
|
-
const projectPath = getProjectRoot();
|
|
1165
|
+
const projectPath = getProjectRoot(req.fileManager);
|
|
1158
1166
|
// 读取项目组件(从 .sloth/components.json)
|
|
1159
1167
|
let projectComponents = [];
|
|
1160
1168
|
const slothPath = path.join(projectPath, '.sloth', 'components.json');
|
|
@@ -1230,7 +1238,7 @@ export async function startHttpServer(port = PORT, mcpServer, configManagerInsta
|
|
|
1230
1238
|
const currentScreenshotPath = path.join(tempDir, `suggest_${Date.now()}.png`);
|
|
1231
1239
|
await fs.promises.writeFile(currentScreenshotPath, currentScreenshotFile.buffer);
|
|
1232
1240
|
// 从 components.json 加载所有组件
|
|
1233
|
-
const components = await fileManager.loadComponentsDatabase();
|
|
1241
|
+
const components = await req.fileManager.loadComponentsDatabase();
|
|
1234
1242
|
if (!components || components.length === 0) {
|
|
1235
1243
|
Logger.log('未找到已保存的组件,无法生成建议');
|
|
1236
1244
|
await fs.promises.unlink(currentScreenshotPath).catch(() => { });
|
|
@@ -1250,7 +1258,7 @@ export async function startHttpServer(port = PORT, mcpServer, configManagerInsta
|
|
|
1250
1258
|
continue;
|
|
1251
1259
|
}
|
|
1252
1260
|
// 根据 signature 搜索截图文件
|
|
1253
|
-
const screenshotPath = await fileManager.findScreenshotByHash(component.signature);
|
|
1261
|
+
const screenshotPath = await req.fileManager.findScreenshotByHash(component.signature);
|
|
1254
1262
|
if (!screenshotPath) {
|
|
1255
1263
|
Logger.log(`组件 ${component.name} 的截图未找到 (hash: ${component.signature}),跳过`);
|
|
1256
1264
|
continue;
|
|
@@ -1334,7 +1342,7 @@ export async function startHttpServer(port = PORT, mcpServer, configManagerInsta
|
|
|
1334
1342
|
}
|
|
1335
1343
|
Logger.log(`接收到截图上传请求: fileKey=${fileKey}, nodeId=${nodeId}, hash=${hash}`);
|
|
1336
1344
|
// 保存截图到 .sloth/{fileKey}/{nodeId}/screenshots/{hash}.png
|
|
1337
|
-
await fileManager.saveScreenshot(fileKey, nodeId, hash, file.buffer);
|
|
1345
|
+
await req.fileManager.saveScreenshot(fileKey, nodeId, hash, file.buffer);
|
|
1338
1346
|
Logger.log(`截图上传成功: ${hash}.png`);
|
|
1339
1347
|
res.json({
|
|
1340
1348
|
success: true,
|
|
@@ -1355,7 +1363,7 @@ export async function startHttpServer(port = PORT, mcpServer, configManagerInsta
|
|
|
1355
1363
|
app.get('/listDesignSnapshots', async (req, res) => {
|
|
1356
1364
|
try {
|
|
1357
1365
|
Logger.log('获取设计稿快照列表');
|
|
1358
|
-
const workspaceRoot = getProjectRoot();
|
|
1366
|
+
const workspaceRoot = getProjectRoot(req.fileManager);
|
|
1359
1367
|
Logger.log('workspaceRoot:', workspaceRoot);
|
|
1360
1368
|
if (!workspaceRoot) {
|
|
1361
1369
|
res.json({
|
|
@@ -1550,8 +1558,8 @@ export async function startHttpServer(port = PORT, mcpServer, configManagerInsta
|
|
|
1550
1558
|
return;
|
|
1551
1559
|
}
|
|
1552
1560
|
// 2. 加载新旧 groupsData
|
|
1553
|
-
const oldGroupsData = (await fileManager.loadGroupsData(oldFileKey, oldNodeId)) || [];
|
|
1554
|
-
const newGroupsData = (await fileManager.loadGroupsData(newFileKey, newNodeId)) || [];
|
|
1561
|
+
const oldGroupsData = (await req.fileManager.loadGroupsData(oldFileKey, oldNodeId)) || [];
|
|
1562
|
+
const newGroupsData = (await req.fileManager.loadGroupsData(newFileKey, newNodeId)) || [];
|
|
1555
1563
|
// 3. 执行 HTML 差异分析
|
|
1556
1564
|
// @ts-ignore
|
|
1557
1565
|
const { diffLines } = await import('diff');
|
|
@@ -1770,8 +1778,9 @@ export async function getUserInput(payload) {
|
|
|
1770
1778
|
// 连接到 Socket 服务器
|
|
1771
1779
|
await socketClient.connect();
|
|
1772
1780
|
Logger.log('Socket 客户端已连接');
|
|
1773
|
-
|
|
1774
|
-
|
|
1781
|
+
const workspaceRoot = fileManager.getWorkspaceRoot();
|
|
1782
|
+
// 注册 token 并等待响应,同时传递 extra 数据给主进程
|
|
1783
|
+
const responsePromise = socketClient.registerToken(token, { workspaceRoot });
|
|
1775
1784
|
// 打开浏览器
|
|
1776
1785
|
await open(authUrl);
|
|
1777
1786
|
// 等待认证响应
|
|
@@ -115,8 +115,10 @@ export class SocketClient {
|
|
|
115
115
|
}
|
|
116
116
|
/**
|
|
117
117
|
* 注册 token,等待认证响应
|
|
118
|
+
* @param token 认证 token
|
|
119
|
+
* @param extra 额外数据(可选),如 { workspaceRoot: string, ... }
|
|
118
120
|
*/
|
|
119
|
-
registerToken(token) {
|
|
121
|
+
registerToken(token, extra) {
|
|
120
122
|
return new Promise((resolve, reject) => {
|
|
121
123
|
// 设置超时(可选,根据需求调整)
|
|
122
124
|
const timeout = setTimeout(() => {
|
|
@@ -132,9 +134,10 @@ export class SocketClient {
|
|
|
132
134
|
this.send({
|
|
133
135
|
type: 'register-token',
|
|
134
136
|
token,
|
|
137
|
+
extra,
|
|
135
138
|
timestamp: Date.now(),
|
|
136
139
|
});
|
|
137
|
-
Logger.log(`已注册 token: ${token},等待认证响应...`);
|
|
140
|
+
Logger.log(`已注册 token: ${token},extra: ${JSON.stringify(extra)},等待认证响应...`);
|
|
138
141
|
});
|
|
139
142
|
}
|
|
140
143
|
/**
|
|
@@ -8,6 +8,7 @@ export class SocketServer {
|
|
|
8
8
|
server = null;
|
|
9
9
|
connections = new Set();
|
|
10
10
|
tokenSockets = new Map(); // token -> socket 映射
|
|
11
|
+
tokenExtras = new Map(); // token -> extra 数据映射
|
|
11
12
|
messageBuffers = new Map(); // socket -> 消息缓冲区
|
|
12
13
|
port = 0;
|
|
13
14
|
constructor() {
|
|
@@ -42,6 +43,7 @@ export class SocketServer {
|
|
|
42
43
|
for (const [token, sock] of this.tokenSockets.entries()) {
|
|
43
44
|
if (sock === socket) {
|
|
44
45
|
this.tokenSockets.delete(token);
|
|
46
|
+
this.tokenExtras.delete(token);
|
|
45
47
|
Logger.log(`清理 token: ${token}`);
|
|
46
48
|
}
|
|
47
49
|
}
|
|
@@ -95,6 +97,11 @@ export class SocketServer {
|
|
|
95
97
|
// 注册 token,建立 token -> socket 映射
|
|
96
98
|
if (message.token) {
|
|
97
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
|
+
}
|
|
98
105
|
Logger.log(`已注册 token: ${message.token} -> ${clientId}`);
|
|
99
106
|
this.sendMessage(socket, {
|
|
100
107
|
type: 'token-registered',
|
|
@@ -204,6 +211,7 @@ export class SocketServer {
|
|
|
204
211
|
}
|
|
205
212
|
this.connections.clear();
|
|
206
213
|
this.tokenSockets.clear();
|
|
214
|
+
this.tokenExtras.clear();
|
|
207
215
|
this.messageBuffers.clear();
|
|
208
216
|
// 关闭服务器
|
|
209
217
|
if (this.server) {
|
|
@@ -230,4 +238,23 @@ export class SocketServer {
|
|
|
230
238
|
getPort() {
|
|
231
239
|
return this.port;
|
|
232
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
|
+
}
|
|
233
260
|
}
|
|
@@ -2,6 +2,7 @@ import { promises as fs } from 'fs';
|
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import envPaths from 'env-paths';
|
|
4
4
|
import { Logger } from '../utils/logger.js';
|
|
5
|
+
import { httpServer, socketServer } from '../server.js';
|
|
5
6
|
/**
|
|
6
7
|
* 通用文件管理器
|
|
7
8
|
* 可以保存任何类型的文件,使用fileKey和nodeId组织目录结构
|
|
@@ -9,11 +10,23 @@ import { Logger } from '../utils/logger.js';
|
|
|
9
10
|
export class FileManager {
|
|
10
11
|
paths; // 应用路径配置
|
|
11
12
|
baseDir; // 基础存储目录
|
|
12
|
-
|
|
13
|
+
_workspaceRoot = null; // MCP 工作目录根路径
|
|
14
|
+
_uuid = '';
|
|
13
15
|
constructor(appName) {
|
|
14
16
|
this.paths = envPaths(appName);
|
|
15
17
|
this.baseDir = path.join(this.paths.data, 'files');
|
|
16
18
|
}
|
|
19
|
+
get workspaceRoot() {
|
|
20
|
+
const isMainProcess = httpServer !== null;
|
|
21
|
+
if (isMainProcess && this._uuid) {
|
|
22
|
+
const workspaceRoot = socketServer?.getTokenExtraField(this._uuid, 'workspaceRoot');
|
|
23
|
+
return workspaceRoot || this._workspaceRoot;
|
|
24
|
+
}
|
|
25
|
+
return this._workspaceRoot;
|
|
26
|
+
}
|
|
27
|
+
set workspaceRoot(rootPath) {
|
|
28
|
+
this._workspaceRoot = rootPath;
|
|
29
|
+
}
|
|
17
30
|
/**
|
|
18
31
|
* 设置工作目录根路径(用于 MCP 工作项目)
|
|
19
32
|
* @param rootPath - 工作目录根路径
|
|
@@ -786,5 +799,19 @@ export class FileManager {
|
|
|
786
799
|
return [];
|
|
787
800
|
}
|
|
788
801
|
}
|
|
802
|
+
/**
|
|
803
|
+
* 创建一个临时的 FileManager 代理,仅在本次链式调用中使用指定的 uuid
|
|
804
|
+
* 不会影响原始实例的 _uuid 值
|
|
805
|
+
* @param uuid - 临时使用的 uuid
|
|
806
|
+
* @returns 带有临时 uuid 的 FileManager 代理对象
|
|
807
|
+
*/
|
|
808
|
+
withUUID(uuid) {
|
|
809
|
+
if (!uuid) {
|
|
810
|
+
return this;
|
|
811
|
+
}
|
|
812
|
+
const proxy = Object.create(this);
|
|
813
|
+
proxy._uuid = uuid;
|
|
814
|
+
return proxy;
|
|
815
|
+
}
|
|
789
816
|
}
|
|
790
817
|
export default FileManager;
|