aicodeswitch 1.10.2 → 2.0.1
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/CHANGELOG.md +2 -0
- package/bin/cli.js +7 -9
- package/bin/restart.js +7 -229
- package/bin/restore.js +1 -1
- package/bin/start.js +75 -87
- package/bin/stop.js +77 -14
- package/bin/ui.js +19 -134
- package/bin/update.js +1 -1
- package/bin/utils/get-server.js +58 -0
- package/bin/utils/port-utils.js +118 -0
- package/bin/version.js +1 -1
- package/dist/server/database.js +196 -116
- package/dist/server/main.js +116 -22
- package/dist/server/proxy-server.js +334 -158
- package/dist/server/transformers/claude-openai.js +86 -3
- package/dist/server/transformers/streaming.js +4 -1
- package/dist/server/utils.js +16 -0
- package/dist/ui/assets/index-BLqGemLn.js +423 -0
- package/dist/ui/assets/index-IVPeH7yC.css +1 -0
- package/dist/ui/index.html +2 -2
- package/dist/ui/migration.md +7 -0
- package/package.json +3 -2
- package/public/migration.md +7 -0
- package/dist/ui/assets/index-D6RrKKB5.js +0 -391
- package/dist/ui/assets/index-DU8EG0kT.css +0 -1
package/CHANGELOG.md
CHANGED
package/bin/cli.js
CHANGED
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const path = require('path');
|
|
4
|
-
|
|
5
3
|
const args = process.argv.slice(2);
|
|
6
4
|
const command = args[0];
|
|
7
5
|
|
|
8
6
|
const commands = {
|
|
9
|
-
start:
|
|
10
|
-
stop:
|
|
11
|
-
restart:
|
|
12
|
-
update:
|
|
13
|
-
restore:
|
|
14
|
-
version:
|
|
15
|
-
ui:
|
|
7
|
+
start: require('./start'),
|
|
8
|
+
stop: require('./stop'),
|
|
9
|
+
restart: require('./restart'),
|
|
10
|
+
update: require('./update'),
|
|
11
|
+
restore: require('./restore'),
|
|
12
|
+
version: require('./version'),
|
|
13
|
+
ui: require('./ui'),
|
|
16
14
|
};
|
|
17
15
|
|
|
18
16
|
if (!command || !commands[command]) {
|
package/bin/restart.js
CHANGED
|
@@ -1,232 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const chalk = require('chalk');
|
|
6
|
-
const boxen = require('boxen');
|
|
7
|
-
const ora = require('ora');
|
|
1
|
+
// 重启服务器 - 依次执行 stop 和 start 命令
|
|
2
|
+
// 这样可以复用 stop 和 start 命令中的所有逻辑
|
|
3
|
+
const stop = require('./stop');
|
|
4
|
+
const start = require('./start');
|
|
8
5
|
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
// 确保目录存在
|
|
13
|
-
const ensureDir = (filePath) => {
|
|
14
|
-
const dir = path.dirname(filePath);
|
|
15
|
-
if (!fs.existsSync(dir)) {
|
|
16
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
17
|
-
}
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const isServerRunning = () => {
|
|
21
|
-
if (!fs.existsSync(PID_FILE)) {
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
try {
|
|
26
|
-
const pid = parseInt(fs.readFileSync(PID_FILE, 'utf-8'), 10);
|
|
27
|
-
// 检查进程是否存在
|
|
28
|
-
process.kill(pid, 0);
|
|
29
|
-
return true;
|
|
30
|
-
} catch (err) {
|
|
31
|
-
// 进程不存在,删除过期的 PID 文件
|
|
32
|
-
fs.unlinkSync(PID_FILE);
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
const getServerInfo = () => {
|
|
38
|
-
// 尝试多个可能的配置文件位置
|
|
39
|
-
const possiblePaths = [
|
|
40
|
-
path.join(os.homedir(), '.aicodeswitch', '.env'),
|
|
41
|
-
path.join(os.homedir(), '.aicodeswitch', 'aicodeswitch.conf')
|
|
42
|
-
];
|
|
43
|
-
|
|
44
|
-
let host = '127.0.0.1';
|
|
45
|
-
let port = 4567;
|
|
46
|
-
|
|
47
|
-
for (const dotenvPath of possiblePaths) {
|
|
48
|
-
if (fs.existsSync(dotenvPath)) {
|
|
49
|
-
const content = fs.readFileSync(dotenvPath, 'utf-8');
|
|
50
|
-
const hostMatch = content.match(/HOST=(.+)/);
|
|
51
|
-
const portMatch = content.match(/PORT=(.+)/);
|
|
52
|
-
|
|
53
|
-
if (hostMatch) host = hostMatch[1].trim();
|
|
54
|
-
if (portMatch) port = parseInt(portMatch[1].trim(), 10);
|
|
55
|
-
break;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return { host, port };
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const stopServer = async () => {
|
|
63
|
-
if (!fs.existsSync(PID_FILE)) {
|
|
64
|
-
return true; // 服务未运行,视为停止成功
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const spinner = ora({
|
|
68
|
-
text: chalk.cyan('Stopping server...'),
|
|
69
|
-
color: 'cyan'
|
|
70
|
-
}).start();
|
|
71
|
-
|
|
72
|
-
try {
|
|
73
|
-
const pid = parseInt(fs.readFileSync(PID_FILE, 'utf-8'), 10);
|
|
74
|
-
|
|
75
|
-
try {
|
|
76
|
-
process.kill(pid, 'SIGTERM');
|
|
77
|
-
|
|
78
|
-
// 等待进程停止
|
|
79
|
-
let attempts = 0;
|
|
80
|
-
const maxAttempts = 10;
|
|
81
|
-
|
|
82
|
-
await new Promise((resolve) => {
|
|
83
|
-
const checkStopped = setInterval(() => {
|
|
84
|
-
attempts++;
|
|
85
|
-
try {
|
|
86
|
-
process.kill(pid, 0);
|
|
87
|
-
if (attempts >= maxAttempts) {
|
|
88
|
-
clearInterval(checkStopped);
|
|
89
|
-
// 强制终止
|
|
90
|
-
try {
|
|
91
|
-
process.kill(pid, 'SIGKILL');
|
|
92
|
-
} catch (e) {
|
|
93
|
-
// 进程可能已经停止
|
|
94
|
-
}
|
|
95
|
-
resolve();
|
|
96
|
-
}
|
|
97
|
-
} catch (err) {
|
|
98
|
-
// 进程已停止
|
|
99
|
-
clearInterval(checkStopped);
|
|
100
|
-
resolve();
|
|
101
|
-
}
|
|
102
|
-
}, 200);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
spinner.succeed(chalk.green('Server stopped'));
|
|
106
|
-
fs.unlinkSync(PID_FILE);
|
|
107
|
-
return true;
|
|
108
|
-
} catch (err) {
|
|
109
|
-
// 进程不存在
|
|
110
|
-
spinner.succeed(chalk.green('Server stopped'));
|
|
111
|
-
fs.unlinkSync(PID_FILE);
|
|
112
|
-
return true;
|
|
113
|
-
}
|
|
114
|
-
} catch (err) {
|
|
115
|
-
spinner.fail(chalk.red('Failed to stop server'));
|
|
116
|
-
return false;
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
const startServer = async () => {
|
|
121
|
-
const spinner = ora({
|
|
122
|
-
text: chalk.cyan('Starting server...'),
|
|
123
|
-
color: 'cyan'
|
|
124
|
-
}).start();
|
|
125
|
-
|
|
126
|
-
ensureDir(PID_FILE);
|
|
127
|
-
ensureDir(LOG_FILE);
|
|
128
|
-
|
|
129
|
-
// 找到 main.js 的路径
|
|
130
|
-
const serverPath = path.join(__dirname, '..', 'dist', 'server', 'main.js');
|
|
131
|
-
|
|
132
|
-
if (!fs.existsSync(serverPath)) {
|
|
133
|
-
spinner.fail(chalk.red('Server file not found!'));
|
|
134
|
-
console.log(chalk.yellow(`\nPlease run ${chalk.cyan('npm run build')} first.\n`));
|
|
135
|
-
return false;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// 启动服务器进程 - 完全分离
|
|
139
|
-
// 打开日志文件用于输出
|
|
140
|
-
const logFd = fs.openSync(LOG_FILE, 'a');
|
|
141
|
-
|
|
142
|
-
const serverProcess = spawn('node', [serverPath], {
|
|
143
|
-
detached: true,
|
|
144
|
-
stdio: ['ignore', logFd, logFd] // 使用文件描述符
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
// 关闭文件描述符(子进程会保持打开)
|
|
148
|
-
fs.closeSync(logFd);
|
|
149
|
-
|
|
150
|
-
// 保存 PID
|
|
151
|
-
fs.writeFileSync(PID_FILE, serverProcess.pid.toString());
|
|
152
|
-
|
|
153
|
-
// 分离进程,让父进程可以退出
|
|
154
|
-
serverProcess.unref();
|
|
155
|
-
|
|
156
|
-
// 等待服务器启动
|
|
157
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
158
|
-
|
|
159
|
-
// 检查服务器是否成功启动
|
|
160
|
-
if (fs.existsSync(PID_FILE)) {
|
|
161
|
-
try {
|
|
162
|
-
const pid = parseInt(fs.readFileSync(PID_FILE, 'utf-8'), 10);
|
|
163
|
-
process.kill(pid, 0);
|
|
164
|
-
spinner.succeed(chalk.green('Server started'));
|
|
165
|
-
return true;
|
|
166
|
-
} catch (err) {
|
|
167
|
-
spinner.fail(chalk.red('Failed to start server'));
|
|
168
|
-
return false;
|
|
169
|
-
}
|
|
170
|
-
} else {
|
|
171
|
-
spinner.fail(chalk.red('Failed to start server'));
|
|
172
|
-
return false;
|
|
173
|
-
}
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
const restart = async () => {
|
|
177
|
-
console.log('\n');
|
|
178
|
-
|
|
179
|
-
const wasRunning = isServerRunning();
|
|
180
|
-
|
|
181
|
-
if (wasRunning) {
|
|
182
|
-
console.log(chalk.cyan('🔄 Restarting AI Code Switch server...\n'));
|
|
183
|
-
|
|
184
|
-
// 停止服务器
|
|
185
|
-
const stopped = await stopServer();
|
|
186
|
-
if (!stopped) {
|
|
187
|
-
console.log(chalk.red('\nFailed to stop server. Restart aborted.\n'));
|
|
188
|
-
process.exit(1);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// 等待一下确保端口释放
|
|
192
|
-
await new Promise(resolve => setTimeout(resolve, 500));
|
|
193
|
-
} else {
|
|
194
|
-
console.log(chalk.cyan('Starting AI Code Switch server...\n'));
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// 启动服务器
|
|
198
|
-
const started = await startServer();
|
|
199
|
-
|
|
200
|
-
if (!started) {
|
|
201
|
-
console.log(chalk.yellow(`\nCheck logs: ${chalk.cyan(LOG_FILE)}\n`));
|
|
202
|
-
process.exit(1);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const { host, port } = getServerInfo();
|
|
206
|
-
const url = `http://${host}:${port}`;
|
|
207
|
-
|
|
208
|
-
// 显示漂亮的启动信息
|
|
209
|
-
console.log(boxen(
|
|
210
|
-
chalk.green.bold('🚀 AI Code Switch Server\n\n') +
|
|
211
|
-
chalk.white('Status: ') + chalk.green.bold('● Running\n') +
|
|
212
|
-
chalk.white('URL: ') + chalk.cyan.bold(url) + '\n' +
|
|
213
|
-
chalk.white('Logs: ') + chalk.gray(LOG_FILE) + '\n\n' +
|
|
214
|
-
chalk.gray('Server has been ' + (wasRunning ? 'restarted' : 'started') + ' successfully'),
|
|
215
|
-
{
|
|
216
|
-
padding: 1,
|
|
217
|
-
margin: 1,
|
|
218
|
-
borderStyle: 'double',
|
|
219
|
-
borderColor: 'green'
|
|
220
|
-
}
|
|
221
|
-
));
|
|
222
|
-
|
|
223
|
-
console.log(chalk.cyan('💡 Tips:\n'));
|
|
224
|
-
console.log(chalk.white(' • Open browser: ') + chalk.cyan(url));
|
|
225
|
-
console.log(chalk.white(' • View logs: ') + chalk.gray(`tail -f ${LOG_FILE}`));
|
|
226
|
-
console.log(chalk.white(' • Stop server: ') + chalk.yellow('aicos stop'));
|
|
227
|
-
console.log('\n');
|
|
228
|
-
|
|
229
|
-
process.exit(0);
|
|
6
|
+
const restart = () => {
|
|
7
|
+
stop({ silent: true, callback: () => start() });
|
|
230
8
|
};
|
|
231
9
|
|
|
232
|
-
module.exports = restart
|
|
10
|
+
module.exports = restart;
|
package/bin/restore.js
CHANGED
package/bin/start.js
CHANGED
|
@@ -5,6 +5,7 @@ const os = require('os');
|
|
|
5
5
|
const chalk = require('chalk');
|
|
6
6
|
const boxen = require('boxen');
|
|
7
7
|
const ora = require('ora');
|
|
8
|
+
const { isServerRunning, getServerInfo } = require('./utils/get-server');
|
|
8
9
|
|
|
9
10
|
const PID_FILE = path.join(os.homedir(), '.aicodeswitch', 'server.pid');
|
|
10
11
|
const LOG_FILE = path.join(os.homedir(), '.aicodeswitch', 'server.log');
|
|
@@ -17,71 +18,49 @@ const ensureDir = (filePath) => {
|
|
|
17
18
|
}
|
|
18
19
|
};
|
|
19
20
|
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
try {
|
|
26
|
-
const pid = parseInt(fs.readFileSync(PID_FILE, 'utf-8'), 10);
|
|
27
|
-
// 检查进程是否存在
|
|
28
|
-
process.kill(pid, 0);
|
|
29
|
-
return true;
|
|
30
|
-
} catch (err) {
|
|
31
|
-
// 进程不存在,删除过期的 PID 文件
|
|
32
|
-
fs.unlinkSync(PID_FILE);
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
const getServerInfo = () => {
|
|
38
|
-
// 尝试多个可能的配置文件位置
|
|
39
|
-
const possiblePaths = [
|
|
40
|
-
path.join(os.homedir(), '.aicodeswitch', '.env'),
|
|
41
|
-
path.join(os.homedir(), '.aicodeswitch', 'aicodeswitch.conf')
|
|
42
|
-
];
|
|
43
|
-
|
|
44
|
-
let host = '127.0.0.1';
|
|
45
|
-
let port = 4567;
|
|
46
|
-
|
|
47
|
-
for (const dotenvPath of possiblePaths) {
|
|
48
|
-
if (fs.existsSync(dotenvPath)) {
|
|
49
|
-
const content = fs.readFileSync(dotenvPath, 'utf-8');
|
|
50
|
-
const hostMatch = content.match(/HOST=(.+)/);
|
|
51
|
-
const portMatch = content.match(/PORT=(.+)/);
|
|
52
|
-
|
|
53
|
-
if (hostMatch) host = hostMatch[1].trim();
|
|
54
|
-
if (portMatch) port = parseInt(portMatch[1].trim(), 10);
|
|
55
|
-
break;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return { host, port };
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const start = async () => {
|
|
21
|
+
const start = async (options = {}) => {
|
|
22
|
+
const { silent = false, noExit = false, callback } = options;
|
|
63
23
|
console.log('\n');
|
|
64
24
|
|
|
65
|
-
//
|
|
25
|
+
// 已经运行
|
|
66
26
|
if (isServerRunning()) {
|
|
67
27
|
const { host, port } = getServerInfo();
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
28
|
+
if (!silent) {
|
|
29
|
+
if (!silent) {
|
|
30
|
+
console.log(boxen(
|
|
31
|
+
chalk.yellow.bold('⚠ Server is already running!\n\n') +
|
|
32
|
+
chalk.white(`URL: `) + chalk.cyan.bold(`http://${host}:${port}`),
|
|
33
|
+
{
|
|
34
|
+
padding: 1,
|
|
35
|
+
margin: 1,
|
|
36
|
+
borderStyle: 'round',
|
|
37
|
+
borderColor: 'yellow'
|
|
38
|
+
}
|
|
39
|
+
));
|
|
40
|
+
console.log('');
|
|
76
41
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
42
|
+
|
|
43
|
+
if (callback) {
|
|
44
|
+
callback();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (!noExit) {
|
|
48
|
+
process.exit(0);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
if (callback) callback();
|
|
54
|
+
if (!noExit) process.exit(0);
|
|
55
|
+
return true;
|
|
80
56
|
}
|
|
81
57
|
|
|
58
|
+
|
|
59
|
+
// 启动服务器
|
|
60
|
+
|
|
82
61
|
const spinner = ora({
|
|
83
62
|
text: chalk.cyan('Starting AI Code Switch server...'),
|
|
84
|
-
color: 'cyan'
|
|
63
|
+
color: 'cyan',
|
|
85
64
|
}).start();
|
|
86
65
|
|
|
87
66
|
ensureDir(PID_FILE);
|
|
@@ -93,7 +72,8 @@ const start = async () => {
|
|
|
93
72
|
if (!fs.existsSync(serverPath)) {
|
|
94
73
|
spinner.fail(chalk.red('Server file not found!'));
|
|
95
74
|
console.log(chalk.yellow(`\nPlease run ${chalk.cyan('npm run build')} first.\n`));
|
|
96
|
-
process.exit(1);
|
|
75
|
+
if (!noExit) process.exit(1);
|
|
76
|
+
return false;
|
|
97
77
|
}
|
|
98
78
|
|
|
99
79
|
// 启动服务器进程 - 完全分离
|
|
@@ -122,45 +102,53 @@ const start = async () => {
|
|
|
122
102
|
try {
|
|
123
103
|
const pid = parseInt(fs.readFileSync(PID_FILE, 'utf-8'), 10);
|
|
124
104
|
process.kill(pid, 0);
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
105
|
+
if (!silent) {
|
|
106
|
+
spinner.succeed(chalk.green('Server started successfully!'));
|
|
107
|
+
|
|
108
|
+
const { host, port } = getServerInfo();
|
|
109
|
+
const url = `http://${host}:${port}`;
|
|
110
|
+
|
|
111
|
+
// 显示漂亮的启动信息
|
|
112
|
+
console.log(boxen(
|
|
113
|
+
chalk.green.bold('🚀 AI Code Switch Server\n\n') +
|
|
114
|
+
chalk.white('Status: ') + chalk.green.bold('● Running\n') +
|
|
115
|
+
chalk.white('URL: ') + chalk.cyan.bold(url) + '\n' +
|
|
116
|
+
chalk.white('PID: ') + chalk.yellow(pid) + '\n' +
|
|
117
|
+
chalk.white('Logs: ') + chalk.gray(LOG_FILE) + '\n\n' +
|
|
118
|
+
chalk.gray('Open the URL in your browser to access the dashboard'),
|
|
119
|
+
{
|
|
120
|
+
padding: 1,
|
|
121
|
+
margin: 1,
|
|
122
|
+
borderStyle: 'double',
|
|
123
|
+
borderColor: 'green'
|
|
124
|
+
}
|
|
125
|
+
));
|
|
126
|
+
|
|
127
|
+
console.log(chalk.cyan('💡 Tips:\n'));
|
|
128
|
+
console.log(chalk.white(' • Open browser: ') + chalk.cyan(url));
|
|
129
|
+
console.log(chalk.white(' • View logs: ') + chalk.gray(`tail -f ${LOG_FILE}`));
|
|
130
|
+
console.log(chalk.white(' • Stop server: ') + chalk.yellow('aicos stop'));
|
|
131
|
+
console.log('\n');
|
|
132
|
+
}
|
|
151
133
|
|
|
134
|
+
// (callback)
|
|
135
|
+
if (callback) callback();
|
|
152
136
|
// 立即退出,返回控制台
|
|
153
|
-
process.exit(0);
|
|
137
|
+
if (!noExit) process.exit(0);
|
|
138
|
+
return true;
|
|
154
139
|
} catch (err) {
|
|
155
140
|
spinner.fail(chalk.red('Failed to start server!'));
|
|
156
141
|
console.log(chalk.yellow(`\nCheck logs: ${chalk.cyan(LOG_FILE)}\n`));
|
|
157
|
-
process.exit(1);
|
|
142
|
+
if (!noExit) process.exit(1);
|
|
143
|
+
return false;
|
|
158
144
|
}
|
|
159
145
|
} else {
|
|
160
146
|
spinner.fail(chalk.red('Failed to start server!'));
|
|
161
147
|
console.log(chalk.yellow(`\nCheck logs: ${chalk.cyan(LOG_FILE)}\n`));
|
|
162
|
-
process.exit(1);
|
|
148
|
+
if (!noExit) process.exit(1);
|
|
149
|
+
return false;
|
|
163
150
|
}
|
|
164
151
|
};
|
|
165
152
|
|
|
166
|
-
|
|
153
|
+
// 导出辅助函数供其他模块使用
|
|
154
|
+
module.exports = start;
|
package/bin/stop.js
CHANGED
|
@@ -4,23 +4,77 @@ const os = require('os');
|
|
|
4
4
|
const chalk = require('chalk');
|
|
5
5
|
const boxen = require('boxen');
|
|
6
6
|
const ora = require('ora');
|
|
7
|
+
const { findPidByPort, killProcess, getProcessInfo } = require('./utils/port-utils');
|
|
8
|
+
const { getServerInfo } = require('./utils/get-server');
|
|
7
9
|
|
|
8
10
|
const PID_FILE = path.join(os.homedir(), '.aicodeswitch', 'server.pid');
|
|
9
11
|
|
|
10
|
-
const stop = () => {
|
|
12
|
+
const stop = async (options = {}) => {
|
|
13
|
+
const { callback, silent } = options;
|
|
14
|
+
|
|
11
15
|
console.log('\n');
|
|
12
16
|
|
|
17
|
+
const { host, port } = getServerInfo();
|
|
18
|
+
|
|
13
19
|
if (!fs.existsSync(PID_FILE)) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
if (!silent) {
|
|
21
|
+
// PID 文件不存在,尝试通过端口检测进程
|
|
22
|
+
console.log(boxen(
|
|
23
|
+
chalk.yellow('⚠ PID file not found, checking port...'),
|
|
24
|
+
{
|
|
25
|
+
padding: 1,
|
|
26
|
+
margin: 1,
|
|
27
|
+
borderStyle: 'round',
|
|
28
|
+
borderColor: 'yellow'
|
|
29
|
+
}
|
|
30
|
+
));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const spinner = ora({
|
|
34
|
+
text: chalk.cyan(`Checking port ${port}...`),
|
|
35
|
+
color: 'cyan'
|
|
36
|
+
}).start();
|
|
37
|
+
|
|
38
|
+
const pid = await findPidByPort(port);
|
|
39
|
+
|
|
40
|
+
if (pid) {
|
|
41
|
+
spinner.text = chalk.cyan(`Found process on port ${port}, stopping...`);
|
|
42
|
+
|
|
43
|
+
const processInfo = await getProcessInfo(pid);
|
|
44
|
+
if (!silent) {
|
|
45
|
+
console.log('\n' + chalk.gray(`Process found: ${chalk.white(pid)} (${chalk.gray(processInfo)})`));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const killed = await killProcess(pid);
|
|
49
|
+
|
|
50
|
+
if (killed) {
|
|
51
|
+
spinner.succeed(chalk.green(`Process ${pid} terminated successfully`));
|
|
52
|
+
showStoppedMessage();
|
|
53
|
+
} else {
|
|
54
|
+
spinner.fail(chalk.red('Failed to terminate process'));
|
|
21
55
|
}
|
|
22
|
-
|
|
23
|
-
|
|
56
|
+
} else {
|
|
57
|
+
spinner.info(chalk.yellow(`No process found on port ${port}`));
|
|
58
|
+
if (!silent) {
|
|
59
|
+
console.log(boxen(
|
|
60
|
+
chalk.yellow.bold('⚠ Server is not running'),
|
|
61
|
+
{
|
|
62
|
+
padding: 1,
|
|
63
|
+
margin: 1,
|
|
64
|
+
borderStyle: 'round',
|
|
65
|
+
borderColor: 'yellow'
|
|
66
|
+
}
|
|
67
|
+
));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (!silent) {
|
|
71
|
+
console.log('\n');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (callback) {
|
|
75
|
+
console.log(boxen(chalk.yellow.bold('⚠ Server is not running')));
|
|
76
|
+
callback();
|
|
77
|
+
}
|
|
24
78
|
return;
|
|
25
79
|
}
|
|
26
80
|
|
|
@@ -50,14 +104,20 @@ const stop = () => {
|
|
|
50
104
|
process.kill(pid, 'SIGKILL');
|
|
51
105
|
spinner.warn(chalk.yellow('Server forcefully stopped'));
|
|
52
106
|
fs.unlinkSync(PID_FILE);
|
|
53
|
-
|
|
107
|
+
if (!silent) {
|
|
108
|
+
showStoppedMessage();
|
|
109
|
+
}
|
|
110
|
+
callback && callback();
|
|
54
111
|
}
|
|
55
112
|
} catch (err) {
|
|
56
113
|
// 进程已停止
|
|
57
114
|
clearInterval(checkStopped);
|
|
58
115
|
spinner.succeed(chalk.green('Server stopped successfully'));
|
|
59
116
|
fs.unlinkSync(PID_FILE);
|
|
60
|
-
|
|
117
|
+
if (!silent) {
|
|
118
|
+
showStoppedMessage();
|
|
119
|
+
}
|
|
120
|
+
callback && callback();
|
|
61
121
|
}
|
|
62
122
|
}, 200);
|
|
63
123
|
|
|
@@ -65,7 +125,10 @@ const stop = () => {
|
|
|
65
125
|
// 进程不存在
|
|
66
126
|
spinner.warn(chalk.yellow('Process not found'));
|
|
67
127
|
fs.unlinkSync(PID_FILE);
|
|
68
|
-
|
|
128
|
+
if (!silent) {
|
|
129
|
+
showStoppedMessage();
|
|
130
|
+
}
|
|
131
|
+
callback && callback();
|
|
69
132
|
}
|
|
70
133
|
} catch (err) {
|
|
71
134
|
spinner.fail(chalk.red('Failed to stop server'));
|
|
@@ -87,4 +150,4 @@ const showStoppedMessage = () => {
|
|
|
87
150
|
console.log(chalk.white('Use ') + chalk.cyan('aicos start') + chalk.white(' to start the server again.\n'));
|
|
88
151
|
};
|
|
89
152
|
|
|
90
|
-
module.exports = stop
|
|
153
|
+
module.exports = stop;
|