aicodeswitch 3.0.7 → 3.0.9

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/README.md CHANGED
@@ -153,6 +153,11 @@ aicodeswitch内部,会根据“源类型”来转换数据。例如,你的
153
153
  你可以在 aicodeswitch 中集中统一管理 skills,把skills分发给claude code和codex,随时启用和停用skills。
154
154
  另外,你可以基于自然语言搜索skills,找到skill之后,支持一键安装。
155
155
 
156
+ ## MCP管理
157
+
158
+ 你可以在 aicodeswitch 中集中统一管理 MCP,把MCP分发给claude code和codex,随时启用和停用MCP。
159
+ 并且,你还可以通过MCP来实现图像理解。
160
+
156
161
  ## 日志
157
162
 
158
163
  在**日志**页面,您可以查看:
package/bin/cli.js CHANGED
@@ -7,7 +7,7 @@ const commands = {
7
7
  start: require('./start'),
8
8
  stop: require('./stop'),
9
9
  restart: require('./restart'),
10
- update: require('./update'),
10
+ upgrade: require('./upgrade'),
11
11
  restore: require('./restore'),
12
12
  version: require('./version'),
13
13
  ui: require('./ui'),
@@ -22,7 +22,7 @@ Commands:
22
22
  stop Stop the AI Code Switch server
23
23
  restart Restart the AI Code Switch server
24
24
  ui Open the web UI in browser (starts server if needed)
25
- update Update to the latest version and restart
25
+ upgrade Upgrade to the latest version and restart
26
26
  restore Restore original configuration files
27
27
  version Show current version information
28
28
 
@@ -31,7 +31,7 @@ Example:
31
31
  aicos stop
32
32
  aicos restart
33
33
  aicos ui
34
- aicos update
34
+ aicos upgrade
35
35
  aicos restore
36
36
  aicos restore claude-code
37
37
  aicos restore codex
@@ -29,7 +29,7 @@ const getLatestVersion = () => {
29
29
  path: `/${PACKAGE_NAME}`,
30
30
  method: 'GET',
31
31
  headers: {
32
- 'User-Agent': 'aicodeswitch-update'
32
+ 'User-Agent': 'aicodeswitch-upgrade'
33
33
  }
34
34
  };
35
35
 
@@ -142,8 +142,8 @@ const compareVersions = (v1, v2) => {
142
142
  return 0;
143
143
  };
144
144
 
145
- // 主更新逻辑
146
- const update = async () => {
145
+ // 主升级逻辑
146
+ const upgrade = async () => {
147
147
  console.log('\n');
148
148
 
149
149
  const currentVersion = getCurrentVersion();
@@ -180,8 +180,8 @@ const update = async () => {
180
180
  } catch (err) {
181
181
  checkSpinner.fail(chalk.red('Failed to check for updates'));
182
182
  console.log(chalk.yellow(`\nError: ${err.message}\n`));
183
- console.log(chalk.white('You can manually update by running:\n'));
184
- console.log(chalk.cyan(' npm update -g aicodeswitch\n'));
183
+ console.log(chalk.white('You can manually upgrade by running:\n'));
184
+ console.log(chalk.cyan(' npm install -g aicodeswitch\n'));
185
185
  process.exit(1);
186
186
  }
187
187
 
@@ -212,7 +212,7 @@ const update = async () => {
212
212
  chalk.yellow.bold('⬆️ New version available!\n\n') +
213
213
  chalk.white('Current: ') + chalk.gray(currentVersion) + '\n' +
214
214
  chalk.white('Latest: ') + chalk.green.bold(latestVersion) + '\n\n' +
215
- chalk.gray('Preparing to update...'),
215
+ chalk.gray('Preparing to upgrade...'),
216
216
  {
217
217
  padding: 1,
218
218
  margin: 1,
@@ -230,9 +230,9 @@ const update = async () => {
230
230
  console.log(boxen(
231
231
  chalk.yellow.bold('⚠️ Sudo privileges required\n\n') +
232
232
  chalk.white('This operation requires ') + chalk.yellow.bold('sudo') + chalk.white(' privileges.\n\n') +
233
- chalk.white('Please run the following command to update:\n\n') +
233
+ chalk.white('Please run the following command to upgrade:\n\n') +
234
234
  chalk.cyan.bold(' sudo npm install -g ' + PACKAGE_NAME + '@latest\n\n') +
235
- chalk.gray('After updating, run ') + chalk.cyan('aicos restart') + chalk.gray(' to restart the server.'),
235
+ chalk.gray('After upgrading, run ') + chalk.cyan('aicos restart') + chalk.gray(' to restart the server.'),
236
236
  {
237
237
  padding: 1,
238
238
  margin: 1,
@@ -244,26 +244,26 @@ const update = async () => {
244
244
  process.exit(0);
245
245
  }
246
246
 
247
- // 执行更新
248
- const updateSpinner = ora({
249
- text: chalk.cyan('Updating to latest version...'),
247
+ // 执行升级
248
+ const upgradeSpinner = ora({
249
+ text: chalk.cyan('Upgrading to latest version...'),
250
250
  color: 'cyan'
251
251
  }).start();
252
252
 
253
253
  try {
254
254
  await execCommand('npm', ['install', '-g', `${PACKAGE_NAME}@latest`]);
255
- updateSpinner.succeed(chalk.green('Update successful!'));
255
+ upgradeSpinner.succeed(chalk.green('Upgrade successful!'));
256
256
  } catch (err) {
257
- updateSpinner.fail(chalk.red('Update failed'));
258
- console.log(chalk.yellow(`\nUpdate failed with error code ${err.code || 'unknown'}\n`));
259
- console.log(chalk.white('You can try manually updating:\n'));
257
+ upgradeSpinner.fail(chalk.red('Upgrade failed'));
258
+ console.log(chalk.yellow(`\nUpgrade failed with error code ${err.code || 'unknown'}\n`));
259
+ console.log(chalk.white('You can try manually upgrading:\n'));
260
260
  console.log(chalk.cyan(` npm install -g ${PACKAGE_NAME}@latest\n`));
261
261
  process.exit(1);
262
262
  }
263
263
 
264
264
  console.log('');
265
265
  console.log(boxen(
266
- chalk.green.bold('✓ Successfully updated!\n\n') +
266
+ chalk.green.bold('✓ Successfully upgraded!\n\n') +
267
267
  chalk.white('Previous version: ') + chalk.gray(currentVersion) + '\n' +
268
268
  chalk.white('New version: ') + chalk.green.bold(latestVersion),
269
269
  {
@@ -280,4 +280,4 @@ const update = async () => {
280
280
  console.log('\n');
281
281
  };
282
282
 
283
- module.exports = update;
283
+ module.exports = upgrade;
package/bin/version.js CHANGED
@@ -38,7 +38,7 @@ const version = () => {
38
38
  ));
39
39
 
40
40
  console.log(chalk.cyan('💡 Tips:\n'));
41
- console.log(chalk.white(' • Check for updates: ') + chalk.yellow('aicos update'));
41
+ console.log(chalk.white(' • Check for updates: ') + chalk.yellow('aicos upgrade'));
42
42
  console.log(chalk.white(' • Restart server: ') + chalk.yellow('aicos restart\n'));
43
43
 
44
44
  process.exit(0);
@@ -45,6 +45,7 @@ class FileSystemDatabaseManager {
45
45
  get errorLogsFile() { return path_1.default.join(this.dataPath, 'error-logs.json'); }
46
46
  get blacklistFile() { return path_1.default.join(this.dataPath, 'blacklist.json'); }
47
47
  get statisticsFile() { return path_1.default.join(this.dataPath, 'statistics.json'); }
48
+ get mcpFile() { return path_1.default.join(this.dataPath, 'mcps.json'); }
48
49
  // 创建空的统计数据结构
49
50
  createEmptyStatistics() {
50
51
  return {
@@ -130,6 +131,12 @@ class FileSystemDatabaseManager {
130
131
  writable: true,
131
132
  value: new Map()
132
133
  });
134
+ Object.defineProperty(this, "mcps", {
135
+ enumerable: true,
136
+ configurable: true,
137
+ writable: true,
138
+ value: []
139
+ });
133
140
  // 持久化统计数据
134
141
  Object.defineProperty(this, "statistics", {
135
142
  enumerable: true,
@@ -215,6 +222,7 @@ class FileSystemDatabaseManager {
215
222
  this.loadErrorLogs(),
216
223
  this.loadBlacklist(),
217
224
  this.loadStatistics(),
225
+ this.loadMCPs(),
218
226
  ]);
219
227
  });
220
228
  }
@@ -684,6 +692,22 @@ class FileSystemDatabaseManager {
684
692
  yield promises_1.default.writeFile(this.statisticsFile, JSON.stringify(this.statistics, null, 2));
685
693
  });
686
694
  }
695
+ loadMCPs() {
696
+ return __awaiter(this, void 0, void 0, function* () {
697
+ try {
698
+ const data = yield promises_1.default.readFile(this.mcpFile, 'utf-8');
699
+ this.mcps = JSON.parse(data);
700
+ }
701
+ catch (_a) {
702
+ this.mcps = [];
703
+ }
704
+ });
705
+ }
706
+ saveMCPs() {
707
+ return __awaiter(this, void 0, void 0, function* () {
708
+ yield promises_1.default.writeFile(this.mcpFile, JSON.stringify(this.mcps, null, 2));
709
+ });
710
+ }
687
711
  ensureDefaultConfig() {
688
712
  return __awaiter(this, void 0, void 0, function* () {
689
713
  if (!this.config) {
@@ -1263,6 +1287,101 @@ class FileSystemDatabaseManager {
1263
1287
  return count;
1264
1288
  });
1265
1289
  }
1290
+ /**
1291
+ * 搜索请求日志内容
1292
+ * @param query 搜索关键词
1293
+ * @param limit 返回��量限制
1294
+ * @param offset 偏移量
1295
+ * @returns 匹配的日志列表
1296
+ */
1297
+ searchLogs(query_1) {
1298
+ return __awaiter(this, arguments, void 0, function* (query, limit = 100, offset = 0) {
1299
+ const searchQuery = query.toLowerCase().trim();
1300
+ if (!searchQuery) {
1301
+ return this.getLogs(limit, offset);
1302
+ }
1303
+ const allMatches = [];
1304
+ const sortedShards = [...this.logShardsIndex].sort((a, b) => b.endTime - a.endTime);
1305
+ // 遍历所有分片进行搜索
1306
+ for (const shard of sortedShards) {
1307
+ const shardLogs = yield this.loadLogShard(shard.filename);
1308
+ for (const log of shardLogs) {
1309
+ if (this.logMatchesQuery(log, searchQuery)) {
1310
+ allMatches.push(log);
1311
+ }
1312
+ }
1313
+ }
1314
+ // 按时间倒序排列并分页
1315
+ return allMatches
1316
+ .sort((a, b) => b.timestamp - a.timestamp)
1317
+ .slice(offset, offset + limit);
1318
+ });
1319
+ }
1320
+ /**
1321
+ * 搜索请求日志内容数量
1322
+ * @param query 搜索关键词
1323
+ * @returns 匹配的日志数量
1324
+ */
1325
+ searchLogsCount(query) {
1326
+ return __awaiter(this, void 0, void 0, function* () {
1327
+ const searchQuery = query.toLowerCase().trim();
1328
+ if (!searchQuery) {
1329
+ return this.getLogsCount();
1330
+ }
1331
+ let count = 0;
1332
+ for (const shard of this.logShardsIndex) {
1333
+ const shardLogs = yield this.loadLogShard(shard.filename);
1334
+ for (const log of shardLogs) {
1335
+ if (this.logMatchesQuery(log, searchQuery)) {
1336
+ count++;
1337
+ }
1338
+ }
1339
+ }
1340
+ return count;
1341
+ });
1342
+ }
1343
+ /**
1344
+ * 检查日志是否匹配搜索查询
1345
+ */
1346
+ logMatchesQuery(log, query) {
1347
+ // 搜索请求体
1348
+ if (log.body) {
1349
+ const bodyStr = typeof log.body === 'string' ? log.body : JSON.stringify(log.body);
1350
+ if (bodyStr.toLowerCase().includes(query)) {
1351
+ return true;
1352
+ }
1353
+ }
1354
+ // 搜索响应体
1355
+ if (log.responseBody && typeof log.responseBody === 'string') {
1356
+ if (log.responseBody.toLowerCase().includes(query)) {
1357
+ return true;
1358
+ }
1359
+ }
1360
+ // 搜索流式响应块
1361
+ if (log.streamChunks && log.streamChunks.length > 0) {
1362
+ for (const chunk of log.streamChunks) {
1363
+ if (chunk.toLowerCase().includes(query)) {
1364
+ return true;
1365
+ }
1366
+ }
1367
+ }
1368
+ // 搜索错误信息
1369
+ if (log.error && log.error.toLowerCase().includes(query)) {
1370
+ return true;
1371
+ }
1372
+ // 搜索路径
1373
+ if (log.path && log.path.toLowerCase().includes(query)) {
1374
+ return true;
1375
+ }
1376
+ // 搜索模型名称
1377
+ if (log.requestModel && log.requestModel.toLowerCase().includes(query)) {
1378
+ return true;
1379
+ }
1380
+ if (log.targetModel && log.targetModel.toLowerCase().includes(query)) {
1381
+ return true;
1382
+ }
1383
+ return false;
1384
+ }
1266
1385
  // Error log operations
1267
1386
  addErrorLog(log) {
1268
1387
  return __awaiter(this, void 0, void 0, function* () {
@@ -1294,6 +1413,78 @@ class FileSystemDatabaseManager {
1294
1413
  return count;
1295
1414
  });
1296
1415
  }
1416
+ /**
1417
+ * 搜索错误日志内容
1418
+ * @param query 搜索关键词
1419
+ * @param limit 返回数量限制
1420
+ * @param offset 偏移量
1421
+ * @returns 匹配的错误日志列表
1422
+ */
1423
+ searchErrorLogs(query_1) {
1424
+ return __awaiter(this, arguments, void 0, function* (query, limit = 100, offset = 0) {
1425
+ const searchQuery = query.toLowerCase().trim();
1426
+ if (!searchQuery) {
1427
+ return this.getErrorLogs(limit, offset);
1428
+ }
1429
+ const matches = this.errorLogs.filter(log => this.errorLogMatchesQuery(log, searchQuery));
1430
+ return matches
1431
+ .sort((a, b) => b.timestamp - a.timestamp)
1432
+ .slice(offset, offset + limit);
1433
+ });
1434
+ }
1435
+ /**
1436
+ * 搜索错误日志内容数量
1437
+ * @param query 搜索关键词
1438
+ * @returns 匹配的错误日志数量
1439
+ */
1440
+ searchErrorLogsCount(query) {
1441
+ return __awaiter(this, void 0, void 0, function* () {
1442
+ const searchQuery = query.toLowerCase().trim();
1443
+ if (!searchQuery) {
1444
+ return this.getErrorLogsCount();
1445
+ }
1446
+ return this.errorLogs.filter(log => this.errorLogMatchesQuery(log, searchQuery)).length;
1447
+ });
1448
+ }
1449
+ /**
1450
+ * 检查错误日志是否匹配搜索查询
1451
+ */
1452
+ errorLogMatchesQuery(log, query) {
1453
+ // 搜索错误信息
1454
+ if (log.errorMessage && log.errorMessage.toLowerCase().includes(query)) {
1455
+ return true;
1456
+ }
1457
+ // 搜索错误堆栈
1458
+ if (log.errorStack && log.errorStack.toLowerCase().includes(query)) {
1459
+ return true;
1460
+ }
1461
+ // 搜索请求体
1462
+ if (log.requestBody) {
1463
+ const bodyStr = typeof log.requestBody === 'string' ? log.requestBody : JSON.stringify(log.requestBody);
1464
+ if (bodyStr.toLowerCase().includes(query)) {
1465
+ return true;
1466
+ }
1467
+ }
1468
+ // 搜索响应体
1469
+ if (log.responseBody) {
1470
+ const bodyStr = typeof log.responseBody === 'string' ? log.responseBody : JSON.stringify(log.responseBody);
1471
+ if (bodyStr.toLowerCase().includes(query)) {
1472
+ return true;
1473
+ }
1474
+ }
1475
+ // 搜索路径
1476
+ if (log.path && log.path.toLowerCase().includes(query)) {
1477
+ return true;
1478
+ }
1479
+ // 搜索模型名称
1480
+ if (log.requestModel && log.requestModel.toLowerCase().includes(query)) {
1481
+ return true;
1482
+ }
1483
+ if (log.targetModel && log.targetModel.toLowerCase().includes(query)) {
1484
+ return true;
1485
+ }
1486
+ return false;
1487
+ }
1297
1488
  // Service blacklist operations
1298
1489
  isServiceBlacklisted(serviceId, routeId, contentType) {
1299
1490
  return __awaiter(this, void 0, void 0, function* () {
@@ -1442,13 +1633,22 @@ class FileSystemDatabaseManager {
1442
1633
  if (!rule.routeId || typeof rule.routeId !== 'string') {
1443
1634
  return { valid: false, error: `规则[${index}](${rule.id}) 缺少有效的 routeId 字段` };
1444
1635
  }
1445
- if (!rule.targetServiceId || typeof rule.targetServiceId !== 'string') {
1446
- return { valid: false, error: `规则[${index}](${rule.id}) 缺少有效的 targetServiceId 字段` };
1447
- }
1448
1636
  const validContentTypes = ['default', 'background', 'thinking', 'long-context', 'image-understanding', 'model-mapping'];
1449
1637
  if (!rule.contentType || !validContentTypes.includes(rule.contentType)) {
1450
1638
  return { valid: false, error: `规则[${index}](${rule.id}) 的 contentType 无效` };
1451
1639
  }
1640
+ // 如果使用MCP(仅对图像理解类型有效),则不需要验证targetServiceId
1641
+ if (rule.useMCP === true && rule.contentType === 'image-understanding') {
1642
+ if (!rule.mcpId || typeof rule.mcpId !== 'string') {
1643
+ return { valid: false, error: `规则[${index}](${rule.id}) 使用MCP时缺少有效的 mcpId 字段` };
1644
+ }
1645
+ }
1646
+ else {
1647
+ // 不使用MCP时,必须验证targetServiceId
1648
+ if (!rule.targetServiceId || typeof rule.targetServiceId !== 'string') {
1649
+ return { valid: false, error: `规则[${index}](${rule.id}) 缺少有效的 targetServiceId 字段` };
1650
+ }
1651
+ }
1452
1652
  return { valid: true };
1453
1653
  }
1454
1654
  /**
@@ -2027,6 +2227,47 @@ class FileSystemDatabaseManager {
2027
2227
  return entry;
2028
2228
  });
2029
2229
  }
2230
+ // MCP 工具相关操作
2231
+ getMCPs() {
2232
+ return this.mcps.sort((a, b) => b.createdAt - a.createdAt);
2233
+ }
2234
+ getMCP(id) {
2235
+ return this.mcps.find(m => m.id === id);
2236
+ }
2237
+ getMCPsByTarget(targetType) {
2238
+ return this.mcps.filter(m => { var _a; return (_a = m.targets) === null || _a === void 0 ? void 0 : _a.includes(targetType); });
2239
+ }
2240
+ createMCP(mcp) {
2241
+ return __awaiter(this, void 0, void 0, function* () {
2242
+ const id = crypto_1.default.randomUUID();
2243
+ const now = Date.now();
2244
+ const newMCP = Object.assign(Object.assign({}, mcp), { id, createdAt: now, updatedAt: now });
2245
+ this.mcps.push(newMCP);
2246
+ yield this.saveMCPs();
2247
+ return newMCP;
2248
+ });
2249
+ }
2250
+ updateMCP(id, mcp) {
2251
+ return __awaiter(this, void 0, void 0, function* () {
2252
+ const index = this.mcps.findIndex(m => m.id === id);
2253
+ if (index === -1)
2254
+ return false;
2255
+ const now = Date.now();
2256
+ this.mcps[index] = Object.assign(Object.assign(Object.assign({}, this.mcps[index]), mcp), { id, updatedAt: now });
2257
+ yield this.saveMCPs();
2258
+ return true;
2259
+ });
2260
+ }
2261
+ deleteMCP(id) {
2262
+ return __awaiter(this, void 0, void 0, function* () {
2263
+ const index = this.mcps.findIndex(m => m.id === id);
2264
+ if (index === -1)
2265
+ return false;
2266
+ this.mcps.splice(index, 1);
2267
+ yield this.saveMCPs();
2268
+ return true;
2269
+ });
2270
+ }
2030
2271
  // Close method for compatibility (no-op for filesystem database)
2031
2272
  close() {
2032
2273
  // 文件系统数据库不需要关闭连接