aicodeswitch 3.0.1 → 3.0.3

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
@@ -18,8 +18,6 @@ AI Code Switch 是帮助你在本地管理 AI 编程工具接入大模型的工
18
18
  npm install -g aicodeswitch
19
19
  ```
20
20
 
21
- **注意:**由于工具依赖sqlite和leveldb作为数据存储,在安装过程中,会执行这两个数据库的编译,如果你是在windows电脑上安装,你需要安装 visual studio 2017 以上版本,才能正常编译数据库,macos 和 linux 系统中一般都自带了编译工具,因此大部分情况下都能正确编译。
22
-
23
21
  ### 使用方法
24
22
 
25
23
  **启动服务**
@@ -143,6 +141,12 @@ aicodeswitch内部,会根据“源类型”来转换数据。例如,你的
143
141
 
144
142
  当同一请求类型配置多个规则时,系统会按排序优先使用第一个,如果某个服务报错(4xx/5xx)或请求超时,将自动切换到下一个可用规则,确保你可以正常使用coding工具。
145
143
 
144
+ ### 数据转流
145
+
146
+ 支持将openai的流式响应数据转换成Claude Code的流式数据,并支持实时转换,从而可以让你使用支持openAI的API服务来在Claude Code中使用。
147
+
148
+ *不过,需要注意,如果你的API服务商不支持tools,或者所支持的max_tokens太小,是无法在Claude Code中使用的。*
149
+
146
150
  ## Skills管理
147
151
 
148
152
  你可以在 aicodeswitch 中集中统一管理 skills,把skills分发给claude code和codex,随时启用和停用skills。
package/bin/restore.js CHANGED
@@ -8,7 +8,11 @@ const ora = require('ora');
8
8
  // 停用所有激活的路由(直接操作数据库文件)
9
9
  const deactivateAllRoutes = () => {
10
10
  const appDir = path.join(os.homedir(), '.aicodeswitch');
11
- const routesFilePath = path.join(appDir, 'data', 'routes.json');
11
+ const primaryRoutesFilePath = path.join(appDir, 'fs-db', 'routes.json');
12
+ const legacyRoutesFilePath = path.join(appDir, 'data', 'routes.json');
13
+ const routesFilePath = fs.existsSync(primaryRoutesFilePath)
14
+ ? primaryRoutesFilePath
15
+ : legacyRoutesFilePath;
12
16
 
13
17
  // 如果数据文件不存在,说明没有路由需要停用
14
18
  if (!fs.existsSync(routesFilePath)) {
@@ -19,14 +23,23 @@ const deactivateAllRoutes = () => {
19
23
  // 读取路由数据
20
24
  const routesData = JSON.parse(fs.readFileSync(routesFilePath, 'utf-8'));
21
25
 
22
- if (!Array.isArray(routesData)) {
26
+ let routes = [];
27
+ let payload = routesData;
28
+
29
+ if (Array.isArray(routesData)) {
30
+ routes = routesData;
31
+ payload = routesData;
32
+ } else if (routesData && typeof routesData === 'object' && Array.isArray(routesData.routes)) {
33
+ routes = routesData.routes;
34
+ payload = { ...routesData, routes };
35
+ } else {
23
36
  return { success: false, error: 'Invalid routes data format' };
24
37
  }
25
38
 
26
39
  let deactivatedCount = 0;
27
40
 
28
41
  // 将所有激活的路由设置为停用状态
29
- routesData.forEach(route => {
42
+ routes.forEach(route => {
30
43
  if (route.isActive === true) {
31
44
  route.isActive = false;
32
45
  deactivatedCount++;
@@ -34,7 +47,7 @@ const deactivateAllRoutes = () => {
34
47
  });
35
48
 
36
49
  // 保存修改后的数据
37
- fs.writeFileSync(routesFilePath, JSON.stringify(routesData, null, 2));
50
+ fs.writeFileSync(routesFilePath, JSON.stringify(payload, null, 2));
38
51
 
39
52
  return { success: true, deactivatedCount };
40
53
  } catch (err) {
@@ -281,4 +294,4 @@ const restore = async () => {
281
294
  console.log('\n');
282
295
  };
283
296
 
284
- module.exports = restore;
297
+ module.exports = restore;
@@ -7,12 +7,23 @@ exports.cleanupInvalidMetadata = exports.checkCodexConfigStatus = exports.checkC
7
7
  const fs_1 = __importDefault(require("fs"));
8
8
  const path_1 = __importDefault(require("path"));
9
9
  const os_1 = __importDefault(require("os"));
10
+ /**
11
+ * 获取元数据目录
12
+ */
13
+ const getMetadataDirs = () => {
14
+ const homeDir = os_1.default.homedir();
15
+ return {
16
+ primary: path_1.default.join(homeDir, '.aicodeswitch', 'fs-db'),
17
+ legacy: path_1.default.join(homeDir, '.aicodeswitch', 'data'),
18
+ };
19
+ };
10
20
  /**
11
21
  * 获取元数据文件路径
12
22
  */
13
- const getMetadataFilePath = (configType) => {
14
- const dataDir = path_1.default.join(os_1.default.homedir(), '.aicodeswitch/data');
15
- return path_1.default.join(dataDir, `.${configType}-metadata.json`);
23
+ const getMetadataFilePath = (configType, useLegacy = false) => {
24
+ const dirs = getMetadataDirs();
25
+ const baseDir = useLegacy ? dirs.legacy : dirs.primary;
26
+ return path_1.default.join(baseDir, `.${configType}-metadata.json`);
16
27
  };
17
28
  /**
18
29
  * 保存配置元数据
@@ -40,11 +51,29 @@ exports.saveMetadata = saveMetadata;
40
51
  const loadMetadata = (configType) => {
41
52
  try {
42
53
  const metadataPath = getMetadataFilePath(configType);
43
- if (!fs_1.default.existsSync(metadataPath)) {
44
- return null;
54
+ if (fs_1.default.existsSync(metadataPath)) {
55
+ const content = fs_1.default.readFileSync(metadataPath, 'utf-8');
56
+ return JSON.parse(content);
57
+ }
58
+ // 兼容旧路径
59
+ const legacyPath = getMetadataFilePath(configType, true);
60
+ if (fs_1.default.existsSync(legacyPath)) {
61
+ const content = fs_1.default.readFileSync(legacyPath, 'utf-8');
62
+ const metadata = JSON.parse(content);
63
+ // 尝试迁移到新路径
64
+ try {
65
+ const dataDir = path_1.default.dirname(metadataPath);
66
+ if (!fs_1.default.existsSync(dataDir)) {
67
+ fs_1.default.mkdirSync(dataDir, { recursive: true });
68
+ }
69
+ fs_1.default.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2), 'utf-8');
70
+ }
71
+ catch (error) {
72
+ console.warn(`Failed to migrate metadata for ${configType}:`, error);
73
+ }
74
+ return metadata;
45
75
  }
46
- const content = fs_1.default.readFileSync(metadataPath, 'utf-8');
47
- return JSON.parse(content);
76
+ return null;
48
77
  }
49
78
  catch (error) {
50
79
  console.error(`Failed to load metadata for ${configType}:`, error);
@@ -58,9 +87,13 @@ exports.loadMetadata = loadMetadata;
58
87
  const deleteMetadata = (configType) => {
59
88
  try {
60
89
  const metadataPath = getMetadataFilePath(configType);
90
+ const legacyPath = getMetadataFilePath(configType, true);
61
91
  if (fs_1.default.existsSync(metadataPath)) {
62
92
  fs_1.default.unlinkSync(metadataPath);
63
93
  }
94
+ if (fs_1.default.existsSync(legacyPath)) {
95
+ fs_1.default.unlinkSync(legacyPath);
96
+ }
64
97
  return true;
65
98
  }
66
99
  catch (error) {
@@ -89,9 +89,10 @@ class DatabaseFactory {
89
89
  * 自动检测并创建数据库实例
90
90
  * 优先使用文件系统数据库,如果存在旧的 SQLite 数据库则自动迁移
91
91
  */
92
- static createAuto(dataPath) {
92
+ static createAuto(dataPath, legacyDataPath) {
93
93
  return __awaiter(this, void 0, void 0, function* () {
94
94
  const fs = yield Promise.resolve().then(() => __importStar(require('fs/promises')));
95
+ const legacyPath = legacyDataPath && legacyDataPath !== dataPath ? legacyDataPath : null;
95
96
  // 检查迁移完成标记文件
96
97
  const migrationMarkerPath = path_1.default.join(dataPath, '.migration-completed');
97
98
  const hasMigrationMarker = yield fs.access(migrationMarkerPath)
@@ -112,6 +113,58 @@ class DatabaseFactory {
112
113
  console.log('[Database] Using existing filesystem database (no migration marker)');
113
114
  return this.create(dataPath, 'filesystem');
114
115
  }
116
+ // 如果新目录为空,尝试从旧 data 目录迁移文件系统数据库
117
+ if (legacyPath) {
118
+ const legacyFsDbExists = yield fs.access(path_1.default.join(legacyPath, 'config.json'))
119
+ .then(() => true)
120
+ .catch(() => false);
121
+ if (legacyFsDbExists) {
122
+ console.log('[Database] Found legacy filesystem database, migrating to new directory...');
123
+ try {
124
+ yield migrateToFs.migrateLegacyFsData(legacyPath, dataPath);
125
+ yield fs.writeFile(migrationMarkerPath, new Date().toISOString(), 'utf-8');
126
+ console.log('[Database] Legacy filesystem database migration completed');
127
+ return this.create(dataPath, 'filesystem');
128
+ }
129
+ catch (error) {
130
+ console.error('[Database] Legacy filesystem migration failed:', error);
131
+ console.log('[Database] Falling back to create new filesystem database');
132
+ }
133
+ }
134
+ }
135
+ // 如果新目录为空,检查旧 data 目录是否存在 SQLite 数据库
136
+ if (legacyPath) {
137
+ const legacySqliteExists = yield fs.access(path_1.default.join(legacyPath, 'app.db'))
138
+ .then(() => true)
139
+ .catch(() => false);
140
+ if (legacySqliteExists) {
141
+ console.log('[Database] Found legacy SQLite database, migrating to new filesystem directory...');
142
+ try {
143
+ yield migrateToFs.migrateToFileSystem(legacyPath, dataPath);
144
+ console.log('[Database] Verifying migration...');
145
+ const verification = yield migrateToFs.verifyMigration(dataPath);
146
+ if (verification.success) {
147
+ console.log('[Database] ✅ Migration verified successfully');
148
+ if (verification.warnings.length > 0) {
149
+ console.log('[Database] Warnings:', verification.warnings);
150
+ }
151
+ yield fs.writeFile(migrationMarkerPath, new Date().toISOString(), 'utf-8');
152
+ console.log('[Database] Migration marker file created:', migrationMarkerPath);
153
+ }
154
+ else {
155
+ console.error('[Database] ❌ Migration verification failed');
156
+ console.error('[Database] Errors:', verification.errors);
157
+ throw new Error('Migration verification failed');
158
+ }
159
+ console.log('[Database] Migration completed, using filesystem database');
160
+ return this.create(dataPath, 'filesystem');
161
+ }
162
+ catch (error) {
163
+ console.error('[Database] Migration failed:', error);
164
+ console.log('[Database] Creating new filesystem database');
165
+ }
166
+ }
167
+ }
115
168
  // 检查是否存在旧的 SQLite 数据库
116
169
  const sqliteDbExists = yield fs.access(path_1.default.join(dataPath, 'app.db'))
117
170
  .then(() => true)
@@ -8,6 +8,17 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
+ var __rest = (this && this.__rest) || function (s, e) {
12
+ var t = {};
13
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
14
+ t[p] = s[p];
15
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
16
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
17
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
18
+ t[p[i]] = s[p[i]];
19
+ }
20
+ return t;
21
+ };
11
22
  var __asyncValues = (this && this.__asyncValues) || function (o) {
12
23
  if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
13
24
  var m = o[Symbol.asyncIterator], i;
@@ -441,14 +452,25 @@ class DatabaseManager {
441
452
  // Vendor operations
442
453
  getVendors() {
443
454
  const rows = this.db.prepare('SELECT * FROM vendors ORDER BY sort_order DESC, created_at DESC').all();
444
- return rows.map((row) => ({
445
- id: row.id,
446
- name: row.name,
447
- description: row.description,
448
- sortOrder: row.sort_order,
449
- createdAt: row.created_at,
450
- updatedAt: row.updated_at,
451
- }));
455
+ return rows.map((row) => {
456
+ const vendorId = row.id;
457
+ // 获取该供应商的所有服务
458
+ const services = this.getAPIServices(vendorId);
459
+ // 移除服务中的 vendorId 字段
460
+ const servicesWithoutVendorId = services.map((_a) => {
461
+ var { vendorId } = _a, service = __rest(_a, ["vendorId"]);
462
+ return service;
463
+ });
464
+ return {
465
+ id: row.id,
466
+ name: row.name,
467
+ description: row.description,
468
+ sortOrder: row.sort_order,
469
+ services: servicesWithoutVendorId, // 添加嵌套的 services 数组
470
+ createdAt: row.created_at,
471
+ updatedAt: row.updated_at,
472
+ };
473
+ });
452
474
  }
453
475
  createVendor(vendor) {
454
476
  const id = crypto_1.default.randomUUID();
@@ -1311,7 +1333,7 @@ class DatabaseManager {
1311
1333
  return {
1312
1334
  serviceId,
1313
1335
  serviceName: (serviceInfo === null || serviceInfo === void 0 ? void 0 : serviceInfo.name) || 'Unknown',
1314
- vendorName: serviceInfo ? vendorMap.get(serviceInfo.vendorId) || 'Unknown' : 'Unknown',
1336
+ vendorName: serviceInfo && serviceInfo.vendorId ? (vendorMap.get(serviceInfo.vendorId) || 'Unknown') : 'Unknown',
1315
1337
  totalRequests: stats.requests,
1316
1338
  totalTokens: stats.tokens,
1317
1339
  avgResponseTime: stats.requests > 0 ? Math.round(stats.responseTime / stats.requests) : 0,