aicodeswitch 5.2.9 → 5.2.10
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 +8 -31
- package/bin/cli.js +3 -0
- package/bin/status.js +84 -0
- package/dist/server/main.js +32 -6
- package/dist/server/session-launcher.js +1 -1
- package/dist/server/tools-service.js +1 -0
- package/dist/server/utils.js +20 -6
- package/dist/ui/aicodeswitch-flow.svg +360 -0
- package/dist/ui/assets/{index-BKBriExY.js → index-NlzYhf99.js} +8 -31
- package/dist/ui/index.html +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -35,50 +35,26 @@ AI Code Switch 是帮助你在本地管理 AI 编程工具接入大模型的工
|
|
|
35
35
|
* 数据完全本地,自主可控
|
|
36
36
|
* 特殊语法:在发送的提示词最前面添加`[!]`来直接切换为高智商模型服务(`[x]`关闭),简单快捷
|
|
37
37
|
|
|
38
|
+

|
|
39
|
+
|
|
38
40
|
## 桌面客户端
|
|
39
41
|
|
|
40
42
|
[进入下载](https://github.com/tangshuang/aicodeswitch/releases)
|
|
41
43
|
|
|
42
44
|
## 命令行工具
|
|
43
45
|
|
|
44
|
-
### 安装
|
|
45
|
-
|
|
46
46
|
```
|
|
47
|
+
# 安装
|
|
47
48
|
npm install -g aicodeswitch
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
### 使用方法
|
|
51
|
-
|
|
52
|
-
**启动服务**
|
|
53
|
-
|
|
54
|
-
```
|
|
49
|
+
# 启动服务
|
|
55
50
|
aicos start
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
或者直接运行
|
|
59
|
-
|
|
60
|
-
```
|
|
51
|
+
# http://127.0.0.1:4567
|
|
52
|
+
# 启动服务并打开管理界面
|
|
61
53
|
aicos ui
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
**停止服务**
|
|
65
|
-
|
|
66
|
-
```
|
|
54
|
+
# 停止服务
|
|
67
55
|
aicos stop
|
|
68
56
|
```
|
|
69
57
|
|
|
70
|
-
**进入管理界面**
|
|
71
|
-
|
|
72
|
-
```
|
|
73
|
-
# 自动启动服务和打开界面
|
|
74
|
-
aicos ui
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
```
|
|
78
|
-
# 手动在浏览器打开管理界面
|
|
79
|
-
http://127.0.0.1:4567
|
|
80
|
-
```
|
|
81
|
-
|
|
82
58
|
## 管理界面
|
|
83
59
|
|
|
84
60
|
**配置供应商**
|
|
@@ -257,6 +233,7 @@ PORT=4567
|
|
|
257
233
|
## 关联资源
|
|
258
234
|
|
|
259
235
|
* [Claude Code 深度教程](https://claudecode.tangshuang.net): 100%免费的Claude Code入门到精通教程
|
|
236
|
+
* [AICodingBus](https://aicodingbus.24x7.to): AI Tokens 交换平台
|
|
260
237
|
|
|
261
238
|
## 支持我
|
|
262
239
|
|
package/bin/cli.js
CHANGED
|
@@ -7,6 +7,7 @@ const commands = {
|
|
|
7
7
|
start: require('./start'),
|
|
8
8
|
stop: require('./stop'),
|
|
9
9
|
restart: require('./restart'),
|
|
10
|
+
status: require('./status'),
|
|
10
11
|
upgrade: require('./upgrade'),
|
|
11
12
|
restore: require('./restore'),
|
|
12
13
|
version: require('./version'),
|
|
@@ -21,6 +22,7 @@ Commands:
|
|
|
21
22
|
start Start the AI Code Switch server
|
|
22
23
|
stop Stop the AI Code Switch server
|
|
23
24
|
restart Restart the AI Code Switch server
|
|
25
|
+
status Show server status, running address and port
|
|
24
26
|
ui Open the web UI in browser (starts server if needed)
|
|
25
27
|
upgrade Upgrade to the latest version and restart
|
|
26
28
|
restore Restore original configuration files
|
|
@@ -30,6 +32,7 @@ Example:
|
|
|
30
32
|
aicos start
|
|
31
33
|
aicos stop
|
|
32
34
|
aicos restart
|
|
35
|
+
aicos status
|
|
33
36
|
aicos ui
|
|
34
37
|
aicos upgrade
|
|
35
38
|
aicos restore
|
package/bin/status.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const boxen = require('boxen');
|
|
6
|
+
const { isServerRunning, getServerInfo } = require('./utils/get-server');
|
|
7
|
+
const { findPidByPort, getProcessInfo } = require('./utils/port-utils');
|
|
8
|
+
|
|
9
|
+
const PID_FILE = path.join(os.homedir(), '.aicodeswitch', 'server.pid');
|
|
10
|
+
const LOG_FILE = path.join(os.homedir(), '.aicodeswitch', 'server.log');
|
|
11
|
+
|
|
12
|
+
const status = async () => {
|
|
13
|
+
console.log('\n');
|
|
14
|
+
|
|
15
|
+
// 读取配置的 host/port
|
|
16
|
+
const { host, port } = getServerInfo();
|
|
17
|
+
const url = `http://${host}:${port}`;
|
|
18
|
+
|
|
19
|
+
// 优先通过 PID 文件判断
|
|
20
|
+
let pidFromFile = null;
|
|
21
|
+
if (fs.existsSync(PID_FILE)) {
|
|
22
|
+
try {
|
|
23
|
+
pidFromFile = parseInt(fs.readFileSync(PID_FILE, 'utf-8'), 10);
|
|
24
|
+
} catch (err) {
|
|
25
|
+
pidFromFile = null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const runningByPidFile = isServerRunning();
|
|
30
|
+
// 通过端口检测(兜底:PID 文件丢失但服务仍在监听的情况)
|
|
31
|
+
const pidByPort = await findPidByPort(port);
|
|
32
|
+
const isRunning = runningByPidFile || !!pidByPort;
|
|
33
|
+
|
|
34
|
+
if (isRunning) {
|
|
35
|
+
// PID 优先使用 PID 文件记录的,其次使用端口检测到的
|
|
36
|
+
const pid = runningByPidFile ? pidFromFile : pidByPort;
|
|
37
|
+
const processInfo = await getProcessInfo(pid);
|
|
38
|
+
|
|
39
|
+
console.log(boxen(
|
|
40
|
+
chalk.green.bold('🟢 AI Code Switch Server\n\n') +
|
|
41
|
+
chalk.white('Status: ') + chalk.green.bold('● Running\n') +
|
|
42
|
+
chalk.white('Host: ') + chalk.cyan(host) + '\n' +
|
|
43
|
+
chalk.white('Port: ') + chalk.cyan.bold(port) + '\n' +
|
|
44
|
+
chalk.white('URL: ') + chalk.cyan.bold(url) + '\n' +
|
|
45
|
+
chalk.white('PID: ') + chalk.yellow(pid) + '\n' +
|
|
46
|
+
chalk.white('Process: ') + chalk.gray(processInfo) + '\n' +
|
|
47
|
+
chalk.white('Logs: ') + chalk.gray(LOG_FILE) + '\n\n' +
|
|
48
|
+
chalk.gray('Open the URL in your browser to access the dashboard'),
|
|
49
|
+
{
|
|
50
|
+
padding: 1,
|
|
51
|
+
margin: 1,
|
|
52
|
+
borderStyle: 'double',
|
|
53
|
+
borderColor: 'green'
|
|
54
|
+
}
|
|
55
|
+
));
|
|
56
|
+
|
|
57
|
+
console.log(chalk.cyan('💡 Tips:\n'));
|
|
58
|
+
console.log(chalk.white(' • Open browser: ') + chalk.cyan(url));
|
|
59
|
+
console.log(chalk.white(' • View logs: ') + chalk.gray(`tail -f ${LOG_FILE}`));
|
|
60
|
+
console.log(chalk.white(' • Stop server: ') + chalk.yellow('aicos stop'));
|
|
61
|
+
console.log(chalk.white(' • Restart: ') + chalk.yellow('aicos restart'));
|
|
62
|
+
console.log('\n');
|
|
63
|
+
} else {
|
|
64
|
+
console.log(boxen(
|
|
65
|
+
chalk.gray('AI Code Switch Server\n\n') +
|
|
66
|
+
chalk.white('Status: ') + chalk.red('● Stopped\n\n') +
|
|
67
|
+
chalk.white('Host: ') + chalk.gray(host) + '\n' +
|
|
68
|
+
chalk.white('Port: ') + chalk.gray(port) + '\n' +
|
|
69
|
+
chalk.white('URL: ') + chalk.gray(url) + ' ' + chalk.gray('(not listening)'),
|
|
70
|
+
{
|
|
71
|
+
padding: 1,
|
|
72
|
+
margin: 1,
|
|
73
|
+
borderStyle: 'round',
|
|
74
|
+
borderColor: 'gray'
|
|
75
|
+
}
|
|
76
|
+
));
|
|
77
|
+
|
|
78
|
+
console.log(chalk.white('Use ') + chalk.cyan('aicos start') + chalk.white(' to start the server.\n'));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
process.exit(0);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
module.exports = status;
|
package/dist/server/main.js
CHANGED
|
@@ -3288,21 +3288,23 @@ ${instruction}
|
|
|
3288
3288
|
}
|
|
3289
3289
|
}
|
|
3290
3290
|
});
|
|
3291
|
+
// listen 就绪标志:区分"启动阶段"与"运行阶段",启动期致命异常应让进程退出
|
|
3292
|
+
let listenReady = false;
|
|
3291
3293
|
const start = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
3292
3294
|
fs_1.default.mkdirSync(dataDir, { recursive: true });
|
|
3293
3295
|
// 自动检测数据库类型并执行迁移(如果需要)
|
|
3294
|
-
console.
|
|
3296
|
+
console.time('[Server] step "database-init"');
|
|
3295
3297
|
const dbManager = yield database_factory_1.DatabaseFactory.createAuto(dataDir, legacyDataDir);
|
|
3296
|
-
console.
|
|
3298
|
+
console.timeEnd('[Server] step "database-init"');
|
|
3297
3299
|
// 服务启动时自动同步配置文件(适用于 CLI 和 dev:server)
|
|
3298
|
-
console.
|
|
3300
|
+
console.time('[Server] step "sync-configs"');
|
|
3299
3301
|
try {
|
|
3300
3302
|
yield syncConfigsOnServerStartup(dbManager);
|
|
3301
|
-
console.log('[Server] Tool config sync completed');
|
|
3302
3303
|
}
|
|
3303
3304
|
catch (error) {
|
|
3304
3305
|
console.error('[Server] Tool config sync failed:', error);
|
|
3305
3306
|
}
|
|
3307
|
+
console.timeEnd('[Server] step "sync-configs"');
|
|
3306
3308
|
// 清理旧的迁移临时文件
|
|
3307
3309
|
try {
|
|
3308
3310
|
(0, session_launcher_1.cleanupOldTempFiles)();
|
|
@@ -3328,8 +3330,10 @@ const start = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
3328
3330
|
// Initialize proxy server and register proxy routes last
|
|
3329
3331
|
proxyServer.initialize();
|
|
3330
3332
|
// Register admin routes first
|
|
3333
|
+
console.time('[Server] step "register-routes"');
|
|
3331
3334
|
yield registerRoutes(dbManager, proxyServer);
|
|
3332
3335
|
yield proxyServer.registerProxyRoutes();
|
|
3336
|
+
console.timeEnd('[Server] step "register-routes"');
|
|
3333
3337
|
app.use(express_1.default.static(path_1.default.resolve(__dirname, '../ui')));
|
|
3334
3338
|
// 404 处理程序 - 确保返回 JSON 而不是 HTML(放在所有路由和静态文件之后)
|
|
3335
3339
|
app.use((_req, res) => {
|
|
@@ -3347,14 +3351,28 @@ const start = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
3347
3351
|
console.error(`端口 ${port} 已被占用,无法启动服务。请执行 aicos stop 后重启。`);
|
|
3348
3352
|
process.exit(1);
|
|
3349
3353
|
}
|
|
3354
|
+
console.time('[Server] step "listen"');
|
|
3350
3355
|
const server = app.listen(port, host, () => {
|
|
3356
|
+
listenReady = true;
|
|
3351
3357
|
console.log(`Admin server running on http://${host}:${port}`);
|
|
3358
|
+
console.timeEnd('[Server] step "listen"');
|
|
3352
3359
|
// 启动后异步执行延迟维护任务(分片校验/修复、日志清理、会话索引构建)
|
|
3353
3360
|
// 不阻塞服务启动,后台静默执行
|
|
3354
3361
|
dbManager.deferredMaintenance().catch(err => {
|
|
3355
3362
|
console.error('[Server] Deferred maintenance error:', err);
|
|
3356
3363
|
});
|
|
3357
3364
|
});
|
|
3365
|
+
// 显式处理 listen 错误(EADDRINUSE/权限不足等),打印明确日志并退出,
|
|
3366
|
+
// 避免被全局 uncaughtException 静默吞掉导致"进程在但不 listen"
|
|
3367
|
+
server.on('error', (err) => {
|
|
3368
|
+
if (err.code === 'EADDRINUSE') {
|
|
3369
|
+
console.error(`[Server] 端口 ${port} 已被占用(EADDRINUSE)。请执行 aicos stop 后重启,或更换端口(PORT 环境变量)。`);
|
|
3370
|
+
}
|
|
3371
|
+
else {
|
|
3372
|
+
console.error('[Server] 监听失败:', err);
|
|
3373
|
+
}
|
|
3374
|
+
setImmediate(() => process.exit(1));
|
|
3375
|
+
});
|
|
3358
3376
|
// 创建 WebSocket 服务器用于工具安装
|
|
3359
3377
|
const toolInstallWss = (0, websocket_service_1.createToolInstallationWSServer)();
|
|
3360
3378
|
// 设置黑名单检查函数,用于在规则状态同步时检查黑名单是否已过期
|
|
@@ -3435,11 +3453,19 @@ const start = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
3435
3453
|
process.on('uncaughtException', (error) => {
|
|
3436
3454
|
console.error('[Uncaught Exception] 服务遇到未捕获的异常:', error);
|
|
3437
3455
|
console.error('[Uncaught Exception] 堆栈信息:', error.stack);
|
|
3438
|
-
//
|
|
3456
|
+
// 启动阶段(listen 之前)的异常通常是致命的(依赖加载失败、初始化崩溃等),
|
|
3457
|
+
// 静默吞掉会导致"进程在但不 listen",Tauri 只能干等超时;此时退出让上层重新探测/诊断。
|
|
3458
|
+
if (!listenReady) {
|
|
3459
|
+
console.error('[Uncaught Exception] 发生在服务监听之前,退出进程');
|
|
3460
|
+
process.exit(1);
|
|
3461
|
+
}
|
|
3439
3462
|
});
|
|
3440
3463
|
process.on('unhandledRejection', (reason) => {
|
|
3441
3464
|
console.error('[Unhandled Rejection] 服务遇到未处理的 Promise 拒绝:', reason);
|
|
3442
|
-
|
|
3465
|
+
if (!listenReady) {
|
|
3466
|
+
console.error('[Unhandled Rejection] 发生在服务监听之前,退出进程');
|
|
3467
|
+
process.exit(1);
|
|
3468
|
+
}
|
|
3443
3469
|
});
|
|
3444
3470
|
start().catch((error) => {
|
|
3445
3471
|
console.error('Failed to start server:', error);
|
|
@@ -22,7 +22,7 @@ const os_1 = require("os");
|
|
|
22
22
|
function which(cmd) {
|
|
23
23
|
try {
|
|
24
24
|
const command = process.platform === 'win32' ? `where ${cmd}` : `which ${cmd}`;
|
|
25
|
-
(0, child_process_1.execSync)(command, { stdio: 'ignore' });
|
|
25
|
+
(0, child_process_1.execSync)(command, { stdio: 'ignore', windowsHide: true }); // 隐藏 Windows 命令行窗口,避免检测时闪窗
|
|
26
26
|
return true;
|
|
27
27
|
}
|
|
28
28
|
catch (_a) {
|
package/dist/server/utils.js
CHANGED
|
@@ -9,13 +9,27 @@ Object.defineProperty(exports, "isLastMessageCompact", { enumerable: true, get:
|
|
|
9
9
|
Object.defineProperty(exports, "isCodexCompactRequest", { enumerable: true, get: function () { return compact_1.isCodexCompactRequest; } });
|
|
10
10
|
function checkPortUsable(port) {
|
|
11
11
|
return new Promise((resolve) => {
|
|
12
|
+
let settled = false;
|
|
12
13
|
const server = net.createConnection({ port });
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
const finish = (val) => {
|
|
15
|
+
if (settled)
|
|
16
|
+
return;
|
|
17
|
+
settled = true;
|
|
18
|
+
try {
|
|
19
|
+
server.destroy();
|
|
20
|
+
}
|
|
21
|
+
catch ( /* ignore */_a) { /* ignore */ }
|
|
22
|
+
resolve(val);
|
|
23
|
+
};
|
|
24
|
+
// 正常:连得上 = 端口被占;连不上(ECONNREFUSED) = 端口可用
|
|
25
|
+
server.on('connect', () => finish(false));
|
|
26
|
+
server.on('error', () => finish(true));
|
|
27
|
+
// 兜底:网络栈异常(防火墙/杀软 hook)时 connect/error 可能都不触发,
|
|
28
|
+
// 1.5s 后强制按可用处理,避免 start() 永久卡死。误判由 app.listen 的 EADDRINUSE 兜底。
|
|
29
|
+
server.setTimeout(1500);
|
|
30
|
+
server.once('timeout', () => {
|
|
31
|
+
console.warn(`[checkPortUsable] 探测端口 ${port} 超时(1.5s),按可用处理`);
|
|
32
|
+
finish(true);
|
|
19
33
|
});
|
|
20
34
|
});
|
|
21
35
|
}
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1400 860" font-family="ui-sans-serif, system-ui, -apple-system, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif" text-rendering="geometricPrecision">
|
|
2
|
+
<title>AICodeSwitch 转流架构图</title>
|
|
3
|
+
<desc>展示 AI 编程工具(Claude Code / Codex / Cursor 等)的请求,经 AICodeSwitch 本地代理完成鉴权、路由、规则匹配、格式转换后,转流到上游 LLM 服务商(Anthropic / OpenAI / Gemini / DeepSeek 等),响应原路回流。</desc>
|
|
4
|
+
|
|
5
|
+
<defs>
|
|
6
|
+
<linearGradient id="bgGrad" x1="0" y1="0" x2="0" y2="1">
|
|
7
|
+
<stop offset="0" stop-color="#0c1326"/>
|
|
8
|
+
<stop offset=".5" stop-color="#111c3a"/>
|
|
9
|
+
<stop offset="1" stop-color="#090f1f"/>
|
|
10
|
+
</linearGradient>
|
|
11
|
+
<radialGradient id="bgGlow" cx=".5" cy=".06" r=".78">
|
|
12
|
+
<stop offset="0" stop-color="#22d3ee" stop-opacity=".18"/>
|
|
13
|
+
<stop offset=".45" stop-color="#7c3aed" stop-opacity=".10"/>
|
|
14
|
+
<stop offset="1" stop-color="#090f1f" stop-opacity="0"/>
|
|
15
|
+
</radialGradient>
|
|
16
|
+
<linearGradient id="titleGrad" x1="0" y1="0" x2="1" y2="0">
|
|
17
|
+
<stop offset="0" stop-color="#5eead4"/>
|
|
18
|
+
<stop offset=".45" stop-color="#22d3ee"/>
|
|
19
|
+
<stop offset=".75" stop-color="#a855f7"/>
|
|
20
|
+
<stop offset="1" stop-color="#ec4899"/>
|
|
21
|
+
</linearGradient>
|
|
22
|
+
<linearGradient id="coreStroke" x1="0" y1="0" x2="1" y2="1">
|
|
23
|
+
<stop offset="0" stop-color="#22d3ee"/>
|
|
24
|
+
<stop offset=".5" stop-color="#a855f7"/>
|
|
25
|
+
<stop offset="1" stop-color="#ec4899"/>
|
|
26
|
+
</linearGradient>
|
|
27
|
+
<linearGradient id="coreFill" x1="0" y1="0" x2="0" y2="1">
|
|
28
|
+
<stop offset="0" stop-color="#0f1830" stop-opacity=".92"/>
|
|
29
|
+
<stop offset="1" stop-color="#0b1124" stop-opacity=".92"/>
|
|
30
|
+
</linearGradient>
|
|
31
|
+
<linearGradient id="panelFill" x1="0" y1="0" x2="1" y2="0">
|
|
32
|
+
<stop offset="0" stop-color="#16234c" stop-opacity=".78"/>
|
|
33
|
+
<stop offset="1" stop-color="#101935" stop-opacity=".78"/>
|
|
34
|
+
</linearGradient>
|
|
35
|
+
<radialGradient id="dotCyan">
|
|
36
|
+
<stop offset="0" stop-color="#cffafe"/>
|
|
37
|
+
<stop offset=".45" stop-color="#22d3ee"/>
|
|
38
|
+
<stop offset="1" stop-color="#22d3ee" stop-opacity="0"/>
|
|
39
|
+
</radialGradient>
|
|
40
|
+
<radialGradient id="dotPurple">
|
|
41
|
+
<stop offset="0" stop-color="#f3e8ff"/>
|
|
42
|
+
<stop offset=".45" stop-color="#c084fc"/>
|
|
43
|
+
<stop offset="1" stop-color="#a855f7" stop-opacity="0"/>
|
|
44
|
+
</radialGradient>
|
|
45
|
+
<marker id="arrowCyan" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
|
46
|
+
<path d="M0,1 L9,5 L0,9 z" fill="#22d3ee"/>
|
|
47
|
+
</marker>
|
|
48
|
+
<marker id="arrowPurple" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
|
49
|
+
<path d="M0,1 L9,5 L0,9 z" fill="#c084fc"/>
|
|
50
|
+
</marker>
|
|
51
|
+
<style>
|
|
52
|
+
@media (prefers-reduced-motion: reduce){
|
|
53
|
+
animate,animateTransform,animateMotion{display:none}
|
|
54
|
+
}
|
|
55
|
+
</style>
|
|
56
|
+
</defs>
|
|
57
|
+
|
|
58
|
+
<!-- ============ background ============ -->
|
|
59
|
+
<rect width="1400" height="860" fill="url(#bgGrad)"/>
|
|
60
|
+
<rect width="1400" height="860" fill="url(#bgGlow)"/>
|
|
61
|
+
|
|
62
|
+
<!-- ============ title ============ -->
|
|
63
|
+
<text x="700" y="60" text-anchor="middle" font-size="32" font-weight="800" fill="url(#titleGrad)" letter-spacing="0.5">AICodeSwitch · 转流架构</text>
|
|
64
|
+
<text x="700" y="89" text-anchor="middle" font-size="14.5" fill="#8c9bc4">本地代理 · 把 AI 编程工具的请求智能路由并转换格式,转流到任意模型 API</text>
|
|
65
|
+
|
|
66
|
+
<!-- column captions -->
|
|
67
|
+
<text x="169" y="196" text-anchor="middle" font-size="12" font-weight="700" fill="#6f7da6" letter-spacing="2">客户端 · AI 编程工具</text>
|
|
68
|
+
<text x="1231" y="158" text-anchor="middle" font-size="12" font-weight="700" fill="#6f7da6" letter-spacing="2">上游 · LLM 服务商</text>
|
|
69
|
+
|
|
70
|
+
<!-- ============ LEFT column · clients ============ -->
|
|
71
|
+
<!-- Claude Code -->
|
|
72
|
+
<g>
|
|
73
|
+
<rect x="64" y="220" width="210" height="118" rx="16" fill="#111a33" stroke="#243156"/>
|
|
74
|
+
<rect x="66" y="238" width="4" height="82" rx="2" fill="#a855f7"/>
|
|
75
|
+
<circle cx="108" cy="264" r="22" fill="#a855f7" fill-opacity=".16" stroke="#a855f7" stroke-width="1.5"/>
|
|
76
|
+
<text x="108" y="271" text-anchor="middle" font-size="18" font-weight="800" fill="#d8b4fe">C</text>
|
|
77
|
+
<text x="142" y="258" font-size="16.5" font-weight="700" fill="#e6edf7">Claude Code</text>
|
|
78
|
+
<text x="142" y="280" font-size="12" fill="#8c9bc4">/v1/messages</text>
|
|
79
|
+
<g transform="translate(82,298)">
|
|
80
|
+
<rect width="118" height="24" rx="12" fill="#a855f7" fill-opacity=".12" stroke="#a855f7" stroke-opacity=".5"/>
|
|
81
|
+
<text x="59" y="16" text-anchor="middle" font-size="11" fill="#d8b4fe">Anthropic 协议</text>
|
|
82
|
+
</g>
|
|
83
|
+
</g>
|
|
84
|
+
|
|
85
|
+
<!-- Codex -->
|
|
86
|
+
<g>
|
|
87
|
+
<rect x="64" y="372" width="210" height="118" rx="16" fill="#111a33" stroke="#243156"/>
|
|
88
|
+
<rect x="66" y="390" width="4" height="82" rx="2" fill="#10b981"/>
|
|
89
|
+
<circle cx="108" cy="416" r="22" fill="#10b981" fill-opacity=".16" stroke="#10b981" stroke-width="1.5"/>
|
|
90
|
+
<text x="108" y="423" text-anchor="middle" font-size="15" font-weight="800" fill="#a7f3d0">Cx</text>
|
|
91
|
+
<text x="142" y="410" font-size="16.5" font-weight="700" fill="#e6edf7">Codex</text>
|
|
92
|
+
<text x="142" y="432" font-size="12" fill="#8c9bc4">/v1/responses</text>
|
|
93
|
+
<g transform="translate(82,450)">
|
|
94
|
+
<rect width="118" height="24" rx="12" fill="#10b981" fill-opacity=".12" stroke="#10b981" stroke-opacity=".5"/>
|
|
95
|
+
<text x="59" y="16" text-anchor="middle" font-size="11" fill="#a7f3d0">OpenAI 协议</text>
|
|
96
|
+
</g>
|
|
97
|
+
</g>
|
|
98
|
+
|
|
99
|
+
<!-- Cursor / others -->
|
|
100
|
+
<g>
|
|
101
|
+
<rect x="64" y="524" width="210" height="118" rx="16" fill="#111a33" stroke="#243156"/>
|
|
102
|
+
<rect x="66" y="542" width="4" height="82" rx="2" fill="#22d3ee"/>
|
|
103
|
+
<circle cx="108" cy="568" r="22" fill="#22d3ee" fill-opacity=".16" stroke="#22d3ee" stroke-width="1.5"/>
|
|
104
|
+
<text x="108" y="576" text-anchor="middle" font-size="20" font-weight="800" fill="#a5f3fc">+</text>
|
|
105
|
+
<text x="142" y="562" font-size="16.5" font-weight="700" fill="#e6edf7">Cursor · 其他</text>
|
|
106
|
+
<text x="142" y="584" font-size="12" fill="#8c9bc4">/v1/chat/completions</text>
|
|
107
|
+
<g transform="translate(82,602)">
|
|
108
|
+
<rect width="132" height="24" rx="12" fill="#22d3ee" fill-opacity=".12" stroke="#22d3ee" stroke-opacity=".5"/>
|
|
109
|
+
<text x="66" y="16" text-anchor="middle" font-size="11" fill="#a5f3fc">OpenAI 兼容</text>
|
|
110
|
+
</g>
|
|
111
|
+
</g>
|
|
112
|
+
|
|
113
|
+
<!-- ============ CENTER · core engine ============ -->
|
|
114
|
+
<g>
|
|
115
|
+
<rect x="360" y="150" width="680" height="550" rx="22" fill="url(#coreFill)" stroke="url(#coreStroke)" stroke-width="1.6">
|
|
116
|
+
<animate attributeName="stroke-opacity" values="0.55;0.95;0.55" dur="4.5s" repeatCount="indefinite"/>
|
|
117
|
+
</rect>
|
|
118
|
+
<!-- top highlight -->
|
|
119
|
+
<path d="M382,150 H1018 a22,22 0 0 1 22,22 V192 H360 V172 a22,22 0 0 1 22,-22 z" fill="#ffffff" fill-opacity=".035"/>
|
|
120
|
+
<!-- header -->
|
|
121
|
+
<rect x="386" y="166" width="16" height="16" rx="4" fill="url(#coreStroke)"/>
|
|
122
|
+
<text x="410" y="180" font-size="17" font-weight="800" fill="#e6edf7">AICodeSwitch Proxy Core</text>
|
|
123
|
+
<text x="1014" y="180" text-anchor="end" font-size="11.5" fill="#6f7da6">localhost · 中转引擎</text>
|
|
124
|
+
|
|
125
|
+
<!-- ── M1 Auth ── -->
|
|
126
|
+
<g>
|
|
127
|
+
<rect x="380" y="210" width="640" height="86" rx="14" fill="url(#panelFill)" stroke="#1f2c52"/>
|
|
128
|
+
<circle cx="416" cy="253" r="18" fill="#22d3ee" fill-opacity=".14" stroke="#22d3ee" stroke-width="1.4"/>
|
|
129
|
+
<g transform="translate(408,243)" stroke="#67e8f9" stroke-width="1.6" fill="none" stroke-linecap="round">
|
|
130
|
+
<rect x="2" y="7" width="12" height="9" rx="2"/>
|
|
131
|
+
<path d="M5,7 V5 a3,3 0 0 1 6,0 V7"/>
|
|
132
|
+
</g>
|
|
133
|
+
<text x="446" y="244" font-size="15.5" font-weight="700" fill="#e6edf7">① 鉴权 Auth</text>
|
|
134
|
+
<text x="446" y="266" font-size="12" fill="#8c9bc4">API Key 解析 · sk_ 池密钥 / skr_ 路由密钥 · JWT</text>
|
|
135
|
+
<g transform="translate(818,242)">
|
|
136
|
+
<rect width="202" height="22" rx="11" fill="#22d3ee" fill-opacity=".10" stroke="#22d3ee" stroke-opacity=".4"/>
|
|
137
|
+
<text x="101" y="15" text-anchor="middle" font-size="10" fill="#67e8f9">Authorization · x-api-key · x-goog-api-key</text>
|
|
138
|
+
</g>
|
|
139
|
+
</g>
|
|
140
|
+
|
|
141
|
+
<!-- ── M2 Route + Rules ── -->
|
|
142
|
+
<g>
|
|
143
|
+
<rect x="380" y="308" width="640" height="148" rx="14" fill="url(#panelFill)" stroke="#1f2c52"/>
|
|
144
|
+
<circle cx="416" cy="338" r="18" fill="#a855f7" fill-opacity=".14" stroke="#a855f7" stroke-width="1.4"/>
|
|
145
|
+
<g transform="translate(407,329)" stroke="#d8b4fe" stroke-width="1.4" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
|
146
|
+
<circle cx="9" cy="9" r="7"/>
|
|
147
|
+
<path d="M9,4 L11,9 L9,14 L7,9 z" fill="#d8b4fe" stroke="none"/>
|
|
148
|
+
</g>
|
|
149
|
+
<text x="446" y="334" font-size="15.5" font-weight="700" fill="#e6edf7">② 路由 & 规则 Route · Rules</text>
|
|
150
|
+
<text x="446" y="356" font-size="12" fill="#8c9bc4">按内容类型分发到候选上游池,失败自动回退(fallback)</text>
|
|
151
|
+
|
|
152
|
+
<!-- content-type pills -->
|
|
153
|
+
<g font-size="11" font-weight="600">
|
|
154
|
+
<g transform="translate(396,378)">
|
|
155
|
+
<rect width="68" height="24" rx="12" fill="#94a3b8" fill-opacity=".10" stroke="#94a3b8" stroke-opacity=".5"/>
|
|
156
|
+
<text x="34" y="16" text-anchor="middle" fill="#cbd5e1">default</text>
|
|
157
|
+
</g>
|
|
158
|
+
<g transform="translate(472,378)">
|
|
159
|
+
<rect width="72" height="24" rx="12" fill="#a855f7" fill-opacity=".12" stroke="#a855f7" stroke-opacity=".55"/>
|
|
160
|
+
<text x="36" y="16" text-anchor="middle" fill="#d8b4fe">thinking</text>
|
|
161
|
+
</g>
|
|
162
|
+
<g transform="translate(554,378)">
|
|
163
|
+
<rect width="100" height="24" rx="12" fill="#3b82f6" fill-opacity=".12" stroke="#3b82f6" stroke-opacity=".55"/>
|
|
164
|
+
<text x="50" y="16" text-anchor="middle" fill="#bfdbfe">long-context</text>
|
|
165
|
+
</g>
|
|
166
|
+
<g transform="translate(396,410)">
|
|
167
|
+
<rect width="140" height="24" rx="12" fill="#ec4899" fill-opacity=".12" stroke="#ec4899" stroke-opacity=".55"/>
|
|
168
|
+
<text x="70" y="16" text-anchor="middle" fill="#f9a8d4">image-understanding</text>
|
|
169
|
+
</g>
|
|
170
|
+
<g transform="translate(544,410)">
|
|
171
|
+
<rect width="86" height="24" rx="12" fill="#06b6d4" fill-opacity=".12" stroke="#06b6d4" stroke-opacity=".55"/>
|
|
172
|
+
<text x="43" y="16" text-anchor="middle" fill="#a5f3fc">background</text>
|
|
173
|
+
</g>
|
|
174
|
+
<g transform="translate(638,410)">
|
|
175
|
+
<rect width="66" height="24" rx="12" fill="#f59e0b" fill-opacity=".12" stroke="#f59e0b" stroke-opacity=".55"/>
|
|
176
|
+
<text x="33" y="16" text-anchor="middle" fill="#fcd34d">high-iq</text>
|
|
177
|
+
</g>
|
|
178
|
+
</g>
|
|
179
|
+
</g>
|
|
180
|
+
|
|
181
|
+
<!-- ── M3 Transformers ── -->
|
|
182
|
+
<g>
|
|
183
|
+
<rect x="380" y="468" width="640" height="104" rx="14" fill="url(#panelFill)" stroke="#1f2c52"/>
|
|
184
|
+
<circle cx="416" cy="500" r="18" fill="#ec4899" fill-opacity=".14" stroke="#ec4899" stroke-width="1.4"/>
|
|
185
|
+
<g transform="translate(407,491)" stroke="#f9a8d4" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
|
186
|
+
<path d="M3,5 H15 M12,2 L15,5 L12,8"/>
|
|
187
|
+
<path d="M15,13 H3 M6,10 L3,13 L6,16"/>
|
|
188
|
+
</g>
|
|
189
|
+
<text x="446" y="498" font-size="15.5" font-weight="700" fill="#e6edf7">③ 格式转换 Transformers</text>
|
|
190
|
+
<text x="446" y="520" font-size="12" fill="#8c9bc4">Claude ↔ OpenAI ↔ Gemini ↔ DeepSeek · 双向转换 · SSE 流式</text>
|
|
191
|
+
<g transform="translate(446,536)" font-size="10.5" font-weight="700">
|
|
192
|
+
<rect x="0" y="0" width="56" height="22" rx="6" fill="#a855f7" fill-opacity=".16" stroke="#a855f7" stroke-opacity=".5"/>
|
|
193
|
+
<text x="28" y="15" text-anchor="middle" fill="#d8b4fe">Claude</text>
|
|
194
|
+
<text x="64" y="15" fill="#6f7da6">↔</text>
|
|
195
|
+
<rect x="78" y="0" width="58" height="22" rx="6" fill="#10b981" fill-opacity=".16" stroke="#10b981" stroke-opacity=".5"/>
|
|
196
|
+
<text x="107" y="15" text-anchor="middle" fill="#a7f3d0">OpenAI</text>
|
|
197
|
+
<text x="144" y="15" fill="#6f7da6">↔</text>
|
|
198
|
+
<rect x="158" y="0" width="58" height="22" rx="6" fill="#3b82f6" fill-opacity=".16" stroke="#3b82f6" stroke-opacity=".5"/>
|
|
199
|
+
<text x="187" y="15" text-anchor="middle" fill="#bfdbfe">Gemini</text>
|
|
200
|
+
<text x="224" y="15" fill="#6f7da6">↔</text>
|
|
201
|
+
<rect x="238" y="0" width="68" height="22" rx="6" fill="#8b5cf6" fill-opacity=".16" stroke="#8b5cf6" stroke-opacity=".5"/>
|
|
202
|
+
<text x="272" y="15" text-anchor="middle" fill="#c4b5fd">DeepSeek</text>
|
|
203
|
+
</g>
|
|
204
|
+
<g transform="translate(912,490)">
|
|
205
|
+
<rect width="96" height="22" rx="11" fill="#ec4899" fill-opacity=".12" stroke="#ec4899" stroke-opacity=".45"/>
|
|
206
|
+
<text x="48" y="15" text-anchor="middle" font-size="10.5" fill="#f9a8d4">SSE 流式</text>
|
|
207
|
+
</g>
|
|
208
|
+
</g>
|
|
209
|
+
|
|
210
|
+
<!-- ── M4 Logging ── -->
|
|
211
|
+
<g>
|
|
212
|
+
<rect x="380" y="584" width="640" height="92" rx="14" fill="url(#panelFill)" stroke="#1f2c52"/>
|
|
213
|
+
<circle cx="416" cy="616" r="18" fill="#10b981" fill-opacity=".14" stroke="#10b981" stroke-width="1.4"/>
|
|
214
|
+
<g transform="translate(407,607)" stroke="#6ee7b7" stroke-width="1.4" fill="none" stroke-linecap="round">
|
|
215
|
+
<path d="M3,15 H17"/>
|
|
216
|
+
<rect x="5" y="9" width="3" height="6" fill="#6ee7b7" stroke="none"/>
|
|
217
|
+
<rect x="10" y="5" width="3" height="10" fill="#6ee7b7" stroke="none"/>
|
|
218
|
+
<rect x="15" y="11" width="3" height="4" fill="#6ee7b7" stroke="none"/>
|
|
219
|
+
</g>
|
|
220
|
+
<text x="446" y="614" font-size="15.5" font-weight="700" fill="#e6edf7">④ 日志 / 会话 / 配额 Logging</text>
|
|
221
|
+
<text x="446" y="636" font-size="12" fill="#8c9bc4">请求日志 · 会话追踪 · 多维配额(Token / RPM / 并发)</text>
|
|
222
|
+
<g transform="translate(842,606)">
|
|
223
|
+
<rect width="78" height="22" rx="11" fill="#10b981" fill-opacity=".10" stroke="#10b981" stroke-opacity=".4"/>
|
|
224
|
+
<text x="39" y="15" text-anchor="middle" font-size="10.5" fill="#6ee7b7">Token 计费</text>
|
|
225
|
+
</g>
|
|
226
|
+
<g transform="translate(928,606)">
|
|
227
|
+
<rect width="78" height="22" rx="11" fill="#10b981" fill-opacity=".10" stroke="#10b981" stroke-opacity=".4"/>
|
|
228
|
+
<text x="39" y="15" text-anchor="middle" font-size="10.5" fill="#6ee7b7">配额限速</text>
|
|
229
|
+
</g>
|
|
230
|
+
</g>
|
|
231
|
+
</g>
|
|
232
|
+
|
|
233
|
+
<!-- ============ RIGHT column · upstream ============ -->
|
|
234
|
+
<!-- Anthropic -->
|
|
235
|
+
<g>
|
|
236
|
+
<rect x="1126" y="180" width="210" height="92" rx="14" fill="#111a33" stroke="#243156"/>
|
|
237
|
+
<rect x="1330" y="200" width="4" height="52" rx="2" fill="#f59e0b"/>
|
|
238
|
+
<circle cx="1158" cy="226" r="18" fill="#f59e0b" fill-opacity=".16" stroke="#f59e0b" stroke-width="1.5"/>
|
|
239
|
+
<text x="1158" y="232" text-anchor="middle" font-size="15" font-weight="800" fill="#fcd34d">A</text>
|
|
240
|
+
<text x="1188" y="221" font-size="14.5" font-weight="700" fill="#e6edf7">Anthropic</text>
|
|
241
|
+
<text x="1188" y="240" font-size="11.5" fill="#8c9bc4">Claude · claude-*</text>
|
|
242
|
+
</g>
|
|
243
|
+
<!-- OpenAI -->
|
|
244
|
+
<g>
|
|
245
|
+
<rect x="1126" y="288" width="210" height="92" rx="14" fill="#111a33" stroke="#243156"/>
|
|
246
|
+
<rect x="1330" y="308" width="4" height="52" rx="2" fill="#10b981"/>
|
|
247
|
+
<circle cx="1158" cy="334" r="18" fill="#10b981" fill-opacity=".16" stroke="#10b981" stroke-width="1.5"/>
|
|
248
|
+
<text x="1158" y="340" text-anchor="middle" font-size="15" font-weight="800" fill="#a7f3d0">O</text>
|
|
249
|
+
<text x="1188" y="329" font-size="14.5" font-weight="700" fill="#e6edf7">OpenAI</text>
|
|
250
|
+
<text x="1188" y="348" font-size="11.5" fill="#8c9bc4">GPT · o-*</text>
|
|
251
|
+
</g>
|
|
252
|
+
<!-- Gemini -->
|
|
253
|
+
<g>
|
|
254
|
+
<rect x="1126" y="396" width="210" height="92" rx="14" fill="#111a33" stroke="#243156"/>
|
|
255
|
+
<rect x="1330" y="416" width="4" height="52" rx="2" fill="#3b82f6"/>
|
|
256
|
+
<circle cx="1158" cy="442" r="18" fill="#3b82f6" fill-opacity=".16" stroke="#3b82f6" stroke-width="1.5"/>
|
|
257
|
+
<text x="1158" y="448" text-anchor="middle" font-size="15" font-weight="800" fill="#bfdbfe">G</text>
|
|
258
|
+
<text x="1188" y="437" font-size="14.5" font-weight="700" fill="#e6edf7">Google</text>
|
|
259
|
+
<text x="1188" y="456" font-size="11.5" fill="#8c9bc4">Gemini · gemini-*</text>
|
|
260
|
+
</g>
|
|
261
|
+
<!-- DeepSeek -->
|
|
262
|
+
<g>
|
|
263
|
+
<rect x="1126" y="504" width="210" height="92" rx="14" fill="#111a33" stroke="#243156"/>
|
|
264
|
+
<rect x="1330" y="524" width="4" height="52" rx="2" fill="#8b5cf6"/>
|
|
265
|
+
<circle cx="1158" cy="550" r="18" fill="#8b5cf6" fill-opacity=".16" stroke="#8b5cf6" stroke-width="1.5"/>
|
|
266
|
+
<text x="1158" y="556" text-anchor="middle" font-size="15" font-weight="800" fill="#c4b5fd">D</text>
|
|
267
|
+
<text x="1188" y="545" font-size="14.5" font-weight="700" fill="#e6edf7">DeepSeek</text>
|
|
268
|
+
<text x="1188" y="564" font-size="11.5" fill="#8c9bc4">deepseek-*</text>
|
|
269
|
+
</g>
|
|
270
|
+
<!-- Qwen / others -->
|
|
271
|
+
<g>
|
|
272
|
+
<rect x="1126" y="612" width="210" height="92" rx="14" fill="#111a33" stroke="#243156"/>
|
|
273
|
+
<rect x="1330" y="632" width="4" height="52" rx="2" fill="#06b6d4"/>
|
|
274
|
+
<circle cx="1158" cy="658" r="18" fill="#06b6d4" fill-opacity=".16" stroke="#06b6d4" stroke-width="1.5"/>
|
|
275
|
+
<text x="1158" y="664" text-anchor="middle" font-size="15" font-weight="800" fill="#a5f3fc">Q</text>
|
|
276
|
+
<text x="1188" y="653" font-size="14.5" font-weight="700" fill="#e6edf7">Qwen · Kimi 等</text>
|
|
277
|
+
<text x="1188" y="672" font-size="11.5" fill="#8c9bc4">兼容 OpenAI 协议</text>
|
|
278
|
+
</g>
|
|
279
|
+
|
|
280
|
+
<!-- ============ flow lines · LEFT (clients → core) ============ -->
|
|
281
|
+
<g fill="none">
|
|
282
|
+
<!-- L1 -->
|
|
283
|
+
<path d="M274,279 C314,279 322,375 360,375" stroke="#1c284f" stroke-width="2.2"/>
|
|
284
|
+
<path d="M274,279 C314,279 322,375 360,375" stroke="#22d3ee" stroke-width="2" stroke-linecap="round" stroke-dasharray="5 9" marker-end="url(#arrowCyan)" opacity=".92">
|
|
285
|
+
<animate attributeName="stroke-dashoffset" from="14" to="0" dur="1.1s" repeatCount="indefinite"/>
|
|
286
|
+
</path>
|
|
287
|
+
<circle r="3.8" fill="url(#dotCyan)">
|
|
288
|
+
<animateMotion dur="2.4s" repeatCount="indefinite" path="M274,279 C314,279 322,375 360,375"/>
|
|
289
|
+
</circle>
|
|
290
|
+
<!-- L2 -->
|
|
291
|
+
<path d="M274,431 C320,431 322,375 360,375" stroke="#1c284f" stroke-width="2.2"/>
|
|
292
|
+
<path d="M274,431 C320,431 322,375 360,375" stroke="#22d3ee" stroke-width="2" stroke-linecap="round" stroke-dasharray="5 9" marker-end="url(#arrowCyan)" opacity=".92">
|
|
293
|
+
<animate attributeName="stroke-dashoffset" from="14" to="0" dur="1.1s" begin="0.35s" repeatCount="indefinite"/>
|
|
294
|
+
</path>
|
|
295
|
+
<circle r="3.8" fill="url(#dotCyan)">
|
|
296
|
+
<animateMotion dur="2.4s" begin="0.8s" repeatCount="indefinite" path="M274,431 C320,431 322,375 360,375"/>
|
|
297
|
+
</circle>
|
|
298
|
+
<!-- L3 -->
|
|
299
|
+
<path d="M274,583 C326,583 324,375 360,375" stroke="#1c284f" stroke-width="2.2"/>
|
|
300
|
+
<path d="M274,583 C326,583 324,375 360,375" stroke="#22d3ee" stroke-width="2" stroke-linecap="round" stroke-dasharray="5 9" marker-end="url(#arrowCyan)" opacity=".92">
|
|
301
|
+
<animate attributeName="stroke-dashoffset" from="14" to="0" dur="1.1s" begin="0.7s" repeatCount="indefinite"/>
|
|
302
|
+
</path>
|
|
303
|
+
<circle r="3.8" fill="url(#dotCyan)">
|
|
304
|
+
<animateMotion dur="2.4s" begin="1.6s" repeatCount="indefinite" path="M274,583 C326,583 324,375 360,375"/>
|
|
305
|
+
</circle>
|
|
306
|
+
</g>
|
|
307
|
+
|
|
308
|
+
<!-- ============ flow lines · RIGHT (core → upstream) ============ -->
|
|
309
|
+
<g fill="none">
|
|
310
|
+
<!-- R1 -->
|
|
311
|
+
<path d="M1040,430 C1082,430 1088,226 1126,226" stroke="#1c284f" stroke-width="2.2"/>
|
|
312
|
+
<path d="M1040,430 C1082,430 1088,226 1126,226" stroke="#c084fc" stroke-width="2" stroke-linecap="round" stroke-dasharray="5 9" marker-end="url(#arrowPurple)" opacity=".92">
|
|
313
|
+
<animate attributeName="stroke-dashoffset" from="14" to="0" dur="1.2s" repeatCount="indefinite"/>
|
|
314
|
+
</path>
|
|
315
|
+
<circle r="3.8" fill="url(#dotPurple)">
|
|
316
|
+
<animateMotion dur="2.8s" repeatCount="indefinite" path="M1040,430 C1082,430 1088,226 1126,226"/>
|
|
317
|
+
</circle>
|
|
318
|
+
<!-- R2 -->
|
|
319
|
+
<path d="M1040,430 C1082,430 1088,334 1126,334" stroke="#1c284f" stroke-width="2.2"/>
|
|
320
|
+
<path d="M1040,430 C1082,430 1088,334 1126,334" stroke="#c084fc" stroke-width="2" stroke-linecap="round" stroke-dasharray="5 9" marker-end="url(#arrowPurple)" opacity=".92">
|
|
321
|
+
<animate attributeName="stroke-dashoffset" from="14" to="0" dur="1.2s" begin="0.3s" repeatCount="indefinite"/>
|
|
322
|
+
</path>
|
|
323
|
+
<circle r="3.8" fill="url(#dotPurple)">
|
|
324
|
+
<animateMotion dur="2.8s" begin="0.56s" repeatCount="indefinite" path="M1040,430 C1082,430 1088,334 1126,334"/>
|
|
325
|
+
</circle>
|
|
326
|
+
<!-- R3 -->
|
|
327
|
+
<path d="M1040,430 C1082,430 1088,442 1126,442" stroke="#1c284f" stroke-width="2.2"/>
|
|
328
|
+
<path d="M1040,430 C1082,430 1088,442 1126,442" stroke="#c084fc" stroke-width="2" stroke-linecap="round" stroke-dasharray="5 9" marker-end="url(#arrowPurple)" opacity=".92">
|
|
329
|
+
<animate attributeName="stroke-dashoffset" from="14" to="0" dur="1.2s" begin="0.6s" repeatCount="indefinite"/>
|
|
330
|
+
</path>
|
|
331
|
+
<circle r="3.8" fill="url(#dotPurple)">
|
|
332
|
+
<animateMotion dur="2.8s" begin="1.12s" repeatCount="indefinite" path="M1040,430 C1082,430 1088,442 1126,442"/>
|
|
333
|
+
</circle>
|
|
334
|
+
<!-- R4 -->
|
|
335
|
+
<path d="M1040,430 C1084,430 1088,550 1126,550" stroke="#1c284f" stroke-width="2.2"/>
|
|
336
|
+
<path d="M1040,430 C1084,430 1088,550 1126,550" stroke="#c084fc" stroke-width="2" stroke-linecap="round" stroke-dasharray="5 9" marker-end="url(#arrowPurple)" opacity=".92">
|
|
337
|
+
<animate attributeName="stroke-dashoffset" from="14" to="0" dur="1.2s" begin="0.9s" repeatCount="indefinite"/>
|
|
338
|
+
</path>
|
|
339
|
+
<circle r="3.8" fill="url(#dotPurple)">
|
|
340
|
+
<animateMotion dur="2.8s" begin="1.68s" repeatCount="indefinite" path="M1040,430 C1084,430 1088,550 1126,550"/>
|
|
341
|
+
</circle>
|
|
342
|
+
<!-- R5 -->
|
|
343
|
+
<path d="M1040,430 C1086,430 1088,658 1126,658" stroke="#1c284f" stroke-width="2.2"/>
|
|
344
|
+
<path d="M1040,430 C1086,430 1088,658 1126,658" stroke="#c084fc" stroke-width="2" stroke-linecap="round" stroke-dasharray="5 9" marker-end="url(#arrowPurple)" opacity=".92">
|
|
345
|
+
<animate attributeName="stroke-dashoffset" from="14" to="0" dur="1.2s" begin="1.2s" repeatCount="indefinite"/>
|
|
346
|
+
</path>
|
|
347
|
+
<circle r="3.8" fill="url(#dotPurple)">
|
|
348
|
+
<animateMotion dur="2.8s" begin="2.24s" repeatCount="indefinite" path="M1040,430 C1086,430 1088,658 1126,658"/>
|
|
349
|
+
</circle>
|
|
350
|
+
</g>
|
|
351
|
+
|
|
352
|
+
<!-- ============ footer caption + legend ============ -->
|
|
353
|
+
<text x="700" y="778" text-anchor="middle" font-size="12.5" fill="#7c89ab">流向:客户端发起请求 → 鉴权 → 路由 / 规则匹配 → 格式转换 → 上游模型;响应原路回流</text>
|
|
354
|
+
<g transform="translate(592,800)" font-size="11" fill="#8c9bc4">
|
|
355
|
+
<line x1="0" y1="0" x2="34" y2="0" stroke="#22d3ee" stroke-width="2" stroke-dasharray="5 9"/>
|
|
356
|
+
<text x="42" y="4">请求汇聚</text>
|
|
357
|
+
<line x1="130" y1="0" x2="164" y2="0" stroke="#c084fc" stroke-width="2" stroke-dasharray="5 9"/>
|
|
358
|
+
<text x="172" y="4">分发上游</text>
|
|
359
|
+
</g>
|
|
360
|
+
</svg>
|
|
@@ -176,50 +176,26 @@ AI Code Switch 是帮助你在本地管理 AI 编程工具接入大模型的工
|
|
|
176
176
|
* 数据完全本地,自主可控
|
|
177
177
|
* 特殊语法:在发送的提示词最前面添加\`[!]\`来直接切换为高智商模型服务(\`[x]\`关闭),简单快捷
|
|
178
178
|
|
|
179
|
+

|
|
180
|
+
|
|
179
181
|
## 桌面客户端
|
|
180
182
|
|
|
181
183
|
[进入下载](https://github.com/tangshuang/aicodeswitch/releases)
|
|
182
184
|
|
|
183
185
|
## 命令行工具
|
|
184
186
|
|
|
185
|
-
### 安装
|
|
186
|
-
|
|
187
187
|
\`\`\`
|
|
188
|
+
# 安装
|
|
188
189
|
npm install -g aicodeswitch
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
### 使用方法
|
|
192
|
-
|
|
193
|
-
**启动服务**
|
|
194
|
-
|
|
195
|
-
\`\`\`
|
|
190
|
+
# 启动服务
|
|
196
191
|
aicos start
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
或者直接运行
|
|
200
|
-
|
|
201
|
-
\`\`\`
|
|
192
|
+
# http://127.0.0.1:4567
|
|
193
|
+
# 启动服务并打开管理界面
|
|
202
194
|
aicos ui
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
**停止服务**
|
|
206
|
-
|
|
207
|
-
\`\`\`
|
|
195
|
+
# 停止服务
|
|
208
196
|
aicos stop
|
|
209
197
|
\`\`\`
|
|
210
198
|
|
|
211
|
-
**进入管理界面**
|
|
212
|
-
|
|
213
|
-
\`\`\`
|
|
214
|
-
# 自动启动服务和打开界面
|
|
215
|
-
aicos ui
|
|
216
|
-
\`\`\`
|
|
217
|
-
|
|
218
|
-
\`\`\`
|
|
219
|
-
# 手动在浏览器打开管理界面
|
|
220
|
-
http://127.0.0.1:4567
|
|
221
|
-
\`\`\`
|
|
222
|
-
|
|
223
199
|
## 管理界面
|
|
224
200
|
|
|
225
201
|
**配置供应商**
|
|
@@ -398,6 +374,7 @@ PORT=4567
|
|
|
398
374
|
## 关联资源
|
|
399
375
|
|
|
400
376
|
* [Claude Code 深度教程](https://claudecode.tangshuang.net): 100%免费的Claude Code入门到精通教程
|
|
377
|
+
* [AICodingBus](https://aicodingbus.24x7.to): AI Tokens 交换平台
|
|
401
378
|
|
|
402
379
|
## 支持我
|
|
403
380
|
|
package/dist/ui/index.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>AI Code Switch</title>
|
|
7
|
-
<script type="module" crossorigin src="./assets/index-
|
|
7
|
+
<script type="module" crossorigin src="./assets/index-NlzYhf99.js"></script>
|
|
8
8
|
<link rel="stylesheet" crossorigin href="./assets/index-MjMlew6J.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aicodeswitch",
|
|
3
|
-
"version": "5.2.
|
|
3
|
+
"version": "5.2.10",
|
|
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",
|