aicodeswitch 3.0.1 → 3.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/README.md +0 -2
- package/bin/restore.js +18 -5
- package/dist/server/config-metadata.js +40 -7
- package/dist/server/database-factory.js +54 -1
- package/dist/server/database.js +31 -9
- package/dist/server/fs-database.js +306 -64
- package/dist/server/main.js +23 -17
- package/dist/server/migrate-to-fs.js +124 -24
- package/dist/ui/assets/index-B8caYm4n.css +1 -0
- package/dist/ui/assets/index-BHXiQgEv.js +466 -0
- package/dist/ui/index.html +2 -2
- package/package.json +1 -1
- package/dist/ui/assets/index-Bo5rJH01.js +0 -472
- package/dist/ui/assets/index-uPfIRVTr.css +0 -1
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
|
**启动服务**
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
15
|
-
|
|
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 (
|
|
44
|
-
|
|
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
|
-
|
|
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)
|
package/dist/server/database.js
CHANGED
|
@@ -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
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
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,
|