sloth-d2c-mcp 1.0.4-beta84 → 1.0.4-beta86

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/cli/run.js CHANGED
@@ -113,6 +113,76 @@ if (args[0] === 'config') {
113
113
 
114
114
  console.log(`[sloth] 已在后台启动,PID: ${child.pid}`);
115
115
  process.exit(0);
116
+ } else if (args[0] === 'stop') {
117
+ // stop 命令:停止监听 3100/3101 端口的 sloth 进程
118
+ const { execSync } = await import('node:child_process');
119
+ const os = await import('node:os');
120
+
121
+ const ports = [3100, 3101];
122
+ const pidsToKill = new Set();
123
+
124
+ for (const port of ports) {
125
+ try {
126
+ let output = '';
127
+ if (os.platform() === 'win32') {
128
+ // Windows: netstat -ano | findstr :PORT
129
+ output = execSync(`netstat -ano | findstr :${port}`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
130
+ const lines = output.trim().split('\n');
131
+ for (const line of lines) {
132
+ const match = line.match(/LISTENING\s+(\d+)/);
133
+ if (match) {
134
+ pidsToKill.add(match[1]);
135
+ }
136
+ }
137
+ } else {
138
+ // macOS/Linux: lsof -i :PORT
139
+ output = execSync(`lsof -i :${port} -t`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
140
+ const pids = output.trim().split('\n').filter(Boolean);
141
+ pids.forEach(pid => pidsToKill.add(pid.trim()));
142
+ }
143
+ } catch {
144
+ // 端口未被占用,忽略错误
145
+ }
146
+ }
147
+
148
+ if (pidsToKill.size === 0) {
149
+ console.log('[sloth] 未找到监听 3100/3101 端口的进程');
150
+ process.exit(0);
151
+ }
152
+
153
+ // 验证进程是否是 sloth 相关进程
154
+ let killedCount = 0;
155
+ for (const pid of pidsToKill) {
156
+ try {
157
+ let cmdline = '';
158
+ if (os.platform() === 'win32') {
159
+ cmdline = execSync(`wmic process where processid=${pid} get commandline`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
160
+ } else {
161
+ cmdline = execSync(`ps -p ${pid} -o command=`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
162
+ }
163
+
164
+ // 检查是否是 sloth 进程
165
+ const isSlothProcess = cmdline.includes('sloth') || cmdline.includes('d2c-mcp');
166
+
167
+ if (isSlothProcess) {
168
+ process.kill(Number(pid), 'SIGTERM');
169
+ console.log(`[sloth] 已停止进程 PID: ${pid}`);
170
+ killedCount++;
171
+ } else {
172
+ console.log(`[sloth] 跳过非 sloth 进程 PID: ${pid} (${cmdline.trim().slice(0, 50)}...)`);
173
+ }
174
+ } catch (e) {
175
+ // 进程可能已经退出
176
+ console.warn(`[sloth] 无法停止进程 PID: ${pid}`, e.message);
177
+ }
178
+ }
179
+
180
+ if (killedCount === 0) {
181
+ console.log('[sloth] 未找到正在运行的 sloth 进程');
182
+ } else {
183
+ console.log(`[sloth] 共停止 ${killedCount} 个进程`);
184
+ }
185
+ process.exit(0);
116
186
  } else {
117
187
  // 设置环境变量为CLI模式
118
188
  process.env.NODE_ENV = "cli";
@@ -11,7 +11,7 @@ import { loadConfig, startHttpServer, stopHttpServer, generateComponentId, stopS
11
11
  import { FileManager } from './utils/file-manager.js';
12
12
  import { updateImageMapIfNeeded } from './utils/update.js';
13
13
  import { Logger } from './utils/logger.js';
14
- import { getAvailablePort, saveImageFile, replaceImageSrc } from './utils/utils.js';
14
+ import { getAvailablePort, saveImageFile, replaceImageSrc, formatListRoots } from './utils/utils.js';
15
15
  import { processSampling, buildNestingStructure } from './core/sampling.js';
16
16
  import { buildComponentMappingPrompt, buildFullUpdatePromptForAI, buildPlaceholderPrompt } from './core/prompt-builder.js';
17
17
  import { detectClientApiSupport, clearCapabilitiesCache } from './utils/client-capabilities.js';
@@ -99,9 +99,8 @@ mcpServer.tool('d2c_figma', 'Convert Figma Design File to Code', {
99
99
  try {
100
100
  const rootRes = await mcpServer.server.listRoots();
101
101
  Logger.log('获取根目录:', rootRes);
102
- root = rootRes.roots[0]?.uri?.slice(7) || './';
103
- // 兼容window下roots返回的路径是url编码的
104
- root = decodeURIComponent(root);
102
+ root = formatListRoots(rootRes);
103
+ Logger.log('标准化根目录:', root);
105
104
  // 设置 fileManager 的工作目录根路径
106
105
  fileManager.setWorkspaceRoot(root);
107
106
  }
@@ -532,9 +531,8 @@ mcpServer.tool('mark_components', 'Mark and save components to project component
532
531
  try {
533
532
  const rootRes = await mcpServer.server.listRoots();
534
533
  Logger.log('获取根目录:', rootRes);
535
- root = rootRes.roots[0]?.uri?.slice(7) || './';
536
- // 兼容window下roots返回的路径是url编码的
537
- root = decodeURIComponent(root);
534
+ root = formatListRoots(rootRes);
535
+ Logger.log('标准化根目录:', root);
538
536
  // 设置 fileManager 的工作目录根路径
539
537
  fileManager.setWorkspaceRoot(root);
540
538
  }
@@ -3,6 +3,7 @@ import { execSync } from 'child_process';
3
3
  import axios from 'axios';
4
4
  import fs from 'fs';
5
5
  import path from 'path';
6
+ import { fileURLToPath } from 'url';
6
7
  /**
7
8
  * 获取可用的端口号,从指定端口开始寻找
8
9
  * @param startPort 起始端口号,默认3100
@@ -163,3 +164,15 @@ export const resetNodeListPosition = (nodeList) => {
163
164
  return node;
164
165
  });
165
166
  };
167
+ export const formatListRoots = (rootRes) => {
168
+ let root = rootRes.roots[0]?.uri;
169
+ if (root) {
170
+ // 兼容window下roots返回的路径是url编码的
171
+ root = decodeURIComponent(root);
172
+ root = fileURLToPath(root);
173
+ }
174
+ else {
175
+ root = './';
176
+ }
177
+ return root;
178
+ };
@@ -1,5 +1,5 @@
1
1
  {
2
- "buildTime": "2025-12-25T13:17:35.078Z",
2
+ "buildTime": "2025-12-26T06:57:50.809Z",
3
3
  "mode": "build",
4
4
  "pages": {
5
5
  "main": {