aicodeswitch 2.0.1 → 2.0.2
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/start.js +5 -3
- package/bin/stop.js +63 -97
- package/dist/server/main.js +2 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/bin/start.js
CHANGED
|
@@ -6,6 +6,7 @@ const chalk = require('chalk');
|
|
|
6
6
|
const boxen = require('boxen');
|
|
7
7
|
const ora = require('ora');
|
|
8
8
|
const { isServerRunning, getServerInfo } = require('./utils/get-server');
|
|
9
|
+
const { findPidByPort } = require('./utils/port-utils');
|
|
9
10
|
|
|
10
11
|
const PID_FILE = path.join(os.homedir(), '.aicodeswitch', 'server.pid');
|
|
11
12
|
const LOG_FILE = path.join(os.homedir(), '.aicodeswitch', 'server.log');
|
|
@@ -23,13 +24,14 @@ const start = async (options = {}) => {
|
|
|
23
24
|
console.log('\n');
|
|
24
25
|
|
|
25
26
|
// 已经运行
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
const { host, port } = getServerInfo();
|
|
28
|
+
if (isServerRunning() || await findPidByPort(port)) {
|
|
28
29
|
if (!silent) {
|
|
29
30
|
if (!silent) {
|
|
30
31
|
console.log(boxen(
|
|
31
32
|
chalk.yellow.bold('⚠ Server is already running!\n\n') +
|
|
32
|
-
chalk.white(`URL: `) + chalk.cyan.bold(`http://${host}:${port}`)
|
|
33
|
+
chalk.white(`URL: `) + chalk.cyan.bold(`http://${host}:${port}\n\n`) +
|
|
34
|
+
chalk.white('Use ') + chalk.cyan('aicos restart') + chalk.white(' to restart the server.\n'),
|
|
33
35
|
{
|
|
34
36
|
padding: 1,
|
|
35
37
|
margin: 1,
|
package/bin/stop.js
CHANGED
|
@@ -14,126 +14,92 @@ const stop = async (options = {}) => {
|
|
|
14
14
|
|
|
15
15
|
console.log('\n');
|
|
16
16
|
|
|
17
|
-
const {
|
|
17
|
+
const spinner = ora({ text: chalk.cyan('Stopping server...'), color: 'cyan' }).start();
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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...`);
|
|
19
|
+
// 第一步:如果 PID 文件存在,优先通过 PID 文件停止服务器
|
|
20
|
+
if (fs.existsSync(PID_FILE)) {
|
|
21
|
+
try {
|
|
22
|
+
const pid = parseInt(fs.readFileSync(PID_FILE, 'utf-8'), 10);
|
|
42
23
|
|
|
43
24
|
const processInfo = await getProcessInfo(pid);
|
|
44
25
|
if (!silent) {
|
|
45
26
|
console.log('\n' + chalk.gray(`Process found: ${chalk.white(pid)} (${chalk.gray(processInfo)})`));
|
|
46
27
|
}
|
|
47
28
|
|
|
48
|
-
|
|
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'));
|
|
55
|
-
}
|
|
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
|
-
}
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const spinner = ora({
|
|
82
|
-
text: chalk.cyan('Stopping server...'),
|
|
83
|
-
color: 'cyan'
|
|
84
|
-
}).start();
|
|
85
|
-
|
|
86
|
-
try {
|
|
87
|
-
const pid = parseInt(fs.readFileSync(PID_FILE, 'utf-8'), 10);
|
|
88
|
-
|
|
89
|
-
// 尝试终止进程
|
|
90
|
-
try {
|
|
29
|
+
// 尝试终止进程
|
|
91
30
|
process.kill(pid, 'SIGTERM');
|
|
92
31
|
|
|
93
32
|
// 等待进程停止
|
|
94
33
|
let attempts = 0;
|
|
95
34
|
const maxAttempts = 10;
|
|
96
35
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
36
|
+
await new Promise((resolve) => {
|
|
37
|
+
const checkStopped = setInterval(() => {
|
|
38
|
+
attempts++;
|
|
39
|
+
try {
|
|
40
|
+
process.kill(pid, 0);
|
|
41
|
+
if (attempts >= maxAttempts) {
|
|
42
|
+
clearInterval(checkStopped);
|
|
43
|
+
// 强制终止
|
|
44
|
+
process.kill(pid, 'SIGKILL');
|
|
45
|
+
spinner.warn(chalk.yellow(`PID ${pid} forcefully killed!`));
|
|
46
|
+
fs.unlinkSync(PID_FILE);
|
|
47
|
+
resolve();
|
|
48
|
+
}
|
|
49
|
+
} catch (err) {
|
|
50
|
+
spinner.succeed(chalk.green(`PID ${pid} killed!`));
|
|
51
|
+
// 进程已停止
|
|
102
52
|
clearInterval(checkStopped);
|
|
103
|
-
// 强制终止
|
|
104
|
-
process.kill(pid, 'SIGKILL');
|
|
105
|
-
spinner.warn(chalk.yellow('Server forcefully stopped'));
|
|
106
53
|
fs.unlinkSync(PID_FILE);
|
|
107
|
-
|
|
108
|
-
showStoppedMessage();
|
|
109
|
-
}
|
|
110
|
-
callback && callback();
|
|
54
|
+
resolve();
|
|
111
55
|
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
fs.unlinkSync(PID_FILE);
|
|
117
|
-
if (!silent) {
|
|
118
|
-
showStoppedMessage();
|
|
119
|
-
}
|
|
120
|
-
callback && callback();
|
|
121
|
-
}
|
|
122
|
-
}, 200);
|
|
123
|
-
|
|
124
|
-
} catch (err) {
|
|
56
|
+
}, 200);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
125
60
|
// 进程不存在
|
|
126
|
-
|
|
61
|
+
if (err.code === 'ESRCH') {
|
|
62
|
+
spinner.warn(chalk.yellow(`PID ${pid} not found!`));
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
spinner.fail(chalk.red(`\nError: ${err.message}\n`));
|
|
66
|
+
}
|
|
127
67
|
fs.unlinkSync(PID_FILE);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// 第二步:如果 PID 文件不存在,通过端口检测进程并停止
|
|
72
|
+
const { port } = getServerInfo();
|
|
73
|
+
spinner.text = chalk.yellow(`⚠ Checking port... (port: ${port})`);
|
|
74
|
+
const pid = await findPidByPort(port);
|
|
75
|
+
if (pid) {
|
|
76
|
+
spinner.text = chalk.cyan(`Found process on port ${port}, stopping...`);
|
|
77
|
+
|
|
78
|
+
const processInfo = await getProcessInfo(pid);
|
|
79
|
+
if (!silent) {
|
|
80
|
+
console.log('\n' + chalk.gray(`Process found: ${chalk.white(pid)} (${chalk.gray(processInfo)})`));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const killed = await killProcess(pid);
|
|
84
|
+
if (killed) {
|
|
85
|
+
spinner.succeed(chalk.green(`Process ${pid} terminated successfully`));
|
|
128
86
|
if (!silent) {
|
|
129
87
|
showStoppedMessage();
|
|
130
88
|
}
|
|
131
|
-
callback && callback();
|
|
132
89
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
90
|
+
else {
|
|
91
|
+
spinner.fail(chalk.red('Failed to terminate process'));
|
|
92
|
+
}
|
|
136
93
|
}
|
|
94
|
+
else {
|
|
95
|
+
spinner.info(chalk.yellow(`No process found on port ${port}`));
|
|
96
|
+
if (!silent) {
|
|
97
|
+
showStoppedMessage();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 第三步:(callback)
|
|
102
|
+
callback && callback();
|
|
137
103
|
};
|
|
138
104
|
|
|
139
105
|
const showStoppedMessage = () => {
|
package/dist/server/main.js
CHANGED
|
@@ -539,8 +539,9 @@ const registerRoutes = (dbManager, proxyServer) => {
|
|
|
539
539
|
const start = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
540
540
|
fs_1.default.mkdirSync(dataDir, { recursive: true });
|
|
541
541
|
const dbManager = new database_1.DatabaseManager(dataDir);
|
|
542
|
-
|
|
542
|
+
// 必须先初始化数据库,否则会报错
|
|
543
543
|
yield dbManager.initialize();
|
|
544
|
+
const proxyServer = new proxy_server_1.ProxyServer(dbManager, app);
|
|
544
545
|
// Initialize proxy server and register proxy routes last
|
|
545
546
|
proxyServer.initialize();
|
|
546
547
|
// Register admin routes first
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aicodeswitch",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "A tool to help you manage AI programming tools to access large language models locally. It allows your Claude Code, Codex and other tools to no longer be limited to official models.",
|
|
5
5
|
"author": "tangshuang",
|
|
6
6
|
"license": "GPL-3.0",
|