aicodeswitch 1.2.5 → 1.2.6
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/bin/update.js +215 -269
- package/package.json +2 -3
package/bin/update.js
CHANGED
|
@@ -1,73 +1,35 @@
|
|
|
1
|
+
const { spawn } = require('child_process');
|
|
2
|
+
const https = require('https');
|
|
1
3
|
const path = require('path');
|
|
2
4
|
const fs = require('fs');
|
|
3
5
|
const os = require('os');
|
|
4
|
-
const https = require('https');
|
|
5
|
-
const http = require('http');
|
|
6
|
-
const { spawn } = require('child_process');
|
|
7
6
|
const chalk = require('chalk');
|
|
8
|
-
const ora = require('ora');
|
|
9
7
|
const boxen = require('boxen');
|
|
10
|
-
const
|
|
8
|
+
const ora = require('ora');
|
|
11
9
|
|
|
12
|
-
const AICOSWITCH_DIR = path.join(os.homedir(), '.aicodeswitch');
|
|
13
|
-
const RELEASES_DIR = path.join(AICOSWITCH_DIR, 'releases');
|
|
14
|
-
const CURRENT_FILE = path.join(AICOSWITCH_DIR, 'current');
|
|
15
10
|
const PACKAGE_NAME = 'aicodeswitch';
|
|
11
|
+
const NPM_REGISTRY = 'registry.npmjs.org';
|
|
16
12
|
|
|
17
|
-
//
|
|
18
|
-
const ensureDir = (dirPath) => {
|
|
19
|
-
if (!fs.existsSync(dirPath)) {
|
|
20
|
-
fs.mkdirSync(dirPath, { recursive: true });
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
// 获取当前使用的版本(从 current 文件或本地 package.json)
|
|
13
|
+
// 获取当前版本
|
|
25
14
|
const getCurrentVersion = () => {
|
|
26
|
-
|
|
27
|
-
if (fs.existsSync(CURRENT_FILE)) {
|
|
28
|
-
try {
|
|
29
|
-
const currentPath = fs.readFileSync(CURRENT_FILE, 'utf-8').trim();
|
|
30
|
-
const currentPackageJson = path.join(currentPath, 'package.json');
|
|
31
|
-
if (fs.existsSync(currentPackageJson)) {
|
|
32
|
-
const pkg = JSON.parse(fs.readFileSync(currentPackageJson, 'utf-8'));
|
|
33
|
-
return pkg.version;
|
|
34
|
-
}
|
|
35
|
-
} catch (err) {
|
|
36
|
-
// 读取失败,fallback 到本地版本
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// 使用本地 package.json
|
|
15
|
+
const packageJsonPath = path.join(__dirname, '..', 'package.json');
|
|
41
16
|
try {
|
|
42
|
-
const packageJson =
|
|
43
|
-
|
|
44
|
-
return pkg.version;
|
|
17
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
18
|
+
return packageJson.version;
|
|
45
19
|
} catch (err) {
|
|
46
|
-
return
|
|
20
|
+
return null;
|
|
47
21
|
}
|
|
48
22
|
};
|
|
49
23
|
|
|
50
|
-
//
|
|
51
|
-
const compareVersions = (v1, v2) => {
|
|
52
|
-
const parts1 = v1.split('.').map(Number);
|
|
53
|
-
const parts2 = v2.split('.').map(Number);
|
|
54
|
-
|
|
55
|
-
for (let i = 0; i < 3; i++) {
|
|
56
|
-
if (parts1[i] > parts2[i]) return 1;
|
|
57
|
-
if (parts1[i] < parts2[i]) return -1;
|
|
58
|
-
}
|
|
59
|
-
return 0;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
// 从 npm registry 获取最新版本
|
|
24
|
+
// 从 npm 获取最新版本
|
|
63
25
|
const getLatestVersion = () => {
|
|
64
26
|
return new Promise((resolve, reject) => {
|
|
65
27
|
const options = {
|
|
66
|
-
hostname:
|
|
28
|
+
hostname: NPM_REGISTRY,
|
|
67
29
|
path: `/${PACKAGE_NAME}`,
|
|
68
30
|
method: 'GET',
|
|
69
31
|
headers: {
|
|
70
|
-
'User-Agent': 'aicodeswitch'
|
|
32
|
+
'User-Agent': 'aicodeswitch-update'
|
|
71
33
|
}
|
|
72
34
|
};
|
|
73
35
|
|
|
@@ -81,164 +43,126 @@ const getLatestVersion = () => {
|
|
|
81
43
|
res.on('end', () => {
|
|
82
44
|
try {
|
|
83
45
|
const packageInfo = JSON.parse(data);
|
|
84
|
-
|
|
85
|
-
resolve({
|
|
86
|
-
version: latestVersion,
|
|
87
|
-
tarball: packageInfo.versions[latestVersion].dist.tarball
|
|
88
|
-
});
|
|
46
|
+
resolve(packageInfo['dist-tags'].latest);
|
|
89
47
|
} catch (err) {
|
|
90
|
-
reject(new Error('Failed to parse
|
|
48
|
+
reject(new Error('Failed to parse npm response'));
|
|
91
49
|
}
|
|
92
50
|
});
|
|
93
51
|
});
|
|
94
52
|
|
|
95
|
-
req.on('error', reject);
|
|
96
|
-
req.end();
|
|
97
|
-
});
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
// 下载 tarball 文件
|
|
101
|
-
const downloadTarball = (url, destPath) => {
|
|
102
|
-
return new Promise((resolve, reject) => {
|
|
103
|
-
const urlObj = new URL(url);
|
|
104
|
-
const protocol = urlObj.protocol === 'http:' ? http : https;
|
|
105
|
-
|
|
106
|
-
const requestOptions = {
|
|
107
|
-
hostname: urlObj.hostname,
|
|
108
|
-
port: urlObj.port,
|
|
109
|
-
path: urlObj.pathname + urlObj.search,
|
|
110
|
-
method: 'GET',
|
|
111
|
-
headers: {
|
|
112
|
-
'User-Agent': 'aicodeswitch'
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
const req = protocol.request(requestOptions, (res) => {
|
|
117
|
-
if (res.statusCode !== 200) {
|
|
118
|
-
reject(new Error(`Failed to download: HTTP ${res.statusCode}`));
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const fileStream = fs.createWriteStream(destPath);
|
|
123
|
-
res.pipe(fileStream);
|
|
124
|
-
|
|
125
|
-
fileStream.on('finish', () => {
|
|
126
|
-
fileStream.close();
|
|
127
|
-
resolve(destPath);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
fileStream.on('error', (err) => {
|
|
131
|
-
fs.unlink(destPath, () => {});
|
|
132
|
-
reject(err);
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
|
|
136
53
|
req.on('error', (err) => {
|
|
137
|
-
if (fs.existsSync(destPath)) {
|
|
138
|
-
fs.unlink(destPath, () => {});
|
|
139
|
-
}
|
|
140
54
|
reject(err);
|
|
141
55
|
});
|
|
142
56
|
|
|
143
|
-
req.setTimeout(
|
|
57
|
+
req.setTimeout(10000, () => {
|
|
144
58
|
req.destroy();
|
|
145
|
-
|
|
146
|
-
fs.unlink(destPath, () => {});
|
|
147
|
-
}
|
|
148
|
-
reject(new Error('Download timeout'));
|
|
59
|
+
reject(new Error('Request timeout'));
|
|
149
60
|
});
|
|
150
61
|
|
|
151
62
|
req.end();
|
|
152
63
|
});
|
|
153
64
|
};
|
|
154
65
|
|
|
155
|
-
//
|
|
156
|
-
const
|
|
157
|
-
return tar.x({
|
|
158
|
-
file: tarballPath,
|
|
159
|
-
cwd: destDir,
|
|
160
|
-
strip: 1, // 去掉 package 目录层级
|
|
161
|
-
});
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
// 安装 npm 依赖
|
|
165
|
-
const installDependencies = (dir) => {
|
|
66
|
+
// 执行命令
|
|
67
|
+
const execCommand = (command, args, options = {}) => {
|
|
166
68
|
return new Promise((resolve, reject) => {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
cwd: dir,
|
|
171
|
-
stdio: 'inherit'
|
|
69
|
+
const proc = spawn(command, args, {
|
|
70
|
+
stdio: options.silent ? 'pipe' : 'inherit',
|
|
71
|
+
...options
|
|
172
72
|
});
|
|
173
73
|
|
|
174
|
-
|
|
74
|
+
let output = '';
|
|
75
|
+
let errorOutput = '';
|
|
76
|
+
|
|
77
|
+
if (options.silent) {
|
|
78
|
+
proc.stdout.on('data', (data) => {
|
|
79
|
+
output += data.toString();
|
|
80
|
+
});
|
|
81
|
+
proc.stderr.on('data', (data) => {
|
|
82
|
+
errorOutput += data.toString();
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
proc.on('close', (code) => {
|
|
175
87
|
if (code === 0) {
|
|
176
|
-
|
|
177
|
-
resolve();
|
|
88
|
+
resolve({ output, errorOutput });
|
|
178
89
|
} else {
|
|
179
|
-
reject(
|
|
90
|
+
reject({ code, output, errorOutput });
|
|
180
91
|
}
|
|
181
92
|
});
|
|
182
93
|
|
|
183
|
-
|
|
94
|
+
proc.on('error', (err) => {
|
|
95
|
+
reject(err);
|
|
96
|
+
});
|
|
184
97
|
});
|
|
185
98
|
};
|
|
186
99
|
|
|
187
|
-
//
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
100
|
+
// 检查是否需要 sudo 权限
|
|
101
|
+
const needsSudo = () => {
|
|
102
|
+
const npmPrefix = process.env.npm_config_prefix || '/usr/local';
|
|
103
|
+
const globalInstallPath = path.join(npmPrefix, 'lib', 'node_modules');
|
|
104
|
+
const aicosPath = path.join(globalInstallPath, PACKAGE_NAME);
|
|
191
105
|
|
|
192
|
-
//
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
106
|
+
// 如果全局安装路径存在且不可写,可能需要 sudo
|
|
107
|
+
if (fs.existsSync(globalInstallPath)) {
|
|
108
|
+
try {
|
|
109
|
+
fs.accessSync(globalInstallPath, fs.constants.W_OK);
|
|
110
|
+
return false;
|
|
111
|
+
} catch (err) {
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
196
115
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
116
|
+
// 检查当前 aicos 安装位置
|
|
117
|
+
const currentLink = path.join(__dirname, 'cli.js');
|
|
118
|
+
const realPath = fs.realpathSync(currentLink);
|
|
200
119
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
120
|
+
// 如果在全局目录下,需要检查权限
|
|
121
|
+
if (realPath.includes('/usr/local/') || realPath.includes('/usr/lib/')) {
|
|
122
|
+
try {
|
|
123
|
+
fs.accessSync(path.dirname(realPath), fs.constants.W_OK);
|
|
124
|
+
return false;
|
|
125
|
+
} catch (err) {
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
208
129
|
|
|
209
|
-
|
|
210
|
-
});
|
|
130
|
+
return false;
|
|
211
131
|
};
|
|
212
132
|
|
|
213
|
-
//
|
|
214
|
-
const
|
|
133
|
+
// 停止服务器
|
|
134
|
+
const stopServer = async () => {
|
|
135
|
+
const stopPath = path.join(__dirname, 'stop.js');
|
|
215
136
|
try {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
return fs.statSync(itemPath).isDirectory();
|
|
224
|
-
})
|
|
225
|
-
.sort((a, b) => {
|
|
226
|
-
// 按版本号降序排序
|
|
227
|
-
return compareVersions(b, a);
|
|
228
|
-
});
|
|
137
|
+
await execCommand('node', [stopPath], { silent: true });
|
|
138
|
+
return true;
|
|
139
|
+
} catch (err) {
|
|
140
|
+
// 停止失败可能是因为服务未运行,这不是致命错误
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
229
144
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
});
|
|
237
|
-
}
|
|
145
|
+
// 启动服务器
|
|
146
|
+
const startServer = async () => {
|
|
147
|
+
const startPath = path.join(__dirname, 'start.js');
|
|
148
|
+
try {
|
|
149
|
+
await execCommand('node', [startPath], { silent: true });
|
|
150
|
+
return true;
|
|
238
151
|
} catch (err) {
|
|
239
|
-
|
|
240
|
-
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// 比较版本号
|
|
157
|
+
const compareVersions = (v1, v2) => {
|
|
158
|
+
const parts1 = v1.split('.').map(Number);
|
|
159
|
+
const parts2 = v2.split('.').map(Number);
|
|
160
|
+
|
|
161
|
+
for (let i = 0; i < 3; i++) {
|
|
162
|
+
if (parts1[i] > parts2[i]) return 1;
|
|
163
|
+
if (parts1[i] < parts2[i]) return -1;
|
|
241
164
|
}
|
|
165
|
+
return 0;
|
|
242
166
|
};
|
|
243
167
|
|
|
244
168
|
// 主更新逻辑
|
|
@@ -246,125 +170,147 @@ const update = async () => {
|
|
|
246
170
|
console.log('\n');
|
|
247
171
|
|
|
248
172
|
const currentVersion = getCurrentVersion();
|
|
249
|
-
|
|
173
|
+
if (!currentVersion) {
|
|
174
|
+
console.log(boxen(
|
|
175
|
+
chalk.red.bold('✗ Failed to read current version\n\n') +
|
|
176
|
+
chalk.white('Please reinstall the package.'),
|
|
177
|
+
{
|
|
178
|
+
padding: 1,
|
|
179
|
+
margin: 1,
|
|
180
|
+
borderStyle: 'round',
|
|
181
|
+
borderColor: 'red'
|
|
182
|
+
}
|
|
183
|
+
));
|
|
184
|
+
console.log('');
|
|
185
|
+
process.exit(1);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// 显示当前版本
|
|
189
|
+
console.log(chalk.cyan('📦 Current Version: ') + chalk.white.bold(currentVersion));
|
|
190
|
+
console.log('');
|
|
191
|
+
|
|
192
|
+
// 检查最新版本
|
|
193
|
+
const checkSpinner = ora({
|
|
250
194
|
text: chalk.cyan('Checking for updates...'),
|
|
251
|
-
color: 'cyan'
|
|
195
|
+
color: 'cyan',
|
|
196
|
+
hideCursor: false
|
|
252
197
|
}).start();
|
|
253
198
|
|
|
199
|
+
let latestVersion;
|
|
254
200
|
try {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
if (comparison <= 0) {
|
|
265
|
-
console.log(chalk.yellow(`\n✓ You are already on the latest version (${chalk.bold(currentVersion)})\n`));
|
|
266
|
-
process.exit(0);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
console.log(chalk.cyan(`\n📦 Update available: ${chalk.bold(currentVersion)} → ${chalk.bold(latestVersion)}\n`));
|
|
270
|
-
|
|
271
|
-
// 确保目录存在
|
|
272
|
-
ensureDir(RELEASES_DIR);
|
|
273
|
-
|
|
274
|
-
// 创建版本目录
|
|
275
|
-
const versionDir = path.join(RELEASES_DIR, latestVersion);
|
|
276
|
-
ensureDir(versionDir);
|
|
277
|
-
|
|
278
|
-
// 下载 tarball
|
|
279
|
-
const downloadSpinner = ora({
|
|
280
|
-
text: chalk.cyan('Downloading from npm...'),
|
|
281
|
-
color: 'cyan'
|
|
282
|
-
}).start();
|
|
283
|
-
|
|
284
|
-
const tarballPath = path.join(versionDir, 'package.tgz');
|
|
285
|
-
|
|
286
|
-
try {
|
|
287
|
-
await downloadTarball(latestInfo.tarball, tarballPath);
|
|
288
|
-
downloadSpinner.succeed(chalk.green('Download completed'));
|
|
289
|
-
} catch (err) {
|
|
290
|
-
downloadSpinner.fail(chalk.red('Download failed'));
|
|
291
|
-
console.log(chalk.red(`Error: ${err.message}\n`));
|
|
292
|
-
process.exit(1);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// 解压 tarball
|
|
296
|
-
const extractSpinner = ora({
|
|
297
|
-
text: chalk.cyan('Extracting package...'),
|
|
298
|
-
color: 'cyan'
|
|
299
|
-
}).start();
|
|
300
|
-
|
|
301
|
-
try {
|
|
302
|
-
await extractTarball(tarballPath, versionDir);
|
|
303
|
-
extractSpinner.succeed(chalk.green('Package extracted'));
|
|
304
|
-
} catch (err) {
|
|
305
|
-
extractSpinner.fail(chalk.red('Extraction failed'));
|
|
306
|
-
console.log(chalk.red(`Error: ${err.message}\n`));
|
|
307
|
-
process.exit(1);
|
|
308
|
-
} finally {
|
|
309
|
-
// 删除 tarball 文件
|
|
310
|
-
if (fs.existsSync(tarballPath)) {
|
|
311
|
-
fs.unlinkSync(tarballPath);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// 安装依赖
|
|
316
|
-
const installSpinner = ora({
|
|
317
|
-
text: chalk.cyan('Installing dependencies...'),
|
|
318
|
-
color: 'cyan'
|
|
319
|
-
}).start();
|
|
320
|
-
|
|
321
|
-
try {
|
|
322
|
-
await installDependencies(versionDir);
|
|
323
|
-
installSpinner.succeed(chalk.green('Dependencies installed'));
|
|
324
|
-
} catch (err) {
|
|
325
|
-
installSpinner.fail(chalk.red('Dependencies installation failed'));
|
|
326
|
-
console.log(chalk.red(`Error: ${err.message}\n`));
|
|
327
|
-
process.exit(1);
|
|
328
|
-
}
|
|
201
|
+
latestVersion = await getLatestVersion();
|
|
202
|
+
checkSpinner.succeed(chalk.green('Checked for updates'));
|
|
203
|
+
} catch (err) {
|
|
204
|
+
checkSpinner.fail(chalk.red('Failed to check for updates'));
|
|
205
|
+
console.log(chalk.yellow(`\nError: ${err.message}\n`));
|
|
206
|
+
console.log(chalk.white('You can manually update by running:\n'));
|
|
207
|
+
console.log(chalk.cyan(' npm update -g aicodeswitch\n'));
|
|
208
|
+
process.exit(1);
|
|
209
|
+
}
|
|
329
210
|
|
|
330
|
-
|
|
331
|
-
|
|
211
|
+
console.log(chalk.cyan('📦 Latest Version: ') + chalk.white.bold(latestVersion));
|
|
212
|
+
console.log('');
|
|
332
213
|
|
|
333
|
-
|
|
334
|
-
|
|
214
|
+
// 比较版本
|
|
215
|
+
const versionCompare = compareVersions(latestVersion, currentVersion);
|
|
335
216
|
|
|
336
|
-
|
|
217
|
+
if (versionCompare <= 0) {
|
|
337
218
|
console.log(boxen(
|
|
338
|
-
chalk.green.bold('
|
|
339
|
-
chalk.white(
|
|
340
|
-
chalk.white(
|
|
341
|
-
chalk.gray('Restarting server with the new version...'),
|
|
219
|
+
chalk.green.bold('✓ You are already using the latest version!\n\n') +
|
|
220
|
+
chalk.white(`Current version: ${chalk.cyan.bold(currentVersion)}\n`) +
|
|
221
|
+
chalk.white(`Latest version: ${chalk.cyan.bold(latestVersion)}`),
|
|
342
222
|
{
|
|
343
223
|
padding: 1,
|
|
344
224
|
margin: 1,
|
|
345
|
-
borderStyle: '
|
|
225
|
+
borderStyle: 'round',
|
|
346
226
|
borderColor: 'green'
|
|
347
227
|
}
|
|
348
228
|
));
|
|
229
|
+
console.log('');
|
|
230
|
+
process.exit(0);
|
|
231
|
+
}
|
|
349
232
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
233
|
+
// 有新版本可用
|
|
234
|
+
console.log(boxen(
|
|
235
|
+
chalk.yellow.bold('⬆️ New version available!\n\n') +
|
|
236
|
+
chalk.white('Current: ') + chalk.gray(currentVersion) + '\n' +
|
|
237
|
+
chalk.white('Latest: ') + chalk.green.bold(latestVersion) + '\n\n' +
|
|
238
|
+
chalk.gray('Preparing to update...'),
|
|
239
|
+
{
|
|
240
|
+
padding: 1,
|
|
241
|
+
margin: 1,
|
|
242
|
+
borderStyle: 'round',
|
|
243
|
+
borderColor: 'yellow'
|
|
357
244
|
}
|
|
245
|
+
));
|
|
246
|
+
console.log('');
|
|
247
|
+
|
|
248
|
+
// 检查是否需要 sudo
|
|
249
|
+
const needSudo = needsSudo();
|
|
250
|
+
if (needSudo) {
|
|
251
|
+
console.log(chalk.yellow.bold('⚠️ Note: '));
|
|
252
|
+
console.log(chalk.white('This operation may require ') + chalk.yellow.bold('sudo') + chalk.white(' privileges.'));
|
|
253
|
+
console.log(chalk.gray('If prompted, please enter your password.\n'));
|
|
254
|
+
}
|
|
358
255
|
|
|
359
|
-
|
|
256
|
+
// 停止服务器
|
|
257
|
+
const stopSpinner = ora({
|
|
258
|
+
text: chalk.cyan('Stopping server...'),
|
|
259
|
+
color: 'cyan'
|
|
260
|
+
}).start();
|
|
261
|
+
|
|
262
|
+
await stopServer();
|
|
263
|
+
stopSpinner.succeed(chalk.green('Server stopped'));
|
|
360
264
|
|
|
265
|
+
// 执行更新
|
|
266
|
+
const updateSpinner = ora({
|
|
267
|
+
text: chalk.cyan('Updating to latest version...'),
|
|
268
|
+
color: 'cyan'
|
|
269
|
+
}).start();
|
|
270
|
+
|
|
271
|
+
try {
|
|
272
|
+
const npmArgs = ['install', '-g', `${PACKAGE_NAME}@latest`];
|
|
273
|
+
if (needSudo) {
|
|
274
|
+
npmArgs.unshift('sudo');
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
await execCommand(needSudo ? 'sudo' : 'npm', npmArgs);
|
|
278
|
+
updateSpinner.succeed(chalk.green('Update successful!'));
|
|
361
279
|
} catch (err) {
|
|
362
|
-
|
|
363
|
-
console.log(chalk.
|
|
364
|
-
console.log(chalk.
|
|
365
|
-
console.log(chalk.cyan('
|
|
280
|
+
updateSpinner.fail(chalk.red('Update failed'));
|
|
281
|
+
console.log(chalk.yellow(`\nUpdate failed with error code ${err.code || 'unknown'}\n`));
|
|
282
|
+
console.log(chalk.white('You can try manually updating:\n'));
|
|
283
|
+
console.log(chalk.cyan(` ${needSudo ? 'sudo ' : ''}npm install -g ${PACKAGE_NAME}@latest\n`));
|
|
284
|
+
|
|
285
|
+
// 尝试重新启动服务器
|
|
286
|
+
console.log(chalk.yellow('Attempting to restart server...\n'));
|
|
287
|
+
await startServer();
|
|
366
288
|
process.exit(1);
|
|
367
289
|
}
|
|
290
|
+
|
|
291
|
+
console.log('');
|
|
292
|
+
console.log(boxen(
|
|
293
|
+
chalk.green.bold('✓ Successfully updated!\n\n') +
|
|
294
|
+
chalk.white('Previous version: ') + chalk.gray(currentVersion) + '\n' +
|
|
295
|
+
chalk.white('New version: ') + chalk.green.bold(latestVersion) + '\n\n' +
|
|
296
|
+
chalk.gray('Starting server...'),
|
|
297
|
+
{
|
|
298
|
+
padding: 1,
|
|
299
|
+
margin: 1,
|
|
300
|
+
borderStyle: 'double',
|
|
301
|
+
borderColor: 'green'
|
|
302
|
+
}
|
|
303
|
+
));
|
|
304
|
+
console.log('');
|
|
305
|
+
|
|
306
|
+
// 启动服务器
|
|
307
|
+
await startServer();
|
|
308
|
+
|
|
309
|
+
console.log('');
|
|
310
|
+
console.log(chalk.cyan('💡 Tips:\n'));
|
|
311
|
+
console.log(chalk.white(' • Check version: ') + chalk.cyan('aicos version'));
|
|
312
|
+
console.log(chalk.white(' • View logs: ') + chalk.gray('tail -f ~/.aicodeswitch/server.log'));
|
|
313
|
+
console.log('\n');
|
|
368
314
|
};
|
|
369
315
|
|
|
370
316
|
module.exports = update();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aicodeswitch",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.6",
|
|
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": "MIT",
|
|
@@ -55,8 +55,7 @@
|
|
|
55
55
|
"express": "^4.18.2",
|
|
56
56
|
"jsonwebtoken": "^9.0.3",
|
|
57
57
|
"level": "^8.0.0",
|
|
58
|
-
"ora": "^5.4.1"
|
|
59
|
-
"tar": "^7.4.3"
|
|
58
|
+
"ora": "^5.4.1"
|
|
60
59
|
},
|
|
61
60
|
"devDependencies": {
|
|
62
61
|
"@types/better-sqlite3": "^7.6.9",
|